partial implementation of gofpdf backend

This commit is contained in:
Stani 2015-06-27 01:13:20 +02:00
parent d47e08f7c9
commit a422b2462d
7 changed files with 433 additions and 0 deletions

8
pdf2d/fileutil.go Normal file
View file

@ -0,0 +1,8 @@
package pdf2d
import "github.com/stanim/gofpdf"
// SaveToPdfFile create and save a pdf document to a file
func SaveToPdfFile(filePath string, pdf *gofpdf.Fpdf) error {
return pdf.OutputFileAndClose(filePath)
}

144
pdf2d/graphiccontext.go Normal file
View file

@ -0,0 +1,144 @@
// Copyright 2015 The draw2d Authors. All rights reserved.
// created: 26/06/2015 by Stani Michiels
package pdf2d
import (
"fmt"
"image"
"image/color"
"log"
"os"
"github.com/stanim/draw2d"
"github.com/stanim/gofpdf"
)
const c255 = 254.0 / 65535.0
var (
caps = map[draw2d.Cap]string{
draw2d.RoundCap: "round",
draw2d.ButtCap: "butt",
draw2d.SquareCap: "square"}
)
func notImplemented(method string) {
fmt.Printf("%s: not implemented\n", method)
}
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)
}
// GraphicContext implements the draw2d.GraphicContext interface
// It provides draw2d with a pdf backend (based on gofpdf)
type GraphicContext struct {
*draw2d.StackGraphicContext
pdf *gofpdf.Fpdf
DPI int
}
// NewGraphicContext creates a new pdf GraphicContext
func NewGraphicContext(pdf *gofpdf.Fpdf) *GraphicContext {
dpi := 92
return &GraphicContext{draw2d.NewStackGraphicContext(), pdf, dpi}
}
func (gc *GraphicContext) DrawImage(image image.Image) {
notImplemented("DrawImage")
}
func (gc *GraphicContext) Clear() {
notImplemented("Clear")
}
func (gc *GraphicContext) ClearRect(x1, y1, x2, y2 int) {
notImplemented("ClearRect")
}
func (gc *GraphicContext) SetDPI(dpi int) {
gc.DPI = dpi
}
func (gc *GraphicContext) GetDPI() int {
return gc.DPI
}
func (gc *GraphicContext) GetStringBounds(s string) (left, top, right, bottom float64) {
notImplemented("GetStringBounds")
return 0, 0, 0, 0
}
func (gc *GraphicContext) CreateStringPath(text string, x, y float64) (cursor float64) {
notImplemented("CreateStringPath")
return 0
}
func (gc *GraphicContext) FillString(text string) (cursor float64) {
notImplemented("FillString")
return 0
}
func (gc *GraphicContext) FillStringAt(text string, x, y float64) (cursor float64) {
notImplemented("FillStringAt")
return 0
}
func (gc *GraphicContext) StrokeString(text string) (cursor float64) {
notImplemented("StrokeString")
return 0
}
func (gc *GraphicContext) StrokeStringAt(text string, x, y float64) (cursor float64) {
notImplemented("StrokeStringAt")
return 0
}
func (gc *GraphicContext) Stroke(paths ...*draw2d.PathStorage) {
gc.draw("D", paths...)
}
func (gc *GraphicContext) Fill(paths ...*draw2d.PathStorage) {
gc.draw("F", paths...)
}
func (gc *GraphicContext) FillStroke(paths ...*draw2d.PathStorage) {
gc.draw("FD", paths...)
}
var logger *log.Logger = log.New(os.Stdout, "", log.Lshortfile)
func (gc *GraphicContext) draw(style string, paths ...*draw2d.PathStorage) {
paths = append(paths, gc.Current.Path)
pathConverter := NewPathConverter(
NewVertexMatrixTransform(gc.Current.Tr,
NewPathLogger(logger, gc.pdf)))
pathConverter.Convert(paths...)
if gc.Current.FillRule.UseNonZeroWinding() {
style += "*"
}
gc.pdf.DrawPath(style)
}
// overwrite StackGraphicContext methods
func (gc *GraphicContext) SetStrokeColor(c color.Color) {
gc.StackGraphicContext.SetStrokeColor(c)
gc.pdf.SetDrawColor(rgb(c))
}
func (gc *GraphicContext) SetFillColor(c color.Color) {
gc.StackGraphicContext.SetFillColor(c)
gc.pdf.SetFillColor(rgb(c))
}
func (gc *GraphicContext) SetLineWidth(LineWidth float64) {
gc.StackGraphicContext.SetLineWidth(LineWidth)
gc.pdf.SetLineWidth(LineWidth)
}
func (gc *GraphicContext) SetLineCap(Cap draw2d.Cap) {
gc.StackGraphicContext.SetLineCap(Cap)
gc.pdf.SetLineCapStyle(caps[Cap])
}

56
pdf2d/path_converter.go Normal file
View file

@ -0,0 +1,56 @@
// Copyright 2015 The draw2d Authors. All rights reserved.
// created: 26/06/2015 by Stani Michiels
package pdf2d
import (
"math"
"github.com/stanim/draw2d"
)
const deg = 180 / math.Pi
type PathConverter struct {
pdf Vectorizer
}
func NewPathConverter(pdf Vectorizer) *PathConverter {
return &PathConverter{pdf: pdf}
}
func (c *PathConverter) Convert(paths ...*draw2d.PathStorage) {
for _, path := range paths {
j := 0
for _, cmd := range path.Commands {
j = j + c.ConvertCommand(cmd, path.Vertices[j:]...)
}
}
}
func (c *PathConverter) ConvertCommand(cmd draw2d.PathCmd, vertices ...float64) int {
switch cmd {
case draw2d.MoveTo:
c.pdf.MoveTo(vertices[0], vertices[1])
return 2
case draw2d.LineTo:
c.pdf.LineTo(vertices[0], vertices[1])
return 2
case draw2d.QuadCurveTo:
c.pdf.CurveTo(vertices[0], vertices[1], vertices[2], vertices[3])
return 4
case draw2d.CubicCurveTo:
c.pdf.CurveBezierCubicTo(vertices[0], vertices[1], vertices[2], vertices[3], vertices[4], vertices[5])
return 6
case draw2d.ArcTo:
c.pdf.ArcTo(vertices[0], vertices[1], vertices[2], vertices[3],
0, // degRotate
vertices[4]*deg, // degStart = startAngle
(vertices[4]-vertices[5])*deg) // degEnd = startAngle-angle
return 6
case draw2d.Close:
c.pdf.ClosePath()
return 0
}
return 0
}

70
pdf2d/path_logger.go Normal file
View file

@ -0,0 +1,70 @@
// Copyright 2015 The draw2d Authors. All rights reserved.
// created: 26/06/2015 by Stani Michiels
package pdf2d
import (
"bytes"
"log"
"strconv"
)
func ftoas(xs ...float64) string {
var buffer bytes.Buffer
for i, x := range xs {
if i > 0 {
buffer.WriteString(", ")
}
buffer.WriteString(strconv.FormatFloat(x, 'f', 2, 64))
}
return buffer.String()
}
// VertexMatrixTransform implements Vectorizer and applies the Matrix
// transformation tr. It is normally wrapped around gofpdf Fpdf.
type PathLogger struct {
logger *log.Logger
Next Vectorizer
}
func NewPathLogger(logger *log.Logger,
vectorizer Vectorizer) *PathLogger {
return &PathLogger{logger, vectorizer}
}
// MoveTo creates a new subpath that start at the specified point
func (pl *PathLogger) MoveTo(x, y float64) {
pl.logger.Printf("MoveTo(x=%.2f, y=%.2f)", x, y)
pl.Next.MoveTo(x, y)
}
// LineTo adds a line to the current subpath
func (pl *PathLogger) LineTo(x, y float64) {
pl.logger.Printf("LineTo(x=%.2f, y=%.2f)", x, y)
pl.Next.LineTo(x, y)
}
// CurveTo adds a quadratic bezier curve to the current subpath
func (pl *PathLogger) CurveTo(cx, cy, x, y float64) {
pl.logger.Printf("CurveTo(cx=%.2f, cy=%.2f, x=%.2f, y=%.2f)", cx, cy, x, y)
pl.Next.CurveTo(cx, cy, x, y)
}
// CurveTo 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)
pl.Next.CurveBezierCubicTo(cx1, cy1, cx2, cy2, x, y)
}
// ArcTo adds an arc to the current subpath
func (pl *PathLogger) ArcTo(x, y, rx, ry, degRotate, degStart, degEnd float64) {
pl.logger.Printf("ArcTo(x=%.2f, y=%.2f, rx=%.2f, ry=%.2f, degRotate=%.2f, degStart=%.2f, degEnd=%.2f)", x, y, rx, ry, degRotate, degStart, degEnd)
pl.Next.ArcTo(x, y, rx, ry, degRotate, degStart, degEnd)
}
// ClosePath closes the subpath
func (pl *PathLogger) ClosePath() {
pl.Next.ClosePath()
}

79
pdf2d/samples/android.go Normal file
View file

@ -0,0 +1,79 @@
// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 21/11/2010 by Laurent Le Goff
// Draw an android avatar to android.png
package main
import (
"fmt"
"image/color"
"math"
"github.com/stanim/draw2d"
"github.com/stanim/draw2d/pdf2d"
"github.com/stanim/gofpdf"
)
func main() {
// Initialize the graphic context on a pdf document
pdf := gofpdf.New("P", "mm", "A4", "../font")
pdf.AddPage()
gc := pdf2d.NewGraphicContext(pdf)
// set the fill and stroke color of the droid
gc.SetFillColor(color.RGBA{0x44, 0xff, 0x44, 0xff})
gc.SetStrokeColor(color.RGBA{0x44, 0x44, 0x44, 0xff})
// Draw the droid
DrawDroid(gc, 10, 10)
// Save to png
pdf2d.SaveToPdfFile("android.pdf", pdf)
}
func DrawDroid(gc draw2d.GraphicContext, x, y float64) {
gc.SetLineCap(draw2d.RoundCap)
gc.SetLineWidth(5)
fmt.Println("\nhead")
gc.MoveTo(x+30, y+70)
gc.ArcTo(x+80, y+70, 50, 50, 180*(math.Pi/180), 180*(math.Pi/180))
gc.Close()
gc.FillStroke()
gc.MoveTo(x+60, y+25)
gc.LineTo(x+50, y+10)
gc.MoveTo(x+100, y+25)
gc.LineTo(x+110, y+10)
gc.Stroke()
fmt.Println("\nleft eye")
draw2d.Circle(gc, x+60, y+45, 5)
gc.FillStroke()
fmt.Println("\nright eye")
draw2d.Circle(gc, x+100, y+45, 5)
gc.FillStroke()
fmt.Println("\nbody")
draw2d.RoundRect(gc, x+30, y+75, x+30+100, y+75+90, 10, 10)
gc.FillStroke()
draw2d.Rect(gc, x+30, y+75, x+30+100, y+75+80)
gc.FillStroke()
fmt.Println("\nleft arm")
draw2d.RoundRect(gc, x+5, y+80, x+5+20, y+80+70, 10, 10)
gc.FillStroke()
fmt.Println("\nright arm")
draw2d.RoundRect(gc, x+135, y+80, x+135+20, y+80+70, 10, 10)
gc.FillStroke()
fmt.Println("\nleft leg")
draw2d.RoundRect(gc, x+50, y+150, x+50+20, y+150+50, 10, 10)
gc.FillStroke()
fmt.Println("\nright leg")
draw2d.RoundRect(gc, x+90, y+150, x+90+20, y+150+50, 10, 10)
gc.FillStroke()
}

55
pdf2d/transform.go Normal file
View file

@ -0,0 +1,55 @@
// Copyright 2015 The draw2d Authors. All rights reserved.
// created: 26/06/2015 by Stani Michiels
package pdf2d
import "github.com/stanim/draw2d"
// VertexMatrixTransform implements Vectorizer and applies the Matrix
// transformation tr. It is normally wrapped around gofpdf Fpdf.
type VertexMatrixTransform struct {
tr draw2d.MatrixTransform
Next Vectorizer
}
func NewVertexMatrixTransform(tr draw2d.MatrixTransform,
vectorizer Vectorizer) *VertexMatrixTransform {
return &VertexMatrixTransform{tr, vectorizer}
}
// MoveTo creates a new subpath that start at the specified point
func (vmt *VertexMatrixTransform) MoveTo(x, y float64) {
vmt.tr.Transform(&x, &y)
vmt.Next.MoveTo(x, y)
}
// LineTo adds a line to the current subpath
func (vmt *VertexMatrixTransform) LineTo(x, y float64) {
vmt.tr.Transform(&x, &y)
vmt.Next.LineTo(x, y)
}
// CurveTo adds a quadratic bezier curve to the current subpath
func (vmt *VertexMatrixTransform) CurveTo(cx, cy, x, y float64) {
vmt.tr.Transform(&cx, &cy, &x, &y)
vmt.Next.CurveTo(cx, cy, x, y)
}
// CurveTo 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)
vmt.Next.CurveBezierCubicTo(cx1, cy1, cx2, cy2, x, y)
}
// ArcTo adds an arc to the current subpath
func (vmt *VertexMatrixTransform) ArcTo(x, y, rx, ry, degRotate, degStart, degEnd float64) {
vmt.tr.Transform(&x, &y)
vmt.Next.ArcTo(x, y, rx, ry, degRotate, degStart, degEnd)
}
// ClosePath closes the subpath
func (vmt *VertexMatrixTransform) ClosePath() {
vmt.Next.ClosePath()
}

21
pdf2d/vectorizer.go Normal file
View file

@ -0,0 +1,21 @@
// Copyright 2015 The draw2d Authors. All rights reserved.
// created: 26/06/2015 by Stani Michiels
package pdf2d
// Vectorizer defines the minimal interface for gofpdf.Fpdf
// It is also implemented by for example VertexMatrixTransform
type Vectorizer interface {
// MoveTo creates a new subpath that start at the specified point
MoveTo(x, y float64)
// LineTo adds a line to the current subpath
LineTo(x, y float64)
// CurveTo adds a quadratic bezier curve to the current subpath
CurveTo(cx, cy, x, y float64)
// CurveTo adds a cubic bezier curve to the current subpath
CurveBezierCubicTo(cx1, cy1, cx2, cy2, x, y float64)
// ArcTo adds an arc to the current subpath
ArcTo(x, y, rx, ry, degRotate, degStart, degEnd float64)
// ClosePath closes the subpath
ClosePath()
}