summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--def.go5
-rw-r--r--doc.go5
-rw-r--r--fpdf.go73
-rw-r--r--fpdf_test.go95
4 files changed, 161 insertions, 17 deletions
diff --git a/def.go b/def.go
index 6d12af3..47b399f 100644
--- a/def.go
+++ b/def.go
@@ -49,6 +49,11 @@ type PointType struct {
X, Y float64
}
+// XY returns the X and Y components of the receiver point.
+func (p PointType) XY() (float64, float64) {
+ return p.X, p.Y
+}
+
// ImageInfoType contains size, color and other information about an image
type ImageInfoType struct {
data []byte
diff --git a/doc.go b/doc.go
index bfefefe..29194e8 100644
--- a/doc.go
+++ b/doc.go
@@ -69,8 +69,9 @@ Kesteloot provided code to allow an image's extent to be determined prior to
placement. Support for vertical alignment within a cell was provided by Stefan
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. Bruno Michel has provided valuable
-assistance with the code.
+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.
The FPDF website is http://www.fpdf.org/.
diff --git a/fpdf.go b/fpdf.go
index e2c8b7c..6c2521b 100644
--- a/fpdf.go
+++ b/fpdf.go
@@ -673,7 +673,7 @@ func colorValue(r, g, b int, grayStr, fullStr string) (clr clrType) {
// SetDrawColor defines the color used for all drawing operations (lines,
// rectangles and cell borders). It is expressed in RGB components (0 - 255).
-// The method can be called before the first page is created and the value is
+// The method can be called before the first page is created. The value is
// retained from page to page.
func (f *Fpdf) SetDrawColor(r, g, b int) {
f.color.draw = colorValue(r, g, b, "G", "RG")
@@ -706,7 +706,7 @@ func (f *Fpdf) GetFillColor() (int, int, int) {
// SetTextColor defines the color used for text. It is expressed in RGB
// components (0 - 255). The method can be called before the first page is
-// created and the value is retained from page to page.
+// created. The value is retained from page to page.
func (f *Fpdf) SetTextColor(r, g, b int) {
f.color.text = colorValue(r, g, b, "g", "rg")
f.colorFlag = f.color.fill.str != f.color.text.str
@@ -734,7 +734,7 @@ func (f *Fpdf) GetStringWidth(s string) float64 {
}
// SetLineWidth defines the line width. By default, the value equals 0.2 mm.
-// The method can be called before the first page is created and the value is
+// The method can be called before the first page is created. The value is
// retained from page to page.
func (f *Fpdf) SetLineWidth(width float64) {
f.lineWidth = width
@@ -743,9 +743,14 @@ func (f *Fpdf) SetLineWidth(width float64) {
}
}
+// GetLineWidth returns the current line thickness.
+func (f *Fpdf) GetLineWidth() float64 {
+ return f.lineWidth
+}
+
// SetLineCapStyle defines the line cap style. styleStr should be "butt",
// "round" or "square". A square style projects from the end of the line. The
-// method can be called before the first page is created and the value is
+// method can be called before the first page is created. The value is
// retained from page to page.
func (f *Fpdf) SetLineCapStyle(styleStr string) {
var capStyle int
@@ -848,15 +853,50 @@ func (f *Fpdf) Polygon(points []PointType, styleStr string) {
}
}
+// Beziergon draws a closed figure defined by a series of cubic Bézier curve
+// segments. The first point in the slice defines the starting point of the
+// figure. Each three following points p1, p2, p3 represent a curve segment to
+// the point p3 using p1 and p2 as the Bézier control points.
+//
+// The x and y fields of the points use the units established in New().
+//
+// styleStr can be "F" for filled, "D" for outlined only, or "DF" or "FD" for
+// outlined and filled. An empty string will be replaced with "D". Drawing uses
+// the current draw color and line width centered on the ellipse's perimeter.
+// Filling uses the current fill color.
+//
+// See tutorial 28 for an example of this function.
+func (f *Fpdf) Beziergon(points []PointType, styleStr string) {
+
+ // Thanks, Robert Lillack, for contributing this function.
+
+ if len(points) < 4 {
+ return
+ }
+ f.point(points[0].XY())
+
+ points = points[1:]
+ for len(points) >= 3 {
+ cx0, cy0 := points[0].XY()
+ cx1, cy1 := points[1].XY()
+ x1, y1 := points[2].XY()
+ f.curve(cx0, cy0, cx1, cy1, x1, y1)
+ points = points[3:]
+ }
+
+ f.outf(fillDrawOp(styleStr))
+}
+
// Outputs current point
func (f *Fpdf) point(x, y float64) {
f.outf("%.2f %.2f m", x*f.k, (f.h-y)*f.k)
}
-// Outputs quadratic curve from current point
-func (f *Fpdf) curve(cx0, cy0, x1, y1, cx1, cy1 float64) {
- f.outf("%.5f %.5f %.5f %.5f %.5f %.5f c", cx0*f.k, (f.h-cy0)*f.k, x1*f.k,
- (f.h-y1)*f.k, cx1*f.k, (f.h-cy1)*f.k)
+// Outputs a single cubic Bézier curve segment from current point
+func (f *Fpdf) curve(cx0, cy0, cx1, cy1, x, y float64) {
+ // Thanks, Robert Lillack, for straightening this out
+ f.outf("%.5f %.5f %.5f %.5f %.5f %.5f c", cx0*f.k, (f.h-cy0)*f.k, cx1*f.k,
+ (f.h-cy1)*f.k, x*f.k, (f.h-y)*f.k)
}
// Curve draws a single-segment quadratic Bézier curve. The curve starts at
@@ -878,7 +918,17 @@ func (f *Fpdf) Curve(x0, y0, cx, cy, x1, y1 float64, styleStr string) {
fillDrawOp(styleStr))
}
-// CurveCubic draws a single-segment cubic Bézier curve. The curve starts at
+// CurveCubic draws a single-segment cubic Bézier curve. This routine performs
+// the same function as CurveBezierCubic() but has a nonstandard argument order.
+// It is retained to preserve backward compatibility.
+func (f *Fpdf) CurveCubic(x0, y0, cx0, cy0, x1, y1, cx1, cy1 float64, styleStr string) {
+ // f.point(x0, y0)
+ // f.outf("%.5f %.5f %.5f %.5f %.5f %.5f c %s", cx0*f.k, (f.h-cy0)*f.k,
+ // cx1*f.k, (f.h-cy1)*f.k, x1*f.k, (f.h-y1)*f.k, fillDrawOp(styleStr))
+ f.CurveBezierCubic(x0, y0, cx0, cy0, cx1, cy1, x1, y1, styleStr)
+}
+
+// CurveBezierCubic draws a single-segment cubic Bézier curve. The curve starts at
// the point (x0, y0) and ends at the point (x1, y1). The control points (cx0,
// cy0) and (cx1, cy1) specify the curvature. At the start point, the curve is
// tangent to the straight line between the start point and the control point
@@ -890,8 +940,11 @@ func (f *Fpdf) Curve(x0, y0, cx, cy, x1, y1 float64, styleStr string) {
// the current draw color, line width, and cap style centered on the curve's
// path. Filling uses the current fill color.
//
+// This routine performs the same function as CurveCubic() but uses standard
+// argument order.
+//
// See tutorials 11 and 20 for examples of this function.
-func (f *Fpdf) CurveCubic(x0, y0, cx0, cy0, x1, y1, cx1, cy1 float64, styleStr string) {
+func (f *Fpdf) CurveBezierCubic(x0, y0, cx0, cy0, cx1, cy1, x1, y1 float64, styleStr string) {
f.point(x0, y0)
f.outf("%.5f %.5f %.5f %.5f %.5f %.5f c %s", cx0*f.k, (f.h-cy0)*f.k,
cx1*f.k, (f.h-cy1)*f.k, x1*f.k, (f.h-y1)*f.k, fillDrawOp(styleStr))
diff --git a/fpdf_test.go b/fpdf_test.go
index 11b96bb..2b51f49 100644
--- a/fpdf_test.go
+++ b/fpdf_test.go
@@ -644,13 +644,13 @@ func ExampleFpdf_tutorial11() {
y += 40.0
pdf.Text(10, y, "Curves (cubic)")
pdf.SetFillColor(220, 200, 220)
- pdf.CurveCubic(10, y+30, 15, y-20, 40, y+30, 10, y+30, "D")
- pdf.CurveCubic(45, y+30, 50, y-20, 75, y+30, 45, y+30, "F")
- pdf.CurveCubic(80, y+30, 85, y-20, 110, y+30, 80, y+30, "FD")
+ pdf.CurveBezierCubic(10, y+30, 15, y-20, 10, y+30, 40, y+30, "D")
+ pdf.CurveBezierCubic(45, y+30, 50, y-20, 45, y+30, 75, y+30, "F")
+ pdf.CurveBezierCubic(80, y+30, 85, y-20, 80, y+30, 110, y+30, "FD")
pdf.SetLineWidth(thick)
- pdf.CurveCubic(115, y+30, 120, y-20, 145, y+30, 115, y+30, "FD")
+ pdf.CurveBezierCubic(115, y+30, 120, y-20, 115, y+30, 145, y+30, "FD")
pdf.SetLineCapStyle("round")
- pdf.CurveCubic(150, y+30, 155, y-20, 180, y+30, 150, y+30, "FD")
+ pdf.CurveBezierCubic(150, y+30, 155, y-20, 150, y+30, 180, y+30, "FD")
pdf.SetLineWidth(thin)
pdf.SetLineCapStyle("butt")
@@ -1327,3 +1327,88 @@ func ExampleFpdf_tutorial27() {
// Successfully generated pdf/tutorial27.pdf
}
+
+// This example demonstrates the Beziergon function.
+func ExampleFpdf_tutorial28() {
+
+ const (
+ margin = 10
+ wd = 210
+ unit = (wd - 2*margin) / 6
+ ht = 297
+ fontSize = 15
+ msgStr = `Demonstration of Beziergon function`
+ coefficient = 0.6
+ delta = coefficient * unit
+ ln = fontSize * 25.4 / 72
+ offsetX = (wd - 4*unit) / 2.0
+ offsetY = offsetX + 2*ln
+ )
+
+ srcList := []gofpdf.PointType{
+ {X: 0, Y: 0},
+ {X: 1, Y: 0},
+ {X: 1, Y: 1},
+ {X: 2, Y: 1},
+ {X: 2, Y: 2},
+ {X: 3, Y: 2},
+ {X: 3, Y: 3},
+ {X: 4, Y: 3},
+ {X: 4, Y: 4},
+ {X: 1, Y: 4},
+ {X: 1, Y: 3},
+ {X: 0, Y: 3},
+ }
+
+ ctrlList := []gofpdf.PointType{
+ {X: 1, Y: -1},
+ {X: 1, Y: 1},
+ {X: 1, Y: 1},
+ {X: 1, Y: 1},
+ {X: 1, Y: 1},
+ {X: 1, Y: 1},
+ {X: 1, Y: 1},
+ {X: 1, Y: 1},
+ {X: -1, Y: 1},
+ {X: -1, Y: -1},
+ {X: -1, Y: -1},
+ {X: -1, Y: -1},
+ }
+
+ pdf := gofpdf.New("P", "mm", "A4", "")
+ pdf.AddPage()
+ pdf.SetFont("Helvetica", "", fontSize)
+ for j, src := range srcList {
+ srcList[j].X = offsetX + src.X*unit
+ srcList[j].Y = offsetY + src.Y*unit
+ }
+ for j, ctrl := range ctrlList {
+ ctrlList[j].X = ctrl.X * delta
+ ctrlList[j].Y = ctrl.Y * delta
+ }
+ jPrev := len(srcList) - 1
+ srcPrev := srcList[jPrev]
+ curveList := []gofpdf.PointType{srcPrev} // point [, control 0, control 1, point]*
+ control := func(x, y float64) {
+ curveList = append(curveList, gofpdf.PointType{X: x, Y: y})
+ }
+ for j, src := range srcList {
+ ctrl := ctrlList[jPrev]
+ control(srcPrev.X+ctrl.X, srcPrev.Y+ctrl.Y) // Control 0
+ ctrl = ctrlList[j]
+ control(src.X-ctrl.X, src.Y-ctrl.Y) // Control 1
+ curveList = append(curveList, src) // Destination
+ jPrev = j
+ srcPrev = src
+ }
+ pdf.MultiCell(wd-margin-margin, ln, msgStr, "", "C", false)
+ pdf.SetDrawColor(224, 224, 224)
+ pdf.Polygon(srcList, "D")
+ pdf.SetDrawColor(64, 64, 128)
+ pdf.SetLineWidth(pdf.GetLineWidth() * 3)
+ pdf.Beziergon(curveList, "D")
+ pdf.OutputAndClose(docWriter(pdf, 28))
+ // Output:
+ // Successfully generated pdf/tutorial28.pdf
+
+}