unipdf/model/optimize/optimize_test.go
2019-05-16 20:44:51 +00:00

213 lines
4.8 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 optimize_test
import (
"bytes"
"fmt"
"io"
"testing"
"github.com/unidoc/unipdf/v3/core"
"github.com/unidoc/unipdf/v3/model/optimize"
)
// parseIndirectObjects parses a sequence of indirect/stream objects sequentially from a `rawpdf` text.
func parseIndirectObjects(rawpdf string) ([]core.PdfObject, error) {
p := core.NewParserFromString(rawpdf)
var indirects []core.PdfObject
for {
obj, err := p.ParseIndirectObject()
if err != nil {
if err == io.EOF {
break
}
return nil, err
}
indirects = append(indirects, obj)
}
return indirects, nil
}
// debugObjects prints objects in a readable fashion, convenient when debugging.
func debugObjects(objects []core.PdfObject) string {
var buf bytes.Buffer
for _, obj := range objects {
switch t := obj.(type) {
case *core.PdfIndirectObject:
buf.WriteString(fmt.Sprintf("%d 0 obj\n", t.ObjectNumber))
buf.WriteString(fmt.Sprintf(" %s\n", t.PdfObject.String()))
}
}
return buf.String()
}
func TestOptimizeIdenticalIndirects1(t *testing.T) {
rawpdf := `
1 0 obj
<<
/Name (1234)
>>
endobj
2 0 obj
<< /Name (1234) >>
endobj
`
objects, err := parseIndirectObjects(rawpdf)
if err != nil {
t.Fatalf("Error: %v", err)
}
if len(objects) != 2 {
t.Fatalf("len(objects) != 2 (%d)", len(objects))
}
// Combine duplicate direct objects - Expect unchanged results.
{
opt := optimize.CombineDuplicateDirectObjects{}
optObjects, err := opt.Optimize(objects)
if err != nil {
t.Fatalf("Error: %v", err)
}
if len(optObjects) != 2 {
t.Fatalf("len(optObjects1) != 2 (%d)", len(optObjects))
}
}
// Combine indirect objects should go from 2 to 1.
{
opt := optimize.CombineIdenticalIndirectObjects{}
optObjects, err := opt.Optimize(objects)
if err != nil {
t.Fatalf("Error: %v", err)
}
if len(optObjects) != 1 {
t.Fatalf("len(optObjects1) != 1 (%d)", len(optObjects))
}
}
}
// More complex case, where has a reference, where as the other does not.
// Expecting this NOT to work as we don't currently support this case.
// TODO: Add support for this.
func TestOptimizeIdenticalIndirectsUnsupported1(t *testing.T) {
rawpdf := `
1 0 obj
(1234)
endobj
2 0 obj
<<
/Name (1234)
>>
endobj
3 0 obj
<< /Name 1 0 R >>
endobj
`
objects, err := parseIndirectObjects(rawpdf)
if err != nil {
t.Fatalf("Error: %v", err)
}
if len(objects) != 3 {
t.Fatalf("len(objects) != 2 (%d)", len(objects))
}
// Combine duplicate direct objects - Expect unchanged results.
{
opt := optimize.CombineDuplicateDirectObjects{}
optObjects, err := opt.Optimize(objects)
if err != nil {
t.Fatalf("Error: %v", err)
}
if len(optObjects) != 3 {
t.Fatalf("len(optObjects1) != 2 (%d)", len(optObjects))
}
}
// Combine indirect objects should go from 3 to 2.
{
opt := optimize.CombineIdenticalIndirectObjects{}
optObjects, err := opt.Optimize(objects)
if err != nil {
t.Fatalf("Error: %v", err)
}
if len(optObjects) != 3 { // TODO: Add support. IF IDEAL: would be 2.
t.Fatalf("len(optObjects1) != 2 (%d)", len(optObjects))
}
}
}
// Showcases problem with sequence of CombineDuplicateDirectObjects followed by CombineIdenticalIndirectObjects
// if object numbers are not updated between steps (due to non-unique object numbering and reference strings).
func TestOptimizationSequence1(t *testing.T) {
rawpdf := `
1 0 obj
<<
/Inner << /Color (red) >>
>>
endobj
2 0 obj
<<
/Inner << /Color (red) >>
/Other (abc)
>>
endobj
3 0 obj
<<
/Inner << /Color (blue) >>
/Other (abc)
>>
endobj
4 0 obj
<<
/Inner << /Color (blue) >>
>>
endobj
`
objects, err := parseIndirectObjects(rawpdf)
if err != nil {
t.Fatalf("Error: %v", err)
}
if len(objects) != 4 {
t.Fatalf("len(objects) != 4 (%d)", len(objects))
}
debugstr1 := debugObjects(objects)
// 1. Combine duplicate direct objects.
// Expect that 2 new indirect objects will be added, as two of the inner dictionaries are identical.
opt := optimize.CombineDuplicateDirectObjects{}
optObjects, err := opt.Optimize(objects)
if err != nil {
t.Fatalf("Error: %v", err)
}
if len(optObjects) != 6 {
t.Fatalf("len(optObjects) != 6 (%d)", len(optObjects))
}
debugstr2 := debugObjects(optObjects)
// 2. Combine indirect objects.
// Should not make any difference here unless there was a problem.
opt2 := optimize.CombineIdenticalIndirectObjects{}
optObjects, err = opt2.Optimize(optObjects)
if err != nil {
t.Fatalf("Error: %v", err)
}
debugstr3 := debugObjects(optObjects)
fmt.Println("==Original")
fmt.Println(debugstr1)
fmt.Println("==After CombineDuplicateDirectObjects")
fmt.Println(debugstr2)
fmt.Println("==After CombineIdenticalIndirectObjects")
fmt.Println(debugstr3)
if len(optObjects) != 6 {
t.Fatalf("len(optObjects) != 6 (%d)", len(optObjects))
}
}