From 56ecb2da47c9086aebdd4ea84a3e7dfa297fa07f Mon Sep 17 00:00:00 2001 From: Stani Date: Mon, 6 Jul 2015 12:11:38 +0200 Subject: added tutorial 31: line cap join style --- fpdf_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/fpdf_test.go b/fpdf_test.go index 8959ece..31faa6f 100644 --- a/fpdf_test.go +++ b/fpdf_test.go @@ -1476,3 +1476,41 @@ func ExampleFpdf_tutorial30() { // Output: // Successfully generated pdf/tutorial30.pdf } + +func ExampleFpdf_tutorial31() { + const offset = 75.0 + pdf := gofpdf.New("L", "mm", "A4", "") + pdf.AddPage() + var draw = func(cap, join string, x0, y0, x1, y1 float64) { + // transform begin & end needed to isolate caps and joins + pdf.SetLineCapStyle(cap) + pdf.SetLineJoinStyle(join) + + // Draw thick line + pdf.SetDrawColor(0x33, 0x33, 0x33) + pdf.SetLineWidth(30.0) + pdf.MoveTo(x0, y0) + pdf.LineTo((x0+x1)/2+offset, (y0+y1)/2) + pdf.LineTo(x1, y1) + pdf.DrawPath("D") + + // Draw thin helping line + pdf.SetDrawColor(0xFF, 0x33, 0x33) + pdf.SetLineWidth(2.56) + pdf.MoveTo(x0, y0) + pdf.LineTo((x0+x1)/2+offset, (y0+y1)/2) + pdf.LineTo(x1, y1) + pdf.DrawPath("D") + + } + x := 35.0 + caps := []string{"butt", "square", "round"} + joins := []string{"bevel", "miter", "round"} + for i := range caps { + draw(caps[i], joins[i], x, 50, x, 160) + x += offset + } + pdf.OutputAndClose(docWriter(pdf, 31)) + // Output: + // Successfully generated pdf/tutorial31.pdf +} -- cgit v1.2.1-24-ge1ad From 74622e58a96b143400c19c7ff408f084bcf78918 Mon Sep 17 00:00:00 2001 From: Stani Date: Mon, 6 Jul 2015 12:36:29 +0200 Subject: fix documentation string MoveTo --- fpdf.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/fpdf.go b/fpdf.go index 6c21210..41d8c25 100644 --- a/fpdf.go +++ b/fpdf.go @@ -3487,15 +3487,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. +// +// Create a "path" by moving a virtual stylus around the page (with +// MoveTo, LineTo, CurveTo, CurveBezierCubicTo, ArcTo & ClosePath) +// then draw it or fill it in (with DrawPath). 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. // // See tutorial 30 for an example of this function. func (f *Fpdf) MoveTo(x, y float64) { -- cgit v1.2.1-24-ge1ad From c58d8b0f8bf2b0e43c107adb8dee06fdc7b3deb6 Mon Sep 17 00:00:00 2001 From: Stani Date: Mon, 6 Jul 2015 14:39:32 +0200 Subject: implemented ArcTo --- fpdf.go | 138 +++++++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 89 insertions(+), 49 deletions(-) diff --git a/fpdf.go b/fpdf.go index 41d8c25..fafca6f 100644 --- a/fpdf.go +++ b/fpdf.go @@ -907,7 +907,7 @@ func (f *Fpdf) Circle(x, y, r float64, styleStr string) { // // See tutorial 11 for an example of this function. func (f *Fpdf) Ellipse(x, y, rx, ry, degRotate float64, styleStr string) { - f.Arc(x, y, rx, ry, degRotate, 0, 360, styleStr) + f.arc(x, y, rx, ry, degRotate, 0, 360, styleStr, false) } // Polygon draws a closed figure defined by a series of vertices specified by @@ -1047,54 +1047,7 @@ func (f *Fpdf) CurveBezierCubic(x0, y0, cx0, cy0, cx1, cy1, x1, y1 float64, styl // // See tutorial 11 for an example of this function. func (f *Fpdf) Arc(x, y, rx, ry, degRotate, degStart, degEnd float64, styleStr string) { - x *= f.k - y = (f.h - y) * f.k - rx *= f.k - ry *= f.k - segments := int(degEnd-degStart) / 60 - if segments < 2 { - segments = 2 - } - angleStart := degStart * math.Pi / 180 - angleEnd := degEnd * math.Pi / 180 - angleTotal := angleEnd - angleStart - dt := angleTotal / float64(segments) - dtm := dt / 3 - if degRotate != 0 { - a := -degRotate * math.Pi / 180 - f.outf("q %.5f %.5f %.5f %.5f %.5f %.5f cm", math.Cos(a), -1*math.Sin(a), - math.Sin(a), math.Cos(a), x, y) - x = 0 - y = 0 - } - t := angleStart - a0 := x + rx*math.Cos(t) - b0 := y + ry*math.Sin(t) - c0 := -rx * math.Sin(t) - d0 := ry * math.Cos(t) - f.point(a0/f.k, f.h-(b0/f.k)) - for j := 1; j <= segments; j++ { - // Draw this bit of the total curve - t = (float64(j) * dt) + angleStart - a1 := x + rx*math.Cos(t) - b1 := y + ry*math.Sin(t) - c1 := -rx * math.Sin(t) - d1 := ry * math.Cos(t) - f.curve((a0+(c0*dtm))/f.k, - f.h-((b0+(d0*dtm))/f.k), - (a1-(c1*dtm))/f.k, - f.h-((b1-(d1*dtm))/f.k), - a1/f.k, - f.h-(b1/f.k)) - a0 = a1 - b0 = b1 - c0 = c1 - d0 = d1 - } - f.out(fillDrawOp(styleStr)) - if degRotate != 0 { - f.out("Q") - } + f.arc(x, y, rx, ry, degRotate, degStart, degEnd, styleStr, false) } // SetAlpha sets the alpha blending channel. The blending effect applies to @@ -3558,3 +3511,90 @@ func (f *Fpdf) ClosePath() { func (f *Fpdf) DrawPath(styleStr string) { f.outf(fillDrawOp(styleStr)) } + +// ArcTo draws an elliptical arc centered at point (x, y). rx and ry specify its +// horizontal and vertical radii. If the start of the arc is not at +// the current position, a connecting line will be drawn. +// +// degRotate specifies the angle that the arc will be rotated. degStart and +// degEnd specify the starting and ending angle of the arc. All angles are +// specified in degrees and measured counter-clockwise from the 3 o'clock +// position. +// +// 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, line width, and cap style centered on the arc's +// path. Filling uses the current fill color. +// +// See tutorial 30 for an example of this function. +func (f *Fpdf) ArcTo(x, y, rx, ry, degRotate, degStart, degEnd float64) { + f.arc(x, y, rx, ry, degRotate, degStart, degEnd, "", true) +} + +func (f *Fpdf) arc(x, y, rx, ry, degRotate, degStart, degEnd float64, + styleStr string, path bool) { + x *= f.k + y = (f.h - y) * f.k + rx *= f.k + ry *= f.k + segments := int(degEnd-degStart) / 60 + if segments < 2 { + segments = 2 + } + angleStart := degStart * math.Pi / 180 + angleEnd := degEnd * math.Pi / 180 + angleTotal := angleEnd - angleStart + dt := angleTotal / float64(segments) + dtm := dt / 3 + if degRotate != 0 { + a := -degRotate * math.Pi / 180 + f.outf("q %.5f %.5f %.5f %.5f %.5f %.5f cm", + math.Cos(a), -1*math.Sin(a), + math.Sin(a), math.Cos(a), x, y) + x = 0 + y = 0 + } + t := angleStart + a0 := x + rx*math.Cos(t) + b0 := y + ry*math.Sin(t) + c0 := -rx * math.Sin(t) + d0 := ry * math.Cos(t) + sx := a0 / f.k // start point of arc + sy := f.h - (b0 / f.k) + if path { + if f.x != sx || f.y != sy { + // Draw connecting line to start point + f.LineTo(sx, sy) + } + } else { + f.point(sx, sy) + } + for j := 1; j <= segments; j++ { + // Draw this bit of the total curve + t = (float64(j) * dt) + angleStart + a1 := x + rx*math.Cos(t) + b1 := y + ry*math.Sin(t) + c1 := -rx * math.Sin(t) + d1 := ry * math.Cos(t) + f.curve((a0+(c0*dtm))/f.k, + f.h-((b0+(d0*dtm))/f.k), + (a1-(c1*dtm))/f.k, + f.h-((b1-(d1*dtm))/f.k), + a1/f.k, + f.h-(b1/f.k)) + a0 = a1 + b0 = b1 + c0 = c1 + d0 = d1 + if path { + f.x = a1 / f.k + f.y = f.h - (b1 / f.k) + } + } + if !path { + f.out(fillDrawOp(styleStr)) + } + if degRotate != 0 { + f.out("Q") + } +} -- cgit v1.2.1-24-ge1ad From 0739cc03f4ea4bd99bf3d695b8574a19dfdaccbd Mon Sep 17 00:00:00 2001 From: Stani Date: Mon, 6 Jul 2015 14:53:34 +0200 Subject: update tutorial 30 for ArcTo --- fpdf_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fpdf_test.go b/fpdf_test.go index 31faa6f..9ac1e18 100644 --- a/fpdf_test.go +++ b/fpdf_test.go @@ -20,7 +20,6 @@ import ( "bufio" "bytes" "fmt" - "github.com/jung-kurt/gofpdf" "io" "io/ioutil" "math" @@ -28,6 +27,8 @@ import ( "os" "path/filepath" "strings" + + "github.com/jung-kurt/gofpdf" ) // Absolute path needed for gocov tool; relative OK for test @@ -1465,7 +1466,8 @@ func ExampleFpdf_tutorial30() { pdf := gofpdf.New("P", "mm", "A4", "") pdf.AddPage() pdf.MoveTo(20, 20) - pdf.LineTo(190, 20) + pdf.LineTo(170, 20) + pdf.ArcTo(170, 40, 20, 20, 0, 90, 0) pdf.CurveTo(190, 100, 105, 100) pdf.CurveBezierCubicTo(20, 100, 105, 200, 20, 200) pdf.ClosePath() -- cgit v1.2.1-24-ge1ad From eedf31684c3a5cc1ab4ae6dfea0e8c0869c59cfe Mon Sep 17 00:00:00 2001 From: Stani Date: Mon, 6 Jul 2015 15:06:30 +0200 Subject: fix path drawing methods for ArcTo --- fpdf.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fpdf.go b/fpdf.go index fafca6f..ea4b384 100644 --- a/fpdf.go +++ b/fpdf.go @@ -3454,7 +3454,8 @@ func (f *Fpdf) enddoc() { // // See tutorial 30 for an example of this function. func (f *Fpdf) MoveTo(x, y float64) { - f.point(x, y) // rename? + f.point(x, y) + f.x, f.y = x, y } // LineTo creates a line from the current stylus location to (x, y), which @@ -3464,6 +3465,7 @@ func (f *Fpdf) MoveTo(x, y float64) { // 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) + f.x, f.y = x, y } // CurveTo creates a single-segment quadratic Bézier curve. The curve starts at @@ -3476,6 +3478,7 @@ func (f *Fpdf) LineTo(x, y float64) { // 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) + f.x, f.y = x, y } // CurveBezierCubicTo creates a single-segment cubic Bézier curve. The curve @@ -3488,7 +3491,8 @@ func (f *Fpdf) CurveTo(cx, cy, x, y float64) { // // 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? + f.curve(cx0, cy0, cx1, cy1, x, y) + f.x, f.y = x, y } // ClosePath creates a line from the current location to the last MoveTo point -- cgit v1.2.1-24-ge1ad From 3442cac58db1e6688e15541da68b0dcfea98b977 Mon Sep 17 00:00:00 2001 From: Stani Date: Mon, 6 Jul 2015 15:12:38 +0200 Subject: fix stanim acknowledgment --- doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc.go b/doc.go index 4e54658..75091e0 100644 --- a/doc.go +++ b/doc.go @@ -72,7 +72,7 @@ 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. Claudio Felber provided implementations for dashed line drawing and generalized -font loading. Stanim provided support for multi-segment path drawing with +font loading. Stani Michiels provided support for multi-segment path drawing with smooth line joins and line join styles. Bruno Michel has provided valuable assistance with the code. -- cgit v1.2.1-24-ge1ad