summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--def.go22
-rw-r--r--fpdf.go216
-rw-r--r--fpdf_test.go45
-rw-r--r--ttfparser.go1
4 files changed, 168 insertions, 116 deletions
diff --git a/def.go b/def.go
index 7272f73..49129c7 100644
--- a/def.go
+++ b/def.go
@@ -36,12 +36,15 @@ type gradientType struct {
objNum int
}
-type sizeType struct {
- wd, ht float64
+// Wd and Ht specify the horizontal and vertical extents of a document page.
+type SizeType struct {
+ Wd, Ht float64
}
-type pointType struct {
- x, y float64
+// X and Y specify the horizontal and vertical coordinates of a point,
+// typically used in drawing.
+type PointType struct {
+ X, Y float64
}
type imageInfoType struct {
@@ -86,10 +89,11 @@ type Fpdf struct {
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
- curPageSize sizeType // current page size
- pageSizes map[int]sizeType // used for pages with non default sizes or orientations
+ stdPageSizes map[string]SizeType // standard page sizes
+ defPageSize SizeType // default page size
+ curPageSize SizeType // current page size
+ pageSizes map[int]SizeType // used for pages with non default sizes or orientations
+ 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
@@ -141,7 +145,7 @@ type Fpdf struct {
blendList []blendModeType // slice[idx] of alpha transparency modes, 1-based
blendMap map[string]int // map into blendList
gradientList []gradientType // slice[idx] of gradient records
- clipActive bool // clippping operation is underway
+ clipNest int // Number of active clipping contexts
err error // Set if error occurs during life cycle of instance
}
diff --git a/fpdf.go b/fpdf.go
index b8422e2..d8718a3 100644
--- a/fpdf.go
+++ b/fpdf.go
@@ -45,24 +45,7 @@ func (b *fmtBuffer) printf(fmtStr string, args ...interface{}) {
b.Buffer.WriteString(fmt.Sprintf(fmtStr, args...))
}
-// New returns a pointer to a new Fpdf instance. Its methods are subsequently
-// called to produce a single PDF document.
-//
-// orientationStr specifies the default page orientation. For portrait mode,
-// specify "P" or "Portrait". For landscape mode, specify "L" or "Landscape".
-// An empty string will be replaced with "P".
-//
-// unitStr specifies the unit of length used in size parameters for elements
-// other than fonts, which are always measured in points. Specify "pt" for
-// point, "mm" for millimeter, "cm" for centimeter, or "in" for inch. An empty
-// string will be replaced with "mm".
-//
-// sizeStr specifies the page size. Acceptable values are "A3", "A4", "A5",
-// "Letter", or "Legal". An empty string will be replaced with "A4".
-//
-// fontDirStr specifies the file system location in which font resources will
-// be found. An empty string is replaced with ".".
-func New(orientationStr, unitStr, sizeStr, fontDirStr string) (f *Fpdf) {
+func fpdfNew(orientationStr, unitStr, sizeStr, fontDirStr string, size SizeType) (f *Fpdf) {
f = new(Fpdf)
if orientationStr == "" {
orientationStr = "P"
@@ -80,7 +63,7 @@ func New(orientationStr, unitStr, sizeStr, fontDirStr string) (f *Fpdf) {
f.n = 2
f.pages = make([]*bytes.Buffer, 0, 8)
f.pages = append(f.pages, bytes.NewBufferString("")) // pages[0] is unused (1-based)
- f.pageSizes = make(map[int]sizeType)
+ f.pageSizes = make(map[int]SizeType)
f.state = 0
f.fonts = make(map[string]fontDefType)
f.fontFiles = make(map[string]fontFileType)
@@ -125,16 +108,21 @@ func New(orientationStr, unitStr, sizeStr, fontDirStr string) (f *Fpdf) {
f.err = fmt.Errorf("Incorrect unit %s", unitStr)
return
}
+ f.unitStr = unitStr
// Page sizes
- f.stdpageSizes = make(map[string]sizeType)
- f.stdpageSizes["a3"] = sizeType{841.89, 1190.55}
- f.stdpageSizes["a4"] = sizeType{595.28, 841.89}
- f.stdpageSizes["a5"] = sizeType{420.94, 595.28}
- f.stdpageSizes["letter"] = sizeType{612, 792}
- f.stdpageSizes["legal"] = sizeType{612, 1008}
- f.defPageSize = f.getpagesizestr(sizeStr)
- if f.err != nil {
- return
+ f.stdPageSizes = make(map[string]SizeType)
+ f.stdPageSizes["a3"] = SizeType{841.89, 1190.55}
+ f.stdPageSizes["a4"] = SizeType{595.28, 841.89}
+ f.stdPageSizes["a5"] = SizeType{420.94, 595.28}
+ f.stdPageSizes["letter"] = SizeType{612, 792}
+ f.stdPageSizes["legal"] = SizeType{612, 1008}
+ if size.Wd > 0 && size.Ht > 0 {
+ f.defPageSize = size
+ } else {
+ f.defPageSize = f.getpagesizestr(sizeStr)
+ if f.err != nil {
+ return
+ }
}
f.curPageSize = f.defPageSize
// Page orientation
@@ -142,13 +130,13 @@ func New(orientationStr, unitStr, sizeStr, fontDirStr string) (f *Fpdf) {
switch orientationStr {
case "p", "portrait":
f.defOrientation = "P"
- f.w = f.defPageSize.wd
- f.h = f.defPageSize.ht
+ f.w = f.defPageSize.Wd
+ f.h = f.defPageSize.Ht
// dbg("Assign h: %8.2f", f.h)
case "l", "landscape":
f.defOrientation = "L"
- f.w = f.defPageSize.ht
- f.h = f.defPageSize.wd
+ f.w = f.defPageSize.Ht
+ f.h = f.defPageSize.Wd
default:
f.err = fmt.Errorf("Incorrect orientation: %s", orientationStr)
return
@@ -185,6 +173,46 @@ func New(orientationStr, unitStr, sizeStr, fontDirStr string) (f *Fpdf) {
return
}
+// 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.
+type InitType struct {
+ OrientationStr string
+ UnitStr string
+ SizeStr string
+ Size SizeType
+ FontDirStr string
+}
+
+// NewCustom returns a pointer to a new Fpdf instance. Its methods are
+// subsequently called to produce a single PDF document. NewCustom() is an
+// alternative to New() that provides additional customization.
+func NewCustom(init *InitType) (f *Fpdf) {
+ return fpdfNew(init.OrientationStr, init.UnitStr, init.SizeStr, init.FontDirStr, init.Size)
+}
+
+// New returns a pointer to a new Fpdf instance. Its methods are subsequently
+// called to produce a single PDF document.
+//
+// orientationStr specifies the default page orientation. For portrait mode,
+// specify "P" or "Portrait". For landscape mode, specify "L" or "Landscape".
+// An empty string will be replaced with "P".
+//
+// unitStr specifies the unit of length used in size parameters for elements
+// other than fonts, which are always measured in points. Specify "pt" for
+// point, "mm" for millimeter, "cm" for centimeter, or "in" for inch. An empty
+// string will be replaced with "mm".
+//
+// sizeStr specifies the page size. Acceptable values are "A3", "A4", "A5",
+// "Letter", or "Legal". An empty string will be replaced with "A4".
+//
+// fontDirStr specifies the file system location in which font resources will
+// be found. An empty string is replaced with ".".
+func New(orientationStr, unitStr, sizeStr, fontDirStr string) (f *Fpdf) {
+ return fpdfNew(orientationStr, unitStr, sizeStr, fontDirStr, SizeType{0, 0})
+}
+
// Returns true if no processing errors have occurred.
func (f *Fpdf) Ok() bool {
return f.err == nil
@@ -407,7 +435,7 @@ func (f *Fpdf) open() {
// an invalid document.
func (f *Fpdf) Close() {
if f.err == nil {
- if f.clipActive {
+ if f.clipNest > 0 {
f.err = fmt.Errorf("Clip procedure must be explicitly ended")
}
}
@@ -436,13 +464,27 @@ func (f *Fpdf) Close() {
return
}
+// Returns the width and height of the specified page in the units established
+// in New(). These return values are followed by the unit of measure itself. If
+// pageNum is zero or otherwise out of bounds, it returns the default page
+// size, that is, the size of the page that would be added by AddPage().
+func (f *Fpdf) PageSize(pageNum int) (wd, ht float64, unitStr string) {
+ sz, ok := f.pageSizes[pageNum]
+ if ok {
+ sz.Wd, sz.Ht = sz.Wd/f.k, sz.Ht/f.k
+ } else {
+ sz = f.defPageSize // user units
+ }
+ return sz.Wd, sz.Ht, f.unitStr
+}
+
// Adds a new page with non-default orientation or size. See AddPage() for more
// details.
//
// See New() for a description of orientationStr.
//
// size specifies the size of the new page in the units established in New().
-func (f *Fpdf) AddPageFormat(orientationStr string, size sizeType) {
+func (f *Fpdf) AddPageFormat(orientationStr string, size SizeType) {
if f.err != nil {
return
}
@@ -948,17 +990,6 @@ func (f *Fpdf) RadialGradient(x, y, w, h float64, r1, g1, b1 int, r2, g2, b2 int
f.gradientClipEnd()
}
-func (f *Fpdf) setClipActive() bool {
- if f.err == nil {
- if f.clipActive {
- f.err = fmt.Errorf("Clipping operation already active")
- } else {
- f.clipActive = true
- }
- }
- return f.err == nil
-}
-
// Begins a rectangular clipping operation. The rectangle is of width w and
// height h. Its upper left corner is positioned at point (x, y). outline is
// true to draw a border with the current draw color and line width centered on
@@ -969,9 +1000,7 @@ func (f *Fpdf) setClipActive() bool {
//
// See tutorial 14 for an example of this function.
func (f *Fpdf) ClipRect(x, y, w, h float64, outline bool) {
- if !f.setClipActive() {
- return
- }
+ f.clipNest++
f.outf("q %.2f %.2f %.2f %.2f re W %s", x*f.k, (f.h-y)*f.k, w*f.k, -h*f.k, strIf(outline, "S", "n"))
}
@@ -986,9 +1015,7 @@ func (f *Fpdf) ClipRect(x, y, w, h float64, outline bool) {
//
// See tutorial 14 for an example of this function.
func (f *Fpdf) ClipText(x, y float64, txtStr string, outline bool) {
- if !f.setClipActive() {
- return
- }
+ f.clipNest++
f.outf("q BT %.2f %.2f Td %d Tr (%s) Tj ET", x*f.k, (f.h-y)*f.k, intIf(outline, 5, 7), f.escape(txtStr))
}
@@ -1009,9 +1036,7 @@ func (f *Fpdf) clipArc(x1, y1, x2, y2, x3, y3 float64) {
//
// See tutorial 14 for an example of this function.
func (f *Fpdf) ClipRoundedRect(x, y, w, h, r float64, outline bool) {
- if !f.setClipActive() {
- return
- }
+ f.clipNest++
k := f.k
hp := f.h
myArc := (4.0 / 3.0) * (math.Sqrt2 - 1.0)
@@ -1045,9 +1070,7 @@ func (f *Fpdf) ClipRoundedRect(x, y, w, h, r float64, outline bool) {
//
// See tutorial 14 for an example of this function.
func (f *Fpdf) ClipEllipse(x, y, rx, ry float64, outline bool) {
- if !f.setClipActive() {
- return
- }
+ f.clipNest++
lx := (4.0 / 3.0) * rx * (math.Sqrt2 - 1)
ly := (4.0 / 3.0) * ry * (math.Sqrt2 - 1)
k := f.k
@@ -1095,16 +1118,14 @@ func (f *Fpdf) ClipCircle(x, y, r float64, outline bool) {
// ClipEnd() to restore unclipped operations.
//
// See tutorial 14 for an example of this function.
-func (f *Fpdf) ClipPolygon(points []pointType, outline bool) {
- if !f.setClipActive() {
- return
- }
+func (f *Fpdf) ClipPolygon(points []PointType, outline bool) {
+ f.clipNest++
var s fmtBuffer
h := f.h
k := f.k
s.printf("q ")
for j, pt := range points {
- s.printf("%.2f %.2f %s ", pt.x*k, (h-pt.y)*k, strIf(j == 0, "m", "l"))
+ s.printf("%.2f %.2f %s ", pt.X*k, (h-pt.Y)*k, strIf(j == 0, "m", "l"))
}
s.printf("h W %s", strIf(outline, "S", "n"))
f.out(s.String())
@@ -1112,14 +1133,14 @@ func (f *Fpdf) ClipPolygon(points []pointType, outline bool) {
// Ends a clipping operation that was started with a call to ClipRect(),
// ClipRoundedRect(), ClipText(), ClipEllipse(), ClipCircle() or ClipPolygon().
-// Only one clipping operation can be active at a time, and the document cannot
-// be successfully output while a clipping operation is active.
+// Clipping operations can be nested. The document cannot be successfully
+// output while a clipping operation is active.
//
// See tutorial 14 for an example of this function.
func (f *Fpdf) ClipEnd() {
if f.err == nil {
- if f.clipActive {
- f.clipActive = false
+ if f.clipNest > 0 {
+ f.clipNest--
f.out("Q")
} else {
f.err = fmt.Errorf("Error attempting to end clip operation")
@@ -1965,45 +1986,40 @@ func (f *Fpdf) Output(w io.Writer) error {
if err != nil {
f.err = err
}
- dump("pdf.txt", f.stdpageSizes,
+ dump("pdf.txt", f.stdPageSizes,
f.defPageSize,
f.curPageSize,
f.pageSizes)
return f.err
}
-func (f *Fpdf) getpagesizestr(sizeStr string) (size sizeType) {
+func (f *Fpdf) getpagesizestr(sizeStr string) (size SizeType) {
if f.err != nil {
return
}
sizeStr = strings.ToLower(sizeStr)
// dbg("Size [%s]", sizeStr)
- if sizeStr == "" {
- // dbg("not found %s", sizeStr)
- size = f.defPageSize
- } else {
- var ok bool
- size, ok = f.stdpageSizes[sizeStr]
- if ok {
- // dbg("found %s", sizeStr)
- size.wd /= f.k
- size.ht /= f.k
+ var ok bool
+ size, ok = f.stdPageSizes[sizeStr]
+ if ok {
+ // dbg("found %s", sizeStr)
+ size.Wd /= f.k
+ size.Ht /= f.k
- } else {
- f.err = fmt.Errorf("Unknown page size %s", sizeStr)
- }
+ } else {
+ f.err = fmt.Errorf("Unknown page size %s", sizeStr)
}
return
}
-func (f *Fpdf) _getpagesize(size sizeType) sizeType {
- if size.wd > size.ht {
- size.wd, size.ht = size.ht, size.wd
+func (f *Fpdf) _getpagesize(size SizeType) SizeType {
+ if size.Wd > size.Ht {
+ size.Wd, size.Ht = size.Ht, size.Wd
}
return size
}
-func (f *Fpdf) beginpage(orientationStr string, size sizeType) {
+func (f *Fpdf) beginpage(orientationStr string, size SizeType) {
if f.err != nil {
return
}
@@ -2020,14 +2036,14 @@ func (f *Fpdf) beginpage(orientationStr string, size sizeType) {
} else {
orientationStr = strings.ToUpper(orientationStr[0:1])
}
- if orientationStr != f.curOrientation || size.wd != f.curPageSize.wd || size.ht != f.curPageSize.ht {
+ if orientationStr != f.curOrientation || size.Wd != f.curPageSize.Wd || size.Ht != f.curPageSize.Ht {
// New size or orientation
if orientationStr == "P" {
- f.w = size.wd
- f.h = size.ht
+ f.w = size.Wd
+ f.h = size.Ht
} else {
- f.w = size.ht
- f.h = size.wd
+ f.w = size.Ht
+ f.h = size.Wd
}
f.wPt = f.w * f.k
f.hPt = f.h * f.k
@@ -2035,8 +2051,8 @@ func (f *Fpdf) beginpage(orientationStr string, size sizeType) {
f.curOrientation = orientationStr
f.curPageSize = size
}
- if orientationStr != f.defOrientation || size.wd != f.defPageSize.wd || size.ht != f.defPageSize.ht {
- f.pageSizes[f.page] = sizeType{f.wPt, f.hPt}
+ if orientationStr != f.defOrientation || size.Wd != f.defPageSize.Wd || size.Ht != f.defPageSize.Ht {
+ f.pageSizes[f.page] = SizeType{f.wPt, f.hPt}
}
return
}
@@ -2411,7 +2427,7 @@ func (f *Fpdf) outf(fmtStr string, args ...interface{}) {
func (f *Fpdf) putpages() {
var wPt, hPt float64
- var pageSize sizeType
+ var pageSize SizeType
// var linkList []linkType
var ok bool
nb := f.page
@@ -2428,11 +2444,11 @@ func (f *Fpdf) putpages() {
}
}
if f.defOrientation == "P" {
- wPt = f.defPageSize.wd * f.k
- hPt = f.defPageSize.ht * f.k
+ wPt = f.defPageSize.Wd * f.k
+ hPt = f.defPageSize.Ht * f.k
} else {
- wPt = f.defPageSize.ht * f.k
- hPt = f.defPageSize.wd * f.k
+ wPt = f.defPageSize.Ht * f.k
+ hPt = f.defPageSize.Wd * f.k
}
for n := 1; n <= nb; n++ {
// Page
@@ -2441,7 +2457,7 @@ func (f *Fpdf) putpages() {
f.out("/Parent 1 0 R")
pageSize, ok = f.pageSizes[n]
if ok {
- f.outf("/MediaBox [0 0 %.2f %.2f]", pageSize.wd, pageSize.ht)
+ f.outf("/MediaBox [0 0 %.2f %.2f]", pageSize.Wd, pageSize.Ht)
}
f.out("/Resources 2 0 R")
// Links
@@ -2455,11 +2471,11 @@ func (f *Fpdf) putpages() {
annots.printf("/A <</S /URI /URI %s>>>>", f.textstring(pl.linkStr))
} else {
l := f.links[pl.link]
- var sz sizeType
+ var sz SizeType
var h float64
sz, ok = f.pageSizes[l.page]
if ok {
- h = sz.ht
+ h = sz.Ht
} else {
h = hPt
}
diff --git a/fpdf_test.go b/fpdf_test.go
index c27263e..9113057 100644
--- a/fpdf_test.go
+++ b/fpdf_test.go
@@ -798,11 +798,15 @@ func ExampleFpdf_tutorial14() {
pdf.ClipEnd()
y += 55
- pdf.ClipRect(10, y, 160, 20, true)
- pdf.SetFillColor(64, 128, 128)
- pdf.Circle(40, y+10, 15, "F")
- pdf.SetFillColor(128, 64, 64)
- pdf.Ellipse(90, y+10, 30, 40, 45, "F")
+ pdf.ClipRect(10, y, 105, 20, true)
+ pdf.SetFillColor(255, 255, 255)
+ pdf.Rect(10, y, 105, 20, "F")
+ pdf.ClipCircle(40, y+10, 15, false)
+ pdf.RadialGradient(25, y, 30, 30, 220, 250, 220, 40, 60, 40, 0.3, 0.85, 0.3, 0.85, 0.5)
+ pdf.ClipEnd()
+ pdf.ClipEllipse(80, y+10, 20, 15, false)
+ pdf.RadialGradient(60, y, 40, 30, 250, 220, 220, 60, 40, 40, 0.3, 0.85, 0.3, 0.85, 0.5)
+ pdf.ClipEnd()
pdf.ClipEnd()
y += 28
@@ -814,8 +818,8 @@ func ExampleFpdf_tutorial14() {
pdf.RadialGradient(50, y, 20, 20, 220, 220, 250, 40, 40, 60, 0.3, 0.7, 0.3, 0.7, 0.5)
pdf.ClipEnd()
- pdf.ClipPolygon([]pointType{{80, y + 20}, {90, y}, {100, y + 20}}, true)
- pdf.LinearGradient(80, y, 20, 20, 240, 240, 250, 80, 80, 220, 0.5, 1, 0.5, 0)
+ pdf.ClipPolygon([]PointType{{80, y + 20}, {90, y}, {100, y + 20}}, true)
+ pdf.LinearGradient(80, y, 20, 20, 250, 220, 250, 60, 40, 60, 0.5, 1, 0.5, 0.5)
pdf.ClipEnd()
y += 30
@@ -832,3 +836,30 @@ func ExampleFpdf_tutorial14() {
// Output:
// Successfully generated pdf/tutorial14.pdf
}
+
+// Page size example
+func ExampleFpdf_tutorial15() {
+ pdf := NewCustom(&InitType{UnitStr: "in", Size: SizeType{6, 6}, FontDirStr: FONT_DIR})
+ pdf.SetMargins(0.5, 1, 0.5)
+ pdf.SetFont("Times", "", 14)
+ pdf.AddPageFormat("L", SizeType{3, 12})
+ pdf.SetXY(0.5, 1.5)
+ pdf.CellFormat(11, 0.2, "12 in x 3 in", "", 0, "C", false, 0, "")
+ pdf.AddPage() // Default size established in NewCustom()
+ pdf.SetXY(0.5, 3)
+ pdf.CellFormat(5, 0.2, "6 in x 6 in", "", 0, "C", false, 0, "")
+ pdf.AddPageFormat("P", SizeType{3, 12})
+ pdf.SetXY(0.5, 6)
+ pdf.CellFormat(2, 0.2, "3 in x 12 in", "", 0, "C", false, 0, "")
+ for j := 0; j <= 3; j++ {
+ wd, ht, u := pdf.PageSize(j)
+ fmt.Printf("%d: %6.2f %s, %6.2f %s\n", j, wd, u, ht, u)
+ }
+ pdf.OutputAndClose(docWriter(pdf, 15))
+ // Output:
+ // 0: 6.00 in, 6.00 in
+ // 1: 12.00 in, 3.00 in
+ // 2: 6.00 in, 6.00 in
+ // 3: 3.00 in, 12.00 in
+ // Successfully generated pdf/tutorial15.pdf
+}
diff --git a/ttfparser.go b/ttfparser.go
index 323f144..41574e2 100644
--- a/ttfparser.go
+++ b/ttfparser.go
@@ -30,6 +30,7 @@ import (
"strings"
)
+// This structure contains metrics of a TrueType font.
type TtfType struct {
Embeddable bool
UnitsPerEm uint16