mirror of
https://github.com/unidoc/unipdf.git
synced 2025-04-27 13:48:51 +08:00
370 lines
10 KiB
Go
370 lines
10 KiB
Go
/*
|
|
* This file is subject to the terms and conditions defined in
|
|
* file 'LICENSE.md', which is part of this source code package.
|
|
*/
|
|
|
|
package core
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"testing"
|
|
|
|
"github.com/unidoc/unipdf/v3/common"
|
|
)
|
|
|
|
func init() {
|
|
common.SetLogger(common.ConsoleLogger{})
|
|
}
|
|
|
|
// Test flate encoding - Predictor 1.
|
|
func TestFlateEncodingPredictor1(t *testing.T) {
|
|
rawStream := []byte("this is a dummy text with some \x01\x02\x03 binary data")
|
|
|
|
encoder := NewFlateEncoder()
|
|
encoder.Predictor = 1
|
|
|
|
encoded, err := encoder.EncodeBytes(rawStream)
|
|
if err != nil {
|
|
t.Errorf("Failed to encode data: %v", err)
|
|
return
|
|
}
|
|
|
|
decoded, err := encoder.DecodeBytes(encoded)
|
|
if err != nil {
|
|
t.Errorf("Failed to decode data: %v", err)
|
|
return
|
|
}
|
|
|
|
if !compareSlices(decoded, rawStream) {
|
|
t.Errorf("Slices not matching")
|
|
t.Errorf("Decoded (%d): % x", len(encoded), encoded)
|
|
t.Errorf("Raw (%d): % x", len(rawStream), rawStream)
|
|
return
|
|
}
|
|
}
|
|
|
|
// Test post decoding predictors.
|
|
func TestPostDecodingPredictors(t *testing.T) {
|
|
|
|
testcases := []struct {
|
|
BitsPerComponent int
|
|
Colors int
|
|
Columns int
|
|
Predictor int
|
|
Input []byte
|
|
Expected []byte
|
|
}{
|
|
// BPC=8, Colors=3, PNG predictor = none.
|
|
{
|
|
BitsPerComponent: 8,
|
|
Colors: 3,
|
|
Columns: 3,
|
|
Predictor: 15,
|
|
Input: []byte{
|
|
pfNone, 1, 2, 3, 1, 2, 3, 1, 2, 3, // Row 1.
|
|
pfNone, 3, 2, 1, 3, 2, 1, 3, 2, 1, // Row 2.
|
|
pfNone, 1, 2, 3, 1, 2, 3, 1, 2, 3, // Row 3.
|
|
},
|
|
Expected: []byte{
|
|
1, 2, 3, 1, 2, 3, 1, 2, 3,
|
|
3, 2, 1, 3, 2, 1, 3, 2, 1,
|
|
1, 2, 3, 1, 2, 3, 1, 2, 3,
|
|
},
|
|
},
|
|
// BPC=8, Colors=3, PNG predictor = sub (same as left).
|
|
{
|
|
BitsPerComponent: 8,
|
|
Colors: 3,
|
|
Columns: 3,
|
|
Predictor: 15,
|
|
Input: []byte{
|
|
pfSub, 1, 2, 3, 1, 2, 3, 1, 2, 3, // Row 1.
|
|
pfSub, 3, 2, 1, 3, 2, 1, 3, 2, 1, // Row 2.
|
|
pfSub, 1, 2, 3, 1, 2, 3, 1, 2, 3, // Row 3.
|
|
},
|
|
Expected: []byte{
|
|
1, 2, 3, 1 + 1, 2 + 2, 3 + 3, 1 + 1 + 1, 2 + 2 + 2, 3 + 3 + 3,
|
|
3, 2, 1, 3 + 3, 2 + 2, 1 + 1, 3 + 3 + 3, 2 + 2 + 2, 1 + 1 + 1,
|
|
1, 2, 3, 1 + 1, 2 + 2, 3 + 3, 1 + 1 + 1, 2 + 2 + 2, 3 + 3 + 3,
|
|
},
|
|
},
|
|
// BPC=8, Colors=3, PNG predictor = up (same as above).
|
|
{
|
|
BitsPerComponent: 8,
|
|
Colors: 3,
|
|
Columns: 3,
|
|
Predictor: 15,
|
|
Input: []byte{
|
|
pfUp, 1, 2, 3, 1, 2, 3, 1, 2, 3, // Row 1.
|
|
pfUp, 3, 2, 1, 3, 2, 1, 3, 2, 1, // Row 2.
|
|
pfUp, 1, 2, 3, 1, 2, 3, 1, 2, 3, // Row 3.
|
|
},
|
|
Expected: []byte{
|
|
1, 2, 3, 1, 2, 3, 1, 2, 3,
|
|
3 + 1, 2 + 2, 1 + 3, 3 + 1, 2 + 2, 1 + 3, 3 + 1, 2 + 2, 1 + 3,
|
|
1 + 3 + 1, 2 + 2 + 2, 3 + 1 + 3, 1 + 3 + 1, 2 + 2 + 2, 3 + 1 + 3, 1 + 3 + 1, 2 + 2 + 2, 3 + 1 + 3,
|
|
},
|
|
},
|
|
// BPC=8, Colors=3, PNG predictor = avg (average of left and above).
|
|
// Use a spreadsheet to get expected values.
|
|
{
|
|
BitsPerComponent: 8,
|
|
Colors: 3,
|
|
Columns: 3,
|
|
Predictor: 15,
|
|
Input: []byte{
|
|
pfAvg, 1, 2, 3, 1, 2, 3, 1, 2, 3, // Row 1.
|
|
pfAvg, 3, 2, 1, 3, 2, 1, 3, 2, 1, // Row 2.
|
|
pfAvg, 1, 2, 3, 1, 2, 3, 1, 2, 3, // Row 3.
|
|
},
|
|
Expected: []byte{
|
|
1, 2, 3, 1, 3, 4, 1, 3, 5,
|
|
3, 3, 2, 5, 5, 4, 6, 6, 5,
|
|
2, 3, 4, 4, 6, 7, 6, 8, 9,
|
|
},
|
|
},
|
|
// BPC=8, Colors=3, PNG predictor = Paeth.
|
|
// Use a spreadsheet to get expected values.
|
|
{
|
|
BitsPerComponent: 8,
|
|
Colors: 3,
|
|
Columns: 3,
|
|
Predictor: 15,
|
|
Input: []byte{
|
|
pfPaeth, 1, 2, 3, 1, 2, 3, 1, 2, 3, // Row 1.
|
|
pfPaeth, 3, 2, 1, 3, 2, 1, 3, 2, 1, // Row 2.
|
|
pfPaeth, 1, 2, 3, 1, 2, 3, 1, 2, 3, // Row 3.
|
|
},
|
|
Expected: []byte{
|
|
1, 2, 3, 2, 4, 6, 3, 6, 9,
|
|
4, 4, 4, 7, 6, 7, 10, 8, 10,
|
|
5, 6, 7, 8, 8, 10, 11, 10, 13,
|
|
},
|
|
},
|
|
}
|
|
|
|
for i, tcase := range testcases {
|
|
encoder := &FlateEncoder{
|
|
BitsPerComponent: tcase.BitsPerComponent,
|
|
Colors: tcase.Colors,
|
|
Columns: tcase.Columns,
|
|
Predictor: tcase.Predictor,
|
|
}
|
|
|
|
predicted, err := encoder.postDecodePredict(tcase.Input)
|
|
if err != nil {
|
|
t.Fatalf("Error: %v", err)
|
|
}
|
|
|
|
t.Logf("%d: % d\n", i, predicted)
|
|
if !compareSlices(predicted, tcase.Expected) {
|
|
t.Errorf("Slices not matching (i = %d)", i)
|
|
t.Errorf("Predicted (%d): % d", len(predicted), predicted)
|
|
t.Fatalf("Expected (%d): % d", len(tcase.Expected), tcase.Expected)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test LZW encoding.
|
|
func TestLZWEncoding(t *testing.T) {
|
|
rawStream := []byte("this is a dummy text with some \x01\x02\x03 binary data")
|
|
|
|
encoder := NewLZWEncoder()
|
|
// Only supporitng early change 0 for encoding at the moment.
|
|
encoder.EarlyChange = 0
|
|
|
|
encoded, err := encoder.EncodeBytes(rawStream)
|
|
if err != nil {
|
|
t.Errorf("Failed to encode data: %v", err)
|
|
return
|
|
}
|
|
|
|
decoded, err := encoder.DecodeBytes(encoded)
|
|
if err != nil {
|
|
t.Errorf("Failed to decode data: %v", err)
|
|
return
|
|
}
|
|
|
|
if !compareSlices(decoded, rawStream) {
|
|
t.Errorf("Slices not matching")
|
|
t.Errorf("Decoded (%d): % x", len(encoded), encoded)
|
|
t.Errorf("Raw (%d): % x", len(rawStream), rawStream)
|
|
return
|
|
}
|
|
}
|
|
|
|
// Test run length encoding.
|
|
func TestRunLengthEncoding(t *testing.T) {
|
|
rawStream := []byte("this is a dummy text with some \x01\x02\x03 binary data")
|
|
encoder := NewRunLengthEncoder()
|
|
encoded, err := encoder.EncodeBytes(rawStream)
|
|
if err != nil {
|
|
t.Errorf("Failed to RunLength encode data: %v", err)
|
|
return
|
|
}
|
|
decoded, err := encoder.DecodeBytes(encoded)
|
|
if err != nil {
|
|
t.Errorf("Failed to RunLength decode data: %v", err)
|
|
return
|
|
}
|
|
if !compareSlices(decoded, rawStream) {
|
|
t.Errorf("Slices not matching. RunLength")
|
|
t.Errorf("Decoded (%d): % x", len(encoded), encoded)
|
|
t.Errorf("Raw (%d): % x", len(rawStream), rawStream)
|
|
return
|
|
}
|
|
}
|
|
|
|
// Test ASCII hex encoding.
|
|
func TestASCIIHexEncoding(t *testing.T) {
|
|
byteData := []byte{0xDE, 0xAD, 0xBE, 0xEF}
|
|
expected := []byte("DE AD BE EF >")
|
|
|
|
encoder := NewASCIIHexEncoder()
|
|
encoded, err := encoder.EncodeBytes(byteData)
|
|
if err != nil {
|
|
t.Errorf("Failed to encode data: %v", err)
|
|
return
|
|
}
|
|
|
|
if !compareSlices(encoded, expected) {
|
|
t.Errorf("Slices not matching")
|
|
t.Errorf("Expected (%d): %s", len(expected), expected)
|
|
t.Errorf("Encoded (%d): %s", len(encoded), encoded)
|
|
return
|
|
}
|
|
}
|
|
|
|
// ASCII85.
|
|
func TestASCII85EncodingWikipediaExample(t *testing.T) {
|
|
expected := `Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.`
|
|
// Base 64 encoded, Ascii85 encoded version (wikipedia).
|
|
encodedInBase64 := `OWpxb15CbGJELUJsZUIxREorKitGKGYscS8wSmhLRjxHTD5DakAuNEdwJGQ3RiEsTDdAPDZAKS8wSkRFRjxHJTwrRVY6MkYhLE88REorKi5APCpLMEA8NkwoRGYtXDBFYzVlO0RmZlooRVplZS5CbC45cEYiQUdYQlBDc2krREdtPkAzQkIvRiomT0NBZnUyL0FLWWkoREliOkBGRCwqKStDXVU9QDNCTiNFY1lmOEFURDNzQHE/ZCRBZnRWcUNoW05xRjxHOjgrRVY6LitDZj4tRkQ1VzhBUmxvbERJYWwoRElkPGpAPD8zckA6RiVhK0Q1OCdBVEQ0JEJsQGwzRGU6LC1ESnNgOEFSb0ZiLzBKTUtAcUI0XkYhLFI8QUtaJi1EZlRxQkclRz51RC5SVHBBS1lvJytDVC81K0NlaSNESUk/KEUsOSlvRioyTTcvY34+`
|
|
encoded, _ := base64.StdEncoding.DecodeString(encodedInBase64)
|
|
|
|
encoder := NewASCII85Encoder()
|
|
enc1, err := encoder.EncodeBytes([]byte(expected))
|
|
if err != nil {
|
|
t.Errorf("Fail")
|
|
return
|
|
}
|
|
if string(enc1) != string(encoded) {
|
|
t.Errorf("ASCII85 encoding wiki example fail")
|
|
return
|
|
}
|
|
|
|
decoded, err := encoder.DecodeBytes([]byte(encoded))
|
|
if err != nil {
|
|
t.Errorf("Fail, error: %v", err)
|
|
return
|
|
}
|
|
if expected != string(decoded) {
|
|
t.Errorf("Mismatch! '%s' vs '%s'", decoded, expected)
|
|
return
|
|
}
|
|
}
|
|
|
|
func TestASCII85Encoding(t *testing.T) {
|
|
encoded := `FD,B0+EVmJAKYo'+D#G#De*R"B-:o0+E_a:A0>T(+AbuZ@;]Tu:ddbqAnc'mEr~>`
|
|
expected := "this type of encoding is used in PS and PDF files"
|
|
|
|
encoder := NewASCII85Encoder()
|
|
|
|
enc1, err := encoder.EncodeBytes([]byte(expected))
|
|
if err != nil {
|
|
t.Errorf("Fail")
|
|
return
|
|
}
|
|
if encoded != string(enc1) {
|
|
t.Errorf("Encoding error")
|
|
return
|
|
}
|
|
|
|
decoded, err := encoder.DecodeBytes([]byte(encoded))
|
|
if err != nil {
|
|
t.Errorf("Fail, error: %v", err)
|
|
return
|
|
}
|
|
if expected != string(decoded) {
|
|
t.Errorf("Mismatch! '%s' vs '%s'", decoded, expected)
|
|
return
|
|
}
|
|
}
|
|
|
|
type TestASCII85DecodingTestCase struct {
|
|
Encoded string
|
|
Expected string
|
|
}
|
|
|
|
func TestASCII85Decoding(t *testing.T) {
|
|
// Map encoded -> Decoded
|
|
testcases := []TestASCII85DecodingTestCase{
|
|
{"z~>", "\x00\x00\x00\x00"},
|
|
{"z ~>", "\x00\x00\x00\x00"},
|
|
{"zz~>", "\x00\x00\x00\x00\x00\x00\x00\x00"},
|
|
{" zz~>", "\x00\x00\x00\x00\x00\x00\x00\x00"},
|
|
{" z z~>", "\x00\x00\x00\x00\x00\x00\x00\x00"},
|
|
{" z z ~>", "\x00\x00\x00\x00\x00\x00\x00\x00"},
|
|
{"+T~>", `!`},
|
|
{"+`d~>", `!s`},
|
|
{"+`hr~>", `!sz`},
|
|
{"+`hsS~>", `!szx`},
|
|
{"+`hsS+T~>", `!szx!`},
|
|
{"+ `hs S +T ~>", `!szx!`},
|
|
}
|
|
|
|
encoder := NewASCII85Encoder()
|
|
|
|
for _, testcase := range testcases {
|
|
encoded := testcase.Encoded
|
|
expected := testcase.Expected
|
|
decoded, err := encoder.DecodeBytes([]byte(encoded))
|
|
if err != nil {
|
|
t.Errorf("Fail, error: %v", err)
|
|
return
|
|
}
|
|
if expected != string(decoded) {
|
|
t.Errorf("Mismatch! '%s' vs '%s'", decoded, expected)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test multi encoder with FlateDecode and ASCIIHexDecode.
|
|
func TestMultiEncoder(t *testing.T) {
|
|
rawStream := []byte("this is a dummy text with some \x01\x02\x03 binary data")
|
|
|
|
encoder := NewMultiEncoder()
|
|
|
|
enc1 := NewFlateEncoder()
|
|
enc1.Predictor = 1
|
|
encoder.AddEncoder(enc1)
|
|
|
|
enc2 := NewASCIIHexEncoder()
|
|
encoder.AddEncoder(enc2)
|
|
|
|
encoded, err := encoder.EncodeBytes(rawStream)
|
|
if err != nil {
|
|
t.Errorf("Failed to encode data: %v", err)
|
|
return
|
|
}
|
|
common.Log.Debug("Multi Encoded: %s", encoded)
|
|
// Multi Encoded: 78 9C 2A C9 C8 2C 56 00 A2 44 85 94 D2 DC DC 4A 85 92 D4 8A 12 85 F2
|
|
// CC 92 0C 85 E2 FC DC 54 05 46 26 66 85 A4CC BC C4 A2 4A 85 94 C4 92 44 40 00 00 00
|
|
// FF FF 78 87 0F 9C >
|
|
// Looks fine..
|
|
|
|
decoded, err := encoder.DecodeBytes(encoded)
|
|
if err != nil {
|
|
t.Errorf("Failed to decode data: %v", err)
|
|
return
|
|
}
|
|
|
|
if !compareSlices(decoded, rawStream) {
|
|
t.Errorf("Slices not matching")
|
|
t.Errorf("Decoded (%d): % x", len(encoded), encoded)
|
|
t.Errorf("Raw (%d): % x", len(rawStream), rawStream)
|
|
return
|
|
}
|
|
}
|