diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | def.go | 13 | ||||
-rw-r--r-- | doc.go | 3 | ||||
-rw-r--r-- | fpdf.go | 104 | ||||
-rw-r--r-- | fpdf_test.go | 43 | ||||
-rw-r--r-- | util.go | 13 |
6 files changed, 159 insertions, 18 deletions
@@ -6,3 +6,4 @@ pdf.txt *.sublime* font/Ubuntu-* *.0 +*.swp @@ -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 @@ -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/. @@ -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 } @@ -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 |