1/*
2 * Copyright (c) 2015 Kurt Jung (Gmail: kurt.w.jung)
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17package gofpdf
18
19import (
20	"bytes"
21	"fmt"
22	"io"
23	"io/ioutil"
24	"sort"
25)
26
27type sortType struct {
28	length int
29	less   func(int, int) bool
30	swap   func(int, int)
31}
32
33func (s *sortType) Len() int {
34	return s.length
35}
36
37func (s *sortType) Less(i, j int) bool {
38	return s.less(i, j)
39}
40
41func (s *sortType) Swap(i, j int) {
42	s.swap(i, j)
43}
44
45func gensort(Len int, Less func(int, int) bool, Swap func(int, int)) {
46	sort.Sort(&sortType{length: Len, less: Less, swap: Swap})
47}
48
49func writeBytes(leadStr string, startPos int, sl []byte) {
50	var pos, max int
51	var b byte
52	fmt.Printf("%s %07x", leadStr, startPos)
53	max = len(sl)
54	for pos < max {
55		fmt.Printf(" ")
56		for k := 0; k < 8; k++ {
57			if pos < max {
58				fmt.Printf(" %02x", sl[pos])
59			} else {
60				fmt.Printf("   ")
61			}
62			pos++
63		}
64	}
65	fmt.Printf("  |")
66	pos = 0
67	for pos < max {
68		b = sl[pos]
69		if b < 32 || b >= 128 {
70			b = '.'
71		}
72		fmt.Printf("%c", b)
73		pos++
74	}
75	fmt.Printf("|\n")
76}
77
78func checkBytes(pos int, sl1, sl2 []byte, printDiff bool) (eq bool) {
79	eq = bytes.Equal(sl1, sl2)
80	if !eq && printDiff {
81		writeBytes("<", pos, sl1)
82		writeBytes(">", pos, sl2)
83	}
84	return
85}
86
87// CompareBytes compares the bytes referred to by sl1 with those referred to by
88// sl2. Nil is returned if the buffers are equal, otherwise an error.
89func CompareBytes(sl1, sl2 []byte, printDiff bool) (err error) {
90	var posStart, posEnd, len1, len2, length int
91	var diffs bool
92
93	len1 = len(sl1)
94	len2 = len(sl2)
95	length = len1
96	if length > len2 {
97		length = len2
98	}
99	for posStart < length-1 {
100		posEnd = posStart + 16
101		if posEnd > length {
102			posEnd = length
103		}
104		if !checkBytes(posStart, sl1[posStart:posEnd], sl2[posStart:posEnd], printDiff) {
105			diffs = true
106		}
107		posStart = posEnd
108	}
109	if diffs {
110		err = fmt.Errorf("documents are different")
111	}
112	return
113}
114
115// ComparePDFs reads and compares the full contents of the two specified
116// readers byte-for-byte. Nil is returned if the buffers are equal, otherwise
117// an error.
118func ComparePDFs(rdr1, rdr2 io.Reader, printDiff bool) (err error) {
119	var b1, b2 *bytes.Buffer
120	_, err = b1.ReadFrom(rdr1)
121	if err == nil {
122		_, err = b2.ReadFrom(rdr2)
123		if err == nil {
124			err = CompareBytes(b1.Bytes(), b2.Bytes(), printDiff)
125		}
126	}
127	return
128}
129
130// ComparePDFFiles reads and compares the full contents of the two specified
131// files byte-for-byte. Nil is returned if the file contents are equal, or if
132// the second file is missing, otherwise an error.
133func ComparePDFFiles(file1Str, file2Str string, printDiff bool) (err error) {
134	var sl1, sl2 []byte
135	sl1, err = ioutil.ReadFile(file1Str)
136	if err == nil {
137		sl2, err = ioutil.ReadFile(file2Str)
138		if err == nil {
139			err = CompareBytes(sl1, sl2, printDiff)
140		} else {
141			// Second file is missing; treat this as success
142			err = nil
143		}
144	}
145	return
146}
147