partial implementation of gofpdf backend
This commit is contained in:
parent
d47e08f7c9
commit
a422b2462d
7 changed files with 433 additions and 0 deletions
8
pdf2d/fileutil.go
Normal file
8
pdf2d/fileutil.go
Normal 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
144
pdf2d/graphiccontext.go
Normal 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
56
pdf2d/path_converter.go
Normal 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
70
pdf2d/path_logger.go
Normal 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
79
pdf2d/samples/android.go
Normal 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
55
pdf2d/transform.go
Normal 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
21
pdf2d/vectorizer.go
Normal 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()
|
||||||
|
}
|
Loading…
Reference in a new issue