Merge pull request #185 from peterwilliams97/render.v3.hungarian

Render.v3.hungarian
This commit is contained in:
Gunnsteinn Hall 2018-09-03 10:55:20 +00:00 committed by GitHub
commit 688ded7dae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 26113 additions and 10808 deletions

View File

@ -144,11 +144,19 @@ func MakeFloat(val float64) *PdfObjectFloat {
} }
// MakeString creates an PdfObjectString from a string. // MakeString creates an PdfObjectString from a string.
// NOTE: PDF does not use utf-8 string encoding like Go so `s` will often not be a utf-8 encoded
// string.
func MakeString(s string) *PdfObjectString { func MakeString(s string) *PdfObjectString {
str := PdfObjectString{val: s} str := PdfObjectString{val: s}
return &str return &str
} }
// MakeStringFromBytes creates an PdfObjectString from a byte array.
// This is more natural than MakeString as `data` is usually not utf-8 encoded.
func MakeStringFromBytes(data []byte) *PdfObjectString {
return MakeString(string(data))
}
// MakeHexString creates an PdfObjectString from a string intended for output as a hexadecimal string. // MakeHexString creates an PdfObjectString from a string intended for output as a hexadecimal string.
func MakeHexString(s string) *PdfObjectString { func MakeHexString(s string) *PdfObjectString {
str := PdfObjectString{val: s, isHex: true} str := PdfObjectString{val: s, isHex: true}

View File

@ -51,8 +51,8 @@ func NewBlock(width float64, height float64) *Block {
return b return b
} }
// NewBlockFromPage creates a Block from a PDF Page. Useful for loading template pages as blocks from a PDF document // NewBlockFromPage creates a Block from a PDF Page. Useful for loading template pages as blocks
// and additional content with the creator. // from a PDF document and additional content with the creator.
func NewBlockFromPage(page *model.PdfPage) (*Block, error) { func NewBlockFromPage(page *model.PdfPage) (*Block, error) {
b := &Block{} b := &Block{}

View File

@ -10,11 +10,11 @@ import (
"fmt" "fmt"
"github.com/unidoc/unidoc/common" "github.com/unidoc/unidoc/common"
"github.com/unidoc/unidoc/pdf/model/fonts" "github.com/unidoc/unidoc/pdf/model"
) )
// Chapter is used to arrange multiple drawables (paragraphs, images, etc) into a single section. The concept is // Chapter is used to arrange multiple drawables (paragraphs, images, etc) into a single section.
// the same as a book or a report chapter. // The concept is the same as a book or a report chapter.
type Chapter struct { type Chapter struct {
number int number int
title string title string
@ -57,7 +57,8 @@ func (c *Creator) NewChapter(title string) *Chapter {
heading := fmt.Sprintf("%d. %s", c.chapters, title) heading := fmt.Sprintf("%d. %s", c.chapters, title)
p := NewParagraph(heading) p := NewParagraph(heading)
p.SetFontSize(16) p.SetFontSize(16)
p.SetFont(fonts.NewFontHelvetica()) // bold? helvetica, _ := model.NewStandard14Font("Helvetica")
p.SetFont(helvetica) // bold?
chap.heading = p chap.heading = p
chap.contents = []Drawable{} chap.contents = []Drawable{}
@ -113,7 +114,7 @@ func (chap *Chapter) Add(d Drawable) error {
switch d.(type) { switch d.(type) {
case *Chapter: case *Chapter:
common.Log.Debug("Error: Cannot add chapter to a chapter") common.Log.Debug("ERROR: Cannot add chapter to a chapter")
return errors.New("Type check error") return errors.New("Type check error")
case *Paragraph, *Image, *Block, *Subchapter, *Table, *PageBreak: case *Paragraph, *Image, *Block, *Subchapter, *Table, *PageBreak:
chap.contents = append(chap.contents, d) chap.contents = append(chap.contents, d)
@ -125,8 +126,8 @@ func (chap *Chapter) Add(d Drawable) error {
return nil return nil
} }
// GeneratePageBlocks generate the Page blocks. Multiple blocks are generated if the contents wrap over // GeneratePageBlocks generate the Page blocks. Multiple blocks are generated if the contents wrap
// multiple pages. // over multiple pages.
func (chap *Chapter) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, error) { func (chap *Chapter) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, error) {
origCtx := ctx origCtx := ctx
@ -175,7 +176,6 @@ func (chap *Chapter) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext,
if chap.positioning.isAbsolute() { if chap.positioning.isAbsolute() {
// If absolute: return original context. // If absolute: return original context.
return blocks, origCtx, nil return blocks, origCtx, nil
} }
return blocks, ctx, nil return blocks, ctx, nil

View File

@ -46,28 +46,31 @@ type Creator struct {
acroForm *model.PdfAcroForm acroForm *model.PdfAcroForm
} }
// SetForms Add Acroforms to a PDF file. Sets the specified form for writing. // SetForms adds an Acroform to a PDF file. Sets the specified form for writing.
func (c *Creator) SetForms(form *model.PdfAcroForm) error { func (c *Creator) SetForms(form *model.PdfAcroForm) error {
c.acroForm = form c.acroForm = form
return nil return nil
} }
// FrontpageFunctionArgs holds the input arguments to a front page drawing function. // FrontpageFunctionArgs holds the input arguments to a front page drawing function.
// It is designed as a struct, so additional parameters can be added in the future with backwards compatibility. // It is designed as a struct, so additional parameters can be added in the future with backwards
// compatibility.
type FrontpageFunctionArgs struct { type FrontpageFunctionArgs struct {
PageNum int PageNum int
TotalPages int TotalPages int
} }
// HeaderFunctionArgs holds the input arguments to a header drawing function. // HeaderFunctionArgs holds the input arguments to a header drawing function.
// It is designed as a struct, so additional parameters can be added in the future with backwards compatibility. // It is designed as a struct, so additional parameters can be added in the future with backwards
// compatibility.
type HeaderFunctionArgs struct { type HeaderFunctionArgs struct {
PageNum int PageNum int
TotalPages int TotalPages int
} }
// FooterFunctionArgs holds the input arguments to a footer drawing function. // FooterFunctionArgs holds the input arguments to a footer drawing function.
// It is designed as a struct, so additional parameters can be added in the future with backwards compatibility. // It is designed as a struct, so additional parameters can be added in the future with backwards
// compatibility.
type FooterFunctionArgs struct { type FooterFunctionArgs struct {
PageNum int PageNum int
TotalPages int TotalPages int
@ -131,7 +134,8 @@ func (c *Creator) getActivePage() *model.PdfPage {
return c.activePage return c.activePage
} }
// SetPageSize sets the Creator's page size. Pages that are added after this will be created with this Page size. // SetPageSize sets the Creator's page size. Pages that are added after this will be created with
// this Page size.
// Does not affect pages already created. // Does not affect pages already created.
// //
// Common page sizes are defined as constants. // Common page sizes are defined as constants.
@ -238,8 +242,8 @@ func (c *Creator) AddPage(page *model.PdfPage) error {
return nil return nil
} }
// RotateDeg rotates the current active page by angle degrees. An error is returned on failure, which can be // RotateDeg rotates the current active page by angle degrees. An error is returned on failure,
// if there is no currently active page, or the angleDeg is not a multiple of 90 degrees. // which can be if there is no currently active page, or the angleDeg is not a multiple of 90 degrees.
func (c *Creator) RotateDeg(angleDeg int64) error { func (c *Creator) RotateDeg(angleDeg int64) error {
page := c.getActivePage() page := c.getActivePage()
if page == nil { if page == nil {
@ -247,7 +251,7 @@ func (c *Creator) RotateDeg(angleDeg int64) error {
return errors.New("No page active") return errors.New("No page active")
} }
if angleDeg%90 != 0 { if angleDeg%90 != 0 {
common.Log.Debug("Error: Page rotation angle not a multiple of 90") common.Log.Debug("ERROR: Page rotation angle not a multiple of 90")
return errors.New("Range check error") return errors.New("Range check error")
} }
@ -267,8 +271,8 @@ func (c *Creator) Context() DrawContext {
return c.context return c.context
} }
// Call before writing out. Takes care of adding headers and footers, as well as generating front Page and // Call before writing out. Takes care of adding headers and footers, as well as generating front
// table of contents. // Page and table of contents.
func (c *Creator) finalize() error { func (c *Creator) finalize() error {
totPages := len(c.pages) totPages := len(c.pages)
@ -356,8 +360,8 @@ func (c *Creator) finalize() error {
c.setActivePage(page) c.setActivePage(page)
if c.drawHeaderFunc != nil { if c.drawHeaderFunc != nil {
// Prepare a block to draw on. // Prepare a block to draw on.
// Header is drawn on the top of the page. Has width of the page, but height limited to the page // Header is drawn on the top of the page. Has width of the page, but height limited to
// margin top height. // the page margin top height.
headerBlock := NewBlock(c.pageWidth, c.pageMargins.top) headerBlock := NewBlock(c.pageWidth, c.pageMargins.top)
args := HeaderFunctionArgs{ args := HeaderFunctionArgs{
PageNum: idx + 1, PageNum: idx + 1,
@ -367,15 +371,15 @@ func (c *Creator) finalize() error {
headerBlock.SetPos(0, 0) headerBlock.SetPos(0, 0)
err := c.Draw(headerBlock) err := c.Draw(headerBlock)
if err != nil { if err != nil {
common.Log.Debug("Error drawing header: %v", err) common.Log.Debug("ERROR: drawing header: %v", err)
return err return err
} }
} }
if c.drawFooterFunc != nil { if c.drawFooterFunc != nil {
// Prepare a block to draw on. // Prepare a block to draw on.
// Footer is drawn on the bottom of the page. Has width of the page, but height limited to the page // Footer is drawn on the bottom of the page. Has width of the page, but height limited
// margin bottom height. // to the page margin bottom height.
footerBlock := NewBlock(c.pageWidth, c.pageMargins.bottom) footerBlock := NewBlock(c.pageWidth, c.pageMargins.bottom)
args := FooterFunctionArgs{ args := FooterFunctionArgs{
PageNum: idx + 1, PageNum: idx + 1,
@ -385,7 +389,7 @@ func (c *Creator) finalize() error {
footerBlock.SetPos(0, c.pageHeight-footerBlock.height) footerBlock.SetPos(0, c.pageHeight-footerBlock.height)
err := c.Draw(footerBlock) err := c.Draw(footerBlock)
if err != nil { if err != nil {
common.Log.Debug("Error drawing footer: %v", err) common.Log.Debug("ERROR: drawing footer: %v", err)
return err return err
} }
} }
@ -422,8 +426,8 @@ func (c *Creator) MoveDown(dy float64) {
c.context.Y += dy c.context.Y += dy
} }
// Draw draws the Drawable widget to the document. This can span over 1 or more pages. Additional pages are added if // Draw draws the Drawable widget to the document. This can span over 1 or more pages. Additional
// the contents go over the current Page. // pages are added if the contents go over the current Page.
func (c *Creator) Draw(d Drawable) error { func (c *Creator) Draw(d Drawable) error {
if c.getActivePage() == nil { if c.getActivePage() == nil {
// Add a new Page if none added already. // Add a new Page if none added already.
@ -464,10 +468,10 @@ func (c *Creator) Write(ws io.WriteSeeker) error {
pdfWriter := model.NewPdfWriter() pdfWriter := model.NewPdfWriter()
// Form fields. // Form fields.
if c.acroForm != nil { if c.acroForm != nil {
errF := pdfWriter.SetForms(c.acroForm) err := pdfWriter.SetForms(c.acroForm)
if errF != nil { if err != nil {
common.Log.Debug("Failure: %v", errF) common.Log.Debug("Failure: %v", err)
return errF return err
} }
} }
@ -483,7 +487,7 @@ func (c *Creator) Write(ws io.WriteSeeker) error {
for _, page := range c.pages { for _, page := range c.pages {
err := pdfWriter.AddPage(page) err := pdfWriter.AddPage(page)
if err != nil { if err != nil {
common.Log.Error("Failed to add Page: %s", err) common.Log.Error("Failed to add Page: %v", err)
return err return err
} }
} }
@ -519,13 +523,7 @@ func (c *Creator) WriteToFile(outputPath string) error {
if err != nil { if err != nil {
return err return err
} }
defer fWrite.Close() defer fWrite.Close()
err = c.Write(fWrite) return c.Write(fWrite)
if err != nil {
return err
}
return nil
} }

View File

@ -18,12 +18,10 @@ import (
"github.com/boombuler/barcode" "github.com/boombuler/barcode"
"github.com/boombuler/barcode/qr" "github.com/boombuler/barcode/qr"
"github.com/unidoc/unidoc/common" "github.com/unidoc/unidoc/common"
"github.com/unidoc/unidoc/pdf/contentstream/draw" "github.com/unidoc/unidoc/pdf/contentstream/draw"
"github.com/unidoc/unidoc/pdf/core" "github.com/unidoc/unidoc/pdf/core"
"github.com/unidoc/unidoc/pdf/model" "github.com/unidoc/unidoc/pdf/model"
"github.com/unidoc/unidoc/pdf/model/fonts"
"github.com/unidoc/unidoc/pdf/model/textencoding" "github.com/unidoc/unidoc/pdf/model/textencoding"
) )
@ -234,7 +232,8 @@ func TestShapes1(t *testing.T) {
} }
} }
// Example drawing image and line shape on a block and applying to pages, also demonstrating block rotation. // Example drawing image and line shape on a block and applying to pages, also demonstrating block
// rotation.
func TestShapesOnBlock(t *testing.T) { func TestShapesOnBlock(t *testing.T) {
creator := New() creator := New()
@ -421,9 +420,10 @@ func TestParagraph1(t *testing.T) {
} }
// Test paragraph and page and text wrapping with left, justify, center and right modes. // Test paragraph and page and text wrapping with left, justify, center and right modes.
// TODO: In the future we would like the paragraph to split up between pages. Split up on line, never allowing // TODO: In the future we would like the paragraph to split up between pages. Split up on line,
// less than 2 lines to go over (common practice). // never allowing less than 2 lines to go over (common practice).
// TODO: In the future we would like to implement Donald Knuth's line wrapping algorithm or something similar. // TODO: In the future we would like to implement Donald Knuth's line wrapping algorithm or
// something similar.
func TestParagraphWrapping(t *testing.T) { func TestParagraphWrapping(t *testing.T) {
creator := New() creator := New()
@ -435,7 +435,8 @@ func TestParagraphWrapping(t *testing.T) {
p.SetMargins(0, 0, 5, 0) p.SetMargins(0, 0, 5, 0)
alignments := []TextAlignment{TextAlignmentLeft, TextAlignmentJustify, TextAlignmentCenter, TextAlignmentRight} alignments := []TextAlignment{TextAlignmentLeft, TextAlignmentJustify, TextAlignmentCenter,
TextAlignmentRight}
for j := 0; j < 25; j++ { for j := 0; j < 25; j++ {
//p.SetAlignment(alignments[j%4]) //p.SetAlignment(alignments[j%4])
p.SetTextAlignment(alignments[1]) p.SetTextAlignment(alignments[1])
@ -463,7 +464,8 @@ func TestParagraphWrapping2(t *testing.T) {
"eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt " + "eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt " +
"mollit anim id est laborum.") "mollit anim id est laborum.")
alignments := []TextAlignment{TextAlignmentLeft, TextAlignmentJustify, TextAlignmentCenter, TextAlignmentRight} alignments := []TextAlignment{TextAlignmentLeft, TextAlignmentJustify, TextAlignmentCenter,
TextAlignmentRight}
for j := 0; j < 25; j++ { for j := 0; j < 25; j++ {
//p.SetAlignment(alignments[j%4]) //p.SetAlignment(alignments[j%4])
p.SetMargins(50, 50, 50, 50) p.SetMargins(50, 50, 50, 50)
@ -499,7 +501,13 @@ func TestParagraphFonts(t *testing.T) {
return return
} }
fonts := []fonts.Font{roboto, robotoBold, fonts.NewFontHelvetica(), roboto, robotoBold, fonts.NewFontHelvetica()} helvetica, err := model.NewStandard14Font("Helvetica")
if err != nil {
t.Errorf("Fail: %v", err)
return
}
fonts := []*model.PdfFont{roboto, robotoBold, helvetica, roboto, robotoBold, helvetica}
for _, font := range fonts { for _, font := range fonts {
p := NewParagraph("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt" + p := NewParagraph("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt" +
"ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut " + "ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut " +
@ -547,22 +555,7 @@ func TestParagraphStandardFonts(t *testing.T) {
"Symbol", "Symbol",
"ZapfDingbats", "ZapfDingbats",
} }
fonts := []fonts.Font{
fonts.NewFontCourier(),
fonts.NewFontCourierBold(),
fonts.NewFontCourierBoldOblique(),
fonts.NewFontCourierOblique(),
fonts.NewFontHelvetica(),
fonts.NewFontHelveticaBold(),
fonts.NewFontHelveticaBoldOblique(),
fonts.NewFontHelveticaOblique(),
fonts.NewFontTimesRoman(),
fonts.NewFontTimesBold(),
fonts.NewFontTimesBoldItalic(),
fonts.NewFontTimesItalic(),
fonts.NewFontSymbol(),
fonts.NewFontZapfDingbats(),
}
texts := []string{ texts := []string{
"Courier: Lorem ipsum dolor sit amet, consectetur adipiscing elit...", "Courier: Lorem ipsum dolor sit amet, consectetur adipiscing elit...",
"Courier-Bold: Lorem ipsum dolor sit amet, consectetur adipiscing elit...", "Courier-Bold: Lorem ipsum dolor sit amet, consectetur adipiscing elit...",
@ -582,8 +575,13 @@ func TestParagraphStandardFonts(t *testing.T) {
"\u2702\u0020\u2709\u261e\u2711\u2714", // a2 (scissors) space a117 (mail) a12 (finger) a17 (pen) a20 (checkmark) "\u2702\u0020\u2709\u261e\u2711\u2714", // a2 (scissors) space a117 (mail) a12 (finger) a17 (pen) a20 (checkmark)
} }
for idx, font := range fonts { for idx, name := range names {
p := NewParagraph(texts[idx]) p := NewParagraph(texts[idx])
font, err := model.NewStandard14Font(name)
if err != nil {
t.Errorf("Fail: %v", err)
return
}
p.SetFont(font) p.SetFont(font)
p.SetFontSize(12) p.SetFontSize(12)
p.SetLineHeight(1.2) p.SetLineHeight(1.2)
@ -597,7 +595,7 @@ func TestParagraphStandardFonts(t *testing.T) {
p.SetEncoder(textencoding.NewZapfDingbatsEncoder()) p.SetEncoder(textencoding.NewZapfDingbatsEncoder())
} }
err := creator.Draw(p) err = creator.Draw(p)
if err != nil { if err != nil {
t.Errorf("Fail: %v\n", err) t.Errorf("Fail: %v\n", err)
return return
@ -1235,7 +1233,7 @@ func TestBorderedTable2(t *testing.T) {
} }
} }
func newContent(text string, alignment TextAlignment, font fonts.Font, fontSize float64, color Color) *Paragraph { func newContent(text string, alignment TextAlignment, font *model.PdfFont, fontSize float64, color Color) *Paragraph {
p := NewParagraph(text) p := NewParagraph(text)
p.SetFontSize(fontSize) p.SetFontSize(fontSize)
p.SetTextAlignment(alignment) p.SetTextAlignment(alignment)
@ -1245,26 +1243,30 @@ func newContent(text string, alignment TextAlignment, font fonts.Font, fontSize
} }
func newBillItem(t *Table, no, date, notes, amount, con, retApplied, ret, netBill string) { func newBillItem(t *Table, no, date, notes, amount, con, retApplied, ret, netBill string) {
timesBold, _ := model.NewStandard14Font("Times-Bold")
billNo := t.NewCell() billNo := t.NewCell()
billNo.SetContent(newContent(no, TextAlignmentLeft, fonts.NewFontTimesBold(), 8, ColorBlack)) billNo.SetContent(newContent(no, TextAlignmentLeft, timesBold, 8, ColorBlack))
billDate := t.NewCell() billDate := t.NewCell()
billDate.SetContent(newContent(date, TextAlignmentCenter, fonts.NewFontTimesBold(), 8, ColorBlack)) billDate.SetContent(newContent(date, TextAlignmentCenter, timesBold, 8, ColorBlack))
billNotes := t.NewCell() billNotes := t.NewCell()
billNotes.SetContent(newContent(notes, TextAlignmentLeft, fonts.NewFontTimesBold(), 8, ColorBlack)) billNotes.SetContent(newContent(notes, TextAlignmentLeft, timesBold, 8, ColorBlack))
billAmount := t.NewCell() billAmount := t.NewCell()
billAmount.SetContent(newContent(amount, TextAlignmentRight, fonts.NewFontTimesBold(), 8, ColorBlack)) billAmount.SetContent(newContent(amount, TextAlignmentRight, timesBold, 8, ColorBlack))
billCon := t.NewCell() billCon := t.NewCell()
billCon.SetContent(newContent(con, TextAlignmentLeft, fonts.NewFontTimesBold(), 8, ColorBlack)) billCon.SetContent(newContent(con, TextAlignmentLeft, timesBold, 8, ColorBlack))
billRetApplied := t.NewCell() billRetApplied := t.NewCell()
billRetApplied.SetContent(newContent(retApplied, TextAlignmentRight, fonts.NewFontTimesBold(), 8, ColorBlack)) billRetApplied.SetContent(newContent(retApplied, TextAlignmentRight, timesBold, 8, ColorBlack))
billRet := t.NewCell() billRet := t.NewCell()
billRet.SetContent(newContent(ret, TextAlignmentLeft, fonts.NewFontTimesBold(), 8, ColorBlack)) billRet.SetContent(newContent(ret, TextAlignmentLeft, timesBold, 8, ColorBlack))
billNetBill := t.NewCell() billNetBill := t.NewCell()
billNetBill.SetContent(newContent(netBill, TextAlignmentRight, fonts.NewFontTimesBold(), 8, ColorBlack)) billNetBill.SetContent(newContent(netBill, TextAlignmentRight, timesBold, 8, ColorBlack))
} }
// Test creating and drawing a table. // Test creating and drawing a table.
func TestCreatorHendricksReq1(t *testing.T) { func TestCreatorHendricksReq1(t *testing.T) {
timesRoman, _ := model.NewStandard14Font("Times-Roman")
timesBold, _ := model.NewStandard14Font("Times-Bold")
table := NewTable(3) // Mx4 table table := NewTable(3) // Mx4 table
// Default, equal column sizes (4x0.25)... // Default, equal column sizes (4x0.25)...
table.SetColumnWidths(0.35, 0.30, 0.35) table.SetColumnWidths(0.35, 0.30, 0.35)
@ -1273,146 +1275,146 @@ func TestCreatorHendricksReq1(t *testing.T) {
projectColorTwo := ColorRed projectColorTwo := ColorRed
companyTitle := table.NewCell() companyTitle := table.NewCell()
companyTitle.SetContent(newContent("Hendricks Consulting LLC", TextAlignmentLeft, fonts.NewFontTimesBold(), 12, projectColorOne)) companyTitle.SetContent(newContent("Hendricks Consulting LLC", TextAlignmentLeft, timesBold, 12, projectColorOne))
table.SkipCells(1) table.SkipCells(1)
pageHeader := table.NewCell() pageHeader := table.NewCell()
pageHeader.SetContent(newContent("Billing Schedule by Project", TextAlignmentCenter, fonts.NewFontTimesBold(), 12, ColorBlack)) pageHeader.SetContent(newContent("Billing Schedule by Project", TextAlignmentCenter, timesBold, 12, ColorBlack))
pageHeader.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 3) pageHeader.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 3)
pageHeader.SetBorderLineStyle(draw.LineStyleSolid) pageHeader.SetBorderLineStyle(draw.LineStyleSolid)
companyAddress := table.NewCell() companyAddress := table.NewCell()
companyAddress.SetContent(newContent("2666 Airport Drive, Apt. 309", TextAlignmentLeft, fonts.NewFontTimesRoman(), 8, ColorBlack)) companyAddress.SetContent(newContent("2666 Airport Drive, Apt. 309", TextAlignmentLeft, timesRoman, 8, ColorBlack))
table.SkipCells(2) table.SkipCells(2)
companyLocation := table.NewCell() companyLocation := table.NewCell()
companyLocation.SetContent(newContent("Portland, Oregon, 92019", TextAlignmentLeft, fonts.NewFontTimesRoman(), 8, ColorBlack)) companyLocation.SetContent(newContent("Portland, Oregon, 92019", TextAlignmentLeft, timesRoman, 8, ColorBlack))
table.SkipCells(1) table.SkipCells(1)
printingDate := table.NewCell() printingDate := table.NewCell()
printingDate.SetContent(newContent("Printed on: 22/02/2011", TextAlignmentRight, fonts.NewFontTimesRoman(), 8, ColorBlack)) printingDate.SetContent(newContent("Printed on: 22/02/2011", TextAlignmentRight, timesRoman, 8, ColorBlack))
companyTelAndFax := table.NewCell() companyTelAndFax := table.NewCell()
companyTelAndFax.SetContent(newContent("Tel: (999) 609-4032 Fax: (999) 999-9922", TextAlignmentLeft, fonts.NewFontTimesRoman(), 8, ColorBlack)) companyTelAndFax.SetContent(newContent("Tel: (999) 609-4032 Fax: (999) 999-9922", TextAlignmentLeft, timesRoman, 8, ColorBlack))
table.SkipCells(1) table.SkipCells(1)
pageOf := table.NewCell() pageOf := table.NewCell()
pageOf.SetContent(newContent("Page 10 of 10", TextAlignmentRight, fonts.NewFontTimesRoman(), 8, ColorBlack)) pageOf.SetContent(newContent("Page 10 of 10", TextAlignmentRight, timesRoman, 8, ColorBlack))
email := table.NewCell() email := table.NewCell()
email.SetContent(newContent("admin@hendricks.com", TextAlignmentLeft, fonts.NewFontTimesRoman(), 8, ColorBlack)) email.SetContent(newContent("admin@hendricks.com", TextAlignmentLeft, timesRoman, 8, ColorBlack))
table.SkipCells(2) table.SkipCells(2)
website := table.NewCell() website := table.NewCell()
website.SetContent(newContent("www.hendricks.com", TextAlignmentLeft, fonts.NewFontTimesRoman(), 8, ColorBlack)) website.SetContent(newContent("www.hendricks.com", TextAlignmentLeft, timesRoman, 8, ColorBlack))
table2 := NewTable(5) table2 := NewTable(5)
table2.SetColumnWidths(0.20, 0.20, 0.20, 0.20, 0.20) table2.SetColumnWidths(0.20, 0.20, 0.20, 0.20, 0.20)
table2.SkipCells(5) table2.SkipCells(5)
projectName := table2.NewCell() projectName := table2.NewCell()
projectName.SetContent(newContent("Project Name (ID):", TextAlignmentLeft, fonts.NewFontTimesBold(), 8, projectColorOne)) projectName.SetContent(newContent("Project Name (ID):", TextAlignmentLeft, timesBold, 8, projectColorOne))
projectNameValue := table2.NewCell() projectNameValue := table2.NewCell()
projectNameValue.SetContent(newContent("Biggi Group", TextAlignmentLeft, fonts.NewFontTimesBold(), 8, ColorBlack)) projectNameValue.SetContent(newContent("Biggi Group", TextAlignmentLeft, timesBold, 8, ColorBlack))
table2.SkipCells(3) table2.SkipCells(3)
projectID := table2.NewCell() projectID := table2.NewCell()
projectID.SetContent(newContent("Project ID:", TextAlignmentLeft, fonts.NewFontTimesBold(), 8, projectColorOne)) projectID.SetContent(newContent("Project ID:", TextAlignmentLeft, timesBold, 8, projectColorOne))
projectIDValue := table2.NewCell() projectIDValue := table2.NewCell()
projectIDValue.SetContent(newContent("BG:01", TextAlignmentLeft, fonts.NewFontTimesBold(), 8, ColorBlack)) projectIDValue.SetContent(newContent("BG:01", TextAlignmentLeft, timesBold, 8, ColorBlack))
table2.SkipCells(1) table2.SkipCells(1)
contractType := table2.NewCell() contractType := table2.NewCell()
contractType.SetContent(newContent("Contract Type:", TextAlignmentRight, fonts.NewFontTimesBold(), 8, projectColorOne)) contractType.SetContent(newContent("Contract Type:", TextAlignmentRight, timesBold, 8, projectColorOne))
contractTypeValue := table2.NewCell() contractTypeValue := table2.NewCell()
contractTypeValue.SetContent(newContent("Percentage", TextAlignmentLeft, fonts.NewFontTimesRoman(), 8, ColorBlack)) contractTypeValue.SetContent(newContent("Percentage", TextAlignmentLeft, timesRoman, 8, ColorBlack))
projectManager := table2.NewCell() projectManager := table2.NewCell()
projectManager.SetContent(newContent("Manager:", TextAlignmentLeft, fonts.NewFontTimesBold(), 8, projectColorOne)) projectManager.SetContent(newContent("Manager:", TextAlignmentLeft, timesBold, 8, projectColorOne))
projectManagerValue := table2.NewCell() projectManagerValue := table2.NewCell()
projectManagerValue.SetContent(newContent("SHH", TextAlignmentLeft, fonts.NewFontTimesBold(), 8, ColorBlack)) projectManagerValue.SetContent(newContent("SHH", TextAlignmentLeft, timesBold, 8, ColorBlack))
table2.SkipCells(1) table2.SkipCells(1)
contractAmount := table2.NewCell() contractAmount := table2.NewCell()
contractAmount.SetContent(newContent("Contract Amount:", TextAlignmentRight, fonts.NewFontTimesBold(), 8, projectColorOne)) contractAmount.SetContent(newContent("Contract Amount:", TextAlignmentRight, timesBold, 8, projectColorOne))
contractAmountValue := table2.NewCell() contractAmountValue := table2.NewCell()
contractAmountValue.SetContent(newContent("$2,975.00", TextAlignmentLeft, fonts.NewFontTimesRoman(), 8, ColorBlack)) contractAmountValue.SetContent(newContent("$2,975.00", TextAlignmentLeft, timesRoman, 8, ColorBlack))
clientID := table2.NewCell() clientID := table2.NewCell()
clientID.SetContent(newContent("Client ID:", TextAlignmentLeft, fonts.NewFontTimesBold(), 8, projectColorOne)) clientID.SetContent(newContent("Client ID:", TextAlignmentLeft, timesBold, 8, projectColorOne))
clientIDValue := table2.NewCell() clientIDValue := table2.NewCell()
clientIDValue.SetContent(newContent("Baggi ehf", TextAlignmentLeft, fonts.NewFontTimesBold(), 8, ColorBlack)) clientIDValue.SetContent(newContent("Baggi ehf", TextAlignmentLeft, timesBold, 8, ColorBlack))
table2.SkipCells(1) table2.SkipCells(1)
retainerAmount := table2.NewCell() retainerAmount := table2.NewCell()
retainerAmount.SetContent(newContent("Retainer Amount:", TextAlignmentRight, fonts.NewFontTimesBold(), 8, projectColorOne)) retainerAmount.SetContent(newContent("Retainer Amount:", TextAlignmentRight, timesBold, 8, projectColorOne))
retainerAmountValue := table2.NewCell() retainerAmountValue := table2.NewCell()
retainerAmountValue.SetContent(newContent("", TextAlignmentLeft, fonts.NewFontTimesRoman(), 8, ColorBlack)) retainerAmountValue.SetContent(newContent("", TextAlignmentLeft, timesRoman, 8, ColorBlack))
table3 := NewTable(8) table3 := NewTable(8)
table3.SetColumnWidths(0.05, 0.10, 0.35, 0.10, 0.10, 0.10, 0.10, 0.10) table3.SetColumnWidths(0.05, 0.10, 0.35, 0.10, 0.10, 0.10, 0.10, 0.10)
table3.SkipCells(8) table3.SkipCells(8)
billNo := table3.NewCell() billNo := table3.NewCell()
billNo.SetContent(newContent("Bill #", TextAlignmentLeft, fonts.NewFontTimesBold(), 8, projectColorOne)) billNo.SetContent(newContent("Bill #", TextAlignmentLeft, timesBold, 8, projectColorOne))
billNo.SetBorder(CellBorderSideTop, CellBorderStyleSingle, 2) billNo.SetBorder(CellBorderSideTop, CellBorderStyleSingle, 2)
billNo.SetBorder(CellBorderSideBottom, CellBorderStyleSingle, 1) billNo.SetBorder(CellBorderSideBottom, CellBorderStyleSingle, 1)
billNo.SetBorderColor(projectColorOne) billNo.SetBorderColor(projectColorOne)
billDate := table3.NewCell() billDate := table3.NewCell()
billDate.SetContent(newContent("Date", TextAlignmentLeft, fonts.NewFontTimesBold(), 8, projectColorOne)) billDate.SetContent(newContent("Date", TextAlignmentLeft, timesBold, 8, projectColorOne))
billDate.SetBorder(CellBorderSideTop, CellBorderStyleSingle, 2) billDate.SetBorder(CellBorderSideTop, CellBorderStyleSingle, 2)
billDate.SetBorder(CellBorderSideBottom, CellBorderStyleSingle, 1) billDate.SetBorder(CellBorderSideBottom, CellBorderStyleSingle, 1)
billDate.SetBorderColor(projectColorOne) billDate.SetBorderColor(projectColorOne)
billNotes := table3.NewCell() billNotes := table3.NewCell()
billNotes.SetContent(newContent("Notes", TextAlignmentLeft, fonts.NewFontTimesBold(), 8, projectColorOne)) billNotes.SetContent(newContent("Notes", TextAlignmentLeft, timesBold, 8, projectColorOne))
billNotes.SetBorder(CellBorderSideTop, CellBorderStyleSingle, 2) billNotes.SetBorder(CellBorderSideTop, CellBorderStyleSingle, 2)
billNotes.SetBorder(CellBorderSideBottom, CellBorderStyleSingle, 1) billNotes.SetBorder(CellBorderSideBottom, CellBorderStyleSingle, 1)
billNotes.SetBorderColor(projectColorOne) billNotes.SetBorderColor(projectColorOne)
billAmount := table3.NewCell() billAmount := table3.NewCell()
billAmount.SetContent(newContent("Bill Amount", TextAlignmentLeft, fonts.NewFontTimesBold(), 8, projectColorOne)) billAmount.SetContent(newContent("Bill Amount", TextAlignmentLeft, timesBold, 8, projectColorOne))
billAmount.SetBorder(CellBorderSideTop, CellBorderStyleSingle, 2) billAmount.SetBorder(CellBorderSideTop, CellBorderStyleSingle, 2)
billAmount.SetBorder(CellBorderSideBottom, CellBorderStyleSingle, 1) billAmount.SetBorder(CellBorderSideBottom, CellBorderStyleSingle, 1)
billAmount.SetBorderColor(projectColorOne) billAmount.SetBorderColor(projectColorOne)
billCon := table3.NewCell() billCon := table3.NewCell()
billCon.SetContent(newContent("% Con", TextAlignmentLeft, fonts.NewFontTimesBold(), 8, projectColorOne)) billCon.SetContent(newContent("% Con", TextAlignmentLeft, timesBold, 8, projectColorOne))
billCon.SetBorder(CellBorderSideTop, CellBorderStyleSingle, 2) billCon.SetBorder(CellBorderSideTop, CellBorderStyleSingle, 2)
billCon.SetBorder(CellBorderSideBottom, CellBorderStyleSingle, 1) billCon.SetBorder(CellBorderSideBottom, CellBorderStyleSingle, 1)
billCon.SetBorderColor(projectColorOne) billCon.SetBorderColor(projectColorOne)
billRetApplied := table3.NewCell() billRetApplied := table3.NewCell()
billRetApplied.SetContent(newContent("Ret Applied", TextAlignmentLeft, fonts.NewFontTimesBold(), 8, projectColorOne)) billRetApplied.SetContent(newContent("Ret Applied", TextAlignmentLeft, timesBold, 8, projectColorOne))
billRetApplied.SetBorder(CellBorderSideTop, CellBorderStyleSingle, 2) billRetApplied.SetBorder(CellBorderSideTop, CellBorderStyleSingle, 2)
billRetApplied.SetBorder(CellBorderSideBottom, CellBorderStyleSingle, 1) billRetApplied.SetBorder(CellBorderSideBottom, CellBorderStyleSingle, 1)
billRetApplied.SetBorderColor(projectColorOne) billRetApplied.SetBorderColor(projectColorOne)
billRet := table3.NewCell() billRet := table3.NewCell()
billRet.SetContent(newContent("% Ret", TextAlignmentLeft, fonts.NewFontTimesBold(), 8, projectColorOne)) billRet.SetContent(newContent("% Ret", TextAlignmentLeft, timesBold, 8, projectColorOne))
billRet.SetBorder(CellBorderSideTop, CellBorderStyleSingle, 2) billRet.SetBorder(CellBorderSideTop, CellBorderStyleSingle, 2)
billRet.SetBorder(CellBorderSideBottom, CellBorderStyleSingle, 1) billRet.SetBorder(CellBorderSideBottom, CellBorderStyleSingle, 1)
billRet.SetBorderColor(projectColorOne) billRet.SetBorderColor(projectColorOne)
billNetBill := table3.NewCell() billNetBill := table3.NewCell()
billNetBill.SetContent(newContent("Net Bill Amt", TextAlignmentLeft, fonts.NewFontTimesBold(), 8, projectColorOne)) billNetBill.SetContent(newContent("Net Bill Amt", TextAlignmentLeft, timesBold, 8, projectColorOne))
billNetBill.SetBorder(CellBorderSideTop, CellBorderStyleSingle, 2) billNetBill.SetBorder(CellBorderSideTop, CellBorderStyleSingle, 2)
billNetBill.SetBorder(CellBorderSideBottom, CellBorderStyleSingle, 1) billNetBill.SetBorder(CellBorderSideBottom, CellBorderStyleSingle, 1)
billNetBill.SetBorderColor(projectColorOne) billNetBill.SetBorderColor(projectColorOne)
@ -1427,24 +1429,24 @@ func TestCreatorHendricksReq1(t *testing.T) {
table3.SkipCells(2 + 8) table3.SkipCells(2 + 8)
totalBill := table3.NewCell() totalBill := table3.NewCell()
totalBill.SetContent(newContent("Total: ", TextAlignmentRight, fonts.NewFontTimesBold(), 8, projectColorTwo)) totalBill.SetContent(newContent("Total: ", TextAlignmentRight, timesBold, 8, projectColorTwo))
totalBillAmount := table3.NewCell() totalBillAmount := table3.NewCell()
totalBillAmount.SetContent(newContent("$3,272.50", TextAlignmentRight, fonts.NewFontTimesBold(), 8, projectColorTwo)) totalBillAmount.SetContent(newContent("$3,272.50", TextAlignmentRight, timesBold, 8, projectColorTwo))
totalBillAmount.SetBorder(CellBorderSideTop, CellBorderStyleDouble, 1) totalBillAmount.SetBorder(CellBorderSideTop, CellBorderStyleDouble, 1)
totalBillAmount.SetBorder(CellBorderSideBottom, CellBorderStyleSingle, 1, ) totalBillAmount.SetBorder(CellBorderSideBottom, CellBorderStyleSingle, 1)
table3.SkipCells(1) table3.SkipCells(1)
totalRetAmount := table3.NewCell() totalRetAmount := table3.NewCell()
totalRetAmount.SetContent(newContent("$0.00", TextAlignmentRight, fonts.NewFontTimesBold(), 8, projectColorTwo)) totalRetAmount.SetContent(newContent("$0.00", TextAlignmentRight, timesBold, 8, projectColorTwo))
totalRetAmount.SetBorder(CellBorderSideTop, CellBorderStyleDouble, 1) totalRetAmount.SetBorder(CellBorderSideTop, CellBorderStyleDouble, 1)
totalRetAmount.SetBorder(CellBorderSideBottom, CellBorderStyleSingle, 1) totalRetAmount.SetBorder(CellBorderSideBottom, CellBorderStyleSingle, 1)
table3.SkipCells(1) table3.SkipCells(1)
totalNetAmount := table3.NewCell() totalNetAmount := table3.NewCell()
totalNetAmount.SetContent(newContent("$3,272.50", TextAlignmentRight, fonts.NewFontTimesBold(), 8, projectColorTwo)) totalNetAmount.SetContent(newContent("$3,272.50", TextAlignmentRight, timesBold, 8, projectColorTwo))
totalNetAmount.SetBorder(CellBorderSideTop, CellBorderStyleDouble, 1) totalNetAmount.SetBorder(CellBorderSideTop, CellBorderStyleDouble, 1)
totalNetAmount.SetBorder(CellBorderSideBottom, CellBorderStyleSingle, 1) totalNetAmount.SetBorder(CellBorderSideBottom, CellBorderStyleSingle, 1)
totalNetAmount.SetBorderLineStyle(draw.LineStyleSolid) totalNetAmount.SetBorderLineStyle(draw.LineStyleSolid)
@ -1462,11 +1464,12 @@ func TestCreatorHendricksReq1(t *testing.T) {
} }
func TestCreatorTableBorderReq1(t *testing.T) { func TestCreatorTableBorderReq1(t *testing.T) {
timesRoman, _ := model.NewStandard14Font("Times-Roman")
table := NewTable(1) // Mx4 table table := NewTable(1) // Mx4 table
table.SetColumnWidths(1) table.SetColumnWidths(1)
fullLengthCell := table.NewCell() fullLengthCell := table.NewCell()
fullLengthCell.SetContent(newContent("boxed, solid, default width", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) fullLengthCell.SetContent(newContent("boxed, solid, default width", TextAlignmentLeft, timesRoman, 10, ColorBlack))
fullLengthCell.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1) fullLengthCell.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
table2 := NewTable(4) // Mx4 table table2 := NewTable(4) // Mx4 table
@ -1475,43 +1478,43 @@ func TestCreatorTableBorderReq1(t *testing.T) {
table2.SkipCells(4) table2.SkipCells(4)
a := table2.NewCell() a := table2.NewCell()
a.SetContent(newContent("A", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) a.SetContent(newContent("A", TextAlignmentLeft, timesRoman, 10, ColorBlack))
a.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1) a.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
b := table2.NewCell() b := table2.NewCell()
b.SetContent(newContent("B", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) b.SetContent(newContent("B", TextAlignmentLeft, timesRoman, 10, ColorBlack))
b.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1) b.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
cc := table2.NewCell() cc := table2.NewCell()
cc.SetContent(newContent("C", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) cc.SetContent(newContent("C", TextAlignmentLeft, timesRoman, 10, ColorBlack))
cc.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1) cc.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
d := table2.NewCell() d := table2.NewCell()
d.SetContent(newContent("D", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) d.SetContent(newContent("D", TextAlignmentLeft, timesRoman, 10, ColorBlack))
d.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1) d.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
e := table2.NewCell() e := table2.NewCell()
e.SetContent(newContent("E", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) e.SetContent(newContent("E", TextAlignmentLeft, timesRoman, 10, ColorBlack))
e.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1) e.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
f := table2.NewCell() f := table2.NewCell()
f.SetContent(newContent("F", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) f.SetContent(newContent("F", TextAlignmentLeft, timesRoman, 10, ColorBlack))
f.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1) f.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
g := table2.NewCell() g := table2.NewCell()
g.SetContent(newContent("G", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) g.SetContent(newContent("G", TextAlignmentLeft, timesRoman, 10, ColorBlack))
g.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1) g.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
h := table2.NewCell() h := table2.NewCell()
h.SetContent(newContent("H", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) h.SetContent(newContent("H", TextAlignmentLeft, timesRoman, 10, ColorBlack))
h.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1) h.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
i := table2.NewCell() i := table2.NewCell()
i.SetContent(newContent("I", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) i.SetContent(newContent("I", TextAlignmentLeft, timesRoman, 10, ColorBlack))
i.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1) i.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
j := table2.NewCell() j := table2.NewCell()
j.SetContent(newContent("J", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) j.SetContent(newContent("J", TextAlignmentLeft, timesRoman, 10, ColorBlack))
j.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1) j.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
table3 := NewTable(1) // Mx4 table table3 := NewTable(1) // Mx4 table
@ -1520,7 +1523,7 @@ func TestCreatorTableBorderReq1(t *testing.T) {
table3.SkipCells(1) table3.SkipCells(1)
dash := table3.NewCell() dash := table3.NewCell()
dash.SetContent(newContent("boxed, dashed, default width", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) dash.SetContent(newContent("boxed, dashed, default width", TextAlignmentLeft, timesRoman, 10, ColorBlack))
dash.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1) dash.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
dash.SetBorderLineStyle(draw.LineStyleDashed) dash.SetBorderLineStyle(draw.LineStyleDashed)
@ -1530,71 +1533,71 @@ func TestCreatorTableBorderReq1(t *testing.T) {
table4.SkipCells(4) table4.SkipCells(4)
ad := table4.NewCell() ad := table4.NewCell()
ad.SetContent(newContent("A", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) ad.SetContent(newContent("A", TextAlignmentLeft, timesRoman, 10, ColorBlack))
ad.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1) ad.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
ad.SetBorderLineStyle(draw.LineStyleDashed) ad.SetBorderLineStyle(draw.LineStyleDashed)
bd := table4.NewCell() bd := table4.NewCell()
bd.SetContent(newContent("B", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) bd.SetContent(newContent("B", TextAlignmentLeft, timesRoman, 10, ColorBlack))
bd.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1) bd.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
bd.SetBorderLineStyle(draw.LineStyleDashed) bd.SetBorderLineStyle(draw.LineStyleDashed)
table4.SkipCells(2) table4.SkipCells(2)
ccd := table4.NewCell() ccd := table4.NewCell()
ccd.SetContent(newContent("C", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) ccd.SetContent(newContent("C", TextAlignmentLeft, timesRoman, 10, ColorBlack))
ccd.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1) ccd.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
ccd.SetBorderLineStyle(draw.LineStyleDashed) ccd.SetBorderLineStyle(draw.LineStyleDashed)
dd := table4.NewCell() dd := table4.NewCell()
dd.SetContent(newContent("D", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) dd.SetContent(newContent("D", TextAlignmentLeft, timesRoman, 10, ColorBlack))
dd.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1) dd.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
dd.SetBorderLineStyle(draw.LineStyleDashed) dd.SetBorderLineStyle(draw.LineStyleDashed)
table4.SkipCells(2) table4.SkipCells(2)
ed := table4.NewCell() ed := table4.NewCell()
ed.SetContent(newContent("E", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) ed.SetContent(newContent("E", TextAlignmentLeft, timesRoman, 10, ColorBlack))
ed.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1) ed.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
ed.SetBorderLineStyle(draw.LineStyleDashed) ed.SetBorderLineStyle(draw.LineStyleDashed)
fd := table4.NewCell() fd := table4.NewCell()
fd.SetContent(newContent("F", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) fd.SetContent(newContent("F", TextAlignmentLeft, timesRoman, 10, ColorBlack))
fd.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1) fd.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
fd.SetBorderLineStyle(draw.LineStyleDashed) fd.SetBorderLineStyle(draw.LineStyleDashed)
gd := table4.NewCell() gd := table4.NewCell()
gd.SetContent(newContent("G", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) gd.SetContent(newContent("G", TextAlignmentLeft, timesRoman, 10, ColorBlack))
gd.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1) gd.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
gd.SetBorderLineStyle(draw.LineStyleDashed) gd.SetBorderLineStyle(draw.LineStyleDashed)
hd := table4.NewCell() hd := table4.NewCell()
hd.SetContent(newContent("H", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) hd.SetContent(newContent("H", TextAlignmentLeft, timesRoman, 10, ColorBlack))
hd.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1) hd.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
hd.SetBorderLineStyle(draw.LineStyleDashed) hd.SetBorderLineStyle(draw.LineStyleDashed)
id := table4.NewCell() id := table4.NewCell()
id.SetContent(newContent("I", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) id.SetContent(newContent("I", TextAlignmentLeft, timesRoman, 10, ColorBlack))
id.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1) id.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
id.SetBorderLineStyle(draw.LineStyleDashed) id.SetBorderLineStyle(draw.LineStyleDashed)
jd := table4.NewCell() jd := table4.NewCell()
jd.SetContent(newContent("J", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) jd.SetContent(newContent("J", TextAlignmentLeft, timesRoman, 10, ColorBlack))
jd.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1) jd.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
jd.SetBorderLineStyle(draw.LineStyleDashed) jd.SetBorderLineStyle(draw.LineStyleDashed)
kd := table4.NewCell() kd := table4.NewCell()
kd.SetContent(newContent("K", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) kd.SetContent(newContent("K", TextAlignmentLeft, timesRoman, 10, ColorBlack))
kd.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1) kd.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
kd.SetBorderLineStyle(draw.LineStyleDashed) kd.SetBorderLineStyle(draw.LineStyleDashed)
ld := table4.NewCell() ld := table4.NewCell()
ld.SetContent(newContent("L", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) ld.SetContent(newContent("L", TextAlignmentLeft, timesRoman, 10, ColorBlack))
ld.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1) ld.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
ld.SetBorderLineStyle(draw.LineStyleDashed) ld.SetBorderLineStyle(draw.LineStyleDashed)
md := table4.NewCell() md := table4.NewCell()
md.SetContent(newContent("M", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) md.SetContent(newContent("M", TextAlignmentLeft, timesRoman, 10, ColorBlack))
md.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1) md.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1)
md.SetBorderLineStyle(draw.LineStyleDashed) md.SetBorderLineStyle(draw.LineStyleDashed)
@ -1604,7 +1607,7 @@ func TestCreatorTableBorderReq1(t *testing.T) {
table5.SkipCells(1) table5.SkipCells(1)
doubled := table5.NewCell() doubled := table5.NewCell()
doubled.SetContent(newContent("boxed, double, default width", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) doubled.SetContent(newContent("boxed, double, default width", TextAlignmentLeft, timesRoman, 10, ColorBlack))
doubled.SetBorder(CellBorderSideAll, CellBorderStyleDouble, 1) doubled.SetBorder(CellBorderSideAll, CellBorderStyleDouble, 1)
table6 := NewTable(4) // Mx4 table table6 := NewTable(4) // Mx4 table
@ -1613,43 +1616,43 @@ func TestCreatorTableBorderReq1(t *testing.T) {
table6.SkipCells(4) table6.SkipCells(4)
add := table6.NewCell() add := table6.NewCell()
add.SetContent(newContent("A", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) add.SetContent(newContent("A", TextAlignmentLeft, timesRoman, 10, ColorBlack))
add.SetBorder(CellBorderSideAll, CellBorderStyleDouble, 1) add.SetBorder(CellBorderSideAll, CellBorderStyleDouble, 1)
bdd := table6.NewCell() bdd := table6.NewCell()
bdd.SetContent(newContent("B", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) bdd.SetContent(newContent("B", TextAlignmentLeft, timesRoman, 10, ColorBlack))
bdd.SetBorder(CellBorderSideAll, CellBorderStyleDouble, 1) bdd.SetBorder(CellBorderSideAll, CellBorderStyleDouble, 1)
ccdd := table6.NewCell() ccdd := table6.NewCell()
ccdd.SetContent(newContent("C", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) ccdd.SetContent(newContent("C", TextAlignmentLeft, timesRoman, 10, ColorBlack))
ccdd.SetBorder(CellBorderSideAll, CellBorderStyleDouble, 1) ccdd.SetBorder(CellBorderSideAll, CellBorderStyleDouble, 1)
ddd := table6.NewCell() ddd := table6.NewCell()
ddd.SetContent(newContent("D", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) ddd.SetContent(newContent("D", TextAlignmentLeft, timesRoman, 10, ColorBlack))
ddd.SetBorder(CellBorderSideAll, CellBorderStyleDouble, 1) ddd.SetBorder(CellBorderSideAll, CellBorderStyleDouble, 1)
edd := table6.NewCell() edd := table6.NewCell()
edd.SetContent(newContent("E", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) edd.SetContent(newContent("E", TextAlignmentLeft, timesRoman, 10, ColorBlack))
edd.SetBorder(CellBorderSideAll, CellBorderStyleDouble, 1) edd.SetBorder(CellBorderSideAll, CellBorderStyleDouble, 1)
fdd := table6.NewCell() fdd := table6.NewCell()
fdd.SetContent(newContent("F", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) fdd.SetContent(newContent("F", TextAlignmentLeft, timesRoman, 10, ColorBlack))
fdd.SetBorder(CellBorderSideAll, CellBorderStyleDouble, 1) fdd.SetBorder(CellBorderSideAll, CellBorderStyleDouble, 1)
gdd := table6.NewCell() gdd := table6.NewCell()
gdd.SetContent(newContent("G", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) gdd.SetContent(newContent("G", TextAlignmentLeft, timesRoman, 10, ColorBlack))
gdd.SetBorder(CellBorderSideAll, CellBorderStyleDouble, 1) gdd.SetBorder(CellBorderSideAll, CellBorderStyleDouble, 1)
hdd := table6.NewCell() hdd := table6.NewCell()
hdd.SetContent(newContent("H", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) hdd.SetContent(newContent("H", TextAlignmentLeft, timesRoman, 10, ColorBlack))
hdd.SetBorder(CellBorderSideAll, CellBorderStyleDouble, 1) hdd.SetBorder(CellBorderSideAll, CellBorderStyleDouble, 1)
idd := table6.NewCell() idd := table6.NewCell()
idd.SetContent(newContent("I", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) idd.SetContent(newContent("I", TextAlignmentLeft, timesRoman, 10, ColorBlack))
idd.SetBorder(CellBorderSideAll, CellBorderStyleDouble, 1) idd.SetBorder(CellBorderSideAll, CellBorderStyleDouble, 1)
jdd := table6.NewCell() jdd := table6.NewCell()
jdd.SetContent(newContent("J", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) jdd.SetContent(newContent("J", TextAlignmentLeft, timesRoman, 10, ColorBlack))
jdd.SetBorder(CellBorderSideAll, CellBorderStyleDouble, 1) jdd.SetBorder(CellBorderSideAll, CellBorderStyleDouble, 1)
table7 := NewTable(1) // Mx4 table table7 := NewTable(1) // Mx4 table
@ -1658,7 +1661,7 @@ func TestCreatorTableBorderReq1(t *testing.T) {
table7.SkipCells(1) table7.SkipCells(1)
fullLengthCell7 := table7.NewCell() fullLengthCell7 := table7.NewCell()
fullLengthCell7.SetContent(newContent("boxed, solid, thick", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) fullLengthCell7.SetContent(newContent("boxed, solid, thick", TextAlignmentLeft, timesRoman, 10, ColorBlack))
fullLengthCell7.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2) fullLengthCell7.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2)
table8 := NewTable(4) // Mx4 table table8 := NewTable(4) // Mx4 table
@ -1667,43 +1670,43 @@ func TestCreatorTableBorderReq1(t *testing.T) {
table8.SkipCells(4) table8.SkipCells(4)
a8 := table8.NewCell() a8 := table8.NewCell()
a8.SetContent(newContent("A", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) a8.SetContent(newContent("A", TextAlignmentLeft, timesRoman, 10, ColorBlack))
a8.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2) a8.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2)
b8 := table8.NewCell() b8 := table8.NewCell()
b8.SetContent(newContent("B", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) b8.SetContent(newContent("B", TextAlignmentLeft, timesRoman, 10, ColorBlack))
b8.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2) b8.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2)
cc8 := table8.NewCell() cc8 := table8.NewCell()
cc8.SetContent(newContent("C", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) cc8.SetContent(newContent("C", TextAlignmentLeft, timesRoman, 10, ColorBlack))
cc8.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2) cc8.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2)
d8 := table8.NewCell() d8 := table8.NewCell()
d8.SetContent(newContent("D", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) d8.SetContent(newContent("D", TextAlignmentLeft, timesRoman, 10, ColorBlack))
d8.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2) d8.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2)
e8 := table8.NewCell() e8 := table8.NewCell()
e8.SetContent(newContent("E", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) e8.SetContent(newContent("E", TextAlignmentLeft, timesRoman, 10, ColorBlack))
e8.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2) e8.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2)
f8 := table8.NewCell() f8 := table8.NewCell()
f8.SetContent(newContent("F", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) f8.SetContent(newContent("F", TextAlignmentLeft, timesRoman, 10, ColorBlack))
f8.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2) f8.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2)
g8 := table8.NewCell() g8 := table8.NewCell()
g8.SetContent(newContent("G", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) g8.SetContent(newContent("G", TextAlignmentLeft, timesRoman, 10, ColorBlack))
g8.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2) g8.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2)
h8 := table8.NewCell() h8 := table8.NewCell()
h8.SetContent(newContent("H", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) h8.SetContent(newContent("H", TextAlignmentLeft, timesRoman, 10, ColorBlack))
h8.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2) h8.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2)
i8 := table8.NewCell() i8 := table8.NewCell()
i8.SetContent(newContent("I", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) i8.SetContent(newContent("I", TextAlignmentLeft, timesRoman, 10, ColorBlack))
i8.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2) i8.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2)
j8 := table8.NewCell() j8 := table8.NewCell()
j8.SetContent(newContent("J", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) j8.SetContent(newContent("J", TextAlignmentLeft, timesRoman, 10, ColorBlack))
j8.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2) j8.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2)
table9 := NewTable(1) // Mx4 table table9 := NewTable(1) // Mx4 table
@ -1712,7 +1715,7 @@ func TestCreatorTableBorderReq1(t *testing.T) {
table9.SkipCells(1) table9.SkipCells(1)
fullLengthCell9 := table9.NewCell() fullLengthCell9 := table9.NewCell()
fullLengthCell9.SetContent(newContent("boxed, dashed, thick", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) fullLengthCell9.SetContent(newContent("boxed, dashed, thick", TextAlignmentLeft, timesRoman, 10, ColorBlack))
fullLengthCell9.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2) fullLengthCell9.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2)
fullLengthCell9.SetBorderLineStyle(draw.LineStyleDashed) fullLengthCell9.SetBorderLineStyle(draw.LineStyleDashed)
@ -1722,52 +1725,52 @@ func TestCreatorTableBorderReq1(t *testing.T) {
table10.SkipCells(4) table10.SkipCells(4)
a10 := table10.NewCell() a10 := table10.NewCell()
a10.SetContent(newContent("A", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) a10.SetContent(newContent("A", TextAlignmentLeft, timesRoman, 10, ColorBlack))
a10.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2) a10.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2)
a10.SetBorderLineStyle(draw.LineStyleDashed) a10.SetBorderLineStyle(draw.LineStyleDashed)
b10 := table10.NewCell() b10 := table10.NewCell()
b10.SetContent(newContent("B", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) b10.SetContent(newContent("B", TextAlignmentLeft, timesRoman, 10, ColorBlack))
b10.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2) b10.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2)
b10.SetBorderLineStyle(draw.LineStyleDashed) b10.SetBorderLineStyle(draw.LineStyleDashed)
cc10 := table10.NewCell() cc10 := table10.NewCell()
cc10.SetContent(newContent("C", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) cc10.SetContent(newContent("C", TextAlignmentLeft, timesRoman, 10, ColorBlack))
cc10.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2) cc10.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2)
cc10.SetBorderLineStyle(draw.LineStyleDashed) cc10.SetBorderLineStyle(draw.LineStyleDashed)
d10 := table10.NewCell() d10 := table10.NewCell()
d10.SetContent(newContent("D", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) d10.SetContent(newContent("D", TextAlignmentLeft, timesRoman, 10, ColorBlack))
d10.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2) d10.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2)
d10.SetBorderLineStyle(draw.LineStyleDashed) d10.SetBorderLineStyle(draw.LineStyleDashed)
e10 := table10.NewCell() e10 := table10.NewCell()
e10.SetContent(newContent("E", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) e10.SetContent(newContent("E", TextAlignmentLeft, timesRoman, 10, ColorBlack))
e10.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2) e10.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2)
e10.SetBorderLineStyle(draw.LineStyleDashed) e10.SetBorderLineStyle(draw.LineStyleDashed)
f10 := table10.NewCell() f10 := table10.NewCell()
f10.SetContent(newContent("F", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) f10.SetContent(newContent("F", TextAlignmentLeft, timesRoman, 10, ColorBlack))
f10.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2) f10.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2)
f10.SetBorderLineStyle(draw.LineStyleDashed) f10.SetBorderLineStyle(draw.LineStyleDashed)
g10 := table10.NewCell() g10 := table10.NewCell()
g10.SetContent(newContent("G", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) g10.SetContent(newContent("G", TextAlignmentLeft, timesRoman, 10, ColorBlack))
g10.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2) g10.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2)
g10.SetBorderLineStyle(draw.LineStyleDashed) g10.SetBorderLineStyle(draw.LineStyleDashed)
h10 := table10.NewCell() h10 := table10.NewCell()
h10.SetContent(newContent("H", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) h10.SetContent(newContent("H", TextAlignmentLeft, timesRoman, 10, ColorBlack))
h10.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2) h10.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2)
h10.SetBorderLineStyle(draw.LineStyleDashed) h10.SetBorderLineStyle(draw.LineStyleDashed)
i10 := table10.NewCell() i10 := table10.NewCell()
i10.SetContent(newContent("I", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) i10.SetContent(newContent("I", TextAlignmentLeft, timesRoman, 10, ColorBlack))
i10.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2) i10.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2)
i10.SetBorderLineStyle(draw.LineStyleDashed) i10.SetBorderLineStyle(draw.LineStyleDashed)
j10 := table10.NewCell() j10 := table10.NewCell()
j10.SetContent(newContent("J", TextAlignmentLeft, fonts.NewFontTimesRoman(), 10, ColorBlack)) j10.SetContent(newContent("J", TextAlignmentLeft, timesRoman, 10, ColorBlack))
j10.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2) j10.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 2)
j10.SetBorderLineStyle(draw.LineStyleDashed) j10.SetBorderLineStyle(draw.LineStyleDashed)
@ -1791,11 +1794,13 @@ func TestCreatorTableBorderReq1(t *testing.T) {
} }
func TestCellBorder(t *testing.T) { func TestCellBorder(t *testing.T) {
timesBold, _ := model.NewStandard14Font("Times-Bold")
table := NewTable(2) table := NewTable(2)
table.SetColumnWidths(0.50, 0.50) table.SetColumnWidths(0.50, 0.50)
cell1 := table.NewCell() cell1 := table.NewCell()
cell1.SetContent(newContent("Cell 1", TextAlignmentLeft, fonts.NewFontTimesBold(), 8, ColorRed)) cell1.SetContent(newContent("Cell 1", TextAlignmentLeft, timesBold, 8, ColorRed))
cell1.SetBorder(CellBorderSideAll, CellBorderStyleDouble, 1) cell1.SetBorder(CellBorderSideAll, CellBorderStyleDouble, 1)
c := New() c := New()
@ -1811,8 +1816,16 @@ func TestCellBorder(t *testing.T) {
func TestTableInSubchapter(t *testing.T) { func TestTableInSubchapter(t *testing.T) {
c := New() c := New()
fontRegular := fonts.NewFontHelvetica() fontRegular, err := model.NewStandard14Font("Helvetica")
fontBold := fonts.NewFontHelveticaBold() if err != nil {
t.Errorf("Fail: %v", err)
return
}
fontBold, err := model.NewStandard14Font("Helvetica-Bold")
if err != nil {
t.Errorf("Fail: %v", err)
return
}
ch := c.NewChapter("Document control") ch := c.NewChapter("Document control")
ch.SetMargins(0, 0, 40, 0) ch.SetMargins(0, 0, 40, 0)
@ -1894,7 +1907,7 @@ func TestTableInSubchapter(t *testing.T) {
myPara.SetLineHeight(1.5) myPara.SetLineHeight(1.5)
sc.Add(myPara) sc.Add(myPara)
err := c.Draw(ch) err = c.Draw(ch)
if err != nil { if err != nil {
t.Errorf("Fail: %v\n", err) t.Errorf("Fail: %v\n", err)
return return

View File

@ -20,9 +20,9 @@ type VectorDrawable interface {
Height() float64 Height() float64
} }
// DrawContext defines the drawing context. The DrawContext is continuously used and updated when drawing the page // DrawContext defines the drawing context. The DrawContext is continuously used and updated when
// contents in relative mode. Keeps track of current X, Y position, available height as well as other page parameters // drawing the page contents in relative mode. Keeps track of current X, Y position, available
// such as margins and dimensions. // height as well as other page parameters such as margins and dimensions.
type DrawContext struct { type DrawContext struct {
// Current page number. // Current page number.
Page int Page int

View File

@ -13,18 +13,17 @@ import (
"github.com/unidoc/unidoc/pdf/contentstream" "github.com/unidoc/unidoc/pdf/contentstream"
"github.com/unidoc/unidoc/pdf/core" "github.com/unidoc/unidoc/pdf/core"
"github.com/unidoc/unidoc/pdf/model" "github.com/unidoc/unidoc/pdf/model"
"github.com/unidoc/unidoc/pdf/model/fonts"
"github.com/unidoc/unidoc/pdf/model/textencoding" "github.com/unidoc/unidoc/pdf/model/textencoding"
) )
// Paragraph represents text drawn with a specified font and can wrap across lines and pages. // Paragraph represents text drawn with a specified font and can wrap across lines and pages.
// By default occupies the available width in the drawing context. // By default it occupies the available width in the drawing context.
type Paragraph struct { type Paragraph struct {
// The input utf-8 text as a string (series of runes). // The input utf-8 text as a string (series of runes).
text string text string
// The font to be used to draw the text. // The font to be used to draw the text.
textFont fonts.Font textFont *model.PdfFont
// The font size (points). // The font size (points).
fontSize float64 fontSize float64
@ -67,18 +66,26 @@ type Paragraph struct {
textLines []string textLines []string
} }
// NewParagraph create a new text paragraph. Uses default parameters: Helvetica, WinAnsiEncoding and wrap enabled // NewParagraph create a new text paragraph. Uses default parameters: Helvetica, WinAnsiEncoding and
// with a wrap width of 100 points. // wrap enabled with a wrap width of 100 points.
func NewParagraph(text string) *Paragraph { func NewParagraph(text string) *Paragraph {
p := &Paragraph{} p := &Paragraph{}
p.text = text p.text = text
p.textFont = fonts.NewFontHelvetica()
p.SetEncoder(textencoding.NewWinAnsiTextEncoder()) font, encoder, err := model.NewStandard14FontWithEncoding("Helvetica", model.GetAlphabet(text))
if err != nil {
common.Log.Debug("ERROR: NewStandard14FontWithEncoding failed err=%v. Falling back.", err)
p.textFont = model.DefaultFont()
}
p.textFont = font
p.SetEncoder(encoder)
p.fontSize = 10 p.fontSize = 10
p.lineHeight = 1.0 p.lineHeight = 1.0
// TODO: Can we wrap intellectually, only if given width is known? // TODO: Can we wrap intellectually, only if given width is known?
p.enableWrap = true
p.enableWrap = false
p.defaultWrap = true p.defaultWrap = true
p.SetColor(ColorRGBFrom8bit(0, 0, 0)) p.SetColor(ColorRGBFrom8bit(0, 0, 0))
p.alignment = TextAlignmentLeft p.alignment = TextAlignmentLeft
@ -93,7 +100,7 @@ func NewParagraph(text string) *Paragraph {
} }
// SetFont sets the Paragraph's font. // SetFont sets the Paragraph's font.
func (p *Paragraph) SetFont(font fonts.Font) { func (p *Paragraph) SetFont(font *model.PdfFont) {
p.textFont = font p.textFont = font
} }
@ -133,7 +140,7 @@ func (p *Paragraph) SetEnableWrap(enableWrap bool) {
p.defaultWrap = false p.defaultWrap = false
} }
// SetColor set the color of the Paragraph text. // SetColor sets the color of the Paragraph text.
// //
// Example: // Example:
// 1. p := NewParagraph("Red paragraph") // 1. p := NewParagraph("Red paragraph")
@ -176,10 +183,11 @@ func (p *Paragraph) GetMargins() (float64, float64, float64, float64) {
return p.margins.left, p.margins.right, p.margins.top, p.margins.bottom return p.margins.left, p.margins.right, p.margins.top, p.margins.bottom
} }
// SetWidth sets the the Paragraph width. This is essentially the wrapping width, i.e. the width the text can extend to // SetWidth sets the the Paragraph width. This is essentially the wrapping width, i.e. the width the
// prior to wrapping over to next line. // text can extend to prior to wrapping over to next line.
func (p *Paragraph) SetWidth(width float64) { func (p *Paragraph) SetWidth(width float64) {
p.wrapWidth = width p.wrapWidth = width
p.enableWrap = true
p.wrapText() p.wrapText()
} }
@ -187,30 +195,28 @@ func (p *Paragraph) SetWidth(width float64) {
func (p *Paragraph) Width() float64 { func (p *Paragraph) Width() float64 {
if p.enableWrap { if p.enableWrap {
return p.wrapWidth return p.wrapWidth
} else { }
return p.getTextWidth() / 1000.0 return p.getTextWidth() / 1000.0
} }
}
// Height returns the height of the Paragraph. The height is calculated based on the input text and how it is wrapped // Height returns the height of the Paragraph. The height is calculated based on the input text and
// within the container. Does not include Margins. // how it is wrapped within the container. Does not include Margins.
func (p *Paragraph) Height() float64 { func (p *Paragraph) Height() float64 {
if p.textLines == nil || len(p.textLines) == 0 { if p.textLines == nil || len(p.textLines) == 0 {
p.wrapText() p.wrapText()
} }
h := float64(len(p.textLines)) * p.lineHeight * p.fontSize return float64(len(p.textLines)) * p.lineHeight * p.fontSize
return h
} }
// Calculate the text width (if not wrapped). // Calculate the text width (if not wrapped).
func (p *Paragraph) getTextWidth() float64 { func (p *Paragraph) getTextWidth() float64 {
w := float64(0.0) w := 0.0
for _, rune := range p.text { for _, r := range p.text {
glyph, found := p.textFont.Encoder().RuneToGlyph(rune) glyph, found := p.textFont.Encoder().RuneToGlyph(r)
if !found { if !found {
common.Log.Debug("Error! Glyph not found for rune: %s\n", rune) common.Log.Debug("ERROR: Glyph not found for rune: 0x%04x=%c", r, r)
return -1 // XXX/FIXME: return error. return -1 // XXX/FIXME: return error.
} }
@ -221,7 +227,7 @@ func (p *Paragraph) getTextWidth() float64 {
metrics, found := p.textFont.GetGlyphCharMetrics(glyph) metrics, found := p.textFont.GetGlyphCharMetrics(glyph)
if !found { if !found {
common.Log.Debug("Glyph char metrics not found! %s\n", glyph) common.Log.Debug("ERROR: Glyph char metrics not found! %q (rune 0x%04x=%c)", glyph, r, r)
return -1 // XXX/FIXME: return error. return -1 // XXX/FIXME: return error.
} }
w += p.fontSize * metrics.Wx w += p.fontSize * metrics.Wx
@ -234,12 +240,12 @@ func (p *Paragraph) getTextWidth() float64 {
// XXX/TODO: Consider the Knuth/Plass algorithm or an alternative. // XXX/TODO: Consider the Knuth/Plass algorithm or an alternative.
func (p *Paragraph) wrapText() error { func (p *Paragraph) wrapText() error {
if !p.enableWrap { if !p.enableWrap {
p.textLines = []string{p.textFont.Encoder().Encode(p.text)} p.textLines = []string{p.text}
return nil return nil
} }
line := []rune{} line := []rune{}
lineWidth := float64(0.0) lineWidth := 0.0
p.textLines = []string{} p.textLines = []string{}
runes := []rune(p.text) runes := []rune(p.text)
@ -249,8 +255,8 @@ func (p *Paragraph) wrapText() error {
for _, val := range runes { for _, val := range runes {
glyph, found := p.textFont.Encoder().RuneToGlyph(val) glyph, found := p.textFont.Encoder().RuneToGlyph(val)
if !found { if !found {
common.Log.Debug("Error! Glyph not found for rune: %v\n", val) common.Log.Debug("ERROR: Glyph not found for rune: %c", val)
return errors.New("Glyph not found for rune") // XXX/FIXME: return error. return errors.New("Glyph not found for rune")
} }
// Newline wrapping. // Newline wrapping.
@ -266,46 +272,40 @@ func (p *Paragraph) wrapText() error {
metrics, found := p.textFont.GetGlyphCharMetrics(glyph) metrics, found := p.textFont.GetGlyphCharMetrics(glyph)
if !found { if !found {
common.Log.Debug("Glyph char metrics not found! %s (%s)\n", glyph, string(val)) common.Log.Debug("ERROR: Glyph char metrics not found! %q rune=0x%04x=%c font=%s %#q",
glyph, val, val, p.textFont.BaseFont(), p.textFont.Subtype())
common.Log.Trace("Font: %#v", p.textFont) common.Log.Trace("Font: %#v", p.textFont)
common.Log.Trace("Encoder: %#v", p.textFont.Encoder()) common.Log.Trace("Encoder: %#v", p.textFont.Encoder())
return errors.New("Glyph char metrics missing") // XXX/FIXME: return error. return errors.New("Glyph char metrics missing")
} }
w := p.fontSize * metrics.Wx w := p.fontSize * metrics.Wx
if lineWidth+w > p.wrapWidth*1000.0 { if lineWidth+w > p.wrapWidth*1000.0 {
// Goes out of bounds: Wrap. // Goes out of bounds: Wrap.
// Breaks on the character. // Breaks on the character.
// XXX/TODO: when goes outside: back up to next space, otherwise break on the character.
idx := -1 idx := -1
for i := len(glyphs) - 1; i >= 0; i-- { for i := len(glyphs) - 1; i >= 0; i-- {
if glyphs[i] == "space" { if glyphs[i] == "space" { // XXX: What about other space glyphs like controlHT?
idx = i idx = i
break break
} }
} }
if idx > 0 { if idx > 0 {
// Back up to last space.
p.textLines = append(p.textLines, string(line[0:idx+1])) p.textLines = append(p.textLines, string(line[0:idx+1]))
line = line[idx+1:] // Remainder of line.
line = append(line, val) line = append(line[idx+1:], val)
glyphs = append(glyphs[idx+1:], glyph)
glyphs = glyphs[idx+1:] widths = append(widths[idx+1:], w)
glyphs = append(glyphs, glyph) lineWidth = sum(widths)
widths = widths[idx+1:]
widths = append(widths, w)
lineWidth = 0
for _, width := range widths {
lineWidth += width
}
} else { } else {
p.textLines = append(p.textLines, string(line)) p.textLines = append(p.textLines, string(line))
line = []rune{val} line = []rune{val}
lineWidth = w
widths = []float64{w}
glyphs = []string{glyph} glyphs = []string{glyph}
widths = []float64{w}
lineWidth = w
} }
} else { } else {
line = append(line, val) line = append(line, val)
@ -321,15 +321,24 @@ func (p *Paragraph) wrapText() error {
return nil return nil
} }
// GeneratePageBlocks generates the page blocks. Multiple blocks are generated if the contents wrap over // sum returns the sums of the elements in `widths`.
// multiple pages. Implements the Drawable interface. func sum(widths []float64) float64 {
total := 0.0
for _, w := range widths {
total += w
}
return total
}
// GeneratePageBlocks generates the page blocks. Multiple blocks are generated if the contents wrap
// over multiple pages. Implements the Drawable interface.
func (p *Paragraph) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, error) { func (p *Paragraph) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, error) {
origContext := ctx origContext := ctx
blocks := []*Block{} blocks := []*Block{}
blk := NewBlock(ctx.PageWidth, ctx.PageHeight) blk := NewBlock(ctx.PageWidth, ctx.PageHeight)
if p.positioning.isRelative() { if p.positioning.isRelative() {
// Account for Paragraph Margins. // Account for Paragraph margins.
ctx.X += p.margins.left ctx.X += p.margins.left
ctx.Y += p.margins.top ctx.Y += p.margins.top
ctx.Width -= p.margins.left + p.margins.right ctx.Width -= p.margins.left + p.margins.right
@ -339,8 +348,8 @@ func (p *Paragraph) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext,
p.SetWidth(ctx.Width) p.SetWidth(ctx.Width)
if p.Height() > ctx.Height { if p.Height() > ctx.Height {
// Goes out of the bounds. Write on a new template instead and create a new context at upper // Goes out of the bounds. Write on a new template instead and create a new context at
// left corner. // upper left corner.
// XXX/TODO: Handle case when Paragraph is larger than the Page... // XXX/TODO: Handle case when Paragraph is larger than the Page...
// Should be fine if we just break on the paragraph, i.e. splitting it up over 2+ pages // Should be fine if we just break on the paragraph, i.e. splitting it up over 2+ pages
@ -384,7 +393,8 @@ func (p *Paragraph) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext,
} }
} }
// Draw block on specified location on Page, adding to the content stream. // drawParagraphOnBlock draws Paragraph `p` on Block `blk` at the specified location on the page,
// adding it to the content stream.
func drawParagraphOnBlock(blk *Block, p *Paragraph, ctx DrawContext) (DrawContext, error) { func drawParagraphOnBlock(blk *Block, p *Paragraph, ctx DrawContext) (DrawContext, error) {
// Find a free name for the font. // Find a free name for the font.
num := 1 num := 1
@ -428,12 +438,12 @@ func drawParagraphOnBlock(blk *Block, p *Paragraph, ctx DrawContext) (DrawContex
runes := []rune(line) runes := []rune(line)
// Get width of the line (excluding spaces). // Get width of the line (excluding spaces).
w := float64(0) w := 0.0
spaces := 0 spaces := 0
for _, runeVal := range runes { for i, r := range runes {
glyph, found := p.textFont.Encoder().RuneToGlyph(runeVal) glyph, found := p.textFont.Encoder().RuneToGlyph(r)
if !found { if !found {
common.Log.Debug("Rune 0x%x not supported by text encoder", runeVal) common.Log.Debug("Rune 0x%x not supported by text encoder", r)
return ctx, errors.New("Unsupported rune in text encoding") return ctx, errors.New("Unsupported rune in text encoding")
} }
if glyph == "space" { if glyph == "space" {
@ -445,7 +455,9 @@ func drawParagraphOnBlock(blk *Block, p *Paragraph, ctx DrawContext) (DrawContex
} }
metrics, found := p.textFont.GetGlyphCharMetrics(glyph) metrics, found := p.textFont.GetGlyphCharMetrics(glyph)
if !found { if !found {
common.Log.Debug("Unsupported glyph %s in font\n", glyph) common.Log.Debug("Unsupported glyph %q i=%d rune=0x%04x=%c in font %s %s",
glyph, i, r, r,
p.textFont.BaseFont(), p.textFont.Subtype())
return ctx, errors.New("Unsupported text glyph") return ctx, errors.New("Unsupported text glyph")
} }
@ -459,47 +471,51 @@ func drawParagraphOnBlock(blk *Block, p *Paragraph, ctx DrawContext) (DrawContex
return ctx, errors.New("The font does not have a space glyph") return ctx, errors.New("The font does not have a space glyph")
} }
spaceWidth := spaceMetrics.Wx spaceWidth := spaceMetrics.Wx
if p.alignment == TextAlignmentJustify { switch p.alignment {
case TextAlignmentJustify:
if spaces > 0 && idx < len(p.textLines)-1 { // Not to justify last line. if spaces > 0 && idx < len(p.textLines)-1 { // Not to justify last line.
spaceWidth = (p.wrapWidth*1000.0 - w) / float64(spaces) / p.fontSize spaceWidth = (p.wrapWidth*1000.0 - w) / float64(spaces) / p.fontSize
} }
} else if p.alignment == TextAlignmentCenter { case TextAlignmentCenter:
// Start with a shift. // Start with a shift.
textWidth := w + float64(spaces)*spaceWidth*p.fontSize textWidth := w + float64(spaces)*spaceWidth*p.fontSize
shift := (p.wrapWidth*1000.0 - textWidth) / 2 / p.fontSize shift := (p.wrapWidth*1000.0 - textWidth) / 2 / p.fontSize
objs = append(objs, core.MakeFloat(-shift)) objs = append(objs, core.MakeFloat(-shift))
} else if p.alignment == TextAlignmentRight { case TextAlignmentRight:
textWidth := w + float64(spaces)*spaceWidth*p.fontSize textWidth := w + float64(spaces)*spaceWidth*p.fontSize
shift := (p.wrapWidth*1000.0 - textWidth) / p.fontSize shift := (p.wrapWidth*1000.0 - textWidth) / p.fontSize
objs = append(objs, core.MakeFloat(-shift)) objs = append(objs, core.MakeFloat(-shift))
} }
encStr := "" encoded := []byte{}
for _, runeVal := range runes { isCID := p.textFont.IsCID()
//creator.Add_Tj(core.PdfObjectString(tb.Encoder.Encode(line))) for _, r := range runes {
glyph, found := p.textFont.Encoder().RuneToGlyph(runeVal) glyph, ok := p.textFont.Encoder().RuneToGlyph(r)
if !found { if !ok {
common.Log.Debug("Rune 0x%x not supported by text encoder", runeVal) common.Log.Debug("Rune 0x%x not supported by text encoder", r)
return ctx, errors.New("Unsupported rune in text encoding") return ctx, errors.New("Unsupported rune in text encoding")
} }
if glyph == "space" { if glyph == "space" { // XXX: What about \t and other spaces.
if !found { if len(encoded) > 0 {
common.Log.Debug("Unsupported glyph %s in font\n", glyph) objs = append(objs, core.MakeStringFromBytes(encoded))
return ctx, errors.New("Unsupported text glyph") encoded = []byte{}
}
if len(encStr) > 0 {
objs = append(objs, core.MakeString(encStr))
encStr = ""
} }
objs = append(objs, core.MakeFloat(-spaceWidth)) objs = append(objs, core.MakeFloat(-spaceWidth))
} else { } else {
encStr += string(p.textFont.Encoder().Encode(string(runeVal))) code, ok := p.textFont.Encoder().RuneToCharcode(r)
if ok {
if isCID {
hi, lo := code>>8, code&0xff
encoded = append(encoded, byte(hi), byte(lo))
} else {
encoded = append(encoded, byte(code))
} }
} }
if len(encStr) > 0 { }
objs = append(objs, core.MakeString(encStr)) }
if len(encoded) > 0 {
objs = append(objs, core.MakeStringFromBytes(encoded))
} }
cc.Add_TJ(objs...) cc.Add_TJ(objs...)

View File

@ -9,11 +9,11 @@ import (
"fmt" "fmt"
"github.com/unidoc/unidoc/common" "github.com/unidoc/unidoc/common"
"github.com/unidoc/unidoc/pdf/model/fonts" "github.com/unidoc/unidoc/pdf/model"
) )
// Subchapter simply represents a sub chapter pertaining to a specific Chapter. It can contain multiple // Subchapter simply represents a sub chapter pertaining to a specific Chapter. It can contain
// Drawables, just like a chapter. // multiple Drawables, just like a chapter.
type Subchapter struct { type Subchapter struct {
chapterNum int chapterNum int
subchapterNum int subchapterNum int
@ -56,7 +56,8 @@ func (c *Creator) NewSubchapter(ch *Chapter, title string) *Subchapter {
p := NewParagraph(heading) p := NewParagraph(heading)
p.SetFontSize(14) p.SetFontSize(14)
p.SetFont(fonts.NewFontHelvetica()) // bold? helvetica, _ := model.NewStandard14Font("Helvetica")
p.SetFont(helvetica) // bold?
subchap.showNumbering = true subchap.showNumbering = true
subchap.includeInTOC = true subchap.includeInTOC = true
@ -131,8 +132,8 @@ func (subchap *Subchapter) Add(d Drawable) {
} }
} }
// GeneratePageBlocks generates the page blocks. Multiple blocks are generated if the contents wrap over // GeneratePageBlocks generates the page blocks. Multiple blocks are generated if the contents wrap
// multiple pages. Implements the Drawable interface. // over multiple pages. Implements the Drawable interface.
func (subchap *Subchapter) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, error) { func (subchap *Subchapter) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, error) {
origCtx := ctx origCtx := ctx
@ -180,7 +181,6 @@ func (subchap *Subchapter) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawCo
if subchap.positioning.isAbsolute() { if subchap.positioning.isAbsolute() {
// If absolute: return original context. // If absolute: return original context.
return blocks, origCtx, nil return blocks, origCtx, nil
} }
return blocks, ctx, nil return blocks, ctx, nil

View File

@ -9,8 +9,9 @@ import (
"errors" "errors"
"github.com/unidoc/unidoc/common" "github.com/unidoc/unidoc/common"
"github.com/unidoc/unidoc/pdf/model"
"github.com/unidoc/unidoc/pdf/contentstream/draw" "github.com/unidoc/unidoc/pdf/contentstream/draw"
"github.com/unidoc/unidoc/pdf/core"
"github.com/unidoc/unidoc/pdf/model"
) )
// Table allows organizing content in an rows X columns matrix, which can spawn across multiple pages. // Table allows organizing content in an rows X columns matrix, which can spawn across multiple pages.
@ -131,7 +132,8 @@ func (table *Table) CurCol() int {
return curCol return curCol
} }
// SetPos sets the Table's positioning to absolute mode and specifies the upper-left corner coordinates as (x,y). // SetPos sets the Table's positioning to absolute mode and specifies the upper-left corner
// coordinates as (x,y).
// Note that this is only sensible to use when the table does not wrap over multiple pages. // Note that this is only sensible to use when the table does not wrap over multiple pages.
// TODO: Should be able to set width too (not just based on context/relative positioning mode). // TODO: Should be able to set width too (not just based on context/relative positioning mode).
func (table *Table) SetPos(x, y float64) { func (table *Table) SetPos(x, y float64) {
@ -140,7 +142,8 @@ func (table *Table) SetPos(x, y float64) {
table.yPos = y table.yPos = y
} }
// GeneratePageBlocks generate the page blocks. Multiple blocks are generated if the contents wrap over multiple pages. // GeneratePageBlocks generate the page blocks. Multiple blocks are generated if the contents wrap
// over multiple pages.
// Implements the Drawable interface. // Implements the Drawable interface.
func (table *Table) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, error) { func (table *Table) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, error) {
blocks := []*Block{} blocks := []*Block{}
@ -265,6 +268,7 @@ func (table *Table) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext,
r := cell.backgroundColor.R() r := cell.backgroundColor.R()
g := cell.backgroundColor.G() g := cell.backgroundColor.G()
b := cell.backgroundColor.B() b := cell.backgroundColor.B()
border.SetFillColor(ColorRGBFromArithmetic(r, g, b)) border.SetFillColor(ColorRGBFromArithmetic(r, g, b))
} }
@ -295,7 +299,7 @@ func (table *Table) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext,
err := block.Draw(border) err := block.Draw(border)
if err != nil { if err != nil {
common.Log.Debug("Error: %v\n", err) common.Log.Debug("ERROR: %v", err)
} }
if cell.content != nil { if cell.content != nil {
@ -340,7 +344,7 @@ func (table *Table) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext,
err := block.DrawWithContext(cell.content, ctx) err := block.DrawWithContext(cell.content, ctx)
if err != nil { if err != nil {
common.Log.Debug("Error: %v\n", err) common.Log.Debug("ERROR: %v", err)
} }
} }
@ -635,8 +639,8 @@ func (cell *TableCell) SetContent(vd VectorDrawable) error {
cell.content = vd cell.content = vd
default: default:
common.Log.Debug("Error: unsupported cell content type %T\n", vd) common.Log.Debug("ERROR: unsupported cell content type %T", vd)
return errors.New("Type check error") return core.ErrTypeError
} }
return nil return nil

View File

@ -8,6 +8,7 @@ package model
import ( import (
"errors" "errors"
"fmt" "fmt"
"sort"
"strings" "strings"
"github.com/unidoc/unidoc/common" "github.com/unidoc/unidoc/common"
@ -50,6 +51,11 @@ func (font PdfFont) Subtype() string {
return subtype return subtype
} }
// IsCID returns true if the underlying font is CID.
func (font PdfFont) IsCID() bool {
return font.baseFields().isCIDFont()
}
// ToUnicode returns the name of the font's "ToUnicode" field if there is one, or "" if there isn't. // ToUnicode returns the name of the font's "ToUnicode" field if there is one, or "" if there isn't.
func (font PdfFont) ToUnicode() string { func (font PdfFont) ToUnicode() string {
if font.baseFields().toUnicodeCmap == nil { if font.baseFields().toUnicodeCmap == nil {
@ -58,6 +64,12 @@ func (font PdfFont) ToUnicode() string {
return font.baseFields().toUnicodeCmap.Name() return font.baseFields().toUnicodeCmap.Name()
} }
// DefaultFont returns the default font, which is currently the built in Helvetica.
func DefaultFont() *PdfFont {
std := standard14Fonts["Helvetica"]
return &PdfFont{context: &std}
}
// NewStandard14Font returns the standard 14 font named `basefont` as a *PdfFont, or an error if it // NewStandard14Font returns the standard 14 font named `basefont` as a *PdfFont, or an error if it
// `basefont` is not one the standard 14 font names. // `basefont` is not one the standard 14 font names.
func NewStandard14Font(basefont string) (*PdfFont, error) { func NewStandard14Font(basefont string) (*PdfFont, error) {
@ -68,6 +80,104 @@ func NewStandard14Font(basefont string) (*PdfFont, error) {
return &PdfFont{context: &std}, nil return &PdfFont{context: &std}, nil
} }
// NewStandard14FontWithEncoding returns the standard 14 font named `basefont` as a *PdfFont and an
// a SimpleEncoder that encodes all the runes in `alphabet`, or an error if this is not possible.
// An error can occur if`basefont` is not one the standard 14 font names.
func NewStandard14FontWithEncoding(basefont string, alphabet map[rune]int) (*PdfFont, *textencoding.SimpleEncoder, error) {
baseEncoder := "MacRomanEncoding"
common.Log.Trace("NewStandard14FontWithEncoding: basefont=%#q baseEncoder=%#q alphabet=%q",
basefont, baseEncoder, string(sortedAlphabet(alphabet)))
std, ok := standard14Fonts[basefont]
if !ok {
return nil, nil, ErrFontNotSupported
}
encoder, err := textencoding.NewSimpleTextEncoder(baseEncoder, nil)
if err != nil {
return nil, nil, err
}
// glyphCode are the encoding glyphs. We need to match them to the font glyphs.
glyphCode := map[string]byte{}
// slots are the indexes in the encoding where the new character codes are added.
// slots are unused indexes, which are filled first. slots1 are the used indexes.
slots := []byte{}
slots1 := []byte{}
for code := uint16(1); code <= 0xff; code++ {
if glyph, ok := encoder.CodeToGlyph[code]; ok {
glyphCode[glyph] = byte(code)
// Don't overwrite space
if glyph != "space" {
slots1 = append(slots1, byte(code))
}
} else {
slots = append(slots, byte(code))
}
}
slots = append(slots, slots1...)
// `glyphs` are the font glyphs that we need to encode.
glyphs := []string{}
for _, r := range sortedAlphabet(alphabet) {
glyph, ok := textencoding.RuneToGlyph(r)
if !ok {
common.Log.Debug("No glyph for rune 0x%02x=%c", r, r)
continue
}
if _, ok = std.fontMetrics[glyph]; !ok {
common.Log.Trace("Glyph %q (0x%04x=%c)not in font", glyph, r, r)
continue
}
if len(glyphs) >= 255 {
common.Log.Debug("Too many characters for encoding")
break
}
glyphs = append(glyphs, glyph)
}
// Fill the slots, starting with the empty ones.
slotIdx := 0
differences := map[byte]string{}
for _, glyph := range glyphs {
if _, ok := glyphCode[glyph]; !ok {
differences[slots[slotIdx]] = glyph
slotIdx++
}
}
encoder, err = textencoding.NewSimpleTextEncoder(baseEncoder, differences)
return &PdfFont{context: &std}, encoder, err
}
// GetAlphabet returns a map of the runes in `text`.
func GetAlphabet(text string) map[rune]int {
alphabet := map[rune]int{}
for _, r := range text {
alphabet[r]++
}
return alphabet
}
// sortedAlphabet the runes in `alphabet` sorted by frequency.
func sortedAlphabet(alphabet map[rune]int) []rune {
runes := []rune{}
for r := range alphabet {
runes = append(runes, r)
}
sort.Slice(runes, func(i, j int) bool {
ri, rj := runes[i], runes[j]
ni, nj := alphabet[ri], alphabet[rj]
if ni != nj {
return ni < nj
}
return ri < rj
})
return runes
}
// NewPdfFontFromPdfObject loads a PdfFont from the dictionary `fontObj`. If there is a problem an // NewPdfFontFromPdfObject loads a PdfFont from the dictionary `fontObj`. If there is a problem an
// error is returned. // error is returned.
func NewPdfFontFromPdfObject(fontObj core.PdfObject) (*PdfFont, error) { func NewPdfFontFromPdfObject(fontObj core.PdfObject) (*PdfFont, error) {
@ -301,18 +411,18 @@ func (font PdfFont) baseFields() *fontCommon {
// fontCommon represents the fields that are common to all PDF fonts. // fontCommon represents the fields that are common to all PDF fonts.
type fontCommon struct { type fontCommon struct {
// All fonts have these fields // All fonts have these fields.
basefont string // The font's "BaseFont" field. basefont string // The font's "BaseFont" field.
subtype string // The font's "Subtype" field. subtype string // The font's "Subtype" field.
// These are optional fields in the PDF font // These are optional fields in the PDF font
toUnicode core.PdfObject // The stream containing toUnicodeCmap. We keep it around for ToPdfObject. toUnicode core.PdfObject // The stream containing toUnicodeCmap. We keep it around for ToPdfObject.
// These objects are computed from optional fields in the PDF font // These objects are computed from optional fields in the PDF font.
toUnicodeCmap *cmap.CMap // Computed from "ToUnicode" toUnicodeCmap *cmap.CMap // Computed from "ToUnicode"
fontDescriptor *PdfFontDescriptor // Computed from "FontDescriptor" fontDescriptor *PdfFontDescriptor // Computed from "FontDescriptor"
// objectNumber helps us find the font in the PDF being processed. This helps with debugging // objectNumber helps us find the font in the PDF being processed. This helps with debugging.
objectNumber int64 objectNumber int64
} }
@ -348,6 +458,7 @@ func (base fontCommon) String() string {
return fmt.Sprintf("FONT{%s}", base.coreString()) return fmt.Sprintf("FONT{%s}", base.coreString())
} }
// coreString returns the contents of fontCommon.String() without the FONT{} wrapper.
func (base fontCommon) coreString() string { func (base fontCommon) coreString() string {
descriptor := "" descriptor := ""
if base.fontDescriptor != nil { if base.fontDescriptor != nil {

View File

@ -140,9 +140,12 @@ func (font *pdfFontType0) ToPdfObject() core.PdfObject {
font.container.PdfObject = d font.container.PdfObject = d
if font.encoder != nil { if font.Encoding != nil {
d.Set("Encoding", font.Encoding)
} else if font.encoder != nil {
d.Set("Encoding", font.encoder.ToPdfObject()) d.Set("Encoding", font.encoder.ToPdfObject())
} }
if font.DescendantFont != nil { if font.DescendantFont != nil {
// Shall be 1 element array. // Shall be 1 element array.
d.Set("DescendantFonts", core.MakeArray(font.DescendantFont.ToPdfObject())) d.Set("DescendantFonts", core.MakeArray(font.DescendantFont.ToPdfObject()))
@ -195,7 +198,6 @@ type pdfCIDFontType0 struct {
// Table 117 Entries in a CIDFont dictionary (page 269) // Table 117 Entries in a CIDFont dictionary (page 269)
CIDSystemInfo *core.PdfObjectDictionary // (Required) Dictionary that defines the character collection of the CIDFont. See Table 116. CIDSystemInfo *core.PdfObjectDictionary // (Required) Dictionary that defines the character collection of the CIDFont. See Table 116.
FontDescriptor core.PdfObject // (Required) Describes the CIDFonts default metrics other than its glyph widths
} }
// pdfCIDFontType0FromSkeleton returns a pdfCIDFontType0 with its common fields initalized. // pdfCIDFontType0FromSkeleton returns a pdfCIDFontType0 with its common fields initalized.
@ -305,16 +307,20 @@ func (font pdfCIDFontType2) GetGlyphCharMetrics(glyph string) (fonts.CharMetrics
enc := textencoding.NewTrueTypeFontEncoder(font.ttfParser.Chars) enc := textencoding.NewTrueTypeFontEncoder(font.ttfParser.Chars)
// Convert the glyph to character code. // Convert the glyph to character code.
rune, found := enc.GlyphToRune(glyph) r, found := enc.GlyphToRune(glyph)
if !found { if !found {
common.Log.Debug("Unable to convert glyph %q to charcode (identity)", glyph) common.Log.Debug("Unable to convert glyph %q to charcode (identity)", glyph)
return metrics, false return metrics, false
} }
w, found := font.runeToWidthMap[uint16(rune)] w, found := font.runeToWidthMap[uint16(r)]
if !found { if !found {
dw, ok := core.GetInt(font.DW)
if !ok {
return metrics, false return metrics, false
} }
w = int(*dw)
}
metrics.GlyphName = glyph metrics.GlyphName = glyph
metrics.Wx = float64(w) metrics.Wx = float64(w)
@ -432,8 +438,8 @@ func NewCompositePdfFontFromTTFFile(filePath string) (*PdfFont, error) {
// Construct W array. Stores character code to width mappings. // Construct W array. Stores character code to width mappings.
wArr := &core.PdfObjectArray{} wArr := &core.PdfObjectArray{}
i := uint16(0)
for int(i) < len(runes) { for i := uint16(0); int(i) < len(runes); {
j := i + 1 j := i + 1
for int(j) < len(runes) { for int(j) < len(runes) {
@ -505,12 +511,14 @@ func NewCompositePdfFontFromTTFFile(filePath string) (*PdfFont, error) {
} }
descriptor.Flags = core.MakeInteger(int64(flags)) descriptor.Flags = core.MakeInteger(int64(flags))
cidfont.basefont = ttf.PostScriptName
cidfont.fontDescriptor = descriptor
// Make root Type0 font. // Make root Type0 font.
type0 := pdfFontType0{ type0 := pdfFontType0{
fontCommon: fontCommon{ fontCommon: fontCommon{
subtype: "Type0", subtype: "Type0",
basefont: ttf.PostScriptName, basefont: ttf.PostScriptName,
fontDescriptor: descriptor,
}, },
DescendantFont: &PdfFont{ DescendantFont: &PdfFont{
context: cidfont, context: cidfont,

View File

@ -180,11 +180,12 @@ func newSimpleFontFromPdfObject(d *core.PdfObjectDictionary, base *fontCommon, s
} }
// addEncoding adds the encoding to the font. // addEncoding adds the encoding to the font.
// The order of precedence is important // The order of precedence is important.
func (font *pdfFontSimple) addEncoding() error { func (font *pdfFontSimple) addEncoding() error {
var baseEncoder string var baseEncoder string
var differences map[byte]string var differences map[byte]string
var err error var err error
var encoder *textencoding.SimpleEncoder
if font.Encoding != nil { if font.Encoding != nil {
baseEncoder, differences, err = getFontEncoding(font.Encoding) baseEncoder, differences, err = getFontEncoding(font.Encoding)
@ -197,42 +198,42 @@ func (font *pdfFontSimple) addEncoding() error {
common.Log.Trace("addEncoding: BaseFont=%q Subtype=%q Encoding=%s (%T)", base.basefont, common.Log.Trace("addEncoding: BaseFont=%q Subtype=%q Encoding=%s (%T)", base.basefont,
base.subtype, font.Encoding, font.Encoding) base.subtype, font.Encoding, font.Encoding)
encoder, err := textencoding.NewSimpleTextEncoder(baseEncoder, differences) encoder, err = textencoding.NewSimpleTextEncoder(baseEncoder, differences)
if err != nil { if err != nil {
return err return err
} }
font.SetEncoder(encoder)
} }
if font.Encoder() == nil { if encoder == nil {
descriptor := font.fontDescriptor descriptor := font.fontDescriptor
if descriptor != nil { if descriptor != nil {
switch font.subtype { switch font.subtype {
case "Type1": case "Type1":
if descriptor.fontFile != nil && descriptor.fontFile.encoder != nil { if descriptor.fontFile != nil && descriptor.fontFile.encoder != nil {
common.Log.Debug("Using fontFile") common.Log.Debug("Using fontFile")
font.SetEncoder(descriptor.fontFile.encoder) encoder = descriptor.fontFile.encoder
} }
case "TrueType": case "TrueType":
if descriptor.fontFile2 != nil { if descriptor.fontFile2 != nil {
common.Log.Debug("Using FontFile2") common.Log.Debug("Using FontFile2")
encoder, err := descriptor.fontFile2.MakeEncoder() enc, err := descriptor.fontFile2.MakeEncoder()
if err == nil { if err == nil {
font.SetEncoder(encoder) encoder = enc
} }
} }
} }
} }
} }
if encoder != nil {
// At the end, apply the differences. // At the end, apply the differences.
if differences != nil { if differences != nil {
common.Log.Trace("differences=%+v font=%s", differences, font.baseFields()) common.Log.Trace("differences=%+v font=%s", differences, font.baseFields())
if se, ok := font.Encoder().(textencoding.SimpleEncoder); ok { encoder.ApplyDifferences(differences)
se.ApplyDifferences(differences)
font.SetEncoder(se)
} }
font.SetEncoder(encoder)
} }
return nil return nil
} }

View File

@ -16,6 +16,7 @@ import (
"github.com/unidoc/unidoc/pdf/core" "github.com/unidoc/unidoc/pdf/core"
"github.com/unidoc/unidoc/pdf/model" "github.com/unidoc/unidoc/pdf/model"
"github.com/unidoc/unidoc/pdf/model/fonts" "github.com/unidoc/unidoc/pdf/model/fonts"
"github.com/unidoc/unidoc/pdf/model/textencoding"
) )
func init() { func init() {
@ -184,7 +185,7 @@ var charcodeBytesToUnicodeTest = []fontFragmentTest{
185, 186, 187, 188, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 185, 186, 187, 188, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204,
205, 206, 207, 208, 209, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 205, 206, 207, 208, 209, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225,
229, 241, 242, 243, 245}, 229, 241, 242, 243, 245},
" !∀#∃%&∋()+,./0123456789:;<=>?≅ΑΒΧΔΕΦΓΗΙϑΚΛΜΝΟΠΘΡΣΤΥςΩΞΨΖ[∴]⊥_αβχδεφγηιϕκλμνοπθρστυϖω" + " !∀#∃%&∋()+,./0123456789:;<=>?≅ΑΒΧ∆ΕΦΓΗΙϑΚΛΜΝΟΠΘΡΣΤΥςΩΞΨΖ[∴]⊥_αβχδεφγηιϕκλµνοπθρστυϖω" +
"ξψζ{|}∼€ϒ′≤⁄∞ƒ♣♦♥♠↔←↑→↓°±″≥×∝∂•÷≠≡≈…↵ℵℑℜ℘⊗⊕∅∩∪⊃⊇⊄⊂⊆∈∉∠∇∏√⋅¬∧∨⇔⇐⇑⇒⇓◊〈∑〉∫⌠⌡", "ξψζ{|}∼€ϒ′≤⁄∞ƒ♣♦♥♠↔←↑→↓°±″≥×∝∂•÷≠≡≈…↵ℵℑℜ℘⊗⊕∅∩∪⊃⊇⊄⊂⊆∈∉∠∇∏√⋅¬∧∨⇔⇐⇑⇒⇓◊〈∑〉∫⌠⌡",
}, },
fontFragmentTest{"ZapfDingbats built-in", fontFragmentTest{"ZapfDingbats built-in",
@ -252,6 +253,70 @@ var charcodeBytesToUnicodeTest = []fontFragmentTest{
177, 151, 178, 179, 183, 185, 188, 205, 184, 189}, 177, 151, 178, 179, 183, 185, 188, 205, 184, 189},
"‘ł’ “Ł” Ø `o´ it's ˝ˆ˜¯˘˙¨˚ˇªº‹ı›—–—†‡•„…˛¸‰", "‘ł’ “Ł” Ø `o´ it's ˝ˆ˜¯˘˙¨˚ˇªº‹ı›—–—†‡•„…˛¸‰",
}, },
fontFragmentTest{"base glyphs",
"./testdata/font/cover.txt", 11,
[]byte{44, 45, 46, 48, 49, 50, 51, 53, 54, 55, 56, 58, 59,
65, 66, 67, 68, 69, 70, 71, 72,
84, 85,
97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 108, 109, 110, 111,
114, 115, 116, 117},
",-.01235678:;ABCDEFGHTUabcdefghijlmnorstu",
},
fontFragmentTest{"tex glyphs 48->",
"./testdata/font/noise-contrast.txt", 36,
[]byte{33, 48, 65, 104, 149, 253},
"!Ah•ý",
},
fontFragmentTest{"tex2 glyphs ",
"./testdata/font/Weil.txt", 30,
[]byte{55, 0, 1, 2, 20, 24, 33, 50, 102, 103, 104, 105},
"↦−·×≤∼→∈{}⟨⟩",
},
fontFragmentTest{"additional glyphs",
"./testdata/font/noise-contrast.txt", 34,
[]byte{32, 40, 48, 64, 80, 88, 65, 104, 149, 253},
"({∑∑h•ý",
},
fontFragmentTest{".notdef glyphs",
"./testdata/font/lec10.txt", 6,
[]byte{59, 66},
string([]rune{textencoding.MissingCodeRune, textencoding.MissingCodeRune}),
},
fontFragmentTest{"Numbered glyphs pattern 1",
"./testdata/font/v14.txt", 14,
[]byte{24, 25, 26, 27, 29},
" ffifflfffi",
},
fontFragmentTest{"Glyph aliases",
"./testdata/font/townes.txt", 10,
[]byte{2, 3, 4, 5, 6, 7, 1, 8, 9, 5, 1, 10, 9, 5, 48},
"Townes van Zan…",
},
fontFragmentTest{"Glyph `.` extensions. e.g. `integral.disp`",
"./testdata/font/preview.txt", 156,
[]byte{83, 0, 4, 67, 62, 64, 100, 65},
"∫=≈≥∈<d>",
},
fontFragmentTest{"A potpourri of glyph naming conventions",
"./testdata/font/Ingmar.txt", 144,
[]byte{18, 20, 10, 11, 13, 14, 15, 16, 21, 22, 23, 25, 26, 27, 28, 29, 30,
31, 33, 12, 17, 19, 24},
"ʼ8ČŽĆřćĐĭűőftffiflfffičž!fbfkffl\u00a0",
},
fontFragmentTest{"Zapf Dingbats",
"./testdata/font/estimation.txt", 122,
[]byte{2, 3, 4, 5, 8, 9, 10, 11, 12, 13, 14},
"✏✮✁☛❄❍❥❇◆✟✙",
},
fontFragmentTest{"Found these by trial and error",
"./testdata/font/helminths.txt", 19,
[]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
75, 76, 77},
" *ﺏﻁﻝﺍﺔﻴﻠﻜ،ﺕﺭﺘﻌﻤﺎﺠﻲﻨﻘﺩﻬ/ﻙﻭﻕﺃﻡﻋﻓﺴ٢٠٣ﻯﻥﺒﺸﺌﺱﻷ,ﺯﺤﺄﻀـﺓﺫ.)٤(٩ل٥٧٨ﻸﻰ%١ﺇ٦ﺡﻱﻅﻐﺼﻑﺨﺀﻊLM",
},
} }
type fontFragmentTest struct { type fontFragmentTest struct {
@ -294,6 +359,18 @@ func (f *fontFragmentTest) check(t *testing.T) {
if actualText != f.expected { if actualText != f.expected {
t.Errorf("Incorrect decoding. %s\nexpected=%q\n actual=%q", t.Errorf("Incorrect decoding. %s\nexpected=%q\n actual=%q",
f, f.expected, actualText) f, f.expected, actualText)
act, exp := []rune(actualText), []rune(f.expected)
if len(act) != len(exp) {
t.Errorf("\texpected=%d actual=%d", len(exp), len(act))
} else {
for i, a := range act {
e := exp[i]
if a != e {
t.Errorf("\ti=%d expected=0x%04x=%c actual=0x%04x=%c", i, e, e, a, a)
}
}
}
} }
if numChars != len([]rune(actualText)) { if numChars != len([]rune(actualText)) {
t.Errorf("Incorrect numChars. %s numChars=%d expected=%d\n%+v\n%c", t.Errorf("Incorrect numChars. %s numChars=%d expected=%d\n%+v\n%c",

View File

@ -33,7 +33,7 @@ import (
type fontFile struct { type fontFile struct {
name string name string
subtype string subtype string
encoder textencoding.TextEncoder encoder *textencoding.SimpleEncoder
} }
// String returns a human readable description of `fontfile`. // String returns a human readable description of `fontfile`.

View File

@ -44,7 +44,7 @@ import (
) )
// MakeEncoder returns an encoder built from the tables in `rec`. // MakeEncoder returns an encoder built from the tables in `rec`.
func (rec *TtfType) MakeEncoder() (textencoding.SimpleEncoder, error) { func (rec *TtfType) MakeEncoder() (*textencoding.SimpleEncoder, error) {
encoding := map[uint16]string{} encoding := map[uint16]string{}
for code := uint16(0); code <= 256; code++ { for code := uint16(0); code <= 256; code++ {
gid, ok := rec.Chars[code] gid, ok := rec.Chars[code]
@ -151,7 +151,8 @@ func (t *ttfParser) Parse() (TtfType, error) {
return TtfType{}, errors.New("fonts based on PostScript outlines are not supported") return TtfType{}, errors.New("fonts based on PostScript outlines are not supported")
} }
if version != "\x00\x01\x00\x00" { if version != "\x00\x01\x00\x00" {
common.Log.Debug("ERROR: Unrecognized TrueType file format. version=%q", version) // This is not an error. In the font_test.go example axes.txt we see version "true".
common.Log.Debug("Unrecognized TrueType file format. version=%q", version)
} }
numTables := int(t.ReadUShort()) numTables := int(t.ReadUShort())
t.Skip(3 * 2) // searchRange, entrySelector, rangeShift t.Skip(3 * 2) // searchRange, entrySelector, rangeShift
@ -379,7 +380,7 @@ func (t *ttfParser) parseCmapSubtable10(offset10 int64) error {
length = t.ReadULong() length = t.ReadULong()
language = t.ReadULong() language = t.ReadULong()
} }
common.Log.Debug("parseCmapSubtable10: format=%d length=%d language=%d", common.Log.Trace("parseCmapSubtable10: format=%d length=%d language=%d",
format, length, language) format, length, language)
if format != 0 { if format != 0 {
@ -407,7 +408,7 @@ func (t *ttfParser) ParseCmap() error {
if err := t.Seek("cmap"); err != nil { if err := t.Seek("cmap"); err != nil {
return err return err
} }
common.Log.Debug("ParseCmap") common.Log.Trace("ParseCmap")
t.ReadUShort() // version is ignored. t.ReadUShort() // version is ignored.
numTables := int(t.ReadUShort()) numTables := int(t.ReadUShort())
offset10 := int64(0) offset10 := int64(0)
@ -465,6 +466,8 @@ func (t *ttfParser) parseCmapVersion(offset int64) error {
return t.parseCmapFormat0() return t.parseCmapFormat0()
case 6: case 6:
return t.parseCmapFormat6() return t.parseCmapFormat6()
case 12:
return t.parseCmapFormat12()
default: default:
common.Log.Debug("ERROR: Unsupported cmap format=%d", format) common.Log.Debug("ERROR: Unsupported cmap format=%d", format)
return nil // XXX: Can't return an error here if creator_test.go is to pass. return nil // XXX: Can't return an error here if creator_test.go is to pass.
@ -501,6 +504,43 @@ func (t *ttfParser) parseCmapFormat6() error {
return nil return nil
} }
func (t *ttfParser) parseCmapFormat12() error {
numGroups := t.ReadULong()
common.Log.Trace("parseCmapFormat12: %s numGroups=%d", t.rec.String(), numGroups)
for i := uint32(0); i < numGroups; i++ {
firstCode := t.ReadULong()
endCode := t.ReadULong()
startGlyph := t.ReadULong()
if firstCode > 0x0010FFFF || (0xD800 <= firstCode && firstCode <= 0xDFFF) {
return errors.New("Invalid characters codes")
}
if endCode < firstCode || endCode > 0x0010FFFF || (0xD800 <= endCode && endCode <= 0xDFFF) {
return errors.New("Invalid characters codes")
}
for j := uint32(0); j <= endCode-firstCode; j++ {
glyphId := startGlyph + j
// if glyphId >= numGlyphs {
// common.Log.Debug("ERROR: Format 12 cmap contains an invalid glyph index")
// break
// }
if firstCode+j > 0x10FFFF {
common.Log.Debug("Format 12 cmap contains character beyond UCS-4")
}
t.rec.Chars[uint16(i+firstCode)] = uint16(glyphId)
}
}
return nil
}
func (t *ttfParser) ParseName() error { func (t *ttfParser) ParseName() error {
if err := t.Seek("name"); err != nil { if err := t.Seek("name"); err != nil {
return err return err

BIN
pdf/model/testdata/font/Ingmar.txt vendored Executable file

Binary file not shown.

BIN
pdf/model/testdata/font/Weil.txt vendored Executable file

Binary file not shown.

BIN
pdf/model/testdata/font/cover.txt vendored Executable file

Binary file not shown.

BIN
pdf/model/testdata/font/estimation.txt vendored Executable file

Binary file not shown.

BIN
pdf/model/testdata/font/helminths.txt vendored Executable file

Binary file not shown.

BIN
pdf/model/testdata/font/lec10.txt vendored Executable file

Binary file not shown.

BIN
pdf/model/testdata/font/noise-contrast.txt vendored Executable file

Binary file not shown.

BIN
pdf/model/testdata/font/preview.txt vendored Executable file

Binary file not shown.

BIN
pdf/model/testdata/font/townes.txt vendored Executable file

Binary file not shown.

BIN
pdf/model/testdata/font/v14.txt vendored Executable file

Binary file not shown.

View File

@ -16,7 +16,7 @@ type TextEncoder interface {
String() string String() string
// Encode converts the Go unicode string `raw` to a PDF encoded string. // Encode converts the Go unicode string `raw` to a PDF encoded string.
Encode(raw string) string Encode(raw string) []byte
// CharcodeToGlyph returns the glyph name for character code `code`. // CharcodeToGlyph returns the glyph name for character code `code`.
// The bool return flag is true if there was a match, and false otherwise. // The bool return flag is true if there was a match, and false otherwise.
@ -51,17 +51,17 @@ type TextEncoder interface {
// Convenience functions // Convenience functions
// doEncode converts a Go unicode string `raw` to a PDF encoded string using the encoder `enc`. // doEncode converts a Go unicode string `raw` to a PDF encoded string using the encoder `enc`.
func doEncode(enc TextEncoder, raw string) string { func doEncode(enc TextEncoder, raw string) []byte {
encoded := []byte{} encoded := []byte{}
for _, r := range raw { for _, r := range raw {
code, found := enc.RuneToCharcode(r) code, found := enc.RuneToCharcode(r)
if !found { if !found {
common.Log.Debug("Failed to map rune to charcode for rune 0x%X", r) common.Log.Debug("Failed to map rune to charcode for rune 0x%04x", r)
continue continue
} }
encoded = append(encoded, byte(code)) encoded = append(encoded, byte(code))
} }
return string(encoded) return encoded
} }
// doRuneToCharcode converts rune `r` to a PDF character code. // doRuneToCharcode converts rune `r` to a PDF character code.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,146 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Format: Semicolon-delimited fields:
# (1) glyph name
# (2) Unicode scalar value
#
# These mappings are missing in glyphlist.txt
#
angbracketleft;3008
angbracketright;3009
circlecopyrt;00A9
controlNULL;0000
#
# TeX-related mappings using named values
#
angbracketleftbig;2329
angbracketleftBig;2329
angbracketleftbigg;2329
angbracketleftBigg;2329
angbracketrightBig;232A
angbracketrightbig;232A
angbracketrightBigg;232A
angbracketrightbigg;232A
arrowhookleft;21AA
arrowhookright;21A9
arrowlefttophalf;21BC
arrowleftbothalf;21BD
arrownortheast;2197
arrownorthwest;2196
arrowrighttophalf;21C0
arrowrightbothalf;21C1
arrowsoutheast;2198
arrowsouthwest;2199
backslashbig;2216
backslashBig;2216
backslashBigg;2216
backslashbigg;2216
bardbl;2016
bracehtipdownleft;FE37
bracehtipdownright;FE37
bracehtipupleft;FE38
bracehtipupright;FE38
braceleftBig;007B
braceleftbig;007B
braceleftbigg;007B
braceleftBigg;007B
bracerightBig;007D
bracerightbig;007D
bracerightbigg;007D
bracerightBigg;007D
bracketleftbig;005B
bracketleftBig;005B
bracketleftbigg;005B
bracketleftBigg;005B
bracketrightBig;005D
bracketrightbig;005D
bracketrightbigg;005D
bracketrightBigg;005D
ceilingleftbig;2308
ceilingleftBig;2308
ceilingleftBigg;2308
ceilingleftbigg;2308
ceilingrightbig;2309
ceilingrightBig;2309
ceilingrightbigg;2309
ceilingrightBigg;2309
circledotdisplay;2299
circledottext;2299
circlemultiplydisplay;2297
circlemultiplytext;2297
circleplusdisplay;2295
circleplustext;2295
contintegraldisplay;222E
contintegraltext;222E
coproductdisplay;2210
coproducttext;2210
floorleftBig;230A
floorleftbig;230A
floorleftbigg;230A
floorleftBigg;230A
floorrightbig;230B
floorrightBig;230B
floorrightBigg;230B
floorrightbigg;230B
hatwide;0302
hatwider;0302
hatwidest;0302
intercal;1D40
integraldisplay;222B
integraltext;222B
intersectiondisplay;22C2
intersectiontext;22C2
logicalanddisplay;2227
logicalandtext;2227
logicalordisplay;2228
logicalortext;2228
parenleftBig;0028
parenleftbig;0028
parenleftBigg;0028
parenleftbigg;0028
parenrightBig;0029
parenrightbig;0029
parenrightBigg;0029
parenrightbigg;0029
prime;2032
productdisplay;220F
producttext;220F
radicalbig;221A
radicalBig;221A
radicalBigg;221A
radicalbigg;221A
radicalbt;221A
radicaltp;221A
radicalvertex;221A
slashbig;002F
slashBig;002F
slashBigg;002F
slashbigg;002F
summationdisplay;2211
summationtext;2211
tildewide;02DC
tildewider;02DC
tildewidest;02DC
uniondisplay;22C3
unionmultidisplay;228E
unionmultitext;228E
unionsqdisplay;2294
unionsqtext;2294
uniontext;22C3
vextenddouble;2225
vextendsingle;2223
#END

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -30,7 +30,7 @@ func (enc IdentityEncoder) String() string {
} }
// Encode converts the Go unicode string `raw` to a PDF encoded string. // Encode converts the Go unicode string `raw` to a PDF encoded string.
func (enc IdentityEncoder) Encode(raw string) string { func (enc IdentityEncoder) Encode(raw string) []byte {
// runes -> character codes -> bytes // runes -> character codes -> bytes
var encoded bytes.Buffer var encoded bytes.Buffer
for _, r := range raw { for _, r := range raw {
@ -44,7 +44,7 @@ func (enc IdentityEncoder) Encode(raw string) string {
encoded.WriteByte(byte((code & 0xff00) >> 8)) encoded.WriteByte(byte((code & 0xff00) >> 8))
encoded.WriteByte(byte(code & 0xff)) encoded.WriteByte(byte(code & 0xff))
} }
return encoded.String() return encoded.Bytes()
} }
// CharcodeToGlyph returns the glyph name matching character code `code`. // CharcodeToGlyph returns the glyph name matching character code `code`.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,68 @@
/*
* This file is subject to the terms and conditions defined in
* file 'LICENSE.md', which is part of this source code package.
*/
package textencoding
import (
"fmt"
"testing"
"github.com/unidoc/unidoc/common"
)
// This test covers all the standard encodings in simple.go
func init() {
common.SetLogger(common.NewConsoleLogger(common.LogLevelDebug))
}
// TestBasicEncodings checks for known glyph->rune mappings in the standard encodings.
func TestBasicEncodings(t *testing.T) {
for _, test := range testCases {
test.check(t)
}
}
var testCases = []encodingTest{
encodingTest{"MacExpertEncoding", "₂₃₄₅", []string{"twoinferior", "threeinferior", "fourinferior", "fiveinferior"}},
encodingTest{"MacRomanEncoding", "◊fl˝ˇ", []string{"lozenge", "fl", "hungarumlaut", "caron"}},
encodingTest{"PdfDocEncoding", "¾Ðí©", []string{"threequarters", "Eth", "iacute", "copyright"}},
encodingTest{"StandardEncoding", "ºªı„", []string{"ordmasculine", "ordfeminine", "dotlessi", "quotedblbase"}},
encodingTest{"SymbolEncoding", "δ∂ℵ⌡", []string{"delta", "partialdiff", "aleph", "integralbt"}},
encodingTest{"WinAnsiEncoding", "×÷®Ï", []string{"multiply", "divide", "registered", "Idieresis"}},
encodingTest{"ZapfDingbatsEncoding", "☎①➔➨", []string{"a4", "a120", "a160", "a178"}},
}
type encodingTest struct {
encoding string
runes string
glyphs []string
}
func (f *encodingTest) String() string {
return fmt.Sprintf("ENCODING_TEST{%#q}", f.encoding)
}
func (f *encodingTest) check(t *testing.T) {
common.Log.Debug("encodingTest: %s", f)
runes := []rune(f.runes)
if len(runes) != len(f.glyphs) {
t.Fatalf("Bad test %s runes=%d glyphs=%d", f, len(runes), len(f.glyphs))
}
enc, err := NewSimpleTextEncoder(f.encoding, nil)
if err != nil {
t.Fatalf("NewSimpleTextEncoder(%#q) failed. err=%v", f.encoding, err)
}
for i, glyph := range f.glyphs {
expected := runes[i]
r, ok := enc.GlyphToRune(glyph)
if !ok {
t.Fatalf("Encoding %#q has no glyph %q", f.encoding, glyph)
}
if r != expected {
t.Fatalf("%s: Expected 0x%04x=%c. Got 0x%04x=%c", f, r, r, expected, expected)
}
}
}

View File

@ -8,5 +8,5 @@ package textencoding
// NewSymbolEncoder returns a SimpleEncoder that implements SymbolEncoding. // NewSymbolEncoder returns a SimpleEncoder that implements SymbolEncoding.
func NewSymbolEncoder() SimpleEncoder { func NewSymbolEncoder() SimpleEncoder {
enc, _ := NewSimpleTextEncoder("SymbolEncoding", nil) enc, _ := NewSimpleTextEncoder("SymbolEncoding", nil)
return enc return *enc
} }

File diff suppressed because it is too large Load Diff

View File

@ -63,7 +63,7 @@ func (enc TrueTypeFontEncoder) String() string {
} }
// Encode converts the Go unicode string `raw` to a PDF encoded string. // Encode converts the Go unicode string `raw` to a PDF encoded string.
func (enc TrueTypeFontEncoder) Encode(raw string) string { func (enc TrueTypeFontEncoder) Encode(raw string) []byte {
// runes -> character codes -> bytes // runes -> character codes -> bytes
var encoded bytes.Buffer var encoded bytes.Buffer
for _, r := range raw { for _, r := range raw {
@ -77,7 +77,7 @@ func (enc TrueTypeFontEncoder) Encode(raw string) string {
encoded.WriteByte(byte((code & 0xff00) >> 8)) encoded.WriteByte(byte((code & 0xff00) >> 8))
encoded.WriteByte(byte(code & 0xff)) encoded.WriteByte(byte(code & 0xff))
} }
return encoded.String() return encoded.Bytes()
} }
// CharcodeToGlyph returns the glyph name matching character code `code`. // CharcodeToGlyph returns the glyph name matching character code `code`.

View File

@ -8,5 +8,5 @@ package textencoding
// NewWinAnsiTextEncoder returns a SimpleEncoder that implements WinAnsiEncoding. // NewWinAnsiTextEncoder returns a SimpleEncoder that implements WinAnsiEncoding.
func NewWinAnsiTextEncoder() SimpleEncoder { func NewWinAnsiTextEncoder() SimpleEncoder {
enc, _ := NewSimpleTextEncoder("WinAnsiEncoding", nil) enc, _ := NewSimpleTextEncoder("WinAnsiEncoding", nil)
return enc return *enc
} }

View File

@ -8,5 +8,5 @@ package textencoding
// NewZapfDingbatsEncoder returns a SimpleEncoder that implements ZapfDingbatsEncoding. // NewZapfDingbatsEncoder returns a SimpleEncoder that implements ZapfDingbatsEncoding.
func NewZapfDingbatsEncoder() SimpleEncoder { func NewZapfDingbatsEncoder() SimpleEncoder {
enc, _ := NewSimpleTextEncoder("ZapfDingbatsEncoding", nil) enc, _ := NewSimpleTextEncoder("ZapfDingbatsEncoding", nil)
return enc return *enc
} }