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.
// 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 {
str := PdfObjectString{val: s}
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.
func MakeHexString(s string) *PdfObjectString {
str := PdfObjectString{val: s, isHex: true}

View File

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

View File

@ -10,11 +10,11 @@ import (
"fmt"
"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
// the same as a book or a report chapter.
// Chapter is used to arrange multiple drawables (paragraphs, images, etc) into a single section.
// The concept is the same as a book or a report chapter.
type Chapter struct {
number int
title string
@ -57,7 +57,8 @@ func (c *Creator) NewChapter(title string) *Chapter {
heading := fmt.Sprintf("%d. %s", c.chapters, title)
p := NewParagraph(heading)
p.SetFontSize(16)
p.SetFont(fonts.NewFontHelvetica()) // bold?
helvetica, _ := model.NewStandard14Font("Helvetica")
p.SetFont(helvetica) // bold?
chap.heading = p
chap.contents = []Drawable{}
@ -113,7 +114,7 @@ func (chap *Chapter) Add(d Drawable) error {
switch d.(type) {
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")
case *Paragraph, *Image, *Block, *Subchapter, *Table, *PageBreak:
chap.contents = append(chap.contents, d)
@ -125,8 +126,8 @@ func (chap *Chapter) Add(d Drawable) error {
return nil
}
// 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.
func (chap *Chapter) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, error) {
origCtx := ctx
@ -175,7 +176,6 @@ func (chap *Chapter) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext,
if chap.positioning.isAbsolute() {
// If absolute: return original context.
return blocks, origCtx, nil
}
return blocks, ctx, nil

View File

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

View File

@ -18,12 +18,10 @@ import (
"github.com/boombuler/barcode"
"github.com/boombuler/barcode/qr"
"github.com/unidoc/unidoc/common"
"github.com/unidoc/unidoc/pdf/contentstream/draw"
"github.com/unidoc/unidoc/pdf/core"
"github.com/unidoc/unidoc/pdf/model"
"github.com/unidoc/unidoc/pdf/model/fonts"
"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) {
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.
// TODO: In the future we would like the paragraph to split up between pages. Split up on line, 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 the paragraph to split up between pages. Split up on line,
// 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.
func TestParagraphWrapping(t *testing.T) {
creator := New()
@ -435,7 +435,8 @@ func TestParagraphWrapping(t *testing.T) {
p.SetMargins(0, 0, 5, 0)
alignments := []TextAlignment{TextAlignmentLeft, TextAlignmentJustify, TextAlignmentCenter, TextAlignmentRight}
alignments := []TextAlignment{TextAlignmentLeft, TextAlignmentJustify, TextAlignmentCenter,
TextAlignmentRight}
for j := 0; j < 25; j++ {
//p.SetAlignment(alignments[j%4])
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 " +
"mollit anim id est laborum.")
alignments := []TextAlignment{TextAlignmentLeft, TextAlignmentJustify, TextAlignmentCenter, TextAlignmentRight}
alignments := []TextAlignment{TextAlignmentLeft, TextAlignmentJustify, TextAlignmentCenter,
TextAlignmentRight}
for j := 0; j < 25; j++ {
//p.SetAlignment(alignments[j%4])
p.SetMargins(50, 50, 50, 50)
@ -499,7 +501,13 @@ func TestParagraphFonts(t *testing.T) {
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 {
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 " +
@ -547,22 +555,7 @@ func TestParagraphStandardFonts(t *testing.T) {
"Symbol",
"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{
"Courier: 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)
}
for idx, font := range fonts {
for idx, name := range names {
p := NewParagraph(texts[idx])
font, err := model.NewStandard14Font(name)
if err != nil {
t.Errorf("Fail: %v", err)
return
}
p.SetFont(font)
p.SetFontSize(12)
p.SetLineHeight(1.2)
@ -597,7 +595,7 @@ func TestParagraphStandardFonts(t *testing.T) {
p.SetEncoder(textencoding.NewZapfDingbatsEncoder())
}
err := creator.Draw(p)
err = creator.Draw(p)
if err != nil {
t.Errorf("Fail: %v\n", err)
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.SetFontSize(fontSize)
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) {
timesBold, _ := model.NewStandard14Font("Times-Bold")
billNo := t.NewCell()
billNo.SetContent(newContent(no, TextAlignmentLeft, fonts.NewFontTimesBold(), 8, ColorBlack))
billNo.SetContent(newContent(no, TextAlignmentLeft, timesBold, 8, ColorBlack))
billDate := t.NewCell()
billDate.SetContent(newContent(date, TextAlignmentCenter, fonts.NewFontTimesBold(), 8, ColorBlack))
billDate.SetContent(newContent(date, TextAlignmentCenter, timesBold, 8, ColorBlack))
billNotes := t.NewCell()
billNotes.SetContent(newContent(notes, TextAlignmentLeft, fonts.NewFontTimesBold(), 8, ColorBlack))
billNotes.SetContent(newContent(notes, TextAlignmentLeft, timesBold, 8, ColorBlack))
billAmount := t.NewCell()
billAmount.SetContent(newContent(amount, TextAlignmentRight, fonts.NewFontTimesBold(), 8, ColorBlack))
billAmount.SetContent(newContent(amount, TextAlignmentRight, timesBold, 8, ColorBlack))
billCon := t.NewCell()
billCon.SetContent(newContent(con, TextAlignmentLeft, fonts.NewFontTimesBold(), 8, ColorBlack))
billCon.SetContent(newContent(con, TextAlignmentLeft, timesBold, 8, ColorBlack))
billRetApplied := t.NewCell()
billRetApplied.SetContent(newContent(retApplied, TextAlignmentRight, fonts.NewFontTimesBold(), 8, ColorBlack))
billRetApplied.SetContent(newContent(retApplied, TextAlignmentRight, timesBold, 8, ColorBlack))
billRet := t.NewCell()
billRet.SetContent(newContent(ret, TextAlignmentLeft, fonts.NewFontTimesBold(), 8, ColorBlack))
billRet.SetContent(newContent(ret, TextAlignmentLeft, timesBold, 8, ColorBlack))
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.
func TestCreatorHendricksReq1(t *testing.T) {
timesRoman, _ := model.NewStandard14Font("Times-Roman")
timesBold, _ := model.NewStandard14Font("Times-Bold")
table := NewTable(3) // Mx4 table
// Default, equal column sizes (4x0.25)...
table.SetColumnWidths(0.35, 0.30, 0.35)
@ -1273,146 +1275,146 @@ func TestCreatorHendricksReq1(t *testing.T) {
projectColorTwo := ColorRed
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)
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.SetBorderLineStyle(draw.LineStyleSolid)
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)
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)
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.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)
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.SetContent(newContent("admin@hendricks.com", TextAlignmentLeft, fonts.NewFontTimesRoman(), 8, ColorBlack))
email.SetContent(newContent("admin@hendricks.com", TextAlignmentLeft, timesRoman, 8, ColorBlack))
table.SkipCells(2)
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.SetColumnWidths(0.20, 0.20, 0.20, 0.20, 0.20)
table2.SkipCells(5)
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.SetContent(newContent("Biggi Group", TextAlignmentLeft, fonts.NewFontTimesBold(), 8, ColorBlack))
projectNameValue.SetContent(newContent("Biggi Group", TextAlignmentLeft, timesBold, 8, ColorBlack))
table2.SkipCells(3)
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.SetContent(newContent("BG:01", TextAlignmentLeft, fonts.NewFontTimesBold(), 8, ColorBlack))
projectIDValue.SetContent(newContent("BG:01", TextAlignmentLeft, timesBold, 8, ColorBlack))
table2.SkipCells(1)
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.SetContent(newContent("Percentage", TextAlignmentLeft, fonts.NewFontTimesRoman(), 8, ColorBlack))
contractTypeValue.SetContent(newContent("Percentage", TextAlignmentLeft, timesRoman, 8, ColorBlack))
projectManager := table2.NewCell()
projectManager.SetContent(newContent("Manager:", TextAlignmentLeft, fonts.NewFontTimesBold(), 8, projectColorOne))
projectManager.SetContent(newContent("Manager:", TextAlignmentLeft, timesBold, 8, projectColorOne))
projectManagerValue := table2.NewCell()
projectManagerValue.SetContent(newContent("SHH", TextAlignmentLeft, fonts.NewFontTimesBold(), 8, ColorBlack))
projectManagerValue.SetContent(newContent("SHH", TextAlignmentLeft, timesBold, 8, ColorBlack))
table2.SkipCells(1)
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.SetContent(newContent("$2,975.00", TextAlignmentLeft, fonts.NewFontTimesRoman(), 8, ColorBlack))
contractAmountValue.SetContent(newContent("$2,975.00", TextAlignmentLeft, timesRoman, 8, ColorBlack))
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.SetContent(newContent("Baggi ehf", TextAlignmentLeft, fonts.NewFontTimesBold(), 8, ColorBlack))
clientIDValue.SetContent(newContent("Baggi ehf", TextAlignmentLeft, timesBold, 8, ColorBlack))
table2.SkipCells(1)
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.SetContent(newContent("", TextAlignmentLeft, fonts.NewFontTimesRoman(), 8, ColorBlack))
retainerAmountValue.SetContent(newContent("", TextAlignmentLeft, timesRoman, 8, ColorBlack))
table3 := NewTable(8)
table3.SetColumnWidths(0.05, 0.10, 0.35, 0.10, 0.10, 0.10, 0.10, 0.10)
table3.SkipCells(8)
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(CellBorderSideBottom, CellBorderStyleSingle, 1)
billNo.SetBorderColor(projectColorOne)
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(CellBorderSideBottom, CellBorderStyleSingle, 1)
billDate.SetBorderColor(projectColorOne)
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(CellBorderSideBottom, CellBorderStyleSingle, 1)
billNotes.SetBorderColor(projectColorOne)
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(CellBorderSideBottom, CellBorderStyleSingle, 1)
billAmount.SetBorderColor(projectColorOne)
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(CellBorderSideBottom, CellBorderStyleSingle, 1)
billCon.SetBorderColor(projectColorOne)
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(CellBorderSideBottom, CellBorderStyleSingle, 1)
billRetApplied.SetBorderColor(projectColorOne)
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(CellBorderSideBottom, CellBorderStyleSingle, 1)
billRet.SetBorderColor(projectColorOne)
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(CellBorderSideBottom, CellBorderStyleSingle, 1)
billNetBill.SetBorderColor(projectColorOne)
@ -1427,24 +1429,24 @@ func TestCreatorHendricksReq1(t *testing.T) {
table3.SkipCells(2 + 8)
totalBill := table3.NewCell()
totalBill.SetContent(newContent("Total: ", TextAlignmentRight, fonts.NewFontTimesBold(), 8, projectColorTwo))
totalBill.SetContent(newContent("Total: ", TextAlignmentRight, timesBold, 8, projectColorTwo))
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(CellBorderSideBottom, CellBorderStyleSingle, 1, )
totalBillAmount.SetBorder(CellBorderSideBottom, CellBorderStyleSingle, 1)
table3.SkipCells(1)
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(CellBorderSideBottom, CellBorderStyleSingle, 1)
table3.SkipCells(1)
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(CellBorderSideBottom, CellBorderStyleSingle, 1)
totalNetAmount.SetBorderLineStyle(draw.LineStyleSolid)
@ -1462,11 +1464,12 @@ func TestCreatorHendricksReq1(t *testing.T) {
}
func TestCreatorTableBorderReq1(t *testing.T) {
timesRoman, _ := model.NewStandard14Font("Times-Roman")
table := NewTable(1) // Mx4 table
table.SetColumnWidths(1)
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)
table2 := NewTable(4) // Mx4 table
@ -1475,43 +1478,43 @@ func TestCreatorTableBorderReq1(t *testing.T) {
table2.SkipCells(4)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
table3 := NewTable(1) // Mx4 table
@ -1520,7 +1523,7 @@ func TestCreatorTableBorderReq1(t *testing.T) {
table3.SkipCells(1)
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.SetBorderLineStyle(draw.LineStyleDashed)
@ -1530,71 +1533,71 @@ func TestCreatorTableBorderReq1(t *testing.T) {
table4.SkipCells(4)
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.SetBorderLineStyle(draw.LineStyleDashed)
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.SetBorderLineStyle(draw.LineStyleDashed)
table4.SkipCells(2)
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.SetBorderLineStyle(draw.LineStyleDashed)
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.SetBorderLineStyle(draw.LineStyleDashed)
table4.SkipCells(2)
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.SetBorderLineStyle(draw.LineStyleDashed)
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.SetBorderLineStyle(draw.LineStyleDashed)
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.SetBorderLineStyle(draw.LineStyleDashed)
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.SetBorderLineStyle(draw.LineStyleDashed)
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.SetBorderLineStyle(draw.LineStyleDashed)
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.SetBorderLineStyle(draw.LineStyleDashed)
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.SetBorderLineStyle(draw.LineStyleDashed)
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.SetBorderLineStyle(draw.LineStyleDashed)
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.SetBorderLineStyle(draw.LineStyleDashed)
@ -1604,7 +1607,7 @@ func TestCreatorTableBorderReq1(t *testing.T) {
table5.SkipCells(1)
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)
table6 := NewTable(4) // Mx4 table
@ -1613,43 +1616,43 @@ func TestCreatorTableBorderReq1(t *testing.T) {
table6.SkipCells(4)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
table7 := NewTable(1) // Mx4 table
@ -1658,7 +1661,7 @@ func TestCreatorTableBorderReq1(t *testing.T) {
table7.SkipCells(1)
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)
table8 := NewTable(4) // Mx4 table
@ -1667,43 +1670,43 @@ func TestCreatorTableBorderReq1(t *testing.T) {
table8.SkipCells(4)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
table9 := NewTable(1) // Mx4 table
@ -1712,7 +1715,7 @@ func TestCreatorTableBorderReq1(t *testing.T) {
table9.SkipCells(1)
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.SetBorderLineStyle(draw.LineStyleDashed)
@ -1722,52 +1725,52 @@ func TestCreatorTableBorderReq1(t *testing.T) {
table10.SkipCells(4)
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.SetBorderLineStyle(draw.LineStyleDashed)
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.SetBorderLineStyle(draw.LineStyleDashed)
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.SetBorderLineStyle(draw.LineStyleDashed)
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.SetBorderLineStyle(draw.LineStyleDashed)
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.SetBorderLineStyle(draw.LineStyleDashed)
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.SetBorderLineStyle(draw.LineStyleDashed)
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.SetBorderLineStyle(draw.LineStyleDashed)
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.SetBorderLineStyle(draw.LineStyleDashed)
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.SetBorderLineStyle(draw.LineStyleDashed)
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.SetBorderLineStyle(draw.LineStyleDashed)
@ -1791,11 +1794,13 @@ func TestCreatorTableBorderReq1(t *testing.T) {
}
func TestCellBorder(t *testing.T) {
timesBold, _ := model.NewStandard14Font("Times-Bold")
table := NewTable(2)
table.SetColumnWidths(0.50, 0.50)
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)
c := New()
@ -1811,8 +1816,16 @@ func TestCellBorder(t *testing.T) {
func TestTableInSubchapter(t *testing.T) {
c := New()
fontRegular := fonts.NewFontHelvetica()
fontBold := fonts.NewFontHelveticaBold()
fontRegular, err := model.NewStandard14Font("Helvetica")
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.SetMargins(0, 0, 40, 0)
@ -1894,7 +1907,7 @@ func TestTableInSubchapter(t *testing.T) {
myPara.SetLineHeight(1.5)
sc.Add(myPara)
err := c.Draw(ch)
err = c.Draw(ch)
if err != nil {
t.Errorf("Fail: %v\n", err)
return

View File

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

View File

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

View File

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

View File

@ -9,8 +9,9 @@ import (
"errors"
"github.com/unidoc/unidoc/common"
"github.com/unidoc/unidoc/pdf/model"
"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.
@ -131,7 +132,8 @@ func (table *Table) CurCol() int {
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.
// TODO: Should be able to set width too (not just based on context/relative positioning mode).
func (table *Table) SetPos(x, y float64) {
@ -140,7 +142,8 @@ func (table *Table) SetPos(x, y float64) {
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.
func (table *Table) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, error) {
blocks := []*Block{}
@ -265,6 +268,7 @@ func (table *Table) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext,
r := cell.backgroundColor.R()
g := cell.backgroundColor.G()
b := cell.backgroundColor.B()
border.SetFillColor(ColorRGBFromArithmetic(r, g, b))
}
@ -295,7 +299,7 @@ func (table *Table) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext,
err := block.Draw(border)
if err != nil {
common.Log.Debug("Error: %v\n", err)
common.Log.Debug("ERROR: %v", err)
}
if cell.content != nil {
@ -340,7 +344,7 @@ func (table *Table) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext,
err := block.DrawWithContext(cell.content, ctx)
if err != nil {
common.Log.Debug("Error: %v\n", err)
common.Log.Debug("ERROR: %v", err)
}
}
@ -380,7 +384,7 @@ type CellBorderSide int
const (
// Left side border.
CellBorderSideLeft CellBorderSide = iota
CellBorderSideLeft CellBorderSide = iota
CellBorderSideRight
CellBorderSideTop
CellBorderSideBottom
@ -635,8 +639,8 @@ func (cell *TableCell) SetContent(vd VectorDrawable) error {
cell.content = vd
default:
common.Log.Debug("Error: unsupported cell content type %T\n", vd)
return errors.New("Type check error")
common.Log.Debug("ERROR: unsupported cell content type %T", vd)
return core.ErrTypeError
}
return nil

View File

@ -8,6 +8,7 @@ package model
import (
"errors"
"fmt"
"sort"
"strings"
"github.com/unidoc/unidoc/common"
@ -50,6 +51,11 @@ func (font PdfFont) Subtype() string {
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.
func (font PdfFont) ToUnicode() string {
if font.baseFields().toUnicodeCmap == nil {
@ -58,6 +64,12 @@ func (font PdfFont) ToUnicode() string {
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
// `basefont` is not one the standard 14 font names.
func NewStandard14Font(basefont string) (*PdfFont, error) {
@ -68,6 +80,104 @@ func NewStandard14Font(basefont string) (*PdfFont, error) {
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
// error is returned.
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.
type fontCommon struct {
// All fonts have these fields
// All fonts have these fields.
basefont string // The font's "BaseFont" field.
subtype string // The font's "Subtype" field.
// These are optional fields in the PDF font
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"
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
}
@ -348,6 +458,7 @@ func (base fontCommon) String() string {
return fmt.Sprintf("FONT{%s}", base.coreString())
}
// coreString returns the contents of fontCommon.String() without the FONT{} wrapper.
func (base fontCommon) coreString() string {
descriptor := ""
if base.fontDescriptor != nil {

View File

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

View File

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

View File

@ -16,6 +16,7 @@ import (
"github.com/unidoc/unidoc/pdf/core"
"github.com/unidoc/unidoc/pdf/model"
"github.com/unidoc/unidoc/pdf/model/fonts"
"github.com/unidoc/unidoc/pdf/model/textencoding"
)
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,
205, 206, 207, 208, 209, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225,
229, 241, 242, 243, 245},
" !∀#∃%&∋()+,./0123456789:;<=>?≅ΑΒΧΔΕΦΓΗΙϑΚΛΜΝΟΠΘΡΣΤΥςΩΞΨΖ[∴]⊥_αβχδεφγηιϕκλμνοπθρστυϖω" +
" !∀#∃%&∋()+,./0123456789:;<=>?≅ΑΒΧ∆ΕΦΓΗΙϑΚΛΜΝΟΠΘΡΣΤΥςΩΞΨΖ[∴]⊥_αβχδεφγηιϕκλµνοπθρστυϖω" +
"ξψζ{|}∼€ϒ′≤⁄∞ƒ♣♦♥♠↔←↑→↓°±″≥×∝∂•÷≠≡≈…↵ℵℑℜ℘⊗⊕∅∩∪⊃⊇⊄⊂⊆∈∉∠∇∏√⋅¬∧∨⇔⇐⇑⇒⇓◊〈∑〉∫⌠⌡",
},
fontFragmentTest{"ZapfDingbats built-in",
@ -252,6 +253,70 @@ var charcodeBytesToUnicodeTest = []fontFragmentTest{
177, 151, 178, 179, 183, 185, 188, 205, 184, 189},
"‘ł’ “Ł” Ø `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 {
@ -294,6 +359,18 @@ func (f *fontFragmentTest) check(t *testing.T) {
if actualText != f.expected {
t.Errorf("Incorrect decoding. %s\nexpected=%q\n actual=%q",
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)) {
t.Errorf("Incorrect numChars. %s numChars=%d expected=%d\n%+v\n%c",

View File

@ -33,7 +33,7 @@ import (
type fontFile struct {
name string
subtype string
encoder textencoding.TextEncoder
encoder *textencoding.SimpleEncoder
}
// 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`.
func (rec *TtfType) MakeEncoder() (textencoding.SimpleEncoder, error) {
func (rec *TtfType) MakeEncoder() (*textencoding.SimpleEncoder, error) {
encoding := map[uint16]string{}
for code := uint16(0); code <= 256; 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")
}
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())
t.Skip(3 * 2) // searchRange, entrySelector, rangeShift
@ -379,7 +380,7 @@ func (t *ttfParser) parseCmapSubtable10(offset10 int64) error {
length = 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)
if format != 0 {
@ -407,7 +408,7 @@ func (t *ttfParser) ParseCmap() error {
if err := t.Seek("cmap"); err != nil {
return err
}
common.Log.Debug("ParseCmap")
common.Log.Trace("ParseCmap")
t.ReadUShort() // version is ignored.
numTables := int(t.ReadUShort())
offset10 := int64(0)
@ -465,6 +466,8 @@ func (t *ttfParser) parseCmapVersion(offset int64) error {
return t.parseCmapFormat0()
case 6:
return t.parseCmapFormat6()
case 12:
return t.parseCmapFormat12()
default:
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.
@ -501,6 +504,43 @@ func (t *ttfParser) parseCmapFormat6() error {
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 {
if err := t.Seek("name"); err != nil {
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
// 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`.
// The bool return flag is true if there was a match, and false otherwise.
@ -51,17 +51,17 @@ type TextEncoder interface {
// Convenience functions
// 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{}
for _, r := range raw {
code, found := enc.RuneToCharcode(r)
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
}
encoded = append(encoded, byte(code))
}
return string(encoded)
return encoded
}
// 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.
func (enc IdentityEncoder) Encode(raw string) string {
func (enc IdentityEncoder) Encode(raw string) []byte {
// runes -> character codes -> bytes
var encoded bytes.Buffer
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 & 0xff))
}
return encoded.String()
return encoded.Bytes()
}
// 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.
func NewSymbolEncoder() SimpleEncoder {
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.
func (enc TrueTypeFontEncoder) Encode(raw string) string {
func (enc TrueTypeFontEncoder) Encode(raw string) []byte {
// runes -> character codes -> bytes
var encoded bytes.Buffer
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 & 0xff))
}
return encoded.String()
return encoded.Bytes()
}
// CharcodeToGlyph returns the glyph name matching character code `code`.

View File

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

View File

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