Merge pull request #237 from adrg/component-creator

Create components through the creator
This commit is contained in:
Gunnsteinn Hall 2018-10-14 15:16:51 +00:00 committed by GitHub
commit 94146c2603
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 731 additions and 526 deletions

View File

@ -8,6 +8,8 @@ package creator
import (
"errors"
"fmt"
"strings"
"unicode"
"github.com/unidoc/unidoc/common"
"github.com/unidoc/unidoc/pdf/contentstream"
@ -391,18 +393,12 @@ func mergeContents(contents *contentstream.ContentStreamOperations, resources *m
if len(op.Params) == 2 {
if name, ok := op.Params[0].(*core.PdfObjectName); ok {
if _, processed := fontMap[*name]; !processed {
var useName core.PdfObjectName
// Process if not already processed.
obj, found := resourcesToAdd.GetFontByName(*name)
if found {
useName = *name
for {
obj2, found := resources.GetFontByName(useName)
if !found || obj2 == obj {
break
}
useName = useName + "0"
}
useName := *name
if found && obj != nil {
useName = resourcesNextUnusedFontName(name.String(), obj, resources)
}
resources.SetFontByName(useName, obj)
@ -548,3 +544,27 @@ func mergeContents(contents *contentstream.ContentStreamOperations, resources *m
return nil
}
func resourcesNextUnusedFontName(name string, font core.PdfObject, resources *model.PdfPageResources) core.PdfObjectName {
prefix := strings.TrimRightFunc(strings.TrimSpace(name), func(r rune) bool {
return unicode.IsNumber(r)
})
if prefix == "" {
prefix = "Font"
}
num := 0
fontName := core.PdfObjectName(name)
for {
f, found := resources.GetFontByName(fontName)
if !found || f == font {
break
}
num++
fontName = core.PdfObjectName(fmt.Sprintf("%s%d", prefix, num))
}
return fontName
}

View File

@ -11,7 +11,6 @@ import (
"strconv"
"github.com/unidoc/unidoc/common"
"github.com/unidoc/unidoc/pdf/model"
)
// Chapter is used to arrange multiple drawables (paragraphs, images, etc) into a single section.
@ -44,30 +43,21 @@ type Chapter struct {
toc *TOC
}
// NewChapter creates a new chapter with the specified title as the heading.
func (c *Creator) NewChapter(title string) *Chapter {
chap := &Chapter{}
c.chapters++
chap.number = c.chapters
chap.title = title
chap.showNumbering = true
chap.includeInTOC = true
heading := fmt.Sprintf("%d. %s", c.chapters, title)
p := NewParagraph(heading)
// newChapter creates a new chapter with the specified title as the heading.
func newChapter(toc *TOC, title string, number int, style TextStyle) *Chapter {
p := newParagraph(fmt.Sprintf("%d. %s", number, title), style)
p.SetFont(style.Font)
p.SetFontSize(16)
helvetica := model.NewStandard14FontMustCompile(model.Helvetica)
p.SetFont(helvetica) // bold?
chap.heading = p
chap.contents = []Drawable{}
// Keep a reference for toc.
chap.toc = c.toc
return chap
return &Chapter{
number: number,
title: title,
showNumbering: true,
includeInTOC: true,
toc: toc,
heading: p,
contents: []Drawable{},
}
}
// SetShowNumbering sets a flag to indicate whether or not to show chapter numbers as part of title.

View File

@ -12,7 +12,8 @@ type PageSize [2]float64
var PPI float64 = 72 // Points per inch. (Default resolution).
// PPMM specifies the default PDF resolution in points/mm.
var PPMM float64 = 72 * 1.0 / 25.4 // Points per mm. (Default resolution).
// Points per mm. (Default resolution).
var PPMM = float64(72 * 1.0 / 25.4)
//
// Commonly used page sizes

View File

@ -7,12 +7,14 @@ package creator
import (
"errors"
goimage "image"
"io"
"os"
"strconv"
"github.com/unidoc/unidoc/common"
"github.com/unidoc/unidoc/pdf/model"
"github.com/unidoc/unidoc/pdf/model/textencoding"
)
// Creator is a wrapper around functionality for creating PDF reports and/or adding new
@ -51,6 +53,13 @@ type Creator struct {
acroForm *model.PdfAcroForm
optimizer model.Optimizer
// Default fonts used by all components instantiated through the creator.
defaultFontRegular *model.PdfFont
defaultFontBold *model.PdfFont
// Default encoder used by all components instantiated through the creator.
defaultTextEncoder textencoding.TextEncoder
}
// SetForms adds an Acroform to a PDF file. Sets the specified form for writing.
@ -103,7 +112,26 @@ func New() *Creator {
c.pageMargins.top = m
c.pageMargins.bottom = m
c.toc = NewTOC("Table of Contents")
// Initialize default text encoder.
c.defaultTextEncoder = textencoding.NewWinAnsiTextEncoder()
// Initialize default fonts.
var err error
c.defaultFontRegular, err = model.NewStandard14Font(model.Helvetica)
if err != nil {
c.defaultFontRegular = model.DefaultFont()
}
c.defaultFontRegular.SetEncoder(c.defaultTextEncoder)
c.defaultFontBold, err = model.NewStandard14Font(model.HelveticaBold)
if err != nil {
c.defaultFontRegular = model.DefaultFont()
}
c.defaultFontBold.SetEncoder(c.defaultTextEncoder)
// Initialize creator table of contents.
c.toc = c.NewTOC("Table of Contents")
return c
}
@ -288,7 +316,7 @@ func (c *Creator) RotateDeg(angleDeg int64) error {
}
// Do the rotation.
var rotation int64 = 0
var rotation int64
if page.Rotate != nil {
rotation = *(page.Rotate)
}
@ -566,3 +594,131 @@ func (c *Creator) WriteToFile(outputPath string) error {
return c.Write(fWrite)
}
/*
Component creation methods.
*/
// NewTextStyle creates a new text style object which can be used to style
// chunks of text.
// Default attributes:
// Font: Helvetica
// Font size: 10
// Encoding: WinAnsiEncoding
// Text color: black
func (c *Creator) NewTextStyle() TextStyle {
return newTextStyle(c.defaultFontRegular)
}
// NewParagraph creates a new text paragraph.
// Default attributes:
// Font: Helvetica,
// Font size: 10
// Encoding: WinAnsiEncoding
// Wrap: enabled
// Text color: black
func (c *Creator) NewParagraph(text string) *Paragraph {
return newParagraph(text, c.NewTextStyle())
}
// NewStyledParagraph creates a new styled paragraph.
// Default attributes:
// Font: Helvetica,
// Font size: 10
// Encoding: WinAnsiEncoding
// Wrap: enabled
// Text color: black
func (c *Creator) NewStyledParagraph() *StyledParagraph {
return newStyledParagraph(c.NewTextStyle())
}
// NewTable create a new Table with a specified number of columns.
func (c *Creator) NewTable(cols int) *Table {
return newTable(cols)
}
// NewDivision returns a new Division container component.
func (c *Creator) NewDivision() *Division {
return newDivision()
}
// NewTOC creates a new table of contents.
func (c *Creator) NewTOC(title string) *TOC {
headingStyle := c.NewTextStyle()
headingStyle.Font = c.defaultFontBold
return newTOC(title, c.NewTextStyle(), headingStyle)
}
// NewTOCLine creates a new table of contents line with the default style.
func (c *Creator) NewTOCLine(number, title, page string, level uint) *TOCLine {
return newTOCLine(number, title, page, level, c.NewTextStyle())
}
// NewStyledTOCLine creates a new table of contents line with the provided style.
func (c *Creator) NewStyledTOCLine(number, title, page TextChunk, level uint, style TextStyle) *TOCLine {
return newStyledTOCLine(number, title, page, level, style)
}
// NewChapter creates a new chapter with the specified title as the heading.
func (c *Creator) NewChapter(title string) *Chapter {
c.chapters++
return newChapter(c.toc, title, c.chapters, c.NewTextStyle())
}
// NewSubchapter creates a new Subchapter under Chapter ch with specified title.
// All other parameters are set to their defaults.
func (c *Creator) NewSubchapter(ch *Chapter, title string) *Subchapter {
return newSubchapter(ch, title, c.NewTextStyle())
}
// NewRectangle creates a new Rectangle with default parameters
// with left corner at (x,y) and width, height as specified.
func (c *Creator) NewRectangle(x, y, width, height float64) *Rectangle {
return newRectangle(x, y, width, height)
}
// NewPageBreak create a new page break.
func (c *Creator) NewPageBreak() *PageBreak {
return newPageBreak()
}
// NewLine creates a new Line with default parameters between (x1,y1) to (x2,y2).
func (c *Creator) NewLine(x1, y1, x2, y2 float64) *Line {
return newLine(x1, y1, x2, y2)
}
// NewFilledCurve returns a instance of filled curve.
func (c *Creator) NewFilledCurve() *FilledCurve {
return newFilledCurve()
}
// NewEllipse creates a new ellipse centered at (xc,yc) with a width and height specified.
func (c *Creator) NewEllipse(xc, yc, width, height float64) *Ellipse {
return newEllipse(xc, yc, width, height)
}
// NewCurve returns new instance of Curve between points (x1,y1) and (x2, y2) with control point (cx,cy).
func (c *Creator) NewCurve(x1, y1, cx, cy, x2, y2 float64) *Curve {
return newCurve(x1, y1, cx, cy, x2, y2)
}
// NewImage create a new image from a unidoc image (model.Image).
func (c *Creator) NewImage(img *model.Image) (*Image, error) {
return newImage(img)
}
// NewImageFromData creates an Image from image data.
func (c *Creator) NewImageFromData(data []byte) (*Image, error) {
return newImageFromData(data)
}
// NewImageFromFile creates an Image from a file.
func (c *Creator) NewImageFromFile(path string) (*Image, error) {
return newImageFromFile(path)
}
// NewImageFromGoImage creates an Image from a go image.Image data structure.
func (c *Creator) NewImageFromGoImage(goimg goimage.Image) (*Image, error) {
return newImageFromGoImage(goimg)
}

File diff suppressed because it is too large Load Diff

View File

@ -12,8 +12,8 @@ import (
"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 {
// 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

View File

@ -10,7 +10,7 @@ import "testing"
func TestNewCurve(t *testing.T) {
creator := New()
creator.NewPage()
curve := NewCurve(20, 20, 15, 35, 40, 150)
curve := creator.NewCurve(20, 20, 15, 35, 40, 150)
curve.SetWidth(3.0)
curve.SetColor(ColorGreen)
err := creator.Draw(curve)
@ -26,15 +26,15 @@ func TestNewCurve(t *testing.T) {
}
}
func CreateCurve(x1, y1, cx, cy, x2, y2 float64, color Color) *Curve {
curve := NewCurve(x1, y1, cx, cy, x2, y2)
func CreateCurve(c *Creator, x1, y1, cx, cy, x2, y2 float64, color Color) *Curve {
curve := c.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)
func CreateLine(c *Creator, x1, y1, x2, y2, width float64) *Line {
line := c.NewLine(x1, y1, x2, y2)
line.SetLineWidth(width)
line.SetColor(ColorRed)
return line
@ -45,23 +45,23 @@ func TestNewCurveWithGlass(t *testing.T) {
creator.NewPage()
// Width 200
creator.Draw(CreateLine(30, 200, 270, 200, 1))
creator.Draw(CreateLine(creator, 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))
creator.Draw(CreateCurve(creator, 50, 200, 75, 145, 150, 150, ColorRed))
creator.Draw(CreateCurve(creator, 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))
creator.Draw(CreateCurve(creator, 50, 200, 75, 245, 150, 250, ColorBlue))
creator.Draw(CreateCurve(creator, 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))
creator.Draw(CreateLine(creator, 50, 200, 51, 400, 1))
creator.Draw(CreateLine(creator, 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))
creator.Draw(CreateCurve(creator, 51, 399, 75, 445, 150, 450, ColorRed))
creator.Draw(CreateCurve(creator, 150, 450, 225, 445, 251, 399, ColorGreen))
err := creator.WriteToFile("/tmp/curve_glass.pdf")
if err != nil {

View File

@ -29,8 +29,8 @@ type Division struct {
inline bool
}
// NewDivision returns a new Division container component.
func NewDivision() *Division {
// newDivision returns a new Division container component.
func newDivision() *Division {
return &Division{
components: []VectorDrawable{},
}

View File

@ -42,7 +42,7 @@ func TestDivVertical(t *testing.T) {
c.NewPage()
// Draw section title.
p := NewParagraph("Regular division component")
p := c.NewParagraph("Regular division component")
p.SetMargins(0, 0, 20, 10)
p.SetFont(fontBold)
@ -52,28 +52,31 @@ func TestDivVertical(t *testing.T) {
}
// Draw division.
div := NewDivision()
div := c.NewDivision()
if div.Inline() {
t.Fatal("Fail: Incorrect inline mode value")
}
p = NewParagraph("Components are stacked vertically ")
p = c.NewParagraph("Components are stacked vertically ")
p.SetFont(fontRegular)
div.Add(p)
p = NewParagraph("but not horizontally")
p = c.NewParagraph("but not horizontally")
p.SetFont(fontBold)
div.Add(p)
// Add styled paragraph
style := NewTextStyle()
style := c.NewTextStyle()
style.Color = ColorRGBFrom8bit(0, 0, 255)
s := NewStyledParagraph("Not even with a styled ", style)
s := c.NewStyledParagraph()
chunk := s.Append("Not even with a styled ")
chunk.Style = style
style.Color = ColorRGBFrom8bit(255, 0, 0)
s.Append("paragraph", style)
chunk = s.Append("paragraph")
chunk.Style = style
div.Add(s)
@ -97,7 +100,7 @@ func TestDivInline(t *testing.T) {
c.NewPage()
// Draw section title.
p := NewParagraph("Inline division component")
p := c.NewParagraph("Inline division component")
p.SetMargins(0, 0, 20, 10)
p.SetFont(fontBold)
@ -107,46 +110,51 @@ func TestDivInline(t *testing.T) {
}
// Draw division.
div := NewDivision()
div := c.NewDivision()
div.SetInline(true)
if !div.Inline() {
t.Fatal("Fail: Incorrect inline mode value")
}
p = NewParagraph("Components are stacked both vertically ")
p = c.NewParagraph("Components are stacked both vertically ")
p.SetEnableWrap(false)
p.SetFont(fontRegular)
div.Add(p)
p = NewParagraph("and horizontally. ")
p = c.NewParagraph("and horizontally. ")
p.SetEnableWrap(false)
p.SetFont(fontBold)
div.Add(p)
p = NewParagraph("Only if they fit right!")
p = c.NewParagraph("Only if they fit right!")
p.SetEnableWrap(false)
p.SetFont(fontRegular)
div.Add(p)
p = NewParagraph("This one did not fit in the available line space. ")
p = c.NewParagraph("This one did not fit in the available line space. ")
p.SetEnableWrap(false)
p.SetFont(fontBold)
div.Add(p)
// Add styled paragraph
style := NewTextStyle()
style := c.NewTextStyle()
style.Color = ColorRGBFrom8bit(0, 0, 255)
s := NewStyledParagraph("This styled paragraph should ", style)
s := c.NewStyledParagraph()
s.SetEnableWrap(false)
chunk := s.Append("This styled paragraph should ")
chunk.Style = style
style.Color = ColorRGBFrom8bit(255, 0, 0)
s.Append("fit", style)
chunk = s.Append("fit")
chunk.Style = style
style.Color = ColorRGBFrom8bit(0, 255, 0)
style.Font = fontBold
s.Append(" in.", style)
chunk = s.Append(" in.")
chunk.Style = style
div.Add(s)
@ -170,7 +178,7 @@ func TestDivNumberMatrix(t *testing.T) {
c.NewPage()
// Draw section title.
p := NewParagraph("A list of numbers in an inline division")
p := c.NewParagraph("A list of numbers in an inline division")
p.SetMargins(0, 0, 20, 10)
p.SetFont(fontBold)
@ -180,7 +188,7 @@ func TestDivNumberMatrix(t *testing.T) {
}
// Draw division.
div := NewDivision()
div := c.NewDivision()
div.SetInline(true)
for i := 0; i < 100; i++ {
@ -188,7 +196,7 @@ func TestDivNumberMatrix(t *testing.T) {
g := byte(seed.Intn(200))
b := byte(seed.Intn(200))
p := NewParagraph(strconv.Itoa(i) + " ")
p := c.NewParagraph(strconv.Itoa(i) + " ")
p.SetEnableWrap(false)
p.SetColor(ColorRGBFrom8bit(r, g, b))
@ -221,7 +229,7 @@ func TestDivRandomSequences(t *testing.T) {
c.NewPage()
// Draw section title.
p := NewParagraph("Inline division of random sequences on multiple pages")
p := c.NewParagraph("Inline division of random sequences on multiple pages")
p.SetMargins(0, 0, 20, 10)
p.SetFont(fontBold)
@ -231,10 +239,10 @@ func TestDivRandomSequences(t *testing.T) {
}
// Draw division.
div := NewDivision()
div := c.NewDivision()
div.SetInline(true)
style := NewTextStyle()
style := c.NewTextStyle()
for i := 0; i < 350; i++ {
r := byte(seed.Intn(200))
@ -245,7 +253,7 @@ func TestDivRandomSequences(t *testing.T) {
fontSize := float64(11 + seed.Intn(3))
if seed.Intn(2)%2 == 0 {
p := NewParagraph(word)
p := c.NewParagraph(word)
p.SetEnableWrap(false)
p.SetColor(ColorRGBFrom8bit(r, g, b))
p.SetFontSize(fontSize)
@ -267,8 +275,12 @@ func TestDivRandomSequences(t *testing.T) {
style.Font = fontRegular
}
s := NewStyledParagraph(word, style)
s := c.NewStyledParagraph()
s.SetEnableWrap(false)
chunk := s.Append(word)
chunk.Style = style
div.Add(s)
}
}
@ -293,7 +305,7 @@ func TestTableDivisions(t *testing.T) {
c.NewPage()
// Draw section title.
p := NewParagraph("Table containing division components")
p := c.NewParagraph("Table containing division components")
p.SetMargins(0, 0, 20, 10)
p.SetFont(fontBold)
@ -302,17 +314,17 @@ func TestTableDivisions(t *testing.T) {
t.Fatalf("Error drawing: %v", err)
}
table := NewTable(2)
table := c.NewTable(2)
table.SetColumnWidths(0.35, 0.65)
// Add regular division to table.
divRegular := NewDivision()
divRegular := c.NewDivision()
p = NewParagraph("Components are stacked vertically ")
p = c.NewParagraph("Components are stacked vertically ")
p.SetFont(fontRegular)
divRegular.Add(p)
p = NewParagraph("but not horizontally")
p = c.NewParagraph("but not horizontally")
p.SetFont(fontBold)
divRegular.Add(p)
@ -321,25 +333,25 @@ func TestTableDivisions(t *testing.T) {
cell.SetContent(divRegular)
// Add inline division to table.
divInline := NewDivision()
divInline := c.NewDivision()
divInline.SetInline(true)
p = NewParagraph("Components are stacked vertically ")
p = c.NewParagraph("Components are stacked vertically ")
p.SetEnableWrap(false)
p.SetFont(fontRegular)
divInline.Add(p)
p = NewParagraph("and horizontally. ")
p = c.NewParagraph("and horizontally. ")
p.SetEnableWrap(false)
p.SetFont(fontBold)
divInline.Add(p)
p = NewParagraph("Only if they fit!")
p = c.NewParagraph("Only if they fit!")
p.SetEnableWrap(false)
p.SetFont(fontRegular)
divInline.Add(p)
p = NewParagraph("This one did not fit in the available line space")
p = c.NewParagraph("This one did not fit in the available line space")
p.SetEnableWrap(false)
p.SetFont(fontBold)
divInline.Add(p)

View File

@ -23,8 +23,8 @@ type Ellipse struct {
borderWidth float64
}
// NewEllipse creates a new ellipse centered at (xc,yc) with a width and height specified.
func NewEllipse(xc, yc, width, height float64) *Ellipse {
// newEllipse creates a new ellipse centered at (xc,yc) with a width and height specified.
func newEllipse(xc, yc, width, height float64) *Ellipse {
ell := &Ellipse{}
ell.xc = xc

View File

@ -22,8 +22,8 @@ type FilledCurve struct {
borderColor *pdf.PdfColorDeviceRGB
}
// NewFilledCurve returns a instance of filled curve.
func NewFilledCurve() *FilledCurve {
// newFilledCurve returns a instance of filled curve.
func newFilledCurve() *FilledCurve {
curve := FilledCurve{}
curve.curves = []draw.CubicBezierCurve{}
return &curve

View File

@ -16,7 +16,9 @@ func CreateFillCurve(x0, y0, x1, y1, x2, y2, x3, y3 float64) draw.CubicBezierCur
}
func TestNewFilledCurve(t *testing.T) {
filledCurve := NewFilledCurve()
creator := New()
filledCurve := creator.NewFilledCurve()
filledCurve.FillEnabled = true
filledCurve.BorderEnabled = true
filledCurve.BorderWidth = 2
@ -34,7 +36,6 @@ func TestNewFilledCurve(t *testing.T) {
// Leaf
filledCurve.AppendCurve(CreateFillCurve(300, 300, 290, 350, 295, 370, 300, 390))
creator := New()
creator.NewPage()
creator.Draw(filledCurve)

View File

@ -52,8 +52,8 @@ type Image struct {
encoder core.StreamEncoder
}
// NewImage create a new image from a unidoc image (model.Image).
func NewImage(img *model.Image) (*Image, error) {
// newImage create a new image from a unidoc image (model.Image).
func newImage(img *model.Image) (*Image, error) {
image := &Image{}
image.img = img
@ -70,8 +70,8 @@ func NewImage(img *model.Image) (*Image, error) {
return image, nil
}
// NewImageFromData creates an Image from image data.
func NewImageFromData(data []byte) (*Image, error) {
// newImageFromData creates an Image from image data.
func newImageFromData(data []byte) (*Image, error) {
imgReader := bytes.NewReader(data)
// Load the image with default handler.
@ -81,17 +81,17 @@ func NewImageFromData(data []byte) (*Image, error) {
return nil, err
}
return NewImage(img)
return newImage(img)
}
// NewImageFromFile creates an Image from a file.
func NewImageFromFile(path string) (*Image, error) {
// newImageFromFile creates an Image from a file.
func newImageFromFile(path string) (*Image, error) {
imgData, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
img, err := NewImageFromData(imgData)
img, err := newImageFromData(imgData)
if err != nil {
return nil, err
}
@ -99,14 +99,14 @@ func NewImageFromFile(path string) (*Image, error) {
return img, nil
}
// NewImageFromGoImage creates an Image from a go image.Image datastructure.
func NewImageFromGoImage(goimg goimage.Image) (*Image, error) {
// newImageFromGoImage creates an Image from a go image.Image data structure.
func newImageFromGoImage(goimg goimage.Image) (*Image, error) {
img, err := model.ImageHandling.NewImageFromGoImage(goimg)
if err != nil {
return nil, err
}
return NewImage(img)
return newImage(img)
}
// SetEncoder sets the encoding/compression mechanism for the image.

View File

@ -24,8 +24,8 @@ type Line struct {
lineWidth float64
}
// NewLine creates a new Line with default parameters between (x1,y1) to (x2,y2).
func NewLine(x1, y1, x2, y2 float64) *Line {
// newLine creates a new Line with default parameters between (x1,y1) to (x2,y2).
func newLine(x1, y1, x2, y2 float64) *Line {
l := &Line{}
l.x1 = x1

View File

@ -4,8 +4,8 @@ package creator
type PageBreak struct {
}
// NewPageBreak create a new page break.
func NewPageBreak() *PageBreak {
// newPageBreak create a new page break.
func newPageBreak() *PageBreak {
return &PageBreak{}
}

View File

@ -66,9 +66,9 @@ type Paragraph struct {
textLines []string
}
// NewParagraph create a new text paragraph. Uses default parameters: Helvetica, WinAnsiEncoding and
// newParagraph create a new text paragraph. Uses default parameters: Helvetica, WinAnsiEncoding and
// wrap enabled with a wrap width of 100 points.
func NewParagraph(text string) *Paragraph {
func newParagraph(text string, style TextStyle) *Paragraph {
p := &Paragraph{}
p.text = text
@ -84,7 +84,6 @@ func NewParagraph(text string) *Paragraph {
p.lineHeight = 1.0
// TODO: Can we wrap intellectually, only if given width is known?
p.enableWrap = true
p.defaultWrap = true
p.SetColor(ColorRGBFrom8bit(0, 0, 0))

View File

@ -23,8 +23,8 @@ type Rectangle struct {
borderWidth float64
}
// NewRectangle creates a new Rectangle with default parameters with left corner at (x,y) and width, height as specified.
func NewRectangle(x, y, width, height float64) *Rectangle {
// newRectangle creates a new Rectangle with default parameters with left corner at (x,y) and width, height as specified.
func newRectangle(x, y, width, height float64) *Rectangle {
rect := &Rectangle{}
rect.x = x

View File

@ -14,21 +14,17 @@ import (
"github.com/unidoc/unidoc/common"
"github.com/unidoc/unidoc/pdf/contentstream"
"github.com/unidoc/unidoc/pdf/core"
"github.com/unidoc/unidoc/pdf/model/textencoding"
)
// StyledParagraph represents text drawn with a specified font and can wrap across lines and pages.
// By default occupies the available width in the drawing context.
type StyledParagraph struct {
// Text chunks with styles that compose the paragraph.
chunks []TextChunk
chunks []*TextChunk
// Style used for the paragraph for spacing and offsets.
defaultStyle TextStyle
// The text encoder which can convert the text (as runes) into a series of glyphs and get character metrics.
encoder textencoding.TextEncoder
// Text alignment: Align left/right/center/justify.
alignment TextAlignment
@ -62,25 +58,18 @@ type StyledParagraph struct {
scaleY float64
// Text chunk lines after wrapping to available width.
lines [][]TextChunk
lines [][]*TextChunk
// Before render callback.
beforeRender func(p *StyledParagraph, ctx DrawContext)
}
// NewStyledParagraph creates a new styled paragraph.
// Uses default parameters: Helvetica, WinAnsiEncoding and wrap enabled
// with a wrap width of 100 points.
func NewStyledParagraph(text string, style TextStyle) *StyledParagraph {
// newStyledParagraph creates a new styled paragraph.
func newStyledParagraph(style TextStyle) *StyledParagraph {
// TODO: Can we wrap intellectually, only if given width is known?
p := &StyledParagraph{
chunks: []TextChunk{
TextChunk{
Text: text,
Style: style,
},
},
defaultStyle: NewTextStyle(),
chunks: []*TextChunk{},
defaultStyle: style,
lineHeight: 1.0,
alignment: TextAlignmentLeft,
enableWrap: true,
@ -91,44 +80,43 @@ func NewStyledParagraph(text string, style TextStyle) *StyledParagraph {
positioning: positionRelative,
}
p.SetEncoder(textencoding.NewWinAnsiTextEncoder())
return p
}
// Append adds a new text chunk with a specified style to the paragraph.
func (p *StyledParagraph) Append(text string, style TextStyle) {
chunk := TextChunk{
// Append adds a new text chunk to the paragraph.
func (p *StyledParagraph) Append(text string) *TextChunk {
chunk := &TextChunk{
Text: text,
Style: style,
Style: p.defaultStyle,
}
chunk.Style.Font.SetEncoder(p.encoder)
p.chunks = append(p.chunks, chunk)
p.wrapText()
return chunk
}
// Insert adds a new text chunk at the specified position in the paragraph.
func (p *StyledParagraph) Insert(index uint, text string, style TextStyle) {
func (p *StyledParagraph) Insert(index uint, text string) *TextChunk {
l := uint(len(p.chunks))
if index > l {
index = l
}
chunk := TextChunk{
chunk := &TextChunk{
Text: text,
Style: style,
Style: p.defaultStyle,
}
chunk.Style.Font.SetEncoder(p.encoder)
p.chunks = append(p.chunks[:index], append([]TextChunk{chunk}, p.chunks[index:]...)...)
p.chunks = append(p.chunks[:index], append([]*TextChunk{chunk}, p.chunks[index:]...)...)
p.wrapText()
return chunk
}
// Reset sets the entire text and also the style of the paragraph
// to those specified. It behaves as if the paragraph was a new one.
func (p *StyledParagraph) Reset(text string, style TextStyle) {
p.chunks = []TextChunk{}
p.Append(text, style)
// Reset removes all the text chunks the paragraph contains.
func (p *StyledParagraph) Reset() {
p.chunks = []*TextChunk{}
}
// SetTextAlignment sets the horizontal alignment of the text within the space provided.
@ -136,18 +124,6 @@ func (p *StyledParagraph) SetTextAlignment(align TextAlignment) {
p.alignment = align
}
// SetEncoder sets the text encoding.
func (p *StyledParagraph) SetEncoder(encoder textencoding.TextEncoder) {
p.encoder = encoder
p.defaultStyle.Font.SetEncoder(encoder)
// Sync with the text font too.
// XXX/FIXME: Keep in 1 place only.
for _, chunk := range p.chunks {
chunk.Style.Font.SetEncoder(encoder)
}
}
// SetLineHeight sets the line height (1.0 default).
func (p *StyledParagraph) SetLineHeight(lineheight float64) {
p.lineHeight = lineheight
@ -230,7 +206,7 @@ func (p *StyledParagraph) getTextWidth() float64 {
style := &chunk.Style
for _, rune := range chunk.Text {
glyph, found := p.encoder.RuneToGlyph(rune)
glyph, found := style.Font.Encoder().RuneToGlyph(rune)
if !found {
common.Log.Debug("Error! Glyph not found for rune: %s\n", rune)
@ -259,13 +235,13 @@ func (p *StyledParagraph) getTextWidth() float64 {
}
// getTextLineWidth calculates the text width of a provided collection of text chunks.
func (p *StyledParagraph) getTextLineWidth(line []TextChunk) float64 {
func (p *StyledParagraph) getTextLineWidth(line []*TextChunk) float64 {
var width float64
for _, chunk := range line {
style := &chunk.Style
for _, r := range chunk.Text {
glyph, found := p.encoder.RuneToGlyph(r)
glyph, found := style.Font.Encoder().RuneToGlyph(r)
if !found {
common.Log.Debug("Error! Glyph not found for rune: %s\n", r)
@ -328,12 +304,12 @@ func (p *StyledParagraph) getTextHeight() float64 {
// XXX/TODO: Consider the Knuth/Plass algorithm or an alternative.
func (p *StyledParagraph) wrapText() error {
if !p.enableWrap || int(p.wrapWidth) <= 0 {
p.lines = [][]TextChunk{p.chunks}
p.lines = [][]*TextChunk{p.chunks}
return nil
}
p.lines = [][]TextChunk{}
var line []TextChunk
p.lines = [][]*TextChunk{}
var line []*TextChunk
var lineWidth float64
for _, chunk := range p.chunks {
@ -344,7 +320,7 @@ func (p *StyledParagraph) wrapText() error {
var widths []float64
for _, r := range chunk.Text {
glyph, found := p.encoder.RuneToGlyph(r)
glyph, found := style.Font.Encoder().RuneToGlyph(r)
if !found {
common.Log.Debug("Error! Glyph not found for rune: %v\n", r)
@ -355,12 +331,12 @@ func (p *StyledParagraph) wrapText() error {
// newline wrapping.
if glyph == "controlLF" {
// moves to next line.
line = append(line, TextChunk{
line = append(line, &TextChunk{
Text: strings.TrimRightFunc(string(part), unicode.IsSpace),
Style: style,
})
p.lines = append(p.lines, line)
line = []TextChunk{}
line = []*TextChunk{}
lineWidth = 0
part = []rune{}
@ -413,12 +389,12 @@ func (p *StyledParagraph) wrapText() error {
widths = []float64{w}
}
line = append(line, TextChunk{
line = append(line, &TextChunk{
Text: strings.TrimRightFunc(string(text), unicode.IsSpace),
Style: style,
})
p.lines = append(p.lines, line)
line = []TextChunk{}
line = []*TextChunk{}
} else {
lineWidth += w
part = append(part, r)
@ -428,7 +404,7 @@ func (p *StyledParagraph) wrapText() error {
}
if len(part) > 0 {
line = append(line, TextChunk{
line = append(line, &TextChunk{
Text: string(part),
Style: style,
})
@ -588,7 +564,7 @@ func drawStyledParagraphOnBlock(blk *Block, p *StyledParagraph, ctx DrawContext)
var chunkSpaces uint
for _, r := range chunk.Text {
glyph, found := p.encoder.RuneToGlyph(r)
glyph, found := style.Font.Encoder().RuneToGlyph(r)
if !found {
common.Log.Debug("Rune 0x%x not supported by text encoder", r)
return ctx, errors.New("Unsupported rune in text encoding")
@ -661,7 +637,7 @@ func drawStyledParagraphOnBlock(blk *Block, p *StyledParagraph, ctx DrawContext)
var encStr []byte
for _, rn := range chunk.Text {
glyph, found := p.encoder.RuneToGlyph(rn)
glyph, found := style.Font.Encoder().RuneToGlyph(rn)
if !found {
common.Log.Debug("Rune 0x%x not supported by text encoder", r)
return ctx, errors.New("Unsupported rune in text encoding")
@ -686,7 +662,7 @@ func drawStyledParagraphOnBlock(blk *Block, p *StyledParagraph, ctx DrawContext)
Add_TL(fontSize * p.lineHeight).
Add_TJ([]core.PdfObject{core.MakeFloat(-spaceWidth)}...)
} else {
encStr = append(encStr, p.encoder.Encode(string(rn))...)
encStr = append(encStr, style.Font.Encoder().Encode(string(rn))...)
}
}

View File

@ -19,7 +19,7 @@ func TestParagraphRegularVsStyled(t *testing.T) {
c.NewPage()
// Draw section title.
p := NewParagraph("Regular paragraph vs styled paragraph (should be identical)")
p := c.NewParagraph("Regular paragraph vs styled paragraph (should be identical)")
p.SetMargins(0, 0, 20, 10)
p.SetFont(fontBold)
@ -28,11 +28,11 @@ func TestParagraphRegularVsStyled(t *testing.T) {
t.Fatalf("Error drawing: %v", err)
}
table := NewTable(2)
table := c.NewTable(2)
table.SetColumnWidths(0.5, 0.5)
// Add regular paragraph to table.
p = NewParagraph("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lacus viverra vitae congue eu consequat. Cras adipiscing enim eu turpis. Lectus magna fringilla urna porttitor. Condimentum id venenatis a condimentum. Quis ipsum suspendisse ultrices gravida dictum fusce. In fermentum posuere urna nec tincidunt. Dis parturient montes nascetur ridiculus mus. Pharetra diam sit amet nisl suscipit adipiscing. Proin fermentum leo vel orci porta. Id diam vel quam elementum pulvinar.")
p = c.NewParagraph("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lacus viverra vitae congue eu consequat. Cras adipiscing enim eu turpis. Lectus magna fringilla urna porttitor. Condimentum id venenatis a condimentum. Quis ipsum suspendisse ultrices gravida dictum fusce. In fermentum posuere urna nec tincidunt. Dis parturient montes nascetur ridiculus mus. Pharetra diam sit amet nisl suscipit adipiscing. Proin fermentum leo vel orci porta. Id diam vel quam elementum pulvinar.")
p.SetMargins(10, 10, 5, 10)
p.SetFont(fontBold)
p.SetEnableWrap(true)
@ -43,20 +43,23 @@ func TestParagraphRegularVsStyled(t *testing.T) {
cell.SetContent(p)
// Add styled paragraph to table.
style := NewTextStyle()
style := c.NewTextStyle()
style.Font = fontBold
s := NewStyledParagraph("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lacus viverra vitae congue eu consequat. Cras adipiscing enim eu turpis. Lectus magna fringilla urna porttitor. Condimentum id venenatis a condimentum. Quis ipsum suspendisse ultrices gravida dictum fusce. In fermentum posuere urna nec tincidunt. Dis parturient montes nascetur ridiculus mus. Pharetra diam sit amet nisl suscipit adipiscing. Proin fermentum leo vel orci porta. Id diam vel quam elementum pulvinar.", style)
s := c.NewStyledParagraph()
s.SetMargins(10, 10, 5, 10)
s.SetEnableWrap(true)
s.SetTextAlignment(TextAlignmentLeft)
chunk := s.Append("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lacus viverra vitae congue eu consequat. Cras adipiscing enim eu turpis. Lectus magna fringilla urna porttitor. Condimentum id venenatis a condimentum. Quis ipsum suspendisse ultrices gravida dictum fusce. In fermentum posuere urna nec tincidunt. Dis parturient montes nascetur ridiculus mus. Pharetra diam sit amet nisl suscipit adipiscing. Proin fermentum leo vel orci porta. Id diam vel quam elementum pulvinar.")
chunk.Style = style
cell = table.NewCell()
cell.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
cell.SetContent(s)
// Add regular paragraph to table.
p = NewParagraph("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lacus viverra vitae congue eu consequat. Cras adipiscing enim eu turpis. Lectus magna fringilla urna porttitor. Condimentum id venenatis a condimentum. Quis ipsum suspendisse ultrices gravida dictum fusce. In fermentum posuere urna nec tincidunt. Dis parturient montes nascetur ridiculus mus. Pharetra diam sit amet nisl suscipit adipiscing. Proin fermentum leo vel orci porta. Id diam vel quam elementum pulvinar.")
p = c.NewParagraph("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lacus viverra vitae congue eu consequat. Cras adipiscing enim eu turpis. Lectus magna fringilla urna porttitor. Condimentum id venenatis a condimentum. Quis ipsum suspendisse ultrices gravida dictum fusce. In fermentum posuere urna nec tincidunt. Dis parturient montes nascetur ridiculus mus. Pharetra diam sit amet nisl suscipit adipiscing. Proin fermentum leo vel orci porta. Id diam vel quam elementum pulvinar.")
p.SetMargins(10, 10, 5, 10)
p.SetFont(fontRegular)
p.SetEnableWrap(true)
@ -71,17 +74,20 @@ func TestParagraphRegularVsStyled(t *testing.T) {
style.Font = fontRegular
style.Color = ColorRGBFrom8bit(0, 0, 255)
s = NewStyledParagraph("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lacus viverra vitae congue eu consequat. Cras adipiscing enim eu turpis. Lectus magna fringilla urna porttitor. Condimentum id venenatis a condimentum. Quis ipsum suspendisse ultrices gravida dictum fusce. In fermentum posuere urna nec tincidunt. Dis parturient montes nascetur ridiculus mus. Pharetra diam sit amet nisl suscipit adipiscing. Proin fermentum leo vel orci porta. Id diam vel quam elementum pulvinar.", style)
s = c.NewStyledParagraph()
s.SetMargins(10, 10, 5, 10)
s.SetEnableWrap(true)
s.SetTextAlignment(TextAlignmentJustify)
chunk = s.Append("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lacus viverra vitae congue eu consequat. Cras adipiscing enim eu turpis. Lectus magna fringilla urna porttitor. Condimentum id venenatis a condimentum. Quis ipsum suspendisse ultrices gravida dictum fusce. In fermentum posuere urna nec tincidunt. Dis parturient montes nascetur ridiculus mus. Pharetra diam sit amet nisl suscipit adipiscing. Proin fermentum leo vel orci porta. Id diam vel quam elementum pulvinar.")
chunk.Style = style
cell = table.NewCell()
cell.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
cell.SetContent(s)
// Add regular paragraph to table.
p = NewParagraph("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lacus viverra vitae congue eu consequat. Cras adipiscing enim eu turpis. Lectus magna fringilla urna porttitor. Condimentum id venenatis a condimentum. Quis ipsum suspendisse ultrices gravida dictum fusce. In fermentum posuere urna nec tincidunt. Dis parturient montes nascetur ridiculus mus. Pharetra diam sit amet nisl suscipit adipiscing. Proin fermentum leo vel orci porta. Id diam vel quam elementum pulvinar.")
p = c.NewParagraph("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lacus viverra vitae congue eu consequat. Cras adipiscing enim eu turpis. Lectus magna fringilla urna porttitor. Condimentum id venenatis a condimentum. Quis ipsum suspendisse ultrices gravida dictum fusce. In fermentum posuere urna nec tincidunt. Dis parturient montes nascetur ridiculus mus. Pharetra diam sit amet nisl suscipit adipiscing. Proin fermentum leo vel orci porta. Id diam vel quam elementum pulvinar.")
p.SetMargins(10, 10, 5, 10)
p.SetFont(fontRegular)
p.SetEnableWrap(true)
@ -95,17 +101,20 @@ func TestParagraphRegularVsStyled(t *testing.T) {
style.Font = fontRegular
style.Color = ColorRGBFrom8bit(0, 0, 0)
s = NewStyledParagraph("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lacus viverra vitae congue eu consequat. Cras adipiscing enim eu turpis. Lectus magna fringilla urna porttitor. Condimentum id venenatis a condimentum. Quis ipsum suspendisse ultrices gravida dictum fusce. In fermentum posuere urna nec tincidunt. Dis parturient montes nascetur ridiculus mus. Pharetra diam sit amet nisl suscipit adipiscing. Proin fermentum leo vel orci porta. Id diam vel quam elementum pulvinar.", style)
s = c.NewStyledParagraph()
s.SetMargins(10, 10, 5, 10)
s.SetEnableWrap(true)
s.SetTextAlignment(TextAlignmentRight)
chunk = s.Append("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lacus viverra vitae congue eu consequat. Cras adipiscing enim eu turpis. Lectus magna fringilla urna porttitor. Condimentum id venenatis a condimentum. Quis ipsum suspendisse ultrices gravida dictum fusce. In fermentum posuere urna nec tincidunt. Dis parturient montes nascetur ridiculus mus. Pharetra diam sit amet nisl suscipit adipiscing. Proin fermentum leo vel orci porta. Id diam vel quam elementum pulvinar.")
chunk.Style = style
cell = table.NewCell()
cell.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
cell.SetContent(s)
// Add regular paragraph to table.
p = NewParagraph("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lacus viverra vitae congue eu consequat. Cras adipiscing enim eu turpis. Lectus magna fringilla urna porttitor. Condimentum id venenatis a condimentum. Quis ipsum suspendisse ultrices gravida dictum fusce. In fermentum posuere urna nec tincidunt. Dis parturient montes nascetur ridiculus mus. Pharetra diam sit amet nisl suscipit adipiscing. Proin fermentum leo vel orci porta. Id diam vel quam elementum pulvinar.")
p = c.NewParagraph("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lacus viverra vitae congue eu consequat. Cras adipiscing enim eu turpis. Lectus magna fringilla urna porttitor. Condimentum id venenatis a condimentum. Quis ipsum suspendisse ultrices gravida dictum fusce. In fermentum posuere urna nec tincidunt. Dis parturient montes nascetur ridiculus mus. Pharetra diam sit amet nisl suscipit adipiscing. Proin fermentum leo vel orci porta. Id diam vel quam elementum pulvinar.")
p.SetMargins(10, 10, 5, 10)
p.SetFont(fontBold)
p.SetEnableWrap(true)
@ -119,20 +128,23 @@ func TestParagraphRegularVsStyled(t *testing.T) {
style.Font = fontBold
style.Color = ColorRGBFrom8bit(0, 0, 0)
s = NewStyledParagraph("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lacus viverra vitae congue eu consequat. Cras adipiscing enim eu turpis. Lectus magna fringilla urna porttitor. Condimentum id venenatis a condimentum. Quis ipsum suspendisse ultrices gravida dictum fusce. In fermentum posuere urna nec tincidunt. Dis parturient montes nascetur ridiculus mus. Pharetra diam sit amet nisl suscipit adipiscing. Proin fermentum leo vel orci porta. Id diam vel quam elementum pulvinar.", style)
s = c.NewStyledParagraph()
s.SetMargins(10, 10, 5, 10)
s.SetEnableWrap(true)
s.SetTextAlignment(TextAlignmentCenter)
chunk = s.Append("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lacus viverra vitae congue eu consequat. Cras adipiscing enim eu turpis. Lectus magna fringilla urna porttitor. Condimentum id venenatis a condimentum. Quis ipsum suspendisse ultrices gravida dictum fusce. In fermentum posuere urna nec tincidunt. Dis parturient montes nascetur ridiculus mus. Pharetra diam sit amet nisl suscipit adipiscing. Proin fermentum leo vel orci porta. Id diam vel quam elementum pulvinar.")
chunk.Style = style
cell = table.NewCell()
cell.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
cell.SetContent(s)
// Test table cell alignment.
style = NewTextStyle()
style = c.NewTextStyle()
// Test left alignment with paragraph wrapping enabled.
p = NewParagraph("Wrap enabled. This text should be left aligned.")
p = c.NewParagraph("Wrap enabled. This text should be left aligned.")
p.SetEnableWrap(true)
cell = table.NewCell()
@ -140,16 +152,19 @@ func TestParagraphRegularVsStyled(t *testing.T) {
cell.SetHorizontalAlignment(CellHorizontalAlignmentLeft)
cell.SetContent(p)
s = NewStyledParagraph("Wrap enabled. This text should be left aligned.", style)
s = c.NewStyledParagraph()
s.SetEnableWrap(true)
chunk = s.Append("Wrap enabled. This text should be left aligned.")
chunk.Style = style
cell = table.NewCell()
cell.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
cell.SetHorizontalAlignment(CellHorizontalAlignmentLeft)
cell.SetContent(s)
// Test left alignment with paragraph wrapping disabled.
p = NewParagraph("Wrap disabled. This text should be left aligned.")
p = c.NewParagraph("Wrap disabled. This text should be left aligned.")
p.SetEnableWrap(false)
cell = table.NewCell()
@ -157,16 +172,19 @@ func TestParagraphRegularVsStyled(t *testing.T) {
cell.SetHorizontalAlignment(CellHorizontalAlignmentLeft)
cell.SetContent(p)
s = NewStyledParagraph("Wrap disabled. This text should be left aligned.", style)
s = c.NewStyledParagraph()
s.SetEnableWrap(false)
chunk = s.Append("Wrap disabled. This text should be left aligned.")
chunk.Style = style
cell = table.NewCell()
cell.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
cell.SetHorizontalAlignment(CellHorizontalAlignmentLeft)
cell.SetContent(s)
// Test center alignment with paragraph wrapping enabled.
p = NewParagraph("Wrap enabled. This text should be center aligned.")
p = c.NewParagraph("Wrap enabled. This text should be center aligned.")
p.SetEnableWrap(true)
cell = table.NewCell()
@ -174,16 +192,19 @@ func TestParagraphRegularVsStyled(t *testing.T) {
cell.SetHorizontalAlignment(CellHorizontalAlignmentCenter)
cell.SetContent(p)
s = NewStyledParagraph("Wrap enabled. This text should be center aligned.", style)
s = c.NewStyledParagraph()
s.SetEnableWrap(true)
chunk = s.Append("Wrap enabled. This text should be center aligned.")
chunk.Style = style
cell = table.NewCell()
cell.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
cell.SetHorizontalAlignment(CellHorizontalAlignmentCenter)
cell.SetContent(s)
// Test center alignment with paragraph wrapping disabled.
p = NewParagraph("Wrap disabled. This text should be center aligned.")
p = c.NewParagraph("Wrap disabled. This text should be center aligned.")
p.SetEnableWrap(false)
cell = table.NewCell()
@ -191,16 +212,19 @@ func TestParagraphRegularVsStyled(t *testing.T) {
cell.SetHorizontalAlignment(CellHorizontalAlignmentCenter)
cell.SetContent(p)
s = NewStyledParagraph("Wrap disabled. This text should be center aligned.", style)
s = c.NewStyledParagraph()
s.SetEnableWrap(false)
chunk = s.Append("Wrap disabled. This text should be center aligned.")
chunk.Style = style
cell = table.NewCell()
cell.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
cell.SetHorizontalAlignment(CellHorizontalAlignmentCenter)
cell.SetContent(s)
// Test right alignment with paragraph wrapping enabled.
p = NewParagraph("Wrap enabled. This text should be right aligned.")
p = c.NewParagraph("Wrap enabled. This text should be right aligned.")
p.SetEnableWrap(true)
cell = table.NewCell()
@ -208,16 +232,19 @@ func TestParagraphRegularVsStyled(t *testing.T) {
cell.SetHorizontalAlignment(CellHorizontalAlignmentRight)
cell.SetContent(p)
s = NewStyledParagraph("Wrap enabled. This text should be right aligned.", style)
s = c.NewStyledParagraph()
s.SetEnableWrap(true)
chunk = s.Append("Wrap enabled. This text should be right aligned.")
chunk.Style = style
cell = table.NewCell()
cell.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
cell.SetHorizontalAlignment(CellHorizontalAlignmentRight)
cell.SetContent(s)
// Test right alignment with paragraph wrapping disabled.
p = NewParagraph("Wrap disabled. This text should be right aligned.")
p = c.NewParagraph("Wrap disabled. This text should be right aligned.")
p.SetEnableWrap(false)
cell = table.NewCell()
@ -225,9 +252,12 @@ func TestParagraphRegularVsStyled(t *testing.T) {
cell.SetHorizontalAlignment(CellHorizontalAlignmentRight)
cell.SetContent(p)
s = NewStyledParagraph("Wrap disabled. This text should be right aligned.", style)
s = c.NewStyledParagraph()
s.SetEnableWrap(false)
chunk = s.Append("Wrap disabled. This text should be right aligned.")
chunk.Style = style
cell = table.NewCell()
cell.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
cell.SetHorizontalAlignment(CellHorizontalAlignmentRight)
@ -255,7 +285,7 @@ func TestStyledParagraph(t *testing.T) {
c.NewPage()
// Draw section title.
p := NewParagraph("Styled paragraph")
p := c.NewParagraph("Styled paragraph")
p.SetMargins(0, 0, 20, 10)
p.SetFont(fontBold)
@ -264,38 +294,47 @@ func TestStyledParagraph(t *testing.T) {
t.Fatalf("Error drawing: %v", err)
}
style := NewTextStyle()
style := c.NewTextStyle()
style.Font = fontRegular
s := NewStyledParagraph("This is a paragraph ", style)
s := c.NewStyledParagraph()
s.SetEnableWrap(true)
s.SetTextAlignment(TextAlignmentJustify)
s.SetMargins(0, 0, 10, 0)
chunk := s.Append("This is a paragraph ")
chunk.Style = style
style.Color = ColorRGBFrom8bit(255, 0, 0)
s.Append("with different colors ", style)
chunk = s.Append("with different colors ")
chunk.Style = style
style.Color = ColorRGBFrom8bit(0, 0, 0)
style.FontSize = 14
s.Append("and with different font sizes ", style)
chunk = s.Append("and with different font sizes ")
chunk.Style = style
style.FontSize = 10
style.Font = fontBold
s.Append("and with different font styles ", style)
chunk = s.Append("and with different font styles ")
chunk.Style = style
style.Font = fontHelvetica
style.FontSize = 13
s.Append("and with different fonts ", style)
chunk = s.Append("and with different fonts ")
chunk.Style = style
style.Font = fontBold
style.Color = ColorRGBFrom8bit(0, 0, 255)
style.FontSize = 15
s.Append("and with the changed properties all at once. ", style)
chunk = s.Append("and with the changed properties all at once. ")
chunk.Style = style
style.Color = ColorRGBFrom8bit(127, 255, 0)
style.FontSize = 12
style.Font = fontHelvetica
s.Append("And maybe try a different color again.", style)
chunk = s.Append("And maybe try a different color again.")
chunk.Style = style
err = c.Draw(s)
if err != nil {
@ -306,13 +345,17 @@ func TestStyledParagraph(t *testing.T) {
style.Color = ColorRGBFrom8bit(255, 0, 0)
style.Font = fontRegular
s.Reset("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lacus viverra vitae congue eu consequat. Cras adipiscing enim eu turpis. Lectus magna fringilla urna porttitor. Condimentum id venenatis a condimentum. Quis ipsum suspendisse ultrices gravida dictum fusce. In fermentum posuere urna nec tincidunt. Dis parturient montes nascetur ridiculus mus. Pharetra diam sit amet nisl suscipit adipiscing. Proin fermentum leo vel orci porta. Id diam vel quam elementum pulvinar. ", style)
s.Reset()
s.SetTextAlignment(TextAlignmentJustify)
chunk = s.Append("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lacus viverra vitae congue eu consequat. Cras adipiscing enim eu turpis. Lectus magna fringilla urna porttitor. Condimentum id venenatis a condimentum. Quis ipsum suspendisse ultrices gravida dictum fusce. In fermentum posuere urna nec tincidunt. Dis parturient montes nascetur ridiculus mus. Pharetra diam sit amet nisl suscipit adipiscing. Proin fermentum leo vel orci porta. Id diam vel quam elementum pulvinar. ")
chunk.Style = style
style.Color = ColorRGBFrom8bit(0, 0, 255)
style.FontSize = 15
style.Font = fontHelvetica
s.Append("And maybe try a different color again.", style)
chunk = s.Append("And maybe try a different color again.")
chunk.Style = style
err = c.Draw(s)
if err != nil {

View File

@ -10,7 +10,6 @@ import (
"strconv"
"github.com/unidoc/unidoc/common"
"github.com/unidoc/unidoc/pdf/model"
)
// Subchapter simply represents a sub chapter pertaining to a specific Chapter. It can contain
@ -42,37 +41,30 @@ type Subchapter struct {
toc *TOC
}
// NewSubchapter creates a new Subchapter under Chapter ch with specified title.
// newSubchapter creates a new Subchapter under Chapter ch with specified title.
// All other parameters are set to their defaults.
func (c *Creator) NewSubchapter(ch *Chapter, title string) *Subchapter {
subchap := &Subchapter{}
func newSubchapter(ch *Chapter, title string, style TextStyle) *Subchapter {
ch.subchapters++
subchap.subchapterNum = ch.subchapters
subchap.chapterNum = ch.number
subchap.title = title
heading := fmt.Sprintf("%d.%d %s", subchap.chapterNum, subchap.subchapterNum, title)
p := NewParagraph(heading)
p := newParagraph(fmt.Sprintf("%d.%d %s", ch.number, ch.subchapters, title), style)
p.SetFont(style.Font) // bold?
p.SetFontSize(14)
helvetica := model.NewStandard14FontMustCompile(model.Helvetica)
p.SetFont(helvetica) // bold?
subchap.showNumbering = true
subchap.includeInTOC = true
subchapter := &Subchapter{
subchapterNum: ch.subchapters,
chapterNum: ch.number,
title: title,
showNumbering: true,
includeInTOC: true,
heading: p,
contents: []Drawable{},
toc: ch.toc,
}
subchap.heading = p
subchap.contents = []Drawable{}
// Add subchapter to chapter.
ch.Add(subchapter)
// Add subchapter to ch.
ch.Add(subchap)
// Keep a reference for toc.
subchap.toc = c.toc
return subchap
return subchapter
}
// SetShowNumbering sets a flag to indicate whether or not to show chapter numbers as part of title.

View File

@ -47,8 +47,8 @@ type Table struct {
margins margins
}
// NewTable create a new Table with a specified number of columns.
func NewTable(cols int) *Table {
// newTable create a new Table with a specified number of columns.
func newTable(cols int) *Table {
t := &Table{}
t.rows = 0
t.cols = cols
@ -454,12 +454,19 @@ const (
type CellBorderSide int
const (
// Left side border.
// CellBorderSideLeft adds border on the left side of the table.
CellBorderSideLeft CellBorderSide = iota
// CellBorderSideRight adds a border on the right side of the table.
CellBorderSideRight
// CellBorderSideTop adds a border on the top side of the table.
CellBorderSideTop
// CellBorderSideBottom adds a border on the bottom side of the table.
CellBorderSideBottom
// Border on all sides.
// CellBorderSideAll adds borders on all sides of the table.
CellBorderSideAll
)

View File

@ -21,7 +21,7 @@ var (
func TestTableMultiParagraphWrapped(t *testing.T) {
c := New()
pageHistoryTable := NewTable(4)
pageHistoryTable := c.NewTable(4)
pageHistoryTable.SetColumnWidths(0.1, 0.6, 0.15, 0.15)
content := [][]string{
{"1", "FullText Search Highlight the Term in Results \n\nissues 60", "120", "130"},
@ -33,7 +33,7 @@ func TestTableMultiParagraphWrapped(t *testing.T) {
for _, rows := range content {
for _, txt := range rows {
p := NewParagraph(txt)
p := c.NewParagraph(txt)
p.SetFontSize(12)
p.SetFont(fontHelvetica)
p.SetColor(ColorBlack)
@ -59,7 +59,7 @@ func TestTableMultiParagraphWrapped(t *testing.T) {
func TestTableWithImage(t *testing.T) {
c := New()
pageHistoryTable := NewTable(4)
pageHistoryTable := c.NewTable(4)
pageHistoryTable.SetColumnWidths(0.1, 0.6, 0.15, 0.15)
content := [][]string{
{"1", "FullText Search Highlight the Term in Results \n\nissues 60", "120", "130"},
@ -70,7 +70,7 @@ func TestTableWithImage(t *testing.T) {
}
for _, rows := range content {
for _, txt := range rows {
p := NewParagraph(txt)
p := c.NewParagraph(txt)
p.SetFontSize(12)
p.SetFont(fontHelvetica)
p.SetColor(ColorBlack)
@ -93,7 +93,7 @@ func TestTableWithImage(t *testing.T) {
t.Errorf("Fail: %v\n", err)
return
}
img, err := NewImageFromData(imgData)
img, err := c.NewImageFromData(imgData)
if err != nil {
t.Errorf("Fail: %v\n", err)
return
@ -124,7 +124,7 @@ func TestTableWithImage(t *testing.T) {
func TestTableWithDiv(t *testing.T) {
c := New()
pageHistoryTable := NewTable(4)
pageHistoryTable := c.NewTable(4)
pageHistoryTable.SetColumnWidths(0.1, 0.6, 0.15, 0.15)
headings := []string{
@ -155,7 +155,7 @@ func TestTableWithDiv(t *testing.T) {
}
for _, rows := range content {
for colIdx, txt := range rows {
p := NewParagraph(txt)
p := c.NewParagraph(txt)
p.SetFontSize(12)
p.SetFont(fontHelvetica)
p.SetColor(ColorBlack)
@ -167,10 +167,10 @@ func TestTableWithDiv(t *testing.T) {
}
// Place cell contents (header and text) inside a div.
div := NewDivision()
div := c.NewDivision()
if len(headings[colIdx]) > 0 {
heading := NewParagraph(headings[colIdx])
heading := c.NewParagraph(headings[colIdx])
heading.SetFontSize(14)
heading.SetFont(fontHelveticaBold)
heading.SetColor(ColorRed)
@ -203,7 +203,7 @@ func TestTableWithDiv(t *testing.T) {
t.Errorf("Fail: %v\n", err)
return
}
img, err := NewImageFromData(imgData)
img, err := c.NewImageFromData(imgData)
if err != nil {
t.Errorf("Fail: %v\n", err)
return

View File

@ -6,8 +6,7 @@
package creator
import (
"github.com/unidoc/unidoc/pdf/model/fonts"
"github.com/unidoc/unidoc/pdf/model/textencoding"
"github.com/unidoc/unidoc/pdf/model"
)
// TextStyle is a collection of properties that can be assigned to a chunk of text.
@ -16,19 +15,14 @@ type TextStyle struct {
Color Color
// The font the text will use.
Font fonts.Font
Font *model.PdfFont
// The size of the font.
FontSize float64
}
// NewTextStyle creates a new text style object which can be used with chunks
// of text. Uses default parameters: Helvetica, WinAnsiEncoding and wrap
// enabled with a wrap width of 100 points.
func NewTextStyle() TextStyle {
font := fonts.NewFontHelvetica()
font.SetEncoder(textencoding.NewWinAnsiTextEncoder())
// newTextStyle creates a new text style object using the specified font.
func newTextStyle(font *model.PdfFont) TextStyle {
return TextStyle{
Color: ColorRGBFrom8bit(0, 0, 0),
Font: font,

View File

@ -5,8 +5,6 @@
package creator
import "github.com/unidoc/unidoc/pdf/model/fonts"
// TOC represents a table of contents component.
// It consists of a paragraph heading and a collection of
// table of contents lines.
@ -43,32 +41,36 @@ type TOC struct {
// Positioning: relative/absolute.
positioning positioning
// Default style used for internal operations.
defaultStyle TextStyle
}
// NewTOC creates a new table of contents.
func NewTOC(title string) *TOC {
headingStyle := NewTextStyle()
headingStyle.Font = fonts.NewFontHelveticaBold()
// newTOC creates a new table of contents.
func newTOC(title string, style, styleHeading TextStyle) *TOC {
headingStyle := styleHeading
headingStyle.FontSize = 14
heading := NewStyledParagraph(title, headingStyle)
heading := newStyledParagraph(headingStyle)
heading.SetEnableWrap(true)
heading.SetTextAlignment(TextAlignmentLeft)
heading.SetMargins(0, 0, 0, 5)
lineStyle := NewTextStyle()
chunk := heading.Append(title)
chunk.Style = headingStyle
return &TOC{
heading: heading,
lines: []*TOCLine{},
lineNumberStyle: lineStyle,
lineTitleStyle: lineStyle,
lineSeparatorStyle: lineStyle,
linePageStyle: lineStyle,
lineNumberStyle: style,
lineTitleStyle: style,
lineSeparatorStyle: style,
linePageStyle: style,
lineSeparator: ".",
lineLevelOffset: 10,
lineMargins: margins{0, 0, 2, 2},
positioning: positionRelative,
defaultStyle: style,
}
}
@ -84,12 +86,16 @@ func (t *TOC) Lines() []*TOCLine {
// SetHeading sets the text and the style of the heading of the TOC component.
func (t *TOC) SetHeading(text string, style TextStyle) {
t.heading.Reset(text, style)
h := t.Heading()
h.Reset()
chunk := h.Append(text)
chunk.Style = style
}
// Add adds a new line with the default style to the table of contents.
func (t *TOC) Add(number, title, page string, level uint) *TOCLine {
tl := t.AddLine(NewStyledTOCLine(
tl := t.AddLine(newStyledTOCLine(
TextChunk{
Text: number,
Style: t.lineNumberStyle,
@ -103,6 +109,7 @@ func (t *TOC) Add(number, title, page string, level uint) *TOCLine {
Style: t.linePageStyle,
},
level,
t.defaultStyle,
))
if tl == nil {

View File

@ -44,11 +44,9 @@ type TOCLine struct {
positioning positioning
}
// NewTOCLine creates a new table of contents line with the default style.
func NewTOCLine(number, title, page string, level uint) *TOCLine {
style := NewTextStyle()
return NewStyledTOCLine(
// newTOCLine creates a new table of contents line with the default style.
func newTOCLine(number, title, page string, level uint, style TextStyle) *TOCLine {
return newStyledTOCLine(
TextChunk{
Text: number,
Style: style,
@ -62,14 +60,13 @@ func NewTOCLine(number, title, page string, level uint) *TOCLine {
Style: style,
},
level,
style,
)
}
// NewStyledTOCLine creates a new table of contents line with the provided style.
func NewStyledTOCLine(number, title, page TextChunk, level uint) *TOCLine {
style := NewTextStyle()
sp := NewStyledParagraph("", style)
// newStyledTOCLine creates a new table of contents line with the provided style.
func newStyledTOCLine(number, title, page TextChunk, level uint, style TextStyle) *TOCLine {
sp := newStyledParagraph(style)
sp.SetEnableWrap(true)
sp.SetTextAlignment(TextAlignmentLeft)
sp.SetMargins(0, 0, 2, 2)
@ -157,19 +154,18 @@ func (tl *TOCLine) prepareParagraph(sp *StyledParagraph, ctx DrawContext) {
page = " " + page
}
sp.chunks = []TextChunk{
tl.Number,
TextChunk{
sp.chunks = []*TextChunk{
&tl.Number,
&TextChunk{
Text: title,
Style: tl.Title.Style,
},
TextChunk{
&TextChunk{
Text: page,
Style: tl.Page.Style,
},
}
sp.SetEncoder(sp.encoder)
sp.wrapText()
// Insert separator.
@ -179,12 +175,13 @@ func (tl *TOCLine) prepareParagraph(sp *StyledParagraph, ctx DrawContext) {
}
availWidth := ctx.Width*1000 - sp.getTextLineWidth(sp.lines[l-1])
sepWidth := sp.getTextLineWidth([]TextChunk{tl.Separator})
sepWidth := sp.getTextLineWidth([]*TextChunk{&tl.Separator})
sepCount := int(availWidth / sepWidth)
sepText := strings.Repeat(tl.Separator.Text, sepCount)
sepStyle := tl.Separator.Style
sp.Insert(2, sepText, sepStyle)
chunk := sp.Insert(2, sepText)
chunk.Style = sepStyle
// Push page numbers to the end of the line.
availWidth = availWidth - float64(sepCount)*sepWidth
@ -195,7 +192,9 @@ func (tl *TOCLine) prepareParagraph(sp *StyledParagraph, ctx DrawContext) {
if spaces > 0 {
style := sepStyle
style.FontSize = 1
sp.Insert(2, strings.Repeat(" ", spaces), style)
chunk = sp.Insert(2, strings.Repeat(" ", spaces))
chunk.Style = style
}
}
}

View File

@ -7,17 +7,17 @@ package creator
import (
"testing"
"github.com/unidoc/unidoc/pdf/model/fonts"
"github.com/unidoc/unidoc/pdf/model"
)
func TestTOCAdvanced(t *testing.T) {
fontHelvetica := fonts.NewFontHelvetica()
fontHelveticaBold := fonts.NewFontHelveticaBold()
fontHelvetica := model.NewStandard14FontMustCompile(model.Helvetica)
fontHelveticaBold := model.NewStandard14FontMustCompile(model.HelveticaBold)
c := New()
c.NewPage()
toc := NewTOC("Table of Contents")
toc := c.NewTOC("Table of Contents")
// Set separator and margins for all the lines.
toc.SetLineSeparator(".")
@ -25,7 +25,7 @@ func TestTOCAdvanced(t *testing.T) {
toc.SetLineLevelOffset(12)
// Set style for all line numbers.
style := NewTextStyle()
style := c.NewTextStyle()
style.Font = fontHelveticaBold
style.Color = ColorRGBFrom8bit(100, 100, 100)
toc.SetLineNumberStyle(style)