fixed dependencies
This commit is contained in:
251
vendor/gonum.org/v1/plot/vg/vgpdf/cp1252.map
generated
vendored
Normal file
251
vendor/gonum.org/v1/plot/vg/vgpdf/cp1252.map
generated
vendored
Normal file
@@ -0,0 +1,251 @@
|
||||
!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
|
||||
490
vendor/gonum.org/v1/plot/vg/vgpdf/vgpdf.go
generated
vendored
Normal file
490
vendor/gonum.org/v1/plot/vg/vgpdf/vgpdf.go
generated
vendored
Normal file
@@ -0,0 +1,490 @@
|
||||
// Copyright ©2015 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package vgpdf implements the vg.Canvas interface
|
||||
// using gofpdf (github.com/phpdave11/gofpdf).
|
||||
package vgpdf // import "gonum.org/v1/plot/vg/vgpdf"
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/png"
|
||||
"io"
|
||||
"log"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
pdf "github.com/go-pdf/fpdf"
|
||||
stdfnt "golang.org/x/image/font"
|
||||
|
||||
"gonum.org/v1/plot/font"
|
||||
"gonum.org/v1/plot/vg"
|
||||
"gonum.org/v1/plot/vg/draw"
|
||||
)
|
||||
|
||||
// codePageEncoding holds informations about the characters encoding of TrueType
|
||||
// font files, needed by gofpdf to embed fonts in a PDF document.
|
||||
// We use cp1252 (code page 1252, Windows Western) to encode characters.
|
||||
// See:
|
||||
// - https://en.wikipedia.org/wiki/Windows-1252
|
||||
//
|
||||
// TODO: provide a Canvas-level func option to embed fonts with a user provided
|
||||
// code page schema?
|
||||
//
|
||||
//go:embed cp1252.map
|
||||
var codePageEncoding []byte
|
||||
|
||||
func init() {
|
||||
draw.RegisterFormat("pdf", func(w, h vg.Length) vg.CanvasWriterTo {
|
||||
return New(w, h)
|
||||
})
|
||||
}
|
||||
|
||||
// DPI is the nominal resolution of drawing in PDF.
|
||||
const DPI = 72
|
||||
|
||||
// Canvas implements the vg.Canvas interface,
|
||||
// drawing to a PDF.
|
||||
type Canvas struct {
|
||||
doc *pdf.Fpdf
|
||||
w, h vg.Length
|
||||
|
||||
dpi int
|
||||
numImages int
|
||||
stack []context
|
||||
fonts map[font.Font]struct{}
|
||||
|
||||
// Switch to embed fonts in PDF file.
|
||||
// The default is to embed fonts.
|
||||
// This makes the PDF file more portable but also larger.
|
||||
embed bool
|
||||
}
|
||||
|
||||
type context struct {
|
||||
fill color.Color
|
||||
line color.Color
|
||||
width vg.Length
|
||||
}
|
||||
|
||||
// New creates a new PDF Canvas.
|
||||
func New(w, h vg.Length) *Canvas {
|
||||
cfg := pdf.InitType{
|
||||
UnitStr: "pt",
|
||||
Size: pdf.SizeType{Wd: w.Points(), Ht: h.Points()},
|
||||
}
|
||||
c := &Canvas{
|
||||
doc: pdf.NewCustom(&cfg),
|
||||
w: w,
|
||||
h: h,
|
||||
dpi: DPI,
|
||||
stack: make([]context, 1),
|
||||
fonts: make(map[font.Font]struct{}),
|
||||
embed: true,
|
||||
}
|
||||
c.NextPage()
|
||||
vg.Initialize(c)
|
||||
return c
|
||||
}
|
||||
|
||||
// EmbedFonts specifies whether the resulting PDF canvas should
|
||||
// embed the fonts or not.
|
||||
// EmbedFonts returns the previous value before modification.
|
||||
func (c *Canvas) EmbedFonts(v bool) bool {
|
||||
prev := c.embed
|
||||
c.embed = v
|
||||
return prev
|
||||
}
|
||||
|
||||
func (c *Canvas) DPI() float64 {
|
||||
return float64(c.dpi)
|
||||
}
|
||||
|
||||
func (c *Canvas) context() *context {
|
||||
return &c.stack[len(c.stack)-1]
|
||||
}
|
||||
|
||||
func (c *Canvas) Size() (w, h vg.Length) {
|
||||
return c.w, c.h
|
||||
}
|
||||
|
||||
func (c *Canvas) SetLineWidth(w vg.Length) {
|
||||
c.context().width = w
|
||||
lw := c.unit(w)
|
||||
c.doc.SetLineWidth(lw)
|
||||
}
|
||||
|
||||
func (c *Canvas) SetLineDash(dashes []vg.Length, offs vg.Length) {
|
||||
ds := make([]float64, len(dashes))
|
||||
for i, d := range dashes {
|
||||
ds[i] = c.unit(d)
|
||||
}
|
||||
c.doc.SetDashPattern(ds, c.unit(offs))
|
||||
}
|
||||
|
||||
func (c *Canvas) SetColor(clr color.Color) {
|
||||
if clr == nil {
|
||||
clr = color.Black
|
||||
}
|
||||
c.context().line = clr
|
||||
c.context().fill = clr
|
||||
r, g, b, a := rgba(clr)
|
||||
c.doc.SetFillColor(r, g, b)
|
||||
c.doc.SetDrawColor(r, g, b)
|
||||
c.doc.SetTextColor(r, g, b)
|
||||
c.doc.SetAlpha(a, "Normal")
|
||||
}
|
||||
|
||||
func (c *Canvas) Rotate(r float64) {
|
||||
c.doc.TransformRotate(-r*180/math.Pi, 0, 0)
|
||||
}
|
||||
|
||||
func (c *Canvas) Translate(pt vg.Point) {
|
||||
xp, yp := c.pdfPoint(pt)
|
||||
c.doc.TransformTranslate(xp, yp)
|
||||
}
|
||||
|
||||
func (c *Canvas) Scale(x float64, y float64) {
|
||||
c.doc.TransformScale(x*100, y*100, 0, 0)
|
||||
}
|
||||
|
||||
func (c *Canvas) Push() {
|
||||
c.stack = append(c.stack, *c.context())
|
||||
c.doc.TransformBegin()
|
||||
}
|
||||
|
||||
func (c *Canvas) Pop() {
|
||||
c.doc.TransformEnd()
|
||||
c.stack = c.stack[:len(c.stack)-1]
|
||||
}
|
||||
|
||||
func (c *Canvas) Stroke(p vg.Path) {
|
||||
if c.context().width > 0 {
|
||||
c.pdfPath(p, "D")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Canvas) Fill(p vg.Path) {
|
||||
c.pdfPath(p, "F")
|
||||
}
|
||||
|
||||
func (c *Canvas) FillString(fnt font.Face, pt vg.Point, str string) {
|
||||
if fnt.Font.Size == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
c.font(fnt, pt)
|
||||
style := ""
|
||||
if fnt.Font.Weight == stdfnt.WeightBold {
|
||||
style += "B"
|
||||
}
|
||||
if fnt.Font.Style == stdfnt.StyleItalic {
|
||||
style += "I"
|
||||
}
|
||||
c.doc.SetFont(fnt.Name(), style, c.unit(fnt.Font.Size))
|
||||
|
||||
c.Push()
|
||||
defer c.Pop()
|
||||
c.Translate(pt)
|
||||
// go-fpdf uses the top left corner as origin.
|
||||
c.Scale(1, -1)
|
||||
left, top, right, bottom := c.sbounds(fnt, str)
|
||||
w := right - left
|
||||
h := bottom - top
|
||||
margin := c.doc.GetCellMargin()
|
||||
|
||||
c.doc.MoveTo(-left-margin, top)
|
||||
c.doc.CellFormat(w, h, str, "", 0, "BL", false, 0, "")
|
||||
}
|
||||
|
||||
func (c *Canvas) sbounds(fnt font.Face, txt string) (left, top, right, bottom float64) {
|
||||
_, h := c.doc.GetFontSize()
|
||||
style := ""
|
||||
if fnt.Font.Weight == stdfnt.WeightBold {
|
||||
style += "B"
|
||||
}
|
||||
if fnt.Font.Style == stdfnt.StyleItalic {
|
||||
style += "I"
|
||||
}
|
||||
d := c.doc.GetFontDesc(fnt.Name(), style)
|
||||
if d.Ascent == 0 {
|
||||
// not defined (standard font?), use average of 81%
|
||||
top = 0.81 * h
|
||||
} else {
|
||||
top = -float64(d.Ascent) * h / float64(d.Ascent-d.Descent)
|
||||
}
|
||||
return 0, top, c.doc.GetStringWidth(txt), top + h
|
||||
}
|
||||
|
||||
// DrawImage implements the vg.Canvas.DrawImage method.
|
||||
func (c *Canvas) DrawImage(rect vg.Rectangle, img image.Image) {
|
||||
opts := pdf.ImageOptions{ImageType: "png", ReadDpi: true}
|
||||
name := c.imageName()
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
err := png.Encode(buf, img)
|
||||
if err != nil {
|
||||
log.Panicf("error encoding image to PNG: %v", err)
|
||||
}
|
||||
c.doc.RegisterImageOptionsReader(name, opts, buf)
|
||||
|
||||
xp, yp := c.pdfPoint(rect.Min)
|
||||
wp, hp := c.pdfPoint(rect.Size())
|
||||
|
||||
c.doc.ImageOptions(name, xp, yp, wp, hp, false, opts, 0, "")
|
||||
}
|
||||
|
||||
// font registers a font and a size with the PDF canvas.
|
||||
func (c *Canvas) font(fnt font.Face, pt vg.Point) {
|
||||
if _, ok := c.fonts[fnt.Font]; ok {
|
||||
return
|
||||
}
|
||||
name := fnt.Name()
|
||||
key := fontKey{font: fnt, embed: c.embed}
|
||||
raw := new(bytes.Buffer)
|
||||
_, err := fnt.Face.WriteSourceTo(nil, raw)
|
||||
if err != nil {
|
||||
log.Panicf("vgpdf: could not generate font %q data for PDF: %+v", name, err)
|
||||
}
|
||||
|
||||
zdata, jdata, err := getFont(key, raw.Bytes(), codePageEncoding)
|
||||
if err != nil {
|
||||
log.Panicf("vgpdf: could not generate font data for PDF: %v", err)
|
||||
}
|
||||
|
||||
c.fonts[fnt.Font] = struct{}{}
|
||||
c.doc.AddFontFromBytes(name, "", jdata, zdata)
|
||||
}
|
||||
|
||||
// pdfPath processes a vg.Path and applies it to the canvas.
|
||||
func (c *Canvas) pdfPath(path vg.Path, style string) {
|
||||
var (
|
||||
xp float64
|
||||
yp float64
|
||||
)
|
||||
for _, comp := range path {
|
||||
switch comp.Type {
|
||||
case vg.MoveComp:
|
||||
xp, yp = c.pdfPoint(comp.Pos)
|
||||
c.doc.MoveTo(xp, yp)
|
||||
case vg.LineComp:
|
||||
c.doc.LineTo(c.pdfPoint(comp.Pos))
|
||||
case vg.ArcComp:
|
||||
c.arc(comp, style)
|
||||
case vg.CurveComp:
|
||||
px, py := c.pdfPoint(comp.Pos)
|
||||
switch len(comp.Control) {
|
||||
case 1:
|
||||
cx, cy := c.pdfPoint(comp.Control[0])
|
||||
c.doc.CurveTo(cx, cy, px, py)
|
||||
case 2:
|
||||
cx, cy := c.pdfPoint(comp.Control[0])
|
||||
dx, dy := c.pdfPoint(comp.Control[1])
|
||||
c.doc.CurveBezierCubicTo(cx, cy, dx, dy, px, py)
|
||||
default:
|
||||
panic("vgpdf: invalid number of control points")
|
||||
}
|
||||
case vg.CloseComp:
|
||||
c.doc.LineTo(xp, yp)
|
||||
c.doc.ClosePath()
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown path component type: %d\n", comp.Type))
|
||||
}
|
||||
}
|
||||
c.doc.DrawPath(style)
|
||||
}
|
||||
|
||||
func (c *Canvas) arc(comp vg.PathComp, style string) {
|
||||
x0 := comp.Pos.X + comp.Radius*vg.Length(math.Cos(comp.Start))
|
||||
y0 := comp.Pos.Y + comp.Radius*vg.Length(math.Sin(comp.Start))
|
||||
c.doc.LineTo(c.pdfPointXY(x0, y0))
|
||||
r := c.unit(comp.Radius)
|
||||
const deg = 180 / math.Pi
|
||||
angle := comp.Angle * deg
|
||||
beg := comp.Start * deg
|
||||
end := beg + angle
|
||||
x := c.unit(comp.Pos.X)
|
||||
y := c.unit(comp.Pos.Y)
|
||||
c.doc.Arc(x, y, r, r, angle, beg, end, style)
|
||||
x1 := comp.Pos.X + comp.Radius*vg.Length(math.Cos(comp.Start+comp.Angle))
|
||||
y1 := comp.Pos.Y + comp.Radius*vg.Length(math.Sin(comp.Start+comp.Angle))
|
||||
c.doc.MoveTo(c.pdfPointXY(x1, y1))
|
||||
}
|
||||
|
||||
func (c *Canvas) pdfPointXY(x, y vg.Length) (float64, float64) {
|
||||
return c.unit(x), c.unit(y)
|
||||
}
|
||||
|
||||
func (c *Canvas) pdfPoint(pt vg.Point) (float64, float64) {
|
||||
return c.unit(pt.X), c.unit(pt.Y)
|
||||
}
|
||||
|
||||
// unit returns a fpdf.Unit, converted from a vg.Length.
|
||||
func (c *Canvas) unit(l vg.Length) float64 {
|
||||
return l.Dots(c.DPI())
|
||||
}
|
||||
|
||||
// imageName generates a unique image name for this PDF canvas
|
||||
func (c *Canvas) imageName() string {
|
||||
c.numImages++
|
||||
return fmt.Sprintf("image_%03d.png", c.numImages)
|
||||
}
|
||||
|
||||
// WriterCounter implements the io.Writer interface, and counts
|
||||
// the total number of bytes written.
|
||||
type writerCounter struct {
|
||||
io.Writer
|
||||
n int64
|
||||
}
|
||||
|
||||
func (w *writerCounter) Write(p []byte) (int, error) {
|
||||
n, err := w.Writer.Write(p)
|
||||
w.n += int64(n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
// WriteTo writes the Canvas to an io.Writer.
|
||||
// After calling Write, the canvas is closed
|
||||
// and may no longer be used for drawing.
|
||||
func (c *Canvas) WriteTo(w io.Writer) (int64, error) {
|
||||
c.Pop()
|
||||
c.doc.Close()
|
||||
wc := writerCounter{Writer: w}
|
||||
b := bufio.NewWriter(&wc)
|
||||
if err := c.doc.Output(b); err != nil {
|
||||
return wc.n, err
|
||||
}
|
||||
err := b.Flush()
|
||||
return wc.n, err
|
||||
}
|
||||
|
||||
// rgba converts a Go color into a gofpdf 3-tuple int + 1 float64
|
||||
func rgba(c color.Color) (int, int, int, float64) {
|
||||
if c == nil {
|
||||
c = color.Black
|
||||
}
|
||||
r, g, b, a := c.RGBA()
|
||||
return int(r >> 8), int(g >> 8), int(b >> 8), float64(a) / math.MaxUint16
|
||||
}
|
||||
|
||||
type fontsCache struct {
|
||||
sync.RWMutex
|
||||
cache map[fontKey]fontVal
|
||||
}
|
||||
|
||||
// fontKey represents a PDF font request.
|
||||
// fontKey needs to know whether the font will be embedded or not,
|
||||
// as gofpdf.MakeFont will generate different informations.
|
||||
type fontKey struct {
|
||||
font font.Face
|
||||
embed bool
|
||||
}
|
||||
|
||||
type fontVal struct {
|
||||
z, j []byte
|
||||
}
|
||||
|
||||
func (c *fontsCache) get(key fontKey) (fontVal, bool) {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
v, ok := c.cache[key]
|
||||
return v, ok
|
||||
}
|
||||
|
||||
func (c *fontsCache) add(k fontKey, v fontVal) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
c.cache[k] = v
|
||||
}
|
||||
|
||||
var pdfFonts = &fontsCache{
|
||||
cache: make(map[fontKey]fontVal),
|
||||
}
|
||||
|
||||
func getFont(key fontKey, font, encoding []byte) (z, j []byte, err error) {
|
||||
if v, ok := pdfFonts.get(key); ok {
|
||||
return v.z, v.j, nil
|
||||
}
|
||||
|
||||
v, err := makeFont(key, font, encoding)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return v.z, v.j, nil
|
||||
}
|
||||
|
||||
func makeFont(key fontKey, font, encoding []byte) (val fontVal, err error) {
|
||||
tmpdir, err := os.MkdirTemp("", "gofpdf-makefont-")
|
||||
if err != nil {
|
||||
return val, err
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
indir := filepath.Join(tmpdir, "input")
|
||||
err = os.Mkdir(indir, 0755)
|
||||
if err != nil {
|
||||
return val, err
|
||||
}
|
||||
|
||||
outdir := filepath.Join(tmpdir, "output")
|
||||
err = os.Mkdir(outdir, 0755)
|
||||
if err != nil {
|
||||
return val, err
|
||||
}
|
||||
|
||||
fname := filepath.Join(indir, "font.ttf")
|
||||
encname := filepath.Join(indir, "cp1252.map")
|
||||
|
||||
err = os.WriteFile(fname, font, 0644)
|
||||
if err != nil {
|
||||
return val, err
|
||||
}
|
||||
|
||||
err = os.WriteFile(encname, encoding, 0644)
|
||||
if err != nil {
|
||||
return val, err
|
||||
}
|
||||
|
||||
err = pdf.MakeFont(fname, encname, outdir, io.Discard, key.embed)
|
||||
if err != nil {
|
||||
return val, err
|
||||
}
|
||||
|
||||
if key.embed {
|
||||
z, err := os.ReadFile(filepath.Join(outdir, "font.z"))
|
||||
if err != nil {
|
||||
return val, err
|
||||
}
|
||||
val.z = z
|
||||
}
|
||||
|
||||
j, err := os.ReadFile(filepath.Join(outdir, "font.json"))
|
||||
if err != nil {
|
||||
return val, err
|
||||
}
|
||||
val.j = j
|
||||
|
||||
pdfFonts.add(key, val)
|
||||
|
||||
return val, nil
|
||||
}
|
||||
|
||||
// NextPage creates a new page in the final PDF document.
|
||||
// The new page is the new current page.
|
||||
// Modifications applied to the canvas will only be applied to that new page.
|
||||
func (c *Canvas) NextPage() {
|
||||
if c.doc.PageNo() > 0 {
|
||||
c.Pop()
|
||||
}
|
||||
c.doc.SetMargins(0, 0, 0)
|
||||
c.doc.AddPage()
|
||||
c.Push()
|
||||
c.Translate(vg.Point{X: 0, Y: c.h})
|
||||
c.Scale(1, -1)
|
||||
}
|
||||
Reference in New Issue
Block a user