Skip to content

Commit

Permalink
Merge pull request #274 from gunnsth/release/v3.5.0
Browse files Browse the repository at this point in the history
Prepare unipdf release v3.5.0
  • Loading branch information
gunnsth authored Mar 9, 2020
2 parents ea9988f + 4bdda0a commit d1c808d
Show file tree
Hide file tree
Showing 26 changed files with 3,498 additions and 104 deletions.
6 changes: 5 additions & 1 deletion common/license/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,11 @@ func (k *LicenseKey) Validate() error {
}

if k.ExpiresAt == nil {
k.ExpiresAt = &noLicenseExpiry
expiresAt := k.CreatedAt.AddDate(1, 0, 0)
if noLicenseExpiry.After(expiresAt) {
expiresAt = noLicenseExpiry
}
k.ExpiresAt = &expiresAt
}

if k.CreatedAt.After(*k.ExpiresAt) {
Expand Down
10 changes: 5 additions & 5 deletions common/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import (
)

const releaseYear = 2020
const releaseMonth = 2
const releaseDay = 10
const releaseHour = 8
const releaseMin = 50
const releaseMonth = 3
const releaseDay = 8
const releaseHour = 23
const releaseMin = 40

// Version holds version information, when bumping this make sure to bump the released at stamp also.
const Version = "3.4.0"
const Version = "3.5.0"

var ReleasedAt = time.Date(releaseYear, releaseMonth, releaseDay, releaseHour, releaseMin, 0, 0, time.UTC)
6 changes: 2 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
module github.com/unidoc/unipdf/v3

require (
github.com/adrg/sysfont v0.1.0
github.com/boombuler/barcode v1.0.0
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/gunnsth/pkcs7 v0.0.0-20181213175627-3cffc6fbfe83
github.com/stretchr/testify v1.3.0
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5
golang.org/x/image v0.0.0-20181116024801-cd38e8056d9b
golang.org/x/lint v0.0.0-20190409202823-959b441ac422 // indirect
golang.org/x/net v0.0.0-20190606173856-1492cefac77f // indirect
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444 // indirect
golang.org/x/text v0.3.2
golang.org/x/tools v0.0.0-20190606174628-0139d5756a7d // indirect
)
18 changes: 7 additions & 11 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
github.com/adrg/strutil v0.1.0 h1:IOQnSOAjbE17+7l1lw4rXgX6JuSeJGdZa7BucTMV3Qg=
github.com/adrg/strutil v0.1.0/go.mod h1:pXRr2+IyX5AEPAF5icj/EeTaiflPSD2hvGjnguilZgE=
github.com/adrg/sysfont v0.1.0 h1:vOk13USVkciGOJj9sPT9Gl9zfHUT2HZgsBnwS1Je4Q8=
github.com/adrg/sysfont v0.1.0/go.mod h1:DzISco90USPZJ+lmtpuz1SOTn1fih6YyB0KG2TEP/0U=
github.com/adrg/xdg v0.2.1 h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic=
github.com/adrg/xdg v0.2.1/go.mod h1:ZuOshBmzV4Ta+s23hdfFZnBsdzmoR3US0d7ErpqSbTQ=
github.com/boombuler/barcode v1.0.0 h1:s1TvRnXwL2xJRaccrdcBQMZxq6X7DvsMogtmJeHDdrc=
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/gunnsth/pkcs7 v0.0.0-20181213175627-3cffc6fbfe83 h1:saj5dTV7eQ1wFg/gVZr1SfbkOmg8CYO9R8frHgQiyR4=
github.com/gunnsth/pkcs7 v0.0.0-20181213175627-3cffc6fbfe83/go.mod h1:xaGEIRenAiJcGgd9p62zbiP4993KaV3PdjczwGnP50I=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand All @@ -10,26 +17,15 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/image v0.0.0-20181116024801-cd38e8056d9b h1:VHyIDlv3XkfCa5/a81uzaoDkHH4rr81Z62g+xlnO8uM=
golang.org/x/image v0.0.0-20181116024801-cd38e8056d9b/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422 h1:QzoH/1pFpZguR8NrRHLcO6jKqfv2zpuSqZLgdm7ZmjI=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190606173856-1492cefac77f/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190606174628-0139d5756a7d h1:CoaGYJ9a8IXms8Q/NUeypLWbStIszTH0IIwqBUkEB9g=
golang.org/x/tools v0.0.0-20190606174628-0139d5756a7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
5 changes: 5 additions & 0 deletions internal/cmap/cmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,11 @@ func (cmap *CMap) Type() int {
return cmap.ctype
}

// Nbits returns 8 bits for simple font CMaps and 16 bits for CID font CMaps.
func (cmap *CMap) NBits() int {
return cmap.nbits
}

// String returns a human readable description of `cmap`.
func (cmap *CMap) String() string {
si := cmap.systemInfo
Expand Down
9 changes: 6 additions & 3 deletions internal/e2etest/extract_images_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import (
"testing"

"github.com/stretchr/testify/require"
"github.com/stretchr/testify/assert"

"github.com/unidoc/unipdf/v3/common"
"github.com/unidoc/unipdf/v3/extractor"
"github.com/unidoc/unipdf/v3/model"
Expand All @@ -38,20 +40,21 @@ var knownExtrImgsHashes = map[string]string{
"7eee345c983461d44ae939b3f800a97642817c8d.pdf": "5a4cd00537f8a1f39fff2d4c6dd5cc53",
"52ab322c1697aca9bad37288f7c502e37fa657af.pdf": "2bddee02dff89a38c08322c9d2c779a6",
"0edf09fd438db2f18c1bb08fccc1f81a7b280bf2.pdf": "583f755b3fb1bd5697036616442687ab",
"cafe55316a45435c3817f4c1b6a19c9cd52db825.pdf": "b199badff0abb0311a2cbe35c7fce580",
"cafe55316a45435c3817f4c1b6a19c9cd52db825.pdf": "8b8c2eaa9b3a64eb5941348280c9c2a0",
"6773e6aa5d8a2d26362cf3fca2874b3a81025bae.pdf": "f052e3e333839508a8bdd8d1a3ba1973",
"d11a3ca55664828b69d7c39d83d5c0a63fcea89d.pdf": "29287cd44f009dce5aa9c2a0dc9a3c83",
"483933bf73cc4fcc264eb69214ff763ccf299e49.pdf": "627dcf88805786d03b2e76d367b42642",
"da1c5c4c4fe36f676dbca6ea01673c9fdf77c7a9.pdf": "7ffe076d1c7c88e07dfba7545b622ec6",
"f856baf7ffcd96003b6bda800171cb0e5680f78e.pdf": "a9505d8c22f1fd063fbe0b05aa33a5fc",
"201c20676fe8da14a8130852c91ed58b48cba8fb.pdf": "ffcb78d126c04be9ca2497bb43b6e964",
"f0152456494aa09e5cf82c4afe9ecd2fdc2e8d72.pdf": "d0e68157aaa7f9f4406807512db3e676",
"d95643acea1ec3f6215bda35e4cd89dbd8898c44.pdf": "1739aed3e1cbfa5e98f8d7fef17a614b",
"d95643acea1ec3f6215bda35e4cd89dbd8898c44.pdf": "b4c7c2ae9671af69f71e9965e9cf67e8",
"110d793aeaa7accbe40b5ab9db249d5a103d3b50.pdf": "a57e347edddfd3f6032b85553b3537cd",
"d15a0aa289524619a971188372dd05fb712f1b2c.pdf": "380907273bb8ea64943d986491d827ec",
"932e0dfa52c20ffe83b8178fb98296a0dab177d1.pdf": "b44d8b073f99ac3db28d7951e3c7d970",
"60a8c28da5c23081834adac4170755904d8c4166.pdf": "9167f381d5eed7a2e5fd10eca567c519",
"e51296be2615b9389482c9c16505286619b6cf36.pdf": "ec6e1f6297dd1cbda6ccba39e0c7d3d2",
"d3dd65300785dcf2114663397e475376bac88e75.pdf": "ea848ed888176368e697f577934f0452", // From unipdf#258.
}

func TestExtractImages(t *testing.T) {
Expand Down Expand Up @@ -93,7 +96,7 @@ func TestExtractImages(t *testing.T) {

knownHash, has := knownExtrImgsHashes[file.Name()]
if has {
require.Equal(t, knownHash, hash)
assert.Equal(t, knownHash, hash)
matchcount++
} else {
t.Logf("%s - hash: %s not in the list of known hashes", file.Name(), hash)
Expand Down
7 changes: 7 additions & 0 deletions internal/textencoding/cmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ func NewCMapEncoder(baseName string, codeToCID, cidToUnicode *cmap.CMap) CMapEnc

// Encode converts the Go unicode string to a PDF encoded string.
func (enc CMapEncoder) Encode(str string) []byte {
if enc.cidToUnicode == nil {
return []byte{}
}

if enc.cidToUnicode.NBits() == 8 {
return encodeString8bit(enc, str)
}
return encodeString16bit(enc, str)
}

Expand Down
47 changes: 41 additions & 6 deletions internal/transform/matrix.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,28 @@ func IdentityMatrix() Matrix {
return NewMatrix(1, 0, 0, 1, 0, 0)
}

// TranslationMatrix returns a matrix that translates by `tx`, `ty`.
// TranslationMatrix returns a matrix that translates by `tx`,`ty`.
func TranslationMatrix(tx, ty float64) Matrix {
return NewMatrix(1, 0, 0, 1, tx, ty)
}

// ScaleMatrix returns a matrix that scales by `x`,`y`.
func ScaleMatrix(x, y float64) Matrix {
return NewMatrix(x, 0, 0, y, 0, 0)
}

// RotationMatrix returns a matrix that rotates by angle `angle`, specified in radians.
func RotationMatrix(angle float64) Matrix {
c := math.Cos(angle)
s := math.Sin(angle)
return NewMatrix(c, s, -s, c, 0, 0)
}

// ShearMatrix returns a matrix that shears `x`,`y`.
func ShearMatrix(x, y float64) Matrix {
return NewMatrix(1, y, x, 1, 0, 0)
}

// NewMatrix returns an affine transform matrix laid out in homogenous coordinates as
// a b 0
// c d 0
Expand Down Expand Up @@ -74,19 +91,37 @@ func (m Matrix) Mult(b Matrix) Matrix {
return m
}

// Translate appends a translation of `dx`,`dy` to `m`.
// Translate appends a translation of `x`,`y` to `m`.
// m.Translate(dx, dy) is equivalent to m.Concat(NewMatrix(1, 0, 0, 1, dx, dy))
func (m *Matrix) Translate(dx, dy float64) {
m[6] += dx
m[7] += dy
m.clampRange()
func (m *Matrix) Translate(x, y float64) {
m.Concat(TranslationMatrix(x, y))
}

// Translation returns the translation part of `m`.
func (m *Matrix) Translation() (float64, float64) {
return m[6], m[7]
}

// Scale scales the current matrix by `x`,`y`.
func (m *Matrix) Scale(x, y float64) {
m.Concat(ScaleMatrix(x, y))
}

// Rotate rotates the current matrix by angle `angle`, specified in radians.
func (m *Matrix) Rotate(angle float64) {
m.Concat(RotationMatrix(angle))
}

// Shear shears the current matrix by `x',`y`.
func (m *Matrix) Shear(x, y float64) {
m.Concat(ShearMatrix(x, y))
}

// Clone returns a copy of the current matrix.
func (m *Matrix) Clone() Matrix {
return NewMatrix(m[0], m[1], m[3], m[4], m[6], m[7])
}

// Transform returns coordinates `x`,`y` transformed by `m`.
func (m *Matrix) Transform(x, y float64) (float64, float64) {
xp := x*m[0] + y*m[1] + m[6]
Expand Down
19 changes: 16 additions & 3 deletions internal/transform/point.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,25 @@ func (p Point) Rotate(theta float64) Point {
return Point{r * cos, r * sin}
}

// transformByMatrix mutates and transforms `p` by the affine transformation `m`.
func (p *Point) transformByMatrix(m Matrix) {
p.X, p.Y = m.Transform(p.X, p.Y)
// Distance returns the distance between `a` and `b`.
func (a Point) Distance(b Point) float64 {
return math.Hypot(a.X-b.X, a.Y-b.Y)
}

// Interpolate does linear interpolation between point `a` and `b` for value `t`.
func (a Point) Interpolate(b Point, t float64) Point {
return Point{
X: (1-t)*a.X + t*b.X,
Y: (1-t)*a.Y + t*b.Y,
}
}

// String returns a string describing `p`.
func (p Point) String() string {
return fmt.Sprintf("(%.2f,%.2f)", p.X, p.Y)
}

// transformByMatrix mutates and transforms `p` by the affine transformation `m`.
func (p *Point) transformByMatrix(m Matrix) {
p.X, p.Y = m.Transform(p.X, p.Y)
}
27 changes: 16 additions & 11 deletions model/colorspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -2217,8 +2217,9 @@ func (cs *PdfColorspaceSpecialPattern) ImageToRGB(img Image) (Image, error) {
return img, errors.New("invalid colorspace for image (pattern)")
}

// PdfColorspaceSpecialIndexed is an indexed color space is a lookup table, where the input element is an index to the
// lookup table and the output is a color defined in the lookup table in the Base colorspace.
// PdfColorspaceSpecialIndexed is an indexed color space is a lookup table, where the input element
// is an index to the lookup table and the output is a color defined in the lookup table in the Base
// colorspace.
// [/Indexed base hival lookup]
type PdfColorspaceSpecialIndexed struct {
Base PdfColorspace
Expand All @@ -2232,9 +2233,7 @@ type PdfColorspaceSpecialIndexed struct {

// NewPdfColorspaceSpecialIndexed returns a new Indexed color.
func NewPdfColorspaceSpecialIndexed() *PdfColorspaceSpecialIndexed {
cs := &PdfColorspaceSpecialIndexed{}
cs.HiVal = 255
return cs
return &PdfColorspaceSpecialIndexed{HiVal: 255}
}

func (cs *PdfColorspaceSpecialIndexed) String() string {
Expand Down Expand Up @@ -2401,32 +2400,38 @@ func (cs *PdfColorspaceSpecialIndexed) ImageToRGB(img Image) (Image, error) {
baseImage.Height = img.Height
baseImage.Width = img.Width
baseImage.alphaData = img.alphaData
baseImage.BitsPerComponent = img.BitsPerComponent
// TODO(peterwilliams97): Add support for other BitsPerComponent values.
// See https://github.com/unidoc/unipdf/issues/260
baseImage.BitsPerComponent = 8
baseImage.hasAlpha = img.hasAlpha
baseImage.ColorComponents = img.ColorComponents
baseImage.ColorComponents = cs.Base.GetNumComponents()

samples := img.GetSamples()
N := cs.Base.GetNumComponents()

if N < 1 {
return Image{}, fmt.Errorf("bad base colorspace NumComponents=%d", N)
}

var baseSamples []uint32
// Convert the indexed data to base color map data.
for i := 0; i < len(samples); i++ {
// Each data point represents an index location.
// For each entry there are N values.
index := int(samples[i]) * N
index := int(samples[i])
common.Log.Trace("Indexed: index=%d N=%d lut=%d", index, N, len(cs.colorLookup))
// Ensure does not go out of bounds.
if index+N-1 >= len(cs.colorLookup) {
if (index+1)*N > len(cs.colorLookup) {
// Clip to the end value.
index = len(cs.colorLookup) - N - 1
index = len(cs.colorLookup)/N - 1
common.Log.Trace("Clipping to index: %d", index)
if index < 0 {
common.Log.Debug("ERROR: Can't clip index. Is PDF file damaged?")
break
}
}

cvals := cs.colorLookup[index : index+N]
cvals := cs.colorLookup[index*N : (index+1)*N]
common.Log.Trace("C Vals: % d", cvals)
for _, val := range cvals {
baseSamples = append(baseSamples, uint32(val))
Expand Down
53 changes: 53 additions & 0 deletions model/font.go
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,59 @@ func (font *PdfFont) CharcodesToUnicode(charcodes []textencoding.CharCode) []run
return strlist
}

// RunesToCharcodeBytes maps the provided runes to charcode bytes and it
// returns the resulting slice of bytes, along with the number of runes which
// could not be converted. If the number of misses is 0, all runes were
// successfully converted.
func (font *PdfFont) RunesToCharcodeBytes(data []rune) ([]byte, int) {
// Create collection of encoders used for rune to charcode mapping:
// - if the font has a to Unicode CMap, use it first.
// - if the font has an encoder, use it as a fallback.
var encoders []textencoding.TextEncoder
if toUnicode := font.baseFields().toUnicodeCmap; toUnicode != nil {
encoders = append(encoders, textencoding.NewCMapEncoder("", nil, toUnicode))
}
if encoder := font.Encoder(); encoder != nil {
encoders = append(encoders, encoder)
}

var buffer bytes.Buffer
var numMisses int
for _, r := range data {
// Attempt to encode the current rune using each of the encoders,
// falling back to the next one in case of failure.
var encoded bool
for _, encoder := range encoders {
if encBytes := encoder.Encode(string(r)); len(encBytes) > 0 {
buffer.Write(encBytes)
encoded = true
break
}
}

if !encoded {
common.Log.Debug("ERROR: failed to map rune `%+q` to charcode", r)
numMisses++
}
}

if numMisses != 0 {
common.Log.Debug("ERROR: could not convert all runes to charcodes.\n"+
"\tnumRunes=%d numMisses=%d\n"+
"\tfont=%s encoders=%+v", len(data), numMisses, font, encoders)
}

return buffer.Bytes(), numMisses
}

// StringToCharcodeBytes maps the provided string runes to charcode bytes and
// it returns the resulting slice of bytes, along with the number of runes
// which could not be converted. If the number of misses is 0, all string runes
// were successfully converted.
func (font *PdfFont) StringToCharcodeBytes(str string) ([]byte, int) {
return font.RunesToCharcodeBytes([]rune(str))
}

// ToPdfObject converts the PdfFont object to its PDF representation.
func (font *PdfFont) ToPdfObject() core.PdfObject {
if font.context == nil {
Expand Down
Loading

0 comments on commit d1c808d

Please sign in to comment.