implemented more methods for pdf graphic context, fixed the code for 100% golint and go vet
This commit is contained in:
parent
bbcbc3df5e
commit
74e84c4493
6 changed files with 98 additions and 22 deletions
|
@ -2,7 +2,7 @@ package pdf2d
|
||||||
|
|
||||||
import "github.com/stanim/gofpdf"
|
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 {
|
func SaveToPdfFile(filePath string, pdf *gofpdf.Fpdf) error {
|
||||||
return pdf.OutputFileAndClose(filePath)
|
return pdf.OutputFileAndClose(filePath)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,20 @@
|
||||||
// Copyright 2015 The draw2d Authors. All rights reserved.
|
// Copyright 2015 The draw2d Authors. All rights reserved.
|
||||||
// created: 26/06/2015 by Stani Michiels
|
// created: 26/06/2015 by Stani Michiels
|
||||||
|
// TODO: fonts, dpi
|
||||||
|
|
||||||
package pdf2d
|
package pdf2d
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
|
"image/jpeg"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"code.google.com/p/freetype-go/freetype/truetype"
|
||||||
|
|
||||||
"github.com/stanim/draw2d"
|
"github.com/stanim/draw2d"
|
||||||
"github.com/stanim/gofpdf"
|
"github.com/stanim/gofpdf"
|
||||||
|
@ -27,11 +33,29 @@ func notImplemented(method string) {
|
||||||
|
|
||||||
const c255 = 255.0 / 65535.0
|
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) {
|
func rgb(c color.Color) (int, int, int) {
|
||||||
r, g, b, _ := c.RGBA()
|
r, g, b, _ := c.RGBA()
|
||||||
return int(float64(r) * c255), int(float64(g) * c255), int(float64(b) * c255)
|
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
|
// GraphicContext implements the draw2d.GraphicContext interface
|
||||||
// It provides draw2d with a pdf backend (based on gofpdf)
|
// It provides draw2d with a pdf backend (based on gofpdf)
|
||||||
type GraphicContext struct {
|
type GraphicContext struct {
|
||||||
|
@ -46,69 +70,93 @@ func NewGraphicContext(pdf *gofpdf.Fpdf) *GraphicContext {
|
||||||
return &GraphicContext{draw2d.NewStackGraphicContext(), pdf, dpi}
|
return &GraphicContext{draw2d.NewStackGraphicContext(), pdf, dpi}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DrawImage draws an image as JPG at 96dpi
|
||||||
func (gc *GraphicContext) DrawImage(image image.Image) {
|
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() {
|
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) {
|
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) {
|
func (gc *GraphicContext) SetDPI(dpi int) {
|
||||||
gc.DPI = dpi
|
gc.DPI = dpi
|
||||||
|
// gc.recalc()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetDPI is a dummy method to implement the GraphicContext interface
|
||||||
func (gc *GraphicContext) GetDPI() int {
|
func (gc *GraphicContext) GetDPI() int {
|
||||||
return gc.DPI
|
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) {
|
func (gc *GraphicContext) GetStringBounds(s string) (left, top, right, bottom float64) {
|
||||||
notImplemented("GetStringBounds")
|
_, h := gc.pdf.GetFontSize()
|
||||||
return 0, 0, 0, 0
|
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) {
|
func (gc *GraphicContext) CreateStringPath(text string, x, y float64) (cursor float64) {
|
||||||
notImplemented("CreateStringPath")
|
gc.pdf.MoveTo(x, y)
|
||||||
return 0
|
_, _, 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) {
|
func (gc *GraphicContext) FillString(text string) (cursor float64) {
|
||||||
notImplemented("FillString")
|
return gc.FillStringAt(text, 0, 0)
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FillStringAt draws a string at x, y
|
||||||
func (gc *GraphicContext) FillStringAt(text string, x, y float64) (cursor float64) {
|
func (gc *GraphicContext) FillStringAt(text string, x, y float64) (cursor float64) {
|
||||||
notImplemented("FillStringAt")
|
return gc.CreateStringPath(text, x, y)
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StrokeString draws a string at 0, 0
|
||||||
func (gc *GraphicContext) StrokeString(text string) (cursor float64) {
|
func (gc *GraphicContext) StrokeString(text string) (cursor float64) {
|
||||||
notImplemented("StrokeString")
|
return gc.StrokeStringAt(text, 0, 0)
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StrokeStringAt draws a string at x, y
|
||||||
func (gc *GraphicContext) StrokeStringAt(text string, x, y float64) (cursor float64) {
|
func (gc *GraphicContext) StrokeStringAt(text string, x, y float64) (cursor float64) {
|
||||||
notImplemented("StrokeStringAt")
|
return gc.CreateStringPath(text, x, y)
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stroke strokes the paths
|
||||||
func (gc *GraphicContext) Stroke(paths ...*draw2d.PathStorage) {
|
func (gc *GraphicContext) Stroke(paths ...*draw2d.PathStorage) {
|
||||||
gc.draw("D", paths...)
|
gc.draw("D", paths...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fill strokes the paths
|
||||||
func (gc *GraphicContext) Fill(paths ...*draw2d.PathStorage) {
|
func (gc *GraphicContext) Fill(paths ...*draw2d.PathStorage) {
|
||||||
gc.draw("F", paths...)
|
gc.draw("F", paths...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FillStroke first fills the paths and than strokes them
|
||||||
func (gc *GraphicContext) FillStroke(paths ...*draw2d.PathStorage) {
|
func (gc *GraphicContext) FillStroke(paths ...*draw2d.PathStorage) {
|
||||||
gc.draw("FD", paths...)
|
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) {
|
func (gc *GraphicContext) draw(style string, paths ...*draw2d.PathStorage) {
|
||||||
paths = append(paths, gc.Current.Path)
|
paths = append(paths, gc.Current.Path)
|
||||||
pathConverter := NewPathConverter(
|
pathConverter := NewPathConverter(
|
||||||
|
@ -123,21 +171,40 @@ func (gc *GraphicContext) draw(style string, paths ...*draw2d.PathStorage) {
|
||||||
|
|
||||||
// overwrite StackGraphicContext methods
|
// overwrite StackGraphicContext methods
|
||||||
|
|
||||||
|
// SetStrokeColor sets the stroke color
|
||||||
func (gc *GraphicContext) SetStrokeColor(c color.Color) {
|
func (gc *GraphicContext) SetStrokeColor(c color.Color) {
|
||||||
gc.StackGraphicContext.SetStrokeColor(c)
|
gc.StackGraphicContext.SetStrokeColor(c)
|
||||||
gc.pdf.SetDrawColor(rgb(c))
|
gc.pdf.SetDrawColor(rgb(c))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetFillColor sets the fill color
|
||||||
func (gc *GraphicContext) SetFillColor(c color.Color) {
|
func (gc *GraphicContext) SetFillColor(c color.Color) {
|
||||||
gc.StackGraphicContext.SetFillColor(c)
|
gc.StackGraphicContext.SetFillColor(c)
|
||||||
gc.pdf.SetFillColor(rgb(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) {
|
func (gc *GraphicContext) SetLineWidth(LineWidth float64) {
|
||||||
gc.StackGraphicContext.SetLineWidth(LineWidth)
|
gc.StackGraphicContext.SetLineWidth(LineWidth)
|
||||||
gc.pdf.SetLineWidth(LineWidth)
|
gc.pdf.SetLineWidth(LineWidth)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetLineCap sets the line cap (round, but or square)
|
||||||
func (gc *GraphicContext) SetLineCap(Cap draw2d.Cap) {
|
func (gc *GraphicContext) SetLineCap(Cap draw2d.Cap) {
|
||||||
gc.StackGraphicContext.SetLineCap(Cap)
|
gc.StackGraphicContext.SetLineCap(Cap)
|
||||||
gc.pdf.SetLineCapStyle(caps[Cap])
|
gc.pdf.SetLineCapStyle(caps[Cap])
|
||||||
|
|
|
@ -11,14 +11,17 @@ import (
|
||||||
|
|
||||||
const deg = 180 / math.Pi
|
const deg = 180 / math.Pi
|
||||||
|
|
||||||
|
// PathConverter converts the paths to the pdf api
|
||||||
type PathConverter struct {
|
type PathConverter struct {
|
||||||
pdf Vectorizer
|
pdf Vectorizer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewPathConverter constructs a PathConverter from a pdf vectorizer
|
||||||
func NewPathConverter(pdf Vectorizer) *PathConverter {
|
func NewPathConverter(pdf Vectorizer) *PathConverter {
|
||||||
return &PathConverter{pdf: pdf}
|
return &PathConverter{pdf: pdf}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert converts the paths to the pdf api
|
||||||
func (c *PathConverter) Convert(paths ...*draw2d.PathStorage) {
|
func (c *PathConverter) Convert(paths ...*draw2d.PathStorage) {
|
||||||
for _, path := range paths {
|
for _, path := range paths {
|
||||||
j := 0
|
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 {
|
func (c *PathConverter) ConvertCommand(cmd draw2d.PathCmd, vertices ...float64) int {
|
||||||
switch cmd {
|
switch cmd {
|
||||||
case draw2d.MoveTo:
|
case draw2d.MoveTo:
|
||||||
|
|
|
@ -20,13 +20,15 @@ func ftoas(xs ...float64) string {
|
||||||
return buffer.String()
|
return buffer.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// VertexMatrixTransform implements Vectorizer and applies the Matrix
|
// PathLogger implements Vectorizer and applies the Matrix
|
||||||
// transformation tr. It is normally wrapped around gofpdf Fpdf.
|
// transformation tr. It is used as debugging middleware.
|
||||||
|
// It should wrap gofpdf.Fpdf directly.
|
||||||
type PathLogger struct {
|
type PathLogger struct {
|
||||||
logger *log.Logger
|
logger *log.Logger
|
||||||
Next Vectorizer
|
Next Vectorizer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewPathLogger constructs a new PathLogger
|
||||||
func NewPathLogger(logger *log.Logger,
|
func NewPathLogger(logger *log.Logger,
|
||||||
vectorizer Vectorizer) *PathLogger {
|
vectorizer Vectorizer) *PathLogger {
|
||||||
return &PathLogger{logger, vectorizer}
|
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,
|
func (pl *PathLogger) CurveBezierCubicTo(cx1, cy1,
|
||||||
cx2, cy2, x, y float64) {
|
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)
|
pl.logger.Printf("CurveBezierCubicTo(cx1=%.2f, cy1=%.2f, cx2=%.2f, cy2=%.2f, x=%.2f, y=%.2f)", cx1, cy1, cx2, cy2, x, y)
|
||||||
|
|
|
@ -6,12 +6,14 @@ package pdf2d
|
||||||
import "github.com/stanim/draw2d"
|
import "github.com/stanim/draw2d"
|
||||||
|
|
||||||
// VertexMatrixTransform implements Vectorizer and applies the Matrix
|
// 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 {
|
type VertexMatrixTransform struct {
|
||||||
tr draw2d.MatrixTransform
|
tr draw2d.MatrixTransform
|
||||||
Next Vectorizer
|
Next Vectorizer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewVertexMatrixTransform constructs an new VertexMatrixTransform
|
||||||
func NewVertexMatrixTransform(tr draw2d.MatrixTransform,
|
func NewVertexMatrixTransform(tr draw2d.MatrixTransform,
|
||||||
vectorizer Vectorizer) *VertexMatrixTransform {
|
vectorizer Vectorizer) *VertexMatrixTransform {
|
||||||
return &VertexMatrixTransform{tr, vectorizer}
|
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,
|
func (vmt *VertexMatrixTransform) CurveBezierCubicTo(cx1, cy1,
|
||||||
cx2, cy2, x, y float64) {
|
cx2, cy2, x, y float64) {
|
||||||
vmt.tr.Transform(&cx1, &cy1, &cx2, &cy2, &x, &y)
|
vmt.tr.Transform(&cx1, &cy1, &cx2, &cy2, &x, &y)
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
package pdf2d
|
package pdf2d
|
||||||
|
|
||||||
// Vectorizer defines the minimal interface for gofpdf.Fpdf
|
// Vectorizer defines the minimal interface for gofpdf.Fpdf
|
||||||
|
// to be passed to a PathConvertor.
|
||||||
// It is also implemented by for example VertexMatrixTransform
|
// It is also implemented by for example VertexMatrixTransform
|
||||||
type Vectorizer interface {
|
type Vectorizer interface {
|
||||||
// MoveTo creates a new subpath that start at the specified point
|
// MoveTo creates a new subpath that start at the specified point
|
||||||
|
|
Loading…
Reference in a new issue