diff options
-rw-r--r-- | def.go | 5 | ||||
-rw-r--r-- | doc.go | 5 | ||||
-rw-r--r-- | fpdf.go | 73 | ||||
-rw-r--r-- | fpdf_test.go | 95 |
4 files changed, 161 insertions, 17 deletions
@@ -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 @@ -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/. @@ -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 + +} |