mirror of
https://github.com/unidoc/unipdf.git
synced 2025-05-01 22:17:29 +08:00
Merge pull request #185 from peterwilliams97/render.v3.hungarian
Render.v3.hungarian
This commit is contained in:
commit
688ded7dae
@ -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}
|
||||
|
@ -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{}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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...)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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 CIDFont’s 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,
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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`.
|
||||
|
@ -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
BIN
pdf/model/testdata/font/Ingmar.txt
vendored
Executable file
Binary file not shown.
BIN
pdf/model/testdata/font/Weil.txt
vendored
Executable file
BIN
pdf/model/testdata/font/Weil.txt
vendored
Executable file
Binary file not shown.
BIN
pdf/model/testdata/font/cover.txt
vendored
Executable file
BIN
pdf/model/testdata/font/cover.txt
vendored
Executable file
Binary file not shown.
BIN
pdf/model/testdata/font/estimation.txt
vendored
Executable file
BIN
pdf/model/testdata/font/estimation.txt
vendored
Executable file
Binary file not shown.
BIN
pdf/model/testdata/font/helminths.txt
vendored
Executable file
BIN
pdf/model/testdata/font/helminths.txt
vendored
Executable file
Binary file not shown.
BIN
pdf/model/testdata/font/lec10.txt
vendored
Executable file
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
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
BIN
pdf/model/testdata/font/preview.txt
vendored
Executable file
Binary file not shown.
BIN
pdf/model/testdata/font/townes.txt
vendored
Executable file
BIN
pdf/model/testdata/font/townes.txt
vendored
Executable file
Binary file not shown.
BIN
pdf/model/testdata/font/v14.txt
vendored
Executable file
BIN
pdf/model/testdata/font/v14.txt
vendored
Executable file
Binary file not shown.
@ -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.
|
||||
|
4648
pdf/model/textencoding/glyphlist/Unicode.txt
Normal file
4648
pdf/model/textencoding/glyphlist/Unicode.txt
Normal file
File diff suppressed because it is too large
Load Diff
146
pdf/model/textencoding/glyphlist/additional.txt
Normal file
146
pdf/model/textencoding/glyphlist/additional.txt
Normal 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
|
2864
pdf/model/textencoding/glyphlist/unimathsymbols.txt
Normal file
2864
pdf/model/textencoding/glyphlist/unimathsymbols.txt
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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
68
pdf/model/textencoding/simple_test.go
Normal file
68
pdf/model/textencoding/simple_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
@ -8,5 +8,5 @@ package textencoding
|
||||
// NewSymbolEncoder returns a SimpleEncoder that implements SymbolEncoding.
|
||||
func NewSymbolEncoder() SimpleEncoder {
|
||||
enc, _ := NewSimpleTextEncoder("SymbolEncoding", nil)
|
||||
return enc
|
||||
return *enc
|
||||
}
|
||||
|
1126
pdf/model/textencoding/testdata/glyphlist/parse_all_glyphs.go
vendored
Normal file
1126
pdf/model/textencoding/testdata/glyphlist/parse_all_glyphs.go
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -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`.
|
||||
|
@ -8,5 +8,5 @@ package textencoding
|
||||
// NewWinAnsiTextEncoder returns a SimpleEncoder that implements WinAnsiEncoding.
|
||||
func NewWinAnsiTextEncoder() SimpleEncoder {
|
||||
enc, _ := NewSimpleTextEncoder("WinAnsiEncoding", nil)
|
||||
return enc
|
||||
return *enc
|
||||
}
|
||||
|
@ -8,5 +8,5 @@ package textencoding
|
||||
// NewZapfDingbatsEncoder returns a SimpleEncoder that implements ZapfDingbatsEncoding.
|
||||
func NewZapfDingbatsEncoder() SimpleEncoder {
|
||||
enc, _ := NewSimpleTextEncoder("ZapfDingbatsEncoding", nil)
|
||||
return enc
|
||||
return *enc
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user