mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Enterprise: add dependencies for upcoming features (#18793)
* Enterprise: add dependencies for upcoming features See enterprise issues
This commit is contained in:
parent
d6fb48c0ff
commit
1a4be4af8c
4
go.mod
4
go.mod
@ -40,7 +40,9 @@ require (
|
||||
github.com/hashicorp/go-plugin v0.0.0-20190220160451-3f118e8ee104
|
||||
github.com/hashicorp/go-version v1.1.0
|
||||
github.com/inconshreveable/log15 v0.0.0-20180818164646-67afb5ed74ec
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af
|
||||
github.com/jonboulle/clockwork v0.1.0 // indirect
|
||||
github.com/jung-kurt/gofpdf v1.10.1
|
||||
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // indirect
|
||||
github.com/klauspost/compress v1.4.1 // indirect
|
||||
github.com/klauspost/cpuid v1.2.0 // indirect
|
||||
@ -52,12 +54,12 @@ require (
|
||||
github.com/onsi/gomega v1.5.0 // indirect
|
||||
github.com/opentracing/opentracing-go v1.1.0
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/prometheus/client_golang v0.9.2
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90
|
||||
github.com/prometheus/common v0.2.0
|
||||
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be // indirect
|
||||
github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967
|
||||
github.com/robfig/cron/v3 v3.0.0
|
||||
github.com/russellhaering/goxmldsig v0.0.0-20180430223755-7acd5e4a6ef7 // indirect
|
||||
github.com/sergi/go-diff v1.0.0 // indirect
|
||||
github.com/smartystreets/assertions v0.0.0-20190401211740-f487f9de1cd3 // indirect
|
||||
|
9
go.sum
9
go.sum
@ -16,6 +16,7 @@ github.com/benbjohnson/clock v0.0.0-20161215174838-7dc76406b6d3 h1:wOysYcIdqv3Wn
|
||||
github.com/benbjohnson/clock v0.0.0-20161215174838-7dc76406b6d3/go.mod h1:UMqtWQTnOe4byzwe7Zhwh8f8s+36uszN51sJrSIZlTE=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737 h1:rRISKWyXfVxvoa702s91Zl5oREZTrR3yv+tXrrX7G/g=
|
||||
github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
|
||||
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
|
||||
@ -117,6 +118,9 @@ github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVY
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
|
||||
github.com/jung-kurt/gofpdf v1.10.1 h1:mbprTswkr0n86clAmJ4NGCFC4fdGySCAshWzrYb3xbE=
|
||||
github.com/jung-kurt/gofpdf v1.10.1/go.mod h1:s/VXv+TdctEOx2wCEguezYaR7f0OwUAd6H9VGfRkcSs=
|
||||
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM=
|
||||
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
|
||||
github.com/klauspost/compress v1.4.1 h1:8VMb5+0wMgdBykOV96DwNwKFQ+WTI4pzYURP99CcB9E=
|
||||
@ -158,6 +162,7 @@ github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsq
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/phpdave11/gofpdi v1.0.7/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@ -179,8 +184,11 @@ github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be h1:ta7tUOvsPHV
|
||||
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be/go.mod h1:MIDFMn7db1kT65GmV94GzpX9Qdi7N/pQlwb+AN8wh+Q=
|
||||
github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967 h1:x7xEyJDP7Hv3LVgvWhzioQqbC/KtuUhTigKlH/8ehhE=
|
||||
github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
|
||||
github.com/robfig/cron/v3 v3.0.0 h1:kQ6Cb7aHOHTSzNVNEhmp8EcWKLb4CbiMW9h9VyIhO4E=
|
||||
github.com/robfig/cron/v3 v3.0.0/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/russellhaering/goxmldsig v0.0.0-20180430223755-7acd5e4a6ef7 h1:J4AOUcOh/t1XbQcJfkEqhzgvMJ2tDxdCVvmHxW5QXao=
|
||||
github.com/russellhaering/goxmldsig v0.0.0-20180430223755-7acd5e4a6ef7/go.mod h1:Oz4y6ImuOQZxynhbSXk7btjEfNBtGlj2dcaOvXl2FSM=
|
||||
github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
|
||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||
@ -227,6 +235,7 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90Pveol
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a h1:Igim7XhdOpBnWPuYJ70XcNpq8q3BCACtVgNfoJxOV7g=
|
||||
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/image v0.0.0-20190507092727-e4e5bf290fec/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
@ -1,9 +1,14 @@
|
||||
package extensions
|
||||
|
||||
import (
|
||||
// Upgrade ldapsync from cron to cron.v3 and
|
||||
// remove the cron (v1) dependency
|
||||
|
||||
_ "github.com/crewjam/saml"
|
||||
_ "github.com/gobwas/glob"
|
||||
_ "github.com/jung-kurt/gofpdf"
|
||||
_ "github.com/robfig/cron"
|
||||
_ "github.com/robfig/cron/v3"
|
||||
_ "github.com/stretchr/testify/require"
|
||||
_ "gopkg.in/square/go-jose.v2"
|
||||
)
|
||||
|
1
vendor/github.com/jung-kurt/gofpdf/.gitattribute
generated
vendored
Normal file
1
vendor/github.com/jung-kurt/gofpdf/.gitattribute
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.pdf binary
|
25
vendor/github.com/jung-kurt/gofpdf/.gitignore
generated
vendored
Normal file
25
vendor/github.com/jung-kurt/gofpdf/.gitignore
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
*.0
|
||||
coverage
|
||||
font/CalligrapherRegular.json
|
||||
font/CalligrapherRegular.z
|
||||
font/Ubuntu-*
|
||||
internal/files/bin/bin
|
||||
look
|
||||
makefont/makefont
|
||||
open
|
||||
**/*.out
|
||||
pdf/*.pdf
|
||||
pdf.txt
|
||||
private
|
||||
*.sublime*
|
||||
*.swp
|
||||
**/*.test
|
||||
.idea/
|
||||
doc/body.html
|
||||
doc/body.md
|
||||
doc/index.html
|
||||
doc/index.html.ok
|
||||
coverage.html
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
21
vendor/github.com/jung-kurt/gofpdf/LICENSE
generated
vendored
Normal file
21
vendor/github.com/jung-kurt/gofpdf/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Kurt Jung and contributors acknowledged in the documentation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
29
vendor/github.com/jung-kurt/gofpdf/Makefile
generated
vendored
Normal file
29
vendor/github.com/jung-kurt/gofpdf/Makefile
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
all : documentation
|
||||
|
||||
documentation : doc/index.html doc.go README.md
|
||||
|
||||
cov : all
|
||||
go test -v -coverprofile=coverage && go tool cover -html=coverage -o=coverage.html
|
||||
|
||||
check :
|
||||
golint .
|
||||
go vet -all .
|
||||
gofmt -s -l .
|
||||
goreportcard-cli -v | grep -v cyclomatic
|
||||
|
||||
README.md : doc/document.md
|
||||
pandoc --read=markdown --write=gfm < $< > $@
|
||||
|
||||
doc/index.html : doc/document.md doc/html.txt
|
||||
pandoc --read=markdown --write=html --template=doc/html.txt \
|
||||
--metadata pagetitle="GoFPDF Document Generator" < $< > $@
|
||||
|
||||
doc.go : doc/document.md doc/go.awk
|
||||
pandoc --read=markdown --write=plain $< | awk --assign=package_name=gofpdf --file=doc/go.awk > $@
|
||||
gofmt -s -w $@
|
||||
|
||||
build :
|
||||
go build -v
|
||||
|
||||
clean :
|
||||
rm -f coverage.html coverage doc/index.html doc.go README.md
|
256
vendor/github.com/jung-kurt/gofpdf/README.md
generated
vendored
Normal file
256
vendor/github.com/jung-kurt/gofpdf/README.md
generated
vendored
Normal file
@ -0,0 +1,256 @@
|
||||
# GoFPDF document generator
|
||||
|
||||
[](https://raw.githubusercontent.com/jung-kurt/gofpdf/master/LICENSE)
|
||||
[](https://goreportcard.com/report/github.com/jung-kurt/gofpdf)
|
||||
[](https://godoc.org/github.com/jung-kurt/gofpdf)
|
||||
|
||||

|
||||
|
||||
Package gofpdf implements a PDF document generator with high level
|
||||
support for text, drawing and images.
|
||||
|
||||
## Features
|
||||
|
||||
- UTF-8 support
|
||||
- Choice of measurement unit, page format and margins
|
||||
- Page header and footer management
|
||||
- Automatic page breaks, line breaks, and text justification
|
||||
- Inclusion of JPEG, PNG, GIF, TIFF and basic path-only SVG images
|
||||
- Colors, gradients and alpha channel transparency
|
||||
- Outline bookmarks
|
||||
- Internal and external links
|
||||
- TrueType, Type1 and encoding support
|
||||
- Page compression
|
||||
- Lines, Bézier curves, arcs, and ellipses
|
||||
- Rotation, scaling, skewing, translation, and mirroring
|
||||
- Clipping
|
||||
- Document protection
|
||||
- Layers
|
||||
- Templates
|
||||
- Barcodes
|
||||
- Charting facility
|
||||
- Import PDFs as templates
|
||||
|
||||
gofpdf has no dependencies other than the Go standard library. All tests
|
||||
pass on Linux, Mac and Windows platforms.
|
||||
|
||||
gofpdf supports UTF-8 TrueType fonts and “right-to-left” languages. Note
|
||||
that Chinese, Japanese, and Korean characters may not be included in
|
||||
many general purpose fonts. For these languages, a specialized font (for
|
||||
example,
|
||||
[NotoSansSC](https://github.com/jsntn/webfonts/blob/master/NotoSansSC-Regular.ttf)
|
||||
for simplified Chinese) can be used.
|
||||
|
||||
Also, support is provided to automatically translate UTF-8 runes to code
|
||||
page encodings for languages that have fewer than 256 glyphs.
|
||||
|
||||
## Installation
|
||||
|
||||
To install the package on your system, run
|
||||
|
||||
``` shell
|
||||
go get github.com/jung-kurt/gofpdf
|
||||
```
|
||||
|
||||
Later, to receive updates, run
|
||||
|
||||
``` shell
|
||||
go get -u -v github.com/jung-kurt/gofpdf/...
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
The following Go code generates a simple PDF file.
|
||||
|
||||
``` go
|
||||
pdf := gofpdf.New("P", "mm", "A4", "")
|
||||
pdf.AddPage()
|
||||
pdf.SetFont("Arial", "B", 16)
|
||||
pdf.Cell(40, 10, "Hello, world")
|
||||
err := pdf.OutputFileAndClose("hello.pdf")
|
||||
```
|
||||
|
||||
See the functions in the
|
||||
[fpdf\_test.go](https://github.com/jung-kurt/gofpdf/blob/master/fpdf_test.go)
|
||||
file (shown as examples in this documentation) for more advanced PDF
|
||||
examples.
|
||||
|
||||
## Errors
|
||||
|
||||
If an error occurs in an Fpdf method, an internal error field is set.
|
||||
After this occurs, Fpdf method calls typically return without performing
|
||||
any operations and the error state is retained. This error management
|
||||
scheme facilitates PDF generation since individual method calls do not
|
||||
need to be examined for failure; it is generally sufficient to wait
|
||||
until after `Output()` is called. For the same reason, if an error
|
||||
occurs in the calling application during PDF generation, it may be
|
||||
desirable for the application to transfer the error to the Fpdf instance
|
||||
by calling the `SetError()` method or the `SetErrorf()` method. At any
|
||||
time during the life cycle of the Fpdf instance, the error state can be
|
||||
determined with a call to `Ok()` or `Err()`. The error itself can be
|
||||
retrieved with a call to `Error()`.
|
||||
|
||||
## Conversion Notes
|
||||
|
||||
This package is a relatively straightforward translation from the
|
||||
original [FPDF](http://www.fpdf.org/) library written in PHP (despite
|
||||
the caveat in the introduction to [Effective
|
||||
Go](https://golang.org/doc/effective_go.html)). The API names have been
|
||||
retained even though the Go idiom would suggest otherwise (for example,
|
||||
`pdf.GetX()` is used rather than simply `pdf.X()`). The similarity of
|
||||
the two libraries makes the original FPDF website a good source of
|
||||
information. It includes a forum and FAQ.
|
||||
|
||||
However, some internal changes have been made. Page content is built up
|
||||
using buffers (of type bytes.Buffer) rather than repeated string
|
||||
concatenation. Errors are handled as explained above rather than
|
||||
panicking. Output is generated through an interface of type io.Writer or
|
||||
io.WriteCloser. A number of the original PHP methods behave differently
|
||||
based on the type of the arguments that are passed to them; in these
|
||||
cases additional methods have been exported to provide similar
|
||||
functionality. Font definition files are produced in JSON rather than
|
||||
PHP.
|
||||
|
||||
## Example PDFs
|
||||
|
||||
A side effect of running `go test ./...` is the production of a number
|
||||
of example PDFs. These can be found in the gofpdf/pdf directory after
|
||||
the tests complete.
|
||||
|
||||
Please note that these examples run in the context of a test. In order
|
||||
run an example as a standalone application, you’ll need to examine
|
||||
[fpdf\_test.go](https://github.com/jung-kurt/gofpdf/blob/master/fpdf_test.go)
|
||||
for some helper routines, for example `exampleFilename()` and
|
||||
`summary()`.
|
||||
|
||||
Example PDFs can be compared with reference copies in order to verify
|
||||
that they have been generated as expected. This comparison will be
|
||||
performed if a PDF with the same name as the example PDF is placed in
|
||||
the gofpdf/pdf/reference directory and if the third argument to
|
||||
`ComparePDFFiles()` in internal/example/example.go is true. (By default
|
||||
it is false.) The routine that summarizes an example will look for this
|
||||
file and, if found, will call `ComparePDFFiles()` to check the example
|
||||
PDF for equality with its reference PDF. If differences exist between
|
||||
the two files they will be printed to standard output and the test will
|
||||
fail. If the reference file is missing, the comparison is considered to
|
||||
succeed. In order to successfully compare two PDFs, the placement of
|
||||
internal resources must be consistent and the internal creation
|
||||
timestamps must be the same. To do this, the methods `SetCatalogSort()`
|
||||
and `SetCreationDate()` need to be called for both files. This is done
|
||||
automatically for all examples.
|
||||
|
||||
## Nonstandard Fonts
|
||||
|
||||
Nothing special is required to use the standard PDF fonts (courier,
|
||||
helvetica, times, zapfdingbats) in your documents other than calling
|
||||
`SetFont()`.
|
||||
|
||||
You should use `AddUTF8Font()` or `AddUTF8FontFromBytes()` to add a
|
||||
TrueType UTF-8 encoded font. Use `RTL()` and `LTR()` methods switch
|
||||
between “right-to-left” and “left-to-right” mode.
|
||||
|
||||
In order to use a different non-UTF-8 TrueType or Type1 font, you will
|
||||
need to generate a font definition file and, if the font will be
|
||||
embedded into PDFs, a compressed version of the font file. This is done
|
||||
by calling the MakeFont function or using the included makefont command
|
||||
line utility. To create the utility, cd into the makefont subdirectory
|
||||
and run “go build”. This will produce a standalone executable named
|
||||
makefont. Select the appropriate encoding file from the font
|
||||
subdirectory and run the command as in the following example.
|
||||
|
||||
``` shell
|
||||
./makefont --embed --enc=../font/cp1252.map --dst=../font ../font/calligra.ttf
|
||||
```
|
||||
|
||||
In your PDF generation code, call `AddFont()` to load the font and, as
|
||||
with the standard fonts, SetFont() to begin using it. Most examples,
|
||||
including the package example, demonstrate this method. Good sources of
|
||||
free, open-source fonts include [Google
|
||||
Fonts](https://fonts.google.com/) and [DejaVu
|
||||
Fonts](http://dejavu-fonts.org/).
|
||||
|
||||
## Related Packages
|
||||
|
||||
The [draw2d](https://github.com/llgcode/draw2d) package is a two
|
||||
dimensional vector graphics library that can generate output in
|
||||
different forms. It uses gofpdf for its document production mode.
|
||||
|
||||
## Contributing Changes
|
||||
|
||||
gofpdf is a global community effort and you are invited to make it even
|
||||
better. If you have implemented a new feature or corrected a problem,
|
||||
please consider contributing your change to the project. A contribution
|
||||
that does not directly pertain to the core functionality of gofpdf
|
||||
should be placed in its own directory directly beneath the `contrib`
|
||||
directory.
|
||||
|
||||
Here are guidelines for making submissions. Your change should
|
||||
|
||||
- be compatible with the MIT License
|
||||
- be properly documented
|
||||
- be formatted with `go fmt`
|
||||
- include an example in
|
||||
[fpdf\_test.go](https://github.com/jung-kurt/gofpdf/blob/master/fpdf_test.go)
|
||||
if appropriate
|
||||
- conform to the standards of [golint](https://github.com/golang/lint)
|
||||
and [go vet](https://golang.org/cmd/vet/), that is, `golint .` and
|
||||
`go vet .` should not generate any warnings
|
||||
- not diminish [test coverage](https://blog.golang.org/cover)
|
||||
|
||||
[Pull requests](https://help.github.com/articles/using-pull-requests/)
|
||||
are the preferred means of accepting your changes.
|
||||
|
||||
## License
|
||||
|
||||
gofpdf is released under the MIT License. It is copyrighted by Kurt Jung
|
||||
and the contributors acknowledged below.
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
This package’s code and documentation are closely derived from the
|
||||
[FPDF](http://www.fpdf.org/) library created by Olivier Plathey, and a
|
||||
number of font and image resources are copied directly from it. Bruno
|
||||
Michel has provided valuable assistance with the code. Drawing support
|
||||
is adapted from the FPDF geometric figures script by David Hernández
|
||||
Sanz. Transparency support is adapted from the FPDF transparency script
|
||||
by Martin Hall-May. Support for gradients and clipping is adapted from
|
||||
FPDF scripts by Andreas Würmser. Support for outline bookmarks is
|
||||
adapted from Olivier Plathey by Manuel Cornes. Layer support is adapted
|
||||
from Olivier Plathey. Support for transformations is adapted from the
|
||||
FPDF transformation script by Moritz Wagner and Andreas Würmser. PDF
|
||||
protection is adapted from the work of Klemen Vodopivec for the FPDF
|
||||
product. Lawrence Kesteloot provided code to allow an image’s extent to
|
||||
be determined prior to placement. Support for vertical alignment within
|
||||
a cell was provided by Stefan Schroeder. Ivan Daniluk generalized the
|
||||
font and image loading code to use the Reader interface while
|
||||
maintaining backward compatibility. Anthony Starks provided code for the
|
||||
Polygon function. Robert Lillack provided the Beziergon function and
|
||||
corrected some naming issues with the internal curve function. Claudio
|
||||
Felber provided implementations for dashed line drawing and generalized
|
||||
font loading. Stani Michiels provided support for multi-segment path
|
||||
drawing with smooth line joins, line join styles, enhanced fill modes,
|
||||
and has helped greatly with package presentation and tests. Templating
|
||||
is adapted by Marcus Downing from the FPDF\_Tpl library created by Jan
|
||||
Slabon and Setasign. Jelmer Snoeck contributed packages that generate a
|
||||
variety of barcodes and help with registering images on the web. Jelmer
|
||||
Snoek and Guillermo Pascual augmented the basic HTML functionality with
|
||||
aligned text. Kent Quirk implemented backwards-compatible support for
|
||||
reading DPI from images that support it, and for setting DPI manually
|
||||
and then having it properly taken into account when calculating image
|
||||
size. Paulo Coutinho provided support for static embedded fonts. Dan
|
||||
Meyers added support for embedded JavaScript. David Fish added a generic
|
||||
alias-replacement function to enable, among other things, table of
|
||||
contents functionality. Andy Bakun identified and corrected a problem in
|
||||
which the internal catalogs were not sorted stably. Paul Montag added
|
||||
encoding and decoding functionality for templates, including images that
|
||||
are embedded in templates; this allows templates to be stored
|
||||
independently of gofpdf. Paul also added support for page boxes used in
|
||||
printing PDF documents. Wojciech Matusiak added supported for word
|
||||
spacing. Artem Korotkiy added support of UTF-8 fonts. Dave Barnes added
|
||||
support for imported objects and templates. Brigham Thompson added
|
||||
support for rounded rectangles.
|
||||
|
||||
## Roadmap
|
||||
|
||||
- Improve test coverage as reported by the coverage tool.
|
146
vendor/github.com/jung-kurt/gofpdf/compare.go
generated
vendored
Normal file
146
vendor/github.com/jung-kurt/gofpdf/compare.go
generated
vendored
Normal file
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Kurt Jung (Gmail: kurt.w.jung)
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package gofpdf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type sortType struct {
|
||||
length int
|
||||
less func(int, int) bool
|
||||
swap func(int, int)
|
||||
}
|
||||
|
||||
func (s *sortType) Len() int {
|
||||
return s.length
|
||||
}
|
||||
|
||||
func (s *sortType) Less(i, j int) bool {
|
||||
return s.less(i, j)
|
||||
}
|
||||
|
||||
func (s *sortType) Swap(i, j int) {
|
||||
s.swap(i, j)
|
||||
}
|
||||
|
||||
func gensort(Len int, Less func(int, int) bool, Swap func(int, int)) {
|
||||
sort.Sort(&sortType{length: Len, less: Less, swap: Swap})
|
||||
}
|
||||
|
||||
func writeBytes(leadStr string, startPos int, sl []byte) {
|
||||
var pos, max int
|
||||
var b byte
|
||||
fmt.Printf("%s %07x", leadStr, startPos)
|
||||
max = len(sl)
|
||||
for pos < max {
|
||||
fmt.Printf(" ")
|
||||
for k := 0; k < 8; k++ {
|
||||
if pos < max {
|
||||
fmt.Printf(" %02x", sl[pos])
|
||||
} else {
|
||||
fmt.Printf(" ")
|
||||
}
|
||||
pos++
|
||||
}
|
||||
}
|
||||
fmt.Printf(" |")
|
||||
pos = 0
|
||||
for pos < max {
|
||||
b = sl[pos]
|
||||
if b < 32 || b >= 128 {
|
||||
b = '.'
|
||||
}
|
||||
fmt.Printf("%c", b)
|
||||
pos++
|
||||
}
|
||||
fmt.Printf("|\n")
|
||||
}
|
||||
|
||||
func checkBytes(pos int, sl1, sl2 []byte, printDiff bool) (eq bool) {
|
||||
eq = bytes.Equal(sl1, sl2)
|
||||
if !eq && printDiff {
|
||||
writeBytes("<", pos, sl1)
|
||||
writeBytes(">", pos, sl2)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CompareBytes compares the bytes referred to by sl1 with those referred to by
|
||||
// sl2. Nil is returned if the buffers are equal, otherwise an error.
|
||||
func CompareBytes(sl1, sl2 []byte, printDiff bool) (err error) {
|
||||
var posStart, posEnd, len1, len2, length int
|
||||
var diffs bool
|
||||
|
||||
len1 = len(sl1)
|
||||
len2 = len(sl2)
|
||||
length = len1
|
||||
if length > len2 {
|
||||
length = len2
|
||||
}
|
||||
for posStart < length-1 {
|
||||
posEnd = posStart + 16
|
||||
if posEnd > length {
|
||||
posEnd = length
|
||||
}
|
||||
if !checkBytes(posStart, sl1[posStart:posEnd], sl2[posStart:posEnd], printDiff) {
|
||||
diffs = true
|
||||
}
|
||||
posStart = posEnd
|
||||
}
|
||||
if diffs {
|
||||
err = fmt.Errorf("documents are different")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ComparePDFs reads and compares the full contents of the two specified
|
||||
// readers byte-for-byte. Nil is returned if the buffers are equal, otherwise
|
||||
// an error.
|
||||
func ComparePDFs(rdr1, rdr2 io.Reader, printDiff bool) (err error) {
|
||||
var b1, b2 *bytes.Buffer
|
||||
_, err = b1.ReadFrom(rdr1)
|
||||
if err == nil {
|
||||
_, err = b2.ReadFrom(rdr2)
|
||||
if err == nil {
|
||||
err = CompareBytes(b1.Bytes(), b2.Bytes(), printDiff)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ComparePDFFiles reads and compares the full contents of the two specified
|
||||
// files byte-for-byte. Nil is returned if the file contents are equal, or if
|
||||
// the second file is missing, otherwise an error.
|
||||
func ComparePDFFiles(file1Str, file2Str string, printDiff bool) (err error) {
|
||||
var sl1, sl2 []byte
|
||||
sl1, err = ioutil.ReadFile(file1Str)
|
||||
if err == nil {
|
||||
sl2, err = ioutil.ReadFile(file2Str)
|
||||
if err == nil {
|
||||
err = CompareBytes(sl1, sl2, printDiff)
|
||||
} else {
|
||||
// Second file is missing; treat this as success
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
734
vendor/github.com/jung-kurt/gofpdf/def.go
generated
vendored
Normal file
734
vendor/github.com/jung-kurt/gofpdf/def.go
generated
vendored
Normal file
@ -0,0 +1,734 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2014 Kurt Jung (Gmail: kurt.w.jung)
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package gofpdf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"encoding/gob"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Version of FPDF from which this package is derived
|
||||
const (
|
||||
cnFpdfVersion = "1.7"
|
||||
)
|
||||
|
||||
type blendModeType struct {
|
||||
strokeStr, fillStr, modeStr string
|
||||
objNum int
|
||||
}
|
||||
|
||||
type gradientType struct {
|
||||
tp int // 2: linear, 3: radial
|
||||
clr1Str, clr2Str string
|
||||
x1, y1, x2, y2, r float64
|
||||
objNum int
|
||||
}
|
||||
|
||||
const (
|
||||
// OrientationPortrait represents the portrait orientation.
|
||||
OrientationPortrait = "portrait"
|
||||
|
||||
// OrientationLandscape represents the landscape orientation.
|
||||
OrientationLandscape = "landscape"
|
||||
)
|
||||
|
||||
const (
|
||||
// UnitPoint represents the size unit point
|
||||
UnitPoint = "pt"
|
||||
// UnitMillimeter represents the size unit millimeter
|
||||
UnitMillimeter = "mm"
|
||||
// UnitCentimeter represents the size unit centimeter
|
||||
UnitCentimeter = "cm"
|
||||
// UnitInch represents the size unit inch
|
||||
UnitInch = "inch"
|
||||
)
|
||||
|
||||
const (
|
||||
// PageSizeA3 represents DIN/ISO A3 page size
|
||||
PageSizeA3 = "A3"
|
||||
// PageSizeA4 represents DIN/ISO A4 page size
|
||||
PageSizeA4 = "A4"
|
||||
// PageSizeA5 represents DIN/ISO A5 page size
|
||||
PageSizeA5 = "A5"
|
||||
// PageSizeLetter represents US Letter page size
|
||||
PageSizeLetter = "Letter"
|
||||
// PageSizeLegal represents US Legal page size
|
||||
PageSizeLegal = "Legal"
|
||||
)
|
||||
|
||||
const (
|
||||
// BorderNone set no border
|
||||
BorderNone = ""
|
||||
// BorderFull sets a full border
|
||||
BorderFull = "1"
|
||||
// BorderLeft sets the border on the left side
|
||||
BorderLeft = "L"
|
||||
// BorderTop sets the border at the top
|
||||
BorderTop = "T"
|
||||
// BorderRight sets the border on the right side
|
||||
BorderRight = "R"
|
||||
// BorderBottom sets the border on the bottom
|
||||
BorderBottom = "B"
|
||||
)
|
||||
|
||||
const (
|
||||
// LineBreakNone disables linebreak
|
||||
LineBreakNone = 0
|
||||
// LineBreakNormal enables normal linebreak
|
||||
LineBreakNormal = 1
|
||||
// LineBreakBelow enables linebreak below
|
||||
LineBreakBelow = 2
|
||||
)
|
||||
|
||||
const (
|
||||
// AlignLeft left aligns the cell
|
||||
AlignLeft = "L"
|
||||
// AlignRight right aligns the cell
|
||||
AlignRight = "R"
|
||||
// AlignCenter centers the cell
|
||||
AlignCenter = "C"
|
||||
// AlignTop aligns the cell to the top
|
||||
AlignTop = "T"
|
||||
// AlignBottom aligns the cell to the bottom
|
||||
AlignBottom = "B"
|
||||
// AlignMiddle aligns the cell to the middle
|
||||
AlignMiddle = "M"
|
||||
// AlignBaseline aligns the cell to the baseline
|
||||
AlignBaseline = "B"
|
||||
)
|
||||
|
||||
type colorMode int
|
||||
|
||||
const (
|
||||
colorModeRGB colorMode = iota
|
||||
colorModeSpot
|
||||
colorModeCMYK
|
||||
)
|
||||
|
||||
type colorType struct {
|
||||
r, g, b float64
|
||||
ir, ig, ib int
|
||||
mode colorMode
|
||||
spotStr string // name of current spot color
|
||||
gray bool
|
||||
str string
|
||||
}
|
||||
|
||||
// SpotColorType specifies a named spot color value
|
||||
type spotColorType struct {
|
||||
id, objID int
|
||||
val cmykColorType
|
||||
}
|
||||
|
||||
// CMYKColorType specifies an ink-based CMYK color value
|
||||
type cmykColorType struct {
|
||||
c, m, y, k byte // 0% to 100%
|
||||
}
|
||||
|
||||
// SizeType fields Wd and Ht specify the horizontal and vertical extents of a
|
||||
// document element such as a page.
|
||||
type SizeType struct {
|
||||
Wd, Ht float64
|
||||
}
|
||||
|
||||
// PointType fields X and Y specify the horizontal and vertical coordinates of
|
||||
// a point, typically used in drawing.
|
||||
type PointType struct {
|
||||
X, Y float64
|
||||
}
|
||||
|
||||
// XY returns the X and Y components of the receiver point.
|
||||
func (p PointType) XY() (float64, float64) {
|
||||
return p.X, p.Y
|
||||
}
|
||||
|
||||
// ImageInfoType contains size, color and other information about an image.
|
||||
// Changes to this structure should be reflected in its GobEncode and GobDecode
|
||||
// methods.
|
||||
type ImageInfoType struct {
|
||||
data []byte
|
||||
smask []byte
|
||||
n int
|
||||
w float64
|
||||
h float64
|
||||
cs string
|
||||
pal []byte
|
||||
bpc int
|
||||
f string
|
||||
dp string
|
||||
trns []int
|
||||
scale float64 // document scaling factor
|
||||
dpi float64
|
||||
i string
|
||||
}
|
||||
|
||||
func generateImageID(info *ImageInfoType) (string, error) {
|
||||
b, err := info.GobEncode()
|
||||
return fmt.Sprintf("%x", sha1.Sum(b)), err
|
||||
}
|
||||
|
||||
// GobEncode encodes the receiving image to a byte slice.
|
||||
func (info *ImageInfoType) GobEncode() (buf []byte, err error) {
|
||||
fields := []interface{}{info.data, info.smask, info.n, info.w, info.h, info.cs,
|
||||
info.pal, info.bpc, info.f, info.dp, info.trns, info.scale, info.dpi}
|
||||
w := new(bytes.Buffer)
|
||||
encoder := gob.NewEncoder(w)
|
||||
for j := 0; j < len(fields) && err == nil; j++ {
|
||||
err = encoder.Encode(fields[j])
|
||||
}
|
||||
if err == nil {
|
||||
buf = w.Bytes()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GobDecode decodes the specified byte buffer (generated by GobEncode) into
|
||||
// the receiving image.
|
||||
func (info *ImageInfoType) GobDecode(buf []byte) (err error) {
|
||||
fields := []interface{}{&info.data, &info.smask, &info.n, &info.w, &info.h,
|
||||
&info.cs, &info.pal, &info.bpc, &info.f, &info.dp, &info.trns, &info.scale, &info.dpi}
|
||||
r := bytes.NewBuffer(buf)
|
||||
decoder := gob.NewDecoder(r)
|
||||
for j := 0; j < len(fields) && err == nil; j++ {
|
||||
err = decoder.Decode(fields[j])
|
||||
}
|
||||
|
||||
info.i, err = generateImageID(info)
|
||||
return
|
||||
}
|
||||
|
||||
// PointConvert returns the value of pt, expressed in points (1/72 inch), as a
|
||||
// value expressed in the unit of measure specified in New(). Since font
|
||||
// management in Fpdf uses points, this method can help with line height
|
||||
// calculations and other methods that require user units.
|
||||
func (f *Fpdf) PointConvert(pt float64) (u float64) {
|
||||
return pt / f.k
|
||||
}
|
||||
|
||||
// PointToUnitConvert is an alias for PointConvert.
|
||||
func (f *Fpdf) PointToUnitConvert(pt float64) (u float64) {
|
||||
return pt / f.k
|
||||
}
|
||||
|
||||
// UnitToPointConvert returns the value of u, expressed in the unit of measure
|
||||
// specified in New(), as a value expressed in points (1/72 inch). Since font
|
||||
// management in Fpdf uses points, this method can help with setting font sizes
|
||||
// based on the sizes of other non-font page elements.
|
||||
func (f *Fpdf) UnitToPointConvert(u float64) (pt float64) {
|
||||
return u * f.k
|
||||
}
|
||||
|
||||
// Extent returns the width and height of the image in the units of the Fpdf
|
||||
// object.
|
||||
func (info *ImageInfoType) Extent() (wd, ht float64) {
|
||||
return info.Width(), info.Height()
|
||||
}
|
||||
|
||||
// Width returns the width of the image in the units of the Fpdf object.
|
||||
func (info *ImageInfoType) Width() float64 {
|
||||
return info.w / (info.scale * info.dpi / 72)
|
||||
}
|
||||
|
||||
// Height returns the height of the image in the units of the Fpdf object.
|
||||
func (info *ImageInfoType) Height() float64 {
|
||||
return info.h / (info.scale * info.dpi / 72)
|
||||
}
|
||||
|
||||
// SetDpi sets the dots per inch for an image. PNG images MAY have their dpi
|
||||
// set automatically, if the image specifies it. DPI information is not
|
||||
// currently available automatically for JPG and GIF images, so if it's
|
||||
// important to you, you can set it here. It defaults to 72 dpi.
|
||||
func (info *ImageInfoType) SetDpi(dpi float64) {
|
||||
info.dpi = dpi
|
||||
}
|
||||
|
||||
type fontFileType struct {
|
||||
length1, length2 int64
|
||||
n int
|
||||
embedded bool
|
||||
content []byte
|
||||
fontType string
|
||||
}
|
||||
|
||||
type linkType struct {
|
||||
x, y, wd, ht float64
|
||||
link int // Auto-generated internal link ID or...
|
||||
linkStr string // ...application-provided external link string
|
||||
}
|
||||
|
||||
type intLinkType struct {
|
||||
page int
|
||||
y float64
|
||||
}
|
||||
|
||||
// outlineType is used for a sidebar outline of bookmarks
|
||||
type outlineType struct {
|
||||
text string
|
||||
level, parent, first, last, next, prev int
|
||||
y float64
|
||||
p int
|
||||
}
|
||||
|
||||
// InitType is used with NewCustom() to customize an Fpdf instance.
|
||||
// OrientationStr, UnitStr, SizeStr and FontDirStr correspond to the arguments
|
||||
// accepted by New(). If the Wd and Ht fields of Size are each greater than
|
||||
// zero, Size will be used to set the default page size rather than SizeStr. Wd
|
||||
// and Ht are specified in the units of measure indicated by UnitStr.
|
||||
type InitType struct {
|
||||
OrientationStr string
|
||||
UnitStr string
|
||||
SizeStr string
|
||||
Size SizeType
|
||||
FontDirStr string
|
||||
}
|
||||
|
||||
// FontLoader is used to read fonts (JSON font specification and zlib compressed font binaries)
|
||||
// from arbitrary locations (e.g. files, zip files, embedded font resources).
|
||||
//
|
||||
// Open provides an io.Reader for the specified font file (.json or .z). The file name
|
||||
// never includes a path. Open returns an error if the specified file cannot be opened.
|
||||
type FontLoader interface {
|
||||
Open(name string) (io.Reader, error)
|
||||
}
|
||||
|
||||
// Pdf defines the interface used for various methods. It is implemented by the
|
||||
// main FPDF instance as well as templates.
|
||||
type Pdf interface {
|
||||
AddFont(familyStr, styleStr, fileStr string)
|
||||
AddFontFromBytes(familyStr, styleStr string, jsonFileBytes, zFileBytes []byte)
|
||||
AddFontFromReader(familyStr, styleStr string, r io.Reader)
|
||||
AddLayer(name string, visible bool) (layerID int)
|
||||
AddLink() int
|
||||
AddPage()
|
||||
AddPageFormat(orientationStr string, size SizeType)
|
||||
AddSpotColor(nameStr string, c, m, y, k byte)
|
||||
AliasNbPages(aliasStr string)
|
||||
ArcTo(x, y, rx, ry, degRotate, degStart, degEnd float64)
|
||||
Arc(x, y, rx, ry, degRotate, degStart, degEnd float64, styleStr string)
|
||||
BeginLayer(id int)
|
||||
Beziergon(points []PointType, styleStr string)
|
||||
Bookmark(txtStr string, level int, y float64)
|
||||
CellFormat(w, h float64, txtStr, borderStr string, ln int, alignStr string, fill bool, link int, linkStr string)
|
||||
Cellf(w, h float64, fmtStr string, args ...interface{})
|
||||
Cell(w, h float64, txtStr string)
|
||||
Circle(x, y, r float64, styleStr string)
|
||||
ClearError()
|
||||
ClipCircle(x, y, r float64, outline bool)
|
||||
ClipEllipse(x, y, rx, ry float64, outline bool)
|
||||
ClipEnd()
|
||||
ClipPolygon(points []PointType, outline bool)
|
||||
ClipRect(x, y, w, h float64, outline bool)
|
||||
ClipRoundedRect(x, y, w, h, r float64, outline bool)
|
||||
ClipText(x, y float64, txtStr string, outline bool)
|
||||
Close()
|
||||
ClosePath()
|
||||
CreateTemplateCustom(corner PointType, size SizeType, fn func(*Tpl)) Template
|
||||
CreateTemplate(fn func(*Tpl)) Template
|
||||
CurveBezierCubicTo(cx0, cy0, cx1, cy1, x, y float64)
|
||||
CurveBezierCubic(x0, y0, cx0, cy0, cx1, cy1, x1, y1 float64, styleStr string)
|
||||
CurveCubic(x0, y0, cx0, cy0, x1, y1, cx1, cy1 float64, styleStr string)
|
||||
CurveTo(cx, cy, x, y float64)
|
||||
Curve(x0, y0, cx, cy, x1, y1 float64, styleStr string)
|
||||
DrawPath(styleStr string)
|
||||
Ellipse(x, y, rx, ry, degRotate float64, styleStr string)
|
||||
EndLayer()
|
||||
Err() bool
|
||||
Error() error
|
||||
GetAlpha() (alpha float64, blendModeStr string)
|
||||
GetAutoPageBreak() (auto bool, margin float64)
|
||||
GetCellMargin() float64
|
||||
GetConversionRatio() float64
|
||||
GetDrawColor() (int, int, int)
|
||||
GetDrawSpotColor() (name string, c, m, y, k byte)
|
||||
GetFillColor() (int, int, int)
|
||||
GetFillSpotColor() (name string, c, m, y, k byte)
|
||||
GetFontDesc(familyStr, styleStr string) FontDescType
|
||||
GetFontSize() (ptSize, unitSize float64)
|
||||
GetImageInfo(imageStr string) (info *ImageInfoType)
|
||||
GetLineWidth() float64
|
||||
GetMargins() (left, top, right, bottom float64)
|
||||
GetPageSizeStr(sizeStr string) (size SizeType)
|
||||
GetPageSize() (width, height float64)
|
||||
GetStringWidth(s string) float64
|
||||
GetTextColor() (int, int, int)
|
||||
GetTextSpotColor() (name string, c, m, y, k byte)
|
||||
GetX() float64
|
||||
GetXY() (float64, float64)
|
||||
GetY() float64
|
||||
HTMLBasicNew() (html HTMLBasicType)
|
||||
Image(imageNameStr string, x, y, w, h float64, flow bool, tp string, link int, linkStr string)
|
||||
ImageOptions(imageNameStr string, x, y, w, h float64, flow bool, options ImageOptions, link int, linkStr string)
|
||||
ImageTypeFromMime(mimeStr string) (tp string)
|
||||
LinearGradient(x, y, w, h float64, r1, g1, b1, r2, g2, b2 int, x1, y1, x2, y2 float64)
|
||||
LineTo(x, y float64)
|
||||
Line(x1, y1, x2, y2 float64)
|
||||
LinkString(x, y, w, h float64, linkStr string)
|
||||
Link(x, y, w, h float64, link int)
|
||||
Ln(h float64)
|
||||
MoveTo(x, y float64)
|
||||
MultiCell(w, h float64, txtStr, borderStr, alignStr string, fill bool)
|
||||
Ok() bool
|
||||
OpenLayerPane()
|
||||
OutputAndClose(w io.WriteCloser) error
|
||||
OutputFileAndClose(fileStr string) error
|
||||
Output(w io.Writer) error
|
||||
PageCount() int
|
||||
PageNo() int
|
||||
PageSize(pageNum int) (wd, ht float64, unitStr string)
|
||||
PointConvert(pt float64) (u float64)
|
||||
PointToUnitConvert(pt float64) (u float64)
|
||||
Polygon(points []PointType, styleStr string)
|
||||
RadialGradient(x, y, w, h float64, r1, g1, b1, r2, g2, b2 int, x1, y1, x2, y2, r float64)
|
||||
RawWriteBuf(r io.Reader)
|
||||
RawWriteStr(str string)
|
||||
Rect(x, y, w, h float64, styleStr string)
|
||||
RegisterAlias(alias, replacement string)
|
||||
RegisterImage(fileStr, tp string) (info *ImageInfoType)
|
||||
RegisterImageOptions(fileStr string, options ImageOptions) (info *ImageInfoType)
|
||||
RegisterImageOptionsReader(imgName string, options ImageOptions, r io.Reader) (info *ImageInfoType)
|
||||
RegisterImageReader(imgName, tp string, r io.Reader) (info *ImageInfoType)
|
||||
SetAcceptPageBreakFunc(fnc func() bool)
|
||||
SetAlpha(alpha float64, blendModeStr string)
|
||||
SetAuthor(authorStr string, isUTF8 bool)
|
||||
SetAutoPageBreak(auto bool, margin float64)
|
||||
SetCatalogSort(flag bool)
|
||||
SetCellMargin(margin float64)
|
||||
SetCompression(compress bool)
|
||||
SetCreationDate(tm time.Time)
|
||||
SetCreator(creatorStr string, isUTF8 bool)
|
||||
SetDashPattern(dashArray []float64, dashPhase float64)
|
||||
SetDisplayMode(zoomStr, layoutStr string)
|
||||
SetDrawColor(r, g, b int)
|
||||
SetDrawSpotColor(nameStr string, tint byte)
|
||||
SetError(err error)
|
||||
SetErrorf(fmtStr string, args ...interface{})
|
||||
SetFillColor(r, g, b int)
|
||||
SetFillSpotColor(nameStr string, tint byte)
|
||||
SetFont(familyStr, styleStr string, size float64)
|
||||
SetFontLoader(loader FontLoader)
|
||||
SetFontLocation(fontDirStr string)
|
||||
SetFontSize(size float64)
|
||||
SetFontStyle(styleStr string)
|
||||
SetFontUnitSize(size float64)
|
||||
SetFooterFunc(fnc func())
|
||||
SetFooterFuncLpi(fnc func(lastPage bool))
|
||||
SetHeaderFunc(fnc func())
|
||||
SetHeaderFuncMode(fnc func(), homeMode bool)
|
||||
SetHomeXY()
|
||||
SetJavascript(script string)
|
||||
SetKeywords(keywordsStr string, isUTF8 bool)
|
||||
SetLeftMargin(margin float64)
|
||||
SetLineCapStyle(styleStr string)
|
||||
SetLineJoinStyle(styleStr string)
|
||||
SetLineWidth(width float64)
|
||||
SetLink(link int, y float64, page int)
|
||||
SetMargins(left, top, right float64)
|
||||
SetPageBoxRec(t string, pb PageBox)
|
||||
SetPageBox(t string, x, y, wd, ht float64)
|
||||
SetPage(pageNum int)
|
||||
SetProtection(actionFlag byte, userPassStr, ownerPassStr string)
|
||||
SetRightMargin(margin float64)
|
||||
SetSubject(subjectStr string, isUTF8 bool)
|
||||
SetTextColor(r, g, b int)
|
||||
SetTextSpotColor(nameStr string, tint byte)
|
||||
SetTitle(titleStr string, isUTF8 bool)
|
||||
SetTopMargin(margin float64)
|
||||
SetUnderlineThickness(thickness float64)
|
||||
SetXmpMetadata(xmpStream []byte)
|
||||
SetX(x float64)
|
||||
SetXY(x, y float64)
|
||||
SetY(y float64)
|
||||
SplitLines(txt []byte, w float64) [][]byte
|
||||
String() string
|
||||
SVGBasicWrite(sb *SVGBasicType, scale float64)
|
||||
Text(x, y float64, txtStr string)
|
||||
TransformBegin()
|
||||
TransformEnd()
|
||||
TransformMirrorHorizontal(x float64)
|
||||
TransformMirrorLine(angle, x, y float64)
|
||||
TransformMirrorPoint(x, y float64)
|
||||
TransformMirrorVertical(y float64)
|
||||
TransformRotate(angle, x, y float64)
|
||||
TransformScale(scaleWd, scaleHt, x, y float64)
|
||||
TransformScaleX(scaleWd, x, y float64)
|
||||
TransformScaleXY(s, x, y float64)
|
||||
TransformScaleY(scaleHt, x, y float64)
|
||||
TransformSkew(angleX, angleY, x, y float64)
|
||||
TransformSkewX(angleX, x, y float64)
|
||||
TransformSkewY(angleY, x, y float64)
|
||||
Transform(tm TransformMatrix)
|
||||
TransformTranslate(tx, ty float64)
|
||||
TransformTranslateX(tx float64)
|
||||
TransformTranslateY(ty float64)
|
||||
UnicodeTranslatorFromDescriptor(cpStr string) (rep func(string) string)
|
||||
UnitToPointConvert(u float64) (pt float64)
|
||||
UseTemplateScaled(t Template, corner PointType, size SizeType)
|
||||
UseTemplate(t Template)
|
||||
WriteAligned(width, lineHeight float64, textStr, alignStr string)
|
||||
Writef(h float64, fmtStr string, args ...interface{})
|
||||
Write(h float64, txtStr string)
|
||||
WriteLinkID(h float64, displayStr string, linkID int)
|
||||
WriteLinkString(h float64, displayStr, targetStr string)
|
||||
}
|
||||
|
||||
// PageBox defines the coordinates and extent of the various page box types
|
||||
type PageBox struct {
|
||||
SizeType
|
||||
PointType
|
||||
}
|
||||
|
||||
// Fpdf is the principal structure for creating a single PDF document
|
||||
type Fpdf struct {
|
||||
isCurrentUTF8 bool // is current font used in utf-8 mode
|
||||
isRTL bool // is is right to left mode enabled
|
||||
page int // current page number
|
||||
n int // current object number
|
||||
offsets []int // array of object offsets
|
||||
templates map[string]Template // templates used in this document
|
||||
templateObjects map[string]int // template object IDs within this document
|
||||
importedObjs map[string][]byte // imported template objects (gofpdi)
|
||||
importedObjPos map[string]map[int]string // imported template objects hashes and their positions (gofpdi)
|
||||
importedTplObjs map[string]string // imported template names and IDs (hashed) (gofpdi)
|
||||
importedTplIDs map[string]int // imported template ids hash to object id int (gofpdi)
|
||||
buffer fmtBuffer // buffer holding in-memory PDF
|
||||
pages []*bytes.Buffer // slice[page] of page content; 1-based
|
||||
state int // current document state
|
||||
compress bool // compression flag
|
||||
k float64 // scale factor (number of points in user unit)
|
||||
defOrientation string // default orientation
|
||||
curOrientation string // current orientation
|
||||
stdPageSizes map[string]SizeType // standard page sizes
|
||||
defPageSize SizeType // default page size
|
||||
defPageBoxes map[string]PageBox // default page size
|
||||
curPageSize SizeType // current page size
|
||||
pageSizes map[int]SizeType // used for pages with non default sizes or orientations
|
||||
pageBoxes map[int]map[string]PageBox // used to define the crop, trim, bleed and art boxes
|
||||
unitStr string // unit of measure for all rendered objects except fonts
|
||||
wPt, hPt float64 // dimensions of current page in points
|
||||
w, h float64 // dimensions of current page in user unit
|
||||
lMargin float64 // left margin
|
||||
tMargin float64 // top margin
|
||||
rMargin float64 // right margin
|
||||
bMargin float64 // page break margin
|
||||
cMargin float64 // cell margin
|
||||
x, y float64 // current position in user unit
|
||||
lasth float64 // height of last printed cell
|
||||
lineWidth float64 // line width in user unit
|
||||
fontpath string // path containing fonts
|
||||
fontLoader FontLoader // used to load font files from arbitrary locations
|
||||
coreFonts map[string]bool // array of core font names
|
||||
fonts map[string]fontDefType // array of used fonts
|
||||
fontFiles map[string]fontFileType // array of font files
|
||||
diffs []string // array of encoding differences
|
||||
fontFamily string // current font family
|
||||
fontStyle string // current font style
|
||||
underline bool // underlining flag
|
||||
currentFont fontDefType // current font info
|
||||
fontSizePt float64 // current font size in points
|
||||
fontSize float64 // current font size in user unit
|
||||
ws float64 // word spacing
|
||||
images map[string]*ImageInfoType // array of used images
|
||||
aliasMap map[string]string // map of alias->replacement
|
||||
pageLinks [][]linkType // pageLinks[page][link], both 1-based
|
||||
links []intLinkType // array of internal links
|
||||
outlines []outlineType // array of outlines
|
||||
outlineRoot int // root of outlines
|
||||
autoPageBreak bool // automatic page breaking
|
||||
acceptPageBreak func() bool // returns true to accept page break
|
||||
pageBreakTrigger float64 // threshold used to trigger page breaks
|
||||
inHeader bool // flag set when processing header
|
||||
headerFnc func() // function provided by app and called to write header
|
||||
headerHomeMode bool // set position to home after headerFnc is called
|
||||
inFooter bool // flag set when processing footer
|
||||
footerFnc func() // function provided by app and called to write footer
|
||||
footerFncLpi func(bool) // function provided by app and called to write footer with last page flag
|
||||
zoomMode string // zoom display mode
|
||||
layoutMode string // layout display mode
|
||||
xmp []byte // XMP metadata
|
||||
producer string // producer
|
||||
title string // title
|
||||
subject string // subject
|
||||
author string // author
|
||||
keywords string // keywords
|
||||
creator string // creator
|
||||
creationDate time.Time // override for dcoument CreationDate value
|
||||
aliasNbPagesStr string // alias for total number of pages
|
||||
pdfVersion string // PDF version number
|
||||
fontDirStr string // location of font definition files
|
||||
capStyle int // line cap style: butt 0, round 1, square 2
|
||||
joinStyle int // line segment join style: miter 0, round 1, bevel 2
|
||||
dashArray []float64 // dash array
|
||||
dashPhase float64 // dash phase
|
||||
blendList []blendModeType // slice[idx] of alpha transparency modes, 1-based
|
||||
blendMap map[string]int // map into blendList
|
||||
blendMode string // current blend mode
|
||||
alpha float64 // current transpacency
|
||||
gradientList []gradientType // slice[idx] of gradient records
|
||||
clipNest int // Number of active clipping contexts
|
||||
transformNest int // Number of active transformation contexts
|
||||
err error // Set if error occurs during life cycle of instance
|
||||
protect protectType // document protection structure
|
||||
layer layerRecType // manages optional layers in document
|
||||
catalogSort bool // sort resource catalogs in document
|
||||
nJs int // JavaScript object number
|
||||
javascript *string // JavaScript code to include in the PDF
|
||||
colorFlag bool // indicates whether fill and text colors are different
|
||||
color struct {
|
||||
// Composite values of colors
|
||||
draw, fill, text colorType
|
||||
}
|
||||
spotColorMap map[string]spotColorType // Map of named ink-based colors
|
||||
userUnderlineThickness float64 // A custom user underline thickness multiplier.
|
||||
}
|
||||
|
||||
type encType struct {
|
||||
uv int
|
||||
name string
|
||||
}
|
||||
|
||||
type encListType [256]encType
|
||||
|
||||
type fontBoxType struct {
|
||||
Xmin, Ymin, Xmax, Ymax int
|
||||
}
|
||||
|
||||
// Font flags for FontDescType.Flags as defined in the pdf specification.
|
||||
const (
|
||||
// FontFlagFixedPitch is set if all glyphs have the same width (as
|
||||
// opposed to proportional or variable-pitch fonts, which have
|
||||
// different widths).
|
||||
FontFlagFixedPitch = 1 << 0
|
||||
// FontFlagSerif is set if glyphs have serifs, which are short
|
||||
// strokes drawn at an angle on the top and bottom of glyph stems.
|
||||
// (Sans serif fonts do not have serifs.)
|
||||
FontFlagSerif = 1 << 1
|
||||
// FontFlagSymbolic is set if font contains glyphs outside the
|
||||
// Adobe standard Latin character set. This flag and the
|
||||
// Nonsymbolic flag shall not both be set or both be clear.
|
||||
FontFlagSymbolic = 1 << 2
|
||||
// FontFlagScript is set if glyphs resemble cursive handwriting.
|
||||
FontFlagScript = 1 << 3
|
||||
// FontFlagNonsymbolic is set if font uses the Adobe standard
|
||||
// Latin character set or a subset of it.
|
||||
FontFlagNonsymbolic = 1 << 5
|
||||
// FontFlagItalic is set if glyphs have dominant vertical strokes
|
||||
// that are slanted.
|
||||
FontFlagItalic = 1 << 6
|
||||
// FontFlagAllCap is set if font contains no lowercase letters;
|
||||
// typically used for display purposes, such as for titles or
|
||||
// headlines.
|
||||
FontFlagAllCap = 1 << 16
|
||||
// SmallCap is set if font contains both uppercase and lowercase
|
||||
// letters. The uppercase letters are similar to those in the
|
||||
// regular version of the same typeface family. The glyphs for the
|
||||
// lowercase letters have the same shapes as the corresponding
|
||||
// uppercase letters, but they are sized and their proportions
|
||||
// adjusted so that they have the same size and stroke weight as
|
||||
// lowercase glyphs in the same typeface family.
|
||||
SmallCap = 1 << 18
|
||||
// ForceBold determines whether bold glyphs shall be painted with
|
||||
// extra pixels even at very small text sizes by a conforming
|
||||
// reader. If the ForceBold flag is set, features of bold glyphs
|
||||
// may be thickened at small text sizes.
|
||||
ForceBold = 1 << 18
|
||||
)
|
||||
|
||||
// FontDescType (font descriptor) specifies metrics and other
|
||||
// attributes of a font, as distinct from the metrics of individual
|
||||
// glyphs (as defined in the pdf specification).
|
||||
type FontDescType struct {
|
||||
// The maximum height above the baseline reached by glyphs in this
|
||||
// font (for example for "S"). The height of glyphs for accented
|
||||
// characters shall be excluded.
|
||||
Ascent int
|
||||
// The maximum depth below the baseline reached by glyphs in this
|
||||
// font. The value shall be a negative number.
|
||||
Descent int
|
||||
// The vertical coordinate of the top of flat capital letters,
|
||||
// measured from the baseline (for example "H").
|
||||
CapHeight int
|
||||
// A collection of flags defining various characteristics of the
|
||||
// font. (See the FontFlag* constants.)
|
||||
Flags int
|
||||
// A rectangle, expressed in the glyph coordinate system, that
|
||||
// shall specify the font bounding box. This should be the smallest
|
||||
// rectangle enclosing the shape that would result if all of the
|
||||
// glyphs of the font were placed with their origins coincident
|
||||
// and then filled.
|
||||
FontBBox fontBoxType
|
||||
// The angle, expressed in degrees counterclockwise from the
|
||||
// vertical, of the dominant vertical strokes of the font. (The
|
||||
// 9-o’clock position is 90 degrees, and the 3-o’clock position
|
||||
// is –90 degrees.) The value shall be negative for fonts that
|
||||
// slope to the right, as almost all italic fonts do.
|
||||
ItalicAngle int
|
||||
// The thickness, measured horizontally, of the dominant vertical
|
||||
// stems of glyphs in the font.
|
||||
StemV int
|
||||
// The width to use for character codes whose widths are not
|
||||
// specified in a font dictionary’s Widths array. This shall have
|
||||
// a predictable effect only if all such codes map to glyphs whose
|
||||
// actual widths are the same as the value of the MissingWidth
|
||||
// entry. (Default value: 0.)
|
||||
MissingWidth int
|
||||
}
|
||||
|
||||
type fontDefType struct {
|
||||
Tp string // "Core", "TrueType", ...
|
||||
Name string // "Courier-Bold", ...
|
||||
Desc FontDescType // Font descriptor
|
||||
Up int // Underline position
|
||||
Ut int // Underline thickness
|
||||
Cw []int // Character width by ordinal
|
||||
Enc string // "cp1252", ...
|
||||
Diff string // Differences from reference encoding
|
||||
File string // "Redressed.z"
|
||||
Size1, Size2 int // Type1 values
|
||||
OriginalSize int // Size of uncompressed font file
|
||||
N int // Set by font loader
|
||||
DiffN int // Position of diff in app array, set by font loader
|
||||
i string // 1-based position in font list, set by font loader, not this program
|
||||
utf8File *utf8FontFile // UTF-8 font
|
||||
usedRunes map[int]int // Array of used runes
|
||||
}
|
||||
|
||||
// generateFontID generates a font Id from the font definition
|
||||
func generateFontID(fdt fontDefType) (string, error) {
|
||||
// file can be different if generated in different instance
|
||||
fdt.File = ""
|
||||
b, err := json.Marshal(&fdt)
|
||||
return fmt.Sprintf("%x", sha1.Sum(b)), err
|
||||
}
|
||||
|
||||
type fontInfoType struct {
|
||||
Data []byte
|
||||
File string
|
||||
OriginalSize int
|
||||
FontName string
|
||||
Bold bool
|
||||
IsFixedPitch bool
|
||||
UnderlineThickness int
|
||||
UnderlinePosition int
|
||||
Widths []int
|
||||
Size1, Size2 uint32
|
||||
Desc FontDescType
|
||||
}
|
268
vendor/github.com/jung-kurt/gofpdf/doc.go
generated
vendored
Normal file
268
vendor/github.com/jung-kurt/gofpdf/doc.go
generated
vendored
Normal file
@ -0,0 +1,268 @@
|
||||
/*
|
||||
Package gofpdf implements a PDF document generator with high level
|
||||
support for text, drawing and images.
|
||||
|
||||
|
||||
Features
|
||||
|
||||
|
||||
- UTF-8 support
|
||||
|
||||
- Choice of measurement unit, page format and margins
|
||||
|
||||
- Page header and footer management
|
||||
|
||||
- Automatic page breaks, line breaks, and text justification
|
||||
|
||||
- Inclusion of JPEG, PNG, GIF, TIFF and basic path-only SVG images
|
||||
|
||||
- Colors, gradients and alpha channel transparency
|
||||
|
||||
- Outline bookmarks
|
||||
|
||||
- Internal and external links
|
||||
|
||||
- TrueType, Type1 and encoding support
|
||||
|
||||
- Page compression
|
||||
|
||||
- Lines, Bézier curves, arcs, and ellipses
|
||||
|
||||
- Rotation, scaling, skewing, translation, and mirroring
|
||||
|
||||
- Clipping
|
||||
|
||||
- Document protection
|
||||
|
||||
- Layers
|
||||
|
||||
- Templates
|
||||
|
||||
- Barcodes
|
||||
|
||||
- Charting facility
|
||||
|
||||
- Import PDFs as templates
|
||||
|
||||
gofpdf has no dependencies other than the Go standard library. All tests
|
||||
pass on Linux, Mac and Windows platforms.
|
||||
|
||||
gofpdf supports UTF-8 TrueType fonts and “right-to-left” languages. Note
|
||||
that Chinese, Japanese, and Korean characters may not be included in
|
||||
many general purpose fonts. For these languages, a specialized font (for
|
||||
example, NotoSansSC for simplified Chinese) can be used.
|
||||
|
||||
Also, support is provided to automatically translate UTF-8 runes to code
|
||||
page encodings for languages that have fewer than 256 glyphs.
|
||||
|
||||
|
||||
Installation
|
||||
|
||||
To install the package on your system, run
|
||||
|
||||
go get github.com/jung-kurt/gofpdf
|
||||
|
||||
Later, to receive updates, run
|
||||
|
||||
go get -u -v github.com/jung-kurt/gofpdf/...
|
||||
|
||||
|
||||
Quick Start
|
||||
|
||||
The following Go code generates a simple PDF file.
|
||||
|
||||
pdf := gofpdf.New("P", "mm", "A4", "")
|
||||
pdf.AddPage()
|
||||
pdf.SetFont("Arial", "B", 16)
|
||||
pdf.Cell(40, 10, "Hello, world")
|
||||
err := pdf.OutputFileAndClose("hello.pdf")
|
||||
|
||||
See the functions in the fpdf_test.go file (shown as examples in this
|
||||
documentation) for more advanced PDF examples.
|
||||
|
||||
|
||||
Errors
|
||||
|
||||
If an error occurs in an Fpdf method, an internal error field is set.
|
||||
After this occurs, Fpdf method calls typically return without performing
|
||||
any operations and the error state is retained. This error management
|
||||
scheme facilitates PDF generation since individual method calls do not
|
||||
need to be examined for failure; it is generally sufficient to wait
|
||||
until after Output() is called. For the same reason, if an error occurs
|
||||
in the calling application during PDF generation, it may be desirable
|
||||
for the application to transfer the error to the Fpdf instance by
|
||||
calling the SetError() method or the SetErrorf() method. At any time
|
||||
during the life cycle of the Fpdf instance, the error state can be
|
||||
determined with a call to Ok() or Err(). The error itself can be
|
||||
retrieved with a call to Error().
|
||||
|
||||
|
||||
Conversion Notes
|
||||
|
||||
This package is a relatively straightforward translation from the
|
||||
original FPDF library written in PHP (despite the caveat in the
|
||||
introduction to Effective Go). The API names have been retained even
|
||||
though the Go idiom would suggest otherwise (for example, pdf.GetX() is
|
||||
used rather than simply pdf.X()). The similarity of the two libraries
|
||||
makes the original FPDF website a good source of information. It
|
||||
includes a forum and FAQ.
|
||||
|
||||
However, some internal changes have been made. Page content is built up
|
||||
using buffers (of type bytes.Buffer) rather than repeated string
|
||||
concatenation. Errors are handled as explained above rather than
|
||||
panicking. Output is generated through an interface of type io.Writer or
|
||||
io.WriteCloser. A number of the original PHP methods behave differently
|
||||
based on the type of the arguments that are passed to them; in these
|
||||
cases additional methods have been exported to provide similar
|
||||
functionality. Font definition files are produced in JSON rather than
|
||||
PHP.
|
||||
|
||||
|
||||
Example PDFs
|
||||
|
||||
A side effect of running go test ./... is the production of a number of
|
||||
example PDFs. These can be found in the gofpdf/pdf directory after the
|
||||
tests complete.
|
||||
|
||||
Please note that these examples run in the context of a test. In order
|
||||
run an example as a standalone application, you’ll need to examine
|
||||
fpdf_test.go for some helper routines, for example exampleFilename() and
|
||||
summary().
|
||||
|
||||
Example PDFs can be compared with reference copies in order to verify
|
||||
that they have been generated as expected. This comparison will be
|
||||
performed if a PDF with the same name as the example PDF is placed in
|
||||
the gofpdf/pdf/reference directory and if the third argument to
|
||||
ComparePDFFiles() in internal/example/example.go is true. (By default it
|
||||
is false.) The routine that summarizes an example will look for this
|
||||
file and, if found, will call ComparePDFFiles() to check the example PDF
|
||||
for equality with its reference PDF. If differences exist between the
|
||||
two files they will be printed to standard output and the test will
|
||||
fail. If the reference file is missing, the comparison is considered to
|
||||
succeed. In order to successfully compare two PDFs, the placement of
|
||||
internal resources must be consistent and the internal creation
|
||||
timestamps must be the same. To do this, the methods SetCatalogSort()
|
||||
and SetCreationDate() need to be called for both files. This is done
|
||||
automatically for all examples.
|
||||
|
||||
|
||||
Nonstandard Fonts
|
||||
|
||||
Nothing special is required to use the standard PDF fonts (courier,
|
||||
helvetica, times, zapfdingbats) in your documents other than calling
|
||||
SetFont().
|
||||
|
||||
You should use AddUTF8Font() or AddUTF8FontFromBytes() to add a TrueType
|
||||
UTF-8 encoded font. Use RTL() and LTR() methods switch between
|
||||
“right-to-left” and “left-to-right” mode.
|
||||
|
||||
In order to use a different non-UTF-8 TrueType or Type1 font, you will
|
||||
need to generate a font definition file and, if the font will be
|
||||
embedded into PDFs, a compressed version of the font file. This is done
|
||||
by calling the MakeFont function or using the included makefont command
|
||||
line utility. To create the utility, cd into the makefont subdirectory
|
||||
and run “go build”. This will produce a standalone executable named
|
||||
makefont. Select the appropriate encoding file from the font
|
||||
subdirectory and run the command as in the following example.
|
||||
|
||||
./makefont --embed --enc=../font/cp1252.map --dst=../font ../font/calligra.ttf
|
||||
|
||||
In your PDF generation code, call AddFont() to load the font and, as
|
||||
with the standard fonts, SetFont() to begin using it. Most examples,
|
||||
including the package example, demonstrate this method. Good sources of
|
||||
free, open-source fonts include Google Fonts and DejaVu Fonts.
|
||||
|
||||
|
||||
Related Packages
|
||||
|
||||
The draw2d package is a two dimensional vector graphics library that can
|
||||
generate output in different forms. It uses gofpdf for its document
|
||||
production mode.
|
||||
|
||||
|
||||
Contributing Changes
|
||||
|
||||
gofpdf is a global community effort and you are invited to make it even
|
||||
better. If you have implemented a new feature or corrected a problem,
|
||||
please consider contributing your change to the project. A contribution
|
||||
that does not directly pertain to the core functionality of gofpdf
|
||||
should be placed in its own directory directly beneath the contrib
|
||||
directory.
|
||||
|
||||
Here are guidelines for making submissions. Your change should
|
||||
|
||||
|
||||
- be compatible with the MIT License
|
||||
|
||||
- be properly documented
|
||||
|
||||
- be formatted with go fmt
|
||||
|
||||
- include an example in fpdf_test.go if appropriate
|
||||
|
||||
- conform to the standards of golint and go vet, that is, golint . and
|
||||
go vet . should not generate any warnings
|
||||
|
||||
- not diminish test coverage
|
||||
|
||||
Pull requests are the preferred means of accepting your changes.
|
||||
|
||||
|
||||
License
|
||||
|
||||
gofpdf is released under the MIT License. It is copyrighted by Kurt Jung
|
||||
and the contributors acknowledged below.
|
||||
|
||||
|
||||
Acknowledgments
|
||||
|
||||
This package’s code and documentation are closely derived from the FPDF
|
||||
library created by Olivier Plathey, and a number of font and image
|
||||
resources are copied directly from it. Bruno Michel has provided
|
||||
valuable assistance with the code. Drawing support is adapted from the
|
||||
FPDF geometric figures script by David Hernández Sanz. Transparency
|
||||
support is adapted from the FPDF transparency script by Martin Hall-May.
|
||||
Support for gradients and clipping is adapted from FPDF scripts by
|
||||
Andreas Würmser. Support for outline bookmarks is adapted from Olivier
|
||||
Plathey by Manuel Cornes. Layer support is adapted from Olivier Plathey.
|
||||
Support for transformations is adapted from the FPDF transformation
|
||||
script by Moritz Wagner and Andreas Würmser. PDF protection is adapted
|
||||
from the work of Klemen Vodopivec for the FPDF product. Lawrence
|
||||
Kesteloot provided code to allow an image’s extent to be determined
|
||||
prior to placement. Support for vertical alignment within a cell was
|
||||
provided by Stefan Schroeder. Ivan Daniluk generalized the font and
|
||||
image loading code to use the Reader interface while maintaining
|
||||
backward compatibility. Anthony Starks provided code for the Polygon
|
||||
function. Robert Lillack provided the Beziergon function and corrected
|
||||
some naming issues with the internal curve function. Claudio Felber
|
||||
provided implementations for dashed line drawing and generalized font
|
||||
loading. Stani Michiels provided support for multi-segment path drawing
|
||||
with smooth line joins, line join styles, enhanced fill modes, and has
|
||||
helped greatly with package presentation and tests. Templating is
|
||||
adapted by Marcus Downing from the FPDF_Tpl library created by Jan
|
||||
Slabon and Setasign. Jelmer Snoeck contributed packages that generate a
|
||||
variety of barcodes and help with registering images on the web. Jelmer
|
||||
Snoek and Guillermo Pascual augmented the basic HTML functionality with
|
||||
aligned text. Kent Quirk implemented backwards-compatible support for
|
||||
reading DPI from images that support it, and for setting DPI manually
|
||||
and then having it properly taken into account when calculating image
|
||||
size. Paulo Coutinho provided support for static embedded fonts. Dan
|
||||
Meyers added support for embedded JavaScript. David Fish added a generic
|
||||
alias-replacement function to enable, among other things, table of
|
||||
contents functionality. Andy Bakun identified and corrected a problem in
|
||||
which the internal catalogs were not sorted stably. Paul Montag added
|
||||
encoding and decoding functionality for templates, including images that
|
||||
are embedded in templates; this allows templates to be stored
|
||||
independently of gofpdf. Paul also added support for page boxes used in
|
||||
printing PDF documents. Wojciech Matusiak added supported for word
|
||||
spacing. Artem Korotkiy added support of UTF-8 fonts. Dave Barnes added
|
||||
support for imported objects and templates. Brigham Thompson added
|
||||
support for rounded rectangles.
|
||||
|
||||
|
||||
Roadmap
|
||||
|
||||
|
||||
- Improve test coverage as reported by the coverage tool.
|
||||
*/
|
||||
package gofpdf
|
559
vendor/github.com/jung-kurt/gofpdf/embedded.go
generated
vendored
Normal file
559
vendor/github.com/jung-kurt/gofpdf/embedded.go
generated
vendored
Normal file
@ -0,0 +1,559 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Kurt Jung (Gmail: kurt.w.jung)
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package gofpdf
|
||||
|
||||
// Embedded standard fonts
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var embeddedFontList = map[string]string{
|
||||
"courierBI": `{"Tp":"Core","Name":"Courier-BoldOblique","Up":-100,"Ut":50,"I":256,"Cw":[600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600]}`,
|
||||
"courierB": `{"Tp":"Core","Name":"Courier-Bold","Up":-100,"Ut":50,"I":256,"Cw":[600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600]}`,
|
||||
"courierI": `{"Tp":"Core","Name":"Courier-Oblique","Up":-100,"Ut":50,"I":256,"Cw":[600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600]}`,
|
||||
"courier": `{"Tp":"Core","Name":"Courier","Up":-100,"Ut":50,"I":256,"Cw":[600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600]}`,
|
||||
"helveticaBI": `{"Tp":"Core","Name":"Helvetica-BoldOblique","Up":-100,"Ut":50,"Cw":[278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,333,474,556,556,889,722,238,333,333,389,584,278,333,278,278,556,556,556,556,556,556,556,556,556,556,333,333,584,584,584,611,975,722,722,722,722,667,611,778,722,278,556,722,611,833,722,778,667,778,722,667,611,722,667,944,667,667,611,333,278,333,584,556,333,556,611,556,611,556,333,611,611,278,278,556,278,889,611,611,611,611,389,556,333,611,556,778,556,556,500,389,280,389,584,350,556,350,278,556,500,1000,556,556,333,1000,667,333,1000,350,611,350,350,278,278,500,500,350,556,1000,333,1000,556,333,944,350,500,667,278,333,556,556,556,556,280,556,333,737,370,556,584,333,737,333,400,584,333,333,333,611,556,278,333,333,365,556,834,834,834,611,722,722,722,722,722,722,1000,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722,722,667,667,611,556,556,556,556,556,556,889,556,556,556,556,556,278,278,278,278,611,611,611,611,611,611,611,584,611,611,611,611,611,556,611,556]}`,
|
||||
"helveticaB": `{"Tp":"Core","Name":"Helvetica-Bold","Up":-100,"Ut":50,"Cw":[278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,333,474,556,556,889,722,238,333,333,389,584,278,333,278,278,556,556,556,556,556,556,556,556,556,556,333,333,584,584,584,611,975,722,722,722,722,667,611,778,722,278,556,722,611,833,722,778,667,778,722,667,611,722,667,944,667,667,611,333,278,333,584,556,333,556,611,556,611,556,333,611,611,278,278,556,278,889,611,611,611,611,389,556,333,611,556,778,556,556,500,389,280,389,584,350,556,350,278,556,500,1000,556,556,333,1000,667,333,1000,350,611,350,350,278,278,500,500,350,556,1000,333,1000,556,333,944,350,500,667,278,333,556,556,556,556,280,556,333,737,370,556,584,333,737,333,400,584,333,333,333,611,556,278,333,333,365,556,834,834,834,611,722,722,722,722,722,722,1000,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722,722,667,667,611,556,556,556,556,556,556,889,556,556,556,556,556,278,278,278,278,611,611,611,611,611,611,611,584,611,611,611,611,611,556,611,556]}`,
|
||||
"helveticaI": `{"Tp":"Core","Name":"Helvetica-Oblique","Up":-100,"Ut":50,"Cw":[278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,355,556,556,889,667,191,333,333,389,584,278,333,278,278,556,556,556,556,556,556,556,556,556,556,278,278,584,584,584,556,1015,667,667,722,722,667,611,778,722,278,500,667,556,833,722,778,667,778,722,667,611,722,667,944,667,667,611,278,278,278,469,556,333,556,556,500,556,556,278,556,556,222,222,500,222,833,556,556,556,556,333,500,278,556,500,722,500,500,500,334,260,334,584,350,556,350,222,556,333,1000,556,556,333,1000,667,333,1000,350,611,350,350,222,222,333,333,350,556,1000,333,1000,500,333,944,350,500,667,278,333,556,556,556,556,260,556,333,737,370,556,584,333,737,333,400,584,333,333,333,556,537,278,333,333,365,556,834,834,834,611,667,667,667,667,667,667,1000,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722,722,667,667,611,556,556,556,556,556,556,889,500,556,556,556,556,278,278,278,278,556,556,556,556,556,556,556,584,611,556,556,556,556,500,556,500]}`,
|
||||
"helvetica": `{"Tp":"Core","Name":"Helvetica","Up":-100,"Ut":50,"Cw":[278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,355,556,556,889,667,191,333,333,389,584,278,333,278,278,556,556,556,556,556,556,556,556,556,556,278,278,584,584,584,556,1015,667,667,722,722,667,611,778,722,278,500,667,556,833,722,778,667,778,722,667,611,722,667,944,667,667,611,278,278,278,469,556,333,556,556,500,556,556,278,556,556,222,222,500,222,833,556,556,556,556,333,500,278,556,500,722,500,500,500,334,260,334,584,350,556,350,222,556,333,1000,556,556,333,1000,667,333,1000,350,611,350,350,222,222,333,333,350,556,1000,333,1000,500,333,944,350,500,667,278,333,556,556,556,556,260,556,333,737,370,556,584,333,737,333,400,584,333,333,333,556,537,278,333,333,365,556,834,834,834,611,667,667,667,667,667,667,1000,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722,722,667,667,611,556,556,556,556,556,556,889,500,556,556,556,556,278,278,278,278,556,556,556,556,556,556,556,584,611,556,556,556,556,500,556,500]}`,
|
||||
"timesBI": `{"Tp":"Core","Name":"Times-BoldItalic","Up":-100,"Ut":50,"Cw":[250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,389,555,500,500,833,778,278,333,333,500,570,250,333,250,278,500,500,500,500,500,500,500,500,500,500,333,333,570,570,570,500,832,667,667,667,722,667,667,722,778,389,500,667,611,889,722,722,611,722,667,556,611,722,667,889,667,611,611,333,278,333,570,500,333,500,500,444,500,444,333,500,556,278,278,500,278,778,556,500,500,500,389,389,278,556,444,667,500,444,389,348,220,348,570,350,500,350,333,500,500,1000,500,500,333,1000,556,333,944,350,611,350,350,333,333,500,500,350,500,1000,333,1000,389,333,722,350,389,611,250,389,500,500,500,500,220,500,333,747,266,500,606,333,747,333,400,570,300,300,333,576,500,250,333,300,300,500,750,750,750,500,667,667,667,667,667,667,944,667,667,667,667,667,389,389,389,389,722,722,722,722,722,722,722,570,722,722,722,722,722,611,611,500,500,500,500,500,500,500,722,444,444,444,444,444,278,278,278,278,500,556,500,500,500,500,500,570,500,556,556,556,556,444,500,444]}`,
|
||||
"timesB": `{"Tp":"Core","Name":"Times-Bold","Up":-100,"Ut":50,"Cw":[250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,333,555,500,500,1000,833,278,333,333,500,570,250,333,250,278,500,500,500,500,500,500,500,500,500,500,333,333,570,570,570,500,930,722,667,722,722,667,611,778,778,389,500,778,667,944,722,778,611,778,722,556,667,722,722,1000,722,722,667,333,278,333,581,500,333,500,556,444,556,444,333,500,556,278,333,556,278,833,556,500,556,556,444,389,333,556,500,722,500,500,444,394,220,394,520,350,500,350,333,500,500,1000,500,500,333,1000,556,333,1000,350,667,350,350,333,333,500,500,350,500,1000,333,1000,389,333,722,350,444,722,250,333,500,500,500,500,220,500,333,747,300,500,570,333,747,333,400,570,300,300,333,556,540,250,333,300,330,500,750,750,750,500,722,722,722,722,722,722,1000,722,667,667,667,667,389,389,389,389,722,722,778,778,778,778,778,570,778,722,722,722,722,722,611,556,500,500,500,500,500,500,722,444,444,444,444,444,278,278,278,278,500,556,500,500,500,500,500,570,500,556,556,556,556,500,556,500]}`,
|
||||
"timesI": `{"Tp":"Core","Name":"Times-Italic","Up":-100,"Ut":50,"Cw":[250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,333,420,500,500,833,778,214,333,333,500,675,250,333,250,278,500,500,500,500,500,500,500,500,500,500,333,333,675,675,675,500,920,611,611,667,722,611,611,722,722,333,444,667,556,833,667,722,611,722,611,500,556,722,611,833,611,556,556,389,278,389,422,500,333,500,500,444,500,444,278,500,500,278,278,444,278,722,500,500,500,500,389,389,278,500,444,667,444,444,389,400,275,400,541,350,500,350,333,500,556,889,500,500,333,1000,500,333,944,350,556,350,350,333,333,556,556,350,500,889,333,980,389,333,667,350,389,556,250,389,500,500,500,500,275,500,333,760,276,500,675,333,760,333,400,675,300,300,333,500,523,250,333,300,310,500,750,750,750,500,611,611,611,611,611,611,889,667,611,611,611,611,333,333,333,333,722,667,722,722,722,722,722,675,722,722,722,722,722,556,611,500,500,500,500,500,500,500,667,444,444,444,444,444,278,278,278,278,500,500,500,500,500,500,500,675,500,500,500,500,500,444,500,444]}`,
|
||||
"times": `{"Tp":"Core","Name":"Times-Roman","Up":-100,"Ut":50,"Cw":[250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,333,408,500,500,833,778,180,333,333,500,564,250,333,250,278,500,500,500,500,500,500,500,500,500,500,278,278,564,564,564,444,921,722,667,667,722,611,556,722,722,333,389,722,611,889,722,722,556,722,667,556,611,722,722,944,722,722,611,333,278,333,469,500,333,444,500,444,500,444,333,500,500,278,278,500,278,778,500,500,500,500,333,389,278,500,500,722,500,500,444,480,200,480,541,350,500,350,333,500,444,1000,500,500,333,1000,556,333,889,350,611,350,350,333,333,444,444,350,500,1000,333,980,389,333,722,350,444,722,250,333,500,500,500,500,200,500,333,760,276,500,564,333,760,333,400,564,300,300,333,500,453,250,333,300,310,500,750,750,750,444,722,722,722,722,722,722,889,667,611,611,611,611,333,333,333,333,722,722,722,722,722,722,722,564,722,722,722,722,722,722,556,500,444,444,444,444,444,444,667,444,444,444,444,444,278,278,278,278,500,500,500,500,500,500,500,564,500,500,500,500,500,500,500,500]}`,
|
||||
"zapfdingbats": `{"Tp":"Core","Name":"ZapfDingbats","Up":-100,"Ut":50,"Cw":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,278,974,961,974,980,719,789,790,791,690,960,939,549,855,911,933,911,945,974,755,846,762,761,571,677,763,760,759,754,494,552,537,577,692,786,788,788,790,793,794,816,823,789,841,823,833,816,831,923,744,723,749,790,792,695,776,768,792,759,707,708,682,701,826,815,789,789,707,687,696,689,786,787,713,791,785,791,873,761,762,762,759,759,892,892,788,784,438,138,277,415,392,392,668,668,0,390,390,317,317,276,276,509,509,410,410,234,234,334,334,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,732,544,544,910,667,760,760,776,595,694,626,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,894,838,1016,458,748,924,748,918,927,928,928,834,873,828,924,924,917,930,931,463,883,836,836,867,867,696,696,874,0,874,760,946,771,865,771,888,967,888,831,873,927,970,918,0]}`,
|
||||
}
|
||||
|
||||
func (f *Fpdf) coreFontReader(familyStr, styleStr string) (r *strings.Reader) {
|
||||
key := familyStr + styleStr
|
||||
str, ok := embeddedFontList[key]
|
||||
if ok {
|
||||
r = strings.NewReader(str)
|
||||
} else {
|
||||
f.SetErrorf("could not locate \"%s\" among embedded core font definition files", key)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var embeddedMapList = map[string]string{
|
||||
"cp1250": `
|
||||
!00 U+0000 .notdef
|
||||
!01 U+0001 .notdef
|
||||
!02 U+0002 .notdef
|
||||
!03 U+0003 .notdef
|
||||
!04 U+0004 .notdef
|
||||
!05 U+0005 .notdef
|
||||
!06 U+0006 .notdef
|
||||
!07 U+0007 .notdef
|
||||
!08 U+0008 .notdef
|
||||
!09 U+0009 .notdef
|
||||
!0A U+000A .notdef
|
||||
!0B U+000B .notdef
|
||||
!0C U+000C .notdef
|
||||
!0D U+000D .notdef
|
||||
!0E U+000E .notdef
|
||||
!0F U+000F .notdef
|
||||
!10 U+0010 .notdef
|
||||
!11 U+0011 .notdef
|
||||
!12 U+0012 .notdef
|
||||
!13 U+0013 .notdef
|
||||
!14 U+0014 .notdef
|
||||
!15 U+0015 .notdef
|
||||
!16 U+0016 .notdef
|
||||
!17 U+0017 .notdef
|
||||
!18 U+0018 .notdef
|
||||
!19 U+0019 .notdef
|
||||
!1A U+001A .notdef
|
||||
!1B U+001B .notdef
|
||||
!1C U+001C .notdef
|
||||
!1D U+001D .notdef
|
||||
!1E U+001E .notdef
|
||||
!1F U+001F .notdef
|
||||
!20 U+0020 space
|
||||
!21 U+0021 exclam
|
||||
!22 U+0022 quotedbl
|
||||
!23 U+0023 numbersign
|
||||
!24 U+0024 dollar
|
||||
!25 U+0025 percent
|
||||
!26 U+0026 ampersand
|
||||
!27 U+0027 quotesingle
|
||||
!28 U+0028 parenleft
|
||||
!29 U+0029 parenright
|
||||
!2A U+002A asterisk
|
||||
!2B U+002B plus
|
||||
!2C U+002C comma
|
||||
!2D U+002D hyphen
|
||||
!2E U+002E period
|
||||
!2F U+002F slash
|
||||
!30 U+0030 zero
|
||||
!31 U+0031 one
|
||||
!32 U+0032 two
|
||||
!33 U+0033 three
|
||||
!34 U+0034 four
|
||||
!35 U+0035 five
|
||||
!36 U+0036 six
|
||||
!37 U+0037 seven
|
||||
!38 U+0038 eight
|
||||
!39 U+0039 nine
|
||||
!3A U+003A colon
|
||||
!3B U+003B semicolon
|
||||
!3C U+003C less
|
||||
!3D U+003D equal
|
||||
!3E U+003E greater
|
||||
!3F U+003F question
|
||||
!40 U+0040 at
|
||||
!41 U+0041 A
|
||||
!42 U+0042 B
|
||||
!43 U+0043 C
|
||||
!44 U+0044 D
|
||||
!45 U+0045 E
|
||||
!46 U+0046 F
|
||||
!47 U+0047 G
|
||||
!48 U+0048 H
|
||||
!49 U+0049 I
|
||||
!4A U+004A J
|
||||
!4B U+004B K
|
||||
!4C U+004C L
|
||||
!4D U+004D M
|
||||
!4E U+004E N
|
||||
!4F U+004F O
|
||||
!50 U+0050 P
|
||||
!51 U+0051 Q
|
||||
!52 U+0052 R
|
||||
!53 U+0053 S
|
||||
!54 U+0054 T
|
||||
!55 U+0055 U
|
||||
!56 U+0056 V
|
||||
!57 U+0057 W
|
||||
!58 U+0058 X
|
||||
!59 U+0059 Y
|
||||
!5A U+005A Z
|
||||
!5B U+005B bracketleft
|
||||
!5C U+005C backslash
|
||||
!5D U+005D bracketright
|
||||
!5E U+005E asciicircum
|
||||
!5F U+005F underscore
|
||||
!60 U+0060 grave
|
||||
!61 U+0061 a
|
||||
!62 U+0062 b
|
||||
!63 U+0063 c
|
||||
!64 U+0064 d
|
||||
!65 U+0065 e
|
||||
!66 U+0066 f
|
||||
!67 U+0067 g
|
||||
!68 U+0068 h
|
||||
!69 U+0069 i
|
||||
!6A U+006A j
|
||||
!6B U+006B k
|
||||
!6C U+006C l
|
||||
!6D U+006D m
|
||||
!6E U+006E n
|
||||
!6F U+006F o
|
||||
!70 U+0070 p
|
||||
!71 U+0071 q
|
||||
!72 U+0072 r
|
||||
!73 U+0073 s
|
||||
!74 U+0074 t
|
||||
!75 U+0075 u
|
||||
!76 U+0076 v
|
||||
!77 U+0077 w
|
||||
!78 U+0078 x
|
||||
!79 U+0079 y
|
||||
!7A U+007A z
|
||||
!7B U+007B braceleft
|
||||
!7C U+007C bar
|
||||
!7D U+007D braceright
|
||||
!7E U+007E asciitilde
|
||||
!7F U+007F .notdef
|
||||
!80 U+20AC Euro
|
||||
!82 U+201A quotesinglbase
|
||||
!84 U+201E quotedblbase
|
||||
!85 U+2026 ellipsis
|
||||
!86 U+2020 dagger
|
||||
!87 U+2021 daggerdbl
|
||||
!89 U+2030 perthousand
|
||||
!8A U+0160 Scaron
|
||||
!8B U+2039 guilsinglleft
|
||||
!8C U+015A Sacute
|
||||
!8D U+0164 Tcaron
|
||||
!8E U+017D Zcaron
|
||||
!8F U+0179 Zacute
|
||||
!91 U+2018 quoteleft
|
||||
!92 U+2019 quoteright
|
||||
!93 U+201C quotedblleft
|
||||
!94 U+201D quotedblright
|
||||
!95 U+2022 bullet
|
||||
!96 U+2013 endash
|
||||
!97 U+2014 emdash
|
||||
!99 U+2122 trademark
|
||||
!9A U+0161 scaron
|
||||
!9B U+203A guilsinglright
|
||||
!9C U+015B sacute
|
||||
!9D U+0165 tcaron
|
||||
!9E U+017E zcaron
|
||||
!9F U+017A zacute
|
||||
!A0 U+00A0 space
|
||||
!A1 U+02C7 caron
|
||||
!A2 U+02D8 breve
|
||||
!A3 U+0141 Lslash
|
||||
!A4 U+00A4 currency
|
||||
!A5 U+0104 Aogonek
|
||||
!A6 U+00A6 brokenbar
|
||||
!A7 U+00A7 section
|
||||
!A8 U+00A8 dieresis
|
||||
!A9 U+00A9 copyright
|
||||
!AA U+015E Scedilla
|
||||
!AB U+00AB guillemotleft
|
||||
!AC U+00AC logicalnot
|
||||
!AD U+00AD hyphen
|
||||
!AE U+00AE registered
|
||||
!AF U+017B Zdotaccent
|
||||
!B0 U+00B0 degree
|
||||
!B1 U+00B1 plusminus
|
||||
!B2 U+02DB ogonek
|
||||
!B3 U+0142 lslash
|
||||
!B4 U+00B4 acute
|
||||
!B5 U+00B5 mu
|
||||
!B6 U+00B6 paragraph
|
||||
!B7 U+00B7 periodcentered
|
||||
!B8 U+00B8 cedilla
|
||||
!B9 U+0105 aogonek
|
||||
!BA U+015F scedilla
|
||||
!BB U+00BB guillemotright
|
||||
!BC U+013D Lcaron
|
||||
!BD U+02DD hungarumlaut
|
||||
!BE U+013E lcaron
|
||||
!BF U+017C zdotaccent
|
||||
!C0 U+0154 Racute
|
||||
!C1 U+00C1 Aacute
|
||||
!C2 U+00C2 Acircumflex
|
||||
!C3 U+0102 Abreve
|
||||
!C4 U+00C4 Adieresis
|
||||
!C5 U+0139 Lacute
|
||||
!C6 U+0106 Cacute
|
||||
!C7 U+00C7 Ccedilla
|
||||
!C8 U+010C Ccaron
|
||||
!C9 U+00C9 Eacute
|
||||
!CA U+0118 Eogonek
|
||||
!CB U+00CB Edieresis
|
||||
!CC U+011A Ecaron
|
||||
!CD U+00CD Iacute
|
||||
!CE U+00CE Icircumflex
|
||||
!CF U+010E Dcaron
|
||||
!D0 U+0110 Dcroat
|
||||
!D1 U+0143 Nacute
|
||||
!D2 U+0147 Ncaron
|
||||
!D3 U+00D3 Oacute
|
||||
!D4 U+00D4 Ocircumflex
|
||||
!D5 U+0150 Ohungarumlaut
|
||||
!D6 U+00D6 Odieresis
|
||||
!D7 U+00D7 multiply
|
||||
!D8 U+0158 Rcaron
|
||||
!D9 U+016E Uring
|
||||
!DA U+00DA Uacute
|
||||
!DB U+0170 Uhungarumlaut
|
||||
!DC U+00DC Udieresis
|
||||
!DD U+00DD Yacute
|
||||
!DE U+0162 Tcommaaccent
|
||||
!DF U+00DF germandbls
|
||||
!E0 U+0155 racute
|
||||
!E1 U+00E1 aacute
|
||||
!E2 U+00E2 acircumflex
|
||||
!E3 U+0103 abreve
|
||||
!E4 U+00E4 adieresis
|
||||
!E5 U+013A lacute
|
||||
!E6 U+0107 cacute
|
||||
!E7 U+00E7 ccedilla
|
||||
!E8 U+010D ccaron
|
||||
!E9 U+00E9 eacute
|
||||
!EA U+0119 eogonek
|
||||
!EB U+00EB edieresis
|
||||
!EC U+011B ecaron
|
||||
!ED U+00ED iacute
|
||||
!EE U+00EE icircumflex
|
||||
!EF U+010F dcaron
|
||||
!F0 U+0111 dcroat
|
||||
!F1 U+0144 nacute
|
||||
!F2 U+0148 ncaron
|
||||
!F3 U+00F3 oacute
|
||||
!F4 U+00F4 ocircumflex
|
||||
!F5 U+0151 ohungarumlaut
|
||||
!F6 U+00F6 odieresis
|
||||
!F7 U+00F7 divide
|
||||
!F8 U+0159 rcaron
|
||||
!F9 U+016F uring
|
||||
!FA U+00FA uacute
|
||||
!FB U+0171 uhungarumlaut
|
||||
!FC U+00FC udieresis
|
||||
!FD U+00FD yacute
|
||||
!FE U+0163 tcommaaccent
|
||||
!FF U+02D9 dotaccent
|
||||
`,
|
||||
"cp1252": `
|
||||
!00 U+0000 .notdef
|
||||
!01 U+0001 .notdef
|
||||
!02 U+0002 .notdef
|
||||
!03 U+0003 .notdef
|
||||
!04 U+0004 .notdef
|
||||
!05 U+0005 .notdef
|
||||
!06 U+0006 .notdef
|
||||
!07 U+0007 .notdef
|
||||
!08 U+0008 .notdef
|
||||
!09 U+0009 .notdef
|
||||
!0A U+000A .notdef
|
||||
!0B U+000B .notdef
|
||||
!0C U+000C .notdef
|
||||
!0D U+000D .notdef
|
||||
!0E U+000E .notdef
|
||||
!0F U+000F .notdef
|
||||
!10 U+0010 .notdef
|
||||
!11 U+0011 .notdef
|
||||
!12 U+0012 .notdef
|
||||
!13 U+0013 .notdef
|
||||
!14 U+0014 .notdef
|
||||
!15 U+0015 .notdef
|
||||
!16 U+0016 .notdef
|
||||
!17 U+0017 .notdef
|
||||
!18 U+0018 .notdef
|
||||
!19 U+0019 .notdef
|
||||
!1A U+001A .notdef
|
||||
!1B U+001B .notdef
|
||||
!1C U+001C .notdef
|
||||
!1D U+001D .notdef
|
||||
!1E U+001E .notdef
|
||||
!1F U+001F .notdef
|
||||
!20 U+0020 space
|
||||
!21 U+0021 exclam
|
||||
!22 U+0022 quotedbl
|
||||
!23 U+0023 numbersign
|
||||
!24 U+0024 dollar
|
||||
!25 U+0025 percent
|
||||
!26 U+0026 ampersand
|
||||
!27 U+0027 quotesingle
|
||||
!28 U+0028 parenleft
|
||||
!29 U+0029 parenright
|
||||
!2A U+002A asterisk
|
||||
!2B U+002B plus
|
||||
!2C U+002C comma
|
||||
!2D U+002D hyphen
|
||||
!2E U+002E period
|
||||
!2F U+002F slash
|
||||
!30 U+0030 zero
|
||||
!31 U+0031 one
|
||||
!32 U+0032 two
|
||||
!33 U+0033 three
|
||||
!34 U+0034 four
|
||||
!35 U+0035 five
|
||||
!36 U+0036 six
|
||||
!37 U+0037 seven
|
||||
!38 U+0038 eight
|
||||
!39 U+0039 nine
|
||||
!3A U+003A colon
|
||||
!3B U+003B semicolon
|
||||
!3C U+003C less
|
||||
!3D U+003D equal
|
||||
!3E U+003E greater
|
||||
!3F U+003F question
|
||||
!40 U+0040 at
|
||||
!41 U+0041 A
|
||||
!42 U+0042 B
|
||||
!43 U+0043 C
|
||||
!44 U+0044 D
|
||||
!45 U+0045 E
|
||||
!46 U+0046 F
|
||||
!47 U+0047 G
|
||||
!48 U+0048 H
|
||||
!49 U+0049 I
|
||||
!4A U+004A J
|
||||
!4B U+004B K
|
||||
!4C U+004C L
|
||||
!4D U+004D M
|
||||
!4E U+004E N
|
||||
!4F U+004F O
|
||||
!50 U+0050 P
|
||||
!51 U+0051 Q
|
||||
!52 U+0052 R
|
||||
!53 U+0053 S
|
||||
!54 U+0054 T
|
||||
!55 U+0055 U
|
||||
!56 U+0056 V
|
||||
!57 U+0057 W
|
||||
!58 U+0058 X
|
||||
!59 U+0059 Y
|
||||
!5A U+005A Z
|
||||
!5B U+005B bracketleft
|
||||
!5C U+005C backslash
|
||||
!5D U+005D bracketright
|
||||
!5E U+005E asciicircum
|
||||
!5F U+005F underscore
|
||||
!60 U+0060 grave
|
||||
!61 U+0061 a
|
||||
!62 U+0062 b
|
||||
!63 U+0063 c
|
||||
!64 U+0064 d
|
||||
!65 U+0065 e
|
||||
!66 U+0066 f
|
||||
!67 U+0067 g
|
||||
!68 U+0068 h
|
||||
!69 U+0069 i
|
||||
!6A U+006A j
|
||||
!6B U+006B k
|
||||
!6C U+006C l
|
||||
!6D U+006D m
|
||||
!6E U+006E n
|
||||
!6F U+006F o
|
||||
!70 U+0070 p
|
||||
!71 U+0071 q
|
||||
!72 U+0072 r
|
||||
!73 U+0073 s
|
||||
!74 U+0074 t
|
||||
!75 U+0075 u
|
||||
!76 U+0076 v
|
||||
!77 U+0077 w
|
||||
!78 U+0078 x
|
||||
!79 U+0079 y
|
||||
!7A U+007A z
|
||||
!7B U+007B braceleft
|
||||
!7C U+007C bar
|
||||
!7D U+007D braceright
|
||||
!7E U+007E asciitilde
|
||||
!7F U+007F .notdef
|
||||
!80 U+20AC Euro
|
||||
!82 U+201A quotesinglbase
|
||||
!83 U+0192 florin
|
||||
!84 U+201E quotedblbase
|
||||
!85 U+2026 ellipsis
|
||||
!86 U+2020 dagger
|
||||
!87 U+2021 daggerdbl
|
||||
!88 U+02C6 circumflex
|
||||
!89 U+2030 perthousand
|
||||
!8A U+0160 Scaron
|
||||
!8B U+2039 guilsinglleft
|
||||
!8C U+0152 OE
|
||||
!8E U+017D Zcaron
|
||||
!91 U+2018 quoteleft
|
||||
!92 U+2019 quoteright
|
||||
!93 U+201C quotedblleft
|
||||
!94 U+201D quotedblright
|
||||
!95 U+2022 bullet
|
||||
!96 U+2013 endash
|
||||
!97 U+2014 emdash
|
||||
!98 U+02DC tilde
|
||||
!99 U+2122 trademark
|
||||
!9A U+0161 scaron
|
||||
!9B U+203A guilsinglright
|
||||
!9C U+0153 oe
|
||||
!9E U+017E zcaron
|
||||
!9F U+0178 Ydieresis
|
||||
!A0 U+00A0 space
|
||||
!A1 U+00A1 exclamdown
|
||||
!A2 U+00A2 cent
|
||||
!A3 U+00A3 sterling
|
||||
!A4 U+00A4 currency
|
||||
!A5 U+00A5 yen
|
||||
!A6 U+00A6 brokenbar
|
||||
!A7 U+00A7 section
|
||||
!A8 U+00A8 dieresis
|
||||
!A9 U+00A9 copyright
|
||||
!AA U+00AA ordfeminine
|
||||
!AB U+00AB guillemotleft
|
||||
!AC U+00AC logicalnot
|
||||
!AD U+00AD hyphen
|
||||
!AE U+00AE registered
|
||||
!AF U+00AF macron
|
||||
!B0 U+00B0 degree
|
||||
!B1 U+00B1 plusminus
|
||||
!B2 U+00B2 twosuperior
|
||||
!B3 U+00B3 threesuperior
|
||||
!B4 U+00B4 acute
|
||||
!B5 U+00B5 mu
|
||||
!B6 U+00B6 paragraph
|
||||
!B7 U+00B7 periodcentered
|
||||
!B8 U+00B8 cedilla
|
||||
!B9 U+00B9 onesuperior
|
||||
!BA U+00BA ordmasculine
|
||||
!BB U+00BB guillemotright
|
||||
!BC U+00BC onequarter
|
||||
!BD U+00BD onehalf
|
||||
!BE U+00BE threequarters
|
||||
!BF U+00BF questiondown
|
||||
!C0 U+00C0 Agrave
|
||||
!C1 U+00C1 Aacute
|
||||
!C2 U+00C2 Acircumflex
|
||||
!C3 U+00C3 Atilde
|
||||
!C4 U+00C4 Adieresis
|
||||
!C5 U+00C5 Aring
|
||||
!C6 U+00C6 AE
|
||||
!C7 U+00C7 Ccedilla
|
||||
!C8 U+00C8 Egrave
|
||||
!C9 U+00C9 Eacute
|
||||
!CA U+00CA Ecircumflex
|
||||
!CB U+00CB Edieresis
|
||||
!CC U+00CC Igrave
|
||||
!CD U+00CD Iacute
|
||||
!CE U+00CE Icircumflex
|
||||
!CF U+00CF Idieresis
|
||||
!D0 U+00D0 Eth
|
||||
!D1 U+00D1 Ntilde
|
||||
!D2 U+00D2 Ograve
|
||||
!D3 U+00D3 Oacute
|
||||
!D4 U+00D4 Ocircumflex
|
||||
!D5 U+00D5 Otilde
|
||||
!D6 U+00D6 Odieresis
|
||||
!D7 U+00D7 multiply
|
||||
!D8 U+00D8 Oslash
|
||||
!D9 U+00D9 Ugrave
|
||||
!DA U+00DA Uacute
|
||||
!DB U+00DB Ucircumflex
|
||||
!DC U+00DC Udieresis
|
||||
!DD U+00DD Yacute
|
||||
!DE U+00DE Thorn
|
||||
!DF U+00DF germandbls
|
||||
!E0 U+00E0 agrave
|
||||
!E1 U+00E1 aacute
|
||||
!E2 U+00E2 acircumflex
|
||||
!E3 U+00E3 atilde
|
||||
!E4 U+00E4 adieresis
|
||||
!E5 U+00E5 aring
|
||||
!E6 U+00E6 ae
|
||||
!E7 U+00E7 ccedilla
|
||||
!E8 U+00E8 egrave
|
||||
!E9 U+00E9 eacute
|
||||
!EA U+00EA ecircumflex
|
||||
!EB U+00EB edieresis
|
||||
!EC U+00EC igrave
|
||||
!ED U+00ED iacute
|
||||
!EE U+00EE icircumflex
|
||||
!EF U+00EF idieresis
|
||||
!F0 U+00F0 eth
|
||||
!F1 U+00F1 ntilde
|
||||
!F2 U+00F2 ograve
|
||||
!F3 U+00F3 oacute
|
||||
!F4 U+00F4 ocircumflex
|
||||
!F5 U+00F5 otilde
|
||||
!F6 U+00F6 odieresis
|
||||
!F7 U+00F7 divide
|
||||
!F8 U+00F8 oslash
|
||||
!F9 U+00F9 ugrave
|
||||
!FA U+00FA uacute
|
||||
!FB U+00FB ucircumflex
|
||||
!FC U+00FC udieresis
|
||||
!FD U+00FD yacute
|
||||
!FE U+00FE thorn
|
||||
!FF U+00FF ydieresis
|
||||
`,
|
||||
}
|
474
vendor/github.com/jung-kurt/gofpdf/font.go
generated
vendored
Normal file
474
vendor/github.com/jung-kurt/gofpdf/font.go
generated
vendored
Normal file
@ -0,0 +1,474 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Kurt Jung (Gmail: kurt.w.jung)
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package gofpdf
|
||||
|
||||
// Utility to generate font definition files
|
||||
|
||||
// Version: 1.2
|
||||
// Date: 2011-06-18
|
||||
// Author: Olivier PLATHEY
|
||||
// Port to Go: Kurt Jung, 2013-07-15
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"compress/zlib"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func baseNoExt(fileStr string) string {
|
||||
str := filepath.Base(fileStr)
|
||||
extLen := len(filepath.Ext(str))
|
||||
if extLen > 0 {
|
||||
str = str[:len(str)-extLen]
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
func loadMap(encodingFileStr string) (encList encListType, err error) {
|
||||
// printf("Encoding file string [%s]\n", encodingFileStr)
|
||||
var f *os.File
|
||||
// f, err = os.Open(encodingFilepath(encodingFileStr))
|
||||
f, err = os.Open(encodingFileStr)
|
||||
if err == nil {
|
||||
defer f.Close()
|
||||
for j := range encList {
|
||||
encList[j].uv = -1
|
||||
encList[j].name = ".notdef"
|
||||
}
|
||||
scanner := bufio.NewScanner(f)
|
||||
var enc encType
|
||||
var pos int
|
||||
for scanner.Scan() {
|
||||
// "!3F U+003F question"
|
||||
_, err = fmt.Sscanf(scanner.Text(), "!%x U+%x %s", &pos, &enc.uv, &enc.name)
|
||||
if err == nil {
|
||||
if pos < 256 {
|
||||
encList[pos] = enc
|
||||
} else {
|
||||
err = fmt.Errorf("map position 0x%2X exceeds 0xFF", pos)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
if err = scanner.Err(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// getInfoFromTrueType returns information from a TrueType font
|
||||
func getInfoFromTrueType(fileStr string, msgWriter io.Writer, embed bool, encList encListType) (info fontInfoType, err error) {
|
||||
info.Widths = make([]int, 256)
|
||||
var ttf TtfType
|
||||
ttf, err = TtfParse(fileStr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if embed {
|
||||
if !ttf.Embeddable {
|
||||
err = fmt.Errorf("font license does not allow embedding")
|
||||
return
|
||||
}
|
||||
info.Data, err = ioutil.ReadFile(fileStr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
info.OriginalSize = len(info.Data)
|
||||
}
|
||||
k := 1000.0 / float64(ttf.UnitsPerEm)
|
||||
info.FontName = ttf.PostScriptName
|
||||
info.Bold = ttf.Bold
|
||||
info.Desc.ItalicAngle = int(ttf.ItalicAngle)
|
||||
info.IsFixedPitch = ttf.IsFixedPitch
|
||||
info.Desc.Ascent = round(k * float64(ttf.TypoAscender))
|
||||
info.Desc.Descent = round(k * float64(ttf.TypoDescender))
|
||||
info.UnderlineThickness = round(k * float64(ttf.UnderlineThickness))
|
||||
info.UnderlinePosition = round(k * float64(ttf.UnderlinePosition))
|
||||
info.Desc.FontBBox = fontBoxType{
|
||||
round(k * float64(ttf.Xmin)),
|
||||
round(k * float64(ttf.Ymin)),
|
||||
round(k * float64(ttf.Xmax)),
|
||||
round(k * float64(ttf.Ymax)),
|
||||
}
|
||||
// printf("FontBBox\n")
|
||||
// dump(info.Desc.FontBBox)
|
||||
info.Desc.CapHeight = round(k * float64(ttf.CapHeight))
|
||||
info.Desc.MissingWidth = round(k * float64(ttf.Widths[0]))
|
||||
var wd int
|
||||
for j := 0; j < len(info.Widths); j++ {
|
||||
wd = info.Desc.MissingWidth
|
||||
if encList[j].name != ".notdef" {
|
||||
uv := encList[j].uv
|
||||
pos, ok := ttf.Chars[uint16(uv)]
|
||||
if ok {
|
||||
wd = round(k * float64(ttf.Widths[pos]))
|
||||
} else {
|
||||
fmt.Fprintf(msgWriter, "Character %s is missing\n", encList[j].name)
|
||||
}
|
||||
}
|
||||
info.Widths[j] = wd
|
||||
}
|
||||
// printf("getInfoFromTrueType/FontBBox\n")
|
||||
// dump(info.Desc.FontBBox)
|
||||
return
|
||||
}
|
||||
|
||||
type segmentType struct {
|
||||
marker uint8
|
||||
tp uint8
|
||||
size uint32
|
||||
data []byte
|
||||
}
|
||||
|
||||
func segmentRead(r io.Reader) (s segmentType, err error) {
|
||||
if err = binary.Read(r, binary.LittleEndian, &s.marker); err != nil {
|
||||
return
|
||||
}
|
||||
if s.marker != 128 {
|
||||
err = fmt.Errorf("font file is not a valid binary Type1")
|
||||
return
|
||||
}
|
||||
if err = binary.Read(r, binary.LittleEndian, &s.tp); err != nil {
|
||||
return
|
||||
}
|
||||
if err = binary.Read(r, binary.LittleEndian, &s.size); err != nil {
|
||||
return
|
||||
}
|
||||
s.data = make([]byte, s.size)
|
||||
_, err = r.Read(s.data)
|
||||
return
|
||||
}
|
||||
|
||||
// -rw-r--r-- 1 root root 9532 2010-04-22 11:27 /usr/share/fonts/type1/mathml/Symbol.afm
|
||||
// -rw-r--r-- 1 root root 37744 2010-04-22 11:27 /usr/share/fonts/type1/mathml/Symbol.pfb
|
||||
|
||||
// getInfoFromType1 return information from a Type1 font
|
||||
func getInfoFromType1(fileStr string, msgWriter io.Writer, embed bool, encList encListType) (info fontInfoType, err error) {
|
||||
info.Widths = make([]int, 256)
|
||||
if embed {
|
||||
var f *os.File
|
||||
f, err = os.Open(fileStr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
// Read first segment
|
||||
var s1, s2 segmentType
|
||||
s1, err = segmentRead(f)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s2, err = segmentRead(f)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
info.Data = s1.data
|
||||
info.Data = append(info.Data, s2.data...)
|
||||
info.Size1 = s1.size
|
||||
info.Size2 = s2.size
|
||||
}
|
||||
afmFileStr := fileStr[0:len(fileStr)-3] + "afm"
|
||||
size, ok := fileSize(afmFileStr)
|
||||
if !ok {
|
||||
err = fmt.Errorf("font file (ATM) %s not found", afmFileStr)
|
||||
return
|
||||
} else if size == 0 {
|
||||
err = fmt.Errorf("font file (AFM) %s empty or not readable", afmFileStr)
|
||||
return
|
||||
}
|
||||
var f *os.File
|
||||
f, err = os.Open(afmFileStr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
scanner := bufio.NewScanner(f)
|
||||
var fields []string
|
||||
var wd int
|
||||
var wt, name string
|
||||
wdMap := make(map[string]int)
|
||||
for scanner.Scan() {
|
||||
fields = strings.Fields(strings.TrimSpace(scanner.Text()))
|
||||
// Comment Generated by FontForge 20080203
|
||||
// FontName Symbol
|
||||
// C 32 ; WX 250 ; N space ; B 0 0 0 0 ;
|
||||
if len(fields) >= 2 {
|
||||
switch fields[0] {
|
||||
case "C":
|
||||
if wd, err = strconv.Atoi(fields[4]); err == nil {
|
||||
name = fields[7]
|
||||
wdMap[name] = wd
|
||||
}
|
||||
case "FontName":
|
||||
info.FontName = fields[1]
|
||||
case "Weight":
|
||||
wt = strings.ToLower(fields[1])
|
||||
case "ItalicAngle":
|
||||
info.Desc.ItalicAngle, err = strconv.Atoi(fields[1])
|
||||
case "Ascender":
|
||||
info.Desc.Ascent, err = strconv.Atoi(fields[1])
|
||||
case "Descender":
|
||||
info.Desc.Descent, err = strconv.Atoi(fields[1])
|
||||
case "UnderlineThickness":
|
||||
info.UnderlineThickness, err = strconv.Atoi(fields[1])
|
||||
case "UnderlinePosition":
|
||||
info.UnderlinePosition, err = strconv.Atoi(fields[1])
|
||||
case "IsFixedPitch":
|
||||
info.IsFixedPitch = fields[1] == "true"
|
||||
case "FontBBox":
|
||||
if info.Desc.FontBBox.Xmin, err = strconv.Atoi(fields[1]); err == nil {
|
||||
if info.Desc.FontBBox.Ymin, err = strconv.Atoi(fields[2]); err == nil {
|
||||
if info.Desc.FontBBox.Xmax, err = strconv.Atoi(fields[3]); err == nil {
|
||||
info.Desc.FontBBox.Ymax, err = strconv.Atoi(fields[4])
|
||||
}
|
||||
}
|
||||
}
|
||||
case "CapHeight":
|
||||
info.Desc.CapHeight, err = strconv.Atoi(fields[1])
|
||||
case "StdVW":
|
||||
info.Desc.StemV, err = strconv.Atoi(fields[1])
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if err = scanner.Err(); err != nil {
|
||||
return
|
||||
}
|
||||
if info.FontName == "" {
|
||||
err = fmt.Errorf("the field FontName missing in AFM file %s", afmFileStr)
|
||||
return
|
||||
}
|
||||
info.Bold = wt == "bold" || wt == "black"
|
||||
var missingWd int
|
||||
missingWd, ok = wdMap[".notdef"]
|
||||
if ok {
|
||||
info.Desc.MissingWidth = missingWd
|
||||
}
|
||||
for j := 0; j < len(info.Widths); j++ {
|
||||
info.Widths[j] = info.Desc.MissingWidth
|
||||
}
|
||||
for j := 0; j < len(info.Widths); j++ {
|
||||
name = encList[j].name
|
||||
if name != ".notdef" {
|
||||
wd, ok = wdMap[name]
|
||||
if ok {
|
||||
info.Widths[j] = wd
|
||||
} else {
|
||||
fmt.Fprintf(msgWriter, "Character %s is missing\n", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
// printf("getInfoFromType1/FontBBox\n")
|
||||
// dump(info.Desc.FontBBox)
|
||||
return
|
||||
}
|
||||
|
||||
func makeFontDescriptor(info *fontInfoType) {
|
||||
if info.Desc.CapHeight == 0 {
|
||||
info.Desc.CapHeight = info.Desc.Ascent
|
||||
}
|
||||
info.Desc.Flags = 1 << 5
|
||||
if info.IsFixedPitch {
|
||||
info.Desc.Flags |= 1
|
||||
}
|
||||
if info.Desc.ItalicAngle != 0 {
|
||||
info.Desc.Flags |= 1 << 6
|
||||
}
|
||||
if info.Desc.StemV == 0 {
|
||||
if info.Bold {
|
||||
info.Desc.StemV = 120
|
||||
} else {
|
||||
info.Desc.StemV = 70
|
||||
}
|
||||
}
|
||||
// printf("makeFontDescriptor/FontBBox\n")
|
||||
// dump(info.Desc.FontBBox)
|
||||
}
|
||||
|
||||
// makeFontEncoding builds differences from reference encoding
|
||||
func makeFontEncoding(encList encListType, refEncFileStr string) (diffStr string, err error) {
|
||||
var refList encListType
|
||||
if refList, err = loadMap(refEncFileStr); err != nil {
|
||||
return
|
||||
}
|
||||
var buf fmtBuffer
|
||||
last := 0
|
||||
for j := 32; j < 256; j++ {
|
||||
if encList[j].name != refList[j].name {
|
||||
if j != last+1 {
|
||||
buf.printf("%d ", j)
|
||||
}
|
||||
last = j
|
||||
buf.printf("/%s ", encList[j].name)
|
||||
}
|
||||
}
|
||||
diffStr = strings.TrimSpace(buf.String())
|
||||
return
|
||||
}
|
||||
|
||||
func makeDefinitionFile(fileStr, tpStr, encodingFileStr string, embed bool, encList encListType, info fontInfoType) error {
|
||||
var err error
|
||||
var def fontDefType
|
||||
def.Tp = tpStr
|
||||
def.Name = info.FontName
|
||||
makeFontDescriptor(&info)
|
||||
def.Desc = info.Desc
|
||||
// printf("makeDefinitionFile/FontBBox\n")
|
||||
// dump(def.Desc.FontBBox)
|
||||
def.Up = info.UnderlinePosition
|
||||
def.Ut = info.UnderlineThickness
|
||||
def.Cw = info.Widths
|
||||
def.Enc = baseNoExt(encodingFileStr)
|
||||
// fmt.Printf("encodingFileStr [%s], def.Enc [%s]\n", encodingFileStr, def.Enc)
|
||||
// fmt.Printf("reference [%s]\n", filepath.Join(filepath.Dir(encodingFileStr), "cp1252.map"))
|
||||
def.Diff, err = makeFontEncoding(encList, filepath.Join(filepath.Dir(encodingFileStr), "cp1252.map"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
def.File = info.File
|
||||
def.Size1 = int(info.Size1)
|
||||
def.Size2 = int(info.Size2)
|
||||
def.OriginalSize = info.OriginalSize
|
||||
// printf("Font definition file [%s]\n", fileStr)
|
||||
var buf []byte
|
||||
buf, err = json.Marshal(def)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var f *os.File
|
||||
f, err = os.Create(fileStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
_, err = f.Write(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = f.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// MakeFont generates a font definition file in JSON format. A definition file
|
||||
// of this type is required to use non-core fonts in the PDF documents that
|
||||
// gofpdf generates. See the makefont utility in the gofpdf package for a
|
||||
// command line interface to this function.
|
||||
//
|
||||
// fontFileStr is the name of the TrueType file (extension .ttf), OpenType file
|
||||
// (extension .otf) or binary Type1 file (extension .pfb) from which to
|
||||
// generate a definition file. If an OpenType file is specified, it must be one
|
||||
// that is based on TrueType outlines, not PostScript outlines; this cannot be
|
||||
// determined from the file extension alone. If a Type1 file is specified, a
|
||||
// metric file with the same pathname except with the extension .afm must be
|
||||
// present.
|
||||
//
|
||||
// encodingFileStr is the name of the encoding file that corresponds to the
|
||||
// font.
|
||||
//
|
||||
// dstDirStr is the name of the directory in which to save the definition file
|
||||
// and, if embed is true, the compressed font file.
|
||||
//
|
||||
// msgWriter is the writer that is called to display messages throughout the
|
||||
// process. Use nil to turn off messages.
|
||||
//
|
||||
// embed is true if the font is to be embedded in the PDF files.
|
||||
func MakeFont(fontFileStr, encodingFileStr, dstDirStr string, msgWriter io.Writer, embed bool) error {
|
||||
if msgWriter == nil {
|
||||
msgWriter = ioutil.Discard
|
||||
}
|
||||
if !fileExist(fontFileStr) {
|
||||
return fmt.Errorf("font file not found: %s", fontFileStr)
|
||||
}
|
||||
extStr := strings.ToLower(fontFileStr[len(fontFileStr)-3:])
|
||||
// printf("Font file extension [%s]\n", extStr)
|
||||
var tpStr string
|
||||
switch extStr {
|
||||
case "ttf":
|
||||
fallthrough
|
||||
case "otf":
|
||||
tpStr = "TrueType"
|
||||
case "pfb":
|
||||
tpStr = "Type1"
|
||||
default:
|
||||
return fmt.Errorf("unrecognized font file extension: %s", extStr)
|
||||
}
|
||||
|
||||
var info fontInfoType
|
||||
encList, err := loadMap(encodingFileStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// printf("Encoding table\n")
|
||||
// dump(encList)
|
||||
if tpStr == "TrueType" {
|
||||
info, err = getInfoFromTrueType(fontFileStr, msgWriter, embed, encList)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
info, err = getInfoFromType1(fontFileStr, msgWriter, embed, encList)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
baseStr := baseNoExt(fontFileStr)
|
||||
// fmt.Printf("Base [%s]\n", baseStr)
|
||||
if embed {
|
||||
var f *os.File
|
||||
info.File = baseStr + ".z"
|
||||
zFileStr := filepath.Join(dstDirStr, info.File)
|
||||
f, err = os.Create(zFileStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
cmp := zlib.NewWriter(f)
|
||||
_, err = cmp.Write(info.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = cmp.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(msgWriter, "Font file compressed: %s\n", zFileStr)
|
||||
}
|
||||
defFileStr := filepath.Join(dstDirStr, baseStr+".json")
|
||||
err = makeDefinitionFile(defFileStr, tpStr, encodingFileStr, embed, encList, info)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(msgWriter, "Font definition file successfully generated: %s\n", defFileStr)
|
||||
return nil
|
||||
}
|
4860
vendor/github.com/jung-kurt/gofpdf/fpdf.go
generated
vendored
Normal file
4860
vendor/github.com/jung-kurt/gofpdf/fpdf.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
213
vendor/github.com/jung-kurt/gofpdf/fpdftrans.go
generated
vendored
Normal file
213
vendor/github.com/jung-kurt/gofpdf/fpdftrans.go
generated
vendored
Normal file
@ -0,0 +1,213 @@
|
||||
package gofpdf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
// Routines in this file are translated from the work of Moritz Wagner and
|
||||
// Andreas Würmser.
|
||||
|
||||
// TransformMatrix is used for generalized transformations of text, drawings
|
||||
// and images.
|
||||
type TransformMatrix struct {
|
||||
A, B, C, D, E, F float64
|
||||
}
|
||||
|
||||
// TransformBegin sets up a transformation context for subsequent text,
|
||||
// drawings and images. The typical usage is to immediately follow a call to
|
||||
// this method with a call to one or more of the transformation methods such as
|
||||
// TransformScale(), TransformSkew(), etc. This is followed by text, drawing or
|
||||
// image output and finally a call to TransformEnd(). All transformation
|
||||
// contexts must be properly ended prior to outputting the document.
|
||||
func (f *Fpdf) TransformBegin() {
|
||||
f.transformNest++
|
||||
f.out("q")
|
||||
}
|
||||
|
||||
// TransformScaleX scales the width of the following text, drawings and images.
|
||||
// scaleWd is the percentage scaling factor. (x, y) is center of scaling.
|
||||
//
|
||||
// The TransformBegin() example demonstrates this method.
|
||||
func (f *Fpdf) TransformScaleX(scaleWd, x, y float64) {
|
||||
f.TransformScale(scaleWd, 100, x, y)
|
||||
}
|
||||
|
||||
// TransformScaleY scales the height of the following text, drawings and
|
||||
// images. scaleHt is the percentage scaling factor. (x, y) is center of
|
||||
// scaling.
|
||||
//
|
||||
// The TransformBegin() example demonstrates this method.
|
||||
func (f *Fpdf) TransformScaleY(scaleHt, x, y float64) {
|
||||
f.TransformScale(100, scaleHt, x, y)
|
||||
}
|
||||
|
||||
// TransformScaleXY uniformly scales the width and height of the following
|
||||
// text, drawings and images. s is the percentage scaling factor for both width
|
||||
// and height. (x, y) is center of scaling.
|
||||
//
|
||||
// The TransformBegin() example demonstrates this method.
|
||||
func (f *Fpdf) TransformScaleXY(s, x, y float64) {
|
||||
f.TransformScale(s, s, x, y)
|
||||
}
|
||||
|
||||
// TransformScale generally scales the following text, drawings and images.
|
||||
// scaleWd and scaleHt are the percentage scaling factors for width and height.
|
||||
// (x, y) is center of scaling.
|
||||
//
|
||||
// The TransformBegin() example demonstrates this method.
|
||||
func (f *Fpdf) TransformScale(scaleWd, scaleHt, x, y float64) {
|
||||
if scaleWd == 0 || scaleHt == 0 {
|
||||
f.err = fmt.Errorf("scale factor cannot be zero")
|
||||
return
|
||||
}
|
||||
y = (f.h - y) * f.k
|
||||
x *= f.k
|
||||
scaleWd /= 100
|
||||
scaleHt /= 100
|
||||
f.Transform(TransformMatrix{scaleWd, 0, 0,
|
||||
scaleHt, x * (1 - scaleWd), y * (1 - scaleHt)})
|
||||
}
|
||||
|
||||
// TransformMirrorHorizontal horizontally mirrors the following text, drawings
|
||||
// and images. x is the axis of reflection.
|
||||
//
|
||||
// The TransformBegin() example demonstrates this method.
|
||||
func (f *Fpdf) TransformMirrorHorizontal(x float64) {
|
||||
f.TransformScale(-100, 100, x, f.y)
|
||||
}
|
||||
|
||||
// TransformMirrorVertical vertically mirrors the following text, drawings and
|
||||
// images. y is the axis of reflection.
|
||||
//
|
||||
// The TransformBegin() example demonstrates this method.
|
||||
func (f *Fpdf) TransformMirrorVertical(y float64) {
|
||||
f.TransformScale(100, -100, f.x, y)
|
||||
}
|
||||
|
||||
// TransformMirrorPoint symmetrically mirrors the following text, drawings and
|
||||
// images on the point specified by (x, y).
|
||||
//
|
||||
// The TransformBegin() example demonstrates this method.
|
||||
func (f *Fpdf) TransformMirrorPoint(x, y float64) {
|
||||
f.TransformScale(-100, -100, x, y)
|
||||
}
|
||||
|
||||
// TransformMirrorLine symmetrically mirrors the following text, drawings and
|
||||
// images on the line defined by angle and the point (x, y). angles is
|
||||
// specified in degrees and measured counter-clockwise from the 3 o'clock
|
||||
// position.
|
||||
//
|
||||
// The TransformBegin() example demonstrates this method.
|
||||
func (f *Fpdf) TransformMirrorLine(angle, x, y float64) {
|
||||
f.TransformScale(-100, 100, x, y)
|
||||
f.TransformRotate(-2*(angle-90), x, y)
|
||||
}
|
||||
|
||||
// TransformTranslateX moves the following text, drawings and images
|
||||
// horizontally by the amount specified by tx.
|
||||
//
|
||||
// The TransformBegin() example demonstrates this method.
|
||||
func (f *Fpdf) TransformTranslateX(tx float64) {
|
||||
f.TransformTranslate(tx, 0)
|
||||
}
|
||||
|
||||
// TransformTranslateY moves the following text, drawings and images vertically
|
||||
// by the amount specified by ty.
|
||||
//
|
||||
// The TransformBegin() example demonstrates this method.
|
||||
func (f *Fpdf) TransformTranslateY(ty float64) {
|
||||
f.TransformTranslate(0, ty)
|
||||
}
|
||||
|
||||
// TransformTranslate moves the following text, drawings and images
|
||||
// horizontally and vertically by the amounts specified by tx and ty.
|
||||
//
|
||||
// The TransformBegin() example demonstrates this method.
|
||||
func (f *Fpdf) TransformTranslate(tx, ty float64) {
|
||||
f.Transform(TransformMatrix{1, 0, 0, 1, tx * f.k, -ty * f.k})
|
||||
}
|
||||
|
||||
// TransformRotate rotates the following text, drawings and images around the
|
||||
// center point (x, y). angle is specified in degrees and measured
|
||||
// counter-clockwise from the 3 o'clock position.
|
||||
//
|
||||
// The TransformBegin() example demonstrates this method.
|
||||
func (f *Fpdf) TransformRotate(angle, x, y float64) {
|
||||
y = (f.h - y) * f.k
|
||||
x *= f.k
|
||||
angle = angle * math.Pi / 180
|
||||
var tm TransformMatrix
|
||||
tm.A = math.Cos(angle)
|
||||
tm.B = math.Sin(angle)
|
||||
tm.C = -tm.B
|
||||
tm.D = tm.A
|
||||
tm.E = x + tm.B*y - tm.A*x
|
||||
tm.F = y - tm.A*y - tm.B*x
|
||||
f.Transform(tm)
|
||||
}
|
||||
|
||||
// TransformSkewX horizontally skews the following text, drawings and images
|
||||
// keeping the point (x, y) stationary. angleX ranges from -90 degrees (skew to
|
||||
// the left) to 90 degrees (skew to the right).
|
||||
//
|
||||
// The TransformBegin() example demonstrates this method.
|
||||
func (f *Fpdf) TransformSkewX(angleX, x, y float64) {
|
||||
f.TransformSkew(angleX, 0, x, y)
|
||||
}
|
||||
|
||||
// TransformSkewY vertically skews the following text, drawings and images
|
||||
// keeping the point (x, y) stationary. angleY ranges from -90 degrees (skew to
|
||||
// the bottom) to 90 degrees (skew to the top).
|
||||
//
|
||||
// The TransformBegin() example demonstrates this method.
|
||||
func (f *Fpdf) TransformSkewY(angleY, x, y float64) {
|
||||
f.TransformSkew(0, angleY, x, y)
|
||||
}
|
||||
|
||||
// TransformSkew generally skews the following text, drawings and images
|
||||
// keeping the point (x, y) stationary. angleX ranges from -90 degrees (skew to
|
||||
// the left) to 90 degrees (skew to the right). angleY ranges from -90 degrees
|
||||
// (skew to the bottom) to 90 degrees (skew to the top).
|
||||
//
|
||||
// The TransformBegin() example demonstrates this method.
|
||||
func (f *Fpdf) TransformSkew(angleX, angleY, x, y float64) {
|
||||
if angleX <= -90 || angleX >= 90 || angleY <= -90 || angleY >= 90 {
|
||||
f.err = fmt.Errorf("skew values must be between -90° and 90°")
|
||||
return
|
||||
}
|
||||
x *= f.k
|
||||
y = (f.h - y) * f.k
|
||||
var tm TransformMatrix
|
||||
tm.A = 1
|
||||
tm.B = math.Tan(angleY * math.Pi / 180)
|
||||
tm.C = math.Tan(angleX * math.Pi / 180)
|
||||
tm.D = 1
|
||||
tm.E = -tm.C * y
|
||||
tm.F = -tm.B * x
|
||||
f.Transform(tm)
|
||||
}
|
||||
|
||||
// Transform generally transforms the following text, drawings and images
|
||||
// according to the specified matrix. It is typically easier to use the various
|
||||
// methods such as TransformRotate() and TransformMirrorVertical() instead.
|
||||
func (f *Fpdf) Transform(tm TransformMatrix) {
|
||||
if f.transformNest > 0 {
|
||||
f.outf("%.5f %.5f %.5f %.5f %.5f %.5f cm",
|
||||
tm.A, tm.B, tm.C, tm.D, tm.E, tm.F)
|
||||
} else if f.err == nil {
|
||||
f.err = fmt.Errorf("transformation context is not active")
|
||||
}
|
||||
}
|
||||
|
||||
// TransformEnd applies a transformation that was begun with a call to TransformBegin().
|
||||
//
|
||||
// The TransformBegin() example demonstrates this method.
|
||||
func (f *Fpdf) TransformEnd() {
|
||||
if f.transformNest > 0 {
|
||||
f.transformNest--
|
||||
f.out("Q")
|
||||
} else {
|
||||
f.err = fmt.Errorf("error attempting to end transformation operation out of sequence")
|
||||
}
|
||||
}
|
12
vendor/github.com/jung-kurt/gofpdf/go.mod
generated
vendored
Normal file
12
vendor/github.com/jung-kurt/gofpdf/go.mod
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
module github.com/jung-kurt/gofpdf
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
github.com/boombuler/barcode v1.0.0
|
||||
github.com/phpdave11/gofpdi v1.0.7
|
||||
github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58
|
||||
golang.org/x/image v0.0.0-20190507092727-e4e5bf290fec
|
||||
)
|
||||
|
||||
replace github.com/jung-kurt/gopdf => ./
|
18
vendor/github.com/jung-kurt/gofpdf/go.sum
generated
vendored
Normal file
18
vendor/github.com/jung-kurt/gofpdf/go.sum
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
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/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
|
||||
github.com/phpdave11/gofpdi v1.0.7 h1:k2oy4yhkQopCK+qW8KjCla0iU2RpDow+QUDmH9DDt44=
|
||||
github.com/phpdave11/gofpdi v1.0.7/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58 h1:nlG4Wa5+minh3S9LVFtNoY+GVRiudA2e3EVfcCi3RCA=
|
||||
github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
golang.org/x/image v0.0.0-20190507092727-e4e5bf290fec h1:arXJwtMuk5vqI1NHX0UTnNw977rYk5Sl4jQqHj+hun4=
|
||||
golang.org/x/image v0.0.0-20190507092727-e4e5bf290fec/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
446
vendor/github.com/jung-kurt/gofpdf/grid.go
generated
vendored
Normal file
446
vendor/github.com/jung-kurt/gofpdf/grid.go
generated
vendored
Normal file
@ -0,0 +1,446 @@
|
||||
package gofpdf
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func unused(args ...interface{}) {
|
||||
}
|
||||
|
||||
// RGBType holds fields for red, green and blue color components (0..255)
|
||||
type RGBType struct {
|
||||
R, G, B int
|
||||
}
|
||||
|
||||
// RGBAType holds fields for red, green and blue color components (0..255) and
|
||||
// an alpha transparency value (0..1)
|
||||
type RGBAType struct {
|
||||
R, G, B int
|
||||
Alpha float64
|
||||
}
|
||||
|
||||
// StateType holds various commonly used drawing values for convenient
|
||||
// retrieval (StateGet()) and restore (Put) methods.
|
||||
type StateType struct {
|
||||
clrDraw, clrText, clrFill RGBType
|
||||
lineWd float64
|
||||
fontSize float64
|
||||
alpha float64
|
||||
blendStr string
|
||||
cellMargin float64
|
||||
}
|
||||
|
||||
// StateGet returns a variable that contains common state values.
|
||||
func StateGet(pdf *Fpdf) (st StateType) {
|
||||
st.clrDraw.R, st.clrDraw.G, st.clrDraw.B = pdf.GetDrawColor()
|
||||
st.clrFill.R, st.clrFill.G, st.clrFill.B = pdf.GetFillColor()
|
||||
st.clrText.R, st.clrText.G, st.clrText.B = pdf.GetTextColor()
|
||||
st.lineWd = pdf.GetLineWidth()
|
||||
_, st.fontSize = pdf.GetFontSize()
|
||||
st.alpha, st.blendStr = pdf.GetAlpha()
|
||||
st.cellMargin = pdf.GetCellMargin()
|
||||
return
|
||||
}
|
||||
|
||||
// Put sets the common state values contained in the state structure
|
||||
// specified by st.
|
||||
func (st StateType) Put(pdf *Fpdf) {
|
||||
pdf.SetDrawColor(st.clrDraw.R, st.clrDraw.G, st.clrDraw.B)
|
||||
pdf.SetFillColor(st.clrFill.R, st.clrFill.G, st.clrFill.B)
|
||||
pdf.SetTextColor(st.clrText.R, st.clrText.G, st.clrText.B)
|
||||
pdf.SetLineWidth(st.lineWd)
|
||||
pdf.SetFontUnitSize(st.fontSize)
|
||||
pdf.SetAlpha(st.alpha, st.blendStr)
|
||||
pdf.SetCellMargin(st.cellMargin)
|
||||
}
|
||||
|
||||
// TickFormatFncType defines a callback for label drawing.
|
||||
type TickFormatFncType func(val float64, precision int) string
|
||||
|
||||
// defaultFormatter returns the string form of val with precision decimal places.
|
||||
func defaultFormatter(val float64, precision int) string {
|
||||
return strconv.FormatFloat(val, 'f', precision, 64)
|
||||
}
|
||||
|
||||
// GridType assists with the generation of graphs. It allows the application to
|
||||
// work with logical data coordinates rather than page coordinates and assists
|
||||
// with the drawing of a background grid.
|
||||
type GridType struct {
|
||||
// Chart coordinates in page units
|
||||
x, y, w, h float64
|
||||
// X, Y, Wd, Ht float64
|
||||
// Slopes and intercepts scale data points to graph coordinates linearly
|
||||
xm, xb, ym, yb float64
|
||||
// Tickmarks
|
||||
xTicks, yTicks []float64
|
||||
// Labels are inside of graph boundary
|
||||
XLabelIn, YLabelIn bool
|
||||
// Labels on X-axis should be rotated
|
||||
XLabelRotate bool
|
||||
// Formatters; use nil to eliminate labels
|
||||
XTickStr, YTickStr TickFormatFncType
|
||||
// Subdivisions between tickmarks
|
||||
XDiv, YDiv int
|
||||
// Formatting precision
|
||||
xPrecision, yPrecision int
|
||||
// Line and label colors
|
||||
ClrText, ClrMain, ClrSub RGBAType
|
||||
// Line thickness
|
||||
WdMain, WdSub float64
|
||||
// Label height in points
|
||||
TextSize float64
|
||||
}
|
||||
|
||||
// linear returns the slope and y-intercept of the straight line joining the
|
||||
// two specified points. For scaling purposes, associate the arguments as
|
||||
// follows: x1: observed low value, y1: desired low value, x2: observed high
|
||||
// value, y2: desired high value.
|
||||
func linear(x1, y1, x2, y2 float64) (slope, intercept float64) {
|
||||
if x2 != x1 {
|
||||
slope = (y2 - y1) / (x2 - x1)
|
||||
intercept = y2 - x2*slope
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// linearTickmark returns the slope and intercept that will linearly map data
|
||||
// values (the range of which is specified by the tickmark slice tm) to page
|
||||
// values (the range of which is specified by lo and hi).
|
||||
func linearTickmark(tm []float64, lo, hi float64) (slope, intercept float64) {
|
||||
ln := len(tm)
|
||||
if ln > 0 {
|
||||
slope, intercept = linear(tm[0], lo, tm[ln-1], hi)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// NewGrid returns a variable of type GridType that is initialized to draw on a
|
||||
// rectangle of width w and height h with the upper left corner positioned at
|
||||
// point (x, y). The coordinates are in page units, that is, the same as those
|
||||
// specified in New().
|
||||
//
|
||||
// The returned variable is initialized with a very simple default tickmark
|
||||
// layout that ranges from 0 to 1 in both axes. Prior to calling Grid(), the
|
||||
// application may establish a more suitable tickmark layout by calling the
|
||||
// methods TickmarksContainX() and TickmarksContainY(). These methods bound the
|
||||
// data range with appropriate boundaries and divisions. Alternatively, if the
|
||||
// exact extent and divisions of the tickmark layout are known, the methods
|
||||
// TickmarksExtentX() and TickmarksExtentY may be called instead.
|
||||
func NewGrid(x, y, w, h float64) (grid GridType) {
|
||||
grid.x = x
|
||||
grid.y = y
|
||||
grid.w = w
|
||||
grid.h = h
|
||||
grid.TextSize = 7 // Points
|
||||
grid.TickmarksExtentX(0, 1, 1)
|
||||
grid.TickmarksExtentY(0, 1, 1)
|
||||
grid.XLabelIn = false
|
||||
grid.YLabelIn = false
|
||||
grid.XLabelRotate = false
|
||||
grid.XDiv = 10
|
||||
grid.YDiv = 10
|
||||
grid.ClrText = RGBAType{R: 0, G: 0, B: 0, Alpha: 1}
|
||||
grid.ClrMain = RGBAType{R: 128, G: 160, B: 128, Alpha: 1}
|
||||
grid.ClrSub = RGBAType{R: 192, G: 224, B: 192, Alpha: 1}
|
||||
grid.WdMain = 0.1
|
||||
grid.WdSub = 0.1
|
||||
grid.YTickStr = defaultFormatter
|
||||
grid.XTickStr = defaultFormatter
|
||||
return
|
||||
}
|
||||
|
||||
// WdAbs returns the absolute value of dataWd, specified in logical data units,
|
||||
// that has been converted to the unit of measure specified in New().
|
||||
func (g GridType) WdAbs(dataWd float64) float64 {
|
||||
return math.Abs(g.xm * dataWd)
|
||||
}
|
||||
|
||||
// Wd converts dataWd, specified in logical data units, to the unit of measure
|
||||
// specified in New().
|
||||
func (g GridType) Wd(dataWd float64) float64 {
|
||||
return g.xm * dataWd
|
||||
}
|
||||
|
||||
// XY converts dataX and dataY, specified in logical data units, to the X and Y
|
||||
// position on the current page.
|
||||
func (g GridType) XY(dataX, dataY float64) (x, y float64) {
|
||||
return g.xm*dataX + g.xb, g.ym*dataY + g.yb
|
||||
}
|
||||
|
||||
// Pos returns the point, in page units, indicated by the relative positions
|
||||
// xRel and yRel. These are values between 0 and 1. xRel specifies the relative
|
||||
// position between the grid's left and right edges. yRel specifies the
|
||||
// relative position between the grid's bottom and top edges.
|
||||
func (g GridType) Pos(xRel, yRel float64) (x, y float64) {
|
||||
x = g.w*xRel + g.x
|
||||
y = g.h*(1-yRel) + g.y
|
||||
return
|
||||
}
|
||||
|
||||
// X converts dataX, specified in logical data units, to the X position on the
|
||||
// current page.
|
||||
func (g GridType) X(dataX float64) float64 {
|
||||
return g.xm*dataX + g.xb
|
||||
}
|
||||
|
||||
// HtAbs returns the absolute value of dataHt, specified in logical data units,
|
||||
// that has been converted to the unit of measure specified in New().
|
||||
func (g GridType) HtAbs(dataHt float64) float64 {
|
||||
return math.Abs(g.ym * dataHt)
|
||||
}
|
||||
|
||||
// Ht converts dataHt, specified in logical data units, to the unit of measure
|
||||
// specified in New().
|
||||
func (g GridType) Ht(dataHt float64) float64 {
|
||||
return g.ym * dataHt
|
||||
}
|
||||
|
||||
// Y converts dataY, specified in logical data units, to the Y position on the
|
||||
// current page.
|
||||
func (g GridType) Y(dataY float64) float64 {
|
||||
return g.ym*dataY + g.yb
|
||||
}
|
||||
|
||||
// XRange returns the minimum and maximum values for the current tickmark
|
||||
// sequence. These correspond to the data values of the graph's left and right
|
||||
// edges.
|
||||
func (g GridType) XRange() (min, max float64) {
|
||||
min = g.xTicks[0]
|
||||
max = g.xTicks[len(g.xTicks)-1]
|
||||
return
|
||||
}
|
||||
|
||||
// YRange returns the minimum and maximum values for the current tickmark
|
||||
// sequence. These correspond to the data values of the graph's bottom and top
|
||||
// edges.
|
||||
func (g GridType) YRange() (min, max float64) {
|
||||
min = g.yTicks[0]
|
||||
max = g.yTicks[len(g.yTicks)-1]
|
||||
return
|
||||
}
|
||||
|
||||
// TickmarksContainX sets the tickmarks to be shown by Grid() in the horizontal
|
||||
// dimension. The argument min and max specify the minimum and maximum values
|
||||
// to be contained within the grid. The tickmark values that are generated are
|
||||
// suitable for general purpose graphs.
|
||||
//
|
||||
// See TickmarkExtentX() for an alternative to this method to be used when the
|
||||
// exact values of the tickmarks are to be set by the application.
|
||||
func (g *GridType) TickmarksContainX(min, max float64) {
|
||||
g.xTicks, g.xPrecision = Tickmarks(min, max)
|
||||
g.xm, g.xb = linearTickmark(g.xTicks, g.x, g.x+g.w)
|
||||
}
|
||||
|
||||
// TickmarksContainY sets the tickmarks to be shown by Grid() in the vertical
|
||||
// dimension. The argument min and max specify the minimum and maximum values
|
||||
// to be contained within the grid. The tickmark values that are generated are
|
||||
// suitable for general purpose graphs.
|
||||
//
|
||||
// See TickmarkExtentY() for an alternative to this method to be used when the
|
||||
// exact values of the tickmarks are to be set by the application.
|
||||
func (g *GridType) TickmarksContainY(min, max float64) {
|
||||
g.yTicks, g.yPrecision = Tickmarks(min, max)
|
||||
g.ym, g.yb = linearTickmark(g.yTicks, g.y+g.h, g.y)
|
||||
}
|
||||
|
||||
func extent(min, div float64, count int) (tm []float64, precision int) {
|
||||
tm = make([]float64, count+1)
|
||||
for j := 0; j <= count; j++ {
|
||||
tm[j] = min
|
||||
min += div
|
||||
}
|
||||
precision = TickmarkPrecision(div)
|
||||
return
|
||||
}
|
||||
|
||||
// TickmarksExtentX sets the tickmarks to be shown by Grid() in the horizontal
|
||||
// dimension. count specifies number of major tickmark subdivisions to be
|
||||
// graphed. min specifies the leftmost data value. div specifies, in data
|
||||
// units, the extent of each major tickmark subdivision.
|
||||
//
|
||||
// See TickmarkContainX() for an alternative to this method to be used when
|
||||
// viewer-friendly tickmarks are to be determined automatically.
|
||||
func (g *GridType) TickmarksExtentX(min, div float64, count int) {
|
||||
g.xTicks, g.xPrecision = extent(min, div, count)
|
||||
g.xm, g.xb = linearTickmark(g.xTicks, g.x, g.x+g.w)
|
||||
}
|
||||
|
||||
// TickmarksExtentY sets the tickmarks to be shown by Grid() in the vertical
|
||||
// dimension. count specifies number of major tickmark subdivisions to be
|
||||
// graphed. min specifies the bottommost data value. div specifies, in data
|
||||
// units, the extent of each major tickmark subdivision.
|
||||
//
|
||||
// See TickmarkContainY() for an alternative to this method to be used when
|
||||
// viewer-friendly tickmarks are to be determined automatically.
|
||||
func (g *GridType) TickmarksExtentY(min, div float64, count int) {
|
||||
g.yTicks, g.yPrecision = extent(min, div, count)
|
||||
g.ym, g.yb = linearTickmark(g.yTicks, g.y+g.h, g.y)
|
||||
}
|
||||
|
||||
// func (g *GridType) SetXExtent(dataLf, paperLf, dataRt, paperRt float64) {
|
||||
// g.xm, g.xb = linear(dataLf, paperLf, dataRt, paperRt)
|
||||
// }
|
||||
|
||||
// func (g *GridType) SetYExtent(dataTp, paperTp, dataBt, paperBt float64) {
|
||||
// g.ym, g.yb = linear(dataTp, paperTp, dataBt, paperBt)
|
||||
// }
|
||||
|
||||
func lineAttr(pdf *Fpdf, clr RGBAType, lineWd float64) {
|
||||
pdf.SetLineWidth(lineWd)
|
||||
pdf.SetAlpha(clr.Alpha, "Normal")
|
||||
pdf.SetDrawColor(clr.R, clr.G, clr.B)
|
||||
}
|
||||
|
||||
// Grid generates a graph-paperlike set of grid lines on the current page.
|
||||
func (g GridType) Grid(pdf *Fpdf) {
|
||||
var st StateType
|
||||
var yLen, xLen int
|
||||
var textSz, halfTextSz, yMin, yMax, xMin, xMax, yDiv, xDiv float64
|
||||
var str string
|
||||
var strOfs, strWd, tp, bt, lf, rt, drawX, drawY float64
|
||||
|
||||
xLen = len(g.xTicks)
|
||||
yLen = len(g.yTicks)
|
||||
if xLen > 1 && yLen > 1 {
|
||||
|
||||
st = StateGet(pdf)
|
||||
|
||||
line := func(x1, y1, x2, y2 float64, heavy bool) {
|
||||
if heavy {
|
||||
lineAttr(pdf, g.ClrMain, g.WdMain)
|
||||
} else {
|
||||
lineAttr(pdf, g.ClrSub, g.WdSub)
|
||||
}
|
||||
pdf.Line(x1, y1, x2, y2)
|
||||
}
|
||||
|
||||
textSz = pdf.PointToUnitConvert(g.TextSize)
|
||||
halfTextSz = textSz / 2
|
||||
|
||||
pdf.SetAutoPageBreak(false, 0)
|
||||
pdf.SetFontUnitSize(textSz)
|
||||
strOfs = pdf.GetStringWidth("0")
|
||||
pdf.SetFillColor(255, 255, 255)
|
||||
pdf.SetCellMargin(0)
|
||||
|
||||
xMin = g.xTicks[0]
|
||||
xMax = g.xTicks[xLen-1]
|
||||
|
||||
yMin = g.yTicks[0]
|
||||
yMax = g.yTicks[yLen-1]
|
||||
|
||||
lf = g.X(xMin)
|
||||
rt = g.X(xMax)
|
||||
bt = g.Y(yMin)
|
||||
tp = g.Y(yMax)
|
||||
|
||||
// Verticals along X axis
|
||||
xDiv = g.xTicks[1] - g.xTicks[0]
|
||||
if g.XDiv > 0 {
|
||||
xDiv = xDiv / float64(g.XDiv)
|
||||
}
|
||||
xDiv = g.Wd(xDiv)
|
||||
for j, x := range g.xTicks {
|
||||
drawX = g.X(x)
|
||||
line(drawX, tp, drawX, bt, true)
|
||||
if j < xLen-1 {
|
||||
for k := 1; k < g.XDiv; k++ {
|
||||
drawX += xDiv
|
||||
line(drawX, tp, drawX, bt, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Horizontals along Y axis
|
||||
yDiv = g.yTicks[1] - g.yTicks[0]
|
||||
if g.YDiv > 0 {
|
||||
yDiv = yDiv / float64(g.YDiv)
|
||||
}
|
||||
yDiv = g.Ht(yDiv)
|
||||
for j, y := range g.yTicks {
|
||||
drawY = g.Y(y)
|
||||
line(lf, drawY, rt, drawY, true)
|
||||
if j < yLen-1 {
|
||||
for k := 1; k < g.YDiv; k++ {
|
||||
drawY += yDiv
|
||||
line(lf, drawY, rt, drawY, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// X labels
|
||||
if g.XTickStr != nil {
|
||||
drawY = bt
|
||||
for _, x := range g.xTicks {
|
||||
str = g.XTickStr(x, g.xPrecision)
|
||||
strWd = pdf.GetStringWidth(str)
|
||||
drawX = g.X(x)
|
||||
if g.XLabelRotate {
|
||||
pdf.TransformBegin()
|
||||
pdf.TransformRotate(90, drawX, drawY)
|
||||
if g.XLabelIn {
|
||||
pdf.SetXY(drawX+strOfs, drawY-halfTextSz)
|
||||
} else {
|
||||
pdf.SetXY(drawX-strOfs-strWd, drawY-halfTextSz)
|
||||
}
|
||||
pdf.CellFormat(strWd, textSz, str, "", 0, "L", true, 0, "")
|
||||
pdf.TransformEnd()
|
||||
} else {
|
||||
drawX -= strWd / 2.0
|
||||
if g.XLabelIn {
|
||||
pdf.SetXY(drawX, drawY-textSz-strOfs)
|
||||
} else {
|
||||
pdf.SetXY(drawX, drawY+strOfs)
|
||||
}
|
||||
pdf.CellFormat(strWd, textSz, str, "", 0, "L", true, 0, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Y labels
|
||||
if g.YTickStr != nil {
|
||||
drawX = lf
|
||||
for _, y := range g.yTicks {
|
||||
// str = strconv.FormatFloat(y, 'f', g.yPrecision, 64)
|
||||
str = g.YTickStr(y, g.yPrecision)
|
||||
strWd = pdf.GetStringWidth(str)
|
||||
if g.YLabelIn {
|
||||
pdf.SetXY(drawX+strOfs, g.Y(y)-halfTextSz)
|
||||
} else {
|
||||
pdf.SetXY(lf-strOfs-strWd, g.Y(y)-halfTextSz)
|
||||
}
|
||||
pdf.CellFormat(strWd, textSz, str, "", 0, "L", true, 0, "")
|
||||
}
|
||||
}
|
||||
|
||||
// Restore drawing attributes
|
||||
st.Put(pdf)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Plot plots a series of count line segments from xMin to xMax. It repeatedly
|
||||
// calls fnc(x) to retrieve the y value associate with x. The currently
|
||||
// selected line drawing attributes are used.
|
||||
func (g GridType) Plot(pdf *Fpdf, xMin, xMax float64, count int, fnc func(x float64) (y float64)) {
|
||||
if count > 0 {
|
||||
var x, delta, drawX0, drawY0, drawX1, drawY1 float64
|
||||
delta = (xMax - xMin) / float64(count)
|
||||
x = xMin
|
||||
for j := 0; j <= count; j++ {
|
||||
if j == 0 {
|
||||
drawX1 = g.X(x)
|
||||
drawY1 = g.Y(fnc(x))
|
||||
} else {
|
||||
pdf.Line(drawX0, drawY0, drawX1, drawY1)
|
||||
}
|
||||
x += delta
|
||||
drawX0 = drawX1
|
||||
drawY0 = drawY1
|
||||
drawX1 = g.X(x)
|
||||
drawY1 = g.Y(fnc(x))
|
||||
}
|
||||
}
|
||||
}
|
220
vendor/github.com/jung-kurt/gofpdf/htmlbasic.go
generated
vendored
Normal file
220
vendor/github.com/jung-kurt/gofpdf/htmlbasic.go
generated
vendored
Normal file
@ -0,0 +1,220 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Kurt Jung (Gmail: kurt.w.jung)
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package gofpdf
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// HTMLBasicSegmentType defines a segment of literal text in which the current
|
||||
// attributes do not vary, or an open tag or a close tag.
|
||||
type HTMLBasicSegmentType struct {
|
||||
Cat byte // 'O' open tag, 'C' close tag, 'T' text
|
||||
Str string // Literal text unchanged, tags are lower case
|
||||
Attr map[string]string // Attribute keys are lower case
|
||||
}
|
||||
|
||||
// HTMLBasicTokenize returns a list of HTML tags and literal elements. This is
|
||||
// done with regular expressions, so the result is only marginally better than
|
||||
// useless.
|
||||
func HTMLBasicTokenize(htmlStr string) (list []HTMLBasicSegmentType) {
|
||||
// This routine is adapted from http://www.fpdf.org/
|
||||
list = make([]HTMLBasicSegmentType, 0, 16)
|
||||
htmlStr = strings.Replace(htmlStr, "\n", " ", -1)
|
||||
htmlStr = strings.Replace(htmlStr, "\r", "", -1)
|
||||
tagRe, _ := regexp.Compile(`(?U)<.*>`)
|
||||
attrRe, _ := regexp.Compile(`([^=]+)=["']?([^"']+)`)
|
||||
capList := tagRe.FindAllStringIndex(htmlStr, -1)
|
||||
if capList != nil {
|
||||
var seg HTMLBasicSegmentType
|
||||
var parts []string
|
||||
pos := 0
|
||||
for _, cap := range capList {
|
||||
if pos < cap[0] {
|
||||
seg.Cat = 'T'
|
||||
seg.Str = htmlStr[pos:cap[0]]
|
||||
seg.Attr = nil
|
||||
list = append(list, seg)
|
||||
}
|
||||
if htmlStr[cap[0]+1] == '/' {
|
||||
seg.Cat = 'C'
|
||||
seg.Str = strings.ToLower(htmlStr[cap[0]+2 : cap[1]-1])
|
||||
seg.Attr = nil
|
||||
list = append(list, seg)
|
||||
} else {
|
||||
// Extract attributes
|
||||
parts = strings.Split(htmlStr[cap[0]+1:cap[1]-1], " ")
|
||||
if len(parts) > 0 {
|
||||
for j, part := range parts {
|
||||
if j == 0 {
|
||||
seg.Cat = 'O'
|
||||
seg.Str = strings.ToLower(parts[0])
|
||||
seg.Attr = make(map[string]string)
|
||||
} else {
|
||||
attrList := attrRe.FindAllStringSubmatch(part, -1)
|
||||
if attrList != nil {
|
||||
for _, attr := range attrList {
|
||||
seg.Attr[strings.ToLower(attr[1])] = attr[2]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
list = append(list, seg)
|
||||
}
|
||||
}
|
||||
pos = cap[1]
|
||||
}
|
||||
if len(htmlStr) > pos {
|
||||
seg.Cat = 'T'
|
||||
seg.Str = htmlStr[pos:]
|
||||
seg.Attr = nil
|
||||
list = append(list, seg)
|
||||
}
|
||||
} else {
|
||||
list = append(list, HTMLBasicSegmentType{Cat: 'T', Str: htmlStr, Attr: nil})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// HTMLBasicType is used for rendering a very basic subset of HTML. It supports
|
||||
// only hyperlinks and bold, italic and underscore attributes. In the Link
|
||||
// structure, the ClrR, ClrG and ClrB fields (0 through 255) define the color
|
||||
// of hyperlinks. The Bold, Italic and Underscore values define the hyperlink
|
||||
// style.
|
||||
type HTMLBasicType struct {
|
||||
pdf *Fpdf
|
||||
Link struct {
|
||||
ClrR, ClrG, ClrB int
|
||||
Bold, Italic, Underscore bool
|
||||
}
|
||||
}
|
||||
|
||||
// HTMLBasicNew returns an instance that facilitates writing basic HTML in the
|
||||
// specified PDF file.
|
||||
func (f *Fpdf) HTMLBasicNew() (html HTMLBasicType) {
|
||||
html.pdf = f
|
||||
html.Link.ClrR, html.Link.ClrG, html.Link.ClrB = 0, 0, 128
|
||||
html.Link.Bold, html.Link.Italic, html.Link.Underscore = false, false, true
|
||||
return
|
||||
}
|
||||
|
||||
// Write prints text from the current position using the currently selected
|
||||
// font. See HTMLBasicNew() to create a receiver that is associated with the
|
||||
// PDF document instance. The text can be encoded with a basic subset of HTML
|
||||
// that includes hyperlinks and tags for italic (I), bold (B), underscore
|
||||
// (U) and center (CENTER) attributes. When the right margin is reached a line
|
||||
// break occurs and text continues from the left margin. Upon method exit, the
|
||||
// current position is left at the end of the text.
|
||||
//
|
||||
// lineHt indicates the line height in the unit of measure specified in New().
|
||||
func (html *HTMLBasicType) Write(lineHt float64, htmlStr string) {
|
||||
var boldLvl, italicLvl, underscoreLvl, linkBold, linkItalic, linkUnderscore int
|
||||
var textR, textG, textB = html.pdf.GetTextColor()
|
||||
var hrefStr string
|
||||
if html.Link.Bold {
|
||||
linkBold = 1
|
||||
}
|
||||
if html.Link.Italic {
|
||||
linkItalic = 1
|
||||
}
|
||||
if html.Link.Underscore {
|
||||
linkUnderscore = 1
|
||||
}
|
||||
setStyle := func(boldAdj, italicAdj, underscoreAdj int) {
|
||||
styleStr := ""
|
||||
boldLvl += boldAdj
|
||||
if boldLvl > 0 {
|
||||
styleStr += "B"
|
||||
}
|
||||
italicLvl += italicAdj
|
||||
if italicLvl > 0 {
|
||||
styleStr += "I"
|
||||
}
|
||||
underscoreLvl += underscoreAdj
|
||||
if underscoreLvl > 0 {
|
||||
styleStr += "U"
|
||||
}
|
||||
html.pdf.SetFont("", styleStr, 0)
|
||||
}
|
||||
putLink := func(urlStr, txtStr string) {
|
||||
// Put a hyperlink
|
||||
html.pdf.SetTextColor(html.Link.ClrR, html.Link.ClrG, html.Link.ClrB)
|
||||
setStyle(linkBold, linkItalic, linkUnderscore)
|
||||
html.pdf.WriteLinkString(lineHt, txtStr, urlStr)
|
||||
setStyle(-linkBold, -linkItalic, -linkUnderscore)
|
||||
html.pdf.SetTextColor(textR, textG, textB)
|
||||
}
|
||||
list := HTMLBasicTokenize(htmlStr)
|
||||
var ok bool
|
||||
alignStr := "L"
|
||||
for _, el := range list {
|
||||
switch el.Cat {
|
||||
case 'T':
|
||||
if len(hrefStr) > 0 {
|
||||
putLink(hrefStr, el.Str)
|
||||
hrefStr = ""
|
||||
} else {
|
||||
if alignStr == "C" || alignStr == "R" {
|
||||
html.pdf.WriteAligned(0, lineHt, el.Str, alignStr)
|
||||
} else {
|
||||
html.pdf.Write(lineHt, el.Str)
|
||||
}
|
||||
}
|
||||
case 'O':
|
||||
switch el.Str {
|
||||
case "b":
|
||||
setStyle(1, 0, 0)
|
||||
case "i":
|
||||
setStyle(0, 1, 0)
|
||||
case "u":
|
||||
setStyle(0, 0, 1)
|
||||
case "br":
|
||||
html.pdf.Ln(lineHt)
|
||||
case "center":
|
||||
html.pdf.Ln(lineHt)
|
||||
alignStr = "C"
|
||||
case "right":
|
||||
html.pdf.Ln(lineHt)
|
||||
alignStr = "R"
|
||||
case "left":
|
||||
html.pdf.Ln(lineHt)
|
||||
alignStr = "L"
|
||||
case "a":
|
||||
hrefStr, ok = el.Attr["href"]
|
||||
if !ok {
|
||||
hrefStr = ""
|
||||
}
|
||||
}
|
||||
case 'C':
|
||||
switch el.Str {
|
||||
case "b":
|
||||
setStyle(-1, 0, 0)
|
||||
case "i":
|
||||
setStyle(0, -1, 0)
|
||||
case "u":
|
||||
setStyle(0, 0, -1)
|
||||
case "center":
|
||||
html.pdf.Ln(lineHt)
|
||||
alignStr = "L"
|
||||
case "right":
|
||||
html.pdf.Ln(lineHt)
|
||||
alignStr = "L"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
82
vendor/github.com/jung-kurt/gofpdf/label.go
generated
vendored
Normal file
82
vendor/github.com/jung-kurt/gofpdf/label.go
generated
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
package gofpdf
|
||||
|
||||
// Adapted from Nice Numbers for Graph Labels by Paul Heckbert from "Graphics
|
||||
// Gems", Academic Press, 1990
|
||||
|
||||
// Paul Heckbert 2 Dec 88
|
||||
|
||||
// https://github.com/erich666/GraphicsGems
|
||||
|
||||
// LICENSE
|
||||
|
||||
// This code repository predates the concept of Open Source, and predates most
|
||||
// licenses along such lines. As such, the official license truly is:
|
||||
|
||||
// EULA: The Graphics Gems code is copyright-protected. In other words, you
|
||||
// cannot claim the text of the code as your own and resell it. Using the code
|
||||
// is permitted in any program, product, or library, non-commercial or
|
||||
// commercial. Giving credit is not required, though is a nice gesture. The
|
||||
// code comes as-is, and if there are any flaws or problems with any Gems code,
|
||||
// nobody involved with Gems - authors, editors, publishers, or webmasters -
|
||||
// are to be held responsible. Basically, don't be a jerk, and remember that
|
||||
// anything free comes with no guarantee.
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
// niceNum returns a "nice" number approximately equal to x. The number is
|
||||
// rounded if round is true, converted to its ceiling otherwise.
|
||||
func niceNum(val float64, round bool) float64 {
|
||||
var nf float64
|
||||
|
||||
exp := int(math.Floor(math.Log10(val)))
|
||||
f := val / math.Pow10(exp)
|
||||
if round {
|
||||
switch {
|
||||
case f < 1.5:
|
||||
nf = 1
|
||||
case f < 3.0:
|
||||
nf = 2
|
||||
case f < 7.0:
|
||||
nf = 5
|
||||
default:
|
||||
nf = 10
|
||||
}
|
||||
} else {
|
||||
switch {
|
||||
case f <= 1:
|
||||
nf = 1
|
||||
case f <= 2.0:
|
||||
nf = 2
|
||||
case f <= 5.0:
|
||||
nf = 5
|
||||
default:
|
||||
nf = 10
|
||||
}
|
||||
}
|
||||
return nf * math.Pow10(exp)
|
||||
}
|
||||
|
||||
// TickmarkPrecision returns an appropriate precision value for label
|
||||
// formatting.
|
||||
func TickmarkPrecision(div float64) int {
|
||||
return int(math.Max(-math.Floor(math.Log10(div)), 0))
|
||||
}
|
||||
|
||||
// Tickmarks returns a slice of tickmarks appropriate for a chart axis and an
|
||||
// appropriate precision for formatting purposes. The values min and max will
|
||||
// be contained within the tickmark range.
|
||||
func Tickmarks(min, max float64) (list []float64, precision int) {
|
||||
if max > min {
|
||||
spread := niceNum(max-min, false)
|
||||
d := niceNum((spread / 4), true)
|
||||
graphMin := math.Floor(min/d) * d
|
||||
graphMax := math.Ceil(max/d) * d
|
||||
precision = TickmarkPrecision(d)
|
||||
for x := graphMin; x < graphMax+0.5*d; x += d {
|
||||
list = append(list, x)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
121
vendor/github.com/jung-kurt/gofpdf/layer.go
generated
vendored
Normal file
121
vendor/github.com/jung-kurt/gofpdf/layer.go
generated
vendored
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Kurt Jung (Gmail: kurt.w.jung)
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package gofpdf
|
||||
|
||||
// Routines in this file are translated from
|
||||
// http://www.fpdf.org/en/script/script97.php
|
||||
|
||||
type layerType struct {
|
||||
name string
|
||||
visible bool
|
||||
objNum int // object number
|
||||
}
|
||||
|
||||
type layerRecType struct {
|
||||
list []layerType
|
||||
currentLayer int
|
||||
openLayerPane bool
|
||||
}
|
||||
|
||||
func (f *Fpdf) layerInit() {
|
||||
f.layer.list = make([]layerType, 0)
|
||||
f.layer.currentLayer = -1
|
||||
f.layer.openLayerPane = false
|
||||
}
|
||||
|
||||
// AddLayer defines a layer that can be shown or hidden when the document is
|
||||
// displayed. name specifies the layer name that the document reader will
|
||||
// display in the layer list. visible specifies whether the layer will be
|
||||
// initially visible. The return value is an integer ID that is used in a call
|
||||
// to BeginLayer().
|
||||
func (f *Fpdf) AddLayer(name string, visible bool) (layerID int) {
|
||||
layerID = len(f.layer.list)
|
||||
f.layer.list = append(f.layer.list, layerType{name: name, visible: visible})
|
||||
return
|
||||
}
|
||||
|
||||
// BeginLayer is called to begin adding content to the specified layer. All
|
||||
// content added to the page between a call to BeginLayer and a call to
|
||||
// EndLayer is added to the layer specified by id. See AddLayer for more
|
||||
// details.
|
||||
func (f *Fpdf) BeginLayer(id int) {
|
||||
f.EndLayer()
|
||||
if id >= 0 && id < len(f.layer.list) {
|
||||
f.outf("/OC /OC%d BDC", id)
|
||||
f.layer.currentLayer = id
|
||||
}
|
||||
}
|
||||
|
||||
// EndLayer is called to stop adding content to the currently active layer. See
|
||||
// BeginLayer for more details.
|
||||
func (f *Fpdf) EndLayer() {
|
||||
if f.layer.currentLayer >= 0 {
|
||||
f.out("EMC")
|
||||
f.layer.currentLayer = -1
|
||||
}
|
||||
}
|
||||
|
||||
// OpenLayerPane advises the document reader to open the layer pane when the
|
||||
// document is initially displayed.
|
||||
func (f *Fpdf) OpenLayerPane() {
|
||||
f.layer.openLayerPane = true
|
||||
}
|
||||
|
||||
func (f *Fpdf) layerEndDoc() {
|
||||
if len(f.layer.list) > 0 {
|
||||
if f.pdfVersion < "1.5" {
|
||||
f.pdfVersion = "1.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Fpdf) layerPutLayers() {
|
||||
for j, l := range f.layer.list {
|
||||
f.newobj()
|
||||
f.layer.list[j].objNum = f.n
|
||||
f.outf("<</Type /OCG /Name %s>>", f.textstring(utf8toutf16(l.name)))
|
||||
f.out("endobj")
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Fpdf) layerPutResourceDict() {
|
||||
if len(f.layer.list) > 0 {
|
||||
f.out("/Properties <<")
|
||||
for j, layer := range f.layer.list {
|
||||
f.outf("/OC%d %d 0 R", j, layer.objNum)
|
||||
}
|
||||
f.out(">>")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (f *Fpdf) layerPutCatalog() {
|
||||
if len(f.layer.list) > 0 {
|
||||
onStr := ""
|
||||
offStr := ""
|
||||
for _, layer := range f.layer.list {
|
||||
onStr += sprintf("%d 0 R ", layer.objNum)
|
||||
if !layer.visible {
|
||||
offStr += sprintf("%d 0 R ", layer.objNum)
|
||||
}
|
||||
}
|
||||
f.outf("/OCProperties <</OCGs [%s] /D <</OFF [%s] /Order [%s]>>>>", onStr, offStr, onStr)
|
||||
if f.layer.openLayerPane {
|
||||
f.out("/PageMode /UseOC")
|
||||
}
|
||||
}
|
||||
}
|
213
vendor/github.com/jung-kurt/gofpdf/png.go
generated
vendored
Normal file
213
vendor/github.com/jung-kurt/gofpdf/png.go
generated
vendored
Normal file
@ -0,0 +1,213 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016 Kurt Jung (Gmail: kurt.w.jung)
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package gofpdf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (f *Fpdf) pngColorSpace(ct byte) (colspace string, colorVal int) {
|
||||
colorVal = 1
|
||||
switch ct {
|
||||
case 0, 4:
|
||||
colspace = "DeviceGray"
|
||||
case 2, 6:
|
||||
colspace = "DeviceRGB"
|
||||
colorVal = 3
|
||||
case 3:
|
||||
colspace = "Indexed"
|
||||
default:
|
||||
f.err = fmt.Errorf("unknown color type in PNG buffer: %d", ct)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (f *Fpdf) parsepngstream(buf *bytes.Buffer, readdpi bool) (info *ImageInfoType) {
|
||||
info = f.newImageInfo()
|
||||
// Check signature
|
||||
if string(buf.Next(8)) != "\x89PNG\x0d\x0a\x1a\x0a" {
|
||||
f.err = fmt.Errorf("not a PNG buffer")
|
||||
return
|
||||
}
|
||||
// Read header chunk
|
||||
_ = buf.Next(4)
|
||||
if string(buf.Next(4)) != "IHDR" {
|
||||
f.err = fmt.Errorf("incorrect PNG buffer")
|
||||
return
|
||||
}
|
||||
w := f.readBeInt32(buf)
|
||||
h := f.readBeInt32(buf)
|
||||
bpc := f.readByte(buf)
|
||||
if bpc > 8 {
|
||||
f.err = fmt.Errorf("16-bit depth not supported in PNG file")
|
||||
}
|
||||
ct := f.readByte(buf)
|
||||
var colspace string
|
||||
var colorVal int
|
||||
colspace, colorVal = f.pngColorSpace(ct)
|
||||
if f.err != nil {
|
||||
return
|
||||
}
|
||||
if f.readByte(buf) != 0 {
|
||||
f.err = fmt.Errorf("'unknown compression method in PNG buffer")
|
||||
return
|
||||
}
|
||||
if f.readByte(buf) != 0 {
|
||||
f.err = fmt.Errorf("'unknown filter method in PNG buffer")
|
||||
return
|
||||
}
|
||||
if f.readByte(buf) != 0 {
|
||||
f.err = fmt.Errorf("interlacing not supported in PNG buffer")
|
||||
return
|
||||
}
|
||||
_ = buf.Next(4)
|
||||
dp := sprintf("/Predictor 15 /Colors %d /BitsPerComponent %d /Columns %d", colorVal, bpc, w)
|
||||
// Scan chunks looking for palette, transparency and image data
|
||||
pal := make([]byte, 0, 32)
|
||||
var trns []int
|
||||
data := make([]byte, 0, 32)
|
||||
loop := true
|
||||
for loop {
|
||||
n := int(f.readBeInt32(buf))
|
||||
// dbg("Loop [%d]", n)
|
||||
switch string(buf.Next(4)) {
|
||||
case "PLTE":
|
||||
// dbg("PLTE")
|
||||
// Read palette
|
||||
pal = buf.Next(n)
|
||||
_ = buf.Next(4)
|
||||
case "tRNS":
|
||||
// dbg("tRNS")
|
||||
// Read transparency info
|
||||
t := buf.Next(n)
|
||||
switch ct {
|
||||
case 0:
|
||||
trns = []int{int(t[1])} // ord(substr($t,1,1)));
|
||||
case 2:
|
||||
trns = []int{int(t[1]), int(t[3]), int(t[5])} // array(ord(substr($t,1,1)), ord(substr($t,3,1)), ord(substr($t,5,1)));
|
||||
default:
|
||||
pos := strings.Index(string(t), "\x00")
|
||||
if pos >= 0 {
|
||||
trns = []int{pos} // array($pos);
|
||||
}
|
||||
}
|
||||
_ = buf.Next(4)
|
||||
case "IDAT":
|
||||
// dbg("IDAT")
|
||||
// Read image data block
|
||||
data = append(data, buf.Next(n)...)
|
||||
_ = buf.Next(4)
|
||||
case "IEND":
|
||||
// dbg("IEND")
|
||||
loop = false
|
||||
case "pHYs":
|
||||
// dbg("pHYs")
|
||||
// png files theoretically support different x/y dpi
|
||||
// but we ignore files like this
|
||||
// but if they're the same then we can stamp our info
|
||||
// object with it
|
||||
x := int(f.readBeInt32(buf))
|
||||
y := int(f.readBeInt32(buf))
|
||||
units := buf.Next(1)[0]
|
||||
// fmt.Printf("got a pHYs block, x=%d, y=%d, u=%d, readdpi=%t\n",
|
||||
// x, y, int(units), readdpi)
|
||||
// only modify the info block if the user wants us to
|
||||
if x == y && readdpi {
|
||||
switch units {
|
||||
// if units is 1 then measurement is px/meter
|
||||
case 1:
|
||||
info.dpi = float64(x) / 39.3701 // inches per meter
|
||||
default:
|
||||
info.dpi = float64(x)
|
||||
}
|
||||
}
|
||||
_ = buf.Next(4)
|
||||
default:
|
||||
// dbg("default")
|
||||
_ = buf.Next(n + 4)
|
||||
}
|
||||
if loop {
|
||||
loop = n > 0
|
||||
}
|
||||
}
|
||||
if colspace == "Indexed" && len(pal) == 0 {
|
||||
f.err = fmt.Errorf("missing palette in PNG buffer")
|
||||
}
|
||||
info.w = float64(w)
|
||||
info.h = float64(h)
|
||||
info.cs = colspace
|
||||
info.bpc = int(bpc)
|
||||
info.f = "FlateDecode"
|
||||
info.dp = dp
|
||||
info.pal = pal
|
||||
info.trns = trns
|
||||
// dbg("ct [%d]", ct)
|
||||
if ct >= 4 {
|
||||
// Separate alpha and color channels
|
||||
var err error
|
||||
data, err = sliceUncompress(data)
|
||||
if err != nil {
|
||||
f.err = err
|
||||
return
|
||||
}
|
||||
var color, alpha bytes.Buffer
|
||||
if ct == 4 {
|
||||
// Gray image
|
||||
width := int(w)
|
||||
height := int(h)
|
||||
length := 2 * width
|
||||
var pos, elPos int
|
||||
for i := 0; i < height; i++ {
|
||||
pos = (1 + length) * i
|
||||
color.WriteByte(data[pos])
|
||||
alpha.WriteByte(data[pos])
|
||||
elPos = pos + 1
|
||||
for k := 0; k < width; k++ {
|
||||
color.WriteByte(data[elPos])
|
||||
alpha.WriteByte(data[elPos+1])
|
||||
elPos += 2
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// RGB image
|
||||
width := int(w)
|
||||
height := int(h)
|
||||
length := 4 * width
|
||||
var pos, elPos int
|
||||
for i := 0; i < height; i++ {
|
||||
pos = (1 + length) * i
|
||||
color.WriteByte(data[pos])
|
||||
alpha.WriteByte(data[pos])
|
||||
elPos = pos + 1
|
||||
for k := 0; k < width; k++ {
|
||||
color.Write(data[elPos : elPos+3])
|
||||
alpha.WriteByte(data[elPos+3])
|
||||
elPos += 4
|
||||
}
|
||||
}
|
||||
}
|
||||
data = sliceCompress(color.Bytes())
|
||||
info.smask = sliceCompress(alpha.Bytes())
|
||||
if f.pdfVersion < "1.4" {
|
||||
f.pdfVersion = "1.4"
|
||||
}
|
||||
}
|
||||
info.data = data
|
||||
return
|
||||
}
|
114
vendor/github.com/jung-kurt/gofpdf/protect.go
generated
vendored
Normal file
114
vendor/github.com/jung-kurt/gofpdf/protect.go
generated
vendored
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2014 Kurt Jung (Gmail: kurt.w.jung)
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
// PDF protection is adapted from the work of Klemen VODOPIVEC for the fpdf
|
||||
// product.
|
||||
|
||||
package gofpdf
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/rc4"
|
||||
"encoding/binary"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
// Advisory bitflag constants that control document activities
|
||||
const (
|
||||
CnProtectPrint = 4
|
||||
CnProtectModify = 8
|
||||
CnProtectCopy = 16
|
||||
CnProtectAnnotForms = 32
|
||||
)
|
||||
|
||||
type protectType struct {
|
||||
encrypted bool
|
||||
uValue []byte
|
||||
oValue []byte
|
||||
pValue int
|
||||
padding []byte
|
||||
encryptionKey []byte
|
||||
objNum int
|
||||
rc4cipher *rc4.Cipher
|
||||
rc4n uint32 // Object number associated with rc4 cipher
|
||||
}
|
||||
|
||||
func (p *protectType) rc4(n uint32, buf *[]byte) {
|
||||
if p.rc4cipher == nil || p.rc4n != n {
|
||||
p.rc4cipher, _ = rc4.NewCipher(p.objectKey(n))
|
||||
p.rc4n = n
|
||||
}
|
||||
p.rc4cipher.XORKeyStream(*buf, *buf)
|
||||
}
|
||||
|
||||
func (p *protectType) objectKey(n uint32) []byte {
|
||||
var nbuf, b []byte
|
||||
nbuf = make([]byte, 8, 8)
|
||||
binary.LittleEndian.PutUint32(nbuf, n)
|
||||
b = append(b, p.encryptionKey...)
|
||||
b = append(b, nbuf[0], nbuf[1], nbuf[2], 0, 0)
|
||||
s := md5.Sum(b)
|
||||
return s[0:10]
|
||||
}
|
||||
|
||||
func oValueGen(userPass, ownerPass []byte) (v []byte) {
|
||||
var c *rc4.Cipher
|
||||
tmp := md5.Sum(ownerPass)
|
||||
c, _ = rc4.NewCipher(tmp[0:5])
|
||||
size := len(userPass)
|
||||
v = make([]byte, size, size)
|
||||
c.XORKeyStream(v, userPass)
|
||||
return
|
||||
}
|
||||
|
||||
func (p *protectType) uValueGen() (v []byte) {
|
||||
var c *rc4.Cipher
|
||||
c, _ = rc4.NewCipher(p.encryptionKey)
|
||||
size := len(p.padding)
|
||||
v = make([]byte, size, size)
|
||||
c.XORKeyStream(v, p.padding)
|
||||
return
|
||||
}
|
||||
|
||||
func (p *protectType) setProtection(privFlag byte, userPassStr, ownerPassStr string) {
|
||||
privFlag = 192 | (privFlag & (CnProtectCopy | CnProtectModify | CnProtectPrint | CnProtectAnnotForms))
|
||||
p.padding = []byte{
|
||||
0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41,
|
||||
0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
|
||||
0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80,
|
||||
0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A,
|
||||
}
|
||||
userPass := []byte(userPassStr)
|
||||
var ownerPass []byte
|
||||
if ownerPassStr == "" {
|
||||
ownerPass = make([]byte, 8, 8)
|
||||
binary.LittleEndian.PutUint64(ownerPass, uint64(rand.Int63()))
|
||||
} else {
|
||||
ownerPass = []byte(ownerPassStr)
|
||||
}
|
||||
userPass = append(userPass, p.padding...)[0:32]
|
||||
ownerPass = append(ownerPass, p.padding...)[0:32]
|
||||
p.encrypted = true
|
||||
p.oValue = oValueGen(userPass, ownerPass)
|
||||
var buf []byte
|
||||
buf = append(buf, userPass...)
|
||||
buf = append(buf, p.oValue...)
|
||||
buf = append(buf, privFlag, 0xff, 0xff, 0xff)
|
||||
sum := md5.Sum(buf)
|
||||
p.encryptionKey = sum[0:5]
|
||||
p.uValue = p.uValueGen()
|
||||
p.pValue = -(int(privFlag^255) + 1)
|
||||
}
|
53
vendor/github.com/jung-kurt/gofpdf/splittext.go
generated
vendored
Normal file
53
vendor/github.com/jung-kurt/gofpdf/splittext.go
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
package gofpdf
|
||||
|
||||
import (
|
||||
"math"
|
||||
// "strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// SplitText splits UTF-8 encoded text into several lines using the current
|
||||
// font. Each line has its length limited to a maximum width given by w. This
|
||||
// function can be used to determine the total height of wrapped text for
|
||||
// vertical placement purposes.
|
||||
func (f *Fpdf) SplitText(txt string, w float64) (lines []string) {
|
||||
cw := f.currentFont.Cw
|
||||
wmax := int(math.Ceil((w - 2*f.cMargin) * 1000 / f.fontSize))
|
||||
s := []rune(txt) // Return slice of UTF-8 runes
|
||||
nb := len(s)
|
||||
for nb > 0 && s[nb-1] == '\n' {
|
||||
nb--
|
||||
}
|
||||
s = s[0:nb]
|
||||
sep := -1
|
||||
i := 0
|
||||
j := 0
|
||||
l := 0
|
||||
for i < nb {
|
||||
c := s[i]
|
||||
l += cw[c]
|
||||
if unicode.IsSpace(c) || isChinese(c) {
|
||||
sep = i
|
||||
}
|
||||
if c == '\n' || l > wmax {
|
||||
if sep == -1 {
|
||||
if i == j {
|
||||
i++
|
||||
}
|
||||
sep = i
|
||||
} else {
|
||||
i = sep + 1
|
||||
}
|
||||
lines = append(lines, string(s[j:sep]))
|
||||
sep = -1
|
||||
j = i
|
||||
l = 0
|
||||
} else {
|
||||
i++
|
||||
}
|
||||
}
|
||||
if i != j {
|
||||
lines = append(lines, string(s[j:i]))
|
||||
}
|
||||
return lines
|
||||
}
|
184
vendor/github.com/jung-kurt/gofpdf/spotcolor.go
generated
vendored
Normal file
184
vendor/github.com/jung-kurt/gofpdf/spotcolor.go
generated
vendored
Normal file
@ -0,0 +1,184 @@
|
||||
// Copyright (c) Kurt Jung (Gmail: kurt.w.jung)
|
||||
//
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
// Adapted from http://www.fpdf.org/en/script/script89.php by Olivier PLATHEY
|
||||
|
||||
package gofpdf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func byteBound(v byte) byte {
|
||||
if v > 100 {
|
||||
return 100
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// AddSpotColor adds an ink-based CMYK color to the gofpdf instance and
|
||||
// associates it with the specified name. The individual components specify
|
||||
// percentages ranging from 0 to 100. Values above this are quietly capped to
|
||||
// 100. An error occurs if the specified name is already associated with a
|
||||
// color.
|
||||
func (f *Fpdf) AddSpotColor(nameStr string, c, m, y, k byte) {
|
||||
if f.err == nil {
|
||||
_, ok := f.spotColorMap[nameStr]
|
||||
if !ok {
|
||||
id := len(f.spotColorMap) + 1
|
||||
f.spotColorMap[nameStr] = spotColorType{
|
||||
id: id,
|
||||
val: cmykColorType{
|
||||
c: byteBound(c),
|
||||
m: byteBound(m),
|
||||
y: byteBound(y),
|
||||
k: byteBound(k),
|
||||
},
|
||||
}
|
||||
} else {
|
||||
f.err = fmt.Errorf("name \"%s\" is already associated with a spot color", nameStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Fpdf) getSpotColor(nameStr string) (clr spotColorType, ok bool) {
|
||||
if f.err == nil {
|
||||
clr, ok = f.spotColorMap[nameStr]
|
||||
if !ok {
|
||||
f.err = fmt.Errorf("spot color name \"%s\" is not registered", nameStr)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SetDrawSpotColor sets the current draw color to the spot color associated
|
||||
// with nameStr. An error occurs if the name is not associated with a color.
|
||||
// The value for tint ranges from 0 (no intensity) to 100 (full intensity). It
|
||||
// is quietly bounded to this range.
|
||||
func (f *Fpdf) SetDrawSpotColor(nameStr string, tint byte) {
|
||||
var clr spotColorType
|
||||
var ok bool
|
||||
|
||||
clr, ok = f.getSpotColor(nameStr)
|
||||
if ok {
|
||||
f.color.draw.mode = colorModeSpot
|
||||
f.color.draw.spotStr = nameStr
|
||||
f.color.draw.str = sprintf("/CS%d CS %.3f SCN", clr.id, float64(byteBound(tint))/100)
|
||||
if f.page > 0 {
|
||||
f.out(f.color.draw.str)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SetFillSpotColor sets the current fill color to the spot color associated
|
||||
// with nameStr. An error occurs if the name is not associated with a color.
|
||||
// The value for tint ranges from 0 (no intensity) to 100 (full intensity). It
|
||||
// is quietly bounded to this range.
|
||||
func (f *Fpdf) SetFillSpotColor(nameStr string, tint byte) {
|
||||
var clr spotColorType
|
||||
var ok bool
|
||||
|
||||
clr, ok = f.getSpotColor(nameStr)
|
||||
if ok {
|
||||
f.color.fill.mode = colorModeSpot
|
||||
f.color.fill.spotStr = nameStr
|
||||
f.color.fill.str = sprintf("/CS%d cs %.3f scn", clr.id, float64(byteBound(tint))/100)
|
||||
f.colorFlag = f.color.fill.str != f.color.text.str
|
||||
if f.page > 0 {
|
||||
f.out(f.color.fill.str)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SetTextSpotColor sets the current text color to the spot color associated
|
||||
// with nameStr. An error occurs if the name is not associated with a color.
|
||||
// The value for tint ranges from 0 (no intensity) to 100 (full intensity). It
|
||||
// is quietly bounded to this range.
|
||||
func (f *Fpdf) SetTextSpotColor(nameStr string, tint byte) {
|
||||
var clr spotColorType
|
||||
var ok bool
|
||||
|
||||
clr, ok = f.getSpotColor(nameStr)
|
||||
if ok {
|
||||
f.color.text.mode = colorModeSpot
|
||||
f.color.text.spotStr = nameStr
|
||||
f.color.text.str = sprintf("/CS%d cs %.3f scn", clr.id, float64(byteBound(tint))/100)
|
||||
f.colorFlag = f.color.text.str != f.color.text.str
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Fpdf) returnSpotColor(clr colorType) (name string, c, m, y, k byte) {
|
||||
var spotClr spotColorType
|
||||
var ok bool
|
||||
|
||||
name = clr.spotStr
|
||||
if name != "" {
|
||||
spotClr, ok = f.getSpotColor(name)
|
||||
if ok {
|
||||
c = spotClr.val.c
|
||||
m = spotClr.val.m
|
||||
y = spotClr.val.y
|
||||
k = spotClr.val.k
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetDrawSpotColor returns the most recently used spot color information for
|
||||
// drawing. This will not be the current drawing color if some other color type
|
||||
// such as RGB is active. If no spot color has been set for drawing, zero
|
||||
// values are returned.
|
||||
func (f *Fpdf) GetDrawSpotColor() (name string, c, m, y, k byte) {
|
||||
return f.returnSpotColor(f.color.draw)
|
||||
}
|
||||
|
||||
// GetTextSpotColor returns the most recently used spot color information for
|
||||
// text output. This will not be the current text color if some other color
|
||||
// type such as RGB is active. If no spot color has been set for text, zero
|
||||
// values are returned.
|
||||
func (f *Fpdf) GetTextSpotColor() (name string, c, m, y, k byte) {
|
||||
return f.returnSpotColor(f.color.text)
|
||||
}
|
||||
|
||||
// GetFillSpotColor returns the most recently used spot color information for
|
||||
// fill output. This will not be the current fill color if some other color
|
||||
// type such as RGB is active. If no fill spot color has been set, zero values
|
||||
// are returned.
|
||||
func (f *Fpdf) GetFillSpotColor() (name string, c, m, y, k byte) {
|
||||
return f.returnSpotColor(f.color.fill)
|
||||
}
|
||||
|
||||
func (f *Fpdf) putSpotColors() {
|
||||
for k, v := range f.spotColorMap {
|
||||
f.newobj()
|
||||
f.outf("[/Separation /%s", strings.Replace(k, " ", "#20", -1))
|
||||
f.out("/DeviceCMYK <<")
|
||||
f.out("/Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0] ")
|
||||
f.outf("/C1 [%.3f %.3f %.3f %.3f] ", float64(v.val.c)/100, float64(v.val.m)/100,
|
||||
float64(v.val.y)/100, float64(v.val.k)/100)
|
||||
f.out("/FunctionType 2 /Domain [0 1] /N 1>>]")
|
||||
f.out("endobj")
|
||||
v.objID = f.n
|
||||
f.spotColorMap[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Fpdf) spotColorPutResourceDict() {
|
||||
f.out("/ColorSpace <<")
|
||||
for _, clr := range f.spotColorMap {
|
||||
f.outf("/CS%d %d 0 R", clr.id, clr.objID)
|
||||
}
|
||||
f.out(">>")
|
||||
}
|
35
vendor/github.com/jung-kurt/gofpdf/subwrite.go
generated
vendored
Normal file
35
vendor/github.com/jung-kurt/gofpdf/subwrite.go
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
package gofpdf
|
||||
|
||||
// Adapted from http://www.fpdf.org/en/script/script61.php by Wirus and released with the FPDF license.
|
||||
|
||||
// SubWrite prints text from the current position in the same way as Write().
|
||||
// ht is the line height in the unit of measure specified in New(). str
|
||||
// specifies the text to write. subFontSize is the size of the font in points.
|
||||
// subOffset is the vertical offset of the text in points; a positive value
|
||||
// indicates a superscript, a negative value indicates a subscript. link is the
|
||||
// identifier returned by AddLink() or 0 for no internal link. linkStr is a
|
||||
// target URL or empty for no external link. A non--zero value for link takes
|
||||
// precedence over linkStr.
|
||||
//
|
||||
// The SubWrite example demonstrates this method.
|
||||
func (f *Fpdf) SubWrite(ht float64, str string, subFontSize, subOffset float64, link int, linkStr string) {
|
||||
if f.err != nil {
|
||||
return
|
||||
}
|
||||
// resize font
|
||||
subFontSizeOld := f.fontSizePt
|
||||
f.SetFontSize(subFontSize)
|
||||
// reposition y
|
||||
subOffset = (((subFontSize - subFontSizeOld) / f.k) * 0.3) + (subOffset / f.k)
|
||||
subX := f.x
|
||||
subY := f.y
|
||||
f.SetXY(subX, subY-subOffset)
|
||||
//Output text
|
||||
f.write(ht, str, link, linkStr)
|
||||
// restore y position
|
||||
subX = f.x
|
||||
subY = f.y
|
||||
f.SetXY(subX, subY+subOffset)
|
||||
// restore font size
|
||||
f.SetFontSize(subFontSizeOld)
|
||||
}
|
246
vendor/github.com/jung-kurt/gofpdf/svgbasic.go
generated
vendored
Normal file
246
vendor/github.com/jung-kurt/gofpdf/svgbasic.go
generated
vendored
Normal file
@ -0,0 +1,246 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Kurt Jung (Gmail: kurt.w.jung)
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package gofpdf
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var pathCmdSub *strings.Replacer
|
||||
|
||||
func init() {
|
||||
// Handle permitted constructions like "100L200,230"
|
||||
pathCmdSub = strings.NewReplacer(",", " ",
|
||||
"L", " L ", "l", " l ",
|
||||
"C", " C ", "c", " c ",
|
||||
"M", " M ", "m", " m ",
|
||||
"H", " H ", "h", " h ",
|
||||
"V", " V ", "v", " v ",
|
||||
"Q", " Q ", "q", " q ",
|
||||
"Z", " Z ", "z", " z ")
|
||||
}
|
||||
|
||||
// SVGBasicSegmentType describes a single curve or position segment
|
||||
type SVGBasicSegmentType struct {
|
||||
Cmd byte // See http://www.w3.org/TR/SVG/paths.html for path command structure
|
||||
Arg [6]float64
|
||||
}
|
||||
|
||||
func absolutizePath(segs []SVGBasicSegmentType) {
|
||||
var x, y float64
|
||||
var segPtr *SVGBasicSegmentType
|
||||
adjust := func(pos int, adjX, adjY float64) {
|
||||
segPtr.Arg[pos] += adjX
|
||||
segPtr.Arg[pos+1] += adjY
|
||||
}
|
||||
for j, seg := range segs {
|
||||
segPtr = &segs[j]
|
||||
if j == 0 && seg.Cmd == 'm' {
|
||||
segPtr.Cmd = 'M'
|
||||
}
|
||||
switch segPtr.Cmd {
|
||||
case 'M':
|
||||
x = seg.Arg[0]
|
||||
y = seg.Arg[1]
|
||||
case 'm':
|
||||
adjust(0, x, y)
|
||||
segPtr.Cmd = 'M'
|
||||
x = segPtr.Arg[0]
|
||||
y = segPtr.Arg[1]
|
||||
case 'L':
|
||||
x = seg.Arg[0]
|
||||
y = seg.Arg[1]
|
||||
case 'l':
|
||||
adjust(0, x, y)
|
||||
segPtr.Cmd = 'L'
|
||||
x = segPtr.Arg[0]
|
||||
y = segPtr.Arg[1]
|
||||
case 'C':
|
||||
x = seg.Arg[4]
|
||||
y = seg.Arg[5]
|
||||
case 'c':
|
||||
adjust(0, x, y)
|
||||
adjust(2, x, y)
|
||||
adjust(4, x, y)
|
||||
segPtr.Cmd = 'C'
|
||||
x = segPtr.Arg[4]
|
||||
y = segPtr.Arg[5]
|
||||
case 'Q':
|
||||
x = seg.Arg[2]
|
||||
y = seg.Arg[3]
|
||||
case 'q':
|
||||
adjust(0, x, y)
|
||||
adjust(2, x, y)
|
||||
segPtr.Cmd = 'Q'
|
||||
x = segPtr.Arg[2]
|
||||
y = segPtr.Arg[3]
|
||||
case 'H':
|
||||
x = seg.Arg[0]
|
||||
case 'h':
|
||||
segPtr.Arg[0] += x
|
||||
segPtr.Cmd = 'H'
|
||||
x += seg.Arg[0]
|
||||
case 'V':
|
||||
y = seg.Arg[0]
|
||||
case 'v':
|
||||
segPtr.Arg[0] += y
|
||||
segPtr.Cmd = 'V'
|
||||
y += seg.Arg[0]
|
||||
case 'z':
|
||||
segPtr.Cmd = 'Z'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func pathParse(pathStr string) (segs []SVGBasicSegmentType, err error) {
|
||||
var seg SVGBasicSegmentType
|
||||
var j, argJ, argCount, prevArgCount int
|
||||
setup := func(n int) {
|
||||
// It is not strictly necessary to clear arguments, but result may be clearer
|
||||
// to caller
|
||||
for j := 0; j < len(seg.Arg); j++ {
|
||||
seg.Arg[j] = 0.0
|
||||
}
|
||||
argJ = 0
|
||||
argCount = n
|
||||
prevArgCount = n
|
||||
}
|
||||
var str string
|
||||
var c byte
|
||||
pathStr = pathCmdSub.Replace(pathStr)
|
||||
strList := strings.Fields(pathStr)
|
||||
count := len(strList)
|
||||
for j = 0; j < count && err == nil; j++ {
|
||||
str = strList[j]
|
||||
if argCount == 0 { // Look for path command or argument continuation
|
||||
c = str[0]
|
||||
if c == '-' || (c >= '0' && c <= '9') { // More arguments
|
||||
if j > 0 {
|
||||
setup(prevArgCount)
|
||||
// Repeat previous action
|
||||
if seg.Cmd == 'M' {
|
||||
seg.Cmd = 'L'
|
||||
} else if seg.Cmd == 'm' {
|
||||
seg.Cmd = 'l'
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("expecting SVG path command at first position, got %s", str)
|
||||
}
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
if argCount == 0 {
|
||||
seg.Cmd = str[0]
|
||||
switch seg.Cmd {
|
||||
case 'M', 'm': // Absolute/relative moveto: x, y
|
||||
setup(2)
|
||||
case 'C', 'c': // Absolute/relative Bézier curve: cx0, cy0, cx1, cy1, x1, y1
|
||||
setup(6)
|
||||
case 'H', 'h': // Absolute/relative horizontal line to: x
|
||||
setup(1)
|
||||
case 'L', 'l': // Absolute/relative lineto: x, y
|
||||
setup(2)
|
||||
case 'Q', 'q': // Absolute/relative quadratic curve: x0, y0, x1, y1
|
||||
setup(4)
|
||||
case 'V', 'v': // Absolute/relative vertical line to: y
|
||||
setup(1)
|
||||
case 'Z', 'z': // closepath instruction (takes no arguments)
|
||||
segs = append(segs, seg)
|
||||
default:
|
||||
err = fmt.Errorf("expecting SVG path command at position %d, got %s", j, str)
|
||||
}
|
||||
} else {
|
||||
seg.Arg[argJ], err = strconv.ParseFloat(str, 64)
|
||||
if err == nil {
|
||||
argJ++
|
||||
argCount--
|
||||
if argCount == 0 {
|
||||
segs = append(segs, seg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
if argCount == 0 {
|
||||
absolutizePath(segs)
|
||||
} else {
|
||||
err = fmt.Errorf("expecting additional (%d) numeric arguments", argCount)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SVGBasicType aggregates the information needed to describe a multi-segment
|
||||
// basic vector image
|
||||
type SVGBasicType struct {
|
||||
Wd, Ht float64
|
||||
Segments [][]SVGBasicSegmentType
|
||||
}
|
||||
|
||||
// SVGBasicParse parses a simple scalable vector graphics (SVG) buffer into a
|
||||
// descriptor. Only a small subset of the SVG standard, in particular the path
|
||||
// information generated by jSignature, is supported. The returned path data
|
||||
// includes only the commands 'M' (absolute moveto: x, y), 'L' (absolute
|
||||
// lineto: x, y), 'C' (absolute cubic Bézier curve: cx0, cy0, cx1, cy1,
|
||||
// x1,y1), 'Q' (absolute quadratic Bézier curve: x0, y0, x1, y1) and 'Z'
|
||||
// (closepath).
|
||||
func SVGBasicParse(buf []byte) (sig SVGBasicType, err error) {
|
||||
type pathType struct {
|
||||
D string `xml:"d,attr"`
|
||||
}
|
||||
type srcType struct {
|
||||
Wd float64 `xml:"width,attr"`
|
||||
Ht float64 `xml:"height,attr"`
|
||||
Paths []pathType `xml:"path"`
|
||||
}
|
||||
var src srcType
|
||||
err = xml.Unmarshal(buf, &src)
|
||||
if err == nil {
|
||||
if src.Wd > 0 && src.Ht > 0 {
|
||||
sig.Wd, sig.Ht = src.Wd, src.Ht
|
||||
var segs []SVGBasicSegmentType
|
||||
for _, path := range src.Paths {
|
||||
if err == nil {
|
||||
segs, err = pathParse(path.D)
|
||||
if err == nil {
|
||||
sig.Segments = append(sig.Segments, segs)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("unacceptable values for basic SVG extent: %.2f x %.2f",
|
||||
sig.Wd, sig.Ht)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SVGBasicFileParse parses a simple scalable vector graphics (SVG) file into a
|
||||
// basic descriptor. The SVGBasicWrite() example demonstrates this method.
|
||||
func SVGBasicFileParse(svgFileStr string) (sig SVGBasicType, err error) {
|
||||
var buf []byte
|
||||
buf, err = ioutil.ReadFile(svgFileStr)
|
||||
if err == nil {
|
||||
sig, err = SVGBasicParse(buf)
|
||||
}
|
||||
return
|
||||
}
|
85
vendor/github.com/jung-kurt/gofpdf/svgwrite.go
generated
vendored
Normal file
85
vendor/github.com/jung-kurt/gofpdf/svgwrite.go
generated
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Kurt Jung (Gmail: kurt.w.jung)
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package gofpdf
|
||||
|
||||
// SVGBasicWrite renders the paths encoded in the basic SVG image specified by
|
||||
// sb. The scale value is used to convert the coordinates in the path to the
|
||||
// unit of measure specified in New(). The current position (as set with a call
|
||||
// to SetXY()) is used as the origin of the image. The current line cap style
|
||||
// (as set with SetLineCapStyle()), line width (as set with SetLineWidth()),
|
||||
// and draw color (as set with SetDrawColor()) are used in drawing the image
|
||||
// paths.
|
||||
func (f *Fpdf) SVGBasicWrite(sb *SVGBasicType, scale float64) {
|
||||
originX, originY := f.GetXY()
|
||||
var x, y, newX, newY float64
|
||||
var cx0, cy0, cx1, cy1 float64
|
||||
var path []SVGBasicSegmentType
|
||||
var seg SVGBasicSegmentType
|
||||
var startX, startY float64
|
||||
sval := func(origin float64, arg int) float64 {
|
||||
return origin + scale*seg.Arg[arg]
|
||||
}
|
||||
xval := func(arg int) float64 {
|
||||
return sval(originX, arg)
|
||||
}
|
||||
yval := func(arg int) float64 {
|
||||
return sval(originY, arg)
|
||||
}
|
||||
val := func(arg int) (float64, float64) {
|
||||
return xval(arg), yval(arg + 1)
|
||||
}
|
||||
for j := 0; j < len(sb.Segments) && f.Ok(); j++ {
|
||||
path = sb.Segments[j]
|
||||
for k := 0; k < len(path) && f.Ok(); k++ {
|
||||
seg = path[k]
|
||||
switch seg.Cmd {
|
||||
case 'M':
|
||||
x, y = val(0)
|
||||
startX, startY = x, y
|
||||
f.SetXY(x, y)
|
||||
case 'L':
|
||||
newX, newY = val(0)
|
||||
f.Line(x, y, newX, newY)
|
||||
x, y = newX, newY
|
||||
case 'C':
|
||||
cx0, cy0 = val(0)
|
||||
cx1, cy1 = val(2)
|
||||
newX, newY = val(4)
|
||||
f.CurveCubic(x, y, cx0, cy0, newX, newY, cx1, cy1, "D")
|
||||
x, y = newX, newY
|
||||
case 'Q':
|
||||
cx0, cy0 = val(0)
|
||||
newX, newY = val(2)
|
||||
f.Curve(x, y, cx0, cy0, newX, newY, "D")
|
||||
x, y = newX, newY
|
||||
case 'H':
|
||||
newX = xval(0)
|
||||
f.Line(x, y, newX, y)
|
||||
x = newX
|
||||
case 'V':
|
||||
newY = yval(0)
|
||||
f.Line(x, y, x, newY)
|
||||
y = newY
|
||||
case 'Z':
|
||||
f.Line(x, y, startX, startY)
|
||||
x, y = startX, startY
|
||||
default:
|
||||
f.SetErrorf("Unexpected path command '%c'", seg.Cmd)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
273
vendor/github.com/jung-kurt/gofpdf/template.go
generated
vendored
Normal file
273
vendor/github.com/jung-kurt/gofpdf/template.go
generated
vendored
Normal file
@ -0,0 +1,273 @@
|
||||
package gofpdf
|
||||
|
||||
/*
|
||||
* Copyright (c) 2015 Kurt Jung (Gmail: kurt.w.jung),
|
||||
* Marcus Downing, Jan Slabon (Setasign)
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// CreateTemplate defines a new template using the current page size.
|
||||
func (f *Fpdf) CreateTemplate(fn func(*Tpl)) Template {
|
||||
return newTpl(PointType{0, 0}, f.curPageSize, f.defOrientation, f.unitStr, f.fontDirStr, fn, f)
|
||||
}
|
||||
|
||||
// CreateTemplateCustom starts a template, using the given bounds.
|
||||
func (f *Fpdf) CreateTemplateCustom(corner PointType, size SizeType, fn func(*Tpl)) Template {
|
||||
return newTpl(corner, size, f.defOrientation, f.unitStr, f.fontDirStr, fn, f)
|
||||
}
|
||||
|
||||
// CreateTemplate creates a template that is not attached to any document.
|
||||
//
|
||||
// This function is deprecated; it incorrectly assumes that a page with a width
|
||||
// smaller than its height is oriented in portrait mode, otherwise it assumes
|
||||
// landscape mode. This causes problems when placing the template in a master
|
||||
// document where this condition does not apply. CreateTpl() is a similar
|
||||
// function that lets you specify the orientation to avoid this problem.
|
||||
func CreateTemplate(corner PointType, size SizeType, unitStr, fontDirStr string, fn func(*Tpl)) Template {
|
||||
orientationStr := "p"
|
||||
if size.Wd > size.Ht {
|
||||
orientationStr = "l"
|
||||
}
|
||||
|
||||
return CreateTpl(corner, size, orientationStr, unitStr, fontDirStr, fn)
|
||||
}
|
||||
|
||||
// CreateTpl creates a template not attached to any document
|
||||
func CreateTpl(corner PointType, size SizeType, orientationStr, unitStr, fontDirStr string, fn func(*Tpl)) Template {
|
||||
return newTpl(corner, size, orientationStr, unitStr, fontDirStr, fn, nil)
|
||||
}
|
||||
|
||||
// UseTemplate adds a template to the current page or another template,
|
||||
// using the size and position at which it was originally written.
|
||||
func (f *Fpdf) UseTemplate(t Template) {
|
||||
if t == nil {
|
||||
f.SetErrorf("template is nil")
|
||||
return
|
||||
}
|
||||
corner, size := t.Size()
|
||||
f.UseTemplateScaled(t, corner, size)
|
||||
}
|
||||
|
||||
// UseTemplateScaled adds a template to the current page or another template,
|
||||
// using the given page coordinates.
|
||||
func (f *Fpdf) UseTemplateScaled(t Template, corner PointType, size SizeType) {
|
||||
if t == nil {
|
||||
f.SetErrorf("template is nil")
|
||||
return
|
||||
}
|
||||
|
||||
// You have to add at least a page first
|
||||
if f.page <= 0 {
|
||||
f.SetErrorf("cannot use a template without first adding a page")
|
||||
return
|
||||
}
|
||||
|
||||
// make a note of the fact that we actually use this template, as well as any other templates,
|
||||
// images or fonts it uses
|
||||
f.templates[t.ID()] = t
|
||||
for _, tt := range t.Templates() {
|
||||
f.templates[tt.ID()] = tt
|
||||
}
|
||||
for name, ti := range t.Images() {
|
||||
name = sprintf("t%s-%s", t.ID(), name)
|
||||
f.images[name] = ti
|
||||
}
|
||||
|
||||
// template data
|
||||
_, templateSize := t.Size()
|
||||
scaleX := size.Wd / templateSize.Wd
|
||||
scaleY := size.Ht / templateSize.Ht
|
||||
tx := corner.X * f.k
|
||||
ty := (f.curPageSize.Ht - corner.Y - size.Ht) * f.k
|
||||
|
||||
f.outf("q %.4f 0 0 %.4f %.4f %.4f cm", scaleX, scaleY, tx, ty) // Translate
|
||||
f.outf("/TPL%s Do Q", t.ID())
|
||||
}
|
||||
|
||||
// Template is an object that can be written to, then used and re-used any number of times within a document.
|
||||
type Template interface {
|
||||
ID() string
|
||||
Size() (PointType, SizeType)
|
||||
Bytes() []byte
|
||||
Images() map[string]*ImageInfoType
|
||||
Templates() []Template
|
||||
NumPages() int
|
||||
FromPage(int) (Template, error)
|
||||
FromPages() []Template
|
||||
Serialize() ([]byte, error)
|
||||
gob.GobDecoder
|
||||
gob.GobEncoder
|
||||
}
|
||||
|
||||
func (f *Fpdf) templateFontCatalog() {
|
||||
var keyList []string
|
||||
var font fontDefType
|
||||
var key string
|
||||
f.out("/Font <<")
|
||||
for key = range f.fonts {
|
||||
keyList = append(keyList, key)
|
||||
}
|
||||
if f.catalogSort {
|
||||
sort.Strings(keyList)
|
||||
}
|
||||
for _, key = range keyList {
|
||||
font = f.fonts[key]
|
||||
f.outf("/F%s %d 0 R", font.i, font.N)
|
||||
}
|
||||
f.out(">>")
|
||||
}
|
||||
|
||||
// putTemplates writes the templates to the PDF
|
||||
func (f *Fpdf) putTemplates() {
|
||||
filter := ""
|
||||
if f.compress {
|
||||
filter = "/Filter /FlateDecode "
|
||||
}
|
||||
|
||||
templates := sortTemplates(f.templates, f.catalogSort)
|
||||
var t Template
|
||||
for _, t = range templates {
|
||||
corner, size := t.Size()
|
||||
|
||||
f.newobj()
|
||||
f.templateObjects[t.ID()] = f.n
|
||||
f.outf("<<%s/Type /XObject", filter)
|
||||
f.out("/Subtype /Form")
|
||||
f.out("/Formtype 1")
|
||||
f.outf("/BBox [%.2f %.2f %.2f %.2f]", corner.X*f.k, corner.Y*f.k, (corner.X+size.Wd)*f.k, (corner.Y+size.Ht)*f.k)
|
||||
if corner.X != 0 || corner.Y != 0 {
|
||||
f.outf("/Matrix [1 0 0 1 %.5f %.5f]", -corner.X*f.k*2, corner.Y*f.k*2)
|
||||
}
|
||||
|
||||
// Template's resource dictionary
|
||||
f.out("/Resources ")
|
||||
f.out("<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]")
|
||||
|
||||
f.templateFontCatalog()
|
||||
|
||||
tImages := t.Images()
|
||||
tTemplates := t.Templates()
|
||||
if len(tImages) > 0 || len(tTemplates) > 0 {
|
||||
f.out("/XObject <<")
|
||||
{
|
||||
var key string
|
||||
var keyList []string
|
||||
var ti *ImageInfoType
|
||||
for key = range tImages {
|
||||
keyList = append(keyList, key)
|
||||
}
|
||||
if gl.catalogSort {
|
||||
sort.Strings(keyList)
|
||||
}
|
||||
for _, key = range keyList {
|
||||
// for _, ti := range tImages {
|
||||
ti = tImages[key]
|
||||
f.outf("/I%s %d 0 R", ti.i, ti.n)
|
||||
}
|
||||
}
|
||||
for _, tt := range tTemplates {
|
||||
id := tt.ID()
|
||||
if objID, ok := f.templateObjects[id]; ok {
|
||||
f.outf("/TPL%s %d 0 R", id, objID)
|
||||
}
|
||||
}
|
||||
f.out(">>")
|
||||
}
|
||||
|
||||
f.out(">>")
|
||||
|
||||
// Write the template's byte stream
|
||||
buffer := t.Bytes()
|
||||
// fmt.Println("Put template bytes", string(buffer[:]))
|
||||
if f.compress {
|
||||
buffer = sliceCompress(buffer)
|
||||
}
|
||||
f.outf("/Length %d >>", len(buffer))
|
||||
f.putstream(buffer)
|
||||
f.out("endobj")
|
||||
}
|
||||
}
|
||||
|
||||
func templateKeyList(mp map[string]Template, sort bool) (keyList []string) {
|
||||
var key string
|
||||
for key = range mp {
|
||||
keyList = append(keyList, key)
|
||||
}
|
||||
if sort {
|
||||
gensort(len(keyList),
|
||||
func(a, b int) bool {
|
||||
return keyList[a] < keyList[b]
|
||||
},
|
||||
func(a, b int) {
|
||||
keyList[a], keyList[b] = keyList[b], keyList[a]
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// sortTemplates puts templates in a suitable order based on dependices
|
||||
func sortTemplates(templates map[string]Template, catalogSort bool) []Template {
|
||||
chain := make([]Template, 0, len(templates)*2)
|
||||
|
||||
// build a full set of dependency chains
|
||||
var keyList []string
|
||||
var key string
|
||||
var t Template
|
||||
keyList = templateKeyList(templates, catalogSort)
|
||||
for _, key = range keyList {
|
||||
t = templates[key]
|
||||
tlist := templateChainDependencies(t)
|
||||
for _, tt := range tlist {
|
||||
if tt != nil {
|
||||
chain = append(chain, tt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reduce that to make a simple list
|
||||
sorted := make([]Template, 0, len(templates))
|
||||
chain:
|
||||
for _, t := range chain {
|
||||
for _, already := range sorted {
|
||||
if t == already {
|
||||
continue chain
|
||||
}
|
||||
}
|
||||
sorted = append(sorted, t)
|
||||
}
|
||||
|
||||
return sorted
|
||||
}
|
||||
|
||||
// templateChainDependencies is a recursive function for determining the full chain of template dependencies
|
||||
func templateChainDependencies(template Template) []Template {
|
||||
requires := template.Templates()
|
||||
chain := make([]Template, len(requires)*2)
|
||||
for _, req := range requires {
|
||||
chain = append(chain, templateChainDependencies(req)...)
|
||||
}
|
||||
chain = append(chain, template)
|
||||
return chain
|
||||
}
|
||||
|
||||
// < 0002640 31 20 31 32 20 30 20 52 0a 2f 54 50 4c 32 20 31 |1 12 0 R./TPL2 1|
|
||||
// < 0002650 35 20 30 20 52 0a 2f 54 50 4c 31 20 31 34 20 30 |5 0 R./TPL1 14 0|
|
||||
|
||||
// > 0002640 31 20 31 32 20 30 20 52 0a 2f 54 50 4c 31 20 31 |1 12 0 R./TPL1 1|
|
||||
// > 0002650 34 20 30 20 52 0a 2f 54 50 4c 32 20 31 35 20 30 |4 0 R./TPL2 15 0|
|
299
vendor/github.com/jung-kurt/gofpdf/template_impl.go
generated
vendored
Normal file
299
vendor/github.com/jung-kurt/gofpdf/template_impl.go
generated
vendored
Normal file
@ -0,0 +1,299 @@
|
||||
package gofpdf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"encoding/gob"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
/*
|
||||
* Copyright (c) 2015 Kurt Jung (Gmail: kurt.w.jung),
|
||||
* Marcus Downing, Jan Slabon (Setasign)
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
// newTpl creates a template, copying graphics settings from a template if one is given
|
||||
func newTpl(corner PointType, size SizeType, orientationStr, unitStr, fontDirStr string, fn func(*Tpl), copyFrom *Fpdf) Template {
|
||||
sizeStr := ""
|
||||
|
||||
fpdf := fpdfNew(orientationStr, unitStr, sizeStr, fontDirStr, size)
|
||||
tpl := Tpl{*fpdf}
|
||||
if copyFrom != nil {
|
||||
tpl.loadParamsFromFpdf(copyFrom)
|
||||
}
|
||||
tpl.Fpdf.AddPage()
|
||||
fn(&tpl)
|
||||
|
||||
bytes := make([][]byte, len(tpl.Fpdf.pages))
|
||||
// skip the first page as it will always be empty
|
||||
for x := 1; x < len(bytes); x++ {
|
||||
bytes[x] = tpl.Fpdf.pages[x].Bytes()
|
||||
}
|
||||
|
||||
templates := make([]Template, 0, len(tpl.Fpdf.templates))
|
||||
for _, key := range templateKeyList(tpl.Fpdf.templates, true) {
|
||||
templates = append(templates, tpl.Fpdf.templates[key])
|
||||
}
|
||||
images := tpl.Fpdf.images
|
||||
|
||||
template := FpdfTpl{corner, size, bytes, images, templates, tpl.Fpdf.page}
|
||||
return &template
|
||||
}
|
||||
|
||||
// FpdfTpl is a concrete implementation of the Template interface.
|
||||
type FpdfTpl struct {
|
||||
corner PointType
|
||||
size SizeType
|
||||
bytes [][]byte
|
||||
images map[string]*ImageInfoType
|
||||
templates []Template
|
||||
page int
|
||||
}
|
||||
|
||||
// ID returns the global template identifier
|
||||
func (t *FpdfTpl) ID() string {
|
||||
return fmt.Sprintf("%x", sha1.Sum(t.Bytes()))
|
||||
}
|
||||
|
||||
// Size gives the bounding dimensions of this template
|
||||
func (t *FpdfTpl) Size() (corner PointType, size SizeType) {
|
||||
return t.corner, t.size
|
||||
}
|
||||
|
||||
// Bytes returns the actual template data, not including resources
|
||||
func (t *FpdfTpl) Bytes() []byte {
|
||||
return t.bytes[t.page]
|
||||
}
|
||||
|
||||
// FromPage creates a new template from a specific Page
|
||||
func (t *FpdfTpl) FromPage(page int) (Template, error) {
|
||||
// pages start at 1
|
||||
if page == 0 {
|
||||
return nil, errors.New("Pages start at 1 No template will have a page 0")
|
||||
}
|
||||
|
||||
if page > t.NumPages() {
|
||||
return nil, fmt.Errorf("The template does not have a page %d", page)
|
||||
}
|
||||
// if it is already pointing to the correct page
|
||||
// there is no need to create a new template
|
||||
if t.page == page {
|
||||
return t, nil
|
||||
}
|
||||
|
||||
t2 := *t
|
||||
t2.page = page
|
||||
return &t2, nil
|
||||
}
|
||||
|
||||
// FromPages creates a template slice with all the pages within a template.
|
||||
func (t *FpdfTpl) FromPages() []Template {
|
||||
p := make([]Template, t.NumPages())
|
||||
for x := 1; x <= t.NumPages(); x++ {
|
||||
// the only error is when accessing a
|
||||
// non existing template... that can't happen
|
||||
// here
|
||||
p[x-1], _ = t.FromPage(x)
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
// Images returns a list of the images used in this template
|
||||
func (t *FpdfTpl) Images() map[string]*ImageInfoType {
|
||||
return t.images
|
||||
}
|
||||
|
||||
// Templates returns a list of templates used in this template
|
||||
func (t *FpdfTpl) Templates() []Template {
|
||||
return t.templates
|
||||
}
|
||||
|
||||
// NumPages returns the number of available pages within the template. Look at FromPage and FromPages on access to that content.
|
||||
func (t *FpdfTpl) NumPages() int {
|
||||
// the first page is empty to
|
||||
// make the pages begin at one
|
||||
return len(t.bytes) - 1
|
||||
}
|
||||
|
||||
// Serialize turns a template into a byte string for later deserialization
|
||||
func (t *FpdfTpl) Serialize() ([]byte, error) {
|
||||
b := new(bytes.Buffer)
|
||||
enc := gob.NewEncoder(b)
|
||||
err := enc.Encode(t)
|
||||
|
||||
return b.Bytes(), err
|
||||
}
|
||||
|
||||
// DeserializeTemplate creaties a template from a previously serialized
|
||||
// template
|
||||
func DeserializeTemplate(b []byte) (Template, error) {
|
||||
tpl := new(FpdfTpl)
|
||||
dec := gob.NewDecoder(bytes.NewBuffer(b))
|
||||
err := dec.Decode(tpl)
|
||||
return tpl, err
|
||||
}
|
||||
|
||||
// childrenImages returns the next layer of children images, it doesn't dig into
|
||||
// children of children. Applies template namespace to keys to ensure
|
||||
// no collisions. See UseTemplateScaled
|
||||
func (t *FpdfTpl) childrenImages() map[string]*ImageInfoType {
|
||||
childrenImgs := make(map[string]*ImageInfoType)
|
||||
|
||||
for x := 0; x < len(t.templates); x++ {
|
||||
imgs := t.templates[x].Images()
|
||||
for key, val := range imgs {
|
||||
name := sprintf("t%s-%s", t.templates[x].ID(), key)
|
||||
childrenImgs[name] = val
|
||||
}
|
||||
}
|
||||
|
||||
return childrenImgs
|
||||
}
|
||||
|
||||
// childrensTemplates returns the next layer of children templates, it doesn't dig into
|
||||
// children of children.
|
||||
func (t *FpdfTpl) childrensTemplates() []Template {
|
||||
childrenTmpls := make([]Template, 0)
|
||||
|
||||
for x := 0; x < len(t.templates); x++ {
|
||||
tmpls := t.templates[x].Templates()
|
||||
childrenTmpls = append(childrenTmpls, tmpls...)
|
||||
}
|
||||
|
||||
return childrenTmpls
|
||||
}
|
||||
|
||||
// GobEncode encodes the receiving template into a byte buffer. Use GobDecode
|
||||
// to decode the byte buffer back to a template.
|
||||
func (t *FpdfTpl) GobEncode() ([]byte, error) {
|
||||
w := new(bytes.Buffer)
|
||||
encoder := gob.NewEncoder(w)
|
||||
|
||||
childrensTemplates := t.childrensTemplates()
|
||||
firstClassTemplates := make([]Template, 0)
|
||||
|
||||
found_continue:
|
||||
for x := 0; x < len(t.templates); x++ {
|
||||
for y := 0; y < len(childrensTemplates); y++ {
|
||||
if childrensTemplates[y].ID() == t.templates[x].ID() {
|
||||
continue found_continue
|
||||
}
|
||||
}
|
||||
|
||||
firstClassTemplates = append(firstClassTemplates, t.templates[x])
|
||||
}
|
||||
err := encoder.Encode(firstClassTemplates)
|
||||
|
||||
childrenImgs := t.childrenImages()
|
||||
firstClassImgs := make(map[string]*ImageInfoType)
|
||||
|
||||
for key, img := range t.images {
|
||||
if _, ok := childrenImgs[key]; !ok {
|
||||
firstClassImgs[key] = img
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
err = encoder.Encode(firstClassImgs)
|
||||
}
|
||||
if err == nil {
|
||||
err = encoder.Encode(t.corner)
|
||||
}
|
||||
if err == nil {
|
||||
err = encoder.Encode(t.size)
|
||||
}
|
||||
if err == nil {
|
||||
err = encoder.Encode(t.bytes)
|
||||
}
|
||||
if err == nil {
|
||||
err = encoder.Encode(t.page)
|
||||
}
|
||||
|
||||
return w.Bytes(), err
|
||||
}
|
||||
|
||||
// GobDecode decodes the specified byte buffer into the receiving template.
|
||||
func (t *FpdfTpl) GobDecode(buf []byte) error {
|
||||
r := bytes.NewBuffer(buf)
|
||||
decoder := gob.NewDecoder(r)
|
||||
|
||||
firstClassTemplates := make([]*FpdfTpl, 0)
|
||||
err := decoder.Decode(&firstClassTemplates)
|
||||
t.templates = make([]Template, len(firstClassTemplates))
|
||||
|
||||
for x := 0; x < len(t.templates); x++ {
|
||||
t.templates[x] = Template(firstClassTemplates[x])
|
||||
}
|
||||
|
||||
firstClassImages := t.childrenImages()
|
||||
|
||||
t.templates = append(t.childrensTemplates(), t.templates...)
|
||||
|
||||
t.images = make(map[string]*ImageInfoType)
|
||||
if err == nil {
|
||||
err = decoder.Decode(&t.images)
|
||||
}
|
||||
|
||||
for k, v := range firstClassImages {
|
||||
t.images[k] = v
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
err = decoder.Decode(&t.corner)
|
||||
}
|
||||
if err == nil {
|
||||
err = decoder.Decode(&t.size)
|
||||
}
|
||||
if err == nil {
|
||||
err = decoder.Decode(&t.bytes)
|
||||
}
|
||||
if err == nil {
|
||||
err = decoder.Decode(&t.page)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Tpl is an Fpdf used for writing a template. It has most of the facilities of
|
||||
// an Fpdf, but cannot add more pages. Tpl is used directly only during the
|
||||
// limited time a template is writable.
|
||||
type Tpl struct {
|
||||
Fpdf
|
||||
}
|
||||
|
||||
func (t *Tpl) loadParamsFromFpdf(f *Fpdf) {
|
||||
t.Fpdf.compress = false
|
||||
|
||||
t.Fpdf.k = f.k
|
||||
t.Fpdf.x = f.x
|
||||
t.Fpdf.y = f.y
|
||||
t.Fpdf.lineWidth = f.lineWidth
|
||||
t.Fpdf.capStyle = f.capStyle
|
||||
t.Fpdf.joinStyle = f.joinStyle
|
||||
|
||||
t.Fpdf.color.draw = f.color.draw
|
||||
t.Fpdf.color.fill = f.color.fill
|
||||
t.Fpdf.color.text = f.color.text
|
||||
|
||||
t.Fpdf.fonts = f.fonts
|
||||
t.Fpdf.currentFont = f.currentFont
|
||||
t.Fpdf.fontFamily = f.fontFamily
|
||||
t.Fpdf.fontSize = f.fontSize
|
||||
t.Fpdf.fontSizePt = f.fontSizePt
|
||||
t.Fpdf.fontStyle = f.fontStyle
|
||||
t.Fpdf.ws = f.ws
|
||||
}
|
374
vendor/github.com/jung-kurt/gofpdf/ttfparser.go
generated
vendored
Normal file
374
vendor/github.com/jung-kurt/gofpdf/ttfparser.go
generated
vendored
Normal file
@ -0,0 +1,374 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Kurt Jung (Gmail: kurt.w.jung)
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package gofpdf
|
||||
|
||||
// Utility to parse TTF font files
|
||||
// Version: 1.0
|
||||
// Date: 2011-06-18
|
||||
// Author: Olivier PLATHEY
|
||||
// Port to Go: Kurt Jung, 2013-07-15
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// TtfType contains metrics of a TrueType font.
|
||||
type TtfType struct {
|
||||
Embeddable bool
|
||||
UnitsPerEm uint16
|
||||
PostScriptName string
|
||||
Bold bool
|
||||
ItalicAngle int16
|
||||
IsFixedPitch bool
|
||||
TypoAscender int16
|
||||
TypoDescender int16
|
||||
UnderlinePosition int16
|
||||
UnderlineThickness int16
|
||||
Xmin, Ymin, Xmax, Ymax int16
|
||||
CapHeight int16
|
||||
Widths []uint16
|
||||
Chars map[uint16]uint16
|
||||
}
|
||||
|
||||
type ttfParser struct {
|
||||
rec TtfType
|
||||
f *os.File
|
||||
tables map[string]uint32
|
||||
numberOfHMetrics uint16
|
||||
numGlyphs uint16
|
||||
}
|
||||
|
||||
// TtfParse extracts various metrics from a TrueType font file.
|
||||
func TtfParse(fileStr string) (TtfRec TtfType, err error) {
|
||||
var t ttfParser
|
||||
t.f, err = os.Open(fileStr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
version, err := t.ReadStr(4)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if version == "OTTO" {
|
||||
err = fmt.Errorf("fonts based on PostScript outlines are not supported")
|
||||
return
|
||||
}
|
||||
if version != "\x00\x01\x00\x00" {
|
||||
err = fmt.Errorf("unrecognized file format")
|
||||
return
|
||||
}
|
||||
numTables := int(t.ReadUShort())
|
||||
t.Skip(3 * 2) // searchRange, entrySelector, rangeShift
|
||||
t.tables = make(map[string]uint32)
|
||||
var tag string
|
||||
for j := 0; j < numTables; j++ {
|
||||
tag, err = t.ReadStr(4)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
t.Skip(4) // checkSum
|
||||
offset := t.ReadULong()
|
||||
t.Skip(4) // length
|
||||
t.tables[tag] = offset
|
||||
}
|
||||
err = t.ParseComponents()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
t.f.Close()
|
||||
TtfRec = t.rec
|
||||
return
|
||||
}
|
||||
|
||||
func (t *ttfParser) ParseComponents() (err error) {
|
||||
err = t.ParseHead()
|
||||
if err == nil {
|
||||
err = t.ParseHhea()
|
||||
if err == nil {
|
||||
err = t.ParseMaxp()
|
||||
if err == nil {
|
||||
err = t.ParseHmtx()
|
||||
if err == nil {
|
||||
err = t.ParseCmap()
|
||||
if err == nil {
|
||||
err = t.ParseName()
|
||||
if err == nil {
|
||||
err = t.ParseOS2()
|
||||
if err == nil {
|
||||
err = t.ParsePost()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (t *ttfParser) ParseHead() (err error) {
|
||||
err = t.Seek("head")
|
||||
t.Skip(3 * 4) // version, fontRevision, checkSumAdjustment
|
||||
magicNumber := t.ReadULong()
|
||||
if magicNumber != 0x5F0F3CF5 {
|
||||
err = fmt.Errorf("incorrect magic number")
|
||||
return
|
||||
}
|
||||
t.Skip(2) // flags
|
||||
t.rec.UnitsPerEm = t.ReadUShort()
|
||||
t.Skip(2 * 8) // created, modified
|
||||
t.rec.Xmin = t.ReadShort()
|
||||
t.rec.Ymin = t.ReadShort()
|
||||
t.rec.Xmax = t.ReadShort()
|
||||
t.rec.Ymax = t.ReadShort()
|
||||
return
|
||||
}
|
||||
|
||||
func (t *ttfParser) ParseHhea() (err error) {
|
||||
err = t.Seek("hhea")
|
||||
if err == nil {
|
||||
t.Skip(4 + 15*2)
|
||||
t.numberOfHMetrics = t.ReadUShort()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (t *ttfParser) ParseMaxp() (err error) {
|
||||
err = t.Seek("maxp")
|
||||
if err == nil {
|
||||
t.Skip(4)
|
||||
t.numGlyphs = t.ReadUShort()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (t *ttfParser) ParseHmtx() (err error) {
|
||||
err = t.Seek("hmtx")
|
||||
if err == nil {
|
||||
t.rec.Widths = make([]uint16, 0, 8)
|
||||
for j := uint16(0); j < t.numberOfHMetrics; j++ {
|
||||
t.rec.Widths = append(t.rec.Widths, t.ReadUShort())
|
||||
t.Skip(2) // lsb
|
||||
}
|
||||
if t.numberOfHMetrics < t.numGlyphs {
|
||||
lastWidth := t.rec.Widths[t.numberOfHMetrics-1]
|
||||
for j := t.numberOfHMetrics; j < t.numGlyphs; j++ {
|
||||
t.rec.Widths = append(t.rec.Widths, lastWidth)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (t *ttfParser) ParseCmap() (err error) {
|
||||
var offset int64
|
||||
if err = t.Seek("cmap"); err != nil {
|
||||
return
|
||||
}
|
||||
t.Skip(2) // version
|
||||
numTables := int(t.ReadUShort())
|
||||
offset31 := int64(0)
|
||||
for j := 0; j < numTables; j++ {
|
||||
platformID := t.ReadUShort()
|
||||
encodingID := t.ReadUShort()
|
||||
offset = int64(t.ReadULong())
|
||||
if platformID == 3 && encodingID == 1 {
|
||||
offset31 = offset
|
||||
}
|
||||
}
|
||||
if offset31 == 0 {
|
||||
err = fmt.Errorf("no Unicode encoding found")
|
||||
return
|
||||
}
|
||||
startCount := make([]uint16, 0, 8)
|
||||
endCount := make([]uint16, 0, 8)
|
||||
idDelta := make([]int16, 0, 8)
|
||||
idRangeOffset := make([]uint16, 0, 8)
|
||||
t.rec.Chars = make(map[uint16]uint16)
|
||||
t.f.Seek(int64(t.tables["cmap"])+offset31, os.SEEK_SET)
|
||||
format := t.ReadUShort()
|
||||
if format != 4 {
|
||||
err = fmt.Errorf("unexpected subtable format: %d", format)
|
||||
return
|
||||
}
|
||||
t.Skip(2 * 2) // length, language
|
||||
segCount := int(t.ReadUShort() / 2)
|
||||
t.Skip(3 * 2) // searchRange, entrySelector, rangeShift
|
||||
for j := 0; j < segCount; j++ {
|
||||
endCount = append(endCount, t.ReadUShort())
|
||||
}
|
||||
t.Skip(2) // reservedPad
|
||||
for j := 0; j < segCount; j++ {
|
||||
startCount = append(startCount, t.ReadUShort())
|
||||
}
|
||||
for j := 0; j < segCount; j++ {
|
||||
idDelta = append(idDelta, t.ReadShort())
|
||||
}
|
||||
offset, _ = t.f.Seek(int64(0), os.SEEK_CUR)
|
||||
for j := 0; j < segCount; j++ {
|
||||
idRangeOffset = append(idRangeOffset, t.ReadUShort())
|
||||
}
|
||||
for j := 0; j < segCount; j++ {
|
||||
c1 := startCount[j]
|
||||
c2 := endCount[j]
|
||||
d := idDelta[j]
|
||||
ro := idRangeOffset[j]
|
||||
if ro > 0 {
|
||||
t.f.Seek(offset+2*int64(j)+int64(ro), os.SEEK_SET)
|
||||
}
|
||||
for c := c1; c <= c2; c++ {
|
||||
if c == 0xFFFF {
|
||||
break
|
||||
}
|
||||
var gid int32
|
||||
if ro > 0 {
|
||||
gid = int32(t.ReadUShort())
|
||||
if gid > 0 {
|
||||
gid += int32(d)
|
||||
}
|
||||
} else {
|
||||
gid = int32(c) + int32(d)
|
||||
}
|
||||
if gid >= 65536 {
|
||||
gid -= 65536
|
||||
}
|
||||
if gid > 0 {
|
||||
t.rec.Chars[c] = uint16(gid)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (t *ttfParser) ParseName() (err error) {
|
||||
err = t.Seek("name")
|
||||
if err == nil {
|
||||
tableOffset, _ := t.f.Seek(0, os.SEEK_CUR)
|
||||
t.rec.PostScriptName = ""
|
||||
t.Skip(2) // format
|
||||
count := t.ReadUShort()
|
||||
stringOffset := t.ReadUShort()
|
||||
for j := uint16(0); j < count && t.rec.PostScriptName == ""; j++ {
|
||||
t.Skip(3 * 2) // platformID, encodingID, languageID
|
||||
nameID := t.ReadUShort()
|
||||
length := t.ReadUShort()
|
||||
offset := t.ReadUShort()
|
||||
if nameID == 6 {
|
||||
// PostScript name
|
||||
t.f.Seek(int64(tableOffset)+int64(stringOffset)+int64(offset), os.SEEK_SET)
|
||||
var s string
|
||||
s, err = t.ReadStr(int(length))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s = strings.Replace(s, "\x00", "", -1)
|
||||
var re *regexp.Regexp
|
||||
if re, err = regexp.Compile("[(){}<> /%[\\]]"); err != nil {
|
||||
return
|
||||
}
|
||||
t.rec.PostScriptName = re.ReplaceAllString(s, "")
|
||||
}
|
||||
}
|
||||
if t.rec.PostScriptName == "" {
|
||||
err = fmt.Errorf("the name PostScript was not found")
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (t *ttfParser) ParseOS2() (err error) {
|
||||
err = t.Seek("OS/2")
|
||||
if err == nil {
|
||||
version := t.ReadUShort()
|
||||
t.Skip(3 * 2) // xAvgCharWidth, usWeightClass, usWidthClass
|
||||
fsType := t.ReadUShort()
|
||||
t.rec.Embeddable = (fsType != 2) && (fsType&0x200) == 0
|
||||
t.Skip(11*2 + 10 + 4*4 + 4)
|
||||
fsSelection := t.ReadUShort()
|
||||
t.rec.Bold = (fsSelection & 32) != 0
|
||||
t.Skip(2 * 2) // usFirstCharIndex, usLastCharIndex
|
||||
t.rec.TypoAscender = t.ReadShort()
|
||||
t.rec.TypoDescender = t.ReadShort()
|
||||
if version >= 2 {
|
||||
t.Skip(3*2 + 2*4 + 2)
|
||||
t.rec.CapHeight = t.ReadShort()
|
||||
} else {
|
||||
t.rec.CapHeight = 0
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (t *ttfParser) ParsePost() (err error) {
|
||||
err = t.Seek("post")
|
||||
if err == nil {
|
||||
t.Skip(4) // version
|
||||
t.rec.ItalicAngle = t.ReadShort()
|
||||
t.Skip(2) // Skip decimal part
|
||||
t.rec.UnderlinePosition = t.ReadShort()
|
||||
t.rec.UnderlineThickness = t.ReadShort()
|
||||
t.rec.IsFixedPitch = t.ReadULong() != 0
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (t *ttfParser) Seek(tag string) (err error) {
|
||||
ofs, ok := t.tables[tag]
|
||||
if ok {
|
||||
t.f.Seek(int64(ofs), os.SEEK_SET)
|
||||
} else {
|
||||
err = fmt.Errorf("table not found: %s", tag)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (t *ttfParser) Skip(n int) {
|
||||
t.f.Seek(int64(n), os.SEEK_CUR)
|
||||
}
|
||||
|
||||
func (t *ttfParser) ReadStr(length int) (str string, err error) {
|
||||
var n int
|
||||
buf := make([]byte, length)
|
||||
n, err = t.f.Read(buf)
|
||||
if err == nil {
|
||||
if n == length {
|
||||
str = string(buf)
|
||||
} else {
|
||||
err = fmt.Errorf("unable to read %d bytes", length)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (t *ttfParser) ReadUShort() (val uint16) {
|
||||
binary.Read(t.f, binary.BigEndian, &val)
|
||||
return
|
||||
}
|
||||
|
||||
func (t *ttfParser) ReadShort() (val int16) {
|
||||
binary.Read(t.f, binary.BigEndian, &val)
|
||||
return
|
||||
}
|
||||
|
||||
func (t *ttfParser) ReadULong() (val uint32) {
|
||||
binary.Read(t.f, binary.BigEndian, &val)
|
||||
return
|
||||
}
|
1153
vendor/github.com/jung-kurt/gofpdf/utf8fontfile.go
generated
vendored
Normal file
1153
vendor/github.com/jung-kurt/gofpdf/utf8fontfile.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
454
vendor/github.com/jung-kurt/gofpdf/util.go
generated
vendored
Normal file
454
vendor/github.com/jung-kurt/gofpdf/util.go
generated
vendored
Normal file
@ -0,0 +1,454 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Kurt Jung (Gmail: kurt.w.jung)
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package gofpdf
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func round(f float64) int {
|
||||
if f < 0 {
|
||||
return -int(math.Floor(-f + 0.5))
|
||||
}
|
||||
return int(math.Floor(f + 0.5))
|
||||
}
|
||||
|
||||
func sprintf(fmtStr string, args ...interface{}) string {
|
||||
return fmt.Sprintf(fmtStr, args...)
|
||||
}
|
||||
|
||||
// fileExist returns true if the specified normal file exists
|
||||
func fileExist(filename string) (ok bool) {
|
||||
info, err := os.Stat(filename)
|
||||
if err == nil {
|
||||
if ^os.ModePerm&info.Mode() == 0 {
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
// fileSize returns the size of the specified file; ok will be false
|
||||
// if the file does not exist or is not an ordinary file
|
||||
func fileSize(filename string) (size int64, ok bool) {
|
||||
info, err := os.Stat(filename)
|
||||
ok = err == nil
|
||||
if ok {
|
||||
size = info.Size()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// bufferFromReader returns a new buffer populated with the contents of the specified Reader
|
||||
func bufferFromReader(r io.Reader) (b *bytes.Buffer, err error) {
|
||||
b = new(bytes.Buffer)
|
||||
_, err = b.ReadFrom(r)
|
||||
return
|
||||
}
|
||||
|
||||
// slicesEqual returns true if the two specified float slices are equal
|
||||
func slicesEqual(a, b []float64) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i := range a {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// sliceCompress returns a zlib-compressed copy of the specified byte array
|
||||
func sliceCompress(data []byte) []byte {
|
||||
var buf bytes.Buffer
|
||||
cmp, _ := zlib.NewWriterLevel(&buf, zlib.BestSpeed)
|
||||
cmp.Write(data)
|
||||
cmp.Close()
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// sliceUncompress returns an uncompressed copy of the specified zlib-compressed byte array
|
||||
func sliceUncompress(data []byte) (outData []byte, err error) {
|
||||
inBuf := bytes.NewReader(data)
|
||||
r, err := zlib.NewReader(inBuf)
|
||||
defer r.Close()
|
||||
if err == nil {
|
||||
var outBuf bytes.Buffer
|
||||
_, err = outBuf.ReadFrom(r)
|
||||
if err == nil {
|
||||
outData = outBuf.Bytes()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// utf8toutf16 converts UTF-8 to UTF-16BE; from http://www.fpdf.org/
|
||||
func utf8toutf16(s string, withBOM ...bool) string {
|
||||
bom := true
|
||||
if len(withBOM) > 0 {
|
||||
bom = withBOM[0]
|
||||
}
|
||||
res := make([]byte, 0, 8)
|
||||
if bom {
|
||||
res = append(res, 0xFE, 0xFF)
|
||||
}
|
||||
nb := len(s)
|
||||
i := 0
|
||||
for i < nb {
|
||||
c1 := byte(s[i])
|
||||
i++
|
||||
switch {
|
||||
case c1 >= 224:
|
||||
// 3-byte character
|
||||
c2 := byte(s[i])
|
||||
i++
|
||||
c3 := byte(s[i])
|
||||
i++
|
||||
res = append(res, ((c1&0x0F)<<4)+((c2&0x3C)>>2),
|
||||
((c2&0x03)<<6)+(c3&0x3F))
|
||||
case c1 >= 192:
|
||||
// 2-byte character
|
||||
c2 := byte(s[i])
|
||||
i++
|
||||
res = append(res, ((c1 & 0x1C) >> 2),
|
||||
((c1&0x03)<<6)+(c2&0x3F))
|
||||
default:
|
||||
// Single-byte character
|
||||
res = append(res, 0, c1)
|
||||
}
|
||||
}
|
||||
return string(res)
|
||||
}
|
||||
|
||||
// intIf returns a if cnd is true, otherwise b
|
||||
func intIf(cnd bool, a, b int) int {
|
||||
if cnd {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// strIf returns aStr if cnd is true, otherwise bStr
|
||||
func strIf(cnd bool, aStr, bStr string) string {
|
||||
if cnd {
|
||||
return aStr
|
||||
}
|
||||
return bStr
|
||||
}
|
||||
|
||||
// doNothing returns the passed string with no translation.
|
||||
func doNothing(s string) string {
|
||||
return s
|
||||
}
|
||||
|
||||
// Dump the internals of the specified values
|
||||
// func dump(fileStr string, a ...interface{}) {
|
||||
// fl, err := os.OpenFile(fileStr, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
|
||||
// if err == nil {
|
||||
// fmt.Fprintf(fl, "----------------\n")
|
||||
// spew.Fdump(fl, a...)
|
||||
// fl.Close()
|
||||
// }
|
||||
// }
|
||||
|
||||
func repClosure(m map[rune]byte) func(string) string {
|
||||
var buf bytes.Buffer
|
||||
return func(str string) string {
|
||||
var ch byte
|
||||
var ok bool
|
||||
buf.Truncate(0)
|
||||
for _, r := range str {
|
||||
if r < 0x80 {
|
||||
ch = byte(r)
|
||||
} else {
|
||||
ch, ok = m[r]
|
||||
if !ok {
|
||||
ch = byte('.')
|
||||
}
|
||||
}
|
||||
buf.WriteByte(ch)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
}
|
||||
|
||||
// UnicodeTranslator returns a function that can be used to translate, where
|
||||
// possible, utf-8 strings to a form that is compatible with the specified code
|
||||
// page. The returned function accepts a string and returns a string.
|
||||
//
|
||||
// r is a reader that should read a buffer made up of content lines that
|
||||
// pertain to the code page of interest. Each line is made up of three
|
||||
// whitespace separated fields. The first begins with "!" and is followed by
|
||||
// two hexadecimal digits that identify the glyph position in the code page of
|
||||
// interest. The second field begins with "U+" and is followed by the unicode
|
||||
// code point value. The third is the glyph name. A number of these code page
|
||||
// map files are packaged with the gfpdf library in the font directory.
|
||||
//
|
||||
// An error occurs only if a line is read that does not conform to the expected
|
||||
// format. In this case, the returned function is valid but does not perform
|
||||
// any rune translation.
|
||||
func UnicodeTranslator(r io.Reader) (f func(string) string, err error) {
|
||||
m := make(map[rune]byte)
|
||||
var uPos, cPos uint32
|
||||
var lineStr, nameStr string
|
||||
sc := bufio.NewScanner(r)
|
||||
for sc.Scan() {
|
||||
lineStr = sc.Text()
|
||||
lineStr = strings.TrimSpace(lineStr)
|
||||
if len(lineStr) > 0 {
|
||||
_, err = fmt.Sscanf(lineStr, "!%2X U+%4X %s", &cPos, &uPos, &nameStr)
|
||||
if err == nil {
|
||||
if cPos >= 0x80 {
|
||||
m[rune(uPos)] = byte(cPos)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
f = repClosure(m)
|
||||
} else {
|
||||
f = doNothing
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// UnicodeTranslatorFromFile returns a function that can be used to translate,
|
||||
// where possible, utf-8 strings to a form that is compatible with the
|
||||
// specified code page. See UnicodeTranslator for more details.
|
||||
//
|
||||
// fileStr identifies a font descriptor file that maps glyph positions to names.
|
||||
//
|
||||
// If an error occurs reading the file, the returned function is valid but does
|
||||
// not perform any rune translation.
|
||||
func UnicodeTranslatorFromFile(fileStr string) (f func(string) string, err error) {
|
||||
var fl *os.File
|
||||
fl, err = os.Open(fileStr)
|
||||
if err == nil {
|
||||
f, err = UnicodeTranslator(fl)
|
||||
fl.Close()
|
||||
} else {
|
||||
f = doNothing
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// UnicodeTranslatorFromDescriptor returns a function that can be used to
|
||||
// translate, where possible, utf-8 strings to a form that is compatible with
|
||||
// the specified code page. See UnicodeTranslator for more details.
|
||||
//
|
||||
// cpStr identifies a code page. A descriptor file in the font directory, set
|
||||
// with the fontDirStr argument in the call to New(), should have this name
|
||||
// plus the extension ".map". If cpStr is empty, it will be replaced with
|
||||
// "cp1252", the gofpdf code page default.
|
||||
//
|
||||
// If an error occurs reading the descriptor, the returned function is valid
|
||||
// but does not perform any rune translation.
|
||||
//
|
||||
// The CellFormat_codepage example demonstrates this method.
|
||||
func (f *Fpdf) UnicodeTranslatorFromDescriptor(cpStr string) (rep func(string) string) {
|
||||
var str string
|
||||
var ok bool
|
||||
if f.err == nil {
|
||||
if len(cpStr) == 0 {
|
||||
cpStr = "cp1252"
|
||||
}
|
||||
str, ok = embeddedMapList[cpStr]
|
||||
if ok {
|
||||
rep, f.err = UnicodeTranslator(strings.NewReader(str))
|
||||
} else {
|
||||
rep, f.err = UnicodeTranslatorFromFile(filepath.Join(f.fontpath, cpStr) + ".map")
|
||||
}
|
||||
} else {
|
||||
rep = doNothing
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Transform moves a point by given X, Y offset
|
||||
func (p *PointType) Transform(x, y float64) PointType {
|
||||
return PointType{p.X + x, p.Y + y}
|
||||
}
|
||||
|
||||
// Orientation returns the orientation of a given size:
|
||||
// "P" for portrait, "L" for landscape
|
||||
func (s *SizeType) Orientation() string {
|
||||
if s == nil || s.Ht == s.Wd {
|
||||
return ""
|
||||
}
|
||||
if s.Wd > s.Ht {
|
||||
return "L"
|
||||
}
|
||||
return "P"
|
||||
}
|
||||
|
||||
// ScaleBy expands a size by a certain factor
|
||||
func (s *SizeType) ScaleBy(factor float64) SizeType {
|
||||
return SizeType{s.Wd * factor, s.Ht * factor}
|
||||
}
|
||||
|
||||
// ScaleToWidth adjusts the height of a size to match the given width
|
||||
func (s *SizeType) ScaleToWidth(width float64) SizeType {
|
||||
height := s.Ht * width / s.Wd
|
||||
return SizeType{width, height}
|
||||
}
|
||||
|
||||
// ScaleToHeight adjusts the width of a size to match the given height
|
||||
func (s *SizeType) ScaleToHeight(height float64) SizeType {
|
||||
width := s.Wd * height / s.Ht
|
||||
return SizeType{width, height}
|
||||
}
|
||||
|
||||
//The untypedKeyMap structure and its methods are copyrighted 2019 by Arteom Korotkiy (Gmail: arteomkorotkiy).
|
||||
//Imitation of untyped Map Array
|
||||
type untypedKeyMap struct {
|
||||
keySet []interface{}
|
||||
valueSet []int
|
||||
}
|
||||
|
||||
//Get position of key=>value in PHP Array
|
||||
func (pa *untypedKeyMap) getIndex(key interface{}) int {
|
||||
if key != nil {
|
||||
for i, mKey := range pa.keySet {
|
||||
if mKey == key {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
//Put key=>value in PHP Array
|
||||
func (pa *untypedKeyMap) put(key interface{}, value int) {
|
||||
if key == nil {
|
||||
var i int
|
||||
for n := 0; ; n++ {
|
||||
i = pa.getIndex(n)
|
||||
if i < 0 {
|
||||
key = n
|
||||
break
|
||||
}
|
||||
}
|
||||
pa.keySet = append(pa.keySet, key)
|
||||
pa.valueSet = append(pa.valueSet, value)
|
||||
} else {
|
||||
i := pa.getIndex(key)
|
||||
if i < 0 {
|
||||
pa.keySet = append(pa.keySet, key)
|
||||
pa.valueSet = append(pa.valueSet, value)
|
||||
} else {
|
||||
pa.valueSet[i] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Delete value in PHP Array
|
||||
func (pa *untypedKeyMap) delete(key interface{}) {
|
||||
if pa == nil || pa.keySet == nil || pa.valueSet == nil {
|
||||
return
|
||||
}
|
||||
i := pa.getIndex(key)
|
||||
if i >= 0 {
|
||||
if i == 0 {
|
||||
pa.keySet = pa.keySet[1:]
|
||||
pa.valueSet = pa.valueSet[1:]
|
||||
} else if i == len(pa.keySet)-1 {
|
||||
pa.keySet = pa.keySet[:len(pa.keySet)-1]
|
||||
pa.valueSet = pa.valueSet[:len(pa.valueSet)-1]
|
||||
} else {
|
||||
pa.keySet = append(pa.keySet[:i], pa.keySet[i+1:]...)
|
||||
pa.valueSet = append(pa.valueSet[:i], pa.valueSet[i+1:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Get value from PHP Array
|
||||
func (pa *untypedKeyMap) get(key interface{}) int {
|
||||
i := pa.getIndex(key)
|
||||
if i >= 0 {
|
||||
return pa.valueSet[i]
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
//Imitation of PHP function pop()
|
||||
func (pa *untypedKeyMap) pop() {
|
||||
pa.keySet = pa.keySet[:len(pa.keySet)-1]
|
||||
pa.valueSet = pa.valueSet[:len(pa.valueSet)-1]
|
||||
}
|
||||
|
||||
//Imitation of PHP function array_merge()
|
||||
func arrayMerge(arr1, arr2 *untypedKeyMap) *untypedKeyMap {
|
||||
answer := untypedKeyMap{}
|
||||
if arr1 == nil && arr2 == nil {
|
||||
answer = untypedKeyMap{
|
||||
make([]interface{}, 0),
|
||||
make([]int, 0),
|
||||
}
|
||||
} else if arr2 == nil {
|
||||
answer.keySet = arr1.keySet[:]
|
||||
answer.valueSet = arr1.valueSet[:]
|
||||
} else if arr1 == nil {
|
||||
answer.keySet = arr2.keySet[:]
|
||||
answer.valueSet = arr2.valueSet[:]
|
||||
} else {
|
||||
answer.keySet = arr1.keySet[:]
|
||||
answer.valueSet = arr1.valueSet[:]
|
||||
for i := 0; i < len(arr2.keySet); i++ {
|
||||
if arr2.keySet[i] == "interval" {
|
||||
if arr1.getIndex("interval") < 0 {
|
||||
answer.put("interval", arr2.valueSet[i])
|
||||
}
|
||||
} else {
|
||||
answer.put(nil, arr2.valueSet[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
return &answer
|
||||
}
|
||||
|
||||
func remove(arr []int, key int) []int {
|
||||
n := 0
|
||||
for i, mKey := range arr {
|
||||
if mKey == key {
|
||||
n = i
|
||||
}
|
||||
}
|
||||
if n == 0 {
|
||||
return arr[1:]
|
||||
} else if n == len(arr)-1 {
|
||||
return arr[:len(arr)-1]
|
||||
}
|
||||
return append(arr[:n], arr[n+1:]...)
|
||||
}
|
||||
|
||||
func isChinese(rune2 rune) bool {
|
||||
// chinese unicode: 4e00-9fa5
|
||||
if rune2 >= rune(0x4e00) && rune2 <= rune(0x9fa5) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
22
vendor/github.com/robfig/cron/v3/.gitignore
generated
vendored
Normal file
22
vendor/github.com/robfig/cron/v3/.gitignore
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
21
vendor/github.com/robfig/cron/v3/LICENSE
generated
vendored
Normal file
21
vendor/github.com/robfig/cron/v3/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
Copyright (C) 2012 Rob Figueiredo
|
||||
All Rights Reserved.
|
||||
|
||||
MIT LICENSE
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
125
vendor/github.com/robfig/cron/v3/README.md
generated
vendored
Normal file
125
vendor/github.com/robfig/cron/v3/README.md
generated
vendored
Normal file
@ -0,0 +1,125 @@
|
||||
[](http://godoc.org/github.com/robfig/cron)
|
||||
[](https://travis-ci.org/robfig/cron)
|
||||
|
||||
# cron
|
||||
|
||||
Cron V3 has been released!
|
||||
|
||||
To download the specific tagged release, run:
|
||||
|
||||
go get github.com/robfig/cron/v3@v3.0.0
|
||||
|
||||
Import it in your program as:
|
||||
|
||||
import "github.com/robfig/cron/v3"
|
||||
|
||||
It requires Go 1.11 or later due to usage of Go Modules.
|
||||
|
||||
Refer to the documentation here:
|
||||
http://godoc.org/github.com/robfig/cron
|
||||
|
||||
The rest of this document describes the the advances in v3 and a list of
|
||||
breaking changes for users that wish to upgrade from an earlier version.
|
||||
|
||||
## Upgrading to v3 (June 2019)
|
||||
|
||||
cron v3 is a major upgrade to the library that addresses all outstanding bugs,
|
||||
feature requests, and rough edges. It is based on a merge of master which
|
||||
contains various fixes to issues found over the years and the v2 branch which
|
||||
contains some backwards-incompatible features like the ability to remove cron
|
||||
jobs. In addition, v3 adds support for Go Modules, cleans up rough edges like
|
||||
the timezone support, and fixes a number of bugs.
|
||||
|
||||
New features:
|
||||
|
||||
- Support for Go modules. Callers must now import this library as
|
||||
`github.com/robfig/cron/v3`, instead of `gopkg.in/...`
|
||||
|
||||
- Fixed bugs:
|
||||
- 0f01e6b parser: fix combining of Dow and Dom (#70)
|
||||
- dbf3220 adjust times when rolling the clock forward to handle non-existent midnight (#157)
|
||||
- eeecf15 spec_test.go: ensure an error is returned on 0 increment (#144)
|
||||
- 70971dc cron.Entries(): update request for snapshot to include a reply channel (#97)
|
||||
- 1cba5e6 cron: fix: removing a job causes the next scheduled job to run too late (#206)
|
||||
|
||||
- Standard cron spec parsing by default (first field is "minute"), with an easy
|
||||
way to opt into the seconds field (quartz-compatible). Although, note that the
|
||||
year field (optional in Quartz) is not supported.
|
||||
|
||||
- Extensible, key/value logging via an interface that complies with
|
||||
the https://github.com/go-logr/logr project.
|
||||
|
||||
- The new Chain & JobWrapper types allow you to install "interceptors" to add
|
||||
cross-cutting behavior like the following:
|
||||
- Recover any panics from jobs
|
||||
- Delay a job's execution if the previous run hasn't completed yet
|
||||
- Skip a job's execution if the previous run hasn't completed yet
|
||||
- Log each job's invocations
|
||||
- Notification when jobs are completed
|
||||
|
||||
It is backwards incompatible with both v1 and v2. These updates are required:
|
||||
|
||||
- The v1 branch accepted an optional seconds field at the beginning of the cron
|
||||
spec. This is non-standard and has led to a lot of confusion. The new default
|
||||
parser conforms to the standard as described by [the Cron wikipedia page].
|
||||
|
||||
UPDATING: To retain the old behavior, construct your Cron with a custom
|
||||
parser:
|
||||
|
||||
// Seconds field, required
|
||||
cron.New(cron.WithSeconds())
|
||||
|
||||
// Seconds field, optional
|
||||
cron.New(
|
||||
cron.WithParser(
|
||||
cron.SecondOptional | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor))
|
||||
|
||||
- The Cron type now accepts functional options on construction rather than the
|
||||
previous ad-hoc behavior modification mechanisms (setting a field, calling a setter).
|
||||
|
||||
UPDATING: Code that sets Cron.ErrorLogger or calls Cron.SetLocation must be
|
||||
updated to provide those values on construction.
|
||||
|
||||
- CRON_TZ is now the recommended way to specify the timezone of a single
|
||||
schedule, which is sanctioned by the specification. The legacy "TZ=" prefix
|
||||
will continue to be supported since it is unambiguous and easy to do so.
|
||||
|
||||
UPDATING: No update is required.
|
||||
|
||||
- By default, cron will no longer recover panics in jobs that it runs.
|
||||
Recovering can be surprising (see issue #192) and seems to be at odds with
|
||||
typical behavior of libraries. Relatedly, the `cron.WithPanicLogger` option
|
||||
has been removed to accommodate the more general JobWrapper type.
|
||||
|
||||
UPDATING: To opt into panic recovery and configure the panic logger:
|
||||
|
||||
cron.New(cron.WithChain(
|
||||
cron.Recover(logger), // or use cron.DefaultLogger
|
||||
))
|
||||
|
||||
- In adding support for https://github.com/go-logr/logr, `cron.WithVerboseLogger` was
|
||||
removed, since it is duplicative with the leveled logging.
|
||||
|
||||
UPDATING: Callers should use `WithLogger` and specify a logger that does not
|
||||
discard `Info` logs. For convenience, one is provided that wraps `*log.Logger`:
|
||||
|
||||
cron.New(
|
||||
cron.WithLogger(cron.VerbosePrintfLogger(logger)))
|
||||
|
||||
|
||||
### Background - Cron spec format
|
||||
|
||||
There are two cron spec formats in common usage:
|
||||
|
||||
- The "standard" cron format, described on [the Cron wikipedia page] and used by
|
||||
the cron Linux system utility.
|
||||
|
||||
- The cron format used by [the Quartz Scheduler], commonly used for scheduled
|
||||
jobs in Java software
|
||||
|
||||
[the Cron wikipedia page]: https://en.wikipedia.org/wiki/Cron
|
||||
[the Quartz Scheduler]: http://www.quartz-scheduler.org/documentation/quartz-2.x/tutorials/crontrigger.html
|
||||
|
||||
The original version of this package included an optional "seconds" field, which
|
||||
made it incompatible with both of these formats. Now, the "standard" format is
|
||||
the default format accepted, and the Quartz format is opt-in.
|
92
vendor/github.com/robfig/cron/v3/chain.go
generated
vendored
Normal file
92
vendor/github.com/robfig/cron/v3/chain.go
generated
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
package cron
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// JobWrapper decorates the given Job with some behavior.
|
||||
type JobWrapper func(Job) Job
|
||||
|
||||
// Chain is a sequence of JobWrappers that decorates submitted jobs with
|
||||
// cross-cutting behaviors like logging or synchronization.
|
||||
type Chain struct {
|
||||
wrappers []JobWrapper
|
||||
}
|
||||
|
||||
// NewChain returns a Chain consisting of the given JobWrappers.
|
||||
func NewChain(c ...JobWrapper) Chain {
|
||||
return Chain{c}
|
||||
}
|
||||
|
||||
// Then decorates the given job with all JobWrappers in the chain.
|
||||
//
|
||||
// This:
|
||||
// NewChain(m1, m2, m3).Then(job)
|
||||
// is equivalent to:
|
||||
// m1(m2(m3(job)))
|
||||
func (c Chain) Then(j Job) Job {
|
||||
for i := range c.wrappers {
|
||||
j = c.wrappers[len(c.wrappers)-i-1](j)
|
||||
}
|
||||
return j
|
||||
}
|
||||
|
||||
// Recover panics in wrapped jobs and log them with the provided logger.
|
||||
func Recover(logger Logger) JobWrapper {
|
||||
return func(j Job) Job {
|
||||
return FuncJob(func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
const size = 64 << 10
|
||||
buf := make([]byte, size)
|
||||
buf = buf[:runtime.Stack(buf, false)]
|
||||
err, ok := r.(error)
|
||||
if !ok {
|
||||
err = fmt.Errorf("%v", r)
|
||||
}
|
||||
logger.Error(err, "panic", "stack", "...\n"+string(buf))
|
||||
}
|
||||
}()
|
||||
j.Run()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// DelayIfStillRunning serializes jobs, delaying subsequent runs until the
|
||||
// previous one is complete. Jobs running after a delay of more than a minute
|
||||
// have the delay logged at Info.
|
||||
func DelayIfStillRunning(logger Logger) JobWrapper {
|
||||
return func(j Job) Job {
|
||||
var mu sync.Mutex
|
||||
return FuncJob(func() {
|
||||
start := time.Now()
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if dur := time.Since(start); dur > time.Minute {
|
||||
logger.Info("delay", "duration", dur)
|
||||
}
|
||||
j.Run()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// SkipIfStillRunning skips an invocation of the Job if a previous invocation is
|
||||
// still running. It logs skips to the given logger at Info level.
|
||||
func SkipIfStillRunning(logger Logger) JobWrapper {
|
||||
var ch = make(chan struct{}, 1)
|
||||
ch <- struct{}{}
|
||||
return func(j Job) Job {
|
||||
return FuncJob(func() {
|
||||
select {
|
||||
case v := <-ch:
|
||||
j.Run()
|
||||
ch <- v
|
||||
default:
|
||||
logger.Info("skip")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
27
vendor/github.com/robfig/cron/v3/constantdelay.go
generated
vendored
Normal file
27
vendor/github.com/robfig/cron/v3/constantdelay.go
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
package cron
|
||||
|
||||
import "time"
|
||||
|
||||
// ConstantDelaySchedule represents a simple recurring duty cycle, e.g. "Every 5 minutes".
|
||||
// It does not support jobs more frequent than once a second.
|
||||
type ConstantDelaySchedule struct {
|
||||
Delay time.Duration
|
||||
}
|
||||
|
||||
// Every returns a crontab Schedule that activates once every duration.
|
||||
// Delays of less than a second are not supported (will round up to 1 second).
|
||||
// Any fields less than a Second are truncated.
|
||||
func Every(duration time.Duration) ConstantDelaySchedule {
|
||||
if duration < time.Second {
|
||||
duration = time.Second
|
||||
}
|
||||
return ConstantDelaySchedule{
|
||||
Delay: duration - time.Duration(duration.Nanoseconds())%time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
// Next returns the next time this should be run.
|
||||
// This rounds so that the next activation time will be on the second.
|
||||
func (schedule ConstantDelaySchedule) Next(t time.Time) time.Time {
|
||||
return t.Add(schedule.Delay - time.Duration(t.Nanosecond())*time.Nanosecond)
|
||||
}
|
350
vendor/github.com/robfig/cron/v3/cron.go
generated
vendored
Normal file
350
vendor/github.com/robfig/cron/v3/cron.go
generated
vendored
Normal file
@ -0,0 +1,350 @@
|
||||
package cron
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Cron keeps track of any number of entries, invoking the associated func as
|
||||
// specified by the schedule. It may be started, stopped, and the entries may
|
||||
// be inspected while running.
|
||||
type Cron struct {
|
||||
entries []*Entry
|
||||
chain Chain
|
||||
stop chan struct{}
|
||||
add chan *Entry
|
||||
remove chan EntryID
|
||||
snapshot chan chan []Entry
|
||||
running bool
|
||||
logger Logger
|
||||
runningMu sync.Mutex
|
||||
location *time.Location
|
||||
parser Parser
|
||||
nextID EntryID
|
||||
jobWaiter sync.WaitGroup
|
||||
}
|
||||
|
||||
// Job is an interface for submitted cron jobs.
|
||||
type Job interface {
|
||||
Run()
|
||||
}
|
||||
|
||||
// Schedule describes a job's duty cycle.
|
||||
type Schedule interface {
|
||||
// Next returns the next activation time, later than the given time.
|
||||
// Next is invoked initially, and then each time the job is run.
|
||||
Next(time.Time) time.Time
|
||||
}
|
||||
|
||||
// EntryID identifies an entry within a Cron instance
|
||||
type EntryID int
|
||||
|
||||
// Entry consists of a schedule and the func to execute on that schedule.
|
||||
type Entry struct {
|
||||
// ID is the cron-assigned ID of this entry, which may be used to look up a
|
||||
// snapshot or remove it.
|
||||
ID EntryID
|
||||
|
||||
// Schedule on which this job should be run.
|
||||
Schedule Schedule
|
||||
|
||||
// Next time the job will run, or the zero time if Cron has not been
|
||||
// started or this entry's schedule is unsatisfiable
|
||||
Next time.Time
|
||||
|
||||
// Prev is the last time this job was run, or the zero time if never.
|
||||
Prev time.Time
|
||||
|
||||
// WrappedJob is the thing to run when the Schedule is activated.
|
||||
WrappedJob Job
|
||||
|
||||
// Job is the thing that was submitted to cron.
|
||||
// It is kept around so that user code that needs to get at the job later,
|
||||
// e.g. via Entries() can do so.
|
||||
Job Job
|
||||
}
|
||||
|
||||
// Valid returns true if this is not the zero entry.
|
||||
func (e Entry) Valid() bool { return e.ID != 0 }
|
||||
|
||||
// byTime is a wrapper for sorting the entry array by time
|
||||
// (with zero time at the end).
|
||||
type byTime []*Entry
|
||||
|
||||
func (s byTime) Len() int { return len(s) }
|
||||
func (s byTime) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
func (s byTime) Less(i, j int) bool {
|
||||
// Two zero times should return false.
|
||||
// Otherwise, zero is "greater" than any other time.
|
||||
// (To sort it at the end of the list.)
|
||||
if s[i].Next.IsZero() {
|
||||
return false
|
||||
}
|
||||
if s[j].Next.IsZero() {
|
||||
return true
|
||||
}
|
||||
return s[i].Next.Before(s[j].Next)
|
||||
}
|
||||
|
||||
// New returns a new Cron job runner, modified by the given options.
|
||||
//
|
||||
// Available Settings
|
||||
//
|
||||
// Time Zone
|
||||
// Description: The time zone in which schedules are interpreted
|
||||
// Default: time.Local
|
||||
//
|
||||
// Parser
|
||||
// Description: Parser converts cron spec strings into cron.Schedules.
|
||||
// Default: Accepts this spec: https://en.wikipedia.org/wiki/Cron
|
||||
//
|
||||
// Chain
|
||||
// Description: Wrap submitted jobs to customize behavior.
|
||||
// Default: A chain that recovers panics and logs them to stderr.
|
||||
//
|
||||
// See "cron.With*" to modify the default behavior.
|
||||
func New(opts ...Option) *Cron {
|
||||
c := &Cron{
|
||||
entries: nil,
|
||||
chain: NewChain(),
|
||||
add: make(chan *Entry),
|
||||
stop: make(chan struct{}),
|
||||
snapshot: make(chan chan []Entry),
|
||||
remove: make(chan EntryID),
|
||||
running: false,
|
||||
runningMu: sync.Mutex{},
|
||||
logger: DefaultLogger,
|
||||
location: time.Local,
|
||||
parser: standardParser,
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(c)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// FuncJob is a wrapper that turns a func() into a cron.Job
|
||||
type FuncJob func()
|
||||
|
||||
func (f FuncJob) Run() { f() }
|
||||
|
||||
// AddFunc adds a func to the Cron to be run on the given schedule.
|
||||
// The spec is parsed using the time zone of this Cron instance as the default.
|
||||
// An opaque ID is returned that can be used to later remove it.
|
||||
func (c *Cron) AddFunc(spec string, cmd func()) (EntryID, error) {
|
||||
return c.AddJob(spec, FuncJob(cmd))
|
||||
}
|
||||
|
||||
// AddJob adds a Job to the Cron to be run on the given schedule.
|
||||
// The spec is parsed using the time zone of this Cron instance as the default.
|
||||
// An opaque ID is returned that can be used to later remove it.
|
||||
func (c *Cron) AddJob(spec string, cmd Job) (EntryID, error) {
|
||||
schedule, err := c.parser.Parse(spec)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return c.Schedule(schedule, cmd), nil
|
||||
}
|
||||
|
||||
// Schedule adds a Job to the Cron to be run on the given schedule.
|
||||
// The job is wrapped with the configured Chain.
|
||||
func (c *Cron) Schedule(schedule Schedule, cmd Job) EntryID {
|
||||
c.runningMu.Lock()
|
||||
defer c.runningMu.Unlock()
|
||||
c.nextID++
|
||||
entry := &Entry{
|
||||
ID: c.nextID,
|
||||
Schedule: schedule,
|
||||
WrappedJob: c.chain.Then(cmd),
|
||||
Job: cmd,
|
||||
}
|
||||
if !c.running {
|
||||
c.entries = append(c.entries, entry)
|
||||
} else {
|
||||
c.add <- entry
|
||||
}
|
||||
return entry.ID
|
||||
}
|
||||
|
||||
// Entries returns a snapshot of the cron entries.
|
||||
func (c *Cron) Entries() []Entry {
|
||||
c.runningMu.Lock()
|
||||
defer c.runningMu.Unlock()
|
||||
if c.running {
|
||||
replyChan := make(chan []Entry, 1)
|
||||
c.snapshot <- replyChan
|
||||
return <-replyChan
|
||||
}
|
||||
return c.entrySnapshot()
|
||||
}
|
||||
|
||||
// Location gets the time zone location
|
||||
func (c *Cron) Location() *time.Location {
|
||||
return c.location
|
||||
}
|
||||
|
||||
// Entry returns a snapshot of the given entry, or nil if it couldn't be found.
|
||||
func (c *Cron) Entry(id EntryID) Entry {
|
||||
for _, entry := range c.Entries() {
|
||||
if id == entry.ID {
|
||||
return entry
|
||||
}
|
||||
}
|
||||
return Entry{}
|
||||
}
|
||||
|
||||
// Remove an entry from being run in the future.
|
||||
func (c *Cron) Remove(id EntryID) {
|
||||
c.runningMu.Lock()
|
||||
defer c.runningMu.Unlock()
|
||||
if c.running {
|
||||
c.remove <- id
|
||||
} else {
|
||||
c.removeEntry(id)
|
||||
}
|
||||
}
|
||||
|
||||
// Start the cron scheduler in its own goroutine, or no-op if already started.
|
||||
func (c *Cron) Start() {
|
||||
c.runningMu.Lock()
|
||||
defer c.runningMu.Unlock()
|
||||
if c.running {
|
||||
return
|
||||
}
|
||||
c.running = true
|
||||
go c.run()
|
||||
}
|
||||
|
||||
// Run the cron scheduler, or no-op if already running.
|
||||
func (c *Cron) Run() {
|
||||
c.runningMu.Lock()
|
||||
if c.running {
|
||||
c.runningMu.Unlock()
|
||||
return
|
||||
}
|
||||
c.running = true
|
||||
c.runningMu.Unlock()
|
||||
c.run()
|
||||
}
|
||||
|
||||
// run the scheduler.. this is private just due to the need to synchronize
|
||||
// access to the 'running' state variable.
|
||||
func (c *Cron) run() {
|
||||
c.logger.Info("start")
|
||||
|
||||
// Figure out the next activation times for each entry.
|
||||
now := c.now()
|
||||
for _, entry := range c.entries {
|
||||
entry.Next = entry.Schedule.Next(now)
|
||||
c.logger.Info("schedule", "now", now, "entry", entry.ID, "next", entry.Next)
|
||||
}
|
||||
|
||||
for {
|
||||
// Determine the next entry to run.
|
||||
sort.Sort(byTime(c.entries))
|
||||
|
||||
var timer *time.Timer
|
||||
if len(c.entries) == 0 || c.entries[0].Next.IsZero() {
|
||||
// If there are no entries yet, just sleep - it still handles new entries
|
||||
// and stop requests.
|
||||
timer = time.NewTimer(100000 * time.Hour)
|
||||
} else {
|
||||
timer = time.NewTimer(c.entries[0].Next.Sub(now))
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case now = <-timer.C:
|
||||
now = now.In(c.location)
|
||||
c.logger.Info("wake", "now", now)
|
||||
|
||||
// Run every entry whose next time was less than now
|
||||
for _, e := range c.entries {
|
||||
if e.Next.After(now) || e.Next.IsZero() {
|
||||
break
|
||||
}
|
||||
c.startJob(e.WrappedJob)
|
||||
e.Prev = e.Next
|
||||
e.Next = e.Schedule.Next(now)
|
||||
c.logger.Info("run", "now", now, "entry", e.ID, "next", e.Next)
|
||||
}
|
||||
|
||||
case newEntry := <-c.add:
|
||||
timer.Stop()
|
||||
now = c.now()
|
||||
newEntry.Next = newEntry.Schedule.Next(now)
|
||||
c.entries = append(c.entries, newEntry)
|
||||
c.logger.Info("added", "now", now, "entry", newEntry.ID, "next", newEntry.Next)
|
||||
|
||||
case replyChan := <-c.snapshot:
|
||||
replyChan <- c.entrySnapshot()
|
||||
continue
|
||||
|
||||
case <-c.stop:
|
||||
timer.Stop()
|
||||
c.logger.Info("stop")
|
||||
return
|
||||
|
||||
case id := <-c.remove:
|
||||
timer.Stop()
|
||||
now = c.now()
|
||||
c.removeEntry(id)
|
||||
c.logger.Info("removed", "entry", id)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// startJob runs the given job in a new goroutine.
|
||||
func (c *Cron) startJob(j Job) {
|
||||
c.jobWaiter.Add(1)
|
||||
go func() {
|
||||
defer c.jobWaiter.Done()
|
||||
j.Run()
|
||||
}()
|
||||
}
|
||||
|
||||
// now returns current time in c location
|
||||
func (c *Cron) now() time.Time {
|
||||
return time.Now().In(c.location)
|
||||
}
|
||||
|
||||
// Stop stops the cron scheduler if it is running; otherwise it does nothing.
|
||||
// A context is returned so the caller can wait for running jobs to complete.
|
||||
func (c *Cron) Stop() context.Context {
|
||||
c.runningMu.Lock()
|
||||
defer c.runningMu.Unlock()
|
||||
if c.running {
|
||||
c.stop <- struct{}{}
|
||||
c.running = false
|
||||
}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
go func() {
|
||||
c.jobWaiter.Wait()
|
||||
cancel()
|
||||
}()
|
||||
return ctx
|
||||
}
|
||||
|
||||
// entrySnapshot returns a copy of the current cron entry list.
|
||||
func (c *Cron) entrySnapshot() []Entry {
|
||||
var entries = make([]Entry, len(c.entries))
|
||||
for i, e := range c.entries {
|
||||
entries[i] = *e
|
||||
}
|
||||
return entries
|
||||
}
|
||||
|
||||
func (c *Cron) removeEntry(id EntryID) {
|
||||
var entries []*Entry
|
||||
for _, e := range c.entries {
|
||||
if e.ID != id {
|
||||
entries = append(entries, e)
|
||||
}
|
||||
}
|
||||
c.entries = entries
|
||||
}
|
212
vendor/github.com/robfig/cron/v3/doc.go
generated
vendored
Normal file
212
vendor/github.com/robfig/cron/v3/doc.go
generated
vendored
Normal file
@ -0,0 +1,212 @@
|
||||
/*
|
||||
Package cron implements a cron spec parser and job runner.
|
||||
|
||||
Usage
|
||||
|
||||
Callers may register Funcs to be invoked on a given schedule. Cron will run
|
||||
them in their own goroutines.
|
||||
|
||||
c := cron.New()
|
||||
c.AddFunc("30 * * * *", func() { fmt.Println("Every hour on the half hour") })
|
||||
c.AddFunc("30 3-6,20-23 * * *", func() { fmt.Println(".. in the range 3-6am, 8-11pm") })
|
||||
c.AddFunc("CRON_TZ=Asia/Tokyo 30 04 * * * *", func() { fmt.Println("Runs at 04:30 Tokyo time every day") })
|
||||
c.AddFunc("@hourly", func() { fmt.Println("Every hour, starting an hour from now") })
|
||||
c.AddFunc("@every 1h30m", func() { fmt.Println("Every hour thirty, starting an hour thirty from now") })
|
||||
c.Start()
|
||||
..
|
||||
// Funcs are invoked in their own goroutine, asynchronously.
|
||||
...
|
||||
// Funcs may also be added to a running Cron
|
||||
c.AddFunc("@daily", func() { fmt.Println("Every day") })
|
||||
..
|
||||
// Inspect the cron job entries' next and previous run times.
|
||||
inspect(c.Entries())
|
||||
..
|
||||
c.Stop() // Stop the scheduler (does not stop any jobs already running).
|
||||
|
||||
CRON Expression Format
|
||||
|
||||
A cron expression represents a set of times, using 5 space-separated fields.
|
||||
|
||||
Field name | Mandatory? | Allowed values | Allowed special characters
|
||||
---------- | ---------- | -------------- | --------------------------
|
||||
Minutes | Yes | 0-59 | * / , -
|
||||
Hours | Yes | 0-23 | * / , -
|
||||
Day of month | Yes | 1-31 | * / , - ?
|
||||
Month | Yes | 1-12 or JAN-DEC | * / , -
|
||||
Day of week | Yes | 0-6 or SUN-SAT | * / , - ?
|
||||
|
||||
Month and Day-of-week field values are case insensitive. "SUN", "Sun", and
|
||||
"sun" are equally accepted.
|
||||
|
||||
The specific interpretation of the format is based on the Cron Wikipedia page:
|
||||
https://en.wikipedia.org/wiki/Cron
|
||||
|
||||
Alternative Formats
|
||||
|
||||
Alternative Cron expression formats support other fields like seconds. You can
|
||||
implement that by creating a custom Parser as follows.
|
||||
|
||||
cron.New(
|
||||
cron.WithParser(
|
||||
cron.SecondOptional | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor))
|
||||
|
||||
The most popular alternative Cron expression format is Quartz:
|
||||
http://www.quartz-scheduler.org/documentation/quartz-2.x/tutorials/crontrigger.html
|
||||
|
||||
Special Characters
|
||||
|
||||
Asterisk ( * )
|
||||
|
||||
The asterisk indicates that the cron expression will match for all values of the
|
||||
field; e.g., using an asterisk in the 5th field (month) would indicate every
|
||||
month.
|
||||
|
||||
Slash ( / )
|
||||
|
||||
Slashes are used to describe increments of ranges. For example 3-59/15 in the
|
||||
1st field (minutes) would indicate the 3rd minute of the hour and every 15
|
||||
minutes thereafter. The form "*\/..." is equivalent to the form "first-last/...",
|
||||
that is, an increment over the largest possible range of the field. The form
|
||||
"N/..." is accepted as meaning "N-MAX/...", that is, starting at N, use the
|
||||
increment until the end of that specific range. It does not wrap around.
|
||||
|
||||
Comma ( , )
|
||||
|
||||
Commas are used to separate items of a list. For example, using "MON,WED,FRI" in
|
||||
the 5th field (day of week) would mean Mondays, Wednesdays and Fridays.
|
||||
|
||||
Hyphen ( - )
|
||||
|
||||
Hyphens are used to define ranges. For example, 9-17 would indicate every
|
||||
hour between 9am and 5pm inclusive.
|
||||
|
||||
Question mark ( ? )
|
||||
|
||||
Question mark may be used instead of '*' for leaving either day-of-month or
|
||||
day-of-week blank.
|
||||
|
||||
Predefined schedules
|
||||
|
||||
You may use one of several pre-defined schedules in place of a cron expression.
|
||||
|
||||
Entry | Description | Equivalent To
|
||||
----- | ----------- | -------------
|
||||
@yearly (or @annually) | Run once a year, midnight, Jan. 1st | 0 0 1 1 *
|
||||
@monthly | Run once a month, midnight, first of month | 0 0 1 * *
|
||||
@weekly | Run once a week, midnight between Sat/Sun | 0 0 * * 0
|
||||
@daily (or @midnight) | Run once a day, midnight | 0 0 * * *
|
||||
@hourly | Run once an hour, beginning of hour | 0 * * * *
|
||||
|
||||
Intervals
|
||||
|
||||
You may also schedule a job to execute at fixed intervals, starting at the time it's added
|
||||
or cron is run. This is supported by formatting the cron spec like this:
|
||||
|
||||
@every <duration>
|
||||
|
||||
where "duration" is a string accepted by time.ParseDuration
|
||||
(http://golang.org/pkg/time/#ParseDuration).
|
||||
|
||||
For example, "@every 1h30m10s" would indicate a schedule that activates after
|
||||
1 hour, 30 minutes, 10 seconds, and then every interval after that.
|
||||
|
||||
Note: The interval does not take the job runtime into account. For example,
|
||||
if a job takes 3 minutes to run, and it is scheduled to run every 5 minutes,
|
||||
it will have only 2 minutes of idle time between each run.
|
||||
|
||||
Time zones
|
||||
|
||||
By default, all interpretation and scheduling is done in the machine's local
|
||||
time zone (time.Local). You can specify a different time zone on construction:
|
||||
|
||||
cron.New(
|
||||
cron.WithLocation(time.UTC))
|
||||
|
||||
Individual cron schedules may also override the time zone they are to be
|
||||
interpreted in by providing an additional space-separated field at the beginning
|
||||
of the cron spec, of the form "CRON_TZ=Asia/Tokyo".
|
||||
|
||||
For example:
|
||||
|
||||
# Runs at 6am in time.Local
|
||||
cron.New().AddFunc("0 6 * * ?", ...)
|
||||
|
||||
# Runs at 6am in America/New_York
|
||||
nyc, _ := time.LoadLocation("America/New_York")
|
||||
c := cron.New(cron.WithLocation(nyc))
|
||||
c.AddFunc("0 6 * * ?", ...)
|
||||
|
||||
# Runs at 6am in Asia/Tokyo
|
||||
cron.New().AddFunc("CRON_TZ=Asia/Tokyo 0 6 * * ?", ...)
|
||||
|
||||
# Runs at 6am in Asia/Tokyo
|
||||
c := cron.New(cron.WithLocation(nyc))
|
||||
c.SetLocation("America/New_York")
|
||||
c.AddFunc("CRON_TZ=Asia/Tokyo 0 6 * * ?", ...)
|
||||
|
||||
The prefix "TZ=(TIME ZONE)" is also supported for legacy compatibility.
|
||||
|
||||
Be aware that jobs scheduled during daylight-savings leap-ahead transitions will
|
||||
not be run!
|
||||
|
||||
Job Wrappers / Chain
|
||||
|
||||
A Cron runner may be configured with a chain of job wrappers to add
|
||||
cross-cutting functionality to all submitted jobs. For example, they may be used
|
||||
to achieve the following effects:
|
||||
|
||||
- Recover any panics from jobs (activated by default)
|
||||
- Delay a job's execution if the previous run hasn't completed yet
|
||||
- Skip a job's execution if the previous run hasn't completed yet
|
||||
- Log each job's invocations
|
||||
|
||||
Install wrappers for all jobs added to a cron using the `cron.WithChain` option:
|
||||
|
||||
cron.New(cron.WithChain(
|
||||
cron.SkipIfStillRunning(logger),
|
||||
))
|
||||
|
||||
Install wrappers for individual jobs by explicitly wrapping them:
|
||||
|
||||
job = cron.NewChain(
|
||||
cron.SkipIfStillRunning(logger),
|
||||
).Then(job)
|
||||
|
||||
Thread safety
|
||||
|
||||
Since the Cron service runs concurrently with the calling code, some amount of
|
||||
care must be taken to ensure proper synchronization.
|
||||
|
||||
All cron methods are designed to be correctly synchronized as long as the caller
|
||||
ensures that invocations have a clear happens-before ordering between them.
|
||||
|
||||
Logging
|
||||
|
||||
Cron defines a Logger interface that is a subset of the one defined in
|
||||
github.com/go-logr/logr. It has two logging levels (Info and Error), and
|
||||
parameters are key/value pairs. This makes it possible for cron logging to plug
|
||||
into structured logging systems. An adapter, [Verbose]PrintfLogger, is provided
|
||||
to wrap the standard library *log.Logger.
|
||||
|
||||
For additional insight into Cron operations, verbose logging may be activated
|
||||
which will record job runs, scheduling decisions, and added or removed jobs.
|
||||
Activate it with a one-off logger as follows:
|
||||
|
||||
cron.New(
|
||||
cron.WithLogger(
|
||||
cron.VerbosePrintfLogger(log.New(os.Stdout, "cron: ", log.LstdFlags))))
|
||||
|
||||
|
||||
Implementation
|
||||
|
||||
Cron entries are stored in an array, sorted by their next activation time. Cron
|
||||
sleeps until the next job is due to be run.
|
||||
|
||||
Upon waking:
|
||||
- it runs each entry that is active on that second
|
||||
- it calculates the next run times for the jobs that were run
|
||||
- it re-sorts the array of entries by next activation time.
|
||||
- it goes to sleep until the soonest job.
|
||||
*/
|
||||
package cron
|
3
vendor/github.com/robfig/cron/v3/go.mod
generated
vendored
Normal file
3
vendor/github.com/robfig/cron/v3/go.mod
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
module github.com/robfig/cron/v3
|
||||
|
||||
go 1.12
|
86
vendor/github.com/robfig/cron/v3/logger.go
generated
vendored
Normal file
86
vendor/github.com/robfig/cron/v3/logger.go
generated
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
package cron
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DefaultLogger is used by Cron if none is specified.
|
||||
var DefaultLogger Logger = PrintfLogger(log.New(os.Stdout, "cron: ", log.LstdFlags))
|
||||
|
||||
// DiscardLogger can be used by callers to discard all log messages.
|
||||
var DiscardLogger Logger = PrintfLogger(log.New(ioutil.Discard, "", 0))
|
||||
|
||||
// Logger is the interface used in this package for logging, so that any backend
|
||||
// can be plugged in. It is a subset of the github.com/go-logr/logr interface.
|
||||
type Logger interface {
|
||||
// Info logs routine messages about cron's operation.
|
||||
Info(msg string, keysAndValues ...interface{})
|
||||
// Error logs an error condition.
|
||||
Error(err error, msg string, keysAndValues ...interface{})
|
||||
}
|
||||
|
||||
// PrintfLogger wraps a Printf-based logger (such as the standard library "log")
|
||||
// into an implementation of the Logger interface which logs errors only.
|
||||
func PrintfLogger(l interface{ Printf(string, ...interface{}) }) Logger {
|
||||
return printfLogger{l, false}
|
||||
}
|
||||
|
||||
// VerbosePrintfLogger wraps a Printf-based logger (such as the standard library
|
||||
// "log") into an implementation of the Logger interface which logs everything.
|
||||
func VerbosePrintfLogger(l interface{ Printf(string, ...interface{}) }) Logger {
|
||||
return printfLogger{l, true}
|
||||
}
|
||||
|
||||
type printfLogger struct {
|
||||
logger interface{ Printf(string, ...interface{}) }
|
||||
logInfo bool
|
||||
}
|
||||
|
||||
func (pl printfLogger) Info(msg string, keysAndValues ...interface{}) {
|
||||
if pl.logInfo {
|
||||
keysAndValues = formatTimes(keysAndValues)
|
||||
pl.logger.Printf(
|
||||
formatString(len(keysAndValues)),
|
||||
append([]interface{}{msg}, keysAndValues...)...)
|
||||
}
|
||||
}
|
||||
|
||||
func (pl printfLogger) Error(err error, msg string, keysAndValues ...interface{}) {
|
||||
keysAndValues = formatTimes(keysAndValues)
|
||||
pl.logger.Printf(
|
||||
formatString(len(keysAndValues)+2),
|
||||
append([]interface{}{msg, "error", err}, keysAndValues...)...)
|
||||
}
|
||||
|
||||
// formatString returns a logfmt-like format string for the number of
|
||||
// key/values.
|
||||
func formatString(numKeysAndValues int) string {
|
||||
var sb strings.Builder
|
||||
sb.WriteString("%s")
|
||||
if numKeysAndValues > 0 {
|
||||
sb.WriteString(", ")
|
||||
}
|
||||
for i := 0; i < numKeysAndValues/2; i++ {
|
||||
if i > 0 {
|
||||
sb.WriteString(", ")
|
||||
}
|
||||
sb.WriteString("%v=%v")
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// formatTimes formats any time.Time values as RFC3339.
|
||||
func formatTimes(keysAndValues []interface{}) []interface{} {
|
||||
var formattedArgs []interface{}
|
||||
for _, arg := range keysAndValues {
|
||||
if t, ok := arg.(time.Time); ok {
|
||||
arg = t.Format(time.RFC3339)
|
||||
}
|
||||
formattedArgs = append(formattedArgs, arg)
|
||||
}
|
||||
return formattedArgs
|
||||
}
|
45
vendor/github.com/robfig/cron/v3/option.go
generated
vendored
Normal file
45
vendor/github.com/robfig/cron/v3/option.go
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
package cron
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Option represents a modification to the default behavior of a Cron.
|
||||
type Option func(*Cron)
|
||||
|
||||
// WithLocation overrides the timezone of the cron instance.
|
||||
func WithLocation(loc *time.Location) Option {
|
||||
return func(c *Cron) {
|
||||
c.location = loc
|
||||
}
|
||||
}
|
||||
|
||||
// WithSeconds overrides the parser used for interpreting job schedules to
|
||||
// include a seconds field as the first one.
|
||||
func WithSeconds() Option {
|
||||
return WithParser(NewParser(
|
||||
Second | Minute | Hour | Dom | Month | Dow | Descriptor,
|
||||
))
|
||||
}
|
||||
|
||||
// WithParser overrides the parser used for interpreting job schedules.
|
||||
func WithParser(p Parser) Option {
|
||||
return func(c *Cron) {
|
||||
c.parser = p
|
||||
}
|
||||
}
|
||||
|
||||
// WithChain specifies Job wrappers to apply to all jobs added to this cron.
|
||||
// Refer to the Chain* functions in this package for provided wrappers.
|
||||
func WithChain(wrappers ...JobWrapper) Option {
|
||||
return func(c *Cron) {
|
||||
c.chain = NewChain(wrappers...)
|
||||
}
|
||||
}
|
||||
|
||||
// WithLogger uses the provided logger.
|
||||
func WithLogger(logger Logger) Option {
|
||||
return func(c *Cron) {
|
||||
c.logger = logger
|
||||
}
|
||||
}
|
434
vendor/github.com/robfig/cron/v3/parser.go
generated
vendored
Normal file
434
vendor/github.com/robfig/cron/v3/parser.go
generated
vendored
Normal file
@ -0,0 +1,434 @@
|
||||
package cron
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Configuration options for creating a parser. Most options specify which
|
||||
// fields should be included, while others enable features. If a field is not
|
||||
// included the parser will assume a default value. These options do not change
|
||||
// the order fields are parse in.
|
||||
type ParseOption int
|
||||
|
||||
const (
|
||||
Second ParseOption = 1 << iota // Seconds field, default 0
|
||||
SecondOptional // Optional seconds field, default 0
|
||||
Minute // Minutes field, default 0
|
||||
Hour // Hours field, default 0
|
||||
Dom // Day of month field, default *
|
||||
Month // Month field, default *
|
||||
Dow // Day of week field, default *
|
||||
DowOptional // Optional day of week field, default *
|
||||
Descriptor // Allow descriptors such as @monthly, @weekly, etc.
|
||||
)
|
||||
|
||||
var places = []ParseOption{
|
||||
Second,
|
||||
Minute,
|
||||
Hour,
|
||||
Dom,
|
||||
Month,
|
||||
Dow,
|
||||
}
|
||||
|
||||
var defaults = []string{
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"*",
|
||||
"*",
|
||||
"*",
|
||||
}
|
||||
|
||||
// A custom Parser that can be configured.
|
||||
type Parser struct {
|
||||
options ParseOption
|
||||
}
|
||||
|
||||
// NewParser creates a Parser with custom options.
|
||||
//
|
||||
// It panics if more than one Optional is given, since it would be impossible to
|
||||
// correctly infer which optional is provided or missing in general.
|
||||
//
|
||||
// Examples
|
||||
//
|
||||
// // Standard parser without descriptors
|
||||
// specParser := NewParser(Minute | Hour | Dom | Month | Dow)
|
||||
// sched, err := specParser.Parse("0 0 15 */3 *")
|
||||
//
|
||||
// // Same as above, just excludes time fields
|
||||
// subsParser := NewParser(Dom | Month | Dow)
|
||||
// sched, err := specParser.Parse("15 */3 *")
|
||||
//
|
||||
// // Same as above, just makes Dow optional
|
||||
// subsParser := NewParser(Dom | Month | DowOptional)
|
||||
// sched, err := specParser.Parse("15 */3")
|
||||
//
|
||||
func NewParser(options ParseOption) Parser {
|
||||
optionals := 0
|
||||
if options&DowOptional > 0 {
|
||||
optionals++
|
||||
}
|
||||
if options&SecondOptional > 0 {
|
||||
optionals++
|
||||
}
|
||||
if optionals > 1 {
|
||||
panic("multiple optionals may not be configured")
|
||||
}
|
||||
return Parser{options}
|
||||
}
|
||||
|
||||
// Parse returns a new crontab schedule representing the given spec.
|
||||
// It returns a descriptive error if the spec is not valid.
|
||||
// It accepts crontab specs and features configured by NewParser.
|
||||
func (p Parser) Parse(spec string) (Schedule, error) {
|
||||
if len(spec) == 0 {
|
||||
return nil, fmt.Errorf("empty spec string")
|
||||
}
|
||||
|
||||
// Extract timezone if present
|
||||
var loc = time.Local
|
||||
if strings.HasPrefix(spec, "TZ=") || strings.HasPrefix(spec, "CRON_TZ=") {
|
||||
var err error
|
||||
i := strings.Index(spec, " ")
|
||||
eq := strings.Index(spec, "=")
|
||||
if loc, err = time.LoadLocation(spec[eq+1 : i]); err != nil {
|
||||
return nil, fmt.Errorf("provided bad location %s: %v", spec[eq+1:i], err)
|
||||
}
|
||||
spec = strings.TrimSpace(spec[i:])
|
||||
}
|
||||
|
||||
// Handle named schedules (descriptors), if configured
|
||||
if strings.HasPrefix(spec, "@") {
|
||||
if p.options&Descriptor == 0 {
|
||||
return nil, fmt.Errorf("parser does not accept descriptors: %v", spec)
|
||||
}
|
||||
return parseDescriptor(spec, loc)
|
||||
}
|
||||
|
||||
// Split on whitespace.
|
||||
fields := strings.Fields(spec)
|
||||
|
||||
// Validate & fill in any omitted or optional fields
|
||||
var err error
|
||||
fields, err = normalizeFields(fields, p.options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
field := func(field string, r bounds) uint64 {
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
var bits uint64
|
||||
bits, err = getField(field, r)
|
||||
return bits
|
||||
}
|
||||
|
||||
var (
|
||||
second = field(fields[0], seconds)
|
||||
minute = field(fields[1], minutes)
|
||||
hour = field(fields[2], hours)
|
||||
dayofmonth = field(fields[3], dom)
|
||||
month = field(fields[4], months)
|
||||
dayofweek = field(fields[5], dow)
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &SpecSchedule{
|
||||
Second: second,
|
||||
Minute: minute,
|
||||
Hour: hour,
|
||||
Dom: dayofmonth,
|
||||
Month: month,
|
||||
Dow: dayofweek,
|
||||
Location: loc,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// normalizeFields takes a subset set of the time fields and returns the full set
|
||||
// with defaults (zeroes) populated for unset fields.
|
||||
//
|
||||
// As part of performing this function, it also validates that the provided
|
||||
// fields are compatible with the configured options.
|
||||
func normalizeFields(fields []string, options ParseOption) ([]string, error) {
|
||||
// Validate optionals & add their field to options
|
||||
optionals := 0
|
||||
if options&SecondOptional > 0 {
|
||||
options |= Second
|
||||
optionals++
|
||||
}
|
||||
if options&DowOptional > 0 {
|
||||
options |= Dow
|
||||
optionals++
|
||||
}
|
||||
if optionals > 1 {
|
||||
return nil, fmt.Errorf("multiple optionals may not be configured")
|
||||
}
|
||||
|
||||
// Figure out how many fields we need
|
||||
max := 0
|
||||
for _, place := range places {
|
||||
if options&place > 0 {
|
||||
max++
|
||||
}
|
||||
}
|
||||
min := max - optionals
|
||||
|
||||
// Validate number of fields
|
||||
if count := len(fields); count < min || count > max {
|
||||
if min == max {
|
||||
return nil, fmt.Errorf("expected exactly %d fields, found %d: %s", min, count, fields)
|
||||
}
|
||||
return nil, fmt.Errorf("expected %d to %d fields, found %d: %s", min, max, count, fields)
|
||||
}
|
||||
|
||||
// Populate the optional field if not provided
|
||||
if min < max && len(fields) == min {
|
||||
switch {
|
||||
case options&DowOptional > 0:
|
||||
fields = append(fields, defaults[5]) // TODO: improve access to default
|
||||
case options&SecondOptional > 0:
|
||||
fields = append([]string{defaults[0]}, fields...)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown optional field")
|
||||
}
|
||||
}
|
||||
|
||||
// Populate all fields not part of options with their defaults
|
||||
n := 0
|
||||
expandedFields := make([]string, len(places))
|
||||
copy(expandedFields, defaults)
|
||||
for i, place := range places {
|
||||
if options&place > 0 {
|
||||
expandedFields[i] = fields[n]
|
||||
n++
|
||||
}
|
||||
}
|
||||
return expandedFields, nil
|
||||
}
|
||||
|
||||
var standardParser = NewParser(
|
||||
Minute | Hour | Dom | Month | Dow | Descriptor,
|
||||
)
|
||||
|
||||
// ParseStandard returns a new crontab schedule representing the given
|
||||
// standardSpec (https://en.wikipedia.org/wiki/Cron). It requires 5 entries
|
||||
// representing: minute, hour, day of month, month and day of week, in that
|
||||
// order. It returns a descriptive error if the spec is not valid.
|
||||
//
|
||||
// It accepts
|
||||
// - Standard crontab specs, e.g. "* * * * ?"
|
||||
// - Descriptors, e.g. "@midnight", "@every 1h30m"
|
||||
func ParseStandard(standardSpec string) (Schedule, error) {
|
||||
return standardParser.Parse(standardSpec)
|
||||
}
|
||||
|
||||
// getField returns an Int with the bits set representing all of the times that
|
||||
// the field represents or error parsing field value. A "field" is a comma-separated
|
||||
// list of "ranges".
|
||||
func getField(field string, r bounds) (uint64, error) {
|
||||
var bits uint64
|
||||
ranges := strings.FieldsFunc(field, func(r rune) bool { return r == ',' })
|
||||
for _, expr := range ranges {
|
||||
bit, err := getRange(expr, r)
|
||||
if err != nil {
|
||||
return bits, err
|
||||
}
|
||||
bits |= bit
|
||||
}
|
||||
return bits, nil
|
||||
}
|
||||
|
||||
// getRange returns the bits indicated by the given expression:
|
||||
// number | number "-" number [ "/" number ]
|
||||
// or error parsing range.
|
||||
func getRange(expr string, r bounds) (uint64, error) {
|
||||
var (
|
||||
start, end, step uint
|
||||
rangeAndStep = strings.Split(expr, "/")
|
||||
lowAndHigh = strings.Split(rangeAndStep[0], "-")
|
||||
singleDigit = len(lowAndHigh) == 1
|
||||
err error
|
||||
)
|
||||
|
||||
var extra uint64
|
||||
if lowAndHigh[0] == "*" || lowAndHigh[0] == "?" {
|
||||
start = r.min
|
||||
end = r.max
|
||||
extra = starBit
|
||||
} else {
|
||||
start, err = parseIntOrName(lowAndHigh[0], r.names)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
switch len(lowAndHigh) {
|
||||
case 1:
|
||||
end = start
|
||||
case 2:
|
||||
end, err = parseIntOrName(lowAndHigh[1], r.names)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
default:
|
||||
return 0, fmt.Errorf("too many hyphens: %s", expr)
|
||||
}
|
||||
}
|
||||
|
||||
switch len(rangeAndStep) {
|
||||
case 1:
|
||||
step = 1
|
||||
case 2:
|
||||
step, err = mustParseInt(rangeAndStep[1])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Special handling: "N/step" means "N-max/step".
|
||||
if singleDigit {
|
||||
end = r.max
|
||||
}
|
||||
if step > 1 {
|
||||
extra = 0
|
||||
}
|
||||
default:
|
||||
return 0, fmt.Errorf("too many slashes: %s", expr)
|
||||
}
|
||||
|
||||
if start < r.min {
|
||||
return 0, fmt.Errorf("beginning of range (%d) below minimum (%d): %s", start, r.min, expr)
|
||||
}
|
||||
if end > r.max {
|
||||
return 0, fmt.Errorf("end of range (%d) above maximum (%d): %s", end, r.max, expr)
|
||||
}
|
||||
if start > end {
|
||||
return 0, fmt.Errorf("beginning of range (%d) beyond end of range (%d): %s", start, end, expr)
|
||||
}
|
||||
if step == 0 {
|
||||
return 0, fmt.Errorf("step of range should be a positive number: %s", expr)
|
||||
}
|
||||
|
||||
return getBits(start, end, step) | extra, nil
|
||||
}
|
||||
|
||||
// parseIntOrName returns the (possibly-named) integer contained in expr.
|
||||
func parseIntOrName(expr string, names map[string]uint) (uint, error) {
|
||||
if names != nil {
|
||||
if namedInt, ok := names[strings.ToLower(expr)]; ok {
|
||||
return namedInt, nil
|
||||
}
|
||||
}
|
||||
return mustParseInt(expr)
|
||||
}
|
||||
|
||||
// mustParseInt parses the given expression as an int or returns an error.
|
||||
func mustParseInt(expr string) (uint, error) {
|
||||
num, err := strconv.Atoi(expr)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to parse int from %s: %s", expr, err)
|
||||
}
|
||||
if num < 0 {
|
||||
return 0, fmt.Errorf("negative number (%d) not allowed: %s", num, expr)
|
||||
}
|
||||
|
||||
return uint(num), nil
|
||||
}
|
||||
|
||||
// getBits sets all bits in the range [min, max], modulo the given step size.
|
||||
func getBits(min, max, step uint) uint64 {
|
||||
var bits uint64
|
||||
|
||||
// If step is 1, use shifts.
|
||||
if step == 1 {
|
||||
return ^(math.MaxUint64 << (max + 1)) & (math.MaxUint64 << min)
|
||||
}
|
||||
|
||||
// Else, use a simple loop.
|
||||
for i := min; i <= max; i += step {
|
||||
bits |= 1 << i
|
||||
}
|
||||
return bits
|
||||
}
|
||||
|
||||
// all returns all bits within the given bounds. (plus the star bit)
|
||||
func all(r bounds) uint64 {
|
||||
return getBits(r.min, r.max, 1) | starBit
|
||||
}
|
||||
|
||||
// parseDescriptor returns a predefined schedule for the expression, or error if none matches.
|
||||
func parseDescriptor(descriptor string, loc *time.Location) (Schedule, error) {
|
||||
switch descriptor {
|
||||
case "@yearly", "@annually":
|
||||
return &SpecSchedule{
|
||||
Second: 1 << seconds.min,
|
||||
Minute: 1 << minutes.min,
|
||||
Hour: 1 << hours.min,
|
||||
Dom: 1 << dom.min,
|
||||
Month: 1 << months.min,
|
||||
Dow: all(dow),
|
||||
Location: loc,
|
||||
}, nil
|
||||
|
||||
case "@monthly":
|
||||
return &SpecSchedule{
|
||||
Second: 1 << seconds.min,
|
||||
Minute: 1 << minutes.min,
|
||||
Hour: 1 << hours.min,
|
||||
Dom: 1 << dom.min,
|
||||
Month: all(months),
|
||||
Dow: all(dow),
|
||||
Location: loc,
|
||||
}, nil
|
||||
|
||||
case "@weekly":
|
||||
return &SpecSchedule{
|
||||
Second: 1 << seconds.min,
|
||||
Minute: 1 << minutes.min,
|
||||
Hour: 1 << hours.min,
|
||||
Dom: all(dom),
|
||||
Month: all(months),
|
||||
Dow: 1 << dow.min,
|
||||
Location: loc,
|
||||
}, nil
|
||||
|
||||
case "@daily", "@midnight":
|
||||
return &SpecSchedule{
|
||||
Second: 1 << seconds.min,
|
||||
Minute: 1 << minutes.min,
|
||||
Hour: 1 << hours.min,
|
||||
Dom: all(dom),
|
||||
Month: all(months),
|
||||
Dow: all(dow),
|
||||
Location: loc,
|
||||
}, nil
|
||||
|
||||
case "@hourly":
|
||||
return &SpecSchedule{
|
||||
Second: 1 << seconds.min,
|
||||
Minute: 1 << minutes.min,
|
||||
Hour: all(hours),
|
||||
Dom: all(dom),
|
||||
Month: all(months),
|
||||
Dow: all(dow),
|
||||
Location: loc,
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
const every = "@every "
|
||||
if strings.HasPrefix(descriptor, every) {
|
||||
duration, err := time.ParseDuration(descriptor[len(every):])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse duration %s: %s", descriptor, err)
|
||||
}
|
||||
return Every(duration), nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unrecognized descriptor: %s", descriptor)
|
||||
}
|
188
vendor/github.com/robfig/cron/v3/spec.go
generated
vendored
Normal file
188
vendor/github.com/robfig/cron/v3/spec.go
generated
vendored
Normal file
@ -0,0 +1,188 @@
|
||||
package cron
|
||||
|
||||
import "time"
|
||||
|
||||
// SpecSchedule specifies a duty cycle (to the second granularity), based on a
|
||||
// traditional crontab specification. It is computed initially and stored as bit sets.
|
||||
type SpecSchedule struct {
|
||||
Second, Minute, Hour, Dom, Month, Dow uint64
|
||||
|
||||
// Override location for this schedule.
|
||||
Location *time.Location
|
||||
}
|
||||
|
||||
// bounds provides a range of acceptable values (plus a map of name to value).
|
||||
type bounds struct {
|
||||
min, max uint
|
||||
names map[string]uint
|
||||
}
|
||||
|
||||
// The bounds for each field.
|
||||
var (
|
||||
seconds = bounds{0, 59, nil}
|
||||
minutes = bounds{0, 59, nil}
|
||||
hours = bounds{0, 23, nil}
|
||||
dom = bounds{1, 31, nil}
|
||||
months = bounds{1, 12, map[string]uint{
|
||||
"jan": 1,
|
||||
"feb": 2,
|
||||
"mar": 3,
|
||||
"apr": 4,
|
||||
"may": 5,
|
||||
"jun": 6,
|
||||
"jul": 7,
|
||||
"aug": 8,
|
||||
"sep": 9,
|
||||
"oct": 10,
|
||||
"nov": 11,
|
||||
"dec": 12,
|
||||
}}
|
||||
dow = bounds{0, 6, map[string]uint{
|
||||
"sun": 0,
|
||||
"mon": 1,
|
||||
"tue": 2,
|
||||
"wed": 3,
|
||||
"thu": 4,
|
||||
"fri": 5,
|
||||
"sat": 6,
|
||||
}}
|
||||
)
|
||||
|
||||
const (
|
||||
// Set the top bit if a star was included in the expression.
|
||||
starBit = 1 << 63
|
||||
)
|
||||
|
||||
// Next returns the next time this schedule is activated, greater than the given
|
||||
// time. If no time can be found to satisfy the schedule, return the zero time.
|
||||
func (s *SpecSchedule) Next(t time.Time) time.Time {
|
||||
// General approach
|
||||
//
|
||||
// For Month, Day, Hour, Minute, Second:
|
||||
// Check if the time value matches. If yes, continue to the next field.
|
||||
// If the field doesn't match the schedule, then increment the field until it matches.
|
||||
// While incrementing the field, a wrap-around brings it back to the beginning
|
||||
// of the field list (since it is necessary to re-verify previous field
|
||||
// values)
|
||||
|
||||
// Convert the given time into the schedule's timezone, if one is specified.
|
||||
// Save the original timezone so we can convert back after we find a time.
|
||||
// Note that schedules without a time zone specified (time.Local) are treated
|
||||
// as local to the time provided.
|
||||
origLocation := t.Location()
|
||||
loc := s.Location
|
||||
if loc == time.Local {
|
||||
loc = t.Location()
|
||||
}
|
||||
if s.Location != time.Local {
|
||||
t = t.In(s.Location)
|
||||
}
|
||||
|
||||
// Start at the earliest possible time (the upcoming second).
|
||||
t = t.Add(1*time.Second - time.Duration(t.Nanosecond())*time.Nanosecond)
|
||||
|
||||
// This flag indicates whether a field has been incremented.
|
||||
added := false
|
||||
|
||||
// If no time is found within five years, return zero.
|
||||
yearLimit := t.Year() + 5
|
||||
|
||||
WRAP:
|
||||
if t.Year() > yearLimit {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
// Find the first applicable month.
|
||||
// If it's this month, then do nothing.
|
||||
for 1<<uint(t.Month())&s.Month == 0 {
|
||||
// If we have to add a month, reset the other parts to 0.
|
||||
if !added {
|
||||
added = true
|
||||
// Otherwise, set the date at the beginning (since the current time is irrelevant).
|
||||
t = time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, loc)
|
||||
}
|
||||
t = t.AddDate(0, 1, 0)
|
||||
|
||||
// Wrapped around.
|
||||
if t.Month() == time.January {
|
||||
goto WRAP
|
||||
}
|
||||
}
|
||||
|
||||
// Now get a day in that month.
|
||||
//
|
||||
// NOTE: This causes issues for daylight savings regimes where midnight does
|
||||
// not exist. For example: Sao Paulo has DST that transforms midnight on
|
||||
// 11/3 into 1am. Handle that by noticing when the Hour ends up != 0.
|
||||
for !dayMatches(s, t) {
|
||||
if !added {
|
||||
added = true
|
||||
t = time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, loc)
|
||||
}
|
||||
t = t.AddDate(0, 0, 1)
|
||||
// Notice if the hour is no longer midnight due to DST.
|
||||
// Add an hour if it's 23, subtract an hour if it's 1.
|
||||
if t.Hour() != 0 {
|
||||
if t.Hour() > 12 {
|
||||
t = t.Add(time.Duration(24-t.Hour()) * time.Hour)
|
||||
} else {
|
||||
t = t.Add(time.Duration(-t.Hour()) * time.Hour)
|
||||
}
|
||||
}
|
||||
|
||||
if t.Day() == 1 {
|
||||
goto WRAP
|
||||
}
|
||||
}
|
||||
|
||||
for 1<<uint(t.Hour())&s.Hour == 0 {
|
||||
if !added {
|
||||
added = true
|
||||
t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), 0, 0, 0, loc)
|
||||
}
|
||||
t = t.Add(1 * time.Hour)
|
||||
|
||||
if t.Hour() == 0 {
|
||||
goto WRAP
|
||||
}
|
||||
}
|
||||
|
||||
for 1<<uint(t.Minute())&s.Minute == 0 {
|
||||
if !added {
|
||||
added = true
|
||||
t = t.Truncate(time.Minute)
|
||||
}
|
||||
t = t.Add(1 * time.Minute)
|
||||
|
||||
if t.Minute() == 0 {
|
||||
goto WRAP
|
||||
}
|
||||
}
|
||||
|
||||
for 1<<uint(t.Second())&s.Second == 0 {
|
||||
if !added {
|
||||
added = true
|
||||
t = t.Truncate(time.Second)
|
||||
}
|
||||
t = t.Add(1 * time.Second)
|
||||
|
||||
if t.Second() == 0 {
|
||||
goto WRAP
|
||||
}
|
||||
}
|
||||
|
||||
return t.In(origLocation)
|
||||
}
|
||||
|
||||
// dayMatches returns true if the schedule's day-of-week and day-of-month
|
||||
// restrictions are satisfied by the given time.
|
||||
func dayMatches(s *SpecSchedule, t time.Time) bool {
|
||||
var (
|
||||
domMatch bool = 1<<uint(t.Day())&s.Dom > 0
|
||||
dowMatch bool = 1<<uint(t.Weekday())&s.Dow > 0
|
||||
)
|
||||
if s.Dom&starBit > 0 || s.Dow&starBit > 0 {
|
||||
return domMatch && dowMatch
|
||||
}
|
||||
return domMatch || dowMatch
|
||||
}
|
4
vendor/modules.txt
vendored
4
vendor/modules.txt
vendored
@ -134,6 +134,8 @@ github.com/jmespath/go-jmespath
|
||||
github.com/jonboulle/clockwork
|
||||
# github.com/jtolds/gls v4.20.0+incompatible
|
||||
github.com/jtolds/gls
|
||||
# github.com/jung-kurt/gofpdf v1.10.1
|
||||
github.com/jung-kurt/gofpdf
|
||||
# github.com/klauspost/compress v1.4.1
|
||||
github.com/klauspost/compress/gzip
|
||||
github.com/klauspost/compress/flate
|
||||
@ -186,6 +188,8 @@ github.com/prometheus/procfs/internal/util
|
||||
github.com/rainycape/unidecode
|
||||
# github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967
|
||||
github.com/robfig/cron
|
||||
# github.com/robfig/cron/v3 v3.0.0
|
||||
github.com/robfig/cron/v3
|
||||
# github.com/russellhaering/goxmldsig v0.0.0-20180430223755-7acd5e4a6ef7
|
||||
github.com/russellhaering/goxmldsig
|
||||
github.com/russellhaering/goxmldsig/etreeutils
|
||||
|
Loading…
Reference in New Issue
Block a user