summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--def.go13
-rw-r--r--doc.go3
-rw-r--r--fpdf.go104
-rw-r--r--fpdf_test.go43
-rw-r--r--util.go13
6 files changed, 159 insertions, 18 deletions
diff --git a/.gitignore b/.gitignore
index cde0305..78d0dfb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@ pdf.txt
*.sublime*
font/Ubuntu-*
*.0
+*.swp
diff --git a/def.go b/def.go
index 47b399f..33eca47 100644
--- a/def.go
+++ b/def.go
@@ -18,6 +18,7 @@ package gofpdf
import (
"bytes"
+ "io"
)
// Version of FPDF from which this package is derived
@@ -132,6 +133,15 @@ type InitType struct {
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
+// does never include a path. Open returns an error if the specified file cannot be opened.
+type FontLoader interface {
+ Open(name string) (io.Reader, error)
+}
+
// Fpdf is the principal structure for creating a single PDF document
type Fpdf struct {
page int // current page number
@@ -160,6 +170,7 @@ type Fpdf struct {
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
@@ -195,6 +206,8 @@ type Fpdf struct {
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
gradientList []gradientType // slice[idx] of gradient records
diff --git a/doc.go b/doc.go
index 55ff390..8e3c0e1 100644
--- a/doc.go
+++ b/doc.go
@@ -71,7 +71,8 @@ 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.
-Bruno Michel has provided valuable assistance with the code.
+Claudio Felber provided implementations for dashed line drawing and generalized
+font loading. Bruno Michel has provided valuable assistance with the code.
The FPDF website is http://www.fpdf.org/.
diff --git a/fpdf.go b/fpdf.go
index 8361d3c..a650d6d 100644
--- a/fpdf.go
+++ b/fpdf.go
@@ -36,6 +36,7 @@ import (
"math"
"os"
"path"
+ "strconv"
"strings"
"time"
)
@@ -312,6 +313,17 @@ func (f *Fpdf) SetFontLocation(fontDirStr string) {
f.fontpath = fontDirStr
}
+// SetFontLoader sets a loader used to read font files (.json and .z) from an
+// arbitrary source. If a font loader has been specified, it is used to load
+// the named font resources when AddFont() is called. If this operation fails,
+// an attempt is made to load the resources from the configured font directory
+// (see SetFontLocation()).
+//
+// See tutorial 29 for an example of this method.
+func (f *Fpdf) SetFontLoader(loader FontLoader) {
+ f.fontLoader = loader
+}
+
// SetHeaderFunc sets the function that lets the application render the page
// header. The specified function is automatically called by AddPage() and
// should not be called directly by the application. The implementation in Fpdf
@@ -565,6 +577,10 @@ func (f *Fpdf) AddPageFormat(orientationStr string, size SizeType) {
// Set line width
f.lineWidth = lw
f.outf("%.2f w", lw*f.k)
+ // Set dash pattern
+ if len(f.dashArray) > 0 {
+ f.outputDashPattern()
+ }
// Set font
if familyStr != "" {
f.SetFont(familyStr, style, fontsize)
@@ -775,6 +791,44 @@ func (f *Fpdf) SetLineCapStyle(styleStr string) {
}
}
+// SetDashPattern sets the dash pattern that is used to draw lines. The
+// dashArray elements are numbers that specify the lengths, in units
+// established in New(), of alternating dashes and gaps. The dash phase
+// specifies the distance into the dash pattern at which to start the dash. The
+// dash pattern is retained from page to page. Call this method with an empty
+// array to restore solid line drawing.
+//
+// See tutorial 28 for an example of this function.
+func (f *Fpdf) SetDashPattern(dashArray []float64, dashPhase float64) {
+ scaled := make([]float64, len(dashArray))
+ for i, value := range dashArray {
+ scaled[i] = value * f.k
+ }
+ dashPhase *= f.k
+ if !slicesEqual(scaled, f.dashArray) || dashPhase != f.dashPhase {
+ f.dashArray = scaled
+ f.dashPhase = dashPhase
+ if f.page > 0 {
+ f.outputDashPattern()
+ }
+ }
+}
+
+func (f *Fpdf) outputDashPattern() {
+ var buf bytes.Buffer
+ buf.WriteByte('[')
+ for i, value := range f.dashArray {
+ if i > 0 {
+ buf.WriteByte(' ')
+ }
+ buf.WriteString(strconv.FormatFloat(value, 'f', 2, 64))
+ }
+ buf.WriteString("] ")
+ buf.WriteString(strconv.FormatFloat(f.dashPhase, 'f', 2, 64))
+ buf.WriteString(" d")
+ f.outbuf(&buf)
+}
+
// Line draws a line between points (x1, y1) and (x2, y2) using the current
// draw color, line width and cap style.
func (f *Fpdf) Line(x1, y1, x2, y2 float64) {
@@ -1319,8 +1373,19 @@ func (f *Fpdf) AddFont(familyStr, styleStr, fileStr string) {
if fileStr == "" {
fileStr = strings.Replace(familyStr, " ", "", -1) + strings.ToLower(styleStr) + ".json"
}
- fileStr = path.Join(f.fontpath, fileStr)
+ if f.fontLoader != nil {
+ reader, err := f.fontLoader.Open(fileStr)
+ if err == nil {
+ f.AddFontFromReader(familyStr, styleStr, reader)
+ if closer, ok := reader.(io.Closer); ok {
+ closer.Close()
+ }
+ return
+ }
+ }
+
+ fileStr = path.Join(f.fontpath, fileStr)
file, err := os.Open(fileStr)
if err != nil {
f.err = err
@@ -2932,7 +2997,7 @@ func (f *Fpdf) putfonts() {
f.newobj()
info.n = f.n
f.fontFiles[file] = info
- font, err := ioutil.ReadFile(path.Join(f.fontpath, file))
+ font, err := f.loadFontFile(file)
if err != nil {
f.err = err
return
@@ -3032,6 +3097,20 @@ func (f *Fpdf) putfonts() {
return
}
+func (f *Fpdf) loadFontFile(name string) ([]byte, error) {
+ if f.fontLoader != nil {
+ reader, err := f.fontLoader.Open(name)
+ if err == nil {
+ data, err := ioutil.ReadAll(reader)
+ if closer, ok := reader.(io.Closer); ok {
+ closer.Close()
+ }
+ return data, err
+ }
+ }
+ return ioutil.ReadFile(path.Join(f.fontpath, name))
+}
+
func (f *Fpdf) putimages() {
for _, img := range f.images {
f.putimage(img)
@@ -3385,16 +3464,17 @@ func (f *Fpdf) enddoc() {
}
// Path Drawing
+//
// Create a "path" by moving a virtual stylus around the page, then draw it or
// fill it in. The main advantage of using the path drawing routines rather
// than multiple Fpdf.Line is that PDF creates nice line joins at the angles,
// rather than just overlaying the lines.
+
+// MoveTo moves the stylus to (x, y) without drawing the path from the previous
+// point. Paths must start with a MoveTo to set the original stylus location or
+// the result is undefined.
//
-// MoveTo moves the stylus to (x, y) without drawing the path from the
-// previous point. Paths must start with a MoveTo to set the original stylus
-// location or the result is undefined.
-//
-// See tutorial 29 for an example of this function.
+// See tutorial 30 for an example of this function.
func (f *Fpdf) MoveTo(x, y float64) {
f.point(x, y) // rename?
}
@@ -3403,7 +3483,7 @@ func (f *Fpdf) MoveTo(x, y float64) {
// becomes the new stylus location. Note that this only creates the line in
// the path; it does not actually draw the line on the page.
//
-// See tutorial 29 for an example of this function.
+// See tutorial 30 for an example of this function.
func (f *Fpdf) LineTo(x, y float64) {
f.outf("%.2f %.2f l", x*f.k, (f.h-y)*f.k)
}
@@ -3415,7 +3495,7 @@ func (f *Fpdf) LineTo(x, y float64) {
// point. At the end point, the curve is tangent to the straight line between
// the end point and the control point.
//
-// See tutorial 29 for an example of this function.
+// See tutorial 30 for an example of this function.
func (f *Fpdf) CurveTo(cx, cy, x, y float64) {
f.outf("%.5f %.5f %.5f %.5f v", cx*f.k, (f.h-cy)*f.k, x*f.k, (f.h-y)*f.k)
}
@@ -3428,7 +3508,7 @@ func (f *Fpdf) CurveTo(cx, cy, x, y float64) {
// the curve is tangent to the straight line between the end point and the
// control point (cx1, cy1).
//
-// See tutorial 29 for examples of this function.
+// See tutorial 30 for examples of this function.
func (f *Fpdf) CurveBezierCubicTo(cx0, cy0, cx1, cy1, x, y float64) {
f.curve(cx0, cy0, cx1, cy1, x, y) // rename?
}
@@ -3437,7 +3517,7 @@ func (f *Fpdf) CurveBezierCubicTo(cx0, cy0, cx1, cy1, x, y float64) {
// (if not the same) and mark the path as closed so the first and last lines
// join nicely.
//
-// See tutorial 29 for an example of this function.
+// See tutorial 30 for an example of this function.
func (f *Fpdf) ClosePath() {
f.outf("h")
}
@@ -3449,7 +3529,7 @@ func (f *Fpdf) ClosePath() {
// the current draw color, line width, and cap style centered on the
// path. Filling uses the current fill color.
//
-// See tutorial 29 for an example of this function.
+// See tutorial 30 for an example of this function.
func (f *Fpdf) DrawPath(styleStr string) {
f.outf(fillDrawOp(styleStr))
}
diff --git a/fpdf_test.go b/fpdf_test.go
index a99fd2d..8959ece 100644
--- a/fpdf_test.go
+++ b/fpdf_test.go
@@ -18,8 +18,10 @@ package gofpdf_test
import (
"bufio"
+ "bytes"
"fmt"
"github.com/jung-kurt/gofpdf"
+ "io"
"io/ioutil"
"math"
"net/http"
@@ -101,6 +103,19 @@ func textFile(fileStr string) string {
return filepath.Join(cnTextDir, fileStr)
}
+type fontResourceType struct {
+}
+
+func (f fontResourceType) Open(name string) (rdr io.Reader, err error) {
+ var buf []byte
+ buf, err = ioutil.ReadFile(fontFile(name))
+ if err == nil {
+ rdr = bytes.NewReader(buf)
+ fmt.Printf("Generalized font loader reading %s\n", name)
+ }
+ return
+}
+
// Convert 'ABCDEFG' to, for example, 'A,BCD,EFG'
func strDelimit(str string, sepstr string, sepcount int) string {
pos := len(str) - sepcount
@@ -1415,8 +1430,10 @@ func ExampleFpdf_tutorial28() {
srcPrev = src
}
pdf.MultiCell(wd-margin-margin, ln, msgStr, "", "C", false)
- pdf.SetDrawColor(224, 224, 224)
+ pdf.SetDashPattern([]float64{0.8, 0.8}, 0)
+ pdf.SetDrawColor(160, 160, 160)
pdf.Polygon(srcList, "D")
+ pdf.SetDashPattern([]float64{}, 0)
pdf.SetDrawColor(64, 64, 128)
pdf.SetLineWidth(pdf.GetLineWidth() * 3)
pdf.Beziergon(curveList, "D")
@@ -1426,9 +1443,25 @@ func ExampleFpdf_tutorial28() {
}
-// This example demonstrates the Path Drawing functions, such as:
-// MoveTo, LineTo, CurveTo, ..., ClosePath and DrawPath.
+// Non-standard font using generalized font loader
func ExampleFpdf_tutorial29() {
+ var fr fontResourceType
+ pdf := gofpdf.New("P", "mm", "A4", cnFontDir)
+ pdf.SetFontLoader(fr)
+ pdf.AddFont("Calligrapher", "", "calligra.json")
+ pdf.AddPage()
+ pdf.SetFont("Calligrapher", "", 35)
+ pdf.Cell(0, 10, "Load fonts from any source")
+ pdf.OutputAndClose(docWriter(pdf, 29))
+ // Output:
+ // Generalized font loader reading calligra.json
+ // Generalized font loader reading calligra.z
+ // Successfully generated pdf/tutorial29.pdf
+}
+
+// This example demonstrates the Path Drawing functions, such as: MoveTo,
+// LineTo, CurveTo, ..., ClosePath and DrawPath.
+func ExampleFpdf_tutorial30() {
pdf := gofpdf.New("P", "mm", "A4", "")
pdf.AddPage()
pdf.MoveTo(20, 20)
@@ -1439,7 +1472,7 @@ func ExampleFpdf_tutorial29() {
pdf.SetFillColor(200, 200, 200)
pdf.SetLineWidth(3)
pdf.DrawPath("DF")
- pdf.OutputAndClose(docWriter(pdf, 29))
+ pdf.OutputAndClose(docWriter(pdf, 30))
// Output:
- // Successfully generated pdf/tutorial29.pdf
+ // Successfully generated pdf/tutorial30.pdf
}
diff --git a/util.go b/util.go
index d3b114f..ed4ffdd 100644
--- a/util.go
+++ b/util.go
@@ -69,6 +69,19 @@ func bufferFromReader(r io.Reader) (b *bytes.Buffer, err error) {
return
}
+// Returns true if the two specified integer 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
+}
+
// Returns a zlib-compressed copy of the specified byte array
func sliceCompress(data []byte) []byte {
var buf bytes.Buffer