mirror of
https://github.com/unidoc/unipdf.git
synced 2025-05-09 19:29:34 +08:00
Merge branch 's4kibs4mi-curve-shape'
This commit is contained in:
commit
fc8925b918
71
pdf/creator/curve.go
Normal file
71
pdf/creator/curve.go
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* This file is subject to the terms and conditions defined in
|
||||
* file 'LICENSE.md', which is part of this source code package.
|
||||
*/
|
||||
|
||||
package creator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/unidoc/unidoc/pdf/model"
|
||||
)
|
||||
|
||||
// NewCurve returns new instance of Curve between points (x1,y1) and (x2, y2) with control point (cx,cy).
|
||||
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
|
||||
}
|
||||
|
||||
// Curve represents a cubic Bezier curve with a control point.
|
||||
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 draws the curve onto 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
|
||||
}
|
71
pdf/creator/curve_test.go
Normal file
71
pdf/creator/curve_test.go
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* This file is subject to the terms and conditions defined in
|
||||
* file 'LICENSE.md', which is part of this source code package.
|
||||
*/
|
||||
|
||||
package creator
|
||||
|
||||
import "testing"
|
||||
|
||||
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("/tmp/curve.pdf")
|
||||
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("/tmp/curve_glass.pdf")
|
||||
if err != nil {
|
||||
t.Errorf("Fail: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
112
pdf/creator/filled_curve.go
Normal file
112
pdf/creator/filled_curve.go
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* This file is subject to the terms and conditions defined in
|
||||
* file 'LICENSE.md', which is part of this source code package.
|
||||
*/
|
||||
|
||||
package creator
|
||||
|
||||
import (
|
||||
pdfcontent "github.com/unidoc/unidoc/pdf/contentstream"
|
||||
"github.com/unidoc/unidoc/pdf/contentstream/draw"
|
||||
pdfcore "github.com/unidoc/unidoc/pdf/core"
|
||||
pdf "github.com/unidoc/unidoc/pdf/model"
|
||||
)
|
||||
|
||||
// FilledCurve represents a closed path of Bezier curves with a border and fill.
|
||||
type FilledCurve struct {
|
||||
curves []draw.CubicBezierCurve
|
||||
FillEnabled bool // Show fill?
|
||||
fillColor *pdf.PdfColorDeviceRGB
|
||||
BorderEnabled bool // Show border?
|
||||
BorderWidth float64
|
||||
borderColor *pdf.PdfColorDeviceRGB
|
||||
}
|
||||
|
||||
// NewFilledCurve returns a instance of filled curve.
|
||||
func NewFilledCurve() *FilledCurve {
|
||||
curve := FilledCurve{}
|
||||
curve.curves = []draw.CubicBezierCurve{}
|
||||
return &curve
|
||||
}
|
||||
|
||||
// AppendCurve appends a Bezier curve to the filled curve.
|
||||
func (this *FilledCurve) AppendCurve(curve draw.CubicBezierCurve) *FilledCurve {
|
||||
this.curves = append(this.curves, curve)
|
||||
return this
|
||||
}
|
||||
|
||||
// SetFillColor sets the fill color for the path.
|
||||
func (this *FilledCurve) SetFillColor(color Color) {
|
||||
this.fillColor = pdf.NewPdfColorDeviceRGB(color.ToRGB())
|
||||
}
|
||||
|
||||
// SetBorderColor sets the border color for the path.
|
||||
func (this *FilledCurve) SetBorderColor(color Color) {
|
||||
this.borderColor = pdf.NewPdfColorDeviceRGB(color.ToRGB())
|
||||
}
|
||||
|
||||
// draw draws the filled curve. 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. (can support transparency).
|
||||
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 draws the filled curve on 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
|
||||
}
|
46
pdf/creator/filled_curve_test.go
Normal file
46
pdf/creator/filled_curve_test.go
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* This file is subject to the terms and conditions defined in
|
||||
* file 'LICENSE.md', which is part of this source code package.
|
||||
*/
|
||||
|
||||
package creator
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/unidoc/unidoc/pdf/contentstream/draw"
|
||||
)
|
||||
|
||||
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("/tmp/filledCurve.pdf")
|
||||
if err != nil {
|
||||
t.Errorf("Fail: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user