From 74e84c44932cbeb769e01470906239555366de38 Mon Sep 17 00:00:00 2001 From: Stani Date: Sat, 27 Jun 2015 16:21:13 +0200 Subject: [PATCH] implemented more methods for pdf graphic context, fixed the code for 100% golint and go vet --- pdf2d/fileutil.go | 2 +- pdf2d/graphiccontext.go | 99 ++++++++++++++++++++++++++++++++++------- pdf2d/path_converter.go | 4 ++ pdf2d/path_logger.go | 8 ++-- pdf2d/transform.go | 6 ++- pdf2d/vectorizer.go | 1 + 6 files changed, 98 insertions(+), 22 deletions(-) diff --git a/pdf2d/fileutil.go b/pdf2d/fileutil.go index 7df9da9..876dae3 100644 --- a/pdf2d/fileutil.go +++ b/pdf2d/fileutil.go @@ -2,7 +2,7 @@ package pdf2d import "github.com/stanim/gofpdf" -// SaveToPdfFile create and save a pdf document to a file +// SaveToPdfFile creates and saves a pdf document to a file func SaveToPdfFile(filePath string, pdf *gofpdf.Fpdf) error { return pdf.OutputFileAndClose(filePath) } diff --git a/pdf2d/graphiccontext.go b/pdf2d/graphiccontext.go index 70d4a88..b53d532 100644 --- a/pdf2d/graphiccontext.go +++ b/pdf2d/graphiccontext.go @@ -1,14 +1,20 @@ // Copyright 2015 The draw2d Authors. All rights reserved. // created: 26/06/2015 by Stani Michiels +// TODO: fonts, dpi package pdf2d import ( + "bytes" "fmt" "image" "image/color" + "image/jpeg" "log" "os" + "strconv" + + "code.google.com/p/freetype-go/freetype/truetype" "github.com/stanim/draw2d" "github.com/stanim/gofpdf" @@ -27,11 +33,29 @@ func notImplemented(method string) { const c255 = 255.0 / 65535.0 +var ( + imageCount uint32 + white color.Color = color.RGBA{255, 255, 255, 255} +) + func rgb(c color.Color) (int, int, int) { r, g, b, _ := c.RGBA() return int(float64(r) * c255), int(float64(g) * c255), int(float64(b) * c255) } +func clearRect(gc *GraphicContext, x1, y1, x2, y2 float64) { + // save state + f := gc.Current.FillColor + x, y := gc.pdf.GetXY() + // cover page with white rectangle + gc.SetFillColor(white) + draw2d.Rect(gc, x1, y1, x2, y2) + gc.Fill() + // restore state + gc.SetFillColor(f) + gc.pdf.MoveTo(x, y) +} + // GraphicContext implements the draw2d.GraphicContext interface // It provides draw2d with a pdf backend (based on gofpdf) type GraphicContext struct { @@ -46,69 +70,93 @@ func NewGraphicContext(pdf *gofpdf.Fpdf) *GraphicContext { return &GraphicContext{draw2d.NewStackGraphicContext(), pdf, dpi} } +// DrawImage draws an image as JPG at 96dpi func (gc *GraphicContext) DrawImage(image image.Image) { - notImplemented("DrawImage") + name := strconv.Itoa(int(imageCount)) + tp := "JPG" // "JPG", "JPEG", "PNG" and "GIF" + b := &bytes.Buffer{} + jpeg.Encode(b, image, nil) + gc.pdf.RegisterImageReader(name, tp, b) + gc.pdf.Image(name, 0, 0, 0, 0, false, tp, 0, "") + // bounds := image.Bounds() + // x, y, w, h := float64(bounds.Min.X), float64(bounds.Min.Y), float64(bounds.Dx()), float64(bounds.Dy()) + //gc.pdf.Image(name, x, y, w, h, false, tp, 0, "") } + +// Clear draws a white rectangle over the whole page func (gc *GraphicContext) Clear() { - notImplemented("Clear") + width, height := gc.pdf.GetPageSize() + clearRect(gc, 0, 0, width, height) } +// ClearRect draws a white rectangle over the specified area func (gc *GraphicContext) ClearRect(x1, y1, x2, y2 int) { - notImplemented("ClearRect") + clearRect(gc, float64(x1), float64(y1), float64(x2), float64(y2)) } +// SetDPI is a dummy method to implement the GraphicContext interface func (gc *GraphicContext) SetDPI(dpi int) { gc.DPI = dpi + // gc.recalc() } +// GetDPI is a dummy method to implement the GraphicContext interface func (gc *GraphicContext) GetDPI() int { return gc.DPI } +// GetStringBounds returns the approximate pixel bounds of the string s at x, y. func (gc *GraphicContext) GetStringBounds(s string) (left, top, right, bottom float64) { - notImplemented("GetStringBounds") - return 0, 0, 0, 0 + _, h := gc.pdf.GetFontSize() + return 0, 0, gc.pdf.GetStringWidth(s), h } +// CreateStringPath creates a path from the string s at x, y, and returns the string width. func (gc *GraphicContext) CreateStringPath(text string, x, y float64) (cursor float64) { - notImplemented("CreateStringPath") - return 0 + gc.pdf.MoveTo(x, y) + _, _, w, h := gc.GetStringBounds(text) + gc.pdf.Cell(w, h, text) + return w } +// FillString draws a string at 0, 0 func (gc *GraphicContext) FillString(text string) (cursor float64) { - notImplemented("FillString") - return 0 + return gc.FillStringAt(text, 0, 0) } +// FillStringAt draws a string at x, y func (gc *GraphicContext) FillStringAt(text string, x, y float64) (cursor float64) { - notImplemented("FillStringAt") - return 0 + return gc.CreateStringPath(text, x, y) } +// StrokeString draws a string at 0, 0 func (gc *GraphicContext) StrokeString(text string) (cursor float64) { - notImplemented("StrokeString") - return 0 + return gc.StrokeStringAt(text, 0, 0) } +// StrokeStringAt draws a string at x, y func (gc *GraphicContext) StrokeStringAt(text string, x, y float64) (cursor float64) { - notImplemented("StrokeStringAt") - return 0 + return gc.CreateStringPath(text, x, y) } +// Stroke strokes the paths func (gc *GraphicContext) Stroke(paths ...*draw2d.PathStorage) { gc.draw("D", paths...) } +// Fill strokes the paths func (gc *GraphicContext) Fill(paths ...*draw2d.PathStorage) { gc.draw("F", paths...) } +// FillStroke first fills the paths and than strokes them func (gc *GraphicContext) FillStroke(paths ...*draw2d.PathStorage) { gc.draw("FD", paths...) } -var logger *log.Logger = log.New(os.Stdout, "", log.Lshortfile) +var logger = log.New(os.Stdout, "", log.Lshortfile) +// draw fills and/or strokes paths func (gc *GraphicContext) draw(style string, paths ...*draw2d.PathStorage) { paths = append(paths, gc.Current.Path) pathConverter := NewPathConverter( @@ -123,21 +171,40 @@ func (gc *GraphicContext) draw(style string, paths ...*draw2d.PathStorage) { // overwrite StackGraphicContext methods +// SetStrokeColor sets the stroke color func (gc *GraphicContext) SetStrokeColor(c color.Color) { gc.StackGraphicContext.SetStrokeColor(c) gc.pdf.SetDrawColor(rgb(c)) } +// SetFillColor sets the fill color func (gc *GraphicContext) SetFillColor(c color.Color) { gc.StackGraphicContext.SetFillColor(c) gc.pdf.SetFillColor(rgb(c)) } +// SetFont sets the font used to draw text. +// It is mandatory to call this method at least once before printing +// text or the resulting document will not be valid. +func (gc *GraphicContext) SetFont(font *truetype.Font) { + // TODO: this api conflict needs to be fixed + gc.pdf.SetFont("Helvetica", "", 12) +} + +// SetFontSize sets the font size in points (as in ``a 12 point font''). +func (gc *GraphicContext) SetFontSize(fontSize float64) { + gc.StackGraphicContext.SetFontSize(fontSize) + gc.pdf.SetFontSize(fontSize) + //gc.recalc() +} + +// SetLineWidth sets the line width func (gc *GraphicContext) SetLineWidth(LineWidth float64) { gc.StackGraphicContext.SetLineWidth(LineWidth) gc.pdf.SetLineWidth(LineWidth) } +// SetLineCap sets the line cap (round, but or square) func (gc *GraphicContext) SetLineCap(Cap draw2d.Cap) { gc.StackGraphicContext.SetLineCap(Cap) gc.pdf.SetLineCapStyle(caps[Cap]) diff --git a/pdf2d/path_converter.go b/pdf2d/path_converter.go index 2f2ab36..65213f2 100644 --- a/pdf2d/path_converter.go +++ b/pdf2d/path_converter.go @@ -11,14 +11,17 @@ import ( const deg = 180 / math.Pi +// PathConverter converts the paths to the pdf api type PathConverter struct { pdf Vectorizer } +// NewPathConverter constructs a PathConverter from a pdf vectorizer func NewPathConverter(pdf Vectorizer) *PathConverter { return &PathConverter{pdf: pdf} } +// Convert converts the paths to the pdf api func (c *PathConverter) Convert(paths ...*draw2d.PathStorage) { for _, path := range paths { j := 0 @@ -28,6 +31,7 @@ func (c *PathConverter) Convert(paths ...*draw2d.PathStorage) { } } +// ConvertCommand converts a single path segment to the pdf api func (c *PathConverter) ConvertCommand(cmd draw2d.PathCmd, vertices ...float64) int { switch cmd { case draw2d.MoveTo: diff --git a/pdf2d/path_logger.go b/pdf2d/path_logger.go index 24faf49..c87ea3b 100644 --- a/pdf2d/path_logger.go +++ b/pdf2d/path_logger.go @@ -20,13 +20,15 @@ func ftoas(xs ...float64) string { return buffer.String() } -// VertexMatrixTransform implements Vectorizer and applies the Matrix -// transformation tr. It is normally wrapped around gofpdf Fpdf. +// PathLogger implements Vectorizer and applies the Matrix +// transformation tr. It is used as debugging middleware. +// It should wrap gofpdf.Fpdf directly. type PathLogger struct { logger *log.Logger Next Vectorizer } +// NewPathLogger constructs a new PathLogger func NewPathLogger(logger *log.Logger, vectorizer Vectorizer) *PathLogger { return &PathLogger{logger, vectorizer} @@ -51,7 +53,7 @@ func (pl *PathLogger) CurveTo(cx, cy, x, y float64) { } -// CurveTo adds a cubic bezier curve to the current subpath +// CurveBezierCubicTo adds a cubic bezier curve to the current subpath func (pl *PathLogger) CurveBezierCubicTo(cx1, cy1, cx2, cy2, x, y float64) { pl.logger.Printf("CurveBezierCubicTo(cx1=%.2f, cy1=%.2f, cx2=%.2f, cy2=%.2f, x=%.2f, y=%.2f)", cx1, cy1, cx2, cy2, x, y) diff --git a/pdf2d/transform.go b/pdf2d/transform.go index 1da048d..6461aa6 100644 --- a/pdf2d/transform.go +++ b/pdf2d/transform.go @@ -6,12 +6,14 @@ package pdf2d import "github.com/stanim/draw2d" // VertexMatrixTransform implements Vectorizer and applies the Matrix -// transformation tr. It is normally wrapped around gofpdf Fpdf. +// transformation tr. It is normally wrapped around gofpdf Fpdf +// or PathLogger. type VertexMatrixTransform struct { tr draw2d.MatrixTransform Next Vectorizer } +// NewVertexMatrixTransform constructs an new VertexMatrixTransform func NewVertexMatrixTransform(tr draw2d.MatrixTransform, vectorizer Vectorizer) *VertexMatrixTransform { return &VertexMatrixTransform{tr, vectorizer} @@ -36,7 +38,7 @@ func (vmt *VertexMatrixTransform) CurveTo(cx, cy, x, y float64) { } -// CurveTo adds a cubic bezier curve to the current subpath +// CurveBezierCubicTo adds a cubic bezier curve to the current subpath func (vmt *VertexMatrixTransform) CurveBezierCubicTo(cx1, cy1, cx2, cy2, x, y float64) { vmt.tr.Transform(&cx1, &cy1, &cx2, &cy2, &x, &y) diff --git a/pdf2d/vectorizer.go b/pdf2d/vectorizer.go index c7cc5a3..dda2312 100644 --- a/pdf2d/vectorizer.go +++ b/pdf2d/vectorizer.go @@ -4,6 +4,7 @@ package pdf2d // Vectorizer defines the minimal interface for gofpdf.Fpdf +// to be passed to a PathConvertor. // It is also implemented by for example VertexMatrixTransform type Vectorizer interface { // MoveTo creates a new subpath that start at the specified point