8000 Add basic image rendering support by adrg · Pull Request #266 · unidoc/unipdf · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Add basic image rendering support #266

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 47 commits into from
Mar 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
3b5d9f5
Add render package
adrg Oct 15, 2019
f78c089
Add text state
adrg Oct 16, 2019
dfef17b
Add more text operators
adrg Oct 16, 2019
9511128
Remove unnecessary files
adrg Oct 16, 2019
8028bbb
Add text font
adrg Oct 17, 2019
4bd9f1a
Add custom text render method
adrg Oct 17, 2019
b3e0818
Improve text rendering method
adrg Oct 17, 2019
8652b4e
Fix go vet warnings
adrg Oct 21, 2019
752191b
Rename text state methods
adrg Oct 22, 2019
311f42e
Refactor and document context interface
adrg Oct 22, 2019
65171ec
Refact text begin/end operators
adrg Oct 22, 2019
5e814b1
Fix graphics state transformations
adrg Oct 24, 2019
1e82098
Keep original font when doing font substitution
adrg Oct 28, 2019
065f013
Take page cropbox into account
adrg Oct 28, 2019
b146051
Revert to substitution font if original font measurement is 0
adrg Oct 28, 2019
9acbeb2
Add font substitution package
adrg Dec 1, 2019
b2dee24
Implement addition transform.Point methods
adrg Dec 9, 2019
cbe2cda
Use transform.Point in the image context package
adrg Dec 9, 2019
0bbf79d
Remove unneeded functionality from the render image package
adrg Dec 9, 2019
7657499
Fix golint notices in the image rendering package
adrg Dec 9, 2019
d599e6a
Fix go vet notices in the render package
adrg Dec 9, 2019
6f245c8
Fix golint notices in the top-level render package
adrg Dec 9, 2019
e6411ab
Improve render context package documentation
adrg Dec 9, 2019
8ce44ac
Document context text state struct.
adrg Dec 10, 2019
844b26e
Document context text font struct.
adrg Dec 10, 2019
71d4cd2
Minor logging improvements
adrg Dec 10, 2019
e1c0c20
Add license disclaimer to the render package files
adrg Dec 11, 2019
86bacf6
Avoid using package aliases where possible
adrg Dec 12, 2019
74e8edb
Change style of section comments
adrg Dec 12, 2019
4925f81
Adapt render package import style to follow the developer guide
adrg Dec 12, 2019
e700ef6
Improve documentation for the internal matrix implementation
adrg Dec 12, 2019
4ed7446
Update render package dependency versions
adrg Dec 12, 2019
1d29f5c
Merge pull request #207 from adrg/render
gunnsth Dec 12, 2019
8befbf3
Apply crop box post render
adrg Dec 16, 2019
f7323e7
Account for offseted media boxes
adrg Dec 17, 2019
ebd20e7
Merge pull request #214 from adrg/render
gunnsth Dec 18, 2019
856669e
Improve metrics of rendered characters
adrg Dec 22, 2019
792c0da
Merge pull request #217 from adrg/render
gunnsth Dec 23, 2019
e9c1b60
Fix text matrix translation
adrg Jan 23, 2020
4d1c365
Merge branch 'development' into render
adrg Feb 20, 2020
2b9d96c
Change priority of fonts used for measuring rendered characters
adrg Feb 20, 2020
768884a
Skip invalid m and l operators on image rendering
adrg Feb 20, 2020
6422bfc
Small fix for v operator
adrg Feb 25, 2020
d3662d7
Fix rendered characters spacing issues
adrg Feb 25, 2020
be232ae
Merge pull request #263 from adrg/render
gunnsth Feb 28, 2020
d5a2751
Refactor naming of internal render packages
adrg Mar 2, 2020
27275f6
Merge pull request #265 from adrg/render-pkg-naming
gunnsth Mar 2, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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=
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)
}
106 changes: 106 additions & 0 deletions render/image_device.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* This file is subject to the terms and conditions defined in
* file 'LICENSE.md', which is part of this source code package.
*/

package render

import (
"errors"
"fmt"
"image"
"image/draw"
"image/jpeg"
"image/png"
"os"
"path/filepath"
"strings"

"github.com/unidoc/unipdf/v3/model"
"github.com/unidoc/unipdf/v3/render/internal/context/imagerender"
)

// ImageDevice is used to render PDF pages to image targets.
type ImageDevice struct {
renderer
}

// NewImageDevice returns a new image device.
func NewImageDevice() *ImageDevice {
return &ImageDevice{}
}

// Render converts the specified PDF page into an image and returns the result.
func (d *ImageDevice) Render(page *model.PdfPage) (image.Image, error) {
// Get page dimensions.
mbox, err := page.GetMediaBox()
if err != nil {
return nil, err
}

// Render page.
width, height := mbox.Llx+mbox.Width(), mbox.Lly+mbox.Height()

ctx := imagerender.NewContext(int(width), int(height))
if err := d.renderPage(ctx, page); err != nil {
return nil, err
}

// Apply crop box, if one exists.
img := ctx.Image()
if box := page.CropBox; box != nil {
// Calculate crop bounds and crop start position.
cropBounds := image.Rect(0, 0, int(box.Width()), int(box.Height()))
cropStart := image.Pt(int(box.Llx), int(height-box.Ury))

// Crop image.
cropImg := image.NewRGBA(cropBounds)
draw.Draw(cropImg, cropBounds, img, cropStart, draw.Src)
img = cropImg
}

return img, nil
}

// RenderToPath converts the specified PDF page into an image and saves the
// result at the specified location.
func (d *ImageDevice) RenderToPath(page *model.PdfPage, outputPath string) error {
image, err := d.Render(page)
if err != nil {
return err
}

extension := strings.ToLower(filepath.Ext(outputPath))
if extension == "" {
return errors.New("could not recognize output file type")
}

switch extension {
case ".png":
return savePNG(outputPath, image)
case ".jpg", ".jpeg":
return saveJPG(outputPath, image, 100)
}

return fmt.Errorf("unrecognized output file type: %s", extension)
}

func savePNG(path string, image image.Image) error {
file, err := os.Create(path)
if err != nil {
return err
}
defer file.Close()

return png.Encode(file, image)
}

func saveJPG(path string, image image.Image, quality int) error {
file, err := os.Create(path)
if err != nil {
return err
}
defer file.Close()

return jpeg.Encode(file, image, &jpeg.Options{Quality: quality})
}
47 changes: 47 additions & 0 deletions render/internal/context/const.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* This file is subject to the terms and conditions defined in
* file 'LICENSE.md', which is part of this source code package.
*/

package context

import "image/color"

// FillRule represents the fill style used by a context instance.
type FillRule int

// Fill rules.
const (
FillRuleWinding FillRule = iota
FillRuleEvenOdd
)

// LineCap represents the line cap style used by a context instance.
type LineCap int

// Line cap styles.
const (
LineCapRound LineCap = iota
LineCapButt
LineCapSquare
)

// LineJoin represents the line join style used by a context instance.
type LineJoin int

// Line join styles.
const (
LineJoinRound LineJoin = iota
LineJoinBevel
)

// Pattern represents a pattern which can be rendered by a context instance.
type Pattern interface {
ColorAt(x, y int) color.Color
}

// Gradient represents a gradient pattern which can be rendered by a context instance.
type Gradient interface {
Pattern
AddColorStop(offset float64, color color.Color)
}
Loading
0