From 5b0eaf3f3adeb1ae38f049fb795d82015aa423b3 Mon Sep 17 00:00:00 2001 From: Denys Smirnov Date: Thu, 29 Nov 2018 02:56:26 +0200 Subject: [PATCH] creator: make output stable when using custom fonts; fixes #232 --- pdf/creator/creator_test.go | 35 +++++++++++++++++++++++++++++++++++ pdf/model/font_composite.go | 5 +++++ 2 files changed, 40 insertions(+) diff --git a/pdf/creator/creator_test.go b/pdf/creator/creator_test.go index 418d1371..0bd42e6f 100644 --- a/pdf/creator/creator_test.go +++ b/pdf/creator/creator_test.go @@ -11,6 +11,8 @@ package creator import ( "bytes" + "crypto/md5" + "encoding/hex" "fmt" goimage "image" "io/ioutil" @@ -2932,3 +2934,36 @@ func TestAllOptimizations(t *testing.T) { t.Errorf("Optimization failed: size not changed %d vs %d", fileInfo.Size(), fileInfoOptimized.Size()) } } + +// Tests that creator's output is predictable and returns exactly the same file given the same input. +func TestCreatorStable(t *testing.T) { + writePDF := func() string { + creator := New() + + font, err := model.NewCompositePdfFontFromTTFFile(testWts11TTFFile) + if err != nil { + t.Fatalf("Fail: %v\n", err) + } + + p := creator.NewParagraph("你好") + p.SetFont(font) + + err = creator.Draw(p) + if err != nil { + t.Fatalf("Fail: %v\n", err) + } + + h := md5.New() + err = creator.Write(h) + if err != nil { + t.Fatalf("Fail: %v\n", err) + } + return hex.EncodeToString(h.Sum(nil)) + } + + h1 := writePDF() + h2 := writePDF() + if h1 != h2 { + t.Fatal("output is not stable") + } +} diff --git a/pdf/model/font_composite.go b/pdf/model/font_composite.go index 968ebc30..4f132c80 100644 --- a/pdf/model/font_composite.go +++ b/pdf/model/font_composite.go @@ -8,6 +8,7 @@ package model import ( "errors" "io/ioutil" + "sort" "github.com/unidoc/unidoc/common" "github.com/unidoc/unidoc/pdf/core" @@ -412,6 +413,10 @@ func NewCompositePdfFontFromTTFFile(filePath string) (*PdfFont, error) { for r := range ttf.Chars { runes = append(runes, r) } + // make sure runes are sorted so PDF output is stable + sort.Slice(runes, func(i, j int) bool { + return runes[i] < runes[j] + }) k := 1000.0 / float64(ttf.UnitsPerEm)