summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKurt Jung <kurt.w.jung@code.google.com>2013-08-13 18:28:16 -0400
committerKurt Jung <kurt.w.jung@code.google.com>2013-08-13 18:28:16 -0400
commitcb9b6b05fca2d3154237d94eae678b8c97a2604e (patch)
tree52d0b9e269fc51aac2fb8e12a1103db263c1a132
parent3d921b9e0ed6dd64b81b477017d1fc79d0e77e9a (diff)
Gradient support adapted from the FPDF gradients script by Andreas Würmser.
-rw-r--r--.hgignore1
-rw-r--r--def.go8
-rw-r--r--doc.go9
-rw-r--r--fpdf.go136
-rw-r--r--fpdf_test.go19
5 files changed, 161 insertions, 12 deletions
diff --git a/.hgignore b/.hgignore
index 33b5206..6e433bb 100644
--- a/.hgignore
+++ b/.hgignore
@@ -2,3 +2,4 @@ syntax: glob
makefont/makefont
pdf/*.pdf
look
+open
diff --git a/def.go b/def.go
index ca31526..146a705 100644
--- a/def.go
+++ b/def.go
@@ -29,6 +29,13 @@ type blendModeType struct {
objNum int
}
+type gradientType struct {
+ tp int // 2: linear, 3: radial
+ clr1Str, clr2Str string
+ x1, y1, x2, y2, r float64
+ objNum int
+}
+
type sizeType struct {
wd, ht float64
}
@@ -129,6 +136,7 @@ type Fpdf struct {
joinStyle int // line segment join style: miter 0, round 1, bevel 2
blendList []blendModeType // slice[idx] of alpha transparency modes, 1-based
blendMap map[string]int // map into blendList
+ gradientList []gradientType // slice[idx] of gradient records
err error // Set if error occurs during life cycle of instance
}
diff --git a/doc.go b/doc.go
index 9254f30..3e5949f 100644
--- a/doc.go
+++ b/doc.go
@@ -21,7 +21,8 @@ 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. 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
+transparency script by Martin Hall-May. Support for gradients is adapted from
+the FPDF gradients script by Andreas Würmser.
The FPDF website is http://www.fpdf.org/.
@@ -37,9 +38,9 @@ Features
• Image support (JPEG, PNG and GIF)
-• Colors and alpha channel transparency
+• Colors, gradients and alpha channel transparency
-• Links
+• Internal and external links
• TrueType, Type1 and encoding support
@@ -50,7 +51,7 @@ Features
gofpdf has no dependencies other than the Go standard library. All tests pass
on Linux, Mac and Windows platforms. Like FPDF version 1.7, from which gofpdf
is derived, this package does not yet support UTF-8 source text. gofpdf is
-released under the MIT License.
+copyrighted by Kurt Jung and is released under the MIT License.
Installation
diff --git a/fpdf.go b/fpdf.go
index d0dc530..24db0be 100644
--- a/fpdf.go
+++ b/fpdf.go
@@ -176,8 +176,10 @@ func New(orientationStr, unitStr, sizeStr, fontDirStr string) (f *Fpdf) {
// Enable compression
f.SetCompression(true)
f.blendList = make([]blendModeType, 0, 8)
- f.blendList = append(f.blendList, blendModeType{}) //blendMode[0] is unused (1-based)
+ f.blendList = append(f.blendList, blendModeType{}) // blendList[0] is unused (1-based)
f.blendMap = make(map[string]int)
+ f.gradientList = make([]gradientType, 0, 8)
+ f.gradientList = append(f.gradientList, gradientType{}) // gradientList[0] is unused
// Set default PDF version number
f.pdfVersion = "1.3"
return
@@ -553,10 +555,24 @@ type clrType struct {
r, g, b float64
}
+func colorComp(v int) float64 {
+ if v < 0 {
+ v = 0
+ } else if v > 255 {
+ v = 255
+ }
+ return float64(v) / 255.0
+}
+
+func colorValueString(r, g, b int) string {
+ clr := colorValue(r, g, b)
+ return sprintf("%.3f %.3f %.3f", clr.r, clr.g, clr.b)
+}
+
func colorValue(r, g, b int) (clr clrType) {
- clr.r = float64(r) / 255.0
- clr.g = float64(g) / 255.0
- clr.b = float64(b) / 255.0
+ clr.r = colorComp(r)
+ clr.g = colorComp(g)
+ clr.b = colorComp(b)
return
}
@@ -857,6 +873,73 @@ func (f *Fpdf) SetAlpha(alpha float64, blendModeStr string) {
f.outf("/GS%d gs", pos)
}
+func (f *Fpdf) clipStart(x, y, w, h float64) {
+ // Save current graphic state and set clipping area
+ f.outf("q %.2f %.2f %.2f %.2f re W n", x*f.k, (f.h-y)*f.k, w*f.k, -h*f.k)
+ // Set up transformation matrix for gradient
+ f.outf("%.3f 0 0 %.3f %.3f %.3f cm", w*f.k, h*f.k, x*f.k, (f.h-(y+h))*f.k)
+}
+
+func (f *Fpdf) clipEnd() {
+ // Restore previous graphic state
+ f.out("Q")
+}
+
+func (f *Fpdf) gradient(tp int, r1, g1, b1 int, r2, g2, b2 int, x1, y1 float64, x2, y2 float64, r float64) {
+ pos := len(f.gradientList)
+ f.gradientList = append(f.gradientList, gradientType{tp, colorValueString(r1, g1, b1),
+ colorValueString(r2, g2, b2), x1, y1, x2, y2, r, 0})
+ f.outf("/Sh%d sh", pos)
+}
+
+// Draws a rectangular area with a blending of one color to another. The
+// rectangle is of width w and height h. Its upper left corner is positioned at
+// point (x, y).
+//
+// Each color is specified with three component values, one each for red, green
+// and blue. The values range from 0 to 255. The first color is specified by
+// (r1, g1, b1) and the second color by (r2, g2, b2).
+//
+// The blending is controlled with a gradient vector that uses normalized
+// coordinates in which the lower left corner is position (0, 0) and the upper
+// right corner is (1, 1). The vector's origin and destination are specified by
+// the points (x1, y1) and (x2, y2). In a linear gradient, blending occurs
+// perpendicularly to the vector. The vector does not necessarily need to be
+// anchored on the rectangle edge. Color 1 is used up to the origin of the
+// vector and color 2 is used beyond the vector's end point. Between the points
+// the colors are gradually blended.
+//
+// See tutorial 13 for an example of this function.
+func (f *Fpdf) LinearGradient(x, y, w, h float64, r1, g1, b1 int, r2, g2, b2 int, x1, y1, x2, y2 float64) {
+ f.clipStart(x, y, w, h)
+ f.gradient(2, r1, g1, b1, r2, g2, b2, x1, y1, x2, y2, 0)
+ f.clipEnd()
+}
+
+// Draws a rectangular area with a blending of one color to another. The
+// rectangle is of width w and height h. Its upper left corner is positioned at
+// point (x, y).
+//
+// Each color is specified with three component values, one each for red, green
+// and blue. The values range from 0 to 255. The first color is specified by
+// (r1, g1, b1) and the second color by (r2, g2, b2).
+//
+// The blending is controlled with a point and a circle, both specified with
+// normalized coordinates in which the lower left corner of the rendered
+// rectangle is position (0, 0) and the upper right corner is (1, 1). Color 1
+// begins at the origin point specified by (x1, y1). Color 2 begins at the
+// circle specified by the center point (x2, y2) and radius r. Colors are
+// gradually blended from the origin to the circle. The origin and the circle's
+// center do not necessarily have to coincide, but the origin must be within
+// the circle to avoid rendering problems.
+//
+// See tutorial 13 for an example of this function.
+func (f *Fpdf) RadialGradient(x, y, w, h float64, r1, g1, b1 int, r2, g2, b2 int, x1, y1, x2, y2, r float64) {
+ f.clipStart(x, y, w, h)
+ f.gradient(3, r1, g1, b1, r2, g2, b2, x1, y1, x2, y2, r)
+ f.clipEnd()
+}
+
// Imports a TrueType, OpenType or Type1 font and makes it available. It is
// necessary to generate a font definition file first with the makefont
// utility. It is not necessary to call this function for the core PDF fonts
@@ -2437,12 +2520,23 @@ func (f *Fpdf) putresourcedict() {
f.out("/XObject <<")
f.putxobjectdict()
f.out(">>")
- f.out("/ExtGState <<")
count := len(f.blendList)
- for j := 1; j < count; j++ {
- f.outf("/GS%d %d 0 R", j, f.blendList[j].objNum)
+ if count > 0 {
+ f.out("/ExtGState <<")
+ for j := 1; j < count; j++ {
+ f.outf("/GS%d %d 0 R", j, f.blendList[j].objNum)
+ }
+ f.out(">>")
}
- f.out(">>")
+ count = len(f.gradientList)
+ if count > 0 {
+ f.out("/Shading <<")
+ for j := 1; j < count; j++ {
+ f.outf("/Sh%d %d 0 R", j, f.gradientList[j].objNum)
+ }
+ f.out(">>")
+ }
+
}
func (f *Fpdf) putBlendModes() {
@@ -2457,11 +2551,37 @@ func (f *Fpdf) putBlendModes() {
}
}
+func (f *Fpdf) putGradients() {
+ count := len(f.gradientList)
+ for j := 1; j < count; j++ {
+ var f1 int
+ gr := f.gradientList[j]
+ if gr.tp == 2 || gr.tp == 3 {
+ f.newobj()
+ f.outf("<</FunctionType 2 /Domain [0.0 1.0] /C0 [%s] /C1 [%s] /N 1>>", gr.clr1Str, gr.clr2Str)
+ f.out("endobj")
+ f1 = f.n
+ }
+ f.newobj()
+ f.outf("<</ShadingType %d /ColorSpace /DeviceRGB", gr.tp)
+ if gr.tp == 2 {
+ f.outf("/Coords [%.3f %.3f %.3f %.3f] /Function %d 0 R /Extend [true true]>>",
+ gr.x1, gr.y1, gr.x2, gr.y2, f1)
+ } else if gr.tp == 3 {
+ f.outf("/Coords [%.3f %.3f 0 %.3f %.3f %.3f] /Function %d 0 R /Extend [true true]>>",
+ gr.x1, gr.y1, gr.x2, gr.y2, gr.r, f1)
+ }
+ f.out("endobj")
+ f.gradientList[j].objNum = f.n
+ }
+}
+
func (f *Fpdf) putresources() {
if f.err != nil {
return
}
f.putBlendModes()
+ f.putGradients()
f.putfonts()
if f.err != nil {
return
diff --git a/fpdf_test.go b/fpdf_test.go
index c121260..08dca43 100644
--- a/fpdf_test.go
+++ b/fpdf_test.go
@@ -721,3 +721,22 @@ func ExampleFpdf_tutorial12() {
// Output:
// Successfully generated pdf/tutorial12.pdf
}
+
+// Gradients
+func ExampleFpdf_tutorial13() {
+ pdf := New("", "", "", FONT_DIR)
+ pdf.SetFont("Helvetica", "", 12)
+ pdf.AddPage()
+ pdf.LinearGradient(0, 0, 210, 100, 250, 250, 255, 220, 220, 225, 0, 0, 0, .5)
+ pdf.LinearGradient(20, 25, 75, 75, 220, 220, 250, 80, 80, 220, 0, .2, 0, .8)
+ pdf.Rect(20, 25, 75, 75, "D")
+ pdf.LinearGradient(115, 25, 75, 75, 220, 220, 250, 80, 80, 220, 0, 0, 1, 1)
+ pdf.Rect(115, 25, 75, 75, "D")
+ pdf.RadialGradient(20, 120, 75, 75, 220, 220, 250, 80, 80, 220, 0.25, 0.75, 0.25, 0.75, 1)
+ pdf.Rect(20, 120, 75, 75, "D")
+ pdf.RadialGradient(115, 120, 75, 75, 220, 220, 250, 80, 80, 220, 0.25, 0.75, 0.75, 0.75, 0.75)
+ pdf.Rect(115, 120, 75, 75, "D")
+ pdf.OutputAndClose(docWriter(pdf, 13))
+ // Output:
+ // Successfully generated pdf/tutorial13.pdf
+}