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