Skip to content

Commit

Permalink
Implemented : Curve & Filled Curve Shape with test
Browse files Browse the repository at this point in the history
  • Loading branch information
s4kibs4mi committed May 30, 2018
1 parent 0c9502a commit f82403e
Show file tree
Hide file tree
Showing 6 changed files with 279 additions and 0 deletions.
64 changes: 64 additions & 0 deletions pdf/creator/curve.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package creator

import (
"strings"
"fmt"
"github.com/unidoc/unidoc/pdf/model"
)

// NewCurve returns new instance of Curve
func NewCurve(x1, y1, cx, cy, x2, y2 float64) *Curve {
c := &Curve{}

c.x1 = x1
c.y1 = y1

c.cx = cx
c.cy = cy

c.x2 = x2
c.y2 = y2

c.lineColor = model.NewPdfColorDeviceRGB(0, 0, 0)
c.lineWidth = 1.0
return c
}

type Curve struct {
x1 float64
y1 float64
cx float64 // control point
cy float64
x2 float64
y2 float64

lineColor *model.PdfColorDeviceRGB
lineWidth float64
}

// SetWidth sets line width
func (c *Curve) SetWidth(width float64) {
c.lineWidth = width
}

// SetColor sets the line color.
func (c *Curve) SetColor(col Color) {
c.lineColor = model.NewPdfColorDeviceRGB(col.ToRGB())
}

// GeneratePageBlocks generates page blocks
func (c *Curve) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, error) {
block := NewBlock(ctx.PageWidth, ctx.PageHeight)

var ops []string
ops = append(ops, fmt.Sprintf("%.2f w", c.lineWidth)) // line widtdh
ops = append(ops, fmt.Sprintf("%.3f %.3f %.3f RG", c.lineColor[0], c.lineColor[1], c.lineColor[2])) // line color
ops = append(ops, fmt.Sprintf("%.2f %.2f m", c.x1, ctx.PageHeight-c.y1)) // move to
ops = append(ops, fmt.Sprintf("%.5f %.5f %.5f %.5f v S", c.cx, ctx.PageHeight-c.cy, c.x2, ctx.PageHeight-c.y2))

err := block.addContentsByString(strings.Join(ops, "\n"))
if err != nil {
return nil, ctx, err
}
return []*Block{block}, ctx, nil
}
68 changes: 68 additions & 0 deletions pdf/creator/curve_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package creator

import "testing"

const testPdfFileWithCurve = "../../testfiles/curve.pdf"

func TestNewCurve(t *testing.T) {
creator := New()
creator.NewPage()
curve := NewCurve(20, 20, 15, 35, 40, 150)
curve.SetWidth(3.0)
curve.SetColor(ColorGreen)
err := creator.Draw(curve)
if err != nil {
t.Errorf("Fail: %v", err)
return
}

err = creator.WriteToFile(testPdfFileWithCurve)
if err != nil {
t.Errorf("Fail: %v", err)
return
}
}

func CreateCurve(x1, y1, cx, cy, x2, y2 float64, color Color) *Curve {
curve := NewCurve(x1, y1, cx, cy, x2, y2)
curve.SetWidth(1)
curve.SetColor(color)
return curve
}

func CreateLine(x1, y1, x2, y2, width float64) *Line {
line := NewLine(x1, y1, x2, y2)
line.SetLineWidth(width)
line.SetColor(ColorRed)
return line
}

func TestNewCurveWithGlass(t *testing.T) {
creator := New()
creator.NewPage()

// Width 200
creator.Draw(CreateLine(30, 200, 270, 200, 1))

// Curve up
creator.Draw(CreateCurve(50, 200, 75, 145, 150, 150, ColorRed))
creator.Draw(CreateCurve(150, 150, 205, 145, 250, 200, ColorGreen))

// Curve down
creator.Draw(CreateCurve(50, 200, 75, 245, 150, 250, ColorBlue))
creator.Draw(CreateCurve(150, 250, 225, 245, 250, 200, ColorBlack))

// Vertical line
creator.Draw(CreateLine(50, 200, 51, 400, 1))
creator.Draw(CreateLine(250, 200, 251, 400, 1))

// Curve down
creator.Draw(CreateCurve(51, 399, 75, 445, 150, 450, ColorRed))
creator.Draw(CreateCurve(150, 450, 225, 445, 251, 399, ColorGreen))

err := creator.WriteToFile(testPdfFileWithCurve)
if err != nil {
t.Errorf("Fail: %v", err)
return
}
}
105 changes: 105 additions & 0 deletions pdf/creator/filled_curve.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package creator

import (
"github.com/unidoc/unidoc/pdf/contentstream/draw"
pdfcontent "github.com/unidoc/unidoc/pdf/contentstream"
pdfcore "github.com/unidoc/unidoc/pdf/core"
pdf "github.com/unidoc/unidoc/pdf/model"
)

type FilledCurve struct {
curves []draw.CubicBezierCurve
FillEnabled bool // Show fill?
fillColor *pdf.PdfColorDeviceRGB
BorderEnabled bool // Show border?
BorderWidth float64
borderColor *pdf.PdfColorDeviceRGB
Opacity float64 // Alpha value (0-1).
}

// NewFilledCurve returns a instance of filled curve
func NewFilledCurve() *FilledCurve {
curve := FilledCurve{}
curve.curves = []draw.CubicBezierCurve{}
return &curve
}

// AppendCurve appends curve to filled curve
func (this *FilledCurve) AppendCurve(curve draw.CubicBezierCurve) *FilledCurve {
this.curves = append(this.curves, curve)
return this
}

func (this *FilledCurve) SetFillColor(color Color) {
this.fillColor = pdf.NewPdfColorDeviceRGB(color.ToRGB())
}

func (this *FilledCurve) SetBorderColor(color Color) {
this.borderColor = pdf.NewPdfColorDeviceRGB(color.ToRGB())
}

// Draw a circle. Can specify a graphics state (gsName) for setting opacity etc. Otherwise leave empty ("").
// Returns the content stream as a byte array, the bounding box and an error on failure.
func (this *FilledCurve) draw(gsName string) ([]byte, *pdf.PdfRectangle, error) {
bpath := draw.NewCubicBezierPath()
for _, c := range this.curves {
bpath = bpath.AppendCurve(c)
}

creator := pdfcontent.NewContentCreator()
creator.Add_q()

if this.FillEnabled {
creator.Add_rg(this.fillColor.R(), this.fillColor.G(), this.fillColor.B())
}
if this.BorderEnabled {
creator.Add_RG(this.borderColor.R(), this.borderColor.G(), this.borderColor.B())
creator.Add_w(this.BorderWidth)
}
if len(gsName) > 1 {
// If a graphics state is provided, use it. (Used for transparency settings here).
creator.Add_gs(pdfcore.PdfObjectName(gsName))
}

draw.DrawBezierPathWithCreator(bpath, creator)
creator.Add_h() // Close the path.

if this.FillEnabled && this.BorderEnabled {
creator.Add_B() // fill and stroke.
} else if this.FillEnabled {
creator.Add_f() // Fill.
} else if this.BorderEnabled {
creator.Add_S() // Stroke.
}
creator.Add_Q()

// Get bounding box.
pathBbox := bpath.GetBoundingBox()
if this.BorderEnabled {
// Account for stroke width.
pathBbox.Height += this.BorderWidth
pathBbox.Width += this.BorderWidth
pathBbox.X -= this.BorderWidth / 2
pathBbox.Y -= this.BorderWidth / 2
}

// Bounding box - global coordinate system.
bbox := &pdf.PdfRectangle{}
bbox.Llx = pathBbox.X
bbox.Lly = pathBbox.Y
bbox.Urx = pathBbox.X + pathBbox.Width
bbox.Ury = pathBbox.Y + pathBbox.Height
return creator.Bytes(), bbox, nil
}

// GeneratePageBlocks generates page blocks
func (this *FilledCurve) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, error) {
block := NewBlock(ctx.PageWidth, ctx.PageHeight)

contents, _, err := this.draw("")
err = block.addContentsByString(string(contents))
if err != nil {
return nil, ctx, err
}
return []*Block{block}, ctx, nil
}
42 changes: 42 additions & 0 deletions pdf/creator/filled_curve_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package creator

import (
"testing"
"github.com/unidoc/unidoc/pdf/contentstream/draw"
)

const testPdfFileWithFilledCurve = "../../testfiles/filledCurve.pdf"

func CreateFillCurve(x0, y0, x1, y1, x2, y2, x3, y3 float64) draw.CubicBezierCurve {
return draw.NewCubicBezierCurve(x0, y0, x1, y1, x2, y2, x3, y3)
}

func TestNewFilledCurve(t *testing.T) {
filledCurve := NewFilledCurve()
filledCurve.FillEnabled = true
filledCurve.BorderEnabled = true
filledCurve.BorderWidth = 2
filledCurve.SetFillColor(ColorGreen)
filledCurve.SetBorderColor(ColorBlue)

// Up Left
filledCurve.AppendCurve(CreateFillCurve(300, 300, 230, 350, 200, 280, 220, 220))
// Down Left
filledCurve.AppendCurve(CreateFillCurve(225, 240, 240, 180, 260, 160, 300, 180))
// Down Right
filledCurve.AppendCurve(CreateFillCurve(305, 170, 335, 165, 350, 185, 365, 220))
// Up Right
filledCurve.AppendCurve(CreateFillCurve(365, 240, 385, 315, 350, 325, 300, 300))
// Leaf
filledCurve.AppendCurve(CreateFillCurve(300, 300, 290, 350, 295, 370, 300, 390))

creator := New()
creator.NewPage()
creator.Draw(filledCurve)

err := creator.WriteToFile(testPdfFileWithFilledCurve)
if err != nil {
t.Errorf("Fail: %v", err)
return
}
}
Binary file added testfiles/curve.pdf
Binary file not shown.
Binary file added testfiles/filledCurve.pdf
Binary file not shown.

0 comments on commit f82403e

Please sign in to comment.