fix wrong git ignore

This commit is contained in:
Adrian Zürcher
2025-12-15 17:44:00 +01:00
parent ed9f31bb96
commit 8f313c00f0
126 changed files with 70589 additions and 1 deletions

2
.gitignore vendored
View File

@@ -1,2 +1,2 @@
dst dst
*pdf *.pdf

View File

@@ -0,0 +1,126 @@
package annotator
import (
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/contentstream/draw"
pdfcore "gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
pdf "gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model"
)
type CircleAnnotationDef struct {
X float64
Y float64
Width float64
Height float64
FillEnabled bool // Show fill?
FillColor *pdf.PdfColorDeviceRGB
BorderEnabled bool // Show border?
BorderWidth float64
BorderColor *pdf.PdfColorDeviceRGB
Opacity float64 // Alpha value (0-1).
}
// Creates a circle/ellipse annotation object with appearance stream that can be added to page PDF annotations.
func CreateCircleAnnotation(circDef CircleAnnotationDef) (*pdf.PdfAnnotation, error) {
circAnnotation := pdf.NewPdfAnnotationCircle()
if circDef.BorderEnabled {
r, g, b := circDef.BorderColor.R(), circDef.BorderColor.G(), circDef.BorderColor.B()
circAnnotation.C = pdfcore.MakeArrayFromFloats([]float64{r, g, b})
bs := pdf.NewBorderStyle()
bs.SetBorderWidth(circDef.BorderWidth)
circAnnotation.BS = bs.ToPdfObject()
}
if circDef.FillEnabled {
r, g, b := circDef.FillColor.R(), circDef.FillColor.G(), circDef.FillColor.B()
circAnnotation.IC = pdfcore.MakeArrayFromFloats([]float64{r, g, b})
} else {
circAnnotation.IC = pdfcore.MakeArrayFromIntegers([]int{}) // No fill.
}
if circDef.Opacity < 1.0 {
circAnnotation.CA = pdfcore.MakeFloat(circDef.Opacity)
}
// Make the appearance stream (for uniform appearance).
apDict, bbox, err := makeCircleAnnotationAppearanceStream(circDef)
if err != nil {
return nil, err
}
circAnnotation.AP = apDict
circAnnotation.Rect = pdfcore.MakeArrayFromFloats([]float64{bbox.Llx, bbox.Lly, bbox.Urx, bbox.Ury})
return circAnnotation.PdfAnnotation, nil
}
func makeCircleAnnotationAppearanceStream(circDef CircleAnnotationDef) (*pdfcore.PdfObjectDictionary, *pdf.PdfRectangle, error) {
form := pdf.NewXObjectForm()
form.Resources = pdf.NewPdfPageResources()
gsName := ""
if circDef.Opacity < 1.0 {
// Create graphics state with right opacity.
gsState := pdfcore.MakeDict()
gsState.Set("ca", pdfcore.MakeFloat(circDef.Opacity))
gsState.Set("CA", pdfcore.MakeFloat(circDef.Opacity))
err := form.Resources.AddExtGState("gs1", gsState)
if err != nil {
common.Log.Debug("Unable to add extgstate gs1")
return nil, nil, err
}
gsName = "gs1"
}
content, localBbox, globalBbox, err := drawPdfCircle(circDef, gsName)
if err != nil {
return nil, nil, err
}
err = form.SetContentStream(content, nil)
if err != nil {
return nil, nil, err
}
// Local bounding box for the XObject Form.
form.BBox = localBbox.ToPdfObject()
apDict := pdfcore.MakeDict()
apDict.Set("N", form.ToPdfObject())
return apDict, globalBbox, nil
}
func drawPdfCircle(circDef CircleAnnotationDef, gsName string) ([]byte, *pdf.PdfRectangle, *pdf.PdfRectangle, error) {
// The annotation is drawn locally in a relative coordinate system with 0,0 as the origin rather than an offset.
circle := draw.Circle{
X: circDef.X,
Y: circDef.Y,
Width: circDef.Width,
Height: circDef.Height,
FillEnabled: circDef.FillEnabled,
FillColor: circDef.FillColor,
BorderEnabled: circDef.BorderEnabled,
BorderWidth: circDef.BorderWidth,
BorderColor: circDef.BorderColor,
Opacity: circDef.Opacity,
}
content, localBbox, err := circle.Draw(gsName)
if err != nil {
return nil, nil, nil, err
}
// Bounding box - global page coordinate system (with offset).
globalBbox := &pdf.PdfRectangle{}
globalBbox.Llx = circDef.X + localBbox.Llx
globalBbox.Lly = circDef.Y + localBbox.Lly
globalBbox.Urx = circDef.X + localBbox.Urx
globalBbox.Ury = circDef.Y + localBbox.Ury
return content, localBbox, globalBbox, nil
}

View File

@@ -0,0 +1,133 @@
package annotator
import (
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/contentstream/draw"
pdfcore "gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
pdf "gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model"
)
// Defines a line between point 1 (X1,Y1) and point 2 (X2,Y2). The line ending styles can be none (regular line),
// or arrows at either end. The line also has a specified width, color and opacity.
type LineAnnotationDef struct {
X1 float64
Y1 float64
X2 float64
Y2 float64
LineColor *pdf.PdfColorDeviceRGB
Opacity float64 // Alpha value (0-1).
LineWidth float64
LineEndingStyle1 draw.LineEndingStyle // Line ending style of point 1.
LineEndingStyle2 draw.LineEndingStyle // Line ending style of point 2.
}
// Creates a line annotation object that can be added to page PDF annotations.
func CreateLineAnnotation(lineDef LineAnnotationDef) (*pdf.PdfAnnotation, error) {
// Line annotation.
lineAnnotation := pdf.NewPdfAnnotationLine()
// Line endpoint locations.
lineAnnotation.L = pdfcore.MakeArrayFromFloats([]float64{lineDef.X1, lineDef.Y1, lineDef.X2, lineDef.Y2})
// Line endings.
le1 := pdfcore.MakeName("None")
if lineDef.LineEndingStyle1 == draw.LineEndingStyleArrow {
le1 = pdfcore.MakeName("ClosedArrow")
}
le2 := pdfcore.MakeName("None")
if lineDef.LineEndingStyle2 == draw.LineEndingStyleArrow {
le2 = pdfcore.MakeName("ClosedArrow")
}
lineAnnotation.LE = pdfcore.MakeArray(le1, le2)
// Opacity.
if lineDef.Opacity < 1.0 {
lineAnnotation.CA = pdfcore.MakeFloat(lineDef.Opacity)
}
r, g, b := lineDef.LineColor.R(), lineDef.LineColor.G(), lineDef.LineColor.B()
lineAnnotation.IC = pdfcore.MakeArrayFromFloats([]float64{r, g, b}) // fill color of line endings, rgb 0-1.
lineAnnotation.C = pdfcore.MakeArrayFromFloats([]float64{r, g, b}) // line color, rgb 0-1.
bs := pdf.NewBorderStyle()
bs.SetBorderWidth(lineDef.LineWidth) // Line width: 3 points.
lineAnnotation.BS = bs.ToPdfObject()
// Make the appearance stream (for uniform appearance).
apDict, bbox, err := makeLineAnnotationAppearanceStream(lineDef)
if err != nil {
return nil, err
}
lineAnnotation.AP = apDict
// The rect specifies the location and dimensions of the annotation. Technically if the annotation could not
// be displayed if it goes outside these bounds, although rarely enforced.
lineAnnotation.Rect = pdfcore.MakeArrayFromFloats([]float64{bbox.Llx, bbox.Lly, bbox.Urx, bbox.Ury})
return lineAnnotation.PdfAnnotation, nil
}
func makeLineAnnotationAppearanceStream(lineDef LineAnnotationDef) (*pdfcore.PdfObjectDictionary, *pdf.PdfRectangle, error) {
form := pdf.NewXObjectForm()
form.Resources = pdf.NewPdfPageResources()
gsName := ""
if lineDef.Opacity < 1.0 {
// Create graphics state with right opacity.
gsState := pdfcore.MakeDict()
gsState.Set("ca", pdfcore.MakeFloat(lineDef.Opacity))
err := form.Resources.AddExtGState("gs1", gsState)
if err != nil {
common.Log.Debug("Unable to add extgstate gs1")
return nil, nil, err
}
gsName = "gs1"
}
content, localBbox, globalBbox, err := drawPdfLine(lineDef, gsName)
if err != nil {
return nil, nil, err
}
err = form.SetContentStream(content, nil)
if err != nil {
return nil, nil, err
}
// Local bounding box for the XObject Form.
form.BBox = localBbox.ToPdfObject()
apDict := pdfcore.MakeDict()
apDict.Set("N", form.ToPdfObject())
return apDict, globalBbox, nil
}
func drawPdfLine(lineDef LineAnnotationDef, gsName string) ([]byte, *pdf.PdfRectangle, *pdf.PdfRectangle, error) {
// The annotation is drawn locally in a relative coordinate system with 0,0 as the origin rather than an offset.
line := draw.Line{
X1: 0,
Y1: 0,
X2: lineDef.X2 - lineDef.X1,
Y2: lineDef.Y2 - lineDef.Y1,
LineColor: lineDef.LineColor,
Opacity: lineDef.Opacity,
LineWidth: lineDef.LineWidth,
LineEndingStyle1: lineDef.LineEndingStyle1,
LineEndingStyle2: lineDef.LineEndingStyle2,
}
content, localBbox, err := line.Draw(gsName)
if err != nil {
return nil, nil, nil, err
}
// Bounding box - global page coordinate system (with offset).
globalBbox := &pdf.PdfRectangle{}
globalBbox.Llx = lineDef.X1 + localBbox.Llx
globalBbox.Lly = lineDef.Y1 + localBbox.Lly
globalBbox.Urx = lineDef.X1 + localBbox.Urx
globalBbox.Ury = lineDef.Y1 + localBbox.Ury
return content, localBbox, globalBbox, nil
}

View File

@@ -0,0 +1,129 @@
package annotator
import (
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/contentstream/draw"
pdfcore "gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
pdf "gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model"
)
// A rectangle defined with a specified Width and Height and a lower left corner at (X,Y). The rectangle can
// optionally have a border and a filling color.
// The Width/Height includes the border (if any specified).
type RectangleAnnotationDef struct {
X float64
Y float64
Width float64
Height float64
FillEnabled bool // Show fill?
FillColor *pdf.PdfColorDeviceRGB
BorderEnabled bool // Show border?
BorderWidth float64
BorderColor *pdf.PdfColorDeviceRGB
Opacity float64 // Alpha value (0-1).
}
// Creates a rectangle annotation object that can be added to page PDF annotations.
func CreateRectangleAnnotation(rectDef RectangleAnnotationDef) (*pdf.PdfAnnotation, error) {
rectAnnotation := pdf.NewPdfAnnotationSquare()
if rectDef.BorderEnabled {
r, g, b := rectDef.BorderColor.R(), rectDef.BorderColor.G(), rectDef.BorderColor.B()
rectAnnotation.C = pdfcore.MakeArrayFromFloats([]float64{r, g, b})
bs := pdf.NewBorderStyle()
bs.SetBorderWidth(rectDef.BorderWidth)
rectAnnotation.BS = bs.ToPdfObject()
}
if rectDef.FillEnabled {
r, g, b := rectDef.FillColor.R(), rectDef.FillColor.G(), rectDef.FillColor.B()
rectAnnotation.IC = pdfcore.MakeArrayFromFloats([]float64{r, g, b})
} else {
rectAnnotation.IC = pdfcore.MakeArrayFromIntegers([]int{}) // No fill.
}
if rectDef.Opacity < 1.0 {
rectAnnotation.CA = pdfcore.MakeFloat(rectDef.Opacity)
}
// Make the appearance stream (for uniform appearance).
apDict, bbox, err := makeRectangleAnnotationAppearanceStream(rectDef)
if err != nil {
return nil, err
}
rectAnnotation.AP = apDict
rectAnnotation.Rect = pdfcore.MakeArrayFromFloats([]float64{bbox.Llx, bbox.Lly, bbox.Urx, bbox.Ury})
return rectAnnotation.PdfAnnotation, nil
}
func makeRectangleAnnotationAppearanceStream(rectDef RectangleAnnotationDef) (*pdfcore.PdfObjectDictionary, *pdf.PdfRectangle, error) {
form := pdf.NewXObjectForm()
form.Resources = pdf.NewPdfPageResources()
gsName := ""
if rectDef.Opacity < 1.0 {
// Create graphics state with right opacity.
gsState := pdfcore.MakeDict()
gsState.Set("ca", pdfcore.MakeFloat(rectDef.Opacity))
gsState.Set("CA", pdfcore.MakeFloat(rectDef.Opacity))
err := form.Resources.AddExtGState("gs1", gsState)
if err != nil {
common.Log.Debug("Unable to add extgstate gs1")
return nil, nil, err
}
gsName = "gs1"
}
content, localBbox, globalBbox, err := drawPdfRectangle(rectDef, gsName)
if err != nil {
return nil, nil, err
}
err = form.SetContentStream(content, nil)
if err != nil {
return nil, nil, err
}
// Local bounding box for the XObject Form.
form.BBox = localBbox.ToPdfObject()
apDict := pdfcore.MakeDict()
apDict.Set("N", form.ToPdfObject())
return apDict, globalBbox, nil
}
func drawPdfRectangle(rectDef RectangleAnnotationDef, gsName string) ([]byte, *pdf.PdfRectangle, *pdf.PdfRectangle, error) {
// The annotation is drawn locally in a relative coordinate system with 0,0 as the origin rather than an offset.
rect := draw.Rectangle{
X: 0,
Y: 0,
Width: rectDef.Width,
Height: rectDef.Height,
FillEnabled: rectDef.FillEnabled,
FillColor: rectDef.FillColor,
BorderEnabled: rectDef.BorderEnabled,
BorderWidth: 2 * rectDef.BorderWidth,
BorderColor: rectDef.BorderColor,
Opacity: rectDef.Opacity,
}
content, localBbox, err := rect.Draw(gsName)
if err != nil {
return nil, nil, nil, err
}
// Bounding box - global page coordinate system (with offset).
globalBbox := &pdf.PdfRectangle{}
globalBbox.Llx = rectDef.X + localBbox.Llx
globalBbox.Lly = rectDef.Y + localBbox.Lly
globalBbox.Urx = rectDef.X + localBbox.Urx
globalBbox.Ury = rectDef.Y + localBbox.Ury
return content, localBbox, globalBbox, nil
}

401
internal/pdf/cmap/cmap.go Normal file
View File

@@ -0,0 +1,401 @@
package cmap
import (
"bytes"
"errors"
"io"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model/textencoding"
)
// CMap represents a character code to unicode mapping used in PDF files.
type CMap struct {
*cMapParser
// Text encoder to look up runes from input glyph names.
encoder textencoding.TextEncoder
// map of character code to string (sequence of runes) for 1-4 byte codes separately.
codeMap [4]map[uint64]string
name string
ctype int
codespaces []codespace
}
// codespace represents a single codespace range used in the CMap.
type codespace struct {
numBytes int
low uint64
high uint64
}
// Name returns the name of the CMap.
func (cmap *CMap) Name() string {
return cmap.name
}
// Type returns the type of the CMap.
func (cmap *CMap) Type() int {
return cmap.ctype
}
// CharcodeBytesToUnicode converts a byte array of charcodes to a unicode string representation.
func (cmap *CMap) CharcodeBytesToUnicode(src []byte) string {
var buf bytes.Buffer
// Maximum number of possible bytes per code.
maxLen := 4
i := 0
for i < len(src) {
var code uint64
var j int
for j = 0; j < maxLen && i+j < len(src); j++ {
b := src[i+j]
code <<= 8
code |= uint64(b)
tgt, has := cmap.codeMap[j][code]
if has {
buf.WriteString(tgt)
break
} else if j == maxLen-1 || i+j == len(src)-1 {
break
}
}
i += j + 1
}
return buf.String()
}
// CharcodeToUnicode converts a single character code to unicode string.
// Note that CharcodeBytesToUnicode is typically more efficient.
func (cmap *CMap) CharcodeToUnicode(srcCode uint64) string {
// Search through different code lengths.
for numBytes := 1; numBytes <= 4; numBytes++ {
if c, has := cmap.codeMap[numBytes-1][srcCode]; has {
return c
}
}
// Not found.
return "?"
}
// newCMap returns an initialized CMap.
func newCMap() *CMap {
cmap := &CMap{}
cmap.codespaces = []codespace{}
cmap.codeMap = [4]map[uint64]string{}
// Maps for 1-4 bytes are initialized. Minimal overhead if not used (most commonly used are 1-2 bytes).
cmap.codeMap[0] = map[uint64]string{}
cmap.codeMap[1] = map[uint64]string{}
cmap.codeMap[2] = map[uint64]string{}
cmap.codeMap[3] = map[uint64]string{}
return cmap
}
// LoadCmapFromData parses CMap data in memory through a byte vector and returns a CMap which
// can be used for character code to unicode conversion.
func LoadCmapFromData(data []byte) (*CMap, error) {
cmap := newCMap()
cmap.cMapParser = newCMapParser(data)
err := cmap.parse()
if err != nil {
return cmap, err
}
return cmap, nil
}
// parse parses the CMap file and loads into the CMap structure.
func (cmap *CMap) parse() error {
for {
o, err := cmap.parseObject()
if err != nil {
if err == io.EOF {
break
}
common.Log.Debug("error parsing CMap: %v", err)
return err
}
if op, isOp := o.(cmapOperand); isOp {
common.Log.Trace("Operand: %s", op.Operand)
switch op.Operand {
case begincodespacerange:
err := cmap.parseCodespaceRange()
if err != nil {
return err
}
case beginbfchar:
err := cmap.parseBfchar()
if err != nil {
return err
}
case beginbfrange:
err := cmap.parseBfrange()
if err != nil {
return err
}
}
} else if n, isName := o.(cmapName); isName {
if n.Name == cmapname {
o, err := cmap.parseObject()
if err != nil {
if err == io.EOF {
break
}
return err
}
name, ok := o.(cmapName)
if !ok {
return errors.New("CMap name not a name")
}
cmap.name = name.Name
} else if n.Name == cmaptype {
o, err := cmap.parseObject()
if err != nil {
if err == io.EOF {
break
}
return err
}
typeInt, ok := o.(cmapInt)
if !ok {
return errors.New("CMap type not an integer")
}
cmap.ctype = int(typeInt.val)
}
} else {
common.Log.Trace("Unhandled object: %T %#v", o, o)
}
}
return nil
}
// parseCodespaceRange parses the codespace range section of a CMap.
func (cmap *CMap) parseCodespaceRange() error {
for {
o, err := cmap.parseObject()
if err != nil {
if err == io.EOF {
break
}
return err
}
hexLow, isHex := o.(cmapHexString)
if !isHex {
if op, isOperand := o.(cmapOperand); isOperand {
if op.Operand == endcodespacerange {
return nil
}
return errors.New("unexpected operand")
}
}
o, err = cmap.parseObject()
if err != nil {
if err == io.EOF {
break
}
return err
}
hexHigh, ok := o.(cmapHexString)
if !ok {
return errors.New("non-hex high")
}
if hexLow.numBytes != hexHigh.numBytes {
return errors.New("unequal number of bytes in range")
}
low := hexToUint64(hexLow)
high := hexToUint64(hexHigh)
numBytes := hexLow.numBytes
cspace := codespace{numBytes: numBytes, low: low, high: high}
cmap.codespaces = append(cmap.codespaces, cspace)
common.Log.Trace("Codespace low: 0x%X, high: 0x%X", low, high)
}
return nil
}
// parseBfchar parses a bfchar section of a CMap file.
func (cmap *CMap) parseBfchar() error {
for {
// Src code.
o, err := cmap.parseObject()
if err != nil {
if err == io.EOF {
break
}
return err
}
var srcCode uint64
var numBytes int
switch v := o.(type) {
case cmapOperand:
if v.Operand == endbfchar {
return nil
}
return errors.New("unexpected operand")
case cmapHexString:
srcCode = hexToUint64(v)
numBytes = v.numBytes
default:
return errors.New("unexpected type")
}
// Target code.
o, err = cmap.parseObject()
if err != nil {
if err == io.EOF {
break
}
return err
}
var toCode string
switch v := o.(type) {
case cmapOperand:
if v.Operand == endbfchar {
return nil
}
return errors.New("unexpected operand")
case cmapHexString:
toCode = hexToString(v)
case cmapName:
toCode = "?"
if cmap.encoder != nil {
if r, found := cmap.encoder.GlyphToRune(v.Name); found {
toCode = string(r)
}
}
default:
return errors.New("unexpected type")
}
if numBytes <= 0 || numBytes > 4 {
return errors.New("invalid code length")
}
cmap.codeMap[numBytes-1][srcCode] = toCode
}
return nil
}
// parseBfrange parses a bfrange section of a CMap file.
func (cmap *CMap) parseBfrange() error {
for {
// The specifications are in pairs of 3.
// <srcCodeFrom> <srcCodeTo> <target>
// where target can be either <destFrom> as a hex code, or a list.
// Src code from.
var srcCodeFrom uint64
var numBytes int
{
o, err := cmap.parseObject()
if err != nil {
if err == io.EOF {
break
}
return err
}
switch v := o.(type) {
case cmapOperand:
if v.Operand == endbfrange {
return nil
}
return errors.New("unexpected operand")
case cmapHexString:
srcCodeFrom = hexToUint64(v)
numBytes = v.numBytes
default:
return errors.New("unexpected type")
}
}
// Src code to.
var srcCodeTo uint64
{
o, err := cmap.parseObject()
if err != nil {
if err == io.EOF {
break
}
return err
}
switch v := o.(type) {
case cmapOperand:
if v.Operand == endbfrange {
return nil
}
return errors.New("unexpected operand")
case cmapHexString:
srcCodeTo = hexToUint64(v)
default:
return errors.New("unexpected type")
}
}
// target(s).
o, err := cmap.parseObject()
if err != nil {
if err == io.EOF {
break
}
return err
}
if numBytes <= 0 || numBytes > 4 {
return errors.New("invalid code length")
}
switch v := o.(type) {
case cmapArray:
sc := srcCodeFrom
for _, o := range v.Array {
hexs, ok := o.(cmapHexString)
if !ok {
return errors.New("non-hex string in array")
}
cmap.codeMap[numBytes-1][sc] = hexToString(hexs)
sc++
}
if sc != srcCodeTo+1 {
return errors.New("invalid number of items in array")
}
case cmapHexString:
// <srcCodeFrom> <srcCodeTo> <dstCode>, maps [from,to] to [dstCode,dstCode+to-from].
// in hex format.
target := hexToUint64(v)
i := uint64(0)
for sc := srcCodeFrom; sc <= srcCodeTo; sc++ {
r := target + i
cmap.codeMap[numBytes-1][sc] = string(r)
i++
}
default:
return errors.New("unexpected type")
}
}
return nil
}

View File

@@ -0,0 +1,13 @@
package cmap
const (
begincodespacerange = "begincodespacerange"
endcodespacerange = "endcodespacerange"
beginbfchar = "beginbfchar"
endbfchar = "endbfchar"
beginbfrange = "beginbfrange"
endbfrange = "endbfrange"
cmapname = "CMapName"
cmaptype = "CMapType"
)

467
internal/pdf/cmap/parser.go Normal file
View File

@@ -0,0 +1,467 @@
package cmap
import (
"bufio"
"bytes"
"errors"
"fmt"
"io"
"strconv"
"encoding/hex"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
)
// cMapParser parses CMap character to unicode mapping files.
type cMapParser struct {
reader *bufio.Reader
}
// cMapParser creates a new instance of the PDF CMap parser from input data.
func newCMapParser(content []byte) *cMapParser {
parser := cMapParser{}
buffer := bytes.NewBuffer(content)
parser.reader = bufio.NewReader(buffer)
return &parser
}
// Detect the signature at the current file position and parse
// the corresponding object.
func (p *cMapParser) parseObject() (cmapObject, error) {
p.skipSpaces()
for {
bb, err := p.reader.Peek(2)
if err != nil {
return nil, err
}
if bb[0] == '%' {
p.parseComment()
p.skipSpaces()
continue
} else if bb[0] == '/' {
name, err := p.parseName()
return name, err
} else if bb[0] == '(' {
str, err := p.parseString()
return str, err
} else if bb[0] == '[' {
arr, err := p.parseArray()
return arr, err
} else if (bb[0] == '<') && (bb[1] == '<') {
dict, err := p.parseDict()
return dict, err
} else if bb[0] == '<' {
shex, err := p.parseHexString()
return shex, err
} else if core.IsDecimalDigit(bb[0]) || (bb[0] == '-' && core.IsDecimalDigit(bb[1])) {
number, err := p.parseNumber()
if err != nil {
return nil, err
}
return number, nil
} else {
// Operand?
operand, err := p.parseOperand()
if err != nil {
return nil, err
}
return operand, nil
}
}
}
// Skip over any spaces. Returns the number of spaces skipped and
// an error if any.
func (p *cMapParser) skipSpaces() (int, error) {
cnt := 0
for {
bb, err := p.reader.Peek(1)
if err != nil {
return 0, err
}
if core.IsWhiteSpace(bb[0]) {
p.reader.ReadByte()
cnt++
} else {
break
}
}
return cnt, nil
}
// parseComment reads a comment line starting with '%'.
func (p *cMapParser) parseComment() (string, error) {
var r bytes.Buffer
_, err := p.skipSpaces()
if err != nil {
return r.String(), err
}
isFirst := true
for {
bb, err := p.reader.Peek(1)
if err != nil {
common.Log.Debug("error %s", err.Error())
return r.String(), err
}
if isFirst && bb[0] != '%' {
return r.String(), errors.New("comment should start with %")
}
isFirst = false
if (bb[0] != '\r') && (bb[0] != '\n') {
b, _ := p.reader.ReadByte()
r.WriteByte(b)
} else {
break
}
}
return r.String(), nil
}
// Parse a name starting with '/'.
func (p *cMapParser) parseName() (cmapName, error) {
name := ""
nameStarted := false
for {
bb, err := p.reader.Peek(1)
if err == io.EOF {
break // Can happen when loading from object stream.
}
if err != nil {
return cmapName{name}, err
}
if !nameStarted {
// Should always start with '/', otherwise not valid.
if bb[0] == '/' {
nameStarted = true
p.reader.ReadByte()
} else {
common.Log.Debug("error Name starting with %s (% x)", bb, bb)
return cmapName{name}, fmt.Errorf("invalid name: (%c)", bb[0])
}
} else {
if core.IsWhiteSpace(bb[0]) {
break
} else if (bb[0] == '/') || (bb[0] == '[') || (bb[0] == '(') || (bb[0] == ']') || (bb[0] == '<') || (bb[0] == '>') {
break // Looks like start of next statement.
} else if bb[0] == '#' {
hexcode, err := p.reader.Peek(3)
if err != nil {
return cmapName{name}, err
}
p.reader.Discard(3)
code, err := hex.DecodeString(string(hexcode[1:3]))
if err != nil {
return cmapName{name}, err
}
name += string(code)
} else {
b, _ := p.reader.ReadByte()
name += string(b)
}
}
}
return cmapName{name}, nil
}
// A string starts with '(' and ends with ')'.
func (p *cMapParser) parseString() (cmapString, error) {
p.reader.ReadByte()
buf := bytes.Buffer{}
count := 1
for {
bb, err := p.reader.Peek(1)
if err != nil {
return cmapString{buf.String()}, err
}
if bb[0] == '\\' { // Escape sequence.
p.reader.ReadByte() // Skip the escape \ byte.
b, err := p.reader.ReadByte()
if err != nil {
return cmapString{buf.String()}, err
}
// Octal '\ddd' number (base 8).
if core.IsOctalDigit(b) {
bb, err := p.reader.Peek(2)
if err != nil {
return cmapString{buf.String()}, err
}
numeric := []byte{}
numeric = append(numeric, b)
for _, val := range bb {
if core.IsOctalDigit(val) {
numeric = append(numeric, val)
} else {
break
}
}
p.reader.Discard(len(numeric) - 1)
common.Log.Trace("Numeric string \"%s\"", numeric)
code, err := strconv.ParseUint(string(numeric), 8, 32)
if err != nil {
return cmapString{buf.String()}, err
}
buf.WriteByte(byte(code))
continue
}
switch b {
case 'n':
buf.WriteByte('\n')
case 'r':
buf.WriteByte('\r')
case 't':
buf.WriteByte('\t')
case 'b':
buf.WriteByte('\b')
case 'f':
buf.WriteByte('\f')
case '(':
buf.WriteByte('(')
case ')':
buf.WriteByte(')')
case '\\':
buf.WriteByte('\\')
}
continue
} else if bb[0] == '(' {
count++
} else if bb[0] == ')' {
count--
if count == 0 {
p.reader.ReadByte()
break
}
}
b, _ := p.reader.ReadByte()
buf.WriteByte(b)
}
return cmapString{buf.String()}, nil
}
// Starts with '<' ends with '>'.
// Currently not converting the hex codes to characters.
func (p *cMapParser) parseHexString() (cmapHexString, error) {
p.reader.ReadByte()
hextable := []byte("0123456789abcdefABCDEF")
buf := bytes.Buffer{}
//tmp := []byte{}
for {
p.skipSpaces()
bb, err := p.reader.Peek(1)
if err != nil {
return cmapHexString{numBytes: 0, b: []byte("")}, err
}
if bb[0] == '>' {
p.reader.ReadByte()
break
}
b, _ := p.reader.ReadByte()
if bytes.IndexByte(hextable, b) >= 0 {
buf.WriteByte(b)
}
}
if buf.Len()%2 == 1 {
buf.WriteByte('0')
}
numBytes := buf.Len() / 2
hexb, _ := hex.DecodeString(buf.String())
return cmapHexString{numBytes: numBytes, b: hexb}, nil
}
// Starts with '[' ends with ']'. Can contain any kinds of direct objects.
func (p *cMapParser) parseArray() (cmapArray, error) {
arr := cmapArray{}
arr.Array = []cmapObject{}
p.reader.ReadByte()
for {
p.skipSpaces()
bb, err := p.reader.Peek(1)
if err != nil {
return arr, err
}
if bb[0] == ']' {
p.reader.ReadByte()
break
}
obj, err := p.parseObject()
if err != nil {
return arr, err
}
arr.Array = append(arr.Array, obj)
}
return arr, nil
}
// Reads and parses a PDF dictionary object enclosed with '<<' and '>>'
func (p *cMapParser) parseDict() (cmapDict, error) {
common.Log.Trace("Reading PDF Dict!")
dict := makeDict()
// Pass the '<<'
c, _ := p.reader.ReadByte()
if c != '<' {
return dict, errors.New("invalid dict")
}
c, _ = p.reader.ReadByte()
if c != '<' {
return dict, errors.New("invalid dict")
}
for {
p.skipSpaces()
bb, err := p.reader.Peek(2)
if err != nil {
return dict, err
}
if (bb[0] == '>') && (bb[1] == '>') {
p.reader.ReadByte()
p.reader.ReadByte()
break
}
key, err := p.parseName()
common.Log.Trace("Key: %s", key.Name)
if err != nil {
common.Log.Debug("error Returning name err %s", err)
return dict, err
}
p.skipSpaces()
val, err := p.parseObject()
if err != nil {
return dict, err
}
dict.Dict[key.Name] = val
// Skip "def" which optionally follows key value dict definitions in CMaps.
p.skipSpaces()
bb, err = p.reader.Peek(3)
if err != nil {
return dict, err
}
if string(bb) == "def" {
p.reader.Discard(3)
}
}
return dict, nil
}
func (p *cMapParser) parseNumber() (cmapObject, error) {
isFloat := false
allowSigns := true
numStr := bytes.Buffer{}
for {
bb, err := p.reader.Peek(1)
if err == io.EOF {
break
}
if err != nil {
return nil, err
}
if allowSigns && (bb[0] == '-' || bb[0] == '+') {
// Only appear in the beginning, otherwise serves as a delimiter.
b, _ := p.reader.ReadByte()
numStr.WriteByte(b)
allowSigns = false // Only allowed in beginning, and after e (exponential).
} else if core.IsDecimalDigit(bb[0]) {
b, _ := p.reader.ReadByte()
numStr.WriteByte(b)
} else if bb[0] == '.' {
b, _ := p.reader.ReadByte()
numStr.WriteByte(b)
isFloat = true
} else if bb[0] == 'e' {
// Exponential number format.
b, _ := p.reader.ReadByte()
numStr.WriteByte(b)
isFloat = true
allowSigns = true
} else {
break
}
}
if isFloat {
fVal, err := strconv.ParseFloat(numStr.String(), 64)
o := cmapFloat{fVal}
return o, err
}
intVal, err := strconv.ParseInt(numStr.String(), 10, 64)
o := cmapInt{intVal}
return o, err
}
// An operand is a text command represented by a word.
func (p *cMapParser) parseOperand() (cmapOperand, error) {
op := cmapOperand{}
buf := bytes.Buffer{}
for {
bb, err := p.reader.Peek(1)
if err != nil {
if err == io.EOF {
break
}
return op, err
}
if core.IsDelimiter(bb[0]) {
break
}
if core.IsWhiteSpace(bb[0]) {
break
}
b, _ := p.reader.ReadByte()
buf.WriteByte(b)
}
if buf.Len() == 0 {
return op, fmt.Errorf("invalid operand (empty)")
}
op.Operand = buf.String()
return op, nil
}

View File

@@ -0,0 +1,43 @@
package cmap
type cmapObject interface {
}
type cmapName struct {
Name string
}
type cmapOperand struct {
Operand string
}
type cmapHexString struct {
numBytes int // original number of bytes in the raw representation
b []byte
}
type cmapString struct {
String string
}
type cmapArray struct {
Array []cmapObject
}
type cmapDict struct {
Dict map[string]cmapObject
}
type cmapFloat struct {
val float64
}
type cmapInt struct {
val int64
}
func makeDict() cmapDict {
d := cmapDict{}
d.Dict = map[string]cmapObject{}
return d
}

View File

@@ -0,0 +1,29 @@
package cmap
import "bytes"
func hexToUint64(shex cmapHexString) uint64 {
val := uint64(0)
for _, v := range shex.b {
val <<= 8
val |= uint64(v)
}
return val
}
func hexToString(shex cmapHexString) string {
var buf bytes.Buffer
// Assumes unicode in format <HHLL> with 2 bytes HH and LL representing a rune.
for i := 0; i < len(shex.b)-1; i += 2 {
b1 := uint64(shex.b[i])
b2 := uint64(shex.b[i+1])
r := rune((b1 << 8) | b2)
buf.WriteRune(r)
}
return buf.String()
}

View File

@@ -0,0 +1,122 @@
package common
import (
"fmt"
"os"
"path/filepath"
"runtime"
)
type Logger interface {
Error(format string, args ...any)
Warning(format string, args ...any)
Notice(format string, args ...any)
Info(format string, args ...any)
Debug(format string, args ...any)
Trace(format string, args ...any)
}
// Dummy Logger does nothing.
type DummyLogger struct{}
func (this DummyLogger) Error(format string, args ...any) {
}
func (this DummyLogger) Warning(format string, args ...any) {
}
func (this DummyLogger) Notice(format string, args ...any) {
}
func (this DummyLogger) Info(format string, args ...any) {
}
func (this DummyLogger) Debug(format string, args ...any) {
}
func (this DummyLogger) Trace(format string, args ...any) {
}
// Simple Console Logger that the tests use.
type LogLevel int
const (
LogLevelTrace LogLevel = 5
LogLevelDebug LogLevel = 4
LogLevelInfo LogLevel = 3
LogLevelNotice LogLevel = 2
LogLevelWarning LogLevel = 1
LogLevelError LogLevel = 0
)
type ConsoleLogger struct {
LogLevel LogLevel
}
func NewConsoleLogger(logLevel LogLevel) *ConsoleLogger {
logger := ConsoleLogger{}
logger.LogLevel = logLevel
return &logger
}
func (this ConsoleLogger) Error(format string, args ...any) {
if this.LogLevel >= LogLevelError {
prefix := "[ERROR] "
this.output(os.Stdout, prefix, format, args...)
}
}
func (this ConsoleLogger) Warning(format string, args ...any) {
if this.LogLevel >= LogLevelWarning {
prefix := "[WARNING] "
this.output(os.Stdout, prefix, format, args...)
}
}
func (this ConsoleLogger) Notice(format string, args ...any) {
if this.LogLevel >= LogLevelNotice {
prefix := "[NOTICE] "
this.output(os.Stdout, prefix, format, args...)
}
}
func (this ConsoleLogger) Info(format string, args ...any) {
if this.LogLevel >= LogLevelInfo {
prefix := "[INFO] "
this.output(os.Stdout, prefix, format, args...)
}
}
func (this ConsoleLogger) Debug(format string, args ...any) {
if this.LogLevel >= LogLevelDebug {
prefix := "[DEBUG] "
this.output(os.Stdout, prefix, format, args...)
}
}
func (this ConsoleLogger) Trace(format string, args ...any) {
if this.LogLevel >= LogLevelTrace {
prefix := "[TRACE] "
this.output(os.Stdout, prefix, format, args...)
}
}
var Log Logger = DummyLogger{}
func SetLogger(logger Logger) {
Log = logger
}
// output writes `format`, `args` log message prefixed by the source file name, line and `prefix`
func (this ConsoleLogger) output(f *os.File, prefix string, format string, args ...any) {
_, file, line, ok := runtime.Caller(3)
if !ok {
file = "???"
line = 0
} else {
file = filepath.Base(file)
}
src := fmt.Sprintf("%s %s:%d ", prefix, file, line) + format + "\n"
fmt.Fprintf(f, src, args...)
}

View File

@@ -0,0 +1,11 @@
package common
import (
"time"
)
const timeFormat = "2 January 2006 at 15:04"
func UtcTimeFormat(t time.Time) string {
return t.Format(timeFormat) + " UTC"
}

View File

@@ -0,0 +1,17 @@
// Package common contains common properties used by the subpackages.
package common
import (
"time"
)
const releaseYear = 2018
const releaseMonth = 11
const releaseDay = 17
const releaseHour = 11
const releaseMin = 30
// Holds version information, when bumping this make sure to bump the released at stamp also.
const Version = "2.2.0"
var ReleasedAt = time.Date(releaseYear, releaseMonth, releaseDay, releaseHour, releaseMin, 0, 0, time.UTC)

View File

@@ -0,0 +1,7 @@
package contentstream
import "errors"
var (
ErrInvalidOperand = errors.New("invalid operand")
)

View File

@@ -0,0 +1,197 @@
package contentstream
import (
"bytes"
"fmt"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
)
type ContentStreamOperation struct {
Params []core.PdfObject
Operand string
}
type ContentStreamOperations []*ContentStreamOperation
// Check if the content stream operations are fully wrapped (within q ... Q)
func (s *ContentStreamOperations) isWrapped() bool {
if len(*s) < 2 {
return false
}
depth := 0
for _, op := range *s {
switch op.Operand {
case "q":
depth++
case "Q":
depth--
default:
if depth < 1 {
return false
}
}
}
// Should end at depth == 0
return depth == 0
}
// Wrap entire contents within q ... Q. If unbalanced, then adds extra Qs at the end.
// Only does if needed. Ensures that when adding new content, one start with all states
// in the default condition.
func (s *ContentStreamOperations) WrapIfNeeded() *ContentStreamOperations {
if len(*s) == 0 {
// No need to wrap if empty.
return s
}
if s.isWrapped() {
return s
}
*s = append([]*ContentStreamOperation{{Operand: "q"}}, *s...)
depth := 0
for _, op := range *s {
switch op.Operand {
case "q":
depth++
case "Q":
depth--
}
}
for depth > 0 {
*s = append(*s, &ContentStreamOperation{Operand: "Q"})
depth--
}
return s
}
// Convert a set of content stream operations to a content stream byte presentation, i.e. the kind that can be
// stored as a PDF stream or string format.
func (s *ContentStreamOperations) Bytes() []byte {
var buf bytes.Buffer
for _, op := range *s {
if op == nil {
continue
}
if op.Operand == "BI" {
// Inline image requires special handling.
buf.WriteString(op.Operand + "\n")
buf.WriteString(op.Params[0].DefaultWriteString())
} else {
// Default handler.
for _, param := range op.Params {
buf.WriteString(param.DefaultWriteString())
buf.WriteString(" ")
}
buf.WriteString(op.Operand + "\n")
}
}
return buf.Bytes()
}
// ExtractText parses and extracts all text data in content streams and returns as a string.
// Does not take into account Encoding table, the output is simply the character codes.
//
// Deprecated: More advanced text extraction is offered in package extractor with character encoding support.
func (s *ContentStreamParser) ExtractText() (string, error) {
operations, err := s.Parse()
if err != nil {
return "", err
}
inText := false
xPos, yPos := float64(-1), float64(-1)
txt := ""
for _, op := range *operations {
switch op.Operand {
case "BT":
inText = true
case "ET":
inText = false
}
if op.Operand == "Td" || op.Operand == "TD" || op.Operand == "T*" {
// Move to next line...
txt += "\n"
}
if op.Operand == "Tm" {
if len(op.Params) != 6 {
continue
}
xfloat, ok := op.Params[4].(*core.PdfObjectFloat)
if !ok {
xint, ok := op.Params[4].(*core.PdfObjectInteger)
if !ok {
continue
}
xfloat = core.MakeFloat(float64(*xint))
}
yfloat, ok := op.Params[5].(*core.PdfObjectFloat)
if !ok {
yint, ok := op.Params[5].(*core.PdfObjectInteger)
if !ok {
continue
}
yfloat = core.MakeFloat(float64(*yint))
}
if yPos == -1 {
yPos = float64(*yfloat)
} else if yPos > float64(*yfloat) {
txt += "\n"
xPos = float64(*xfloat)
yPos = float64(*yfloat)
continue
}
if xPos == -1 {
xPos = float64(*xfloat)
} else if xPos < float64(*xfloat) {
txt += "\t"
xPos = float64(*xfloat)
}
}
if inText && op.Operand == "TJ" {
if len(op.Params) < 1 {
continue
}
paramList, ok := op.Params[0].(*core.PdfObjectArray)
if !ok {
return "", fmt.Errorf("invalid parameter type, no array (%T)", op.Params[0])
}
for _, obj := range *paramList {
switch v := obj.(type) {
case *core.PdfObjectString:
txt += string(*v)
case *core.PdfObjectFloat:
if *v < -100 {
txt += " "
}
case *core.PdfObjectInteger:
if *v < -100 {
txt += " "
}
}
}
} else if inText && op.Operand == "Tj" {
if len(op.Params) < 1 {
continue
}
param, ok := op.Params[0].(*core.PdfObjectString)
if !ok {
return "", fmt.Errorf("invalid parameter type, not string (%T)", op.Params[0])
}
txt += string(*param)
}
}
return txt, nil
}

View File

@@ -0,0 +1,613 @@
package contentstream
import (
"math"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
)
type ContentCreator struct {
operands ContentStreamOperations
}
func NewContentCreator() *ContentCreator {
creator := &ContentCreator{}
creator.operands = ContentStreamOperations{}
return creator
}
// Get the list of operations.
func (cc *ContentCreator) Operations() *ContentStreamOperations {
return &cc.operands
}
// Convert a set of content stream operations to a content stream byte presentation, i.e. the kind that can be
// stored as a PDF stream or string format.
func (cc *ContentCreator) Bytes() []byte {
return cc.operands.Bytes()
}
// Same as Bytes() except returns as a string for convenience.
func (cc *ContentCreator) String() string {
return string(cc.operands.Bytes())
}
/* Graphics state operators. */
// Save the current graphics state on the stack - push.
func (cc *ContentCreator) Add_q() *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "q"
cc.operands = append(cc.operands, &op)
return cc
}
// Restore the most recently stored state from the stack - pop.
func (cc *ContentCreator) Add_Q() *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "Q"
cc.operands = append(cc.operands, &op)
return cc
}
// Display XObject - image or form.
func (cc *ContentCreator) Add_Do(name core.PdfObjectName) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "Do"
op.Params = makeParamsFromNames([]core.PdfObjectName{name})
cc.operands = append(cc.operands, &op)
return cc
}
// Modify the current transformation matrix (ctm).
func (cc *ContentCreator) Add_cm(a, b, c, d, e, f float64) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "cm"
op.Params = makeParamsFromFloats([]float64{a, b, c, d, e, f})
cc.operands = append(cc.operands, &op)
return cc
}
// Convenience function for generating a cm operation to translate the transformation matrix.
func (cc *ContentCreator) Translate(tx, ty float64) *ContentCreator {
return cc.Add_cm(1, 0, 0, 1, tx, ty)
}
// Convenience function for generating a cm command to scale the transformation matrix.
func (cc *ContentCreator) Scale(sx, sy float64) *ContentCreator {
return cc.Add_cm(sx, 0, 0, sy, 0, 0)
}
// Convenience function for generating a cm command to rotate transformation matrix.
func (cc *ContentCreator) RotateDeg(angle float64) *ContentCreator {
u1 := math.Cos(angle * math.Pi / 180.0)
u2 := math.Sin(angle * math.Pi / 180.0)
u3 := -math.Sin(angle * math.Pi / 180.0)
u4 := math.Cos(angle * math.Pi / 180.0)
return cc.Add_cm(u1, u2, u3, u4, 0, 0)
}
// Set the line width.
func (cc *ContentCreator) Add_w(lineWidth float64) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "w"
op.Params = makeParamsFromFloats([]float64{lineWidth})
cc.operands = append(cc.operands, &op)
return cc
}
// Set the line cap style.
func (cc *ContentCreator) Add_J(lineCapStyle string) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "J"
op.Params = makeParamsFromNames([]core.PdfObjectName{core.PdfObjectName(lineCapStyle)})
cc.operands = append(cc.operands, &op)
return cc
}
// Set the line join style.
func (cc *ContentCreator) Add_j(lineJoinStyle string) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "j"
op.Params = makeParamsFromNames([]core.PdfObjectName{core.PdfObjectName(lineJoinStyle)})
cc.operands = append(cc.operands, &op)
return cc
}
// Set the miter limit.
func (cc *ContentCreator) Add_M(miterlimit float64) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "M"
op.Params = makeParamsFromFloats([]float64{miterlimit})
cc.operands = append(cc.operands, &op)
return cc
}
// Set the line dash pattern.
func (cc *ContentCreator) Add_d(dashArray []int64, dashPhase int64) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "d"
op.Params = []core.PdfObject{}
op.Params = append(op.Params, core.MakeArrayFromIntegers64(dashArray))
op.Params = append(op.Params, core.MakeInteger(dashPhase))
cc.operands = append(cc.operands, &op)
return cc
}
// Set the color rendering intent.
func (cc *ContentCreator) Add_ri(intent core.PdfObjectName) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "ri"
op.Params = makeParamsFromNames([]core.PdfObjectName{intent})
cc.operands = append(cc.operands, &op)
return cc
}
// Set the flatness tolerance.
func (cc *ContentCreator) Add_i(flatness float64) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "i"
op.Params = makeParamsFromFloats([]float64{flatness})
cc.operands = append(cc.operands, &op)
return cc
}
// Set the graphics state.
func (cc *ContentCreator) Add_gs(dictName core.PdfObjectName) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "gs"
op.Params = makeParamsFromNames([]core.PdfObjectName{dictName})
cc.operands = append(cc.operands, &op)
return cc
}
/* Path construction operators. */
// m: Move the current point to (x,y).
func (cc *ContentCreator) Add_m(x, y float64) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "m"
op.Params = makeParamsFromFloats([]float64{x, y})
cc.operands = append(cc.operands, &op)
return cc
}
// l: Append a straight line segment from the current point to (x,y).
func (cc *ContentCreator) Add_l(x, y float64) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "l"
op.Params = makeParamsFromFloats([]float64{x, y})
cc.operands = append(cc.operands, &op)
return cc
}
// c: Append a Bezier curve to the current path from the current point to (x3,y3) with (x1,x1) and (x2,y2) as control
// points.
func (cc *ContentCreator) Add_c(x1, y1, x2, y2, x3, y3 float64) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "c"
op.Params = makeParamsFromFloats([]float64{x1, y1, x2, y2, x3, y3})
cc.operands = append(cc.operands, &op)
return cc
}
// v: Append a Bezier curve to the current path from the current point to (x3,y3) with the current point and (x2,y2) as
// control points.
func (cc *ContentCreator) Add_v(x2, y2, x3, y3 float64) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "v"
op.Params = makeParamsFromFloats([]float64{x2, y2, x3, y3})
cc.operands = append(cc.operands, &op)
return cc
}
// y: Append a Bezier curve to the current path from the current point to (x3,y3) with (x1, y1) and (x3,y3) as
// control points.
func (cc *ContentCreator) Add_y(x1, y1, x3, y3 float64) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "y"
op.Params = makeParamsFromFloats([]float64{x1, y1, x3, y3})
cc.operands = append(cc.operands, &op)
return cc
}
// h: Close the current subpath by adding a line between the current position and the starting position.
func (cc *ContentCreator) Add_h() *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "h"
cc.operands = append(cc.operands, &op)
return cc
}
// re: Append a rectangle to the current path as a complete subpath, with lower left corner (x,y).
func (cc *ContentCreator) Add_re(x, y, width, height float64) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "re"
op.Params = makeParamsFromFloats([]float64{x, y, width, height})
cc.operands = append(cc.operands, &op)
return cc
}
/* Path painting operators. */
// S: stroke the path.
func (cc *ContentCreator) Add_S() *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "S"
cc.operands = append(cc.operands, &op)
return cc
}
// s: Close and stroke the path.
func (cc *ContentCreator) Add_s() *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "s"
cc.operands = append(cc.operands, &op)
return cc
}
// f: Fill the path using the nonzero winding number rule to determine fill region.
func (cc *ContentCreator) Add_f() *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "f"
cc.operands = append(cc.operands, &op)
return cc
}
// f*: Fill the path using the even-odd rule to determine fill region.
func (cc *ContentCreator) Add_f_starred() *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "f*"
cc.operands = append(cc.operands, &op)
return cc
}
// B: Fill and then stroke the path (nonzero winding number rule).
func (cc *ContentCreator) Add_B() *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "B"
cc.operands = append(cc.operands, &op)
return cc
}
// B*: Fill and then stroke the path (even-odd rule).
func (cc *ContentCreator) Add_B_starred() *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "B*"
cc.operands = append(cc.operands, &op)
return cc
}
// b: Close, fill and then stroke the path (nonzero winding number rule).
func (cc *ContentCreator) Add_b() *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "b"
cc.operands = append(cc.operands, &op)
return cc
}
// b*: Close, fill and then stroke the path (even-odd winding number rule).
func (cc *ContentCreator) Add_b_starred() *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "b*"
cc.operands = append(cc.operands, &op)
return cc
}
// n: End the path without filling or stroking.
func (cc *ContentCreator) Add_n() *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "n"
cc.operands = append(cc.operands, &op)
return cc
}
/* Clipping path operators. */
// W: Modify the current clipping path by intersecting with the current path (nonzero winding rule).
func (cc *ContentCreator) Add_W() *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "W"
cc.operands = append(cc.operands, &op)
return cc
}
// W*: Modify the current clipping path by intersecting with the current path (even odd rule).
func (cc *ContentCreator) Add_W_starred() *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "W*"
cc.operands = append(cc.operands, &op)
return cc
}
/* Color operators. */
// CS: Set the current colorspace for stroking operations.
func (cc *ContentCreator) Add_CS(name core.PdfObjectName) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "CS"
op.Params = makeParamsFromNames([]core.PdfObjectName{name})
cc.operands = append(cc.operands, &op)
return cc
}
// cs: Same as CS but for non-stroking operations.
func (cc *ContentCreator) Add_cs(name core.PdfObjectName) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "cs"
op.Params = makeParamsFromNames([]core.PdfObjectName{name})
cc.operands = append(cc.operands, &op)
return cc
}
// SC: Set color for stroking operations. Input: c1, ..., cn.
func (cc *ContentCreator) Add_SC(c ...float64) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "SC"
op.Params = makeParamsFromFloats(c)
cc.operands = append(cc.operands, &op)
return cc
}
// SCN: Same as SC but supports more colorspaces.
func (cc *ContentCreator) Add_SCN(c ...float64) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "SCN"
op.Params = makeParamsFromFloats(c)
cc.operands = append(cc.operands, &op)
return cc
}
// SCN with name attribute (for pattern). Syntax: c1 ... cn name SCN.
func (cc *ContentCreator) Add_SCN_pattern(name core.PdfObjectName, c ...float64) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "SCN"
op.Params = makeParamsFromFloats(c)
op.Params = append(op.Params, core.MakeName(string(name)))
cc.operands = append(cc.operands, &op)
return cc
}
// scn: Same as SC but for nonstroking operations.
func (cc *ContentCreator) Add_scn(c ...float64) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "scn"
op.Params = makeParamsFromFloats(c)
cc.operands = append(cc.operands, &op)
return cc
}
// scn with name attribute (for pattern). Syntax: c1 ... cn name scn.
func (cc *ContentCreator) Add_scn_pattern(name core.PdfObjectName, c ...float64) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "scn"
op.Params = makeParamsFromFloats(c)
op.Params = append(op.Params, core.MakeName(string(name)))
cc.operands = append(cc.operands, &op)
return cc
}
// G: Set the stroking colorspace to DeviceGray and sets the gray level (0-1).
func (cc *ContentCreator) Add_G(gray float64) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "G"
op.Params = makeParamsFromFloats([]float64{gray})
cc.operands = append(cc.operands, &op)
return cc
}
// g: Same as G but used for nonstroking operations.
func (cc *ContentCreator) Add_g(gray float64) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "g"
op.Params = makeParamsFromFloats([]float64{gray})
cc.operands = append(cc.operands, &op)
return cc
}
// RG: Set the stroking colorspace to DeviceRGB and sets the r,g,b colors (0-1 each).
func (cc *ContentCreator) Add_RG(r, g, b float64) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "RG"
op.Params = makeParamsFromFloats([]float64{r, g, b})
cc.operands = append(cc.operands, &op)
return cc
}
// rg: Same as RG but used for nonstroking operations.
func (cc *ContentCreator) Add_rg(r, g, b float64) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "rg"
op.Params = makeParamsFromFloats([]float64{r, g, b})
cc.operands = append(cc.operands, &op)
return cc
}
// K: Set the stroking colorspace to DeviceCMYK and sets the c,m,y,k color (0-1 each component).
func (cc *ContentCreator) Add_K(c, m, y, k float64) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "K"
op.Params = makeParamsFromFloats([]float64{c, m, y, k})
cc.operands = append(cc.operands, &op)
return cc
}
// k: Same as K but used for nonstroking operations.
func (cc *ContentCreator) Add_k(c, m, y, k float64) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "k"
op.Params = makeParamsFromFloats([]float64{c, m, y, k})
cc.operands = append(cc.operands, &op)
return cc
}
/* Shading operators. */
func (cc *ContentCreator) Add_sh(name core.PdfObjectName) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "sh"
op.Params = makeParamsFromNames([]core.PdfObjectName{name})
cc.operands = append(cc.operands, &op)
return cc
}
/* Text related operators */
/* Text state operators */
// BT: Begin text.
func (cc *ContentCreator) Add_BT() *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "BT"
cc.operands = append(cc.operands, &op)
return cc
}
// ET: End text.
func (cc *ContentCreator) Add_ET() *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "ET"
cc.operands = append(cc.operands, &op)
return cc
}
// Tc: Set character spacing.
func (cc *ContentCreator) Add_Tc(charSpace float64) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "Tc"
op.Params = makeParamsFromFloats([]float64{charSpace})
cc.operands = append(cc.operands, &op)
return cc
}
// Tw: Set word spacing.
func (cc *ContentCreator) Add_Tw(wordSpace float64) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "Tw"
op.Params = makeParamsFromFloats([]float64{wordSpace})
cc.operands = append(cc.operands, &op)
return cc
}
// Tz: Set horizontal scaling.
func (cc *ContentCreator) Add_Tz(scale float64) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "Tz"
op.Params = makeParamsFromFloats([]float64{scale})
cc.operands = append(cc.operands, &op)
return cc
}
// TL: Set leading.
func (cc *ContentCreator) Add_TL(leading float64) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "TL"
op.Params = makeParamsFromFloats([]float64{leading})
cc.operands = append(cc.operands, &op)
return cc
}
// Tf: Set font and font size.
func (cc *ContentCreator) Add_Tf(fontName core.PdfObjectName, fontSize float64) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "Tf"
op.Params = makeParamsFromNames([]core.PdfObjectName{fontName})
op.Params = append(op.Params, makeParamsFromFloats([]float64{fontSize})...)
cc.operands = append(cc.operands, &op)
return cc
}
// Tr: Set text rendering mode.
func (cc *ContentCreator) Add_Tr(render int64) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "Tr"
op.Params = makeParamsFromInts([]int64{render})
cc.operands = append(cc.operands, &op)
return cc
}
// Ts: Set text rise.
func (cc *ContentCreator) Add_Ts(rise float64) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "Ts"
op.Params = makeParamsFromFloats([]float64{rise})
cc.operands = append(cc.operands, &op)
return cc
}
/* Text positioning operators. */
// Td: Move to start of next line with offset (tx, ty).
func (cc *ContentCreator) Add_Td(tx, ty float64) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "Td"
op.Params = makeParamsFromFloats([]float64{tx, ty})
cc.operands = append(cc.operands, &op)
return cc
}
// TD: Move to start of next line with offset (tx, ty).
func (cc *ContentCreator) Add_TD(tx, ty float64) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "TD"
op.Params = makeParamsFromFloats([]float64{tx, ty})
cc.operands = append(cc.operands, &op)
return cc
}
// Tm: Set the text line matrix.
func (cc *ContentCreator) Add_Tm(a, b, c, d, e, f float64) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "Tm"
op.Params = makeParamsFromFloats([]float64{a, b, c, d, e, f})
cc.operands = append(cc.operands, &op)
return cc
}
// T*: Move to the start of next line.
func (cc *ContentCreator) Add_Tstar() *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "T*"
cc.operands = append(cc.operands, &op)
return cc
}
/* Text showing operators */
// Tj: Show a text string.
func (cc *ContentCreator) Add_Tj(textstr core.PdfObjectString) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "Tj"
op.Params = makeParamsFromStrings([]core.PdfObjectString{textstr})
cc.operands = append(cc.operands, &op)
return cc
}
// ': Move to next line and show a string.
func (cc *ContentCreator) Add_quote(textstr core.PdfObjectString) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "'"
op.Params = makeParamsFromStrings([]core.PdfObjectString{textstr})
cc.operands = append(cc.operands, &op)
return cc
}
// ”: Move to next line and show a string, using aw and ac as word and character spacing respectively.
func (cc *ContentCreator) Add_quotes(textstr core.PdfObjectString, aw, ac float64) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "''"
op.Params = makeParamsFromFloats([]float64{aw, ac})
op.Params = append(op.Params, makeParamsFromStrings([]core.PdfObjectString{textstr})...)
cc.operands = append(cc.operands, &op)
return cc
}
// TJ. Show one or more text string. Array of numbers (displacement) and strings.
func (cc *ContentCreator) Add_TJ(vals ...core.PdfObject) *ContentCreator {
op := ContentStreamOperation{}
op.Operand = "TJ"
op.Params = []core.PdfObject{core.MakeArray(vals...)}
cc.operands = append(cc.operands, &op)
return cc
}

View File

@@ -0,0 +1,149 @@
package draw
import (
"math"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model"
)
// Cubic bezier curves are defined by:
// R(t) = P0*(1-t)^3 + P1*3*t*(1-t)^2 + P2*3*t^2*(1-t) + P3*t^3
// where P0 is the current point, P1, P2 control points and P3 the final point.
type CubicBezierCurve struct {
P0 Point // Starting point.
P1 Point // Control point 1.
P2 Point // Control point 2.
P3 Point // Final point.
}
func NewCubicBezierCurve(x0, y0, x1, y1, x2, y2, x3, y3 float64) CubicBezierCurve {
curve := CubicBezierCurve{}
curve.P0 = NewPoint(x0, y0)
curve.P1 = NewPoint(x1, y1)
curve.P2 = NewPoint(x2, y2)
curve.P3 = NewPoint(x3, y3)
return curve
}
// Add X,Y offset to all points on a curve.
func (curve CubicBezierCurve) AddOffsetXY(offX, offY float64) CubicBezierCurve {
curve.P0.X += offX
curve.P1.X += offX
curve.P2.X += offX
curve.P3.X += offX
curve.P0.Y += offY
curve.P1.Y += offY
curve.P2.Y += offY
curve.P3.Y += offY
return curve
}
func (curve CubicBezierCurve) GetBounds() model.PdfRectangle {
minX := curve.P0.X
maxX := curve.P0.X
minY := curve.P0.Y
maxY := curve.P0.Y
// 1000 points.
for t := 0.0; t <= 1.0; t += 0.001 {
Rx := curve.P0.X*math.Pow(1-t, 3) +
curve.P1.X*3*t*math.Pow(1-t, 2) +
curve.P2.X*3*math.Pow(t, 2)*(1-t) +
curve.P3.X*math.Pow(t, 3)
Ry := curve.P0.Y*math.Pow(1-t, 3) +
curve.P1.Y*3*t*math.Pow(1-t, 2) +
curve.P2.Y*3*math.Pow(t, 2)*(1-t) +
curve.P3.Y*math.Pow(t, 3)
if Rx < minX {
minX = Rx
}
if Rx > maxX {
maxX = Rx
}
if Ry < minY {
minY = Ry
}
if Ry > maxY {
maxY = Ry
}
}
bounds := model.PdfRectangle{}
bounds.Llx = minX
bounds.Lly = minY
bounds.Urx = maxX
bounds.Ury = maxY
return bounds
}
type CubicBezierPath struct {
Curves []CubicBezierCurve
}
func NewCubicBezierPath() CubicBezierPath {
bpath := CubicBezierPath{}
bpath.Curves = []CubicBezierCurve{}
return bpath
}
func (this CubicBezierPath) AppendCurve(curve CubicBezierCurve) CubicBezierPath {
this.Curves = append(this.Curves, curve)
return this
}
func (bpath CubicBezierPath) Copy() CubicBezierPath {
bpathcopy := CubicBezierPath{}
bpathcopy.Curves = []CubicBezierCurve{}
for _, c := range bpath.Curves {
bpathcopy.Curves = append(bpathcopy.Curves, c)
}
return bpathcopy
}
func (bpath CubicBezierPath) Offset(offX, offY float64) CubicBezierPath {
for i, c := range bpath.Curves {
bpath.Curves[i] = c.AddOffsetXY(offX, offY)
}
return bpath
}
func (bpath CubicBezierPath) GetBoundingBox() Rectangle {
bbox := Rectangle{}
minX := 0.0
maxX := 0.0
minY := 0.0
maxY := 0.0
for idx, c := range bpath.Curves {
curveBounds := c.GetBounds()
if idx == 0 {
minX = curveBounds.Llx
maxX = curveBounds.Urx
minY = curveBounds.Lly
maxY = curveBounds.Ury
continue
}
if curveBounds.Llx < minX {
minX = curveBounds.Llx
}
if curveBounds.Urx > maxX {
maxX = curveBounds.Urx
}
if curveBounds.Lly < minY {
minY = curveBounds.Lly
}
if curveBounds.Ury > maxY {
maxY = curveBounds.Ury
}
}
bbox.X = minX
bbox.Y = minY
bbox.Width = maxX - minX
bbox.Height = maxY - minY
return bbox
}

View File

@@ -0,0 +1,98 @@
package draw
// A path consists of straight line connections between each point defined in an array of points.
type Path struct {
Points []Point
}
func NewPath() Path {
path := Path{}
path.Points = []Point{}
return path
}
func (this Path) AppendPoint(point Point) Path {
this.Points = append(this.Points, point)
return this
}
func (this Path) RemovePoint(number int) Path {
if number < 1 || number > len(this.Points) {
return this
}
idx := number - 1
this.Points = append(this.Points[:idx], this.Points[idx+1:]...)
return this
}
func (this Path) Length() int {
return len(this.Points)
}
func (this Path) GetPointNumber(number int) Point {
if number < 1 || number > len(this.Points) {
return Point{}
}
return this.Points[number-1]
}
func (path Path) Copy() Path {
pathcopy := Path{}
pathcopy.Points = []Point{}
for _, p := range path.Points {
pathcopy.Points = append(pathcopy.Points, p)
}
return pathcopy
}
func (path Path) Offset(offX, offY float64) Path {
for i, p := range path.Points {
path.Points[i] = p.Add(offX, offY)
}
return path
}
func (path Path) GetBoundingBox() BoundingBox {
bbox := BoundingBox{}
minX := 0.0
maxX := 0.0
minY := 0.0
maxY := 0.0
for idx, p := range path.Points {
if idx == 0 {
minX = p.X
maxX = p.X
minY = p.Y
maxY = p.Y
continue
}
if p.X < minX {
minX = p.X
}
if p.X > maxX {
maxX = p.X
}
if p.Y < minY {
minY = p.Y
}
if p.Y > maxY {
maxY = p.Y
}
}
bbox.X = minX
bbox.Y = minY
bbox.Width = maxX - minX
bbox.Height = maxY - minY
return bbox
}
type BoundingBox struct {
X float64
Y float64
Width float64
Height float64
}

View File

@@ -0,0 +1,32 @@
package draw
import "fmt"
type Point struct {
X float64
Y float64
}
func NewPoint(x, y float64) Point {
point := Point{}
point.X = x
point.Y = y
return point
}
func (p Point) Add(dx, dy float64) Point {
p.X += dx
p.Y += dy
return p
}
// Add vector to a point.
func (this Point) AddVector(v Vector) Point {
this.X += v.Dx
this.Y += v.Dy
return this
}
func (p Point) String() string {
return fmt.Sprintf("(%.1f,%.1f)", p.X, p.Y)
}

View File

@@ -0,0 +1,353 @@
package draw
import (
"math"
pdfcontent "gitea.tecamino.com/paadi/pdfmerge/internal/pdf/contentstream"
pdfcore "gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
pdf "gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model"
)
type Circle struct {
X float64
Y float64
Width float64
Height float64
FillEnabled bool // Show fill?
FillColor *pdf.PdfColorDeviceRGB
BorderEnabled bool // Show border?
BorderWidth float64
BorderColor *pdf.PdfColorDeviceRGB
Opacity float64 // Alpha value (0-1).
}
// Draw a circle. Can specify a graphics state (gsName) for setting opacity etc. Otherwise leave empty ("").
// Returns the content stream as a byte array, the bounding box and an error on failure.
func (c Circle) Draw(gsName string) ([]byte, *pdf.PdfRectangle, error) {
xRad := c.Width / 2
yRad := c.Height / 2
if c.BorderEnabled {
xRad -= c.BorderWidth / 2
yRad -= c.BorderWidth / 2
}
magic := 0.551784
xMagic := xRad * magic
yMagic := yRad * magic
bpath := NewCubicBezierPath()
bpath = bpath.AppendCurve(NewCubicBezierCurve(-xRad, 0, -xRad, yMagic, -xMagic, yRad, 0, yRad))
bpath = bpath.AppendCurve(NewCubicBezierCurve(0, yRad, xMagic, yRad, xRad, yMagic, xRad, 0))
bpath = bpath.AppendCurve(NewCubicBezierCurve(xRad, 0, xRad, -yMagic, xMagic, -yRad, 0, -yRad))
bpath = bpath.AppendCurve(NewCubicBezierCurve(0, -yRad, -xMagic, -yRad, -xRad, -yMagic, -xRad, 0))
bpath = bpath.Offset(xRad, yRad)
if c.BorderEnabled {
bpath = bpath.Offset(c.BorderWidth/2, c.BorderWidth/2)
}
if c.X != 0 || c.Y != 0 {
bpath = bpath.Offset(c.X, c.Y)
}
creator := pdfcontent.NewContentCreator()
creator.Add_q()
if c.FillEnabled {
creator.Add_rg(c.FillColor.R(), c.FillColor.G(), c.FillColor.B())
}
if c.BorderEnabled {
creator.Add_RG(c.BorderColor.R(), c.BorderColor.G(), c.BorderColor.B())
creator.Add_w(c.BorderWidth)
}
if len(gsName) > 1 {
// If a graphics state is provided, use it. (Used for transparency settings here).
creator.Add_gs(pdfcore.PdfObjectName(gsName))
}
DrawBezierPathWithCreator(bpath, creator)
creator.Add_h() // Close the path.
if c.FillEnabled && c.BorderEnabled {
creator.Add_B() // fill and stroke.
} else if c.FillEnabled {
creator.Add_f() // Fill.
} else if c.BorderEnabled {
creator.Add_S() // Stroke.
}
creator.Add_Q()
// Get bounding box.
pathBbox := bpath.GetBoundingBox()
if c.BorderEnabled {
// Account for stroke width.
pathBbox.Height += c.BorderWidth
pathBbox.Width += c.BorderWidth
pathBbox.X -= c.BorderWidth / 2
pathBbox.Y -= c.BorderWidth / 2
}
// Bounding box - global coordinate system.
bbox := &pdf.PdfRectangle{}
bbox.Llx = pathBbox.X
bbox.Lly = pathBbox.Y
bbox.Urx = pathBbox.X + pathBbox.Width
bbox.Ury = pathBbox.Y + pathBbox.Height
return creator.Bytes(), bbox, nil
}
// A rectangle defined with a specified Width and Height and a lower left corner at (X,Y). The rectangle can
// optionally have a border and a filling color.
// The Width/Height includes the border (if any specified), i.e. is positioned inside.
type Rectangle struct {
X float64
Y float64
Width float64
Height float64
FillEnabled bool // Show fill?
FillColor *pdf.PdfColorDeviceRGB
BorderEnabled bool // Show border?
BorderWidth float64
BorderColor *pdf.PdfColorDeviceRGB
Opacity float64 // Alpha value (0-1).
}
// Draw the circle. Can specify a graphics state (gsName) for setting opacity etc. Otherwise leave empty ("").
// Returns the content stream as a byte array, bounding box and an error on failure.
func (rect Rectangle) Draw(gsName string) ([]byte, *pdf.PdfRectangle, error) {
path := NewPath()
path = path.AppendPoint(NewPoint(0, 0))
path = path.AppendPoint(NewPoint(0, rect.Height))
path = path.AppendPoint(NewPoint(rect.Width, rect.Height))
path = path.AppendPoint(NewPoint(rect.Width, 0))
path = path.AppendPoint(NewPoint(0, 0))
if rect.X != 0 || rect.Y != 0 {
path = path.Offset(rect.X, rect.Y)
}
creator := pdfcontent.NewContentCreator()
creator.Add_q()
if rect.FillEnabled {
creator.Add_rg(rect.FillColor.R(), rect.FillColor.G(), rect.FillColor.B())
}
if rect.BorderEnabled {
creator.Add_RG(rect.BorderColor.R(), rect.BorderColor.G(), rect.BorderColor.B())
creator.Add_w(rect.BorderWidth)
}
if len(gsName) > 1 {
// If a graphics state is provided, use it. (Used for transparency settings here).
creator.Add_gs(pdfcore.PdfObjectName(gsName))
}
DrawPathWithCreator(path, creator)
creator.Add_h() // Close the path.
if rect.FillEnabled && rect.BorderEnabled {
creator.Add_B() // fill and stroke.
} else if rect.FillEnabled {
creator.Add_f() // Fill.
} else if rect.BorderEnabled {
creator.Add_S() // Stroke.
}
creator.Add_Q()
// Get bounding box.
pathBbox := path.GetBoundingBox()
// Bounding box - global coordinate system.
bbox := &pdf.PdfRectangle{}
bbox.Llx = pathBbox.X
bbox.Lly = pathBbox.Y
bbox.Urx = pathBbox.X + pathBbox.Width
bbox.Ury = pathBbox.Y + pathBbox.Height
return creator.Bytes(), bbox, nil
}
// The currently supported line ending styles are None, Arrow (ClosedArrow) and Butt.
type LineEndingStyle int
const (
LineEndingStyleNone LineEndingStyle = 0
LineEndingStyleArrow LineEndingStyle = 1
LineEndingStyleButt LineEndingStyle = 2
)
// Defines a line between point 1 (X1,Y1) and point 2 (X2,Y2). The line ending styles can be none (regular line),
// or arrows at either end. The line also has a specified width, color and opacity.
type Line struct {
X1 float64
Y1 float64
X2 float64
Y2 float64
LineColor *pdf.PdfColorDeviceRGB
Opacity float64 // Alpha value (0-1).
LineWidth float64
LineEndingStyle1 LineEndingStyle // Line ending style of point 1.
LineEndingStyle2 LineEndingStyle // Line ending style of point 2.
}
// Draw a line in PDF. Generates the content stream which can be used in page contents or appearance stream of annotation.
// Returns the stream content, XForm bounding box (local), bounding box and an error if one occurred.
func (line Line) Draw(gsName string) ([]byte, *pdf.PdfRectangle, error) {
x1, x2 := line.X1, line.X2
y1, y2 := line.Y1, line.Y2
dy := y2 - y1
dx := x2 - x1
theta := math.Atan2(dy, dx)
L := math.Sqrt(math.Pow(dx, 2.0) + math.Pow(dy, 2.0))
w := line.LineWidth
pi := math.Pi
mul := 1.0
if dx < 0 {
mul *= -1.0
}
if dy < 0 {
mul *= -1.0
}
// Vs.
VsX := mul * (-w / 2 * math.Cos(theta+pi/2))
VsY := mul * (-w/2*math.Sin(theta+pi/2) + w*math.Sin(theta+pi/2))
// V1.
V1X := VsX + w/2*math.Cos(theta+pi/2)
V1Y := VsY + w/2*math.Sin(theta+pi/2)
// P2.
V2X := VsX + w/2*math.Cos(theta+pi/2) + L*math.Cos(theta)
V2Y := VsY + w/2*math.Sin(theta+pi/2) + L*math.Sin(theta)
// P3.
V3X := VsX + w/2*math.Cos(theta+pi/2) + L*math.Cos(theta) + w*math.Cos(theta-pi/2)
V3Y := VsY + w/2*math.Sin(theta+pi/2) + L*math.Sin(theta) + w*math.Sin(theta-pi/2)
// P4.
V4X := VsX + w/2*math.Cos(theta-pi/2)
V4Y := VsY + w/2*math.Sin(theta-pi/2)
path := NewPath()
path = path.AppendPoint(NewPoint(V1X, V1Y))
path = path.AppendPoint(NewPoint(V2X, V2Y))
path = path.AppendPoint(NewPoint(V3X, V3Y))
path = path.AppendPoint(NewPoint(V4X, V4Y))
lineEnding1 := line.LineEndingStyle1
lineEnding2 := line.LineEndingStyle2
// TODO: Allow custom height/widths.
arrowHeight := 3 * w
arrowWidth := 3 * w
arrowExtruding := (arrowWidth - w) / 2
if lineEnding2 == LineEndingStyleArrow {
// Convert P2, P3
p2 := path.GetPointNumber(2)
va1 := NewVectorPolar(arrowHeight, theta+pi)
pa1 := p2.AddVector(va1)
bVec := NewVectorPolar(arrowWidth/2, theta+pi/2)
aVec := NewVectorPolar(arrowHeight, theta)
va2 := NewVectorPolar(arrowExtruding, theta+pi/2)
pa2 := pa1.AddVector(va2)
va3 := aVec.Add(bVec.Flip())
pa3 := pa2.AddVector(va3)
va4 := bVec.Scale(2).Flip().Add(va3.Flip())
pa4 := pa3.AddVector(va4)
pa5 := pa1.AddVector(NewVectorPolar(w, theta-pi/2))
newpath := NewPath()
newpath = newpath.AppendPoint(path.GetPointNumber(1))
newpath = newpath.AppendPoint(pa1)
newpath = newpath.AppendPoint(pa2)
newpath = newpath.AppendPoint(pa3)
newpath = newpath.AppendPoint(pa4)
newpath = newpath.AppendPoint(pa5)
newpath = newpath.AppendPoint(path.GetPointNumber(4))
path = newpath
}
if lineEnding1 == LineEndingStyleArrow {
// Get the first and last points.
p1 := path.GetPointNumber(1)
pn := path.GetPointNumber(path.Length())
// First three points on arrow.
v1 := NewVectorPolar(w/2, theta+pi+pi/2)
pa1 := p1.AddVector(v1)
v2 := NewVectorPolar(arrowHeight, theta).Add(NewVectorPolar(arrowWidth/2, theta+pi/2))
pa2 := pa1.AddVector(v2)
v3 := NewVectorPolar(arrowExtruding, theta-pi/2)
pa3 := pa2.AddVector(v3)
// Last three points
v5 := NewVectorPolar(arrowHeight, theta)
pa5 := pn.AddVector(v5)
v6 := NewVectorPolar(arrowExtruding, theta+pi+pi/2)
pa6 := pa5.AddVector(v6)
pa7 := pa1
newpath := NewPath()
newpath = newpath.AppendPoint(pa1)
newpath = newpath.AppendPoint(pa2)
newpath = newpath.AppendPoint(pa3)
for _, p := range path.Points[1 : len(path.Points)-1] {
newpath = newpath.AppendPoint(p)
}
newpath = newpath.AppendPoint(pa5)
newpath = newpath.AppendPoint(pa6)
newpath = newpath.AppendPoint(pa7)
path = newpath
}
creator := pdfcontent.NewContentCreator()
// Draw line with arrow
creator.
Add_q().
Add_rg(line.LineColor.R(), line.LineColor.G(), line.LineColor.B())
if len(gsName) > 1 {
// If a graphics state is provided, use it. (Used for transparency settings here).
creator.Add_gs(pdfcore.PdfObjectName(gsName))
}
path = path.Offset(line.X1, line.Y1)
pathBbox := path.GetBoundingBox()
DrawPathWithCreator(path, creator)
creator.Add_f().
//creator.Add_S().
Add_Q()
/*
// Offsets (needed for placement of annotations bbox).
offX := x1 - VsX
offY := y1 - VsY
*/
// Bounding box - global coordinate system.
bbox := &pdf.PdfRectangle{}
bbox.Llx = pathBbox.X
bbox.Lly = pathBbox.Y
bbox.Urx = pathBbox.X + pathBbox.Width
bbox.Ury = pathBbox.Y + pathBbox.Height
return creator.Bytes(), bbox, nil
}

View File

@@ -0,0 +1,28 @@
package draw
import (
pdfcontent "gitea.tecamino.com/paadi/pdfmerge/internal/pdf/contentstream"
)
// Make the path with the content creator.
// Adds the PDF commands to draw the path to the creator instance.
func DrawPathWithCreator(path Path, creator *pdfcontent.ContentCreator) {
for idx, p := range path.Points {
if idx == 0 {
creator.Add_m(p.X, p.Y)
} else {
creator.Add_l(p.X, p.Y)
}
}
}
// Make the bezier path with the content creator.
// Adds the PDF commands to draw the path to the creator instance.
func DrawBezierPathWithCreator(bpath CubicBezierPath, creator *pdfcontent.ContentCreator) {
for idx, c := range bpath.Curves {
if idx == 0 {
creator.Add_m(c.P0.X, c.P0.Y)
}
creator.Add_c(c.P1.X, c.P1.Y, c.P2.X, c.P2.Y, c.P3.X, c.P3.Y)
}
}

View File

@@ -0,0 +1,80 @@
package draw
import "math"
type Vector struct {
Dx float64
Dy float64
}
func NewVector(dx, dy float64) Vector {
v := Vector{}
v.Dx = dx
v.Dy = dy
return v
}
func NewVectorBetween(a Point, b Point) Vector {
v := Vector{}
v.Dx = b.X - a.X
v.Dy = b.Y - a.Y
return v
}
func NewVectorPolar(length float64, theta float64) Vector {
v := Vector{}
v.Dx = length * math.Cos(theta)
v.Dy = length * math.Sin(theta)
return v
}
func (v Vector) Add(other Vector) Vector {
v.Dx += other.Dx
v.Dy += other.Dy
return v
}
func (v Vector) Rotate(phi float64) Vector {
mag := v.Magnitude()
angle := v.GetPolarAngle()
return NewVectorPolar(mag, angle+phi)
}
// Change the sign of the vector: -vector.
func (this Vector) Flip() Vector {
mag := this.Magnitude()
theta := this.GetPolarAngle()
this.Dx = mag * math.Cos(theta+math.Pi)
this.Dy = mag * math.Sin(theta+math.Pi)
return this
}
func (v Vector) FlipY() Vector {
v.Dy = -v.Dy
return v
}
func (v Vector) FlipX() Vector {
v.Dx = -v.Dx
return v
}
func (this Vector) Scale(factor float64) Vector {
mag := this.Magnitude()
theta := this.GetPolarAngle()
this.Dx = factor * mag * math.Cos(theta)
this.Dy = factor * mag * math.Sin(theta)
return this
}
func (this Vector) Magnitude() float64 {
return math.Sqrt(math.Pow(this.Dx, 2.0) + math.Pow(this.Dy, 2.0))
}
func (this Vector) GetPolarAngle() float64 {
return math.Atan2(this.Dy, this.Dx)
}

View File

@@ -0,0 +1,390 @@
package contentstream
import (
"bytes"
"errors"
"fmt"
gocolor "image/color"
"image/jpeg"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
)
// Creates the encoder for the inline image's Filter and DecodeParms.
func newEncoderFromInlineImage(inlineImage *ContentStreamInlineImage) (core.StreamEncoder, error) {
if inlineImage.Filter == nil {
// No filter, return raw data back.
return core.NewRawEncoder(), nil
}
// The filter should be a name or an array with a list of filter names.
filterName, ok := inlineImage.Filter.(*core.PdfObjectName)
if !ok {
array, ok := inlineImage.Filter.(*core.PdfObjectArray)
if !ok {
return nil, fmt.Errorf("filter not a Name or Array object")
}
if len(*array) == 0 {
// Empty array -> indicates raw filter (no filter).
return core.NewRawEncoder(), nil
}
if len(*array) != 1 {
menc, err := newMultiEncoderFromInlineImage(inlineImage)
if err != nil {
common.Log.Error("failed creating multi encoder: %v", err)
return nil, err
}
common.Log.Trace("Multi enc: %s\n", menc)
return menc, nil
}
// Single element.
filterObj := (*array)[0]
filterName, ok = filterObj.(*core.PdfObjectName)
if !ok {
return nil, fmt.Errorf("filter array member not a Name object")
}
}
// From Table 94 p. 224 (PDF32000_2008):
// Additional Abbreviations in an Inline Image Object:
switch *filterName {
case "AHx", "ASCIIHexDecode":
return core.NewASCIIHexEncoder(), nil
case "A85", "ASCII85Decode":
return core.NewASCII85Encoder(), nil
case "DCT", "DCTDecode":
return newDCTEncoderFromInlineImage(inlineImage)
case "Fl", "FlateDecode":
return newFlateEncoderFromInlineImage(inlineImage, nil)
case "LZW", "LZWDecode":
return newLZWEncoderFromInlineImage(inlineImage, nil)
case "CCF", "CCITTFaxDecode":
return core.NewCCITTFaxEncoder(), nil
case "RL", "RunLengthDecode":
return core.NewRunLengthEncoder(), nil
default:
common.Log.Debug("unsupported inline image encoding filter name : %s", *filterName)
return nil, errors.New("unsupported inline encoding method")
}
}
// Create a new flate decoder from an inline image object, getting all the encoding parameters
// from the DecodeParms stream object dictionary entry that can be provided optionally, usually
// only when a multi filter is used.
func newFlateEncoderFromInlineImage(inlineImage *ContentStreamInlineImage, decodeParams *core.PdfObjectDictionary) (*core.FlateEncoder, error) {
encoder := core.NewFlateEncoder()
// If decodeParams not provided, see if we can get from the stream.
if decodeParams == nil {
obj := inlineImage.DecodeParms
if obj != nil {
dp, isDict := obj.(*core.PdfObjectDictionary)
if !isDict {
common.Log.Debug("error: DecodeParms not a dictionary (%T)", obj)
return nil, fmt.Errorf("invalid DecodeParms")
}
decodeParams = dp
}
}
if decodeParams == nil {
// Can safely return here if no decode params, as the following depend on the decode params.
return encoder, nil
}
common.Log.Trace("decode params: %s", decodeParams.String())
obj := decodeParams.Get("Predictor")
if obj == nil {
common.Log.Debug("error: Predictor missing from DecodeParms - Continue with default (1)")
} else {
predictor, ok := obj.(*core.PdfObjectInteger)
if !ok {
common.Log.Debug("error: Predictor specified but not numeric (%T)", obj)
return nil, fmt.Errorf("invalid Predictor")
}
encoder.Predictor = int(*predictor)
}
// Bits per component. Use default if not specified (8).
obj = decodeParams.Get("BitsPerComponent")
if obj != nil {
bpc, ok := obj.(*core.PdfObjectInteger)
if !ok {
common.Log.Debug("error: Invalid BitsPerComponent")
return nil, fmt.Errorf("invalid BitsPerComponent")
}
encoder.BitsPerComponent = int(*bpc)
}
if encoder.Predictor > 1 {
// Columns.
encoder.Columns = 1
obj = decodeParams.Get("Columns")
if obj != nil {
columns, ok := obj.(*core.PdfObjectInteger)
if !ok {
return nil, fmt.Errorf("predictor column invalid")
}
encoder.Columns = int(*columns)
}
// Colors.
// Number of interleaved color components per sample (Default 1 if not specified)
encoder.Colors = 1
obj := decodeParams.Get("Colors")
if obj != nil {
colors, ok := obj.(*core.PdfObjectInteger)
if !ok {
return nil, fmt.Errorf("predictor colors not an integer")
}
encoder.Colors = int(*colors)
}
}
return encoder, nil
}
// Create a new LZW encoder/decoder based on an inline image object, getting all the encoding parameters
// from the DecodeParms stream object dictionary entry.
func newLZWEncoderFromInlineImage(inlineImage *ContentStreamInlineImage, decodeParams *core.PdfObjectDictionary) (*core.LZWEncoder, error) {
// Start with default settings.
encoder := core.NewLZWEncoder()
// If decodeParams not provided, see if we can get from the inline image directly.
if decodeParams == nil {
if inlineImage.DecodeParms != nil {
dp, isDict := inlineImage.DecodeParms.(*core.PdfObjectDictionary)
if !isDict {
common.Log.Debug("error: DecodeParms not a dictionary (%T)", inlineImage.DecodeParms)
return nil, fmt.Errorf("invalid DecodeParms")
}
decodeParams = dp
}
}
if decodeParams == nil {
// No decode parameters. Can safely return here if not set as the following options
// are related to the decode Params.
return encoder, nil
}
// The EarlyChange indicates when to increase code length, as different
// implementations use a different mechanisms. Essentially this chooses
// which LZW implementation to use.
// The default is 1 (one code early)
//
// The EarlyChange parameter is specified in the object stream dictionary for regular streams,
// but it is not specified explicitly where to check for it in the case of inline images.
// We will check in the decodeParms for now, we can adjust later if we come across cases of this.
obj := decodeParams.Get("EarlyChange")
if obj != nil {
earlyChange, ok := obj.(*core.PdfObjectInteger)
if !ok {
common.Log.Debug("error: EarlyChange specified but not numeric (%T)", obj)
return nil, fmt.Errorf("invalid EarlyChange")
}
if *earlyChange != 0 && *earlyChange != 1 {
return nil, fmt.Errorf("invalid EarlyChange value (not 0 or 1)")
}
encoder.EarlyChange = int(*earlyChange)
} else {
encoder.EarlyChange = 1 // default
}
obj = decodeParams.Get("Predictor")
if obj != nil {
predictor, ok := obj.(*core.PdfObjectInteger)
if !ok {
common.Log.Debug("error: Predictor specified but not numeric (%T)", obj)
return nil, fmt.Errorf("invalid Predictor")
}
encoder.Predictor = int(*predictor)
}
// Bits per component. Use default if not specified (8).
obj = decodeParams.Get("BitsPerComponent")
if obj != nil {
bpc, ok := obj.(*core.PdfObjectInteger)
if !ok {
common.Log.Debug("error: Invalid BitsPerComponent")
return nil, fmt.Errorf("invalid BitsPerComponent")
}
encoder.BitsPerComponent = int(*bpc)
}
if encoder.Predictor > 1 {
// Columns.
encoder.Columns = 1
obj = decodeParams.Get("Columns")
if obj != nil {
columns, ok := obj.(*core.PdfObjectInteger)
if !ok {
return nil, fmt.Errorf("predictor column invalid")
}
encoder.Columns = int(*columns)
}
// Colors.
// Number of interleaved color components per sample (Default 1 if not specified)
encoder.Colors = 1
obj = decodeParams.Get("Colors")
if obj != nil {
colors, ok := obj.(*core.PdfObjectInteger)
if !ok {
return nil, fmt.Errorf("predictor colors not an integer")
}
encoder.Colors = int(*colors)
}
}
common.Log.Trace("decode params: %s", decodeParams.String())
return encoder, nil
}
// Create a new DCT encoder/decoder based on an inline image, getting all the encoding parameters
// from the stream object dictionary entry and the image data itself.
func newDCTEncoderFromInlineImage(inlineImage *ContentStreamInlineImage) (*core.DCTEncoder, error) {
// Start with default settings.
encoder := core.NewDCTEncoder()
bufReader := bytes.NewReader(inlineImage.stream)
cfg, err := jpeg.DecodeConfig(bufReader)
//img, _, err := goimage.Decode(bufReader)
if err != nil {
common.Log.Debug("error decoding file: %s", err)
return nil, err
}
switch cfg.ColorModel {
case gocolor.RGBAModel:
encoder.BitsPerComponent = 8
encoder.ColorComponents = 3 // alpha is not included in pdf.
case gocolor.RGBA64Model:
encoder.BitsPerComponent = 16
encoder.ColorComponents = 3
case gocolor.GrayModel:
encoder.BitsPerComponent = 8
encoder.ColorComponents = 1
case gocolor.Gray16Model:
encoder.BitsPerComponent = 16
encoder.ColorComponents = 1
case gocolor.CMYKModel:
encoder.BitsPerComponent = 8
encoder.ColorComponents = 4
case gocolor.YCbCrModel:
// YCbCr is not supported by PDF, but it could be a different colorspace
// with 3 components. Would be specified by the ColorSpace entry.
encoder.BitsPerComponent = 8
encoder.ColorComponents = 3
default:
return nil, errors.New("unsupported color model")
}
encoder.Width = cfg.Width
encoder.Height = cfg.Height
common.Log.Trace("DCT Encoder: %+v", encoder)
return encoder, nil
}
// Create a new multi-filter encoder/decoder based on an inline image, getting all the encoding parameters
// from the filter specification and the DecodeParms (DP) dictionaries.
func newMultiEncoderFromInlineImage(inlineImage *ContentStreamInlineImage) (*core.MultiEncoder, error) {
mencoder := core.NewMultiEncoder()
// Prepare the decode params array (one for each filter type)
// Optional, not always present.
var decodeParamsDict *core.PdfObjectDictionary
decodeParamsArray := []core.PdfObject{}
if obj := inlineImage.DecodeParms; obj != nil {
// If it is a dictionary, assume it applies to all
dict, isDict := obj.(*core.PdfObjectDictionary)
if isDict {
decodeParamsDict = dict
}
// If it is an array, assume there is one for each
arr, isArray := obj.(*core.PdfObjectArray)
if isArray {
for _, dictObj := range *arr {
if dict, is := dictObj.(*core.PdfObjectDictionary); is {
decodeParamsArray = append(decodeParamsArray, dict)
} else {
decodeParamsArray = append(decodeParamsArray, nil)
}
}
}
}
obj := inlineImage.Filter
if obj == nil {
return nil, fmt.Errorf("filter missing")
}
array, ok := obj.(*core.PdfObjectArray)
if !ok {
return nil, fmt.Errorf("multi filter can only be made from array")
}
for idx, obj := range *array {
name, ok := obj.(*core.PdfObjectName)
if !ok {
return nil, fmt.Errorf("multi filter array element not a name")
}
var dp core.PdfObject
// If decode params dict is set, use it. Otherwise take from array..
if decodeParamsDict != nil {
dp = decodeParamsDict
} else {
// Only get the dp if provided. Oftentimes there is no decode params dict
// provided.
if len(decodeParamsArray) > 0 {
if idx >= len(decodeParamsArray) {
return nil, fmt.Errorf("missing elements in decode params array")
}
dp = decodeParamsArray[idx]
}
}
var dParams *core.PdfObjectDictionary
if dict, is := dp.(*core.PdfObjectDictionary); is {
dParams = dict
}
switch *name {
case core.StreamEncodingFilterNameFlate, "Fl":
// XXX: need to separate out the DecodeParms..
encoder, err := newFlateEncoderFromInlineImage(inlineImage, dParams)
if err != nil {
return nil, err
}
mencoder.AddEncoder(encoder)
case core.StreamEncodingFilterNameLZW:
encoder, err := newLZWEncoderFromInlineImage(inlineImage, dParams)
if err != nil {
return nil, err
}
mencoder.AddEncoder(encoder)
case core.StreamEncodingFilterNameASCIIHex:
encoder := core.NewASCIIHexEncoder()
mencoder.AddEncoder(encoder)
case core.StreamEncodingFilterNameASCII85, "A85":
encoder := core.NewASCII85Encoder()
mencoder.AddEncoder(encoder)
default:
common.Log.Error("Unsupported filter %s", *name)
return nil, fmt.Errorf("invalid filter in multi filter array")
}
}
return mencoder, nil
}

View File

@@ -0,0 +1,456 @@
package contentstream
import (
"bytes"
"errors"
"fmt"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model"
)
// A representation of an inline image in a Content stream. Everything between the BI and EI operands.
// ContentStreamInlineImage implements the core.PdfObject interface although strictly it is not a PDF object.
type ContentStreamInlineImage struct {
BitsPerComponent core.PdfObject
ColorSpace core.PdfObject
Decode core.PdfObject
DecodeParms core.PdfObject
Filter core.PdfObject
Height core.PdfObject
ImageMask core.PdfObject
Intent core.PdfObject
Interpolate core.PdfObject
Width core.PdfObject
stream []byte
}
// Make a new content stream inline image object from an image.
func NewInlineImageFromImage(img model.Image, encoder core.StreamEncoder) (*ContentStreamInlineImage, error) {
if encoder == nil {
encoder = core.NewRawEncoder()
}
inlineImage := ContentStreamInlineImage{}
switch img.ColorComponents {
case 1:
inlineImage.ColorSpace = core.MakeName("G") // G short for DeviceGray
case 3:
inlineImage.ColorSpace = core.MakeName("RGB") // RGB short for DeviceRGB
case 4:
inlineImage.ColorSpace = core.MakeName("CMYK") // CMYK short for DeviceCMYK
default:
common.Log.Debug("invalid number of color components for inline image: %d", img.ColorComponents)
return nil, errors.New("invalid number of color components")
}
inlineImage.BitsPerComponent = core.MakeInteger(img.BitsPerComponent)
inlineImage.Width = core.MakeInteger(img.Width)
inlineImage.Height = core.MakeInteger(img.Height)
encoded, err := encoder.EncodeBytes(img.Data)
if err != nil {
return nil, err
}
inlineImage.stream = encoded
filterName := encoder.GetFilterName()
if filterName != core.StreamEncodingFilterNameRaw {
inlineImage.Filter = core.MakeName(filterName)
}
// XXX/FIXME: Add decode params?
return &inlineImage, nil
}
func (si *ContentStreamInlineImage) String() string {
s := fmt.Sprintf("InlineImage(len=%d)\n", len(si.stream))
if si.BitsPerComponent != nil {
s += "- BPC " + si.BitsPerComponent.DefaultWriteString() + "\n"
}
if si.ColorSpace != nil {
s += "- CS " + si.ColorSpace.DefaultWriteString() + "\n"
}
if si.Decode != nil {
s += "- D " + si.Decode.DefaultWriteString() + "\n"
}
if si.DecodeParms != nil {
s += "- DP " + si.DecodeParms.DefaultWriteString() + "\n"
}
if si.Filter != nil {
s += "- F " + si.Filter.DefaultWriteString() + "\n"
}
if si.Height != nil {
s += "- H " + si.Height.DefaultWriteString() + "\n"
}
if si.ImageMask != nil {
s += "- IM " + si.ImageMask.DefaultWriteString() + "\n"
}
if si.Intent != nil {
s += "- Intent " + si.Intent.DefaultWriteString() + "\n"
}
if si.Interpolate != nil {
s += "- I " + si.Interpolate.DefaultWriteString() + "\n"
}
if si.Width != nil {
s += "- W " + si.Width.DefaultWriteString() + "\n"
}
return s
}
func (si *ContentStreamInlineImage) DefaultWriteString() string {
var output bytes.Buffer
// We do not start with "BI" as that is the operand and is written out separately.
// Write out the parameters
s := ""
if si.BitsPerComponent != nil {
s += "/BPC " + si.BitsPerComponent.DefaultWriteString() + "\n"
}
if si.ColorSpace != nil {
s += "/CS " + si.ColorSpace.DefaultWriteString() + "\n"
}
if si.Decode != nil {
s += "/D " + si.Decode.DefaultWriteString() + "\n"
}
if si.DecodeParms != nil {
s += "/DP " + si.DecodeParms.DefaultWriteString() + "\n"
}
if si.Filter != nil {
s += "/F " + si.Filter.DefaultWriteString() + "\n"
}
if si.Height != nil {
s += "/H " + si.Height.DefaultWriteString() + "\n"
}
if si.ImageMask != nil {
s += "/IM " + si.ImageMask.DefaultWriteString() + "\n"
}
if si.Intent != nil {
s += "/Intent " + si.Intent.DefaultWriteString() + "\n"
}
if si.Interpolate != nil {
s += "/I " + si.Interpolate.DefaultWriteString() + "\n"
}
if si.Width != nil {
s += "/W " + si.Width.DefaultWriteString() + "\n"
}
output.WriteString(s)
output.WriteString("ID ")
output.Write(si.stream)
output.WriteString("\nEI\n")
return output.String()
}
func (s *ContentStreamInlineImage) GetColorSpace(resources *model.PdfPageResources) (model.PdfColorspace, error) {
if s.ColorSpace == nil {
// Default.
common.Log.Debug("Inline image not having specified colorspace, assuming Gray")
return model.NewPdfColorspaceDeviceGray(), nil
}
// If is an array, then could be an indexed colorspace.
if arr, isArr := s.ColorSpace.(*core.PdfObjectArray); isArr {
return newIndexedColorspaceFromPdfObject(arr)
}
name, ok := s.ColorSpace.(*core.PdfObjectName)
if !ok {
common.Log.Debug("error: Invalid object type (%T;%+v)", s.ColorSpace, s.ColorSpace)
return nil, errors.New("type check error")
}
switch *name {
case "G", "DeviceGray":
return model.NewPdfColorspaceDeviceGray(), nil
case "RGB", "DeviceRGB":
return model.NewPdfColorspaceDeviceRGB(), nil
case "CMYK", "DeviceCMYK":
return model.NewPdfColorspaceDeviceCMYK(), nil
case "I", "Indexed":
return nil, errors.New("unsupported Index colorspace")
default:
if resources.ColorSpace == nil {
// Can also refer to a name in the PDF page resources...
common.Log.Debug("error, unsupported inline image colorspace: %s", *name)
return nil, errors.New("unknown colorspace")
}
cs, has := resources.ColorSpace.Colorspaces[string(*name)]
if !has {
// Can also refer to a name in the PDF page resources...
common.Log.Debug("error, unsupported inline image colorspace: %s", *name)
return nil, errors.New("unknown colorspace")
}
return cs, nil
}
}
func (s *ContentStreamInlineImage) GetEncoder() (core.StreamEncoder, error) {
return newEncoderFromInlineImage(s)
}
// Is a mask ?
// The image mask entry in the image dictionary specifies that the image data shall be used as a stencil
// mask for painting in the current color. The mask data is 1bpc, grayscale.
func (s *ContentStreamInlineImage) IsMask() (bool, error) {
if s.ImageMask != nil {
imMask, ok := s.ImageMask.(*core.PdfObjectBool)
if !ok {
common.Log.Debug("Image mask not a boolean")
return false, errors.New("invalid object type")
}
return bool(*imMask), nil
} else {
return false, nil
}
}
// Export the inline image to Image which can be transformed or exported easily.
// Page resources are needed to look up colorspace information.
func (si *ContentStreamInlineImage) ToImage(resources *model.PdfPageResources) (*model.Image, error) {
// Decode the imaging data if encoded.
encoder, err := newEncoderFromInlineImage(si)
if err != nil {
return nil, err
}
common.Log.Trace("encoder: %+v %T", encoder, encoder)
common.Log.Trace("inline image: %+v", si)
decoded, err := encoder.DecodeBytes(si.stream)
if err != nil {
return nil, err
}
image := &model.Image{}
// Height.
if si.Height == nil {
return nil, errors.New("height attribute missing")
}
height, ok := si.Height.(*core.PdfObjectInteger)
if !ok {
return nil, errors.New("invalid height")
}
image.Height = int64(*height)
// Width.
if si.Width == nil {
return nil, errors.New("width attribute missing")
}
width, ok := si.Width.(*core.PdfObjectInteger)
if !ok {
return nil, errors.New("invalid width")
}
image.Width = int64(*width)
// Image mask?
isMask, err := si.IsMask()
if err != nil {
return nil, err
}
if isMask {
// Masks are grayscale 1bpc.
image.BitsPerComponent = 1
image.ColorComponents = 1
} else {
// BPC.
if si.BitsPerComponent == nil {
common.Log.Debug("Inline Bits per component missing - assuming 8")
image.BitsPerComponent = 8
} else {
bpc, ok := si.BitsPerComponent.(*core.PdfObjectInteger)
if !ok {
common.Log.Debug("error invalid bits per component value, type %T", si.BitsPerComponent)
return nil, errors.New("BPC Type error")
}
image.BitsPerComponent = int64(*bpc)
}
// Color components.
if si.ColorSpace != nil {
cs, err := si.GetColorSpace(resources)
if err != nil {
return nil, err
}
image.ColorComponents = cs.GetNumComponents()
} else {
// Default gray if not specified.
common.Log.Debug("Inline Image colorspace not specified - assuming 1 color component")
image.ColorComponents = 1
}
}
image.Data = decoded
return image, nil
}
// Parse an inline image from a content stream, both read its properties and binary data.
// When called, "BI" has already been read from the stream. This function
// finishes reading through "EI" and then returns the ContentStreamInlineImage.
func (s *ContentStreamParser) ParseInlineImage() (*ContentStreamInlineImage, error) {
// Reading parameters.
im := ContentStreamInlineImage{}
for {
s.skipSpaces()
obj, err, isOperand := s.parseObject()
if err != nil {
return nil, err
}
if !isOperand {
// Not an operand.. Read key value properties..
param, ok := obj.(*core.PdfObjectName)
if !ok {
common.Log.Debug("invalid inline image property (expecting name) - %T", obj)
return nil, fmt.Errorf("invalid inline image property (expecting name) - %T", obj)
}
valueObj, err, isOperand := s.parseObject()
if err != nil {
return nil, err
}
if isOperand {
return nil, fmt.Errorf("not expecting an operand")
}
// From 8.9.7 "Inline Images" p. 223 (PDF32000_2008):
// The key-value pairs appearing between the BI and ID operators are analogous to those in the dictionary
// portion of an image XObject (though the syntax is different).
// Table 93 shows the entries that are valid for an inline image, all of which shall have the same meanings
// as in a stream dictionary (see Table 5) or an image dictionary (see Table 89).
// Entries other than those listed shall be ignored; in particular, the Type, Subtype, and Length
// entries normally found in a stream or image dictionary are unnecessary.
// For convenience, the abbreviations shown in the table may be used in place of the fully spelled-out keys.
// Table 94 shows additional abbreviations that can be used for the names of colour spaces and filters.
switch *param {
case "BPC", "BitsPerComponent":
im.BitsPerComponent = valueObj
case "CS", "ColorSpace":
im.ColorSpace = valueObj
case "D", "Decode":
im.Decode = valueObj
case "DP", "DecodeParms":
im.DecodeParms = valueObj
case "F", "Filter":
im.Filter = valueObj
case "H", "Height":
im.Height = valueObj
case "IM", "ImageMask":
im.ImageMask = valueObj
case "Intent":
im.Intent = valueObj
case "I", "Interpolate":
im.Interpolate = valueObj
case "W", "Width":
im.Width = valueObj
default:
return nil, fmt.Errorf("unknown inline image parameter %s", *param)
}
}
if isOperand {
operand, ok := obj.(*core.PdfObjectString)
if !ok {
return nil, fmt.Errorf("failed to read inline image - invalid operand")
}
switch *operand {
case "EI":
// Image fully defined
common.Log.Trace("Inline image finished...")
return &im, nil
case "ID":
// Inline image data.
// Should get a single space (0x20) followed by the data and then EI.
common.Log.Trace("ID start")
// Skip the space if its there.
b, err := s.reader.Peek(1)
if err != nil {
return nil, err
}
if core.IsWhiteSpace(b[0]) {
s.reader.Discard(1)
}
// Unfortunately there is no good way to know how many bytes to read since it
// depends on the Filter and encoding etc.
// Therefore we will simply read until we find "<ws>EI<ws>" where <ws> is whitespace
// although of course that could be a part of the data (even if unlikely).
im.stream = []byte{}
state := 0
var skipBytes []byte
for {
c, err := s.reader.ReadByte()
if err != nil {
common.Log.Debug("Unable to find end of image EI in inline image data")
return nil, err
}
switch state {
case 0:
if core.IsWhiteSpace(c) {
skipBytes = []byte{}
skipBytes = append(skipBytes, c)
state = 1
} else {
im.stream = append(im.stream, c)
}
case 1:
skipBytes = append(skipBytes, c)
if c == 'E' {
state = 2
} else {
im.stream = append(im.stream, skipBytes...)
skipBytes = []byte{} // Clear.
// Need an extra check to decide if we fall back to state 0 or 1.
if core.IsWhiteSpace(c) {
state = 1
} else {
state = 0
}
}
case 2:
skipBytes = append(skipBytes, c)
if c == 'I' {
state = 3
} else {
im.stream = append(im.stream, skipBytes...)
skipBytes = []byte{} // Clear.
state = 0
}
case 3:
skipBytes = append(skipBytes, c)
if core.IsWhiteSpace(c) {
// image data finished.
if len(im.stream) > 100 {
common.Log.Trace("Image stream (%d): % x ...", len(im.stream), im.stream[:100])
} else {
common.Log.Trace("Image stream (%d): % x", len(im.stream), im.stream)
}
// Exit point.
return &im, nil
} else {
// Seems like "<ws>EI" was part of the data.
im.stream = append(im.stream, skipBytes...)
skipBytes = []byte{} // Clear.
state = 0
}
}
}
// Never reached (exit point is at end of EI).
}
}
}
}

View File

@@ -0,0 +1,586 @@
package contentstream
import (
"bufio"
"bytes"
"encoding/hex"
"errors"
"fmt"
"io"
"strconv"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
)
// Content stream parser.
type ContentStreamParser struct {
reader *bufio.Reader
}
// Create a new instance of the content stream parser from an input content
// stream string.
func NewContentStreamParser(contentStr string) *ContentStreamParser {
// Each command has parameters and an operand (command).
parser := ContentStreamParser{}
buffer := bytes.NewBufferString(contentStr + "\n") // Add newline at end to get last operand without EOF error.
parser.reader = bufio.NewReader(buffer)
return &parser
}
// Parses all commands in content stream, returning a list of operation data.
func (sp *ContentStreamParser) Parse() (*ContentStreamOperations, error) {
operations := ContentStreamOperations{}
for {
operation := ContentStreamOperation{}
for {
obj, err, isOperand := sp.parseObject()
if err != nil {
if err == io.EOF {
// End of data. Successful exit point.
return &operations, nil
}
return &operations, err
}
if isOperand {
operation.Operand = string(*obj.(*core.PdfObjectString))
operations = append(operations, &operation)
break
} else {
operation.Params = append(operation.Params, obj)
}
}
if operation.Operand == "BI" {
// Parse an inline image, reads everything between the "BI" and "EI".
// The image is stored as the parameter.
im, err := sp.ParseInlineImage()
if err != nil {
return &operations, err
}
operation.Params = append(operation.Params, im)
}
}
}
// Skip over any spaces. Returns the number of spaces skipped and
// an error if any.
func (sp *ContentStreamParser) skipSpaces() (int, error) {
cnt := 0
for {
bb, err := sp.reader.Peek(1)
if err != nil {
return 0, err
}
if core.IsWhiteSpace(bb[0]) {
sp.reader.ReadByte()
cnt++
} else {
break
}
}
return cnt, nil
}
// Skip over comments and spaces. Can handle multi-line comments.
func (sp *ContentStreamParser) skipComments() error {
if _, err := sp.skipSpaces(); err != nil {
return err
}
isFirst := true
for {
bb, err := sp.reader.Peek(1)
if err != nil {
common.Log.Debug("error %s", err.Error())
return err
}
if isFirst && bb[0] != '%' {
// Not a comment clearly.
return nil
} else {
isFirst = false
}
if (bb[0] != '\r') && (bb[0] != '\n') {
sp.reader.ReadByte()
} else {
break
}
}
// Call recursively to handle multiline comments.
return sp.skipComments()
}
// Parse a name starting with '/'.
func (sp *ContentStreamParser) parseName() (core.PdfObjectName, error) {
name := ""
nameStarted := false
for {
bb, err := sp.reader.Peek(1)
if err == io.EOF {
break // Can happen when loading from object stream.
}
if err != nil {
return core.PdfObjectName(name), err
}
if !nameStarted {
// Should always start with '/', otherwise not valid.
if bb[0] == '/' {
nameStarted = true
sp.reader.ReadByte()
} else {
common.Log.Error("Name starting with %s (% x)", bb, bb)
return core.PdfObjectName(name), fmt.Errorf("invalid name: (%c)", bb[0])
}
} else {
if core.IsWhiteSpace(bb[0]) {
break
} else if (bb[0] == '/') || (bb[0] == '[') || (bb[0] == '(') || (bb[0] == ']') || (bb[0] == '<') || (bb[0] == '>') {
break // Looks like start of next statement.
} else if bb[0] == '#' {
hexcode, err := sp.reader.Peek(3)
if err != nil {
return core.PdfObjectName(name), err
}
sp.reader.Discard(3)
code, err := hex.DecodeString(string(hexcode[1:3]))
if err != nil {
return core.PdfObjectName(name), err
}
name += string(code)
} else {
b, _ := sp.reader.ReadByte()
name += string(b)
}
}
}
return core.PdfObjectName(name), nil
}
// Numeric objects.
// Section 7.3.3.
// Integer or Float.
//
// An integer shall be written as one or more decimal digits optionally
// preceded by a sign. The value shall be interpreted as a signed
// decimal integer and shall be converted to an integer object.
//
// A real value shall be written as one or more decimal digits with an
// optional sign and a leading, trailing, or embedded PERIOD (2Eh)
// (decimal point). The value shall be interpreted as a real number
// and shall be converted to a real object.
//
// Regarding exponential numbers: 7.3.3 Numeric Objects:
// A conforming writer shall not use the PostScript syntax for numbers
// with non-decimal radices (such as 16#FFFE) or in exponential format
// (such as 6.02E23).
// Nonetheless, we sometimes get numbers with exponential format, so
// we will support it in the reader (no confusion with other types, so
// no compromise).
func (sp *ContentStreamParser) parseNumber() (core.PdfObject, error) {
isFloat := false
allowSigns := true
numStr := ""
for {
common.Log.Trace("Parsing number \"%s\"", numStr)
bb, err := sp.reader.Peek(1)
if err == io.EOF {
// GH: EOF handling. Handle EOF like end of line. Can happen with
// encoded object streams that the object is at the end.
// In other cases, we will get the EOF error elsewhere at any rate.
break // Handle like EOF
}
if err != nil {
common.Log.Error("error %s", err)
return nil, err
}
if allowSigns && (bb[0] == '-' || bb[0] == '+') {
// Only appear in the beginning, otherwise serves as a delimiter.
b, _ := sp.reader.ReadByte()
numStr += string(b)
allowSigns = false // Only allowed in beginning, and after e (exponential).
} else if core.IsDecimalDigit(bb[0]) {
b, _ := sp.reader.ReadByte()
numStr += string(b)
} else if bb[0] == '.' {
b, _ := sp.reader.ReadByte()
numStr += string(b)
isFloat = true
} else if bb[0] == 'e' {
// Exponential number format.
b, _ := sp.reader.ReadByte()
numStr += string(b)
isFloat = true
allowSigns = true
} else {
break
}
}
if isFloat {
fVal, err := strconv.ParseFloat(numStr, 64)
if err != nil {
common.Log.Debug("error parsing number %q err=%v. Using 0.0. Output may be incorrect", numStr, err)
fVal = 0.0
err = nil
}
o := core.PdfObjectFloat(fVal)
return &o, err
} else {
intVal, err := strconv.ParseInt(numStr, 10, 64)
if err != nil {
common.Log.Debug("error parsing integer %q err=%v. Using 0. Output may be incorrect", numStr, err)
intVal = 0
err = nil
}
o := core.PdfObjectInteger(intVal)
return &o, err
}
}
// A string starts with '(' and ends with ')'.
func (sp *ContentStreamParser) parseString() (core.PdfObjectString, error) {
sp.reader.ReadByte()
bytes := []byte{}
count := 1
for {
bb, err := sp.reader.Peek(1)
if err != nil {
return core.PdfObjectString(bytes), err
}
if bb[0] == '\\' { // Escape sequence.
sp.reader.ReadByte() // Skip the escape \ byte.
b, err := sp.reader.ReadByte()
if err != nil {
return core.PdfObjectString(bytes), err
}
// Octal '\ddd' number (base 8).
if core.IsOctalDigit(b) {
bb, err := sp.reader.Peek(2)
if err != nil {
return core.PdfObjectString(bytes), err
}
numeric := []byte{}
numeric = append(numeric, b)
for _, val := range bb {
if core.IsOctalDigit(val) {
numeric = append(numeric, val)
} else {
break
}
}
sp.reader.Discard(len(numeric) - 1)
common.Log.Trace("Numeric string \"%s\"", numeric)
code, err := strconv.ParseUint(string(numeric), 8, 32)
if err != nil {
return core.PdfObjectString(bytes), err
}
bytes = append(bytes, byte(code))
continue
}
switch b {
case 'n':
bytes = append(bytes, '\n')
case 'r':
bytes = append(bytes, '\r')
case 't':
bytes = append(bytes, '\t')
case 'b':
bytes = append(bytes, '\b')
case 'f':
bytes = append(bytes, '\f')
case '(':
bytes = append(bytes, '(')
case ')':
bytes = append(bytes, ')')
case '\\':
bytes = append(bytes, '\\')
}
continue
} else if bb[0] == '(' {
count++
} else if bb[0] == ')' {
count--
if count == 0 {
sp.reader.ReadByte()
break
}
}
b, _ := sp.reader.ReadByte()
bytes = append(bytes, b)
}
return core.PdfObjectString(bytes), nil
}
// Starts with '<' ends with '>'.
func (sp *ContentStreamParser) parseHexString() (core.PdfObjectString, error) {
sp.reader.ReadByte()
hextable := []byte("0123456789abcdefABCDEF")
tmp := []byte{}
for {
sp.skipSpaces()
bb, err := sp.reader.Peek(1)
if err != nil {
return core.PdfObjectString(""), err
}
if bb[0] == '>' {
sp.reader.ReadByte()
break
}
b, _ := sp.reader.ReadByte()
if bytes.IndexByte(hextable, b) >= 0 {
tmp = append(tmp, b)
}
}
if len(tmp)%2 == 1 {
tmp = append(tmp, '0')
}
buf, _ := hex.DecodeString(string(tmp))
return core.PdfObjectString(buf), nil
}
// Starts with '[' ends with ']'. Can contain any kinds of direct objects.
func (sp *ContentStreamParser) parseArray() (core.PdfObjectArray, error) {
arr := make(core.PdfObjectArray, 0)
sp.reader.ReadByte()
for {
sp.skipSpaces()
bb, err := sp.reader.Peek(1)
if err != nil {
return arr, err
}
if bb[0] == ']' {
sp.reader.ReadByte()
break
}
obj, err, _ := sp.parseObject()
if err != nil {
return arr, err
}
arr = append(arr, obj)
}
return arr, nil
}
// Parse bool object.
func (sp *ContentStreamParser) parseBool() (core.PdfObjectBool, error) {
bb, err := sp.reader.Peek(4)
if err != nil {
return core.PdfObjectBool(false), err
}
if (len(bb) >= 4) && (string(bb[:4]) == "true") {
sp.reader.Discard(4)
return core.PdfObjectBool(true), nil
}
bb, err = sp.reader.Peek(5)
if err != nil {
return core.PdfObjectBool(false), err
}
if (len(bb) >= 5) && (string(bb[:5]) == "false") {
sp.reader.Discard(5)
return core.PdfObjectBool(false), nil
}
return core.PdfObjectBool(false), errors.New("unexpected boolean string")
}
// Parse null object.
func (sp *ContentStreamParser) parseNull() (core.PdfObjectNull, error) {
_, err := sp.reader.Discard(4)
return core.PdfObjectNull{}, err
}
func (sp *ContentStreamParser) parseDict() (*core.PdfObjectDictionary, error) {
common.Log.Trace("Reading content stream dict!")
dict := core.MakeDict()
// Pass the '<<'
c, _ := sp.reader.ReadByte()
if c != '<' {
return nil, errors.New("invalid dict")
}
c, _ = sp.reader.ReadByte()
if c != '<' {
return nil, errors.New("invalid dict")
}
for {
sp.skipSpaces()
bb, err := sp.reader.Peek(2)
if err != nil {
return nil, err
}
common.Log.Trace("Dict peek: %s (% x)!", string(bb), string(bb))
if (bb[0] == '>') && (bb[1] == '>') {
common.Log.Trace("EOF dictionary")
sp.reader.ReadByte()
sp.reader.ReadByte()
break
}
common.Log.Trace("Parse the name!")
keyName, err := sp.parseName()
common.Log.Trace("Key: %s", keyName)
if err != nil {
common.Log.Debug("error Returning name err %s", err)
return nil, err
}
if len(keyName) > 4 && keyName[len(keyName)-4:] == "null" {
// Some writers have a bug where the null is appended without
// space. For example "\Boundsnull"
newKey := keyName[0 : len(keyName)-4]
common.Log.Trace("Taking care of null bug (%s)", keyName)
common.Log.Trace("New key \"%s\" = null", newKey)
sp.skipSpaces()
bb, _ := sp.reader.Peek(1)
if bb[0] == '/' {
dict.Set(newKey, core.MakeNull())
continue
}
}
sp.skipSpaces()
val, err, _ := sp.parseObject()
if err != nil {
return nil, err
}
dict.Set(keyName, val)
common.Log.Trace("dict[%s] = %s", keyName, val.String())
}
return dict, nil
}
// An operand is a text command represented by a word.
func (sp *ContentStreamParser) parseOperand() (core.PdfObjectString, error) {
bytes := []byte{}
for {
bb, err := sp.reader.Peek(1)
if err != nil {
return core.PdfObjectString(bytes), err
}
if core.IsDelimiter(bb[0]) {
break
}
if core.IsWhiteSpace(bb[0]) {
break
}
b, _ := sp.reader.ReadByte()
bytes = append(bytes, b)
}
return core.PdfObjectString(bytes), nil
}
// Parse a generic object. Returns the object, an error code, and a bool
// value indicating whether the object is an operand. An operand
// is contained in a pdf string object.
func (sp *ContentStreamParser) parseObject() (core.PdfObject, error, bool) {
// Determine the kind of object.
// parse it!
// make a list of operands, then once operand arrives put into a package.
sp.skipSpaces()
for {
bb, err := sp.reader.Peek(2)
if err != nil {
return nil, err, false
}
common.Log.Trace("Peek string: %s", string(bb))
// Determine type.
if bb[0] == '%' {
sp.skipComments()
continue
} else if bb[0] == '/' {
name, err := sp.parseName()
common.Log.Trace("->Name: '%s'", name)
return &name, err, false
} else if bb[0] == '(' {
common.Log.Trace("->String!")
str, err := sp.parseString()
common.Log.Trace("(%s)\n", str.String())
return &str, err, false
} else if bb[0] == '<' && bb[1] != '<' {
common.Log.Trace("->Hex String!")
str, err := sp.parseHexString()
return &str, err, false
} else if bb[0] == '[' {
common.Log.Trace("->Array!")
arr, err := sp.parseArray()
return &arr, err, false
} else if core.IsFloatDigit(bb[0]) || (bb[0] == '-' && core.IsFloatDigit(bb[1])) {
common.Log.Trace("->Number!")
number, err := sp.parseNumber()
return number, err, false
} else if bb[0] == '<' && bb[1] == '<' {
dict, err := sp.parseDict()
return dict, err, false
} else {
// Otherwise, can be: keyword such as "null", "false", "true" or an operand...
common.Log.Trace("->Operand or bool?")
// Let's peek farther to find out.
bb, _ = sp.reader.Peek(5)
peekStr := string(bb)
common.Log.Trace("cont Peek str: %s", peekStr)
if (len(peekStr) > 3) && (peekStr[:4] == "null") {
null, err := sp.parseNull()
return &null, err, false
} else if (len(peekStr) > 4) && (peekStr[:5] == "false") {
b, err := sp.parseBool()
return &b, err, false
} else if (len(peekStr) > 3) && (peekStr[:4] == "true") {
b, err := sp.parseBool()
return &b, err, false
}
operand, err := sp.parseOperand()
if err != nil {
return &operand, err, false
}
if len(operand.String()) < 1 {
return &operand, ErrInvalidOperand, false
}
return &operand, nil, true
}
}
}

View File

@@ -0,0 +1,541 @@
package contentstream
import (
"errors"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model"
)
// Basic graphics state implementation.
// Initially only implementing and tracking a portion of the information specified. Easy to add more.
type GraphicsState struct {
ColorspaceStroking model.PdfColorspace
ColorspaceNonStroking model.PdfColorspace
ColorStroking model.PdfColor
ColorNonStroking model.PdfColor
}
type GraphicStateStack []GraphicsState
func (gsStack *GraphicStateStack) Push(gs GraphicsState) {
*gsStack = append(*gsStack, gs)
}
func (gsStack *GraphicStateStack) Pop() GraphicsState {
gs := (*gsStack)[len(*gsStack)-1]
*gsStack = (*gsStack)[:len(*gsStack)-1]
return gs
}
// ContentStreamProcessor defines a data structure and methods for processing a content stream, keeping track of the
// current graphics state, and allowing external handlers to define their own functions as a part of the processing,
// for example rendering or extracting certain information.
type ContentStreamProcessor struct {
graphicsStack GraphicStateStack
operations []*ContentStreamOperation
graphicsState GraphicsState
handlers []HandlerEntry
currentIndex int
}
type HandlerFunc func(op *ContentStreamOperation, gs GraphicsState, resources *model.PdfPageResources) error
type HandlerEntry struct {
Condition HandlerConditionEnum
Operand string
Handler HandlerFunc
}
type HandlerConditionEnum int
func (ce HandlerConditionEnum) All() bool {
return ce == HandlerConditionEnumAllOperands
}
func (ce HandlerConditionEnum) Operand() bool {
return ce == HandlerConditionEnumOperand
}
const (
HandlerConditionEnumOperand HandlerConditionEnum = iota
HandlerConditionEnumAllOperands HandlerConditionEnum = iota
)
func NewContentStreamProcessor(ops []*ContentStreamOperation) *ContentStreamProcessor {
csp := ContentStreamProcessor{}
csp.graphicsStack = GraphicStateStack{}
// Set defaults..
gs := GraphicsState{}
csp.graphicsState = gs
csp.handlers = []HandlerEntry{}
csp.currentIndex = 0
csp.operations = ops
return &csp
}
func (csp *ContentStreamProcessor) AddHandler(condition HandlerConditionEnum, operand string, handler HandlerFunc) {
entry := HandlerEntry{}
entry.Condition = condition
entry.Operand = operand
entry.Handler = handler
csp.handlers = append(csp.handlers, entry)
}
func (csp *ContentStreamProcessor) getColorspace(name string, resources *model.PdfPageResources) (model.PdfColorspace, error) {
switch name {
case "DeviceGray":
return model.NewPdfColorspaceDeviceGray(), nil
case "DeviceRGB":
return model.NewPdfColorspaceDeviceRGB(), nil
case "DeviceCMYK":
return model.NewPdfColorspaceDeviceCMYK(), nil
case "Pattern":
return model.NewPdfColorspaceSpecialPattern(), nil
}
// Next check the colorspace dictionary.
cs, has := resources.ColorSpace.Colorspaces[name]
if has {
return cs, nil
}
// Lastly check other potential colormaps.
switch name {
case "CalGray":
return model.NewPdfColorspaceCalGray(), nil
case "CalRGB":
return model.NewPdfColorspaceCalRGB(), nil
case "Lab":
return model.NewPdfColorspaceLab(), nil
}
// Otherwise unsupported.
common.Log.Debug("Unknown colorspace requested: %s", name)
return nil, errors.New("unsupported colorspace")
}
// Get initial color for a given colorspace.
func (csp *ContentStreamProcessor) getInitialColor(cs model.PdfColorspace) (model.PdfColor, error) {
switch cs := cs.(type) {
case *model.PdfColorspaceDeviceGray:
return model.NewPdfColorDeviceGray(0.0), nil
case *model.PdfColorspaceDeviceRGB:
return model.NewPdfColorDeviceRGB(0.0, 0.0, 0.0), nil
case *model.PdfColorspaceDeviceCMYK:
return model.NewPdfColorDeviceCMYK(0.0, 0.0, 0.0, 1.0), nil
case *model.PdfColorspaceCalGray:
return model.NewPdfColorCalGray(0.0), nil
case *model.PdfColorspaceCalRGB:
return model.NewPdfColorCalRGB(0.0, 0.0, 0.0), nil
case *model.PdfColorspaceLab:
l := 0.0
a := 0.0
b := 0.0
if cs.Range[0] > 0 {
l = cs.Range[0]
}
if cs.Range[2] > 0 {
a = cs.Range[2]
}
return model.NewPdfColorLab(l, a, b), nil
case *model.PdfColorspaceICCBased:
if cs.Alternate == nil {
// Alternate not defined.
// Try to fall back to DeviceGray, DeviceRGB or DeviceCMYK.
common.Log.Trace("ICC Based not defined - attempting fall back (N = %d)", cs.N)
switch cs.N {
case 1:
common.Log.Trace("Falling back to DeviceGray")
return csp.getInitialColor(model.NewPdfColorspaceDeviceGray())
case 3:
common.Log.Trace("Falling back to DeviceRGB")
return csp.getInitialColor(model.NewPdfColorspaceDeviceRGB())
case 4:
common.Log.Trace("Falling back to DeviceCMYK")
return csp.getInitialColor(model.NewPdfColorspaceDeviceCMYK())
default:
return nil, errors.New("alternate space not defined for ICC")
}
}
return csp.getInitialColor(cs.Alternate)
case *model.PdfColorspaceSpecialIndexed:
if cs.Base == nil {
return nil, errors.New("indexed base not specified")
}
return csp.getInitialColor(cs.Base)
case *model.PdfColorspaceSpecialSeparation:
if cs.AlternateSpace == nil {
return nil, errors.New("alternate space not specified")
}
return csp.getInitialColor(cs.AlternateSpace)
case *model.PdfColorspaceDeviceN:
if cs.AlternateSpace == nil {
return nil, errors.New("alternate space not specified")
}
return csp.getInitialColor(cs.AlternateSpace)
case *model.PdfColorspaceSpecialPattern:
// FIXME/check: A pattern does not have an initial color...
return nil, nil
}
common.Log.Debug("Unable to determine initial color for unknown colorspace: %T", cs)
return nil, errors.New("unsupported colorspace")
}
// Process the entire operations.
func (ce *ContentStreamProcessor) Process(resources *model.PdfPageResources) error {
// Initialize graphics state
ce.graphicsState.ColorspaceStroking = model.NewPdfColorspaceDeviceGray()
ce.graphicsState.ColorspaceNonStroking = model.NewPdfColorspaceDeviceGray()
ce.graphicsState.ColorStroking = model.NewPdfColorDeviceGray(0)
ce.graphicsState.ColorNonStroking = model.NewPdfColorDeviceGray(0)
for _, op := range ce.operations {
var err error
// Internal handling.
switch op.Operand {
case "q":
ce.graphicsStack.Push(ce.graphicsState)
case "Q":
ce.graphicsState = ce.graphicsStack.Pop()
// Color operations (Table 74 p. 179)
case "CS":
err = ce.handleCommand_CS(op, resources)
case "cs":
err = ce.handleCommand_cs(op, resources)
case "SC":
err = ce.handleCommand_SC(op)
case "SCN":
err = ce.handleCommand_SCN(op)
case "sc":
err = ce.handleCommand_sc(op)
case "scn":
err = ce.handleCommand_scn(op)
case "G":
err = ce.handleCommand_G(op)
case "g":
err = ce.handleCommand_g(op)
case "RG":
err = ce.handleCommand_RG(op)
case "rg":
err = ce.handleCommand_rg(op)
case "K":
err = ce.handleCommand_K(op)
case "k":
err = ce.handleCommand_k(op)
}
if err != nil {
common.Log.Debug("Processor handling error (%s): %v", op.Operand, err)
common.Log.Debug("Operand: %#v", op.Operand)
return err
}
// Check if have external handler also, and process if so.
for _, entry := range ce.handlers {
var err error
if entry.Condition.All() {
err = entry.Handler(op, ce.graphicsState, resources)
} else if entry.Condition.Operand() && op.Operand == entry.Operand {
err = entry.Handler(op, ce.graphicsState, resources)
}
if err != nil {
common.Log.Debug("Processor handler error: %v", err)
return err
}
}
}
return nil
}
// CS: Set the current color space for stroking operations.
func (csp *ContentStreamProcessor) handleCommand_CS(op *ContentStreamOperation, resources *model.PdfPageResources) error {
if len(op.Params) < 1 {
common.Log.Debug("invalid cs command, skipping over")
return errors.New("too few parameters")
}
if len(op.Params) > 1 {
common.Log.Debug("cs command with too many parameters - continuing")
return errors.New("too many parameters")
}
name, ok := op.Params[0].(*core.PdfObjectName)
if !ok {
common.Log.Debug("error: cs command with invalid parameter, skipping over")
return errors.New("type check error")
}
// Set the current color space to use for stroking operations.
// Either device based or referring to resource dict.
cs, err := csp.getColorspace(string(*name), resources)
if err != nil {
return err
}
csp.graphicsState.ColorspaceStroking = cs
// Set initial color.
color, err := csp.getInitialColor(cs)
if err != nil {
return err
}
csp.graphicsState.ColorStroking = color
return nil
}
// cs: Set the current color space for non-stroking operations.
func (csp *ContentStreamProcessor) handleCommand_cs(op *ContentStreamOperation, resources *model.PdfPageResources) error {
if len(op.Params) < 1 {
common.Log.Debug("invalid CS command, skipping over")
return errors.New("too few parameters")
}
if len(op.Params) > 1 {
common.Log.Debug("CS command with too many parameters - continuing")
return errors.New("too many parameters")
}
name, ok := op.Params[0].(*core.PdfObjectName)
if !ok {
common.Log.Debug("error: CS command with invalid parameter, skipping over")
return errors.New("type check error")
}
// Set the current color space to use for non-stroking operations.
// Either device based or referring to resource dict.
cs, err := csp.getColorspace(string(*name), resources)
if err != nil {
return err
}
csp.graphicsState.ColorspaceNonStroking = cs
// Set initial color.
color, err := csp.getInitialColor(cs)
if err != nil {
return err
}
csp.graphicsState.ColorNonStroking = color
return nil
}
// SC: Set the color to use for stroking operations in a device, CIE-based or Indexed colorspace. (not ICC based)
func (sp *ContentStreamProcessor) handleCommand_SC(op *ContentStreamOperation) error {
// For DeviceGray, CalGray, Indexed: one operand is required
// For DeviceRGB, CalRGB, Lab: 3 operands required
cs := sp.graphicsState.ColorspaceStroking
if len(op.Params) != cs.GetNumComponents() {
common.Log.Debug("invalid number of parameters for SC")
common.Log.Debug("Number %d not matching colorspace %T", len(op.Params), cs)
return errors.New("invalid number of parameters")
}
color, err := cs.ColorFromPdfObjects(op.Params)
if err != nil {
return err
}
sp.graphicsState.ColorStroking = color
return nil
}
func isPatternCS(cs model.PdfColorspace) bool {
_, isPattern := cs.(*model.PdfColorspaceSpecialPattern)
return isPattern
}
// SCN: Same as SC but also supports Pattern, Separation, DeviceN and ICCBased color spaces.
func (sp *ContentStreamProcessor) handleCommand_SCN(op *ContentStreamOperation) error {
cs := sp.graphicsState.ColorspaceStroking
if !isPatternCS(cs) {
if len(op.Params) != cs.GetNumComponents() {
common.Log.Debug("invalid number of parameters for SC")
common.Log.Debug("Number %d not matching colorspace %T", len(op.Params), cs)
return errors.New("invalid number of parameters")
}
}
color, err := cs.ColorFromPdfObjects(op.Params)
if err != nil {
return err
}
sp.graphicsState.ColorStroking = color
return nil
}
// sc: Same as SC except used for non-stroking operations.
func (sp *ContentStreamProcessor) handleCommand_sc(op *ContentStreamOperation) error {
cs := sp.graphicsState.ColorspaceNonStroking
if !isPatternCS(cs) {
if len(op.Params) != cs.GetNumComponents() {
common.Log.Debug("invalid number of parameters for SC")
common.Log.Debug("Number %d not matching colorspace %T", len(op.Params), cs)
return errors.New("invalid number of parameters")
}
}
color, err := cs.ColorFromPdfObjects(op.Params)
if err != nil {
return err
}
sp.graphicsState.ColorNonStroking = color
return nil
}
// scn: Same as SCN except used for non-stroking operations.
func (sp *ContentStreamProcessor) handleCommand_scn(op *ContentStreamOperation) error {
cs := sp.graphicsState.ColorspaceNonStroking
if !isPatternCS(cs) {
if len(op.Params) != cs.GetNumComponents() {
common.Log.Debug("invalid number of parameters for SC")
common.Log.Debug("Number %d not matching colorspace %T", len(op.Params), cs)
return errors.New("invalid number of parameters")
}
}
color, err := cs.ColorFromPdfObjects(op.Params)
if err != nil {
common.Log.Debug("error: Fail to get color from params: %+v (CS is %+v)", op.Params, cs)
return err
}
sp.graphicsState.ColorNonStroking = color
return nil
}
// G: Set the stroking colorspace to DeviceGray, and the color to the specified graylevel (range [0-1]).
// gray G
func (sp *ContentStreamProcessor) handleCommand_G(op *ContentStreamOperation) error {
cs := model.NewPdfColorspaceDeviceGray()
if len(op.Params) != cs.GetNumComponents() {
common.Log.Debug("invalid number of parameters for SC")
common.Log.Debug("Number %d not matching colorspace %T", len(op.Params), cs)
return errors.New("invalid number of parameters")
}
color, err := cs.ColorFromPdfObjects(op.Params)
if err != nil {
return err
}
sp.graphicsState.ColorspaceStroking = cs
sp.graphicsState.ColorStroking = color
return nil
}
// g: Same as G, but for non-stroking colorspace and color (range [0-1]).
// gray g
func (sp *ContentStreamProcessor) handleCommand_g(op *ContentStreamOperation) error {
cs := model.NewPdfColorspaceDeviceGray()
if len(op.Params) != cs.GetNumComponents() {
common.Log.Debug("invalid number of parameters for SC")
common.Log.Debug("Number %d not matching colorspace %T", len(op.Params), cs)
return errors.New("invalid number of parameters")
}
color, err := cs.ColorFromPdfObjects(op.Params)
if err != nil {
return err
}
sp.graphicsState.ColorspaceNonStroking = cs
sp.graphicsState.ColorNonStroking = color
return nil
}
// RG: Sets the stroking colorspace to DeviceRGB and the stroking color to r,g,b. [0-1] ranges.
// r g b RG
func (sp *ContentStreamProcessor) handleCommand_RG(op *ContentStreamOperation) error {
cs := model.NewPdfColorspaceDeviceRGB()
if len(op.Params) != cs.GetNumComponents() {
common.Log.Debug("invalid number of parameters for SC")
common.Log.Debug("Number %d not matching colorspace %T", len(op.Params), cs)
return errors.New("invalid number of parameters")
}
color, err := cs.ColorFromPdfObjects(op.Params)
if err != nil {
return err
}
sp.graphicsState.ColorspaceStroking = cs
sp.graphicsState.ColorStroking = color
return nil
}
// rg: Same as RG but for non-stroking colorspace, color.
func (sp *ContentStreamProcessor) handleCommand_rg(op *ContentStreamOperation) error {
cs := model.NewPdfColorspaceDeviceRGB()
if len(op.Params) != cs.GetNumComponents() {
common.Log.Debug("invalid number of parameters for SC")
common.Log.Debug("Number %d not matching colorspace %T", len(op.Params), cs)
return errors.New("invalid number of parameters")
}
color, err := cs.ColorFromPdfObjects(op.Params)
if err != nil {
return err
}
sp.graphicsState.ColorspaceNonStroking = cs
sp.graphicsState.ColorNonStroking = color
return nil
}
// K: Sets the stroking colorspace to DeviceCMYK and the stroking color to c,m,y,k. [0-1] ranges.
// c m y k K
func (sp *ContentStreamProcessor) handleCommand_K(op *ContentStreamOperation) error {
cs := model.NewPdfColorspaceDeviceCMYK()
if len(op.Params) != cs.GetNumComponents() {
common.Log.Debug("invalid number of parameters for SC")
common.Log.Debug("Number %d not matching colorspace %T", len(op.Params), cs)
return errors.New("invalid number of parameters")
}
color, err := cs.ColorFromPdfObjects(op.Params)
if err != nil {
return err
}
sp.graphicsState.ColorspaceStroking = cs
sp.graphicsState.ColorStroking = color
return nil
}
// k: Same as K but for non-stroking colorspace, color.
func (sp *ContentStreamProcessor) handleCommand_k(op *ContentStreamOperation) error {
cs := model.NewPdfColorspaceDeviceCMYK()
if len(op.Params) != cs.GetNumComponents() {
common.Log.Debug("invalid number of parameters for SC")
common.Log.Debug("Number %d not matching colorspace %T", len(op.Params), cs)
return errors.New("invalid number of parameters")
}
color, err := cs.ColorFromPdfObjects(op.Params)
if err != nil {
return err
}
sp.graphicsState.ColorspaceNonStroking = cs
sp.graphicsState.ColorNonStroking = color
return nil
}

View File

@@ -0,0 +1,90 @@
package contentstream
import (
"errors"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model"
)
func makeParamsFromFloats(vals []float64) []core.PdfObject {
params := []core.PdfObject{}
for _, val := range vals {
params = append(params, core.MakeFloat(val))
}
return params
}
func makeParamsFromNames(vals []core.PdfObjectName) []core.PdfObject {
params := []core.PdfObject{}
for _, val := range vals {
params = append(params, core.MakeName(string(val)))
}
return params
}
func makeParamsFromStrings(vals []core.PdfObjectString) []core.PdfObject {
params := []core.PdfObject{}
for _, val := range vals {
params = append(params, core.MakeString(string(val)))
}
return params
}
func makeParamsFromInts(vals []int64) []core.PdfObject {
params := []core.PdfObject{}
for _, val := range vals {
params = append(params, core.MakeInteger(val))
}
return params
}
func newIndexedColorspaceFromPdfObject(obj core.PdfObject) (model.PdfColorspace, error) {
arr, ok := obj.(*core.PdfObjectArray)
if !ok {
common.Log.Debug("error: Invalid indexed cs not in array (%#v)", obj)
return nil, errors.New("type check error")
}
if len(*arr) != 4 {
common.Log.Debug("error: Invalid cs array, length != 4 (%d)", len(*arr))
return nil, errors.New("range check error")
}
// Format is [/I base 255 bytes], where base = /G,/RGB,/CMYK
name, ok := (*arr)[0].(*core.PdfObjectName)
if !ok {
common.Log.Debug("error: Invalid cs array first element not a name (array: %#v)", *arr)
return nil, errors.New("type check error")
}
if *name != "I" && *name != "Indexed" {
common.Log.Debug("error: Invalid cs array first element != I (got: %v)", *name)
return nil, errors.New("range check error")
}
// Check base
name, ok = (*arr)[1].(*core.PdfObjectName)
if !ok {
common.Log.Debug("error: Invalid cs array 2nd element not a name (array: %#v)", *arr)
return nil, errors.New("type check error")
}
if *name != "G" && *name != "RGB" && *name != "CMYK" && *name != "DeviceGray" && *name != "DeviceRGB" && *name != "DeviceCMYK" {
common.Log.Debug("error: Invalid cs array 2nd element != G/RGB/CMYK (got: %v)", *name)
return nil, errors.New("range check error")
}
basename := ""
switch *name {
case "G", "DeviceGray":
basename = "DeviceGray"
case "RGB", "DeviceRGB":
basename = "DeviceRGB"
case "CMYK", "DeviceCMYK":
basename = "DeviceCMYK"
}
// Prepare to a format that can be loaded by model's newPdfColorspaceFromPdfObject.
csArr := core.MakeArray(core.MakeName("Indexed"), core.MakeName(basename), (*arr)[2], (*arr)[3])
return model.NewPdfColorspaceFromPdfObject(csArr)
}

View File

@@ -0,0 +1,13 @@
package core
import "errors"
var (
// ErrUnsupportedEncodingParameters error indicates that encoding/decoding was attempted with unsupported
// encoding parameters.
// For example when trying to encode with an unsupported Predictor (flate).
ErrUnsupportedEncodingParameters = errors.New("unsupported encoding parameters")
ErrNoCCITTFaxDecode = errors.New(" CCITTFaxDecode encoding is not yet implemented")
ErrNoJBIG2Decode = errors.New(" JBIG2Decode encoding is not yet implemented")
ErrNoJPXDecode = errors.New(" JPXDecode encoding is not yet implemented")
)

View File

@@ -0,0 +1,372 @@
package core
import (
"bufio"
"bytes"
"errors"
"os"
"strings"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
)
// TODO (v3): Create a new type xrefType which can be an integer and can be used for improved type checking.
// TODO (v3): Unexport these constants and rename with camelCase.
const (
// XREF_TABLE_ENTRY indicates a normal xref table entry.
XREF_TABLE_ENTRY = iota
// XREF_OBJECT_STREAM indicates an xref entry in an xref object stream.
XREF_OBJECT_STREAM = iota
)
// XrefObject defines a cross reference entry which is a map between object number (with generation number) and the
// location of the actual object, either as a file offset (xref table entry), or as a location within an xref
// stream object (xref object stream).
// TODO (v3): Unexport.
type XrefObject struct {
xtype int
objectNumber int
generation int
// For normal xrefs (defined by OFFSET)
offset int64
// For xrefs to object streams.
osObjNumber int
osObjIndex int
}
// XrefTable is a map between object number and corresponding XrefObject.
// TODO (v3): Unexport.
// TODO: Consider changing to a slice, so can maintain the object order without sorting when analyzing.
type XrefTable map[int]XrefObject
// ObjectStream represents an object stream's information which can contain multiple indirect objects.
// The information specifies the number of objects and has information about offset locations for
// each object.
// TODO (v3): Unexport.
type ObjectStream struct {
N int // TODO (v3): Unexport.
ds []byte
offsets map[int]int64
}
// ObjectStreams defines a map between object numbers (object streams only) and underlying ObjectStream information.
type ObjectStreams map[int]ObjectStream
// ObjectCache defines a map between object numbers and corresponding PdfObject. Serves as a cache for PdfObjects that
// have already been parsed.
// TODO (v3): Unexport.
type ObjectCache map[int]PdfObject
// Get an object from an object stream.
func (parser *PdfParser) lookupObjectViaOS(sobjNumber int, objNum int) (PdfObject, error) {
var bufReader *bytes.Reader
var objstm ObjectStream
var cached bool
objstm, cached = parser.objstms[sobjNumber]
if !cached {
soi, err := parser.LookupByNumber(sobjNumber)
if err != nil {
common.Log.Debug("Missing object stream with number %d", sobjNumber)
return nil, err
}
so, ok := soi.(*PdfObjectStream)
if !ok {
return nil, errors.New("invalid object stream")
}
if parser.crypter != nil && !parser.crypter.isDecrypted(so) {
return nil, errors.New("need to decrypt the stream")
}
sod := so.PdfObjectDictionary
common.Log.Trace("so d: %s\n", *sod)
name, ok := sod.Get("Type").(*PdfObjectName)
if !ok {
common.Log.Debug("error: Object stream should always have a Type")
return nil, errors.New("object stream missing Type")
}
if strings.ToLower(string(*name)) != "objstm" {
common.Log.Debug("error: Object stream type shall always be ObjStm !")
return nil, errors.New("object stream type != ObjStm")
}
N, ok := sod.Get("N").(*PdfObjectInteger)
if !ok {
return nil, errors.New("invalid N in stream dictionary")
}
firstOffset, ok := sod.Get("First").(*PdfObjectInteger)
if !ok {
return nil, errors.New("invalid First in stream dictionary")
}
common.Log.Trace("type: %s number of objects: %d", name, *N)
ds, err := DecodeStream(so)
if err != nil {
return nil, err
}
common.Log.Trace("Decoded: %s", ds)
// Temporarily change the reader object to this decoded buffer.
// Change back afterwards.
bakOffset := parser.GetFileOffset()
defer func() { parser.SetFileOffset(bakOffset) }()
bufReader = bytes.NewReader(ds)
parser.reader = bufio.NewReader(bufReader)
common.Log.Trace("Parsing offset map")
// Load the offset map (relative to the beginning of the stream...)
offsets := map[int]int64{}
// Object list and offsets.
for i := 0; i < int(*N); i++ {
parser.skipSpaces()
// Object number.
obj, err := parser.parseNumber()
if err != nil {
return nil, err
}
onum, ok := obj.(*PdfObjectInteger)
if !ok {
return nil, errors.New("invalid object stream offset table")
}
parser.skipSpaces()
// Offset.
obj, err = parser.parseNumber()
if err != nil {
return nil, err
}
offset, ok := obj.(*PdfObjectInteger)
if !ok {
return nil, errors.New("invalid object stream offset table")
}
common.Log.Trace("obj %d offset %d", *onum, *offset)
offsets[int(*onum)] = int64(*firstOffset + *offset)
}
objstm = ObjectStream{N: int(*N), ds: ds, offsets: offsets}
parser.objstms[sobjNumber] = objstm
} else {
// Temporarily change the reader object to this decoded buffer.
// Point back afterwards.
bakOffset := parser.GetFileOffset()
defer func() { parser.SetFileOffset(bakOffset) }()
bufReader = bytes.NewReader(objstm.ds)
// Temporarily change the reader object to this decoded buffer.
parser.reader = bufio.NewReader(bufReader)
}
offset := objstm.offsets[objNum]
common.Log.Trace("ACTUAL offset[%d] = %d", objNum, offset)
bufReader.Seek(offset, os.SEEK_SET)
parser.reader = bufio.NewReader(bufReader)
bb, _ := parser.reader.Peek(100)
common.Log.Trace("OBJ peek \"%s\"", string(bb))
val, err := parser.parseObject()
if err != nil {
common.Log.Debug("error Fail to read object (%s)", err)
return nil, err
}
if val == nil {
return nil, errors.New("object cannot be null")
}
// Make an indirect object around it.
io := PdfIndirectObject{}
io.ObjectNumber = int64(objNum)
io.PdfObject = val
return &io, nil
}
// LookupByNumber looks up a PdfObject by object number. Returns an error on failure.
// TODO (v3): Unexport.
func (parser *PdfParser) LookupByNumber(objNumber int) (PdfObject, error) {
// Outside interface for lookupByNumberWrapper. Default attempts repairs of bad xref tables.
obj, _, err := parser.lookupByNumberWrapper(objNumber, true)
return obj, err
}
// Wrapper for lookupByNumber, checks if object encrypted etc.
func (parser *PdfParser) lookupByNumberWrapper(objNumber int, attemptRepairs bool) (PdfObject, bool, error) {
obj, inObjStream, err := parser.lookupByNumber(objNumber, attemptRepairs)
if err != nil {
return nil, inObjStream, err
}
// If encrypted, decrypt it prior to returning.
// Do not attempt to decrypt objects within object streams.
if !inObjStream && parser.crypter != nil && !parser.crypter.isDecrypted(obj) {
err := parser.crypter.Decrypt(obj, 0, 0)
if err != nil {
return nil, inObjStream, err
}
}
return obj, inObjStream, nil
}
func getObjectNumber(obj PdfObject) (int64, int64, error) {
if io, isIndirect := obj.(*PdfIndirectObject); isIndirect {
return io.ObjectNumber, io.GenerationNumber, nil
}
if so, isStream := obj.(*PdfObjectStream); isStream {
return so.ObjectNumber, so.GenerationNumber, nil
}
return 0, 0, errors.New("not an indirect/stream object")
}
// LookupByNumber
// Repair signals whether to repair if broken.
func (parser *PdfParser) lookupByNumber(objNumber int, attemptRepairs bool) (PdfObject, bool, error) {
obj, ok := parser.ObjCache[objNumber]
if ok {
common.Log.Trace("Returning cached object %d", objNumber)
return obj, false, nil
}
xref, ok := parser.xrefs[objNumber]
if !ok {
// An indirect reference to an undefined object shall not be
// considered an error by a conforming reader; it shall be
// treated as a reference to the null object.
common.Log.Trace("Unable to locate object in xrefs! - Returning null object")
var nullObj PdfObjectNull
return &nullObj, false, nil
}
common.Log.Trace("Lookup obj number %d", objNumber)
switch xref.xtype {
case XREF_TABLE_ENTRY:
common.Log.Trace("xrefobj obj num %d", xref.objectNumber)
common.Log.Trace("xrefobj gen %d", xref.generation)
common.Log.Trace("xrefobj offset %d", xref.offset)
parser.rs.Seek(xref.offset, os.SEEK_SET)
parser.reader = bufio.NewReader(parser.rs)
obj, err := parser.ParseIndirectObject()
if err != nil {
common.Log.Debug("error Failed reading xref (%s)", err)
// Offset pointing to a non-object. Try to repair the file.
if attemptRepairs {
common.Log.Debug("Attempting to repair xrefs (top down)")
xrefTable, err := parser.repairRebuildXrefsTopDown()
if err != nil {
common.Log.Debug("error Failed repair (%s)", err)
return nil, false, err
}
parser.xrefs = *xrefTable
return parser.lookupByNumber(objNumber, false)
}
return nil, false, err
}
if attemptRepairs {
// Check the object number..
// If it does not match, then try to rebuild, i.e. loop through
// all the items in the xref and look each one up and correct.
realObjNum, _, _ := getObjectNumber(obj)
if int(realObjNum) != objNumber {
common.Log.Debug("invalid xrefs: Rebuilding")
err := parser.rebuildXrefTable()
if err != nil {
return nil, false, err
}
// Empty the cache.
parser.ObjCache = ObjectCache{}
// Try looking up again and return.
return parser.lookupByNumberWrapper(objNumber, false)
}
}
common.Log.Trace("Returning obj")
parser.ObjCache[objNumber] = obj
return obj, false, nil
case XREF_OBJECT_STREAM:
common.Log.Trace("xref from object stream!")
common.Log.Trace(">Load via OS!")
common.Log.Trace("Object stream available in object %d/%d", xref.osObjNumber, xref.osObjIndex)
if xref.osObjNumber == objNumber {
common.Log.Debug("error Circular reference!?!")
return nil, true, errors.New(" Xref circular reference")
}
_, exists := parser.xrefs[xref.osObjNumber]
if exists {
optr, err := parser.lookupObjectViaOS(xref.osObjNumber, objNumber) //xref.osObjIndex)
if err != nil {
common.Log.Debug("error Returning ERR (%s)", err)
return nil, true, err
}
common.Log.Trace("<Loaded via OS")
parser.ObjCache[objNumber] = optr
if parser.crypter != nil {
// Mark as decrypted (inside object stream) for caching.
// and avoid decrypting decrypted object.
parser.crypter.DecryptedObjects[optr] = true
}
return optr, true, nil
} else {
common.Log.Debug("?? Belongs to a non-cross referenced object ...!")
return nil, true, errors.New("OS belongs to a non cross referenced object")
}
}
return nil, false, errors.New("unknown xref type")
}
// LookupByReference looks up a PdfObject by a reference.
func (parser *PdfParser) LookupByReference(ref PdfObjectReference) (PdfObject, error) {
common.Log.Trace("Looking up reference %s", ref.String())
return parser.LookupByNumber(int(ref.ObjectNumber))
}
// Trace traces a PdfObject to direct object, looking up and resolving references as needed (unlike TraceToDirect).
// TODO (v3): Unexport.
func (parser *PdfParser) Trace(obj PdfObject) (PdfObject, error) {
ref, isRef := obj.(*PdfObjectReference)
if !isRef {
// Direct object already.
return obj, nil
}
bakOffset := parser.GetFileOffset()
defer func() { parser.SetFileOffset(bakOffset) }()
o, err := parser.LookupByReference(*ref)
if err != nil {
return nil, err
}
io, isInd := o.(*PdfIndirectObject)
if !isInd {
// Not indirect (Stream or null object).
return o, nil
}
o = io.PdfObject
_, isRef = o.(*PdfObjectReference)
if isRef {
return io, errors.New("multi depth trace pointer to pointer")
}
return o, nil
}
func printXrefTable(xrefTable XrefTable) {
common.Log.Debug("=X=X=X=")
common.Log.Debug("Xref table:")
i := 0
for _, xref := range xrefTable {
common.Log.Debug("i+1: %d (obj num: %d gen: %d) -> %d", i+1, xref.objectNumber, xref.generation, xref.offset)
i++
}
}

1732
internal/pdf/core/crypt.go Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,265 @@
package core
import (
"crypto/aes"
"crypto/cipher"
"crypto/md5"
"crypto/rand"
"crypto/rc4"
"fmt"
"io"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
)
var (
cryptMethods = make(map[string]cryptFilterMethod)
)
// registerCryptFilterMethod registers a CFM.
func registerCryptFilterMethod(m cryptFilterMethod) {
cryptMethods[m.CFM()] = m
}
// getCryptFilterMethod check if a CFM with a specified name is supported an returns its implementation.
func getCryptFilterMethod(name string) (cryptFilterMethod, error) {
f := cryptMethods[name]
if f == nil {
return nil, fmt.Errorf("unsupported crypt filter: %q", name)
}
return f, nil
}
func init() {
// register supported crypt filter methods
registerCryptFilterMethod(cryptFilterV2{})
registerCryptFilterMethod(cryptFilterAESV2{})
registerCryptFilterMethod(cryptFilterAESV3{})
}
// cryptFilterMethod is a common interface for crypt filter methods.
type cryptFilterMethod interface {
// CFM returns a name of the filter that should be used in CFM field of Encrypt dictionary.
CFM() string
// MakeKey generates a object encryption key based on file encryption key and object numbers.
// Used only for legacy filters - AESV3 doesn't change the key for each object.
MakeKey(objNum, genNum uint32, fkey []byte) ([]byte, error)
// EncryptBytes encrypts a buffer using object encryption key, as returned by MakeKey.
// Implementation may reuse a buffer and encrypt data in-place.
EncryptBytes(p []byte, okey []byte) ([]byte, error)
// DecryptBytes decrypts a buffer using object encryption key, as returned by MakeKey.
// Implementation may reuse a buffer and decrypt data in-place.
DecryptBytes(p []byte, okey []byte) ([]byte, error)
}
// makeKeyV2 is a common object key generation shared by V2 and AESV2 crypt filters.
func makeKeyV2(objNum, genNum uint32, ekey []byte, isAES bool) ([]byte, error) {
key := make([]byte, len(ekey)+5)
copy(key, ekey)
for i := 0; i < 3; i++ {
b := byte((objNum >> uint32(8*i)) & 0xff)
key[i+len(ekey)] = b
}
for i := 0; i < 2; i++ {
b := byte((genNum >> uint32(8*i)) & 0xff)
key[i+len(ekey)+3] = b
}
if isAES {
// If using the AES algorithm, extend the encryption key an
// additional 4 bytes by adding the value “sAlT”, which
// corresponds to the hexadecimal values 0x73, 0x41, 0x6C, 0x54.
key = append(key, 0x73)
key = append(key, 0x41)
key = append(key, 0x6C)
key = append(key, 0x54)
}
// Take the MD5.
h := md5.New()
h.Write(key)
hashb := h.Sum(nil)
if len(ekey)+5 < 16 {
return hashb[0 : len(ekey)+5], nil
}
return hashb, nil
}
// cryptFilterV2 is a RC4-based filter
type cryptFilterV2 struct{}
func (cryptFilterV2) CFM() string {
return CryptFilterV2
}
func (f cryptFilterV2) MakeKey(objNum, genNum uint32, ekey []byte) ([]byte, error) {
return makeKeyV2(objNum, genNum, ekey, false)
}
func (cryptFilterV2) EncryptBytes(buf []byte, okey []byte) ([]byte, error) {
// Standard RC4 algorithm.
ciph, err := rc4.NewCipher(okey)
if err != nil {
return nil, err
}
common.Log.Trace("RC4 Encrypt: % x", buf)
ciph.XORKeyStream(buf, buf)
common.Log.Trace("to: % x", buf)
return buf, nil
}
func (cryptFilterV2) DecryptBytes(buf []byte, okey []byte) ([]byte, error) {
// Standard RC4 algorithm.
ciph, err := rc4.NewCipher(okey)
if err != nil {
return nil, err
}
common.Log.Trace("RC4 Decrypt: % x", buf)
ciph.XORKeyStream(buf, buf)
common.Log.Trace("to: % x", buf)
return buf, nil
}
// cryptFilterAES implements a generic AES encryption and decryption algorithm used by AESV2 and AESV3 filter methods.
type cryptFilterAES struct{}
func (cryptFilterAES) EncryptBytes(buf []byte, okey []byte) ([]byte, error) {
// Strings and streams encrypted with AES shall use a padding
// scheme that is described in Internet RFC 2898, PKCS #5:
// Password-Based Cryptography Specification Version 2.0; see
// the Bibliography. For an original message length of M,
// the pad shall consist of 16 - (M mod 16) bytes whose value
// shall also be 16 - (M mod 16).
//
// A 9-byte message has a pad of 7 bytes, each with the value
// 0x07. The pad can be unambiguously removed to determine the
// original message length when decrypting. Note that the pad is
// present when M is evenly divisible by 16; it contains 16 bytes
// of 0x10.
ciph, err := aes.NewCipher(okey)
if err != nil {
return nil, err
}
common.Log.Trace("AES Encrypt (%d): % x", len(buf), buf)
// If using the AES algorithm, the Cipher Block Chaining (CBC)
// mode, which requires an initialization vector, is used. The
// block size parameter is set to 16 bytes, and the initialization
// vector is a 16-byte random number that is stored as the first
// 16 bytes of the encrypted stream or string.
const block = aes.BlockSize // 16
pad := block - len(buf)%block
for i := 0; i < pad; i++ {
buf = append(buf, byte(pad))
}
common.Log.Trace("Padded to %d bytes", len(buf))
// Generate random 16 bytes, place in beginning of buffer.
ciphertext := make([]byte, block+len(buf))
iv := ciphertext[:block]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return nil, err
}
mode := cipher.NewCBCEncrypter(ciph, iv)
mode.CryptBlocks(ciphertext[block:], buf)
buf = ciphertext
common.Log.Trace("to (%d): % x", len(buf), buf)
return buf, nil
}
func (cryptFilterAES) DecryptBytes(buf []byte, okey []byte) ([]byte, error) {
// Strings and streams encrypted with AES shall use a padding
// scheme that is described in Internet RFC 2898, PKCS #5:
// Password-Based Cryptography Specification Version 2.0; see
// the Bibliography. For an original message length of M,
// the pad shall consist of 16 - (M mod 16) bytes whose value
// shall also be 16 - (M mod 16).
//
// A 9-byte message has a pad of 7 bytes, each with the value
// 0x07. The pad can be unambiguously removed to determine the
// original message length when decrypting. Note that the pad is
// present when M is evenly divisible by 16; it contains 16 bytes
// of 0x10.
ciph, err := aes.NewCipher(okey)
if err != nil {
return nil, err
}
// If using the AES algorithm, the Cipher Block Chaining (CBC)
// mode, which requires an initialization vector, is used. The
// block size parameter is set to 16 bytes, and the initialization
// vector is a 16-byte random number that is stored as the first
// 16 bytes of the encrypted stream or string.
if len(buf) < 16 {
common.Log.Debug("error AES invalid buf %s", buf)
return buf, fmt.Errorf("AES: Buf len < 16 (%d)", len(buf))
}
iv := buf[:16]
buf = buf[16:]
if len(buf)%16 != 0 {
common.Log.Debug(" iv (%d): % x", len(iv), iv)
common.Log.Debug("buf (%d): % x", len(buf), buf)
return buf, fmt.Errorf("AES buf length not multiple of 16 (%d)", len(buf))
}
mode := cipher.NewCBCDecrypter(ciph, iv)
common.Log.Trace("AES Decrypt (%d): % x", len(buf), buf)
common.Log.Trace("chop AES Decrypt (%d): % x", len(buf), buf)
mode.CryptBlocks(buf, buf)
common.Log.Trace("to (%d): % x", len(buf), buf)
if len(buf) == 0 {
common.Log.Trace("Empty buf, returning empty string")
return buf, nil
}
// The padded length is indicated by the last values. Remove those.
padLen := int(buf[len(buf)-1])
if padLen >= len(buf) {
common.Log.Debug("Illegal pad length")
return buf, fmt.Errorf("invalid pad length")
}
buf = buf[:len(buf)-padLen]
return buf, nil
}
// cryptFilterAESV2 is an AES-based filter (128 bit key, PDF 1.6)
type cryptFilterAESV2 struct {
cryptFilterAES
}
func (cryptFilterAESV2) CFM() string {
return CryptFilterAESV2
}
func (cryptFilterAESV2) MakeKey(objNum, genNum uint32, ekey []byte) ([]byte, error) {
return makeKeyV2(objNum, genNum, ekey, true)
}
// cryptFilterAESV3 is an AES-based filter (256 bit key, PDF 2.0)
type cryptFilterAESV3 struct {
cryptFilterAES
}
func (cryptFilterAESV3) CFM() string {
return CryptFilterAESV3
}
func (cryptFilterAESV3) MakeKey(_, _ uint32, ekey []byte) ([]byte, error) {
return ekey, nil
}

61
internal/pdf/core/ecb.go Normal file
View File

@@ -0,0 +1,61 @@
package core
import "crypto/cipher"
// ecb implements an Electronic Codebook encryption mode.
// This mode is used to compute or validate document permissions for R=6.
type ecb struct {
b cipher.Block
blockSize int
}
func newECB(b cipher.Block) *ecb {
return &ecb{
b: b,
blockSize: b.BlockSize(),
}
}
type ecbEncrypter ecb
func newECBEncrypter(b cipher.Block) cipher.BlockMode {
return (*ecbEncrypter)(newECB(b))
}
func (x *ecbEncrypter) BlockSize() int { return x.blockSize }
func (x *ecbEncrypter) CryptBlocks(dst, src []byte) {
if len(src)%x.blockSize != 0 {
panic("crypto/cipher: input not full blocks")
}
if len(dst) < len(src) {
panic("crypto/cipher: output smaller than input")
}
for len(src) > 0 {
x.b.Encrypt(dst, src[:x.blockSize])
src = src[x.blockSize:]
dst = dst[x.blockSize:]
}
}
type ecbDecrypter ecb
func newECBDecrypter(b cipher.Block) cipher.BlockMode {
return (*ecbDecrypter)(newECB(b))
}
func (x *ecbDecrypter) BlockSize() int { return x.blockSize }
func (x *ecbDecrypter) CryptBlocks(dst, src []byte) {
if len(src)%x.blockSize != 0 {
panic("crypto/cipher: input not full blocks")
}
if len(dst) < len(src) {
panic("crypto/cipher: output smaller than input")
}
for len(src) > 0 {
x.b.Decrypt(dst, src[:x.blockSize])
src = src[x.blockSize:]
dst = dst[x.blockSize:]
}
}

File diff suppressed because it is too large Load Diff

44
internal/pdf/core/io.go Normal file
View File

@@ -0,0 +1,44 @@
package core
import (
"bufio"
"errors"
"os"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
)
// ReadAtLeast reads at least n bytes into slice p.
// Returns the number of bytes read (should always be == n), and an error on failure.
// TODO (v3): Unexport.
func (parser *PdfParser) ReadAtLeast(p []byte, n int) (int, error) {
remaining := n
start := 0
numRounds := 0
for remaining > 0 {
nRead, err := parser.reader.Read(p[start:])
if err != nil {
common.Log.Debug("error Failed reading (%d;%d) %s", nRead, numRounds, err.Error())
return start, errors.New("failed reading")
}
numRounds++
start += nRead
remaining -= nRead
}
return start, nil
}
// Get the current file offset, accounting for buffered position.
// TODO (v3): Unexport.
func (parser *PdfParser) GetFileOffset() int64 {
offset, _ := parser.rs.Seek(0, os.SEEK_CUR)
offset -= int64(parser.reader.Buffered())
return offset
}
// Seek the file to an offset position.
// TODO (v3): Unexport.
func (parser *PdfParser) SetFileOffset(offset int64) {
parser.rs.Seek(offset, os.SEEK_SET)
parser.reader = bufio.NewReader(parser.rs)
}

1644
internal/pdf/core/parser.go Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,570 @@
package core
import (
"bytes"
"fmt"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
)
// PdfObject is an interface which all primitive PDF objects must implement.
type PdfObject interface {
// Output a string representation of the primitive (for debugging).
String() string
// Output the PDF primitive as written to file as expected by the standard.
DefaultWriteString() string
}
// PdfObjectBool represents the primitive PDF boolean object.
type PdfObjectBool bool
// PdfObjectInteger represents the primitive PDF integer numerical object.
type PdfObjectInteger int64
// PdfObjectFloat represents the primitive PDF floating point numerical object.
type PdfObjectFloat float64
// PdfObjectString represents the primitive PDF string object.
// TODO (v3): Change to a struct and add a flag for hex/plaintext.
type PdfObjectString string
// PdfObjectName represents the primitive PDF name object.
type PdfObjectName string
// PdfObjectArray represents the primitive PDF array object.
type PdfObjectArray []PdfObject
// PdfObjectDictionary represents the primitive PDF dictionary/map object.
type PdfObjectDictionary struct {
dict map[PdfObjectName]PdfObject
keys []PdfObjectName
}
// PdfObjectNull represents the primitive PDF null object.
type PdfObjectNull struct{}
// PdfObjectReference represents the primitive PDF reference object.
type PdfObjectReference struct {
ObjectNumber int64
GenerationNumber int64
}
// PdfIndirectObject represents the primitive PDF indirect object.
type PdfIndirectObject struct {
PdfObjectReference
PdfObject
}
// PdfObjectStream represents the primitive PDF Object stream.
type PdfObjectStream struct {
PdfObjectReference
*PdfObjectDictionary
Stream []byte
}
// MakeDict creates and returns an empty PdfObjectDictionary.
func MakeDict() *PdfObjectDictionary {
d := &PdfObjectDictionary{}
d.dict = map[PdfObjectName]PdfObject{}
d.keys = []PdfObjectName{}
return d
}
// MakeName creates a PdfObjectName from a string.
func MakeName(s string) *PdfObjectName {
name := PdfObjectName(s)
return &name
}
// MakeInteger creates a PdfObjectInteger from an int64.
func MakeInteger(val int64) *PdfObjectInteger {
num := PdfObjectInteger(val)
return &num
}
// MakeArray creates an PdfObjectArray from a list of PdfObjects.
func MakeArray(objects ...PdfObject) *PdfObjectArray {
array := PdfObjectArray{}
for _, obj := range objects {
array = append(array, obj)
}
return &array
}
// MakeArrayFromIntegers creates an PdfObjectArray from a slice of ints, where each array element is
// an PdfObjectInteger.
func MakeArrayFromIntegers(vals []int) *PdfObjectArray {
array := PdfObjectArray{}
for _, val := range vals {
array = append(array, MakeInteger(int64(val)))
}
return &array
}
// MakeArrayFromIntegers64 creates an PdfObjectArray from a slice of int64s, where each array element
// is an PdfObjectInteger.
func MakeArrayFromIntegers64(vals []int64) *PdfObjectArray {
array := PdfObjectArray{}
for _, val := range vals {
array = append(array, MakeInteger(val))
}
return &array
}
// MakeArrayFromFloats creates an PdfObjectArray from a slice of float64s, where each array element is an
// PdfObjectFloat.
func MakeArrayFromFloats(vals []float64) *PdfObjectArray {
array := PdfObjectArray{}
for _, val := range vals {
array = append(array, MakeFloat(val))
}
return &array
}
// MakeBool creates an PdfObjectBool from a bool.
func MakeBool(val bool) *PdfObjectBool {
v := PdfObjectBool(val)
return &v
}
// MakeFloat creates an PdfObjectFloat from a float64.
func MakeFloat(val float64) *PdfObjectFloat {
num := PdfObjectFloat(val)
return &num
}
// MakeString creates an PdfObjectString from a string.
func MakeString(s string) *PdfObjectString {
str := PdfObjectString(s)
return &str
}
// MakeNull creates an PdfObjectNull.
func MakeNull() *PdfObjectNull {
null := PdfObjectNull{}
return &null
}
// MakeIndirectObject creates an PdfIndirectObject with a specified direct object PdfObject.
func MakeIndirectObject(obj PdfObject) *PdfIndirectObject {
ind := &PdfIndirectObject{}
ind.PdfObject = obj
return ind
}
// MakeStream creates an PdfObjectStream with specified contents and encoding. If encoding is nil, then raw encoding
// will be used (i.e. no encoding applied).
func MakeStream(contents []byte, encoder StreamEncoder) (*PdfObjectStream, error) {
stream := &PdfObjectStream{}
if encoder == nil {
encoder = NewRawEncoder()
}
stream.PdfObjectDictionary = encoder.MakeStreamDict()
encoded, err := encoder.EncodeBytes(contents)
if err != nil {
return nil, err
}
stream.PdfObjectDictionary.Set("Length", MakeInteger(int64(len(encoded))))
stream.Stream = encoded
return stream, nil
}
func (bool *PdfObjectBool) String() string {
if *bool {
return "true"
} else {
return "false"
}
}
// DefaultWriteString outputs the object as it is to be written to file.
func (bool *PdfObjectBool) DefaultWriteString() string {
if *bool {
return "true"
} else {
return "false"
}
}
func (int *PdfObjectInteger) String() string {
return fmt.Sprintf("%d", *int)
}
// DefaultWriteString outputs the object as it is to be written to file.
func (int *PdfObjectInteger) DefaultWriteString() string {
return fmt.Sprintf("%d", *int)
}
func (float *PdfObjectFloat) String() string {
return fmt.Sprintf("%f", *float)
}
// DefaultWriteString outputs the object as it is to be written to file.
func (float *PdfObjectFloat) DefaultWriteString() string {
return fmt.Sprintf("%f", *float)
}
func (str *PdfObjectString) String() string {
return string(*str)
}
// DefaultWriteString outputs the object as it is to be written to file.
func (str *PdfObjectString) DefaultWriteString() string {
var output bytes.Buffer
escapeSequences := map[byte]string{
'\n': "\\n",
'\r': "\\r",
'\t': "\\t",
'\b': "\\b",
'\f': "\\f",
'(': "\\(",
')': "\\)",
'\\': "\\\\",
}
output.WriteString("(")
for i := 0; i < len(*str); i++ {
char := (*str)[i]
if escStr, useEsc := escapeSequences[char]; useEsc {
output.WriteString(escStr)
} else {
output.WriteByte(char)
}
}
output.WriteString(")")
return output.String()
}
func (name *PdfObjectName) String() string {
return string(*name)
}
// DefaultWriteString outputs the object as it is to be written to file.
func (name *PdfObjectName) DefaultWriteString() string {
var output bytes.Buffer
if len(*name) > 127 {
common.Log.Debug("error: Name too long (%s)", *name)
}
output.WriteString("/")
for i := 0; i < len(*name); i++ {
char := (*name)[i]
if !IsPrintable(char) || char == '#' || IsDelimiter(char) {
output.WriteString(fmt.Sprintf("#%.2x", char))
} else {
output.WriteByte(char)
}
}
return output.String()
}
// ToFloat64Array returns a slice of all elements in the array as a float64 slice. An error is returned if the array
// contains non-numeric objects (each element can be either PdfObjectInteger or PdfObjectFloat).
func (array *PdfObjectArray) ToFloat64Array() ([]float64, error) {
vals := []float64{}
for _, obj := range *array {
if number, is := obj.(*PdfObjectInteger); is {
vals = append(vals, float64(*number))
} else if number, is := obj.(*PdfObjectFloat); is {
vals = append(vals, float64(*number))
} else {
return nil, fmt.Errorf("type error")
}
}
return vals, nil
}
// ToIntegerArray returns a slice of all array elements as an int slice. An error is returned if the array contains
// non-integer objects. Each element can only be PdfObjectInteger.
func (array *PdfObjectArray) ToIntegerArray() ([]int, error) {
vals := []int{}
for _, obj := range *array {
if number, is := obj.(*PdfObjectInteger); is {
vals = append(vals, int(*number))
} else {
return nil, fmt.Errorf("type error")
}
}
return vals, nil
}
func (array *PdfObjectArray) String() string {
outStr := "["
for ind, o := range *array {
outStr += o.String()
if ind < (len(*array) - 1) {
outStr += ", "
}
}
outStr += "]"
return outStr
}
// DefaultWriteString outputs the object as it is to be written to file.
func (array *PdfObjectArray) DefaultWriteString() string {
outStr := "["
for ind, o := range *array {
outStr += o.DefaultWriteString()
if ind < (len(*array) - 1) {
outStr += " "
}
}
outStr += "]"
return outStr
}
// Append adds an PdfObject to the array.
func (array *PdfObjectArray) Append(obj PdfObject) {
*array = append(*array, obj)
}
func getNumberAsFloat(obj PdfObject) (float64, error) {
if fObj, ok := obj.(*PdfObjectFloat); ok {
return float64(*fObj), nil
}
if iObj, ok := obj.(*PdfObjectInteger); ok {
return float64(*iObj), nil
}
return 0, fmt.Errorf("not a number")
}
// GetAsFloat64Slice returns the array as []float64 slice.
// Returns an error if not entirely numeric (only PdfObjectIntegers, PdfObjectFloats).
func (array *PdfObjectArray) GetAsFloat64Slice() ([]float64, error) {
slice := []float64{}
for _, obj := range *array {
obj := TraceToDirectObject(obj)
number, err := getNumberAsFloat(obj)
if err != nil {
return nil, fmt.Errorf("array element not a number")
}
slice = append(slice, number)
}
return slice, nil
}
// Merge merges in key/values from another dictionary. Overwriting if has same keys.
func (d *PdfObjectDictionary) Merge(another *PdfObjectDictionary) {
if another != nil {
for _, key := range another.Keys() {
val := another.Get(key)
d.Set(key, val)
}
}
}
func (d *PdfObjectDictionary) String() string {
outStr := "Dict("
for _, k := range d.keys {
v := d.dict[k]
outStr += fmt.Sprintf("\"%s\": %s, ", k, v.String())
}
outStr += ")"
return outStr
}
// DefaultWriteString outputs the object as it is to be written to file.
func (d *PdfObjectDictionary) DefaultWriteString() string {
outStr := "<<"
for _, k := range d.keys {
v := d.dict[k]
common.Log.Trace("Writing k: %s %T %v %v", k, v, k, v)
outStr += k.DefaultWriteString()
outStr += " "
outStr += v.DefaultWriteString()
}
outStr += ">>"
return outStr
}
// Set sets the dictionary's key -> val mapping entry. Overwrites if key already set.
func (d *PdfObjectDictionary) Set(key PdfObjectName, val PdfObject) {
found := false
for _, k := range d.keys {
if k == key {
found = true
break
}
}
if !found {
d.keys = append(d.keys, key)
}
d.dict[key] = val
}
// Get returns the PdfObject corresponding to the specified key.
// Returns a nil value if the key is not set.
//
// The design is such that we only return 1 value.
// The reason is that, it will be easy to do type casts such as
// name, ok := dict.Get("mykey").(*PdfObjectName)
// if !ok ....
func (d *PdfObjectDictionary) Get(key PdfObjectName) PdfObject {
val, has := d.dict[key]
if !has {
return nil
}
return val
}
// Keys returns the list of keys in the dictionary.
func (d *PdfObjectDictionary) Keys() []PdfObjectName {
return d.keys
}
// Remove removes an element specified by key.
func (d *PdfObjectDictionary) Remove(key PdfObjectName) {
idx := -1
for i, k := range d.keys {
if k == key {
idx = i
break
}
}
if idx >= 0 {
// Found. Remove from key list and map.
d.keys = append(d.keys[:idx], d.keys[idx+1:]...)
delete(d.dict, key)
}
}
// SetIfNotNil sets the dictionary's key -> val mapping entry -IF- val is not nil.
// Note that we take care to perform a type switch. Otherwise if we would supply a nil value
// of another type, e.g. (PdfObjectArray*)(nil), then it would not be a PdfObject(nil) and thus
// would get set.
func (d *PdfObjectDictionary) SetIfNotNil(key PdfObjectName, val PdfObject) {
if val != nil {
switch t := val.(type) {
case *PdfObjectName:
if t != nil {
d.Set(key, val)
}
case *PdfObjectDictionary:
if t != nil {
d.Set(key, val)
}
case *PdfObjectStream:
if t != nil {
d.Set(key, val)
}
case *PdfObjectString:
if t != nil {
d.Set(key, val)
}
case *PdfObjectNull:
if t != nil {
d.Set(key, val)
}
case *PdfObjectInteger:
if t != nil {
d.Set(key, val)
}
case *PdfObjectArray:
if t != nil {
d.Set(key, val)
}
case *PdfObjectBool:
if t != nil {
d.Set(key, val)
}
case *PdfObjectFloat:
if t != nil {
d.Set(key, val)
}
case *PdfObjectReference:
if t != nil {
d.Set(key, val)
}
case *PdfIndirectObject:
if t != nil {
d.Set(key, val)
}
default:
common.Log.Error("error: Unknown type: %T - should never happen!", val)
}
}
}
func (ref *PdfObjectReference) String() string {
return fmt.Sprintf("Ref(%d %d)", ref.ObjectNumber, ref.GenerationNumber)
}
// DefaultWriteString outputs the object as it is to be written to file.
func (ref *PdfObjectReference) DefaultWriteString() string {
return fmt.Sprintf("%d %d R", ref.ObjectNumber, ref.GenerationNumber)
}
func (ind *PdfIndirectObject) String() string {
// Avoid printing out the object, can cause problems with circular
// references.
return fmt.Sprintf("IObject:%d", (*ind).ObjectNumber)
}
// DefaultWriteString outputs the object as it is to be written to file.
func (ind *PdfIndirectObject) DefaultWriteString() string {
outStr := fmt.Sprintf("%d 0 R", (*ind).ObjectNumber)
return outStr
}
func (stream *PdfObjectStream) String() string {
return fmt.Sprintf("Object stream %d: %s", stream.ObjectNumber, stream.PdfObjectDictionary)
}
// DefaultWriteString outputs the object as it is to be written to file.
func (stream *PdfObjectStream) DefaultWriteString() string {
outStr := fmt.Sprintf("%d 0 R", (*stream).ObjectNumber)
return outStr
}
func (null *PdfObjectNull) String() string {
return "null"
}
// DefaultWriteString outputs the object as it is to be written to file.
func (null *PdfObjectNull) DefaultWriteString() string {
return "null"
}
// Handy functions to work with primitive objects.
// TraceMaxDepth specifies the maximum recursion depth allowed.
const TraceMaxDepth = 20
// TraceToDirectObject traces a PdfObject to a direct object. For example direct objects contained
// in indirect objects (can be double referenced even).
//
// Note: This function does not trace/resolve references. That needs to be done beforehand.
func TraceToDirectObject(obj PdfObject) PdfObject {
iobj, isIndirectObj := obj.(*PdfIndirectObject)
depth := 0
for isIndirectObj {
obj = iobj.PdfObject
iobj, isIndirectObj = obj.(*PdfIndirectObject)
depth++
if depth > TraceMaxDepth {
common.Log.Error("error: Trace depth level beyond %d - not going deeper!", TraceMaxDepth)
return nil
}
}
return obj
}

View File

@@ -0,0 +1,281 @@
// Routines related to repairing malformed pdf files.
package core
import (
"errors"
"fmt"
"os"
"regexp"
"bufio"
"io"
"strconv"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
)
var repairReXrefTable = regexp.MustCompile(`[\r\n]\s*(xref)\s*[\r\n]`)
// Locates a standard Xref table by looking for the "xref" entry.
// Xref object stream not supported.
func (parser *PdfParser) repairLocateXref() (int64, error) {
readBuf := int64(1000)
parser.rs.Seek(-readBuf, os.SEEK_CUR)
curOffset, err := parser.rs.Seek(0, os.SEEK_CUR)
if err != nil {
return 0, err
}
b2 := make([]byte, readBuf)
parser.rs.Read(b2)
results := repairReXrefTable.FindAllStringIndex(string(b2), -1)
if len(results) < 1 {
common.Log.Debug("error: Repair: xref not found!")
return 0, errors.New("repair: xref not found")
}
localOffset := int64(results[len(results)-1][0])
xrefOffset := curOffset + localOffset
return xrefOffset, nil
}
// Renumbers the xref table.
// Useful when the cross reference is pointing to an object with the wrong number.
// Update the table.
func (parser *PdfParser) rebuildXrefTable() error {
newXrefs := XrefTable{}
for objNum, xref := range parser.xrefs {
obj, _, err := parser.lookupByNumberWrapper(objNum, false)
if err != nil {
common.Log.Debug("error: Unable to look up object (%s)", err)
common.Log.Debug("error: Xref table completely broken - attempting to repair ")
xrefTable, err := parser.repairRebuildXrefsTopDown()
if err != nil {
common.Log.Debug("error: Failed xref rebuild repair (%s)", err)
return err
}
parser.xrefs = *xrefTable
common.Log.Debug("Repaired xref table built")
return nil
}
actObjNum, actGenNum, err := getObjectNumber(obj)
if err != nil {
return err
}
xref.objectNumber = int(actObjNum)
xref.generation = int(actGenNum)
newXrefs[int(actObjNum)] = xref
}
parser.xrefs = newXrefs
common.Log.Debug("New xref table built")
printXrefTable(parser.xrefs)
return nil
}
// Parses and returns the object and generation number from a string such as "12 0 obj" -> (12,0,nil).
func parseObjectNumberFromString(str string) (int, int, error) {
result := reIndirectObject.FindStringSubmatch(str)
if len(result) < 3 {
return 0, 0, errors.New("unable to detect indirect object signature")
}
on, _ := strconv.Atoi(result[1])
gn, _ := strconv.Atoi(result[2])
return on, gn, nil
}
// Parse the entire file from top down.
// Goes through the file byte-by-byte looking for "<num> <generation> obj" patterns.
// N.B. This collects the XREF_TABLE_ENTRY data only.
func (parser *PdfParser) repairRebuildXrefsTopDown() (*XrefTable, error) {
if parser.repairsAttempted {
// Avoid multiple repairs (only try once).
return nil, fmt.Errorf("repair failed")
}
parser.repairsAttempted = true
// Go to beginning, reset reader.
parser.rs.Seek(0, os.SEEK_SET)
parser.reader = bufio.NewReader(parser.rs)
// Keep a running buffer of last bytes.
bufLen := 20
last := make([]byte, bufLen)
xrefTable := XrefTable{}
for {
b, err := parser.reader.ReadByte()
if err != nil {
if err == io.EOF {
break
} else {
return nil, err
}
}
// Format:
// object number - whitespace - generation number - obj
// e.g. "12 0 obj"
if b == 'j' && last[bufLen-1] == 'b' && last[bufLen-2] == 'o' && IsWhiteSpace(last[bufLen-3]) {
i := bufLen - 4
// Go past whitespace
for IsWhiteSpace(last[i]) && i > 0 {
i--
}
if i == 0 || !IsDecimalDigit(last[i]) {
continue
}
// Go past generation number
for IsDecimalDigit(last[i]) && i > 0 {
i--
}
if i == 0 || !IsWhiteSpace(last[i]) {
continue
}
// Go past whitespace
for IsWhiteSpace(last[i]) && i > 0 {
i--
}
if i == 0 || !IsDecimalDigit(last[i]) {
continue
}
// Go past object number.
for IsDecimalDigit(last[i]) && i > 0 {
i--
}
if i == 0 {
continue // Probably too long to be a valid object...
}
objOffset := parser.GetFileOffset() - int64(bufLen-i)
objstr := append(last[i+1:], b)
objNum, genNum, err := parseObjectNumberFromString(string(objstr))
if err != nil {
common.Log.Debug("Unable to parse object number: %v", err)
return nil, err
}
// Create and insert the XREF entry if not existing, or the generation number is higher.
if curXref, has := xrefTable[objNum]; !has || curXref.generation < genNum {
// Make the entry for the cross ref table.
xrefEntry := XrefObject{}
xrefEntry.xtype = XREF_TABLE_ENTRY
xrefEntry.objectNumber = int(objNum)
xrefEntry.generation = int(genNum)
xrefEntry.offset = objOffset
xrefTable[objNum] = xrefEntry
}
}
last = append(last[1:bufLen], b)
}
return &xrefTable, nil
}
// Look for first sign of xref table from end of file.
func (parser *PdfParser) repairSeekXrefMarker() error {
// Get the file size.
fSize, err := parser.rs.Seek(0, os.SEEK_END)
if err != nil {
return err
}
reXrefTableStart := regexp.MustCompile(`\sxref\s*`)
// Define the starting point (from the end of the file) to search from.
var offset int64 = 0
// Define an buffer length in terms of how many bytes to read from the end of the file.
var buflen int64 = 1000
for offset < fSize {
if fSize <= (buflen + offset) {
buflen = fSize - offset
}
// Move back enough (as we need to read forward).
_, err := parser.rs.Seek(-offset-buflen, os.SEEK_END)
if err != nil {
return err
}
// Read the data.
b1 := make([]byte, buflen)
parser.rs.Read(b1)
common.Log.Trace("Looking for xref : \"%s\"", string(b1))
ind := reXrefTableStart.FindAllStringIndex(string(b1), -1)
if ind != nil {
// Found it.
lastInd := ind[len(ind)-1]
common.Log.Trace("Ind: % d", ind)
parser.rs.Seek(-offset-buflen+int64(lastInd[0]), os.SEEK_END)
parser.reader = bufio.NewReader(parser.rs)
// Go past whitespace, finish at 'x'.
for {
bb, err := parser.reader.Peek(1)
if err != nil {
return err
}
common.Log.Trace("B: %d %c", bb[0], bb[0])
if !IsWhiteSpace(bb[0]) {
break
}
parser.reader.Discard(1)
}
return nil
} else {
common.Log.Debug("warning: EOF marker not found! - continue seeking")
}
offset += buflen
}
common.Log.Debug("error: Xref table marker was not found.")
return errors.New("xref not found ")
}
// Called when Pdf version not found normally. Looks for the PDF version by scanning top-down.
// %PDF-1.7
func (parser *PdfParser) seekPdfVersionTopDown() (int, int, error) {
// Go to beginning, reset reader.
parser.rs.Seek(0, os.SEEK_SET)
parser.reader = bufio.NewReader(parser.rs)
// Keep a running buffer of last bytes.
bufLen := 20
last := make([]byte, bufLen)
for {
b, err := parser.reader.ReadByte()
if err != nil {
if err == io.EOF {
break
} else {
return 0, 0, err
}
}
// Format:
// object number - whitespace - generation number - obj
// e.g. "12 0 obj"
if IsDecimalDigit(b) && last[bufLen-1] == '.' && IsDecimalDigit(last[bufLen-2]) && last[bufLen-3] == '-' &&
last[bufLen-4] == 'F' && last[bufLen-5] == 'D' && last[bufLen-6] == 'P' {
major := int(last[bufLen-2] - '0')
minor := int(b - '0')
return major, minor, nil
}
last = append(last[1:bufLen], b)
}
return 0, 0, errors.New("version not found")
}

129
internal/pdf/core/stream.go Normal file
View File

@@ -0,0 +1,129 @@
package core
import (
"fmt"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
)
// NewEncoderFromStream creates a StreamEncoder based on the stream's dictionary.
func NewEncoderFromStream(streamObj *PdfObjectStream) (StreamEncoder, error) {
filterObj := TraceToDirectObject(streamObj.PdfObjectDictionary.Get("Filter"))
if filterObj == nil {
// No filter, return raw data back.
return NewRawEncoder(), nil
}
if _, isNull := filterObj.(*PdfObjectNull); isNull {
// Filter is null -> raw data.
return NewRawEncoder(), nil
}
// The filter should be a name or an array with a list of filter names.
method, ok := filterObj.(*PdfObjectName)
if !ok {
array, ok := filterObj.(*PdfObjectArray)
if !ok {
return nil, fmt.Errorf("filter not a Name or Array object")
}
if len(*array) == 0 {
// Empty array -> indicates raw filter (no filter).
return NewRawEncoder(), nil
}
if len(*array) != 1 {
menc, err := newMultiEncoderFromStream(streamObj)
if err != nil {
common.Log.Error("Failed creating multi encoder: %v", err)
return nil, err
}
common.Log.Trace("Multi enc: %s\n", menc)
return menc, nil
}
// Single element.
filterObj = (*array)[0]
method, ok = filterObj.(*PdfObjectName)
if !ok {
return nil, fmt.Errorf("filter array member not a Name object")
}
}
switch *method {
case StreamEncodingFilterNameFlate:
return newFlateEncoderFromStream(streamObj, nil)
case StreamEncodingFilterNameLZW:
return newLZWEncoderFromStream(streamObj, nil)
case StreamEncodingFilterNameDCT:
return newDCTEncoderFromStream(streamObj, nil)
case StreamEncodingFilterNameRunLength:
return newRunLengthEncoderFromStream()
case StreamEncodingFilterNameASCIIHex:
return NewASCIIHexEncoder(), nil
case StreamEncodingFilterNameASCII85, "A85":
return NewASCII85Encoder(), nil
case StreamEncodingFilterNameCCITTFax:
return NewCCITTFaxEncoder(), nil
case StreamEncodingFilterNameJBIG2:
return NewJBIG2Encoder(), nil
case StreamEncodingFilterNameJPX:
return NewJPXEncoder(), nil
default:
common.Log.Debug("error: Unsupported encoding method!")
return nil, fmt.Errorf("unsupported encoding method (%s)", *method)
}
}
// DecodeStream decodes the stream data and returns the decoded data.
// An error is returned upon failure.
func DecodeStream(streamObj *PdfObjectStream) ([]byte, error) {
common.Log.Trace("Decode stream")
encoder, err := NewEncoderFromStream(streamObj)
if err != nil {
common.Log.Debug("Stream decoding failed: %v", err)
return nil, err
}
common.Log.Trace("Encoder: %#v\n", encoder)
decoded, err := encoder.DecodeStream(streamObj)
if err != nil {
common.Log.Debug("Stream decoding failed: %v", err)
return nil, err
}
return decoded, nil
}
// EncodeStream encodes the stream data using the encoded specified by the stream's dictionary.
func EncodeStream(streamObj *PdfObjectStream) error {
common.Log.Trace("Encode stream")
encoder, err := NewEncoderFromStream(streamObj)
if err != nil {
common.Log.Debug("Stream decoding failed: %v", err)
return err
}
if lzwenc, is := encoder.(*LZWEncoder); is {
// If LZW:
// Make sure to use EarlyChange 0.. We do not have write support for 1 yet.
lzwenc.EarlyChange = 0
streamObj.PdfObjectDictionary.Set("EarlyChange", MakeInteger(0))
}
common.Log.Trace("Encoder: %+v\n", encoder)
encoded, err := encoder.EncodeBytes(streamObj.Stream)
if err != nil {
common.Log.Debug("Stream encoding failed: %v", err)
return err
}
streamObj.Stream = encoded
// Update length
streamObj.PdfObjectDictionary.Set("Length", MakeInteger(int64(len(encoded))))
return nil
}

View File

@@ -0,0 +1,74 @@
package core
// IsWhiteSpace checks if byte represents a white space character.
// TODO (v3): Unexport.
func IsWhiteSpace(ch byte) bool {
// Table 1 white-space characters (7.2.2 Character Set)
// spaceCharacters := string([]byte{0x00, 0x09, 0x0A, 0x0C, 0x0D, 0x20})
if (ch == 0x00) || (ch == 0x09) || (ch == 0x0A) || (ch == 0x0C) || (ch == 0x0D) || (ch == 0x20) {
return true
}
return false
}
// IsFloatDigit checks if a character can be a part of a float number string.
// TODO (v3): Unexport.
func IsFloatDigit(c byte) bool {
return ('0' <= c && c <= '9') || c == '.'
}
// IsDecimalDigit checks if the character is a part of a decimal number string.
// TODO (v3): Unexport.
func IsDecimalDigit(c byte) bool {
if c >= '0' && c <= '9' {
return true
} else {
return false
}
}
// IsOctalDigit checks if a character can be part of an octal digit string.
// TODO (v3): Unexport.
func IsOctalDigit(c byte) bool {
if c >= '0' && c <= '7' {
return true
} else {
return false
}
}
// IsPrintable checks if a character is printable.
// Regular characters that are outside the range EXCLAMATION MARK(21h)
// (!) to TILDE (7Eh) (~) should be written using the hexadecimal notation.
// TODO (v3): Unexport.
func IsPrintable(char byte) bool {
if char < 0x21 || char > 0x7E {
return false
}
return true
}
// IsDelimiter checks if a character represents a delimiter.
// TODO (v3): Unexport.
func IsDelimiter(char byte) bool {
if char == '(' || char == ')' {
return true
}
if char == '<' || char == '>' {
return true
}
if char == '[' || char == ']' {
return true
}
if char == '{' || char == '}' {
return true
}
if char == '/' {
return true
}
if char == '%' {
return true
}
return false
}

172
internal/pdf/core/utils.go Normal file
View File

@@ -0,0 +1,172 @@
package core
import (
"errors"
"fmt"
"sort"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
)
// Check slice range to make sure within bounds for accessing:
//
// slice[a:b] where sliceLen=len(slice).
func checkBounds(sliceLen, a, b int) error {
if a < 0 || a > sliceLen {
return errors.New("slice index a out of bounds")
}
if b < a {
return errors.New("invalid slice index b < a")
}
if b > sliceLen {
return errors.New("slice index b out of bounds")
}
return nil
}
// Inspect analyzes the document object structure.
func (parser *PdfParser) Inspect() (map[string]int, error) {
return parser.inspect()
}
// GetObjectNums returns a sorted list of object numbers of the PDF objects in the file.
func (parser *PdfParser) GetObjectNums() []int {
objNums := []int{}
for _, x := range parser.xrefs {
objNums = append(objNums, x.objectNumber)
}
// Sort the object numbers to give consistent ordering of PDF objects in output.
// Needed since parser.xrefs is a map.
sort.Ints(objNums)
return objNums
}
/*
* Inspect object types.
* Go through all objects in the cross ref table and detect the types.
* Mostly for debugging purposes and inspecting odd PDF files.
*/
func (parser *PdfParser) inspect() (map[string]int, error) {
common.Log.Trace("--------INSPECT ----------")
common.Log.Trace("Xref table:")
objTypes := map[string]int{}
objCount := 0
failedCount := 0
keys := []int{}
for k := range parser.xrefs {
keys = append(keys, k)
}
sort.Ints(keys)
i := 0
for _, k := range keys {
xref := parser.xrefs[k]
if xref.objectNumber == 0 {
continue
}
objCount++
common.Log.Trace("==========")
common.Log.Trace("Looking up object number: %d", xref.objectNumber)
o, err := parser.LookupByNumber(xref.objectNumber)
if err != nil {
common.Log.Trace("error: Fail to lookup obj %d (%s)", xref.objectNumber, err)
failedCount++
continue
}
common.Log.Trace("obj: %s", o)
iobj, isIndirect := o.(*PdfIndirectObject)
if isIndirect {
common.Log.Trace("IND OOBJ %d: %s", xref.objectNumber, iobj)
dict, isDict := iobj.PdfObject.(*PdfObjectDictionary)
if isDict {
// Check if has Type parameter.
if ot, has := dict.Get("Type").(*PdfObjectName); has {
otype := string(*ot)
common.Log.Trace("---> Obj type: %s", otype)
_, isDefined := objTypes[otype]
if isDefined {
objTypes[otype]++
} else {
objTypes[otype] = 1
}
} else if ot, has := dict.Get("Subtype").(*PdfObjectName); has {
// Check if subtype
otype := string(*ot)
common.Log.Trace("---> Obj subtype: %s", otype)
_, isDefined := objTypes[otype]
if isDefined {
objTypes[otype]++
} else {
objTypes[otype] = 1
}
}
if val, has := dict.Get("S").(*PdfObjectName); has && *val == "JavaScript" {
// Check if Javascript.
_, isDefined := objTypes["JavaScript"]
if isDefined {
objTypes["JavaScript"]++
} else {
objTypes["JavaScript"] = 1
}
}
}
} else if sobj, isStream := o.(*PdfObjectStream); isStream {
if otype, ok := sobj.PdfObjectDictionary.Get("Type").(*PdfObjectName); ok {
common.Log.Trace("--> Stream object type: %s", *otype)
k := string(*otype)
objTypes[k]++
}
} else { // Direct.
dict, isDict := o.(*PdfObjectDictionary)
if isDict {
ot, isName := dict.Get("Type").(*PdfObjectName)
if isName {
otype := string(*ot)
common.Log.Trace("--- obj type %s", otype)
objTypes[otype]++
}
}
common.Log.Trace("DIRECT OBJ %d: %s", xref.objectNumber, o)
}
i++
}
common.Log.Trace("--------EOF INSPECT ----------")
common.Log.Trace("=======")
common.Log.Trace("Object count: %d", objCount)
common.Log.Trace("Failed lookup: %d", failedCount)
for t, c := range objTypes {
common.Log.Trace("%s: %d", t, c)
}
common.Log.Trace("=======")
if len(parser.xrefs) < 1 {
common.Log.Debug("error: This document is invalid (xref table missing!)")
return nil, fmt.Errorf("invalid document (xref table missing)")
}
fontObjs, ok := objTypes["Font"]
if !ok || fontObjs < 2 {
common.Log.Trace("This document is probably scanned!")
} else {
common.Log.Trace("This document is valid for extraction!")
}
return objTypes, nil
}
func absInt(x int) int {
if x < 0 {
return -x
} else {
return x
}
}

View File

@@ -0,0 +1,543 @@
package creator
import (
"errors"
"fmt"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/contentstream"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model"
)
// Block contains a portion of PDF Page contents. It has a width and a position and can
// be placed anywhere on a Page. It can even contain a whole Page, and is used in the creator
// where each Drawable object can output one or more blocks, each representing content for separate pages
// (typically needed when Page breaks occur).
type Block struct {
// Block contents and resources.
contents *contentstream.ContentStreamOperations
resources *model.PdfPageResources
// Positioning: relative / absolute.
positioning positioning
// Absolute coordinates (when in absolute mode).
xPos, yPos float64
// The bounding box for the block.
width float64
height float64
// Rotation angle.
angle float64
// Margins to be applied around the block when drawing on Page.
margins margins
}
// NewBlock creates a new Block with specified width and height.
func NewBlock(width float64, height float64) *Block {
b := &Block{}
b.contents = &contentstream.ContentStreamOperations{}
b.resources = model.NewPdfPageResources()
b.width = width
b.height = height
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.
func NewBlockFromPage(page *model.PdfPage) (*Block, error) {
b := &Block{}
content, err := page.GetAllContentStreams()
if err != nil {
return nil, err
}
contentParser := contentstream.NewContentStreamParser(content)
operations, err := contentParser.Parse()
if err != nil {
return nil, err
}
operations.WrapIfNeeded()
b.contents = operations
if page.Resources != nil {
b.resources = page.Resources
} else {
b.resources = model.NewPdfPageResources()
}
mbox, err := page.GetMediaBox()
if err != nil {
return nil, err
}
if mbox.Llx != 0 || mbox.Lly != 0 {
// Account for media box offset if any.
b.translate(-mbox.Llx, mbox.Lly)
}
b.width = mbox.Urx - mbox.Llx
b.height = mbox.Ury - mbox.Lly
return b, nil
}
// SetAngle sets the rotation angle in degrees.
func (blk *Block) SetAngle(angleDeg float64) {
blk.angle = angleDeg
}
// duplicate duplicates the block with a new copy of the operations list.
func (blk *Block) duplicate() *Block {
dup := &Block{}
// Copy over.
*dup = *blk
dupContents := contentstream.ContentStreamOperations{}
dupContents = append(dupContents, *blk.contents...)
dup.contents = &dupContents
return dup
}
// GeneratePageBlocks draws the block contents on a template Page block.
// Implements the Drawable interface.
func (blk *Block) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, error) {
blocks := []*Block{}
if blk.positioning.isRelative() {
// Draw at current ctx.X, ctx.Y position
dup := blk.duplicate()
cc := contentstream.NewContentCreator()
cc.Translate(ctx.X, ctx.PageHeight-ctx.Y-blk.height)
if blk.angle != 0 {
// Make the rotation about the upper left corner.
// XXX/TODO: Account for rotation origin. (Consider).
cc.Translate(0, blk.Height())
cc.RotateDeg(blk.angle)
cc.Translate(0, -blk.Height())
}
contents := append(*cc.Operations(), *dup.contents...)
contents.WrapIfNeeded()
dup.contents = &contents
blocks = append(blocks, dup)
ctx.Y += blk.height
} else {
// Absolute. Draw at blk.xPos, blk.yPos position
dup := blk.duplicate()
cc := contentstream.NewContentCreator()
cc.Translate(blk.xPos, ctx.PageHeight-blk.yPos-blk.height)
if blk.angle != 0 {
// Make the rotation about the upper left corner.
// XXX/TODO: Consider supporting specification of rotation origin.
cc.Translate(0, blk.Height())
cc.RotateDeg(blk.angle)
cc.Translate(0, -blk.Height())
}
contents := append(*cc.Operations(), *dup.contents...)
contents.WrapIfNeeded()
dup.contents = &contents
blocks = append(blocks, dup)
}
return blocks, ctx, nil
}
// Height returns the Block's height.
func (blk *Block) Height() float64 {
return blk.height
}
// Width returns the Block's width.
func (blk *Block) Width() float64 {
return blk.width
}
// addContents adds contents to a block. Wrap both existing and new contents to ensure
// independence of content operations.
func (blk *Block) addContents(operations *contentstream.ContentStreamOperations) {
blk.contents.WrapIfNeeded()
operations.WrapIfNeeded()
*blk.contents = append(*blk.contents, *operations...)
}
// addContentsByString adds contents to a block by contents string.
func (blk *Block) addContentsByString(contents string) error {
cc := contentstream.NewContentStreamParser(contents)
operations, err := cc.Parse()
if err != nil {
return err
}
blk.contents.WrapIfNeeded()
operations.WrapIfNeeded()
*blk.contents = append(*blk.contents, *operations...)
return nil
}
// SetMargins sets the Block's left, right, top, bottom, margins.
func (blk *Block) SetMargins(left, right, top, bottom float64) {
blk.margins.left = left
blk.margins.right = right
blk.margins.top = top
blk.margins.bottom = bottom
}
// GetMargins returns the Block's margins: left, right, top, bottom.
func (blk *Block) GetMargins() (float64, float64, float64, float64) {
return blk.margins.left, blk.margins.right, blk.margins.top, blk.margins.bottom
}
// SetPos sets the Block's positioning to absolute mode with the specified coordinates.
func (blk *Block) SetPos(x, y float64) {
blk.positioning = positionAbsolute
blk.xPos = x
blk.yPos = y
}
// Scale block by specified factors in the x and y directions.
func (blk *Block) Scale(sx, sy float64) {
ops := contentstream.NewContentCreator().
Scale(sx, sy).
Operations()
*blk.contents = append(*ops, *blk.contents...)
blk.contents.WrapIfNeeded()
blk.width *= sx
blk.height *= sy
}
// ScaleToWidth scales the Block to a specified width, maintaining the same aspect ratio.
func (blk *Block) ScaleToWidth(w float64) {
ratio := w / blk.width
blk.Scale(ratio, ratio)
}
// ScaleToHeight scales the Block to a specified height, maintaining the same aspect ratio.
func (blk *Block) ScaleToHeight(h float64) {
ratio := h / blk.height
blk.Scale(ratio, ratio)
}
// translate translates the block, moving block contents on the PDF. For internal use.
func (blk *Block) translate(tx, ty float64) {
ops := contentstream.NewContentCreator().
Translate(tx, -ty).
Operations()
*blk.contents = append(*ops, *blk.contents...)
blk.contents.WrapIfNeeded()
}
// drawToPage draws the block on a PdfPage. Generates the content streams and appends to the PdfPage's content
// stream and links needed resources.
func (blk *Block) drawToPage(page *model.PdfPage) error {
// Check if Page contents are wrapped - if not wrap it.
content, err := page.GetAllContentStreams()
if err != nil {
return err
}
contentParser := contentstream.NewContentStreamParser(content)
ops, err := contentParser.Parse()
if err != nil {
return err
}
ops.WrapIfNeeded()
// Ensure resource dictionaries are available.
if page.Resources == nil {
page.Resources = model.NewPdfPageResources()
}
// Merge the contents into ops.
err = mergeContents(ops, page.Resources, blk.contents, blk.resources)
if err != nil {
return err
}
err = page.SetContentStreams([]string{string(ops.Bytes())}, core.NewFlateEncoder())
if err != nil {
return err
}
return nil
}
// Draw draws the drawable d on the block.
// Note that the drawable must not wrap, i.e. only return one block. Otherwise an error is returned.
func (blk *Block) Draw(d Drawable) error {
ctx := DrawContext{}
ctx.Width = blk.width
ctx.Height = blk.height
ctx.PageWidth = blk.width
ctx.PageHeight = blk.height
ctx.X = 0 // Upper left corner of block
ctx.Y = 0
blocks, _, err := d.GeneratePageBlocks(ctx)
if err != nil {
return err
}
if len(blocks) != 1 {
return errors.New("too many output blocks")
}
for _, newBlock := range blocks {
err := mergeContents(blk.contents, blk.resources, newBlock.contents, newBlock.resources)
if err != nil {
return err
}
}
return nil
}
// DrawWithContext draws the Block using the specified drawing context.
func (blk *Block) DrawWithContext(d Drawable, ctx DrawContext) error {
blocks, _, err := d.GeneratePageBlocks(ctx)
if err != nil {
return err
}
if len(blocks) != 1 {
return errors.New("too many output blocks")
}
for _, newBlock := range blocks {
err := mergeContents(blk.contents, blk.resources, newBlock.contents, newBlock.resources)
if err != nil {
return err
}
}
return nil
}
// mergeBlocks appends another block onto the block.
func (blk *Block) mergeBlocks(toAdd *Block) error {
err := mergeContents(blk.contents, blk.resources, toAdd.contents, toAdd.resources)
return err
}
// mergeContents merges contents and content streams.
// Active in the sense that it modified the input contents and resources.
func mergeContents(contents *contentstream.ContentStreamOperations, resources *model.PdfPageResources,
contentsToAdd *contentstream.ContentStreamOperations, resourcesToAdd *model.PdfPageResources) error {
// To properly add contents from a block, we need to handle the resources that the block is
// using and make sure it is accessible in the modified Page.
//
// Currently supporting: Font, XObject, Colormap, Pattern, Shading, GState resources
// from the block.
//
xobjectMap := map[core.PdfObjectName]core.PdfObjectName{}
fontMap := map[core.PdfObjectName]core.PdfObjectName{}
csMap := map[core.PdfObjectName]core.PdfObjectName{}
patternMap := map[core.PdfObjectName]core.PdfObjectName{}
shadingMap := map[core.PdfObjectName]core.PdfObjectName{}
gstateMap := map[core.PdfObjectName]core.PdfObjectName{}
for _, op := range *contentsToAdd {
switch op.Operand {
case "Do":
// XObject.
if len(op.Params) == 1 {
if name, ok := op.Params[0].(*core.PdfObjectName); ok {
if _, processed := xobjectMap[*name]; !processed {
var useName core.PdfObjectName
// Process if not already processed..
obj, _ := resourcesToAdd.GetXObjectByName(*name)
if obj != nil {
useName = *name
for {
obj2, _ := resources.GetXObjectByName(useName)
if obj2 == nil || obj2 == obj {
break
}
// If there is a conflict... then append "0" to the name..
useName = useName + "0"
}
}
resources.SetXObjectByName(useName, obj)
xobjectMap[*name] = useName
}
useName := xobjectMap[*name]
op.Params[0] = &useName
}
}
case "Tf":
// Font.
if len(op.Params) == 2 {
if name, ok := op.Params[0].(*core.PdfObjectName); ok {
if _, processed := fontMap[*name]; !processed {
var useName core.PdfObjectName
// Process if not already processed.
obj, found := resourcesToAdd.GetFontByName(*name)
if found {
useName = *name
for {
obj2, found := resources.GetFontByName(useName)
if !found || obj2 == obj {
break
}
useName = useName + "0"
}
}
resources.SetFontByName(useName, obj)
fontMap[*name] = useName
}
useName := fontMap[*name]
op.Params[0] = &useName
}
}
case "CS", "cs":
// Colorspace.
if len(op.Params) == 1 {
if name, ok := op.Params[0].(*core.PdfObjectName); ok {
if _, processed := csMap[*name]; !processed {
var useName core.PdfObjectName
// Process if not already processed.
cs, found := resourcesToAdd.GetColorspaceByName(*name)
if found {
useName = *name
for {
cs2, found := resources.GetColorspaceByName(useName)
if !found || cs == cs2 {
break
}
useName = useName + "0"
}
resources.SetColorspaceByName(useName, cs)
csMap[*name] = useName
} else {
common.Log.Debug("Colorspace not found")
}
}
if useName, has := csMap[*name]; has {
op.Params[0] = &useName
} else {
common.Log.Debug("error: Colorspace %s not found", *name)
}
}
}
case "SCN", "scn":
if len(op.Params) == 1 {
if name, ok := op.Params[0].(*core.PdfObjectName); ok {
if _, processed := patternMap[*name]; !processed {
var useName core.PdfObjectName
p, found := resourcesToAdd.GetPatternByName(*name)
if found {
useName = *name
for {
p2, found := resources.GetPatternByName(useName)
if !found || p2 == p {
break
}
useName = useName + "0"
}
err := resources.SetPatternByName(useName, p.ToPdfObject())
if err != nil {
return err
}
patternMap[*name] = useName
}
}
if useName, has := patternMap[*name]; has {
op.Params[0] = &useName
}
}
}
case "sh":
// Shading.
if len(op.Params) == 1 {
if name, ok := op.Params[0].(*core.PdfObjectName); ok {
if _, processed := shadingMap[*name]; !processed {
var useName core.PdfObjectName
// Process if not already processed.
sh, found := resourcesToAdd.GetShadingByName(*name)
if found {
useName = *name
for {
sh2, found := resources.GetShadingByName(useName)
if !found || sh == sh2 {
break
}
useName = useName + "0"
}
err := resources.SetShadingByName(useName, sh.ToPdfObject())
if err != nil {
common.Log.Debug("error Set shading: %v", err)
return err
}
shadingMap[*name] = useName
} else {
common.Log.Debug("Shading not found")
}
}
if useName, has := shadingMap[*name]; has {
op.Params[0] = &useName
} else {
common.Log.Debug("error: Shading %s not found", *name)
}
}
}
case "gs":
// ExtGState.
if len(op.Params) == 1 {
if name, ok := op.Params[0].(*core.PdfObjectName); ok {
if _, processed := gstateMap[*name]; !processed {
var useName core.PdfObjectName
// Process if not already processed.
gs, found := resourcesToAdd.GetExtGState(*name)
if found {
useName = *name
i := 1
for {
gs2, found := resources.GetExtGState(useName)
if !found || gs == gs2 {
break
}
useName = core.PdfObjectName(fmt.Sprintf("GS%d", i))
i++
}
}
resources.AddExtGState(useName, gs)
gstateMap[*name] = useName
}
useName := gstateMap[*name]
op.Params[0] = &useName
}
}
}
*contents = append(*contents, op)
}
return nil
}

View File

@@ -0,0 +1,174 @@
package creator
import (
"errors"
"fmt"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model/fonts"
)
// 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
heading *Paragraph
subchapters int
contents []Drawable
// Show chapter numbering
showNumbering bool
// Include in TOC.
includeInTOC bool
// Positioning: relative / absolute.
positioning positioning
// Margins to be applied around the block when drawing on Page.
margins margins
// Reference to the creator's TOC.
toc *TableOfContents
}
// NewChapter creates a new chapter with the specified title as the heading.
func (c *Creator) NewChapter(title string) *Chapter {
chap := &Chapter{}
c.chapters++
chap.number = c.chapters
chap.title = title
chap.showNumbering = true
chap.includeInTOC = true
heading := fmt.Sprintf("%d. %s", c.chapters, title)
p := NewParagraph(heading)
p.SetFontSize(16)
p.SetFont(fonts.NewFontHelvetica()) // bold?
chap.heading = p
chap.contents = []Drawable{}
// Keep a reference for toc.
chap.toc = c.toc
return chap
}
// SetShowNumbering sets a flag to indicate whether or not to show chapter numbers as part of title.
func (chap *Chapter) SetShowNumbering(show bool) {
if show {
heading := fmt.Sprintf("%d. %s", chap.number, chap.title)
chap.heading.SetText(heading)
} else {
heading := chap.title
chap.heading.SetText(heading)
}
chap.showNumbering = show
}
// SetIncludeInTOC sets a flag to indicate whether or not to include in tOC.
func (chap *Chapter) SetIncludeInTOC(includeInTOC bool) {
chap.includeInTOC = includeInTOC
}
// GetHeading returns the chapter heading paragraph. Used to give access to address style: font, sizing etc.
func (chap *Chapter) GetHeading() *Paragraph {
return chap.heading
}
// SetMargins sets the Chapter margins: left, right, top, bottom.
// Typically not needed as the creator's page margins are used.
func (chap *Chapter) SetMargins(left, right, top, bottom float64) {
chap.margins.left = left
chap.margins.right = right
chap.margins.top = top
chap.margins.bottom = bottom
}
// GetMargins returns the Chapter's margin: left, right, top, bottom.
func (chap *Chapter) GetMargins() (float64, float64, float64, float64) {
return chap.margins.left, chap.margins.right, chap.margins.top, chap.margins.bottom
}
// Add adds a new Drawable to the chapter.
func (chap *Chapter) Add(d Drawable) error {
if Drawable(chap) == d {
common.Log.Debug("error: Cannot add itself")
return errors.New("range check error")
}
switch d.(type) {
case *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)
default:
common.Log.Debug("Unsupported: %T", d)
return errors.New("type check error")
}
return nil
}
// 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
if chap.positioning.isRelative() {
// Update context.
ctx.X += chap.margins.left
ctx.Y += chap.margins.top
ctx.Width -= chap.margins.left + chap.margins.right
ctx.Height -= chap.margins.top
}
blocks, ctx, err := chap.heading.GeneratePageBlocks(ctx)
if err != nil {
return blocks, ctx, err
}
if len(blocks) > 1 {
ctx.Page++ // Did not fit, moved to new Page block.
}
if chap.includeInTOC {
// Add to TOC.
chap.toc.add(chap.title, chap.number, 0, ctx.Page)
}
for _, d := range chap.contents {
newBlocks, c, err := d.GeneratePageBlocks(ctx)
if err != nil {
return blocks, ctx, err
}
if len(newBlocks) < 1 {
continue
}
// The first block is always appended to the last..
blocks[len(blocks)-1].mergeBlocks(newBlocks[0])
blocks = append(blocks, newBlocks[1:]...)
ctx = c
}
if chap.positioning.isRelative() {
// Move back X to same start of line.
ctx.X = origCtx.X
}
if chap.positioning.isAbsolute() {
// If absolute: return original context.
return blocks, origCtx, nil
}
return blocks, ctx, nil
}

View File

@@ -0,0 +1,115 @@
package creator
import (
"fmt"
"math"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
)
// Color interface represents colors in the PDF creator.
type Color interface {
ToRGB() (float64, float64, float64)
}
// Represents RGB color values.
type rgbColor struct {
// Arithmetic representation of r,g,b (range 0-1).
r, g, b float64
}
func (col rgbColor) ToRGB() (float64, float64, float64) {
return col.r, col.g, col.b
}
// Commonly used colors.
var (
ColorBlack = ColorRGBFromArithmetic(0, 0, 0)
ColorWhite = ColorRGBFromArithmetic(1, 1, 1)
ColorRed = ColorRGBFromArithmetic(1, 0, 0)
ColorGreen = ColorRGBFromArithmetic(0, 1, 0)
ColorBlue = ColorRGBFromArithmetic(0, 0, 1)
ColorYellow = ColorRGBFromArithmetic(1, 1, 0)
)
// ColorRGBFromHex converts color hex code to rgb color for using with creator.
// NOTE: If there is a problem interpreting the string, then will use black color and log a debug message.
// Example hex code: #ffffff -> (1,1,1) white.
func ColorRGBFromHex(hexStr string) Color {
color := rgbColor{}
if (len(hexStr) != 4 && len(hexStr) != 7) || hexStr[0] != '#' {
common.Log.Debug("invalid hex code: %s", hexStr)
return color
}
var r, g, b int
if len(hexStr) == 4 {
// Special case: 4 digits: #abc ; where r = a*16+a, e.g. #ffffff -> #fff
var tmp1, tmp2, tmp3 int
n, err := fmt.Sscanf(hexStr, "#%1x%1x%1x", &tmp1, &tmp2, &tmp3)
if err != nil {
common.Log.Debug("invalid hex code: %s, error: %v", hexStr, err)
return color
}
if n != 3 {
common.Log.Debug("invalid hex code: %s", hexStr)
return color
}
r = tmp1*16 + tmp1
g = tmp2*16 + tmp2
b = tmp3*16 + tmp3
} else {
// Default case: 7 digits: #rrggbb
n, err := fmt.Sscanf(hexStr, "#%2x%2x%2x", &r, &g, &b)
if err != nil {
common.Log.Debug("invalid hex code: %s", hexStr)
return color
}
if n != 3 {
common.Log.Debug("invalid hex code: %s, n != 3 (%d)", hexStr, n)
return color
}
}
rNorm := float64(r) / 255.0
gNorm := float64(g) / 255.0
bNorm := float64(b) / 255.0
color.r = rNorm
color.g = gNorm
color.b = bNorm
return color
}
// ColorRGBFrom8bit creates a Color from 8bit (0-255) r,g,b values.
// Example:
//
// red := ColorRGBFrom8Bit(255, 0, 0)
func ColorRGBFrom8bit(r, g, b byte) Color {
color := rgbColor{}
color.r = float64(r) / 255.0
color.g = float64(g) / 255.0
color.b = float64(b) / 255.0
return color
}
// ColorRGBFromArithmetic creates a Color from arithmetic (0-1.0) color values.
// Example:
//
// green := ColorRGBFromArithmetic(0, 1.0, 0)
func ColorRGBFromArithmetic(r, g, b float64) Color {
// Ensure is in the range 0-1:
r = math.Max(math.Min(r, 1.0), 0.0)
g = math.Max(math.Min(g, 1.0), 0.0)
b = math.Max(math.Min(b, 1.0), 0.0)
color := rgbColor{}
color.r = r
color.g = g
color.b = b
return color
}

View File

@@ -0,0 +1,49 @@
package creator
// PageSize represents the page size as a 2 element array representing the width and height in PDF document units (points).
type PageSize [2]float64
// PPI specifies the default PDF resolution in points/inch.
var PPI float64 = 72 // Points per inch. (Default resolution).
// PPMM specifies the default PDF resolution in points/mm.
var PPMM float64 = 72 * 1.0 / 25.4 // Points per mm. (Default resolution).
// Commonly used page sizes
var (
PageSizeA3 = PageSize{297 * PPMM, 420 * PPMM}
PageSizeA4 = PageSize{210 * PPMM, 297 * PPMM}
PageSizeA5 = PageSize{148 * PPMM, 210 * PPMM}
PageSizeLetter = PageSize{8.5 * PPI, 11 * PPI}
PageSizeLegal = PageSize{8.5 * PPI, 14 * PPI}
)
// TextAlignment options for paragraph.
type TextAlignment int
// The options supported for text alignment are:
// left - TextAlignmentLeft
// right - TextAlignmentRight
// center - TextAlignmentCenter
// justify - TextAlignmentJustify
const (
TextAlignmentLeft TextAlignment = iota
TextAlignmentRight
TextAlignmentCenter
TextAlignmentJustify
)
// Relative and absolute positioning types.
type positioning int
const (
positionRelative positioning = iota
positionAbsolute
)
func (p positioning) isRelative() bool {
return p == positionRelative
}
func (p positioning) isAbsolute() bool {
return p == positionAbsolute
}

View File

@@ -0,0 +1,524 @@
package creator
import (
"errors"
"io"
"os"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model"
)
// Creator is a wrapper around functionality for creating PDF reports and/or adding new
// content onto imported PDF pages, etc.
type Creator struct {
pages []*model.PdfPage
activePage *model.PdfPage
pagesize PageSize
context DrawContext
pageMargins margins
pageWidth, pageHeight float64
// Keep track of number of chapters for indexing.
chapters int
// Hooks.
genFrontPageFunc func(args FrontpageFunctionArgs)
genTableOfContentFunc func(toc *TableOfContents) (*Chapter, error)
drawHeaderFunc func(header *Block, args HeaderFunctionArgs)
drawFooterFunc func(footer *Block, args FooterFunctionArgs)
pdfWriterAccessFunc func(writer *model.PdfWriter) error
finalized bool
toc *TableOfContents
// Forms.
acroForm *model.PdfAcroForm
}
// SetForms Add Acroforms 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.
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.
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.
type FooterFunctionArgs struct {
PageNum int
TotalPages int
}
// Margins. Can be page margins, or margins around an element.
type margins struct {
left float64
right float64
top float64
bottom float64
}
// New creates a new instance of the PDF Creator.
func New() *Creator {
c := &Creator{}
c.pages = []*model.PdfPage{}
c.SetPageSize(PageSizeLetter)
m := 0.1 * c.pageWidth
c.pageMargins.left = m
c.pageMargins.right = m
c.pageMargins.top = m
c.pageMargins.bottom = m
c.toc = newTableOfContents()
return c
}
// SetPageMargins sets the page margins: left, right, top, bottom.
// The default page margins are 10% of document width.
func (c *Creator) SetPageMargins(left, right, top, bottom float64) {
c.pageMargins.left = left
c.pageMargins.right = right
c.pageMargins.top = top
c.pageMargins.bottom = bottom
}
// Width returns the current page width.
func (c *Creator) Width() float64 {
return c.pageWidth
}
// Height returns the current page height.
func (c *Creator) Height() float64 {
return c.pageHeight
}
func (c *Creator) setActivePage(p *model.PdfPage) {
c.activePage = p
}
func (c *Creator) getActivePage() *model.PdfPage {
if c.activePage == nil {
if len(c.pages) == 0 {
return nil
}
return c.pages[len(c.pages)-1]
}
return c.activePage
}
// 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.
// Examples:
// 1. c.SetPageSize(creator.PageSizeA4)
// 2. c.SetPageSize(creator.PageSizeA3)
// 3. c.SetPageSize(creator.PageSizeLegal)
// 4. c.SetPageSize(creator.PageSizeLetter)
//
// For custom sizes: Use the PPMM (points per mm) and PPI (points per inch) when defining those based on
// physical page sizes:
//
// Examples:
// 1. 10x15 sq. mm: SetPageSize(PageSize{10*creator.PPMM, 15*creator.PPMM}) where PPMM is points per mm.
// 2. 3x2 sq. inches: SetPageSize(PageSize{3*creator.PPI, 2*creator.PPI}) where PPI is points per inch.
func (c *Creator) SetPageSize(size PageSize) {
c.pagesize = size
c.pageWidth = size[0]
c.pageHeight = size[1]
// Update default margins to 10% of width.
m := 0.1 * c.pageWidth
c.pageMargins.left = m
c.pageMargins.right = m
c.pageMargins.top = m
c.pageMargins.bottom = m
}
// DrawHeader sets a function to draw a header on created output pages.
func (c *Creator) DrawHeader(drawHeaderFunc func(header *Block, args HeaderFunctionArgs)) {
c.drawHeaderFunc = drawHeaderFunc
}
// DrawFooter sets a function to draw a footer on created output pages.
func (c *Creator) DrawFooter(drawFooterFunc func(footer *Block, args FooterFunctionArgs)) {
c.drawFooterFunc = drawFooterFunc
}
// CreateFrontPage sets a function to generate a front Page.
func (c *Creator) CreateFrontPage(genFrontPageFunc func(args FrontpageFunctionArgs)) {
c.genFrontPageFunc = genFrontPageFunc
}
// CreateTableOfContents sets a function to generate table of contents.
func (c *Creator) CreateTableOfContents(genTOCFunc func(toc *TableOfContents) (*Chapter, error)) {
c.genTableOfContentFunc = genTOCFunc
}
// Create a new Page with current parameters.
func (c *Creator) newPage() *model.PdfPage {
page := model.NewPdfPage()
width := c.pagesize[0]
height := c.pagesize[1]
bbox := model.PdfRectangle{Llx: 0, Lly: 0, Urx: width, Ury: height}
page.MediaBox = &bbox
c.pageWidth = width
c.pageHeight = height
c.initContext()
return page
}
// Initialize the drawing context, moving to upper left corner.
func (c *Creator) initContext() {
// Update context, move to upper left corner.
c.context.X = c.pageMargins.left
c.context.Y = c.pageMargins.top
c.context.Width = c.pageWidth - c.pageMargins.right - c.pageMargins.left
c.context.Height = c.pageHeight - c.pageMargins.bottom - c.pageMargins.top
c.context.PageHeight = c.pageHeight
c.context.PageWidth = c.pageWidth
c.context.Margins = c.pageMargins
}
// NewPage adds a new Page to the Creator and sets as the active Page.
func (c *Creator) NewPage() {
page := c.newPage()
c.pages = append(c.pages, page)
c.context.Page++
}
// AddPage adds the specified page to the creator.
func (c *Creator) AddPage(page *model.PdfPage) error {
mbox, err := page.GetMediaBox()
if err != nil {
common.Log.Debug("Failed to get page mediabox: %v", err)
return err
}
c.context.X = mbox.Llx + c.pageMargins.left
c.context.Y = c.pageMargins.top
c.context.PageHeight = mbox.Ury - mbox.Lly
c.context.PageWidth = mbox.Urx - mbox.Llx
c.pages = append(c.pages, page)
c.context.Page++
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.
func (c *Creator) RotateDeg(angleDeg int64) error {
page := c.getActivePage()
if page == nil {
common.Log.Debug("Fail to rotate: no page currently active")
return errors.New("no page active")
}
if angleDeg%90 != 0 {
common.Log.Debug("error: Page rotation angle not a multiple of 90")
return errors.New("range check error")
}
// Do the rotation.
var rotation int64 = 0
if page.Rotate != nil {
rotation = *(page.Rotate)
}
rotation += angleDeg // Rotate by angleDeg degrees.
page.Rotate = &rotation
return nil
}
// Context returns the current drawing context.
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.
func (c *Creator) finalize() error {
totPages := len(c.pages)
// Estimate number of additional generated pages and update TOC.
genpages := 0
if c.genFrontPageFunc != nil {
genpages++
}
if c.genTableOfContentFunc != nil {
c.initContext()
c.context.Page = genpages + 1
ch, err := c.genTableOfContentFunc(c.toc)
if err != nil {
return err
}
// Make an estimate of the number of pages.
blocks, _, err := ch.GeneratePageBlocks(c.context)
if err != nil {
common.Log.Debug("Failed to generate blocks: %v", err)
return err
}
genpages += len(blocks)
// Update the table of content Page numbers, accounting for front Page and TOC.
for idx := range c.toc.entries {
c.toc.entries[idx].PageNumber += genpages
}
// Remove the TOC chapter entry.
c.toc.entries = c.toc.entries[:len(c.toc.entries)-1]
}
hasFrontPage := false
// Generate the front Page.
if c.genFrontPageFunc != nil {
totPages++
p := c.newPage()
// Place at front.
c.pages = append([]*model.PdfPage{p}, c.pages...)
c.setActivePage(p)
args := FrontpageFunctionArgs{
PageNum: 1,
TotalPages: totPages,
}
c.genFrontPageFunc(args)
hasFrontPage = true
}
if c.genTableOfContentFunc != nil {
c.initContext()
ch, err := c.genTableOfContentFunc(c.toc)
if err != nil {
common.Log.Debug("error generating TOC: %v", err)
return err
}
ch.SetShowNumbering(false)
ch.SetIncludeInTOC(false)
blocks, _, _ := ch.GeneratePageBlocks(c.context)
tocpages := []*model.PdfPage{}
for _, block := range blocks {
block.SetPos(0, 0)
totPages++
p := c.newPage()
// Place at front.
tocpages = append(tocpages, p)
c.setActivePage(p)
c.Draw(block)
}
if hasFrontPage {
front := c.pages[0]
rest := c.pages[1:]
c.pages = append([]*model.PdfPage{front}, tocpages...)
c.pages = append(c.pages, rest...)
} else {
c.pages = append(tocpages, c.pages...)
}
}
for idx, page := range c.pages {
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.
headerBlock := NewBlock(c.pageWidth, c.pageMargins.top)
args := HeaderFunctionArgs{
PageNum: idx + 1,
TotalPages: totPages,
}
c.drawHeaderFunc(headerBlock, args)
headerBlock.SetPos(0, 0)
err := c.Draw(headerBlock)
if err != nil {
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.
footerBlock := NewBlock(c.pageWidth, c.pageMargins.bottom)
args := FooterFunctionArgs{
PageNum: idx + 1,
TotalPages: totPages,
}
c.drawFooterFunc(footerBlock, args)
footerBlock.SetPos(0, c.pageHeight-footerBlock.height)
err := c.Draw(footerBlock)
if err != nil {
common.Log.Debug("error drawing footer: %v", err)
return err
}
}
}
c.finalized = true
return nil
}
// MoveTo moves the drawing context to absolute coordinates (x, y).
func (c *Creator) MoveTo(x, y float64) {
c.context.X = x
c.context.Y = y
}
// MoveX moves the drawing context to absolute position x.
func (c *Creator) MoveX(x float64) {
c.context.X = x
}
// MoveY moves the drawing context to absolute position y.
func (c *Creator) MoveY(y float64) {
c.context.Y = y
}
// MoveRight moves the drawing context right by relative displacement dx (negative goes left).
func (c *Creator) MoveRight(dx float64) {
c.context.X += dx
}
// MoveDown moves the drawing context down by relative displacement dy (negative goes up).
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.
func (c *Creator) Draw(d Drawable) error {
if c.getActivePage() == nil {
// Add a new Page if none added already.
c.NewPage()
}
blocks, ctx, err := d.GeneratePageBlocks(c.context)
if err != nil {
return err
}
for idx, blk := range blocks {
if idx > 0 {
c.NewPage()
}
p := c.getActivePage()
err := blk.drawToPage(p)
if err != nil {
return err
}
}
// Inner elements can affect X, Y position and available height.
c.context.X = ctx.X
c.context.Y = ctx.Y
c.context.Height = ctx.PageHeight - ctx.Y - ctx.Margins.bottom
return nil
}
// Write output of creator to io.WriteSeeker interface.
func (c *Creator) Write(ws io.WriteSeeker) error {
if !c.finalized {
c.finalize()
}
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
}
}
// Pdf Writer access hook. Can be used to encrypt, etc. via the PdfWriter instance.
if c.pdfWriterAccessFunc != nil {
err := c.pdfWriterAccessFunc(&pdfWriter)
if err != nil {
common.Log.Debug("Failure: %v", err)
return err
}
}
for _, page := range c.pages {
err := pdfWriter.AddPage(page)
if err != nil {
common.Log.Error("Failed to add Page: %s", err)
return err
}
}
err := pdfWriter.Write(ws)
if err != nil {
return err
}
return nil
}
// SetPdfWriterAccessFunc sets a PdfWriter access function/hook.
// Exposes the PdfWriter just prior to writing the PDF. Can be used to encrypt the output PDF, etc.
//
// Example of encrypting with a user/owner password "password"
// Prior to calling c.WriteFile():
//
// c.SetPdfWriterAccessFunc(func(w *model.PdfWriter) error {
// userPass := []byte("password")
// ownerPass := []byte("password")
// err := w.Encrypt(userPass, ownerPass, nil)
// return err
// })
func (c *Creator) SetPdfWriterAccessFunc(pdfWriterAccessFunc func(writer *model.PdfWriter) error) {
c.pdfWriterAccessFunc = pdfWriterAccessFunc
}
// WriteToFile writes the Creator output to file specified by path.
func (c *Creator) WriteToFile(outputPath string) error {
fWrite, err := os.Create(outputPath)
if err != nil {
return err
}
defer fWrite.Close()
err = c.Write(fWrite)
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,66 @@
package creator
import (
"fmt"
"strings"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model"
)
// NewCurve returns new instance of Curve between points (x1,y1) and (x2, y2) with control point (cx,cy).
func NewCurve(x1, y1, cx, cy, x2, y2 float64) *Curve {
c := &Curve{}
c.x1 = x1
c.y1 = y1
c.cx = cx
c.cy = cy
c.x2 = x2
c.y2 = y2
c.lineColor = model.NewPdfColorDeviceRGB(0, 0, 0)
c.lineWidth = 1.0
return c
}
// Curve represents a cubic Bezier curve with a control point.
type Curve struct {
x1 float64
y1 float64
cx float64 // control point
cy float64
x2 float64
y2 float64
lineColor *model.PdfColorDeviceRGB
lineWidth float64
}
// SetWidth sets line width.
func (c *Curve) SetWidth(width float64) {
c.lineWidth = width
}
// SetColor sets the line color.
func (c *Curve) SetColor(col Color) {
c.lineColor = model.NewPdfColorDeviceRGB(col.ToRGB())
}
// GeneratePageBlocks draws the curve onto page blocks.
func (c *Curve) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, error) {
block := NewBlock(ctx.PageWidth, ctx.PageHeight)
var ops []string
ops = append(ops, fmt.Sprintf("%.2f w", c.lineWidth)) // line widtdh
ops = append(ops, fmt.Sprintf("%.3f %.3f %.3f RG", c.lineColor[0], c.lineColor[1], c.lineColor[2])) // line color
ops = append(ops, fmt.Sprintf("%.2f %.2f m", c.x1, ctx.PageHeight-c.y1)) // move to
ops = append(ops, fmt.Sprintf("%.5f %.5f %.5f %.5f v S", c.cx, ctx.PageHeight-c.cy, c.x2, ctx.PageHeight-c.y2))
err := block.addContentsByString(strings.Join(ops, "\n"))
if err != nil {
return nil, ctx, err
}
return []*Block{block}, ctx, nil
}

View File

@@ -0,0 +1,189 @@
package creator
import (
"errors"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
)
// Division is a container component which can wrap across multiple pages (unlike Block).
// It can contain multiple Drawable components (currently supporting Paragraph and Image).
//
// The component stacking behavior is vertical, where the Drawables are drawn on top of each other.
// Also supports horizontal stacking by activating the inline mode.
type Division struct {
components []VectorDrawable
// Positioning: relative / absolute.
positioning positioning
// Margins to be applied around the block when drawing on Page.
margins margins
// Controls whether the components are stacked horizontally
inline bool
}
// NewDivision returns a new Division container component.
func NewDivision() *Division {
return &Division{
components: []VectorDrawable{},
}
}
// Inline returns whether the inline mode of the division is active.
func (div *Division) Inline() bool {
return div.inline
}
// SetInline sets the inline mode of the division.
func (div *Division) SetInline(inline bool) {
div.inline = inline
}
// Add adds a VectorDrawable to the Division container.
// Currently supported VectorDrawables: *Paragraph, *StyledParagraph, *Image.
func (div *Division) Add(d VectorDrawable) error {
supported := false
switch d.(type) {
case *Paragraph:
supported = true
case *StyledParagraph:
supported = true
case *Image:
supported = true
}
if !supported {
return errors.New("unsupported type in Division")
}
div.components = append(div.components, d)
return nil
}
// Height returns the height for the Division component assuming all stacked on top of each other.
func (div *Division) Height() float64 {
y := 0.0
yMax := 0.0
for _, component := range div.components {
compWidth, compHeight := component.Width(), component.Height()
switch t := component.(type) {
case *Paragraph:
p := t
compWidth += p.margins.left + p.margins.right
compHeight += p.margins.top + p.margins.bottom
}
// Vertical stacking.
y += compHeight
yMax = y
}
return yMax
}
// Width is not used. Not used as a Division element is designed to fill into available width depending on
// context. Returns 0.
func (div *Division) Width() float64 {
return 0
}
// GeneratePageBlocks generates the page blocks for the Division component.
// Multiple blocks are generated if the contents wrap over multiple pages.
func (div *Division) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, error) {
pageblocks := []*Block{}
origCtx := ctx
if div.positioning.isRelative() {
// Update context.
ctx.X += div.margins.left
ctx.Y += div.margins.top
ctx.Width -= div.margins.left + div.margins.right
ctx.Height -= div.margins.top
}
// Set the inline mode of the division to the context.
ctx.Inline = div.inline
// Draw.
divCtx := ctx
tmpCtx := ctx
var lineHeight float64
for _, component := range div.components {
if ctx.Inline {
// Check whether the component fits on the current line.
if (ctx.X-divCtx.X)+component.Width() <= ctx.Width {
ctx.Y = tmpCtx.Y
ctx.Height = tmpCtx.Height
} else {
ctx.X = divCtx.X
ctx.Width = divCtx.Width
tmpCtx.Y += lineHeight
tmpCtx.Height -= lineHeight
lineHeight = 0
}
}
newblocks, updCtx, err := component.GeneratePageBlocks(ctx)
if err != nil {
common.Log.Debug("error generating page blocks: %v", err)
return nil, ctx, err
}
if len(newblocks) < 1 {
continue
}
if len(pageblocks) > 0 {
// If there are pageblocks already in place.
// merge the first block in with current Block and append the rest.
pageblocks[len(pageblocks)-1].mergeBlocks(newblocks[0])
pageblocks = append(pageblocks, newblocks[1:]...)
} else {
pageblocks = append(pageblocks, newblocks[0:]...)
}
// Apply padding/margins.
if ctx.Inline {
// Recalculate positions on page change.
if ctx.Page != updCtx.Page {
divCtx.Y = ctx.Margins.top
divCtx.Height = ctx.PageHeight - ctx.Margins.top
tmpCtx.Y = divCtx.Y
tmpCtx.Height = divCtx.Height
lineHeight = updCtx.Height - divCtx.Height
} else {
// Calculate current line max height.
if dl := ctx.Height - updCtx.Height; dl > lineHeight {
lineHeight = dl
}
}
} else {
updCtx.X = ctx.X
}
ctx = updCtx
}
// Restore the original inline mode of the context.
ctx.Inline = origCtx.Inline
if div.positioning.isRelative() {
// Move back X to same start of line.
ctx.X = origCtx.X
}
if div.positioning.isAbsolute() {
// If absolute: return original context.
return pageblocks, origCtx, nil
}
return pageblocks, ctx, nil
}

View File

@@ -0,0 +1,44 @@
package creator
// Drawable is a widget that can be used to draw with the Creator.
type Drawable interface {
// Draw onto blocks representing Page contents. As the content can wrap over many pages, multiple
// templates are returned, one per Page. The function also takes a draw context containing information
// where to draw (if relative positioning) and the available height to draw on accounting for Margins etc.
GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, error)
}
// VectorDrawable is a Drawable with a specified width and height.
type VectorDrawable interface {
Drawable
// Width returns the width of the Drawable.
Width() float64
// Height returns the height of the Drawable.
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.
type DrawContext struct {
// Current page number.
Page int
// Current position. In a relative positioning mode, a drawable will be placed at these coordinates.
X, Y float64
// Context dimensions. Available width and height (on current page).
Width, Height float64
// Page Margins.
Margins margins
// Absolute Page size, widths and height.
PageWidth float64
PageHeight float64
// Controls whether the components are stacked horizontally
Inline bool
}

View File

@@ -0,0 +1,89 @@
package creator
import (
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/contentstream/draw"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model"
)
// Ellipse defines an ellipse with a center at (xc,yc) and a specified width and height. The ellipse can have a colored
// fill and/or border with a specified width.
// Implements the Drawable interface and can be drawn on PDF using the Creator.
type Ellipse struct {
xc float64
yc float64
width float64
height float64
fillColor *model.PdfColorDeviceRGB
borderColor *model.PdfColorDeviceRGB
borderWidth float64
}
// NewEllipse creates a new ellipse centered at (xc,yc) with a width and height specified.
func NewEllipse(xc, yc, width, height float64) *Ellipse {
ell := &Ellipse{}
ell.xc = xc
ell.yc = yc
ell.width = width
ell.height = height
ell.borderColor = model.NewPdfColorDeviceRGB(0, 0, 0)
ell.borderWidth = 1.0
return ell
}
// GetCoords returns the coordinates of the Ellipse's center (xc,yc).
func (ell *Ellipse) GetCoords() (float64, float64) {
return ell.xc, ell.yc
}
// SetBorderWidth sets the border width.
func (ell *Ellipse) SetBorderWidth(bw float64) {
ell.borderWidth = bw
}
// SetBorderColor sets the border color.
func (ell *Ellipse) SetBorderColor(col Color) {
ell.borderColor = model.NewPdfColorDeviceRGB(col.ToRGB())
}
// SetFillColor sets the fill color.
func (ell *Ellipse) SetFillColor(col Color) {
ell.fillColor = model.NewPdfColorDeviceRGB(col.ToRGB())
}
// GeneratePageBlocks draws the rectangle on a new block representing the page.
func (ell *Ellipse) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, error) {
block := NewBlock(ctx.PageWidth, ctx.PageHeight)
drawell := draw.Circle{
X: ell.xc - ell.width/2,
Y: ctx.PageHeight - ell.yc - ell.height/2,
Width: ell.width,
Height: ell.height,
Opacity: 1.0,
BorderWidth: ell.borderWidth,
}
if ell.fillColor != nil {
drawell.FillEnabled = true
drawell.FillColor = ell.fillColor
}
if ell.borderColor != nil {
drawell.BorderEnabled = true
drawell.BorderColor = ell.borderColor
drawell.BorderWidth = ell.borderWidth
}
contents, _, err := drawell.Draw("")
if err != nil {
return nil, ctx, err
}
err = block.addContentsByString(string(contents))
if err != nil {
return nil, ctx, err
}
return []*Block{block}, ctx, nil
}

View File

@@ -0,0 +1,107 @@
package creator
import (
pdfcontent "gitea.tecamino.com/paadi/pdfmerge/internal/pdf/contentstream"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/contentstream/draw"
pdfcore "gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
pdf "gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model"
)
// FilledCurve represents a closed path of Bezier curves with a border and fill.
type FilledCurve struct {
curves []draw.CubicBezierCurve
FillEnabled bool // Show fill?
fillColor *pdf.PdfColorDeviceRGB
BorderEnabled bool // Show border?
BorderWidth float64
borderColor *pdf.PdfColorDeviceRGB
}
// NewFilledCurve returns a instance of filled curve.
func NewFilledCurve() *FilledCurve {
curve := FilledCurve{}
curve.curves = []draw.CubicBezierCurve{}
return &curve
}
// AppendCurve appends a Bezier curve to the filled curve.
func (fc *FilledCurve) AppendCurve(curve draw.CubicBezierCurve) *FilledCurve {
fc.curves = append(fc.curves, curve)
return fc
}
// SetFillColor sets the fill color for the path.
func (fc *FilledCurve) SetFillColor(color Color) {
fc.fillColor = pdf.NewPdfColorDeviceRGB(color.ToRGB())
}
// SetBorderColor sets the border color for the path.
func (fc *FilledCurve) SetBorderColor(color Color) {
fc.borderColor = pdf.NewPdfColorDeviceRGB(color.ToRGB())
}
// draw draws the filled curve. Can specify a graphics state (gsName) for setting opacity etc. Otherwise leave empty ("").
// Returns the content stream as a byte array, the bounding box and an error on failure.
func (fc *FilledCurve) draw(gsName string) ([]byte, *pdf.PdfRectangle, error) {
bpath := draw.NewCubicBezierPath()
for _, c := range fc.curves {
bpath = bpath.AppendCurve(c)
}
creator := pdfcontent.NewContentCreator()
creator.Add_q()
if fc.FillEnabled {
creator.Add_rg(fc.fillColor.R(), fc.fillColor.G(), fc.fillColor.B())
}
if fc.BorderEnabled {
creator.Add_RG(fc.borderColor.R(), fc.borderColor.G(), fc.borderColor.B())
creator.Add_w(fc.BorderWidth)
}
if len(gsName) > 1 {
// If a graphics state is provided, use it. (can support transparency).
creator.Add_gs(pdfcore.PdfObjectName(gsName))
}
draw.DrawBezierPathWithCreator(bpath, creator)
creator.Add_h() // Close the path.
if fc.FillEnabled && fc.BorderEnabled {
creator.Add_B() // fill and stroke.
} else if fc.FillEnabled {
creator.Add_f() // Fill.
} else if fc.BorderEnabled {
creator.Add_S() // Stroke.
}
creator.Add_Q()
// Get bounding box.
pathBbox := bpath.GetBoundingBox()
if fc.BorderEnabled {
// Account for stroke width.
pathBbox.Height += fc.BorderWidth
pathBbox.Width += fc.BorderWidth
pathBbox.X -= fc.BorderWidth / 2
pathBbox.Y -= fc.BorderWidth / 2
}
// Bounding box - global coordinate system.
bbox := &pdf.PdfRectangle{}
bbox.Llx = pathBbox.X
bbox.Lly = pathBbox.Y
bbox.Urx = pathBbox.X + pathBbox.Width
bbox.Ury = pathBbox.Y + pathBbox.Height
return creator.Bytes(), bbox, nil
}
// GeneratePageBlocks draws the filled curve on page blocks.
func (fc *FilledCurve) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, error) {
block := NewBlock(ctx.PageWidth, ctx.PageHeight)
contents, _, _ := fc.draw("")
err := block.addContentsByString(string(contents))
if err != nil {
return nil, ctx, err
}
return []*Block{block}, ctx, nil
}

View File

@@ -0,0 +1,329 @@
package creator
import (
"bytes"
"fmt"
goimage "image"
"os"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/contentstream"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model"
)
// The Image type is used to draw an image onto PDF.
type Image struct {
xobj *model.XObjectImage
img *model.Image
// Rotation angle.
angle float64
// The dimensions of the image. As to be placed on the PDF.
width, height float64
// The original dimensions of the image (pixel based).
origWidth, origHeight float64
// Positioning: relative / absolute.
positioning positioning
// Absolute coordinates (when in absolute mode).
xPos float64
yPos float64
// Opacity (alpha value).
opacity float64
// Margins to be applied around the block when drawing on Page.
margins margins
// Encoder
encoder core.StreamEncoder
}
// NewImage create a new image from a image (model.Image).
func NewImage(img *model.Image) (*Image, error) {
image := &Image{}
image.img = img
// Image original size in points = pixel size.
image.origWidth = float64(img.Width)
image.origHeight = float64(img.Height)
image.width = image.origWidth
image.height = image.origHeight
image.angle = 0
image.opacity = 1.0
image.positioning = positionRelative
return image, nil
}
// NewImageFromData creates an Image from image data.
func NewImageFromData(data []byte) (*Image, error) {
imgReader := bytes.NewReader(data)
// Load the image with default handler.
img, err := model.ImageHandling.Read(imgReader)
if err != nil {
common.Log.Error("error loading image: %s", err)
return nil, err
}
return NewImage(img)
}
// NewImageFromFile creates an Image from a file.
func NewImageFromFile(path string) (*Image, error) {
imgData, err := os.ReadFile(path)
if err != nil {
return nil, err
}
img, err := NewImageFromData(imgData)
if err != nil {
return nil, err
}
return img, nil
}
// NewImageFromGoImage creates an Image from a go image.Image datastructure.
func NewImageFromGoImage(goimg goimage.Image) (*Image, error) {
img, err := model.ImageHandling.NewImageFromGoImage(goimg)
if err != nil {
return nil, err
}
return NewImage(img)
}
// SetEncoder sets the encoding/compression mechanism for the image.
func (img *Image) SetEncoder(encoder core.StreamEncoder) {
img.encoder = encoder
}
// Height returns Image's document height.
func (img *Image) Height() float64 {
return img.height
}
// Width returns Image's document width.
func (img *Image) Width() float64 {
return img.width
}
// SetOpacity sets opacity for Image.
func (img *Image) SetOpacity(opacity float64) {
img.opacity = opacity
}
// SetMargins sets the margins for the Image (in relative mode): left, right, top, bottom.
func (img *Image) SetMargins(left, right, top, bottom float64) {
img.margins.left = left
img.margins.right = right
img.margins.top = top
img.margins.bottom = bottom
}
// GetMargins returns the Image's margins: left, right, top, bottom.
func (img *Image) GetMargins() (float64, float64, float64, float64) {
return img.margins.left, img.margins.right, img.margins.top, img.margins.bottom
}
// makeXObject makes the encoded XObject Image that will be used in the PDF.
func (img *Image) makeXObject() error {
encoder := img.encoder
if encoder == nil {
// Default: Use flate encoder.
encoder = core.NewFlateEncoder()
}
// Create the XObject image.
ximg, err := model.NewXObjectImageFromImage(img.img, nil, encoder)
if err != nil {
common.Log.Error("Failed to create xobject image: %s", err)
return err
}
img.xobj = ximg
return nil
}
// GeneratePageBlocks generate the Page blocks. Draws the Image on a block, implementing the Drawable interface.
func (img *Image) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, error) {
if img.xobj == nil {
// Build the XObject Image if not already prepared.
img.makeXObject()
}
blocks := []*Block{}
origCtx := ctx
blk := NewBlock(ctx.PageWidth, ctx.PageHeight)
if img.positioning.isRelative() {
if img.height > ctx.Height {
// Goes out of the bounds. Write on a new template instead and create a new context at upper
// left corner.
blocks = append(blocks, blk)
blk = NewBlock(ctx.PageWidth, ctx.PageHeight)
// New Page.
ctx.Page++
newContext := ctx
newContext.Y = ctx.Margins.top // + p.Margins.top
newContext.X = ctx.Margins.left + img.margins.left
newContext.Height = ctx.PageHeight - ctx.Margins.top - ctx.Margins.bottom - img.margins.bottom
newContext.Width = ctx.PageWidth - ctx.Margins.left - ctx.Margins.right - img.margins.left - img.margins.right
ctx = newContext
} else {
ctx.Y += img.margins.top
ctx.Height -= img.margins.top + img.margins.bottom
ctx.X += img.margins.left
ctx.Width -= img.margins.left + img.margins.right
}
} else {
// Absolute.
ctx.X = img.xPos
ctx.Y = img.yPos
}
// Place the Image on the template at position (x,y) based on the ctx.
ctx, err := drawImageOnBlock(blk, img, ctx)
if err != nil {
return nil, ctx, err
}
blocks = append(blocks, blk)
if img.positioning.isAbsolute() {
// Absolute drawing should not affect context.
ctx = origCtx
} else {
// XXX/TODO: Use projected height.
ctx.Y += img.margins.bottom
ctx.Height -= img.margins.bottom
}
return blocks, ctx, nil
}
// SetPos sets the absolute position. Changes object positioning to absolute.
func (img *Image) SetPos(x, y float64) {
img.positioning = positionAbsolute
img.xPos = x
img.yPos = y
}
// Scale scales Image by a constant factor, both width and height.
func (img *Image) Scale(xFactor, yFactor float64) {
img.width = xFactor * img.width
img.height = yFactor * img.height
}
// ScaleToWidth scale Image to a specified width w, maintaining the aspect ratio.
func (img *Image) ScaleToWidth(w float64) {
ratio := img.height / img.width
img.width = w
img.height = w * ratio
}
// ScaleToHeight scale Image to a specified height h, maintaining the aspect ratio.
func (img *Image) ScaleToHeight(h float64) {
ratio := img.width / img.height
img.height = h
img.width = h * ratio
}
// SetWidth set the Image's document width to specified w. This does not change the raw image data, i.e.
// no actual scaling of data is performed. That is handled by the PDF viewer.
func (img *Image) SetWidth(w float64) {
img.width = w
}
// SetHeight sets the Image's document height to specified h.
func (img *Image) SetHeight(h float64) {
img.height = h
}
// SetAngle sets Image rotation angle in degrees.
func (img *Image) SetAngle(angle float64) {
img.angle = angle
}
// Draw the image onto the specified blk.
func drawImageOnBlock(blk *Block, img *Image, ctx DrawContext) (DrawContext, error) {
origCtx := ctx
// Find a free name for the image.
num := 1
imgName := core.PdfObjectName(fmt.Sprintf("Img%d", num))
for blk.resources.HasXObjectByName(imgName) {
num++
imgName = core.PdfObjectName(fmt.Sprintf("Img%d", num))
}
// Add to the Page resources.
err := blk.resources.SetXObjectImageByName(imgName, img.xobj)
if err != nil {
return ctx, err
}
// Find an available GS name.
i := 0
gsName := core.PdfObjectName(fmt.Sprintf("GS%d", i))
for blk.resources.HasExtGState(gsName) {
i++
gsName = core.PdfObjectName(fmt.Sprintf("GS%d", i))
}
// Graphics state with normal blend mode.
gs0 := core.MakeDict()
gs0.Set("BM", core.MakeName("Normal"))
if img.opacity < 1.0 {
gs0.Set("CA", core.MakeFloat(img.opacity))
gs0.Set("ca", core.MakeFloat(img.opacity))
}
err = blk.resources.AddExtGState(gsName, core.MakeIndirectObject(gs0))
if err != nil {
return ctx, err
}
xPos := ctx.X
yPos := ctx.PageHeight - ctx.Y - img.Height()
angle := img.angle
// Create content stream to add to the Page contents.
contentCreator := contentstream.NewContentCreator()
contentCreator.Add_gs(gsName) // Set graphics state.
contentCreator.Translate(xPos, yPos)
if angle != 0 {
// Make the rotation about the upper left corner.
contentCreator.Translate(0, img.Height())
contentCreator.RotateDeg(angle)
contentCreator.Translate(0, -img.Height())
}
contentCreator.
Scale(img.Width(), img.Height()).
Add_Do(imgName) // Draw the image.
ops := contentCreator.Operations()
ops.WrapIfNeeded()
blk.addContents(ops)
if img.positioning.isRelative() {
ctx.Y += img.Height()
ctx.Height -= img.Height()
return ctx, nil
}
// Absolute positioning - return original context.
return origCtx, nil
}

View File

@@ -0,0 +1,85 @@
package creator
import (
"math"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/contentstream/draw"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model"
)
// Line defines a line between point 1 (X1,Y1) and point 2 (X2,Y2). The line ending styles can be none (regular line),
// or arrows at either end. The line also has a specified width, color and opacity.
// Implements the Drawable interface and can be drawn on PDF using the Creator.
type Line struct {
x1 float64
y1 float64
x2 float64
y2 float64
lineColor *model.PdfColorDeviceRGB
lineWidth float64
}
// NewLine creates a new Line with default parameters between (x1,y1) to (x2,y2).
func NewLine(x1, y1, x2, y2 float64) *Line {
l := &Line{}
l.x1 = x1
l.y1 = y1
l.x2 = x2
l.y2 = y2
l.lineColor = model.NewPdfColorDeviceRGB(0, 0, 0)
l.lineWidth = 1.0
return l
}
// GetCoords returns the (x1, y1), (x2, y2) points defining the Line.
func (l *Line) GetCoords() (float64, float64, float64, float64) {
return l.x1, l.y1, l.x2, l.y2
}
// SetLineWidth sets the line width.
func (l *Line) SetLineWidth(lw float64) {
l.lineWidth = lw
}
// SetColor sets the line color.
// Use ColorRGBFromHex, ColorRGBFrom8bit or ColorRGBFromArithmetic to make the color object.
func (l *Line) SetColor(col Color) {
l.lineColor = model.NewPdfColorDeviceRGB(col.ToRGB())
}
// Length calculates and returns the line length.
func (l *Line) Length() float64 {
return math.Sqrt(math.Pow(l.x2-l.x1, 2.0) + math.Pow(l.y2-l.y1, 2.0))
}
// GeneratePageBlocks draws the line on a new block representing the page. Implements the Drawable interface.
func (l *Line) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, error) {
block := NewBlock(ctx.PageWidth, ctx.PageHeight)
drawline := draw.Line{
LineWidth: l.lineWidth,
Opacity: 1.0,
LineColor: l.lineColor,
LineEndingStyle1: draw.LineEndingStyleNone,
LineEndingStyle2: draw.LineEndingStyleNone,
X1: l.x1,
Y1: ctx.PageHeight - l.y1,
X2: l.x2,
Y2: ctx.PageHeight - l.y2,
}
contents, _, err := drawline.Draw("")
if err != nil {
return nil, ctx, err
}
err = block.addContentsByString(string(contents))
if err != nil {
return nil, ctx, err
}
return []*Block{block}, ctx, nil
}

View File

@@ -0,0 +1,31 @@
package creator
// PageBreak represents a page break for a chapter.
type PageBreak struct {
}
// NewPageBreak create a new page break.
func NewPageBreak() *PageBreak {
return &PageBreak{}
}
// GeneratePageBlocks generates a page break block.
func (p *PageBreak) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, error) {
// Return two empty blocks. First one simply means that there is nothing more to add at the current page.
// The second one starts a new page.
blocks := []*Block{
NewBlock(ctx.PageWidth, ctx.PageHeight-ctx.Y),
NewBlock(ctx.PageWidth, ctx.PageHeight),
}
// New Page. Place context in upper left corner (with margins).
ctx.Page++
newContext := ctx
newContext.Y = ctx.Margins.top
newContext.X = ctx.Margins.left
newContext.Height = ctx.PageHeight - ctx.Margins.top - ctx.Margins.bottom
newContext.Width = ctx.PageWidth - ctx.Margins.left - ctx.Margins.right
ctx = newContext
return blocks, ctx, nil
}

View File

@@ -0,0 +1,525 @@
package creator
import (
"errors"
"fmt"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/contentstream"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model/fonts"
"gitea.tecamino.com/paadi/pdfmerge/internal/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.
type Paragraph struct {
// The input utf-8 text as a string (series of runes).
text string
// The text encoder which can convert the text (as runes) into a series of glyphs and get character metrics.
encoder textencoding.TextEncoder
// The font to be used to draw the text.
textFont fonts.Font
// The font size (points).
fontSize float64
// The line relative height (default 1).
lineHeight float64
// The text color.
color model.PdfColorDeviceRGB
// Text alignment: Align left/right/center/justify.
alignment TextAlignment
// Wrapping properties.
enableWrap bool
wrapWidth float64
// defaultWrap defines whether wrapping has been defined explictly or whether default behavior should
// be observed. Default behavior depends on context: normally wrap is expected, except for example in
// table cells wrapping is off by default.
defaultWrap bool
// Rotation angle (degrees).
angle float64
// Margins to be applied around the block when drawing on Page.
margins margins
// Positioning: relative / absolute.
positioning positioning
// Absolute coordinates (when in absolute mode).
xPos float64
yPos float64
// Scaling factors (1 default).
scaleX, scaleY float64
// Text lines after wrapping to available width.
textLines []string
}
// 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())
p.fontSize = 10
p.lineHeight = 1.0
// TODO: Can we wrap intellectually, only if given width is known?
p.enableWrap = true
p.defaultWrap = true
p.SetColor(ColorRGBFrom8bit(0, 0, 0))
p.alignment = TextAlignmentLeft
p.angle = 0
p.scaleX = 1
p.scaleY = 1
p.positioning = positionRelative
return p
}
// SetFont sets the Paragraph's font.
func (p *Paragraph) SetFont(font fonts.Font) {
p.textFont = font
}
// SetFontSize sets the font size in document units (points).
func (p *Paragraph) SetFontSize(fontSize float64) {
p.fontSize = fontSize
}
// SetTextAlignment sets the horizontal alignment of the text within the space provided.
func (p *Paragraph) SetTextAlignment(align TextAlignment) {
p.alignment = align
}
// SetEncoder sets the text encoding.
func (p *Paragraph) SetEncoder(encoder textencoding.TextEncoder) {
p.encoder = encoder
// Sync with the text font too.
// XXX/FIXME: Keep in 1 place only.
p.textFont.SetEncoder(encoder)
}
// SetLineHeight sets the line height (1.0 default).
func (p *Paragraph) SetLineHeight(lineheight float64) {
p.lineHeight = lineheight
}
// SetText sets the text content of the Paragraph.
func (p *Paragraph) SetText(text string) {
p.text = text
}
// Text sets the text content of the Paragraph.
func (p *Paragraph) Text() string {
return p.text
}
// SetEnableWrap sets the line wrapping enabled flag.
func (p *Paragraph) SetEnableWrap(enableWrap bool) {
p.enableWrap = enableWrap
p.defaultWrap = false
}
// SetColor set the color of the Paragraph text.
//
// Example:
//
// 1. p := NewParagraph("Red paragraph")
// // Set to red color with a hex code:
// p.SetColor(creator.ColorRGBFromHex("#ff0000"))
//
// 2. Make Paragraph green with 8-bit rgb values (0-255 each component)
// p.SetColor(creator.ColorRGBFrom8bit(0, 255, 0)
//
// 3. Make Paragraph blue with arithmetic (0-1) rgb components.
// p.SetColor(creator.ColorRGBFromArithmetic(0, 0, 1.0)
func (p *Paragraph) SetColor(col Color) {
pdfColor := model.NewPdfColorDeviceRGB(col.ToRGB())
p.color = *pdfColor
}
// SetPos sets absolute positioning with specified coordinates.
func (p *Paragraph) SetPos(x, y float64) {
p.positioning = positionAbsolute
p.xPos = x
p.yPos = y
}
// SetAngle sets the rotation angle of the text.
func (p *Paragraph) SetAngle(angle float64) {
p.angle = angle
}
// SetMargins sets the Paragraph's margins.
func (p *Paragraph) SetMargins(left, right, top, bottom float64) {
p.margins.left = left
p.margins.right = right
p.margins.top = top
p.margins.bottom = bottom
}
// GetMargins returns the Paragraph's margins: left, right, top, bottom.
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.
func (p *Paragraph) SetWidth(width float64) {
p.wrapWidth = width
p.wrapText()
}
// Width returns the width of the Paragraph.
func (p *Paragraph) Width() float64 {
if p.enableWrap {
return p.wrapWidth
}
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.
func (p *Paragraph) Height() float64 {
if len(p.textLines) == 0 {
p.wrapText()
}
h := float64(len(p.textLines)) * p.lineHeight * p.fontSize
return h
}
// getTextWidth calculates the text width as if all in one line (not taking wrapping into account).
func (p *Paragraph) getTextWidth() float64 {
w := float64(0.0)
for _, rune := range p.text {
glyph, found := p.encoder.RuneToGlyph(rune)
if !found {
common.Log.Debug("error! Glyph not found for rune: %s\n", rune)
return -1 // XXX/FIXME: return error.
}
// Ignore newline for this.. Handles as if all in one line.
if glyph == "controlLF" {
continue
}
metrics, found := p.textFont.GetGlyphCharMetrics(glyph)
if !found {
common.Log.Debug("Glyph char metrics not found! %s\n", glyph)
return -1 // XXX/FIXME: return error.
}
w += p.fontSize * metrics.Wx
}
return w
}
// Simple algorithm to wrap the text into lines (greedy algorithm - fill the lines).
// XXX/TODO: Consider the Knuth/Plass algorithm or an alternative.
func (p *Paragraph) wrapText() error {
if !p.enableWrap {
p.textLines = []string{p.text}
return nil
}
line := []rune{}
lineWidth := float64(0.0)
p.textLines = []string{}
runes := []rune(p.text)
glyphs := []string{}
widths := []float64{}
for _, val := range runes {
glyph, found := p.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.
}
// Newline wrapping.
if glyph == "controlLF" {
// Moves to next line.
p.textLines = append(p.textLines, string(line))
line = []rune{}
lineWidth = 0
widths = []float64{}
glyphs = []string{}
continue
}
metrics, found := p.textFont.GetGlyphCharMetrics(glyph)
if !found {
common.Log.Debug("Glyph char metrics not found! %s\n", glyph)
return errors.New("glyph char metrics missing") // XXX/FIXME: return error.
}
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" {
idx = i
break
}
}
if idx > 0 {
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
}
} else {
p.textLines = append(p.textLines, string(line))
line = []rune{val}
lineWidth = w
widths = []float64{w}
glyphs = []string{glyph}
}
} else {
line = append(line, val)
lineWidth += w
glyphs = append(glyphs, glyph)
widths = append(widths, w)
}
}
if len(line) > 0 {
p.textLines = append(p.textLines, string(line))
}
return nil
}
// 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.
ctx.X += p.margins.left
ctx.Y += p.margins.top
ctx.Width -= p.margins.left + p.margins.right
ctx.Height -= p.margins.top + p.margins.bottom
// Use available space.
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.
// 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
blocks = append(blocks, blk)
blk = NewBlock(ctx.PageWidth, ctx.PageHeight)
// New Page.
ctx.Page++
newContext := ctx
newContext.Y = ctx.Margins.top // + p.Margins.top
newContext.X = ctx.Margins.left + p.margins.left
newContext.Height = ctx.PageHeight - ctx.Margins.top - ctx.Margins.bottom - p.margins.bottom
newContext.Width = ctx.PageWidth - ctx.Margins.left - ctx.Margins.right - p.margins.left - p.margins.right
ctx = newContext
}
} else {
// Absolute.
if p.wrapWidth == 0 {
// Use necessary space.
p.SetWidth(p.getTextWidth())
}
ctx.X = p.xPos
ctx.Y = p.yPos
}
// Place the Paragraph on the template at position (x,y) based on the ctx.
ctx, err := drawParagraphOnBlock(blk, p, ctx)
if err != nil {
common.Log.Debug("error: %v", err)
return nil, ctx, err
}
blocks = append(blocks, blk)
if p.positioning.isRelative() {
ctx.X -= p.margins.left // Move back.
ctx.Width = origContext.Width
return blocks, ctx, nil
}
// Absolute: not changing the context.
return blocks, origContext, nil
}
// Draw block on specified location on Page, adding to the content stream.
func drawParagraphOnBlock(blk *Block, p *Paragraph, ctx DrawContext) (DrawContext, error) {
// Find a free name for the font.
num := 1
fontName := core.PdfObjectName(fmt.Sprintf("Font%d", num))
for blk.resources.HasFontByName(fontName) {
num++
fontName = core.PdfObjectName(fmt.Sprintf("Font%d", num))
}
// Add to the Page resources.
err := blk.resources.SetFontByName(fontName, p.textFont.ToPdfObject())
if err != nil {
return ctx, err
}
// Wrap the text into lines.
p.wrapText()
// Create the content stream.
cc := contentstream.NewContentCreator()
cc.Add_q()
yPos := ctx.PageHeight - ctx.Y - p.fontSize*p.lineHeight
cc.Translate(ctx.X, yPos)
if p.angle != 0 {
cc.RotateDeg(p.angle)
}
cc.Add_BT().
Add_rg(p.color.R(), p.color.G(), p.color.B()).
Add_Tf(fontName, p.fontSize).
Add_TL(p.fontSize * p.lineHeight)
for idx, line := range p.textLines {
if idx != 0 {
// Move to next line if not first.
cc.Add_Tstar()
}
runes := []rune(line)
// Get width of the line (excluding spaces).
w := float64(0)
spaces := 0
for _, runeVal := range runes {
glyph, found := p.encoder.RuneToGlyph(runeVal)
if !found {
common.Log.Debug("Rune 0x%x not supported by text encoder", runeVal)
return ctx, errors.New("unsupported rune in text encoding")
}
if glyph == "space" {
spaces++
continue
}
if glyph == "controlLF" {
continue
}
metrics, found := p.textFont.GetGlyphCharMetrics(glyph)
if !found {
common.Log.Debug("Unsupported glyph %s in font\n", glyph)
return ctx, errors.New("unsupported text glyph")
}
w += p.fontSize * metrics.Wx
}
objs := []core.PdfObject{}
spaceMetrics, found := p.textFont.GetGlyphCharMetrics("space")
if !found {
return ctx, errors.New("the font does not have a space glyph")
}
spaceWidth := spaceMetrics.Wx
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
}
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))
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.encoder.RuneToGlyph(runeVal)
if !found {
common.Log.Debug("Rune 0x%x not supported by text encoder", runeVal)
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 = ""
}
objs = append(objs, core.MakeFloat(-spaceWidth))
} else {
encStr += string(p.encoder.Encode(string(runeVal)))
}
}
if len(encStr) > 0 {
objs = append(objs, core.MakeString(encStr))
}
cc.Add_TJ(objs...)
}
cc.Add_ET()
cc.Add_Q()
ops := cc.Operations()
ops.WrapIfNeeded()
blk.addContents(ops)
if p.positioning.isRelative() {
pHeight := p.Height() + p.margins.bottom
ctx.Y += pHeight
ctx.Height -= pHeight
// If the division is inline, calculate context new X coordinate.
if ctx.Inline {
ctx.X += p.Width() + p.margins.right
}
}
return ctx, nil
}

View File

@@ -0,0 +1,88 @@
package creator
import (
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/contentstream/draw"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model"
)
// Rectangle defines a rectangle with upper left corner at (x,y) and a specified width and height. The rectangle
// can have a colored fill and/or border with a specified width.
// Implements the Drawable interface and can be drawn on PDF using the Creator.
type Rectangle struct {
x float64 // Upper left corner
y float64
width float64
height float64
fillColor *model.PdfColorDeviceRGB
borderColor *model.PdfColorDeviceRGB
borderWidth float64
}
// NewRectangle creates a new Rectangle with default parameters with left corner at (x,y) and width, height as specified.
func NewRectangle(x, y, width, height float64) *Rectangle {
rect := &Rectangle{}
rect.x = x
rect.y = y
rect.width = width
rect.height = height
rect.borderColor = model.NewPdfColorDeviceRGB(0, 0, 0)
rect.borderWidth = 1.0
return rect
}
// GetCoords returns coordinates of the Rectangle's upper left corner (x,y).
func (rect *Rectangle) GetCoords() (float64, float64) {
return rect.x, rect.y
}
// SetBorderWidth sets the border width.
func (rect *Rectangle) SetBorderWidth(bw float64) {
rect.borderWidth = bw
}
// SetBorderColor sets border color.
func (rect *Rectangle) SetBorderColor(col Color) {
rect.borderColor = model.NewPdfColorDeviceRGB(col.ToRGB())
}
// SetFillColor sets the fill color.
func (rect *Rectangle) SetFillColor(col Color) {
rect.fillColor = model.NewPdfColorDeviceRGB(col.ToRGB())
}
// GeneratePageBlocks draws the rectangle on a new block representing the page. Implements the Drawable interface.
func (rect *Rectangle) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, error) {
block := NewBlock(ctx.PageWidth, ctx.PageHeight)
drawrect := draw.Rectangle{
Opacity: 1.0,
X: rect.x,
Y: ctx.PageHeight - rect.y - rect.height,
Height: rect.height,
Width: rect.width,
}
if rect.fillColor != nil {
drawrect.FillEnabled = true
drawrect.FillColor = rect.fillColor
}
if rect.borderColor != nil && rect.borderWidth > 0 {
drawrect.BorderEnabled = true
drawrect.BorderColor = rect.borderColor
drawrect.BorderWidth = rect.borderWidth
}
contents, _, err := drawrect.Draw("")
if err != nil {
return nil, ctx, err
}
err = block.addContentsByString(string(contents))
if err != nil {
return nil, ctx, err
}
return []*Block{block}, ctx, nil
}

View File

@@ -0,0 +1,638 @@
package creator
import (
"errors"
"fmt"
"strings"
"unicode"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/contentstream"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model/textencoding"
)
// StyledParagraph represents text drawn with a specified font and can wrap across lines and pages.
// By default occupies the available width in the drawing context.
type StyledParagraph struct {
// Text chunks with styles that compose the paragraph
chunks []TextChunk
// Style used for the paragraph for spacing and offsets
defaultStyle TextStyle
// The text encoder which can convert the text (as runes) into a series of glyphs and get character metrics.
encoder textencoding.TextEncoder
// Text alignment: Align left/right/center/justify.
alignment TextAlignment
// The line relative height (default 1).
lineHeight float64
// Wrapping properties.
enableWrap bool
wrapWidth float64
// defaultWrap defines whether wrapping has been defined explictly or whether default behavior should
// be observed. Default behavior depends on context: normally wrap is expected, except for example in
// table cells wrapping is off by default.
defaultWrap bool
// Rotation angle (degrees).
angle float64
// Margins to be applied around the block when drawing on Page.
margins margins
// Positioning: relative / absolute.
positioning positioning
// Absolute coordinates (when in absolute mode).
xPos float64
yPos float64
// Scaling factors (1 default).
scaleX float64
scaleY float64
// Text chunk lines after wrapping to available width.
lines [][]TextChunk
}
// NewStyledParagraph creates a new styled paragraph.
// Uses default parameters: Helvetica, WinAnsiEncoding and wrap enabled
// with a wrap width of 100 points.
func NewStyledParagraph(text string, style TextStyle) *StyledParagraph {
// TODO: Can we wrap intellectually, only if given width is known?
p := &StyledParagraph{
chunks: []TextChunk{
{
Text: text,
Style: style,
},
},
defaultStyle: NewTextStyle(),
lineHeight: 1.0,
alignment: TextAlignmentLeft,
enableWrap: true,
defaultWrap: true,
angle: 0,
scaleX: 1,
scaleY: 1,
positioning: positionRelative,
}
p.SetEncoder(textencoding.NewWinAnsiTextEncoder())
return p
}
// Append adds a new text chunk with a specified style to the paragraph.
func (p *StyledParagraph) Append(text string, style TextStyle) {
chunk := TextChunk{
Text: text,
Style: style,
}
chunk.Style.Font.SetEncoder(p.encoder)
p.chunks = append(p.chunks, chunk)
p.wrapText()
}
// Reset sets the entire text and also the style of the paragraph
// to those specified. It behaves as if the paragraph was a new one.
func (p *StyledParagraph) Reset(text string, style TextStyle) {
p.chunks = []TextChunk{}
p.Append(text, style)
}
// SetTextAlignment sets the horizontal alignment of the text within the space provided.
func (p *StyledParagraph) SetTextAlignment(align TextAlignment) {
p.alignment = align
}
// SetEncoder sets the text encoding.
func (p *StyledParagraph) SetEncoder(encoder textencoding.TextEncoder) {
p.encoder = encoder
p.defaultStyle.Font.SetEncoder(encoder)
// Sync with the text font too.
// XXX/FIXME: Keep in 1 place only.
for _, chunk := range p.chunks {
chunk.Style.Font.SetEncoder(encoder)
}
}
// SetLineHeight sets the line height (1.0 default).
func (p *StyledParagraph) SetLineHeight(lineheight float64) {
p.lineHeight = lineheight
}
// SetEnableWrap sets the line wrapping enabled flag.
func (p *StyledParagraph) SetEnableWrap(enableWrap bool) {
p.enableWrap = enableWrap
p.defaultWrap = false
}
// SetPos sets absolute positioning with specified coordinates.
func (p *StyledParagraph) SetPos(x, y float64) {
p.positioning = positionAbsolute
p.xPos = x
p.yPos = y
}
// SetAngle sets the rotation angle of the text.
func (p *StyledParagraph) SetAngle(angle float64) {
p.angle = angle
}
// SetMargins sets the Paragraph's margins.
func (p *StyledParagraph) SetMargins(left, right, top, bottom float64) {
p.margins.left = left
p.margins.right = right
p.margins.top = top
p.margins.bottom = bottom
}
// GetMargins returns the Paragraph's margins: left, right, top, bottom.
func (p *StyledParagraph) 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.
func (p *StyledParagraph) SetWidth(width float64) {
p.wrapWidth = width
p.wrapText()
}
// Width returns the width of the Paragraph.
func (p *StyledParagraph) Width() float64 {
if p.enableWrap {
return p.wrapWidth
}
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.
func (p *StyledParagraph) Height() float64 {
if len(p.lines) == 0 {
p.wrapText()
}
var height float64
for _, line := range p.lines {
var lineHeight float64
for _, chunk := range line {
h := p.lineHeight * chunk.Style.FontSize
if h > lineHeight {
lineHeight = h
}
}
height += lineHeight
}
return height
}
// getTextWidth calculates the text width as if all in one line (not taking wrapping into account).
func (p *StyledParagraph) getTextWidth() float64 {
var width float64
for _, chunk := range p.chunks {
style := &chunk.Style
for _, rune := range chunk.Text {
glyph, found := p.encoder.RuneToGlyph(rune)
if !found {
common.Log.Debug("error! Glyph not found for rune: %s\n", rune)
// XXX/FIXME: return error.
return -1
}
// Ignore newline for this.. Handles as if all in one line.
if glyph == "controlLF" {
continue
}
metrics, found := style.Font.GetGlyphCharMetrics(glyph)
if !found {
common.Log.Debug("Glyph char metrics not found! %s\n", glyph)
// XXX/FIXME: return error.
return -1
}
width += style.FontSize * metrics.Wx
}
}
return width
}
// getTextHeight calculates the text height as if all in one line (not taking wrapping into account).
func (p *StyledParagraph) getTextHeight() float64 {
var height float64
for _, chunk := range p.chunks {
h := chunk.Style.FontSize * p.lineHeight
if h > height {
height = h
}
}
return height
}
// wrapText splits text into lines. It uses a simple greedy algorithm to wrap
// fill the lines.
// XXX/TODO: Consider the Knuth/Plass algorithm or an alternative.
func (p *StyledParagraph) wrapText() error {
if !p.enableWrap {
p.lines = [][]TextChunk{p.chunks}
return nil
}
p.lines = [][]TextChunk{}
var line []TextChunk
var lineWidth float64
for _, chunk := range p.chunks {
style := chunk.Style
var part []rune
var glyphs []string
var widths []float64
for _, r := range chunk.Text {
glyph, found := p.encoder.RuneToGlyph(r)
if !found {
common.Log.Debug("error! Glyph not found for rune: %v\n", r)
// XXX/FIXME: return error.
return errors.New("glyph not found for rune")
}
// newline wrapping.
if glyph == "controlLF" {
// moves to next line.
line = append(line, TextChunk{
Text: strings.TrimRightFunc(string(part), unicode.IsSpace),
Style: style,
})
p.lines = append(p.lines, line)
line = []TextChunk{}
lineWidth = 0
part = []rune{}
widths = []float64{}
glyphs = []string{}
continue
}
metrics, found := style.Font.GetGlyphCharMetrics(glyph)
if !found {
common.Log.Debug("Glyph char metrics not found! %s\n", glyph)
// XXX/FIXME: return error.
return errors.New("glyph char metrics missing")
}
w := style.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 j := len(glyphs) - 1; j >= 0; j-- {
if glyphs[j] == "space" {
idx = j
break
}
}
text := string(part)
if idx >= 0 {
text = string(part[0 : idx+1])
part = part[idx+1:]
part = append(part, r)
glyphs = glyphs[idx+1:]
glyphs = append(glyphs, glyph)
widths = widths[idx+1:]
widths = append(widths, w)
lineWidth = 0
for _, width := range widths {
lineWidth += width
}
} else {
lineWidth = w
part = []rune{r}
glyphs = []string{glyph}
widths = []float64{w}
}
line = append(line, TextChunk{
Text: strings.TrimRightFunc(string(text), unicode.IsSpace),
Style: style,
})
p.lines = append(p.lines, line)
line = []TextChunk{}
} else {
lineWidth += w
part = append(part, r)
glyphs = append(glyphs, glyph)
widths = append(widths, w)
}
}
if len(part) > 0 {
line = append(line, TextChunk{
Text: string(part),
Style: style,
})
}
}
if len(line) > 0 {
p.lines = append(p.lines, line)
}
return nil
}
// GeneratePageBlocks generates the page blocks. Multiple blocks are generated
// if the contents wrap over multiple pages. Implements the Drawable interface.
func (p *StyledParagraph) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, error) {
origContext := ctx
blocks := []*Block{}
blk := NewBlock(ctx.PageWidth, ctx.PageHeight)
if p.positioning.isRelative() {
// Account for Paragraph Margins.
ctx.X += p.margins.left
ctx.Y += p.margins.top
ctx.Width -= p.margins.left + p.margins.right
ctx.Height -= p.margins.top + p.margins.bottom
// Use available space.
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.
// 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
blocks = append(blocks, blk)
blk = NewBlock(ctx.PageWidth, ctx.PageHeight)
// New Page.
ctx.Page++
newContext := ctx
newContext.Y = ctx.Margins.top // + p.Margins.top
newContext.X = ctx.Margins.left + p.margins.left
newContext.Height = ctx.PageHeight - ctx.Margins.top - ctx.Margins.bottom - p.margins.bottom
newContext.Width = ctx.PageWidth - ctx.Margins.left - ctx.Margins.right - p.margins.left - p.margins.right
ctx = newContext
}
} else {
// Absolute.
if p.wrapWidth == 0 {
// Use necessary space.
p.SetWidth(p.getTextWidth())
}
ctx.X = p.xPos
ctx.Y = p.yPos
}
// Place the Paragraph on the template at position (x,y) based on the ctx.
ctx, err := drawStyledParagraphOnBlock(blk, p, ctx)
if err != nil {
common.Log.Debug("error: %v", err)
return nil, ctx, err
}
blocks = append(blocks, blk)
if p.positioning.isRelative() {
ctx.X -= p.margins.left // Move back.
ctx.Width = origContext.Width
return blocks, ctx, nil
}
// Absolute: not changing the context.
return blocks, origContext, nil
}
// Draw block on specified location on Page, adding to the content stream.
func drawStyledParagraphOnBlock(blk *Block, p *StyledParagraph, ctx DrawContext) (DrawContext, error) {
// Find first free index for the font resources of the paragraph
num := 1
fontName := core.PdfObjectName(fmt.Sprintf("Font%d", num))
for blk.resources.HasFontByName(fontName) {
num++
fontName = core.PdfObjectName(fmt.Sprintf("Font%d", num))
}
// Add default font to the page resources
err := blk.resources.SetFontByName(fontName, p.defaultStyle.Font.ToPdfObject())
if err != nil {
return ctx, err
}
num++
defaultFontName := fontName
defaultFontSize := p.defaultStyle.FontSize
// Wrap the text into lines.
p.wrapText()
// Add the fonts of all chunks to the page resources
fonts := [][]core.PdfObjectName{}
for _, line := range p.lines {
fontLine := []core.PdfObjectName{}
for _, chunk := range line {
fontName = core.PdfObjectName(fmt.Sprintf("Font%d", num))
err := blk.resources.SetFontByName(fontName, chunk.Style.Font.ToPdfObject())
if err != nil {
return ctx, err
}
fontLine = append(fontLine, fontName)
num++
}
fonts = append(fonts, fontLine)
}
// Create the content stream.
cc := contentstream.NewContentCreator()
cc.Add_q()
yPos := ctx.PageHeight - ctx.Y - defaultFontSize*p.lineHeight
cc.Translate(ctx.X, yPos)
if p.angle != 0 {
cc.RotateDeg(p.angle)
}
cc.Add_BT()
for idx, line := range p.lines {
if idx != 0 {
// Move to next line if not first.
cc.Add_Tstar()
}
isLastLine := idx == len(p.lines)-1
// Get width of the line (excluding spaces).
var width float64
var spaceWidth float64
var spaces uint
for _, chunk := range line {
style := &chunk.Style
spaceMetrics, found := style.Font.GetGlyphCharMetrics("space")
if !found {
return ctx, errors.New("the font does not have a space glyph")
}
var chunkSpaces uint
for _, r := range chunk.Text {
glyph, found := p.encoder.RuneToGlyph(r)
if !found {
common.Log.Debug("Rune 0x%x not supported by text encoder", r)
return ctx, errors.New("unsupported rune in text encoding")
}
if glyph == "space" {
chunkSpaces++
continue
}
if glyph == "controlLF" {
continue
}
metrics, found := style.Font.GetGlyphCharMetrics(glyph)
if !found {
common.Log.Debug("Unsupported glyph %s in font\n", glyph)
return ctx, errors.New("unsupported text glyph")
}
width += style.FontSize * metrics.Wx
}
spaceWidth += float64(chunkSpaces) * spaceMetrics.Wx * style.FontSize
spaces += chunkSpaces
}
// Add line shifts
objs := []core.PdfObject{}
switch p.alignment {
case TextAlignmentJustify:
// Not to justify last line.
if spaces > 0 && !isLastLine {
spaceWidth = (p.wrapWidth*1000.0 - width) / float64(spaces) / defaultFontSize
}
case TextAlignmentCenter:
// Start with a shift.
shift := (p.wrapWidth*1000.0 - width - spaceWidth) / 2 / defaultFontSize
objs = append(objs, core.MakeFloat(-shift))
case TextAlignmentRight:
shift := (p.wrapWidth*1000.0 - width - spaceWidth) / defaultFontSize
objs = append(objs, core.MakeFloat(-shift))
}
if len(objs) > 0 {
cc.Add_Tf(defaultFontName, defaultFontSize).
Add_TL(defaultFontSize * p.lineHeight).
Add_TJ(objs...)
}
// Render line text chunks
for k, chunk := range line {
style := &chunk.Style
r, g, b := style.Color.ToRGB()
fontName := defaultFontName
fontSize := defaultFontSize
if p.alignment != TextAlignmentJustify || isLastLine {
spaceMetrics, found := style.Font.GetGlyphCharMetrics("space")
if !found {
return ctx, errors.New("the font does not have a space glyph")
}
fontName = fonts[idx][k]
fontSize = style.FontSize
spaceWidth = spaceMetrics.Wx
}
encStr := ""
for _, rn := range chunk.Text {
glyph, found := p.encoder.RuneToGlyph(rn)
if !found {
common.Log.Debug("Rune 0x%x not supported by text encoder", r)
return ctx, errors.New("unsupported rune in text encoding")
}
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 {
cc.Add_rg(r, g, b).
Add_Tf(fonts[idx][k], style.FontSize).
Add_TL(style.FontSize * p.lineHeight).
Add_TJ([]core.PdfObject{core.MakeString(encStr)}...)
encStr = ""
}
cc.Add_Tf(fontName, fontSize).
Add_TL(fontSize * p.lineHeight).
Add_TJ([]core.PdfObject{core.MakeFloat(-spaceWidth)}...)
} else {
encStr += p.encoder.Encode(string(rn))
}
}
if len(encStr) > 0 {
cc.Add_rg(r, g, b).
Add_Tf(fonts[idx][k], style.FontSize).
Add_TL(style.FontSize * p.lineHeight).
Add_TJ([]core.PdfObject{core.MakeString(encStr)}...)
}
}
}
cc.Add_ET()
cc.Add_Q()
ops := cc.Operations()
ops.WrapIfNeeded()
blk.addContents(ops)
if p.positioning.isRelative() {
pHeight := p.Height() + p.margins.bottom
ctx.Y += pHeight
ctx.Height -= pHeight
// If the division is inline, calculate context new X coordinate.
if ctx.Inline {
ctx.X += p.Width() + p.margins.right
}
}
return ctx, nil
}

View File

@@ -0,0 +1,179 @@
package creator
import (
"fmt"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model/fonts"
)
// 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
title string
heading *Paragraph
contents []Drawable
// Show chapter numbering
showNumbering bool
// Include in TOC.
includeInTOC bool
// Positioning: relative / absolute.
positioning positioning
// Margins to be applied around the block when drawing on Page.
margins margins
// Reference to the creator's TOC.
toc *TableOfContents
}
// NewSubchapter creates a new Subchapter under Chapter ch with specified title.
// All other parameters are set to their defaults.
func (c *Creator) NewSubchapter(ch *Chapter, title string) *Subchapter {
subchap := &Subchapter{}
ch.subchapters++
subchap.subchapterNum = ch.subchapters
subchap.chapterNum = ch.number
subchap.title = title
heading := fmt.Sprintf("%d.%d %s", subchap.chapterNum, subchap.subchapterNum, title)
p := NewParagraph(heading)
p.SetFontSize(14)
p.SetFont(fonts.NewFontHelvetica()) // bold?
subchap.showNumbering = true
subchap.includeInTOC = true
subchap.heading = p
subchap.contents = []Drawable{}
// Add subchapter to ch.
ch.Add(subchap)
// Keep a reference for toc.
subchap.toc = c.toc
return subchap
}
// SetShowNumbering sets a flag to indicate whether or not to show chapter numbers as part of title.
func (subchap *Subchapter) SetShowNumbering(show bool) {
if show {
heading := fmt.Sprintf("%d.%d. %s", subchap.chapterNum, subchap.subchapterNum, subchap.title)
subchap.heading.SetText(heading)
} else {
heading := subchap.title
subchap.heading.SetText(heading)
}
subchap.showNumbering = show
}
// SetIncludeInTOC sets a flag to indicate whether or not to include in the table of contents.
func (subchap *Subchapter) SetIncludeInTOC(includeInTOC bool) {
subchap.includeInTOC = includeInTOC
}
// GetHeading returns the Subchapter's heading Paragraph to address style (font type, size, etc).
func (subchap *Subchapter) GetHeading() *Paragraph {
return subchap.heading
}
// Set absolute coordinates.
/*
func (subchap *subchapter) SetPos(x, y float64) {
subchap.positioning = positionAbsolute
subchap.xPos = x
subchap.yPos = y
}
*/
// SetMargins sets the Subchapter's margins (left, right, top, bottom).
// These margins are typically not needed as the Creator's page margins are used preferably.
func (subchap *Subchapter) SetMargins(left, right, top, bottom float64) {
subchap.margins.left = left
subchap.margins.right = right
subchap.margins.top = top
subchap.margins.bottom = bottom
}
// GetMargins returns the Subchapter's margins: left, right, top, bottom.
func (subchap *Subchapter) GetMargins() (float64, float64, float64, float64) {
return subchap.margins.left, subchap.margins.right, subchap.margins.top, subchap.margins.bottom
}
// Add adds a new Drawable to the chapter.
// The currently supported Drawables are: *Paragraph, *Image, *Block, *Table.
func (subchap *Subchapter) Add(d Drawable) {
switch d.(type) {
case *Chapter, *Subchapter:
common.Log.Debug("error: Cannot add chapter or subchapter to a subchapter")
case *Paragraph, *Image, *Block, *Table, *PageBreak:
subchap.contents = append(subchap.contents, d)
default:
common.Log.Debug("Unsupported: %T", d)
}
}
// 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
if subchap.positioning.isRelative() {
// Update context.
ctx.X += subchap.margins.left
ctx.Y += subchap.margins.top
ctx.Width -= subchap.margins.left + subchap.margins.right
ctx.Height -= subchap.margins.top
}
blocks, ctx, err := subchap.heading.GeneratePageBlocks(ctx)
if err != nil {
return blocks, ctx, err
}
if len(blocks) > 1 {
ctx.Page++ // did not fit - moved to next Page.
}
if subchap.includeInTOC {
// Add to TOC.
subchap.toc.add(subchap.title, subchap.chapterNum, subchap.subchapterNum, ctx.Page)
}
for _, d := range subchap.contents {
newBlocks, c, err := d.GeneratePageBlocks(ctx)
if err != nil {
return blocks, ctx, err
}
if len(newBlocks) < 1 {
continue
}
// The first block is always appended to the last..
blocks[len(blocks)-1].mergeBlocks(newBlocks[0])
blocks = append(blocks, newBlocks[1:]...)
ctx = c
}
if subchap.positioning.isRelative() {
// Move back X to same start of line.
ctx.X = origCtx.X
}
if subchap.positioning.isAbsolute() {
// If absolute: return original context.
return blocks, origCtx, nil
}
return blocks, ctx, nil
}

View File

@@ -0,0 +1,628 @@
package creator
import (
"errors"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model"
)
// Table allows organizing content in an rows X columns matrix, which can spawn across multiple pages.
type Table struct {
// Number of rows and columns.
rows int
cols int
// Current cell. Current cell in the table.
// For 4x4 table, if in the 2nd row, 3rd column, then
// curCell = 4+3 = 7
curCell int
// Column width fractions: should add up to 1.
colWidths []float64
// Row heights.
rowHeights []float64
// Default row height.
defaultRowHeight float64
// Content cells.
cells []*TableCell
// Positioning: relative / absolute.
positioning positioning
// Absolute coordinates (when in absolute mode).
xPos, yPos float64
// Margins to be applied around the block when drawing on Page.
margins margins
}
// NewTable create a new Table with a specified number of columns.
func NewTable(cols int) *Table {
t := &Table{}
t.rows = 0
t.cols = cols
t.curCell = 0
// Initialize column widths as all equal.
t.colWidths = []float64{}
colWidth := float64(1.0) / float64(cols)
for i := 0; i < cols; i++ {
t.colWidths = append(t.colWidths, colWidth)
}
t.rowHeights = []float64{}
// Default row height
// XXX/TODO: Base on contents instead?
t.defaultRowHeight = 10.0
t.cells = []*TableCell{}
return t
}
// SetColumnWidths sets the fractional column widths.
// Each width should be in the range 0-1 and is a fraction of the table width.
// The number of width inputs must match number of columns, otherwise an error is returned.
func (table *Table) SetColumnWidths(widths ...float64) error {
if len(widths) != table.cols {
common.Log.Debug("Mismatching number of widths and columns")
return errors.New("range check error")
}
table.colWidths = widths
return nil
}
// Height returns the total height of all rows.
func (table *Table) Height() float64 {
sum := float64(0.0)
for _, h := range table.rowHeights {
sum += h
}
return sum
}
// SetMargins sets the Table's left, right, top, bottom margins.
func (table *Table) SetMargins(left, right, top, bottom float64) {
table.margins.left = left
table.margins.right = right
table.margins.top = top
table.margins.bottom = bottom
}
// GetMargins returns the left, right, top, bottom Margins.
func (table *Table) GetMargins() (float64, float64, float64, float64) {
return table.margins.left, table.margins.right, table.margins.top, table.margins.bottom
}
// SetRowHeight sets the height for a specified row.
func (table *Table) SetRowHeight(row int, h float64) error {
if row < 1 || row > len(table.rowHeights) {
return errors.New("range check error")
}
table.rowHeights[row-1] = h
return nil
}
// CurRow returns the currently active cell's row number.
func (table *Table) CurRow() int {
curRow := (table.curCell-1)/table.cols + 1
return curRow
}
// CurCol returns the currently active cell's column number.
func (table *Table) CurCol() int {
curCol := (table.curCell-1)%(table.cols) + 1
return curCol
}
// 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) {
table.positioning = positionAbsolute
table.xPos = x
table.yPos = y
}
// 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{}
block := NewBlock(ctx.PageWidth, ctx.PageHeight)
origCtx := ctx
if table.positioning.isAbsolute() {
ctx.X = table.xPos
ctx.Y = table.yPos
} else {
// Relative mode: add margins.
ctx.X += table.margins.left
ctx.Y += table.margins.top
ctx.Width -= table.margins.left + table.margins.right
ctx.Height -= table.margins.bottom + table.margins.top
}
tableWidth := ctx.Width
// Store table's upper left corner.
ulX := ctx.X
ulY := ctx.Y
ctx.Height = ctx.PageHeight - ctx.Y - ctx.Margins.bottom
origHeight := ctx.Height
// Start row keeps track of starting row (wraps to 0 on new page).
startrow := 0
// Prepare for drawing: Calculate cell dimensions, row, cell heights.
for _, cell := range table.cells {
// Get total width fraction
wf := float64(0.0)
for i := 0; i < cell.colspan; i++ {
wf += table.colWidths[cell.col+i-1]
}
// Get x pos relative to table upper left corner.
xrel := float64(0.0)
for i := 0; i < cell.col-1; i++ {
xrel += table.colWidths[i] * tableWidth
}
// Get y pos relative to table upper left corner.
yrel := float64(0.0)
for i := startrow; i < cell.row-1; i++ {
yrel += table.rowHeights[i]
}
// Calculate the width out of available width.
w := wf * tableWidth
// Get total height.
h := float64(0.0)
for i := 0; i < cell.rowspan; i++ {
h += table.rowHeights[cell.row+i-1]
}
// For text: Calculate width, height, wrapping within available space if specified.
switch t := cell.content.(type) {
case *Paragraph:
p := t
if p.enableWrap {
p.SetWidth(w - cell.indent)
}
newh := p.Height() + p.margins.bottom + p.margins.bottom
newh += 0.5 * p.fontSize * p.lineHeight // TODO: Make the top margin configurable?
if newh > h {
diffh := newh - h
// Add diff to last row.
table.rowHeights[cell.row+cell.rowspan-2] += diffh
}
case *StyledParagraph:
sp := t
if sp.enableWrap {
sp.SetWidth(w - cell.indent)
}
newh := sp.Height() + sp.margins.top + sp.margins.bottom
newh += 0.5 * sp.getTextHeight() // TODO: Make the top margin configurable?
if newh > h {
diffh := newh - h
// Add diff to last row.
table.rowHeights[cell.row+cell.rowspan-2] += diffh
}
case *Image:
img := t
newh := img.Height() + img.margins.top + img.margins.bottom
if newh > h {
diffh := newh - h
// Add diff to last row.
table.rowHeights[cell.row+cell.rowspan-2] += diffh
}
case *Division:
div := t
ctx := DrawContext{
X: xrel,
Y: yrel,
Width: w,
}
// Mock call to generate page blocks.
divBlocks, updCtx, err := div.GeneratePageBlocks(ctx)
if err != nil {
return nil, ctx, err
}
if len(divBlocks) > 1 {
// Wraps across page, make cell reach all the way to bottom of current page.
newh := ctx.Height - h
if newh > h {
diffh := newh - h
// Add diff to last row.
table.rowHeights[cell.row+cell.rowspan-2] += diffh
}
}
newh := div.Height() + div.margins.top + div.margins.bottom
_ = updCtx
// Get available width and height.
if newh > h {
diffh := newh - h
// Add diff to last row.
table.rowHeights[cell.row+cell.rowspan-2] += diffh
}
}
}
// Draw cells.
// row height, cell height
for _, cell := range table.cells {
// Get total width fraction
wf := float64(0.0)
for i := 0; i < cell.colspan; i++ {
wf += table.colWidths[cell.col+i-1]
}
// Get x pos relative to table upper left corner.
xrel := float64(0.0)
for i := 0; i < cell.col-1; i++ {
xrel += table.colWidths[i] * tableWidth
}
// Get y pos relative to table upper left corner.
yrel := float64(0.0)
for i := startrow; i < cell.row-1; i++ {
yrel += table.rowHeights[i]
}
// Calculate the width out of available width.
w := wf * tableWidth
// Get total height.
h := float64(0.0)
for i := 0; i < cell.rowspan; i++ {
h += table.rowHeights[cell.row+i-1]
}
ctx.Height = origHeight - yrel
if h > ctx.Height {
// Go to next page.
blocks = append(blocks, block)
block = NewBlock(ctx.PageWidth, ctx.PageHeight)
ulX = ctx.Margins.left
ulY = ctx.Margins.top
ctx.Height = ctx.PageHeight - ctx.Margins.top - ctx.Margins.bottom
startrow = cell.row - 1
yrel = 0
}
// Height should be how much space there is left of the page.
ctx.Width = w
ctx.X = ulX + xrel
ctx.Y = ulY + yrel
if cell.backgroundColor != nil {
// Draw background (fill)
rect := NewRectangle(ctx.X, ctx.Y, w, h)
r := cell.backgroundColor.R()
g := cell.backgroundColor.G()
b := cell.backgroundColor.B()
rect.SetFillColor(ColorRGBFromArithmetic(r, g, b))
if cell.borderStyle != CellBorderStyleNone {
// and border.
rect.SetBorderWidth(cell.borderWidth)
r := cell.borderColor.R()
g := cell.borderColor.G()
b := cell.borderColor.B()
rect.SetBorderColor(ColorRGBFromArithmetic(r, g, b))
} else {
rect.SetBorderWidth(0)
}
err := block.Draw(rect)
if err != nil {
common.Log.Debug("error: %v\n", err)
}
} else if cell.borderStyle != CellBorderStyleNone {
// Draw border (no fill).
rect := NewRectangle(ctx.X, ctx.Y, w, h)
rect.SetBorderWidth(cell.borderWidth)
r := cell.borderColor.R()
g := cell.borderColor.G()
b := cell.borderColor.B()
rect.SetBorderColor(ColorRGBFromArithmetic(r, g, b))
err := block.Draw(rect)
if err != nil {
common.Log.Debug("error: %v\n", err)
}
}
if cell.content != nil {
// Account for horizontal alignment:
cw := cell.content.Width() // content width.
switch cell.horizontalAlignment {
case CellHorizontalAlignmentLeft:
// Account for indent.
ctx.X += cell.indent
ctx.Width -= cell.indent
case CellHorizontalAlignmentCenter:
// Difference between available space and content space.
dw := w - cw
if dw > 0 {
ctx.X += dw / 2
ctx.Width -= dw / 2
}
case CellHorizontalAlignmentRight:
if w > cw {
ctx.X = ctx.X + w - cw - cell.indent
ctx.Width = cw
}
}
// Account for vertical alignment.
ch := cell.content.Height() // content height.
switch cell.verticalAlignment {
case CellVerticalAlignmentTop:
// Default: do nothing.
case CellVerticalAlignmentMiddle:
dh := h - ch
if dh > 0 {
ctx.Y += dh / 2
ctx.Height -= dh / 2
}
case CellVerticalAlignmentBottom:
if h > ch {
ctx.Y = ctx.Y + h - ch
ctx.Height = ch
}
}
err := block.DrawWithContext(cell.content, ctx)
if err != nil {
common.Log.Debug("error: %v\n", err)
}
}
ctx.Y += h
}
blocks = append(blocks, block)
if table.positioning.isAbsolute() {
return blocks, origCtx, nil
}
// Relative mode.
// Move back X after.
ctx.X = origCtx.X
// Return original width.
ctx.Width = origCtx.Width
// Add the bottom margin.
ctx.Y += table.margins.bottom
return blocks, ctx, nil
}
// CellBorderStyle defines the table cell's border style.
type CellBorderStyle int
// Currently supported table styles are: None (no border) and boxed (line along each side).
const (
// No border
CellBorderStyleNone CellBorderStyle = iota
// Borders along all sides (boxed).
CellBorderStyleBox
)
// CellHorizontalAlignment defines the table cell's horizontal alignment.
type CellHorizontalAlignment int
// Table cells have three horizontal alignment modes: left, center and right.
const (
// Align cell content on the left (with specified indent); unused space on the right.
CellHorizontalAlignmentLeft CellHorizontalAlignment = iota
// Align cell content in the middle (unused space divided equally on the left/right).
CellHorizontalAlignmentCenter
// Align the cell content on the right; unsued space on the left.
CellHorizontalAlignmentRight
)
// CellVerticalAlignment defines the table cell's vertical alignment.
type CellVerticalAlignment int
// Table cells have three vertical alignment modes: top, middle and bottom.
const (
// Align cell content vertically to the top; unused space below.
CellVerticalAlignmentTop CellVerticalAlignment = iota
// Align cell content in the middle; unused space divided equally above and below.
CellVerticalAlignmentMiddle
// Align cell content on the bottom; unused space above.
CellVerticalAlignmentBottom
)
// TableCell defines a table cell which can contain a Drawable as content.
type TableCell struct {
// Background
backgroundColor *model.PdfColorDeviceRGB
// Border
borderStyle CellBorderStyle
borderColor *model.PdfColorDeviceRGB
borderWidth float64
// The row and column which the cell starts from.
row, col int
// Row, column span.
rowspan int
colspan int
// Each cell can contain 1 drawable.
content VectorDrawable
// Alignment
horizontalAlignment CellHorizontalAlignment
verticalAlignment CellVerticalAlignment
// Left indent.
indent float64
// Table reference
table *Table
}
// NewCell makes a new cell and inserts into the table at current position in the table.
func (table *Table) NewCell() *TableCell {
table.curCell++
curRow := (table.curCell-1)/table.cols + 1
for curRow > table.rows {
table.rows++
table.rowHeights = append(table.rowHeights, table.defaultRowHeight)
}
curCol := (table.curCell-1)%(table.cols) + 1
cell := &TableCell{}
cell.row = curRow
cell.col = curCol
// Default left indent
cell.indent = 5
cell.borderStyle = CellBorderStyleNone
cell.borderColor = model.NewPdfColorDeviceRGB(0, 0, 0)
// Alignment defaults.
cell.horizontalAlignment = CellHorizontalAlignmentLeft
cell.verticalAlignment = CellVerticalAlignmentTop
cell.rowspan = 1
cell.colspan = 1
table.cells = append(table.cells, cell)
// Keep reference to the table.
cell.table = table
return cell
}
// SkipCells skips over a specified number of cells in the table.
func (table *Table) SkipCells(num int) {
if num < 0 {
common.Log.Debug("Table: cannot skip back to previous cells")
return
}
table.curCell += num
}
// SkipRows skips over a specified number of rows in the table.
func (table *Table) SkipRows(num int) {
ncells := num*table.cols - 1
if ncells < 0 {
common.Log.Debug("Table: cannot skip back to previous cells")
return
}
table.curCell += ncells
}
// SkipOver skips over a specified number of rows and cols.
func (table *Table) SkipOver(rows, cols int) {
ncells := rows*table.cols + cols - 1
if ncells < 0 {
common.Log.Debug("Table: cannot skip back to previous cells")
return
}
table.curCell += ncells
}
// SetIndent sets the cell's left indent.
func (cell *TableCell) SetIndent(indent float64) {
cell.indent = indent
}
// SetHorizontalAlignment sets the cell's horizontal alignment of content.
// Can be one of:
// - CellHorizontalAlignmentLeft
// - CellHorizontalAlignmentCenter
// - CellHorizontalAlignmentRight
func (cell *TableCell) SetHorizontalAlignment(halign CellHorizontalAlignment) {
cell.horizontalAlignment = halign
}
// SetVerticalAlignment set the cell's vertical alignment of content.
// Can be one of:
// - CellHorizontalAlignmentTop
// - CellHorizontalAlignmentMiddle
// - CellHorizontalAlignmentBottom
func (cell *TableCell) SetVerticalAlignment(valign CellVerticalAlignment) {
cell.verticalAlignment = valign
}
// SetBorder sets the cell's border style.
func (cell *TableCell) SetBorder(style CellBorderStyle, width float64) {
cell.borderStyle = style
cell.borderWidth = width
}
// SetBorderColor sets the cell's border color.
func (cell *TableCell) SetBorderColor(col Color) {
cell.borderColor = model.NewPdfColorDeviceRGB(col.ToRGB())
}
// SetBackgroundColor sets the cell's background color.
func (cell *TableCell) SetBackgroundColor(col Color) {
cell.backgroundColor = model.NewPdfColorDeviceRGB(col.ToRGB())
}
// Width returns the cell's width based on the input draw context.
func (cell *TableCell) Width(ctx DrawContext) float64 {
fraction := float64(0.0)
for j := 0; j < cell.colspan; j++ {
fraction += cell.table.colWidths[cell.col+j-1]
}
w := ctx.Width * fraction
return w
}
// SetContent sets the cell's content. The content is a VectorDrawable, i.e. a Drawable with a known height and width.
// The currently supported VectorDrawable is: *Paragraph, *StyledParagraph.
func (cell *TableCell) SetContent(vd VectorDrawable) error {
switch t := vd.(type) {
case *Paragraph:
if t.defaultWrap {
// Default paragraph settings in table: no wrapping.
t.enableWrap = false // No wrapping.
}
cell.content = vd
case *StyledParagraph:
if t.defaultWrap {
// Default styled paragraph settings in table: no wrapping.
t.enableWrap = false // No wrapping.
}
cell.content = vd
case *Image:
cell.content = vd
case *Division:
cell.content = vd
default:
common.Log.Debug("error: unsupported cell content type %T\n", vd)
return errors.New("type check error")
}
return nil
}

View File

@@ -0,0 +1,38 @@
package creator
import (
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model/fonts"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model/textencoding"
)
// TextStyle is a collection of properties that can be assigned to a chunk of text.
type TextStyle struct {
// The color of the text.
Color Color
// The font the text will use.
Font fonts.Font
// The size of the font.
FontSize float64
}
// NewTextStyle creates a new text style object which can be used with chunks
// of text. Uses default parameters: Helvetica, WinAnsiEncoding and wrap
// enabled with a wrap width of 100 points.
func NewTextStyle() TextStyle {
font := fonts.NewFontHelvetica()
font.SetEncoder(textencoding.NewWinAnsiTextEncoder())
return TextStyle{
Color: ColorRGBFrom8bit(0, 0, 0),
Font: font,
FontSize: 10,
}
}
// TextChunk represents a chunk of text along with a particular style.
type TextChunk struct {
Text string
Style TextStyle
}

View File

@@ -0,0 +1,38 @@
package creator
// TableOfContents provides an overview over chapters and subchapters when creating a document with Creator.
type TableOfContents struct {
entries []TableOfContentsEntry
}
// Make a new table of contents.
func newTableOfContents() *TableOfContents {
toc := TableOfContents{}
toc.entries = []TableOfContentsEntry{}
return &toc
}
// Entries returns the table of content entries.
func (toc *TableOfContents) Entries() []TableOfContentsEntry {
return toc.entries
}
// Add a TOC entry.
func (toc *TableOfContents) add(title string, chapter, subchapter, pageNum int) {
entry := TableOfContentsEntry{}
entry.Title = title
entry.Chapter = chapter
entry.Subchapter = subchapter
entry.PageNumber = pageNum
toc.entries = append(toc.entries, entry)
}
// TableOfContentsEntry defines a single entry in the TableOfContents.
// Each entry has a title, chapter number, sub chapter (0 if chapter) and the page number.
type TableOfContentsEntry struct {
Title string
Chapter int
Subchapter int // 0 if chapter
PageNumber int // Page number
}

View File

@@ -0,0 +1,3 @@
package extractor
var isTesting = false

View File

@@ -0,0 +1,23 @@
package extractor
import "gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model"
// Extractor stores and offers functionality for extracting content from PDF pages.
type Extractor struct {
contents string
resources *model.PdfPageResources
}
// New returns an Extractor instance for extracting content from the input PDF page.
func New(page *model.PdfPage) (*Extractor, error) {
contents, err := page.GetAllContentStreams()
if err != nil {
return nil, err
}
e := &Extractor{}
e.contents = contents
e.resources = page.Resources
return e, nil
}

View File

@@ -0,0 +1,225 @@
package extractor
import (
"bytes"
"errors"
"fmt"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/cmap"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/contentstream"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model"
)
// ExtractText processes and extracts all text data in content streams and returns as a string. Takes into
// account character encoding via CMaps in the PDF file.
// The text is processed linearly e.g. in the order in which it appears. A best effort is done to add
// spaces and newlines.
func (e *Extractor) ExtractText() (string, error) {
var buf bytes.Buffer
cstreamParser := contentstream.NewContentStreamParser(e.contents)
operations, err := cstreamParser.Parse()
if err != nil {
return buf.String(), err
}
processor := contentstream.NewContentStreamProcessor(*operations)
var codemap *cmap.CMap
inText := false
xPos, yPos := float64(-1), float64(-1)
processor.AddHandler(contentstream.HandlerConditionEnumAllOperands, "",
func(op *contentstream.ContentStreamOperation, gs contentstream.GraphicsState, resources *model.PdfPageResources) error {
operand := op.Operand
switch operand {
case "BT":
inText = true
case "ET":
inText = false
case "Tf":
if !inText {
common.Log.Debug("Tf operand outside text")
return nil
}
if len(op.Params) != 2 {
common.Log.Debug("error Tf should only get 2 input params, got %d", len(op.Params))
return errors.New("Incorrect parameter count")
}
codemap = nil
fontName, ok := op.Params[0].(*core.PdfObjectName)
if !ok {
common.Log.Debug("error Tf font input not a name")
return errors.New("Tf range error")
}
if resources == nil {
return nil
}
fontObj, found := resources.GetFontByName(*fontName)
if !found {
common.Log.Debug("Font not found...")
return errors.New("Font not in resources")
}
fontObj = core.TraceToDirectObject(fontObj)
if fontDict, isDict := fontObj.(*core.PdfObjectDictionary); isDict {
toUnicode := fontDict.Get("ToUnicode")
if toUnicode != nil {
toUnicode = core.TraceToDirectObject(toUnicode)
toUnicodeStream, ok := toUnicode.(*core.PdfObjectStream)
if !ok {
return errors.New("invalid ToUnicode entry - not a stream")
}
decoded, err := core.DecodeStream(toUnicodeStream)
if err != nil {
return err
}
codemap, err = cmap.LoadCmapFromData(decoded)
if err != nil {
return err
}
}
}
case "T*":
if !inText {
common.Log.Debug("T* operand outside text")
return nil
}
buf.WriteString("\n")
case "Td", "TD":
if !inText {
common.Log.Debug("Td/TD operand outside text")
return nil
}
// Params: [tx ty], corresponeds to Tm=Tlm=[1 0 0;0 1 0;tx ty 1]*Tm
if len(op.Params) != 2 {
common.Log.Debug("Td/TD invalid arguments")
return nil
}
tx, err := getNumberAsFloat(op.Params[0])
if err != nil {
common.Log.Debug("Td Float parse error")
return nil
}
ty, err := getNumberAsFloat(op.Params[1])
if err != nil {
common.Log.Debug("Td Float parse error")
return nil
}
if tx > 0 {
buf.WriteString(" ")
}
if ty < 0 {
// TODO: More flexible space characters?
buf.WriteString("\n")
}
case "Tm":
if !inText {
common.Log.Debug("Tm operand outside text")
return nil
}
// Params: a,b,c,d,e,f as in Tm = [a b 0; c d 0; e f 1].
// The last two (e,f) represent translation.
if len(op.Params) != 6 {
return errors.New("Tm: Invalid number of inputs")
}
xfloat, ok := op.Params[4].(*core.PdfObjectFloat)
if !ok {
xint, ok := op.Params[4].(*core.PdfObjectInteger)
if !ok {
return nil
}
xfloat = core.MakeFloat(float64(*xint))
}
yfloat, ok := op.Params[5].(*core.PdfObjectFloat)
if !ok {
yint, ok := op.Params[5].(*core.PdfObjectInteger)
if !ok {
return nil
}
yfloat = core.MakeFloat(float64(*yint))
}
if yPos == -1 {
yPos = float64(*yfloat)
} else if yPos > float64(*yfloat) {
buf.WriteString("\n")
xPos = float64(*xfloat)
yPos = float64(*yfloat)
return nil
}
if xPos == -1 {
xPos = float64(*xfloat)
} else if xPos < float64(*xfloat) {
buf.WriteString("\t")
xPos = float64(*xfloat)
}
case "TJ":
if !inText {
common.Log.Debug("TJ operand outside text")
return nil
}
if len(op.Params) < 1 {
return nil
}
paramList, ok := op.Params[0].(*core.PdfObjectArray)
if !ok {
return fmt.Errorf("invalid parameter type, no array (%T)", op.Params[0])
}
for _, obj := range *paramList {
switch v := obj.(type) {
case *core.PdfObjectString:
if codemap != nil {
buf.WriteString(codemap.CharcodeBytesToUnicode([]byte(*v)))
} else {
buf.WriteString(string(*v))
}
case *core.PdfObjectFloat:
if *v < -100 {
buf.WriteString(" ")
}
case *core.PdfObjectInteger:
if *v < -100 {
buf.WriteString(" ")
}
}
}
case "Tj":
if !inText {
common.Log.Debug("Tj operand outside text")
return nil
}
if len(op.Params) < 1 {
return nil
}
param, ok := op.Params[0].(*core.PdfObjectString)
if !ok {
return fmt.Errorf("invalid parameter type, not string (%T)", op.Params[0])
}
if codemap != nil {
buf.WriteString(codemap.CharcodeBytesToUnicode([]byte(*param)))
} else {
buf.WriteString(string(*param))
}
}
return nil
})
err = processor.Process(e.resources)
if err != nil {
common.Log.Error("error processing: %v", err)
return buf.String(), err
}
return buf.String(), nil
}

View File

@@ -0,0 +1,20 @@
package extractor
import (
"errors"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
)
// getNumberAsFloat can retrieve numeric values from PdfObject (both integer/float).
func getNumberAsFloat(obj core.PdfObject) (float64, error) {
if fObj, ok := obj.(*core.PdfObjectFloat); ok {
return float64(*fObj), nil
}
if iObj, ok := obj.(*core.PdfObjectInteger); ok {
return float64(*iObj), nil
}
return 0, errors.New("Not a number")
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,14 @@
package model
import (
"errors"
)
var (
ErrRequiredAttributeMissing = errors.New("required attribute missing")
ErrInvalidAttribute = errors.New("invalid attribute")
ErrTypeError = errors.New("type check error")
// ErrRangeError typically occurs when an input parameter is out of range or has invalid value.
ErrRangeError = errors.New("range check error")
)

393
internal/pdf/model/font.go Normal file
View File

@@ -0,0 +1,393 @@
package model
import (
"errors"
"os"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model/fonts"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model/textencoding"
)
// The PdfFont structure represents an underlying font structure which can be of type:
// - Type0
// - Type1
// - TrueType
// etc.
type PdfFont struct {
context any // The underlying font: Type0, Type1, Truetype, etc..
}
// Set the encoding for the underlying font.
func (font PdfFont) SetEncoder(encoder textencoding.TextEncoder) {
switch t := font.context.(type) {
case *pdfFontTrueType:
t.SetEncoder(encoder)
}
}
func (font PdfFont) GetGlyphCharMetrics(glyph string) (fonts.CharMetrics, bool) {
switch t := font.context.(type) {
case *pdfFontTrueType:
return t.GetGlyphCharMetrics(glyph)
}
return fonts.CharMetrics{}, false
}
func (font PdfFont) ToPdfObject() core.PdfObject {
switch f := font.context.(type) {
case *pdfFontTrueType:
return f.ToPdfObject()
}
// If not supported, return null..
common.Log.Debug("Unsupported font (%T) - returning null object", font.context)
return core.MakeNull()
}
type pdfFontTrueType struct {
Encoder textencoding.TextEncoder
firstChar int
lastChar int
charWidths []float64
// Subtype shall be TrueType.
// Encoding is subject to limitations that are described in 9.6.6, "Character Encoding".
// BaseFont is derived differently.
BaseFont core.PdfObject
FirstChar core.PdfObject
LastChar core.PdfObject
Widths core.PdfObject
FontDescriptor *PdfFontDescriptor
Encoding core.PdfObject
ToUnicode core.PdfObject
container *core.PdfIndirectObject
}
func (font pdfFontTrueType) SetEncoder(encoder textencoding.TextEncoder) {
font.Encoder = encoder
}
func (font pdfFontTrueType) GetGlyphCharMetrics(glyph string) (fonts.CharMetrics, bool) {
metrics := fonts.CharMetrics{}
code, found := font.Encoder.GlyphToCharcode(glyph)
if !found {
return metrics, false
}
if int(code) < font.firstChar {
common.Log.Debug("Code lower than firstchar (%d < %d)", code, font.firstChar)
return metrics, false
}
if int(code) > font.lastChar {
common.Log.Debug("Code higher than lastchar (%d < %d)", code, font.lastChar)
return metrics, false
}
index := int(code) - font.firstChar
if index >= len(font.charWidths) {
common.Log.Debug("Code outside of widths range")
return metrics, false
}
width := font.charWidths[index]
metrics.Wx = width
return metrics, true
}
func (ftt *pdfFontTrueType) ToPdfObject() core.PdfObject {
if ftt.container == nil {
ftt.container = &core.PdfIndirectObject{}
}
d := core.MakeDict()
ftt.container.PdfObject = d
d.Set("Type", core.MakeName("Font"))
d.Set("Subtype", core.MakeName("TrueType"))
if ftt.BaseFont != nil {
d.Set("BaseFont", ftt.BaseFont)
}
if ftt.FirstChar != nil {
d.Set("FirstChar", ftt.FirstChar)
}
if ftt.LastChar != nil {
d.Set("LastChar", ftt.LastChar)
}
if ftt.Widths != nil {
d.Set("Widths", ftt.Widths)
}
if ftt.FontDescriptor != nil {
d.Set("FontDescriptor", ftt.FontDescriptor.ToPdfObject())
}
if ftt.Encoding != nil {
d.Set("Encoding", ftt.Encoding)
}
if ftt.ToUnicode != nil {
d.Set("ToUnicode", ftt.ToUnicode)
}
return ftt.container
}
func NewPdfFontFromTTFFile(filePath string) (*PdfFont, error) {
ttf, err := fonts.TtfParse(filePath)
if err != nil {
common.Log.Debug("error loading ttf font: %v", err)
return nil, err
}
truefont := &pdfFontTrueType{}
truefont.Encoder = textencoding.NewWinAnsiTextEncoder()
truefont.firstChar = 32
truefont.lastChar = 255
truefont.BaseFont = core.MakeName(ttf.PostScriptName)
truefont.FirstChar = core.MakeInteger(32)
truefont.LastChar = core.MakeInteger(255)
k := 1000.0 / float64(ttf.UnitsPerEm)
if len(ttf.Widths) <= 0 {
return nil, errors.New("missing required attribute (Widths)")
}
missingWidth := k * float64(ttf.Widths[0])
vals := []float64{}
for charcode := 32; charcode <= 255; charcode++ {
runeVal, found := truefont.Encoder.CharcodeToRune(byte(charcode))
if !found {
common.Log.Debug("Rune not found (charcode: %d)", charcode)
vals = append(vals, missingWidth)
continue
}
pos, ok := ttf.Chars[uint16(runeVal)]
if !ok {
common.Log.Debug("Rune not in TTF Chars")
vals = append(vals, missingWidth)
continue
}
w := k * float64(ttf.Widths[pos])
vals = append(vals, w)
}
truefont.Widths = &core.PdfIndirectObject{PdfObject: core.MakeArrayFromFloats(vals)}
if len(vals) < (255 - 32 + 1) {
common.Log.Debug("invalid length of widths, %d < %d", len(vals), 255-32+1)
return nil, errors.New("range check error")
}
truefont.charWidths = vals[:255-32+1]
// Default.
// XXX/FIXME TODO: Only use the encoder object.
truefont.Encoding = core.MakeName("WinAnsiEncoding")
descriptor := &PdfFontDescriptor{}
descriptor.Ascent = core.MakeFloat(k * float64(ttf.TypoAscender))
descriptor.Descent = core.MakeFloat(k * float64(ttf.TypoDescender))
descriptor.CapHeight = core.MakeFloat(k * float64(ttf.CapHeight))
descriptor.FontBBox = core.MakeArrayFromFloats([]float64{k * float64(ttf.Xmin), k * float64(ttf.Ymin), k * float64(ttf.Xmax), k * float64(ttf.Ymax)})
descriptor.ItalicAngle = core.MakeFloat(float64(ttf.ItalicAngle))
descriptor.MissingWidth = core.MakeFloat(k * float64(ttf.Widths[0]))
ttfBytes, err := os.ReadFile(filePath)
if err != nil {
common.Log.Debug("Unable to read file contents: %v", err)
return nil, err
}
// XXX/TODO: Encode the file...
stream, err := core.MakeStream(ttfBytes, core.NewFlateEncoder())
if err != nil {
common.Log.Debug("Unable to make stream: %v", err)
return nil, err
}
stream.PdfObjectDictionary.Set("Length1", core.MakeInteger(int64(len(ttfBytes))))
descriptor.FontFile2 = stream
if ttf.Bold {
descriptor.StemV = core.MakeInteger(120)
} else {
descriptor.StemV = core.MakeInteger(70)
}
// Flags.
flags := 1 << 5
if ttf.IsFixedPitch {
flags |= 1
}
if ttf.ItalicAngle != 0 {
flags |= 1 << 6
}
descriptor.Flags = core.MakeInteger(int64(flags))
// Build Font.
truefont.FontDescriptor = descriptor
font := &PdfFont{}
font.context = truefont
return font, nil
}
// Font descriptors specifies metrics and other attributes of a font.
type PdfFontDescriptor struct {
FontName core.PdfObject
FontFamily core.PdfObject
FontStretch core.PdfObject
FontWeight core.PdfObject
Flags core.PdfObject
FontBBox core.PdfObject
ItalicAngle core.PdfObject
Ascent core.PdfObject
Descent core.PdfObject
Leading core.PdfObject
CapHeight core.PdfObject
XHeight core.PdfObject
StemV core.PdfObject
StemH core.PdfObject
AvgWidth core.PdfObject
MaxWidth core.PdfObject
MissingWidth core.PdfObject
FontFile core.PdfObject
FontFile2 core.PdfObject
FontFile3 core.PdfObject
CharSet core.PdfObject
// Additional entries for CIDFonts
Style core.PdfObject
Lang core.PdfObject
FD core.PdfObject
CIDSet core.PdfObject
// Container.
container *core.PdfIndirectObject
}
// Convert to a PDF dictionary inside an indirect object.
func (pfd *PdfFontDescriptor) ToPdfObject() core.PdfObject {
d := core.MakeDict()
if pfd.container == nil {
pfd.container = &core.PdfIndirectObject{}
}
pfd.container.PdfObject = d
d.Set("Type", core.MakeName("FontDescriptor"))
if pfd.FontName != nil {
d.Set("FontName", pfd.FontName)
}
if pfd.FontFamily != nil {
d.Set("FontFamily", pfd.FontFamily)
}
if pfd.FontStretch != nil {
d.Set("FontStretch", pfd.FontStretch)
}
if pfd.FontWeight != nil {
d.Set("FontWeight", pfd.FontWeight)
}
if pfd.Flags != nil {
d.Set("Flags", pfd.Flags)
}
if pfd.FontBBox != nil {
d.Set("FontBBox", pfd.FontBBox)
}
if pfd.ItalicAngle != nil {
d.Set("ItalicAngle", pfd.ItalicAngle)
}
if pfd.Ascent != nil {
d.Set("Ascent", pfd.Ascent)
}
if pfd.Descent != nil {
d.Set("Descent", pfd.Descent)
}
if pfd.Leading != nil {
d.Set("Leading", pfd.Leading)
}
if pfd.CapHeight != nil {
d.Set("CapHeight", pfd.CapHeight)
}
if pfd.XHeight != nil {
d.Set("XHeight", pfd.XHeight)
}
if pfd.StemV != nil {
d.Set("StemV", pfd.StemV)
}
if pfd.StemH != nil {
d.Set("StemH", pfd.StemH)
}
if pfd.AvgWidth != nil {
d.Set("AvgWidth", pfd.AvgWidth)
}
if pfd.MaxWidth != nil {
d.Set("MaxWidth", pfd.MaxWidth)
}
if pfd.MissingWidth != nil {
d.Set("MissingWidth", pfd.MissingWidth)
}
if pfd.FontFile != nil {
d.Set("FontFile", pfd.FontFile)
}
if pfd.FontFile2 != nil {
d.Set("FontFile2", pfd.FontFile2)
}
if pfd.FontFile3 != nil {
d.Set("FontFile3", pfd.FontFile3)
}
if pfd.CharSet != nil {
d.Set("CharSet", pfd.CharSet)
}
if pfd.Style != nil {
d.Set("FontName", pfd.FontName)
}
if pfd.Lang != nil {
d.Set("Lang", pfd.Lang)
}
if pfd.FD != nil {
d.Set("FD", pfd.FD)
}
if pfd.CIDSet != nil {
d.Set("CIDSet", pfd.CIDSet)
}
return pfd.container
}

View File

@@ -0,0 +1,342 @@
StartFontMetrics 4.1
Comment Copyright (c) 1989, 1990, 1991, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
Comment Creation Date: Mon Jun 23 16:28:00 1997
Comment UniqueID 43048
Comment VMusage 41139 52164
FontName Courier-Bold
FullName Courier Bold
FamilyName Courier
Weight Bold
ItalicAngle 0
IsFixedPitch true
CharacterSet ExtendedRoman
FontBBox -113 -250 749 801
UnderlinePosition -100
UnderlineThickness 50
Version 003.000
Notice Copyright (c) 1989, 1990, 1991, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
EncodingScheme AdobeStandardEncoding
CapHeight 562
XHeight 439
Ascender 629
Descender -157
StdHW 84
StdVW 106
StartCharMetrics 315
C 32 ; WX 600 ; N space ; B 0 0 0 0 ;
C 33 ; WX 600 ; N exclam ; B 202 -15 398 572 ;
C 34 ; WX 600 ; N quotedbl ; B 135 277 465 562 ;
C 35 ; WX 600 ; N numbersign ; B 56 -45 544 651 ;
C 36 ; WX 600 ; N dollar ; B 82 -126 519 666 ;
C 37 ; WX 600 ; N percent ; B 5 -15 595 616 ;
C 38 ; WX 600 ; N ampersand ; B 36 -15 546 543 ;
C 39 ; WX 600 ; N quoteright ; B 171 277 423 562 ;
C 40 ; WX 600 ; N parenleft ; B 219 -102 461 616 ;
C 41 ; WX 600 ; N parenright ; B 139 -102 381 616 ;
C 42 ; WX 600 ; N asterisk ; B 91 219 509 601 ;
C 43 ; WX 600 ; N plus ; B 71 39 529 478 ;
C 44 ; WX 600 ; N comma ; B 123 -111 393 174 ;
C 45 ; WX 600 ; N hyphen ; B 100 203 500 313 ;
C 46 ; WX 600 ; N period ; B 192 -15 408 171 ;
C 47 ; WX 600 ; N slash ; B 98 -77 502 626 ;
C 48 ; WX 600 ; N zero ; B 87 -15 513 616 ;
C 49 ; WX 600 ; N one ; B 81 0 539 616 ;
C 50 ; WX 600 ; N two ; B 61 0 499 616 ;
C 51 ; WX 600 ; N three ; B 63 -15 501 616 ;
C 52 ; WX 600 ; N four ; B 53 0 507 616 ;
C 53 ; WX 600 ; N five ; B 70 -15 521 601 ;
C 54 ; WX 600 ; N six ; B 90 -15 521 616 ;
C 55 ; WX 600 ; N seven ; B 55 0 494 601 ;
C 56 ; WX 600 ; N eight ; B 83 -15 517 616 ;
C 57 ; WX 600 ; N nine ; B 79 -15 510 616 ;
C 58 ; WX 600 ; N colon ; B 191 -15 407 425 ;
C 59 ; WX 600 ; N semicolon ; B 123 -111 408 425 ;
C 60 ; WX 600 ; N less ; B 66 15 523 501 ;
C 61 ; WX 600 ; N equal ; B 71 118 529 398 ;
C 62 ; WX 600 ; N greater ; B 77 15 534 501 ;
C 63 ; WX 600 ; N question ; B 98 -14 501 580 ;
C 64 ; WX 600 ; N at ; B 16 -15 584 616 ;
C 65 ; WX 600 ; N A ; B -9 0 609 562 ;
C 66 ; WX 600 ; N B ; B 30 0 573 562 ;
C 67 ; WX 600 ; N C ; B 22 -18 560 580 ;
C 68 ; WX 600 ; N D ; B 30 0 594 562 ;
C 69 ; WX 600 ; N E ; B 25 0 560 562 ;
C 70 ; WX 600 ; N F ; B 39 0 570 562 ;
C 71 ; WX 600 ; N G ; B 22 -18 594 580 ;
C 72 ; WX 600 ; N H ; B 20 0 580 562 ;
C 73 ; WX 600 ; N I ; B 77 0 523 562 ;
C 74 ; WX 600 ; N J ; B 37 -18 601 562 ;
C 75 ; WX 600 ; N K ; B 21 0 599 562 ;
C 76 ; WX 600 ; N L ; B 39 0 578 562 ;
C 77 ; WX 600 ; N M ; B -2 0 602 562 ;
C 78 ; WX 600 ; N N ; B 8 -12 610 562 ;
C 79 ; WX 600 ; N O ; B 22 -18 578 580 ;
C 80 ; WX 600 ; N P ; B 48 0 559 562 ;
C 81 ; WX 600 ; N Q ; B 32 -138 578 580 ;
C 82 ; WX 600 ; N R ; B 24 0 599 562 ;
C 83 ; WX 600 ; N S ; B 47 -22 553 582 ;
C 84 ; WX 600 ; N T ; B 21 0 579 562 ;
C 85 ; WX 600 ; N U ; B 4 -18 596 562 ;
C 86 ; WX 600 ; N V ; B -13 0 613 562 ;
C 87 ; WX 600 ; N W ; B -18 0 618 562 ;
C 88 ; WX 600 ; N X ; B 12 0 588 562 ;
C 89 ; WX 600 ; N Y ; B 12 0 589 562 ;
C 90 ; WX 600 ; N Z ; B 62 0 539 562 ;
C 91 ; WX 600 ; N bracketleft ; B 245 -102 475 616 ;
C 92 ; WX 600 ; N backslash ; B 99 -77 503 626 ;
C 93 ; WX 600 ; N bracketright ; B 125 -102 355 616 ;
C 94 ; WX 600 ; N asciicircum ; B 108 250 492 616 ;
C 95 ; WX 600 ; N underscore ; B 0 -125 600 -75 ;
C 96 ; WX 600 ; N quoteleft ; B 178 277 428 562 ;
C 97 ; WX 600 ; N a ; B 35 -15 570 454 ;
C 98 ; WX 600 ; N b ; B 0 -15 584 626 ;
C 99 ; WX 600 ; N c ; B 40 -15 545 459 ;
C 100 ; WX 600 ; N d ; B 20 -15 591 626 ;
C 101 ; WX 600 ; N e ; B 40 -15 563 454 ;
C 102 ; WX 600 ; N f ; B 83 0 547 626 ; L i fi ; L l fl ;
C 103 ; WX 600 ; N g ; B 30 -146 580 454 ;
C 104 ; WX 600 ; N h ; B 5 0 592 626 ;
C 105 ; WX 600 ; N i ; B 77 0 523 658 ;
C 106 ; WX 600 ; N j ; B 63 -146 440 658 ;
C 107 ; WX 600 ; N k ; B 20 0 585 626 ;
C 108 ; WX 600 ; N l ; B 77 0 523 626 ;
C 109 ; WX 600 ; N m ; B -22 0 626 454 ;
C 110 ; WX 600 ; N n ; B 18 0 592 454 ;
C 111 ; WX 600 ; N o ; B 30 -15 570 454 ;
C 112 ; WX 600 ; N p ; B -1 -142 570 454 ;
C 113 ; WX 600 ; N q ; B 20 -142 591 454 ;
C 114 ; WX 600 ; N r ; B 47 0 580 454 ;
C 115 ; WX 600 ; N s ; B 68 -17 535 459 ;
C 116 ; WX 600 ; N t ; B 47 -15 532 562 ;
C 117 ; WX 600 ; N u ; B -1 -15 569 439 ;
C 118 ; WX 600 ; N v ; B -1 0 601 439 ;
C 119 ; WX 600 ; N w ; B -18 0 618 439 ;
C 120 ; WX 600 ; N x ; B 6 0 594 439 ;
C 121 ; WX 600 ; N y ; B -4 -142 601 439 ;
C 122 ; WX 600 ; N z ; B 81 0 520 439 ;
C 123 ; WX 600 ; N braceleft ; B 160 -102 464 616 ;
C 124 ; WX 600 ; N bar ; B 255 -250 345 750 ;
C 125 ; WX 600 ; N braceright ; B 136 -102 440 616 ;
C 126 ; WX 600 ; N asciitilde ; B 71 153 530 356 ;
C 161 ; WX 600 ; N exclamdown ; B 202 -146 398 449 ;
C 162 ; WX 600 ; N cent ; B 66 -49 518 614 ;
C 163 ; WX 600 ; N sterling ; B 72 -28 558 611 ;
C 164 ; WX 600 ; N fraction ; B 25 -60 576 661 ;
C 165 ; WX 600 ; N yen ; B 10 0 590 562 ;
C 166 ; WX 600 ; N florin ; B -30 -131 572 616 ;
C 167 ; WX 600 ; N section ; B 83 -70 517 580 ;
C 168 ; WX 600 ; N currency ; B 54 49 546 517 ;
C 169 ; WX 600 ; N quotesingle ; B 227 277 373 562 ;
C 170 ; WX 600 ; N quotedblleft ; B 71 277 535 562 ;
C 171 ; WX 600 ; N guillemotleft ; B 8 70 553 446 ;
C 172 ; WX 600 ; N guilsinglleft ; B 141 70 459 446 ;
C 173 ; WX 600 ; N guilsinglright ; B 141 70 459 446 ;
C 174 ; WX 600 ; N fi ; B 12 0 593 626 ;
C 175 ; WX 600 ; N fl ; B 12 0 593 626 ;
C 177 ; WX 600 ; N endash ; B 65 203 535 313 ;
C 178 ; WX 600 ; N dagger ; B 106 -70 494 580 ;
C 179 ; WX 600 ; N daggerdbl ; B 106 -70 494 580 ;
C 180 ; WX 600 ; N periodcentered ; B 196 165 404 351 ;
C 182 ; WX 600 ; N paragraph ; B 6 -70 576 580 ;
C 183 ; WX 600 ; N bullet ; B 140 132 460 430 ;
C 184 ; WX 600 ; N quotesinglbase ; B 175 -142 427 143 ;
C 185 ; WX 600 ; N quotedblbase ; B 65 -142 529 143 ;
C 186 ; WX 600 ; N quotedblright ; B 61 277 525 562 ;
C 187 ; WX 600 ; N guillemotright ; B 47 70 592 446 ;
C 188 ; WX 600 ; N ellipsis ; B 26 -15 574 116 ;
C 189 ; WX 600 ; N perthousand ; B -113 -15 713 616 ;
C 191 ; WX 600 ; N questiondown ; B 99 -146 502 449 ;
C 193 ; WX 600 ; N grave ; B 132 508 395 661 ;
C 194 ; WX 600 ; N acute ; B 205 508 468 661 ;
C 195 ; WX 600 ; N circumflex ; B 103 483 497 657 ;
C 196 ; WX 600 ; N tilde ; B 89 493 512 636 ;
C 197 ; WX 600 ; N macron ; B 88 505 512 585 ;
C 198 ; WX 600 ; N breve ; B 83 468 517 631 ;
C 199 ; WX 600 ; N dotaccent ; B 230 498 370 638 ;
C 200 ; WX 600 ; N dieresis ; B 128 498 472 638 ;
C 202 ; WX 600 ; N ring ; B 198 481 402 678 ;
C 203 ; WX 600 ; N cedilla ; B 205 -206 387 0 ;
C 205 ; WX 600 ; N hungarumlaut ; B 68 488 588 661 ;
C 206 ; WX 600 ; N ogonek ; B 169 -199 400 0 ;
C 207 ; WX 600 ; N caron ; B 103 493 497 667 ;
C 208 ; WX 600 ; N emdash ; B -10 203 610 313 ;
C 225 ; WX 600 ; N AE ; B -29 0 602 562 ;
C 227 ; WX 600 ; N ordfeminine ; B 147 196 453 580 ;
C 232 ; WX 600 ; N Lslash ; B 39 0 578 562 ;
C 233 ; WX 600 ; N Oslash ; B 22 -22 578 584 ;
C 234 ; WX 600 ; N OE ; B -25 0 595 562 ;
C 235 ; WX 600 ; N ordmasculine ; B 147 196 453 580 ;
C 241 ; WX 600 ; N ae ; B -4 -15 601 454 ;
C 245 ; WX 600 ; N dotlessi ; B 77 0 523 439 ;
C 248 ; WX 600 ; N lslash ; B 77 0 523 626 ;
C 249 ; WX 600 ; N oslash ; B 30 -24 570 463 ;
C 250 ; WX 600 ; N oe ; B -18 -15 611 454 ;
C 251 ; WX 600 ; N germandbls ; B 22 -15 596 626 ;
C -1 ; WX 600 ; N Idieresis ; B 77 0 523 761 ;
C -1 ; WX 600 ; N eacute ; B 40 -15 563 661 ;
C -1 ; WX 600 ; N abreve ; B 35 -15 570 661 ;
C -1 ; WX 600 ; N uhungarumlaut ; B -1 -15 628 661 ;
C -1 ; WX 600 ; N ecaron ; B 40 -15 563 667 ;
C -1 ; WX 600 ; N Ydieresis ; B 12 0 589 761 ;
C -1 ; WX 600 ; N divide ; B 71 16 529 500 ;
C -1 ; WX 600 ; N Yacute ; B 12 0 589 784 ;
C -1 ; WX 600 ; N Acircumflex ; B -9 0 609 780 ;
C -1 ; WX 600 ; N aacute ; B 35 -15 570 661 ;
C -1 ; WX 600 ; N Ucircumflex ; B 4 -18 596 780 ;
C -1 ; WX 600 ; N yacute ; B -4 -142 601 661 ;
C -1 ; WX 600 ; N scommaaccent ; B 68 -250 535 459 ;
C -1 ; WX 600 ; N ecircumflex ; B 40 -15 563 657 ;
C -1 ; WX 600 ; N Uring ; B 4 -18 596 801 ;
C -1 ; WX 600 ; N Udieresis ; B 4 -18 596 761 ;
C -1 ; WX 600 ; N aogonek ; B 35 -199 586 454 ;
C -1 ; WX 600 ; N Uacute ; B 4 -18 596 784 ;
C -1 ; WX 600 ; N uogonek ; B -1 -199 585 439 ;
C -1 ; WX 600 ; N Edieresis ; B 25 0 560 761 ;
C -1 ; WX 600 ; N Dcroat ; B 30 0 594 562 ;
C -1 ; WX 600 ; N commaaccent ; B 205 -250 397 -57 ;
C -1 ; WX 600 ; N copyright ; B 0 -18 600 580 ;
C -1 ; WX 600 ; N Emacron ; B 25 0 560 708 ;
C -1 ; WX 600 ; N ccaron ; B 40 -15 545 667 ;
C -1 ; WX 600 ; N aring ; B 35 -15 570 678 ;
C -1 ; WX 600 ; N Ncommaaccent ; B 8 -250 610 562 ;
C -1 ; WX 600 ; N lacute ; B 77 0 523 801 ;
C -1 ; WX 600 ; N agrave ; B 35 -15 570 661 ;
C -1 ; WX 600 ; N Tcommaaccent ; B 21 -250 579 562 ;
C -1 ; WX 600 ; N Cacute ; B 22 -18 560 784 ;
C -1 ; WX 600 ; N atilde ; B 35 -15 570 636 ;
C -1 ; WX 600 ; N Edotaccent ; B 25 0 560 761 ;
C -1 ; WX 600 ; N scaron ; B 68 -17 535 667 ;
C -1 ; WX 600 ; N scedilla ; B 68 -206 535 459 ;
C -1 ; WX 600 ; N iacute ; B 77 0 523 661 ;
C -1 ; WX 600 ; N lozenge ; B 66 0 534 740 ;
C -1 ; WX 600 ; N Rcaron ; B 24 0 599 790 ;
C -1 ; WX 600 ; N Gcommaaccent ; B 22 -250 594 580 ;
C -1 ; WX 600 ; N ucircumflex ; B -1 -15 569 657 ;
C -1 ; WX 600 ; N acircumflex ; B 35 -15 570 657 ;
C -1 ; WX 600 ; N Amacron ; B -9 0 609 708 ;
C -1 ; WX 600 ; N rcaron ; B 47 0 580 667 ;
C -1 ; WX 600 ; N ccedilla ; B 40 -206 545 459 ;
C -1 ; WX 600 ; N Zdotaccent ; B 62 0 539 761 ;
C -1 ; WX 600 ; N Thorn ; B 48 0 557 562 ;
C -1 ; WX 600 ; N Omacron ; B 22 -18 578 708 ;
C -1 ; WX 600 ; N Racute ; B 24 0 599 784 ;
C -1 ; WX 600 ; N Sacute ; B 47 -22 553 784 ;
C -1 ; WX 600 ; N dcaron ; B 20 -15 727 626 ;
C -1 ; WX 600 ; N Umacron ; B 4 -18 596 708 ;
C -1 ; WX 600 ; N uring ; B -1 -15 569 678 ;
C -1 ; WX 600 ; N threesuperior ; B 138 222 433 616 ;
C -1 ; WX 600 ; N Ograve ; B 22 -18 578 784 ;
C -1 ; WX 600 ; N Agrave ; B -9 0 609 784 ;
C -1 ; WX 600 ; N Abreve ; B -9 0 609 784 ;
C -1 ; WX 600 ; N multiply ; B 81 39 520 478 ;
C -1 ; WX 600 ; N uacute ; B -1 -15 569 661 ;
C -1 ; WX 600 ; N Tcaron ; B 21 0 579 790 ;
C -1 ; WX 600 ; N partialdiff ; B 63 -38 537 728 ;
C -1 ; WX 600 ; N ydieresis ; B -4 -142 601 638 ;
C -1 ; WX 600 ; N Nacute ; B 8 -12 610 784 ;
C -1 ; WX 600 ; N icircumflex ; B 73 0 523 657 ;
C -1 ; WX 600 ; N Ecircumflex ; B 25 0 560 780 ;
C -1 ; WX 600 ; N adieresis ; B 35 -15 570 638 ;
C -1 ; WX 600 ; N edieresis ; B 40 -15 563 638 ;
C -1 ; WX 600 ; N cacute ; B 40 -15 545 661 ;
C -1 ; WX 600 ; N nacute ; B 18 0 592 661 ;
C -1 ; WX 600 ; N umacron ; B -1 -15 569 585 ;
C -1 ; WX 600 ; N Ncaron ; B 8 -12 610 790 ;
C -1 ; WX 600 ; N Iacute ; B 77 0 523 784 ;
C -1 ; WX 600 ; N plusminus ; B 71 24 529 515 ;
C -1 ; WX 600 ; N brokenbar ; B 255 -175 345 675 ;
C -1 ; WX 600 ; N registered ; B 0 -18 600 580 ;
C -1 ; WX 600 ; N Gbreve ; B 22 -18 594 784 ;
C -1 ; WX 600 ; N Idotaccent ; B 77 0 523 761 ;
C -1 ; WX 600 ; N summation ; B 15 -10 586 706 ;
C -1 ; WX 600 ; N Egrave ; B 25 0 560 784 ;
C -1 ; WX 600 ; N racute ; B 47 0 580 661 ;
C -1 ; WX 600 ; N omacron ; B 30 -15 570 585 ;
C -1 ; WX 600 ; N Zacute ; B 62 0 539 784 ;
C -1 ; WX 600 ; N Zcaron ; B 62 0 539 790 ;
C -1 ; WX 600 ; N greaterequal ; B 26 0 523 696 ;
C -1 ; WX 600 ; N Eth ; B 30 0 594 562 ;
C -1 ; WX 600 ; N Ccedilla ; B 22 -206 560 580 ;
C -1 ; WX 600 ; N lcommaaccent ; B 77 -250 523 626 ;
C -1 ; WX 600 ; N tcaron ; B 47 -15 532 703 ;
C -1 ; WX 600 ; N eogonek ; B 40 -199 563 454 ;
C -1 ; WX 600 ; N Uogonek ; B 4 -199 596 562 ;
C -1 ; WX 600 ; N Aacute ; B -9 0 609 784 ;
C -1 ; WX 600 ; N Adieresis ; B -9 0 609 761 ;
C -1 ; WX 600 ; N egrave ; B 40 -15 563 661 ;
C -1 ; WX 600 ; N zacute ; B 81 0 520 661 ;
C -1 ; WX 600 ; N iogonek ; B 77 -199 523 658 ;
C -1 ; WX 600 ; N Oacute ; B 22 -18 578 784 ;
C -1 ; WX 600 ; N oacute ; B 30 -15 570 661 ;
C -1 ; WX 600 ; N amacron ; B 35 -15 570 585 ;
C -1 ; WX 600 ; N sacute ; B 68 -17 535 661 ;
C -1 ; WX 600 ; N idieresis ; B 77 0 523 618 ;
C -1 ; WX 600 ; N Ocircumflex ; B 22 -18 578 780 ;
C -1 ; WX 600 ; N Ugrave ; B 4 -18 596 784 ;
C -1 ; WX 600 ; N Delta ; B 6 0 594 688 ;
C -1 ; WX 600 ; N thorn ; B -14 -142 570 626 ;
C -1 ; WX 600 ; N twosuperior ; B 143 230 436 616 ;
C -1 ; WX 600 ; N Odieresis ; B 22 -18 578 761 ;
C -1 ; WX 600 ; N mu ; B -1 -142 569 439 ;
C -1 ; WX 600 ; N igrave ; B 77 0 523 661 ;
C -1 ; WX 600 ; N ohungarumlaut ; B 30 -15 668 661 ;
C -1 ; WX 600 ; N Eogonek ; B 25 -199 576 562 ;
C -1 ; WX 600 ; N dcroat ; B 20 -15 591 626 ;
C -1 ; WX 600 ; N threequarters ; B -47 -60 648 661 ;
C -1 ; WX 600 ; N Scedilla ; B 47 -206 553 582 ;
C -1 ; WX 600 ; N lcaron ; B 77 0 597 626 ;
C -1 ; WX 600 ; N Kcommaaccent ; B 21 -250 599 562 ;
C -1 ; WX 600 ; N Lacute ; B 39 0 578 784 ;
C -1 ; WX 600 ; N trademark ; B -9 230 749 562 ;
C -1 ; WX 600 ; N edotaccent ; B 40 -15 563 638 ;
C -1 ; WX 600 ; N Igrave ; B 77 0 523 784 ;
C -1 ; WX 600 ; N Imacron ; B 77 0 523 708 ;
C -1 ; WX 600 ; N Lcaron ; B 39 0 637 562 ;
C -1 ; WX 600 ; N onehalf ; B -47 -60 648 661 ;
C -1 ; WX 600 ; N lessequal ; B 26 0 523 696 ;
C -1 ; WX 600 ; N ocircumflex ; B 30 -15 570 657 ;
C -1 ; WX 600 ; N ntilde ; B 18 0 592 636 ;
C -1 ; WX 600 ; N Uhungarumlaut ; B 4 -18 638 784 ;
C -1 ; WX 600 ; N Eacute ; B 25 0 560 784 ;
C -1 ; WX 600 ; N emacron ; B 40 -15 563 585 ;
C -1 ; WX 600 ; N gbreve ; B 30 -146 580 661 ;
C -1 ; WX 600 ; N onequarter ; B -56 -60 656 661 ;
C -1 ; WX 600 ; N Scaron ; B 47 -22 553 790 ;
C -1 ; WX 600 ; N Scommaaccent ; B 47 -250 553 582 ;
C -1 ; WX 600 ; N Ohungarumlaut ; B 22 -18 628 784 ;
C -1 ; WX 600 ; N degree ; B 86 243 474 616 ;
C -1 ; WX 600 ; N ograve ; B 30 -15 570 661 ;
C -1 ; WX 600 ; N Ccaron ; B 22 -18 560 790 ;
C -1 ; WX 600 ; N ugrave ; B -1 -15 569 661 ;
C -1 ; WX 600 ; N radical ; B -19 -104 473 778 ;
C -1 ; WX 600 ; N Dcaron ; B 30 0 594 790 ;
C -1 ; WX 600 ; N rcommaaccent ; B 47 -250 580 454 ;
C -1 ; WX 600 ; N Ntilde ; B 8 -12 610 759 ;
C -1 ; WX 600 ; N otilde ; B 30 -15 570 636 ;
C -1 ; WX 600 ; N Rcommaaccent ; B 24 -250 599 562 ;
C -1 ; WX 600 ; N Lcommaaccent ; B 39 -250 578 562 ;
C -1 ; WX 600 ; N Atilde ; B -9 0 609 759 ;
C -1 ; WX 600 ; N Aogonek ; B -9 -199 625 562 ;
C -1 ; WX 600 ; N Aring ; B -9 0 609 801 ;
C -1 ; WX 600 ; N Otilde ; B 22 -18 578 759 ;
C -1 ; WX 600 ; N zdotaccent ; B 81 0 520 638 ;
C -1 ; WX 600 ; N Ecaron ; B 25 0 560 790 ;
C -1 ; WX 600 ; N Iogonek ; B 77 -199 523 562 ;
C -1 ; WX 600 ; N kcommaaccent ; B 20 -250 585 626 ;
C -1 ; WX 600 ; N minus ; B 71 203 529 313 ;
C -1 ; WX 600 ; N Icircumflex ; B 77 0 523 780 ;
C -1 ; WX 600 ; N ncaron ; B 18 0 592 667 ;
C -1 ; WX 600 ; N tcommaaccent ; B 47 -250 532 562 ;
C -1 ; WX 600 ; N logicalnot ; B 71 103 529 413 ;
C -1 ; WX 600 ; N odieresis ; B 30 -15 570 638 ;
C -1 ; WX 600 ; N udieresis ; B -1 -15 569 638 ;
C -1 ; WX 600 ; N notequal ; B 12 -47 537 563 ;
C -1 ; WX 600 ; N gcommaaccent ; B 30 -146 580 714 ;
C -1 ; WX 600 ; N eth ; B 58 -27 543 626 ;
C -1 ; WX 600 ; N zcaron ; B 81 0 520 667 ;
C -1 ; WX 600 ; N ncommaaccent ; B 18 -250 592 454 ;
C -1 ; WX 600 ; N onesuperior ; B 153 230 447 616 ;
C -1 ; WX 600 ; N imacron ; B 77 0 523 585 ;
C -1 ; WX 600 ; N Euro ; B 0 0 0 0 ;
EndCharMetrics
EndFontMetrics

View File

@@ -0,0 +1,342 @@
StartFontMetrics 4.1
Comment Copyright (c) 1989, 1990, 1991, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
Comment Creation Date: Mon Jun 23 16:28:46 1997
Comment UniqueID 43049
Comment VMusage 17529 79244
FontName Courier-BoldOblique
FullName Courier Bold Oblique
FamilyName Courier
Weight Bold
ItalicAngle -12
IsFixedPitch true
CharacterSet ExtendedRoman
FontBBox -57 -250 869 801
UnderlinePosition -100
UnderlineThickness 50
Version 003.000
Notice Copyright (c) 1989, 1990, 1991, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
EncodingScheme AdobeStandardEncoding
CapHeight 562
XHeight 439
Ascender 629
Descender -157
StdHW 84
StdVW 106
StartCharMetrics 315
C 32 ; WX 600 ; N space ; B 0 0 0 0 ;
C 33 ; WX 600 ; N exclam ; B 215 -15 495 572 ;
C 34 ; WX 600 ; N quotedbl ; B 211 277 585 562 ;
C 35 ; WX 600 ; N numbersign ; B 88 -45 641 651 ;
C 36 ; WX 600 ; N dollar ; B 87 -126 630 666 ;
C 37 ; WX 600 ; N percent ; B 101 -15 625 616 ;
C 38 ; WX 600 ; N ampersand ; B 61 -15 595 543 ;
C 39 ; WX 600 ; N quoteright ; B 229 277 543 562 ;
C 40 ; WX 600 ; N parenleft ; B 265 -102 592 616 ;
C 41 ; WX 600 ; N parenright ; B 117 -102 444 616 ;
C 42 ; WX 600 ; N asterisk ; B 179 219 598 601 ;
C 43 ; WX 600 ; N plus ; B 114 39 596 478 ;
C 44 ; WX 600 ; N comma ; B 99 -111 430 174 ;
C 45 ; WX 600 ; N hyphen ; B 143 203 567 313 ;
C 46 ; WX 600 ; N period ; B 206 -15 427 171 ;
C 47 ; WX 600 ; N slash ; B 90 -77 626 626 ;
C 48 ; WX 600 ; N zero ; B 135 -15 593 616 ;
C 49 ; WX 600 ; N one ; B 93 0 562 616 ;
C 50 ; WX 600 ; N two ; B 61 0 594 616 ;
C 51 ; WX 600 ; N three ; B 71 -15 571 616 ;
C 52 ; WX 600 ; N four ; B 81 0 559 616 ;
C 53 ; WX 600 ; N five ; B 77 -15 621 601 ;
C 54 ; WX 600 ; N six ; B 135 -15 652 616 ;
C 55 ; WX 600 ; N seven ; B 147 0 622 601 ;
C 56 ; WX 600 ; N eight ; B 115 -15 604 616 ;
C 57 ; WX 600 ; N nine ; B 75 -15 592 616 ;
C 58 ; WX 600 ; N colon ; B 205 -15 480 425 ;
C 59 ; WX 600 ; N semicolon ; B 99 -111 481 425 ;
C 60 ; WX 600 ; N less ; B 120 15 613 501 ;
C 61 ; WX 600 ; N equal ; B 96 118 614 398 ;
C 62 ; WX 600 ; N greater ; B 97 15 589 501 ;
C 63 ; WX 600 ; N question ; B 183 -14 592 580 ;
C 64 ; WX 600 ; N at ; B 65 -15 642 616 ;
C 65 ; WX 600 ; N A ; B -9 0 632 562 ;
C 66 ; WX 600 ; N B ; B 30 0 630 562 ;
C 67 ; WX 600 ; N C ; B 74 -18 675 580 ;
C 68 ; WX 600 ; N D ; B 30 0 664 562 ;
C 69 ; WX 600 ; N E ; B 25 0 670 562 ;
C 70 ; WX 600 ; N F ; B 39 0 684 562 ;
C 71 ; WX 600 ; N G ; B 74 -18 675 580 ;
C 72 ; WX 600 ; N H ; B 20 0 700 562 ;
C 73 ; WX 600 ; N I ; B 77 0 643 562 ;
C 74 ; WX 600 ; N J ; B 58 -18 721 562 ;
C 75 ; WX 600 ; N K ; B 21 0 692 562 ;
C 76 ; WX 600 ; N L ; B 39 0 636 562 ;
C 77 ; WX 600 ; N M ; B -2 0 722 562 ;
C 78 ; WX 600 ; N N ; B 8 -12 730 562 ;
C 79 ; WX 600 ; N O ; B 74 -18 645 580 ;
C 80 ; WX 600 ; N P ; B 48 0 643 562 ;
C 81 ; WX 600 ; N Q ; B 83 -138 636 580 ;
C 82 ; WX 600 ; N R ; B 24 0 617 562 ;
C 83 ; WX 600 ; N S ; B 54 -22 673 582 ;
C 84 ; WX 600 ; N T ; B 86 0 679 562 ;
C 85 ; WX 600 ; N U ; B 101 -18 716 562 ;
C 86 ; WX 600 ; N V ; B 84 0 733 562 ;
C 87 ; WX 600 ; N W ; B 79 0 738 562 ;
C 88 ; WX 600 ; N X ; B 12 0 690 562 ;
C 89 ; WX 600 ; N Y ; B 109 0 709 562 ;
C 90 ; WX 600 ; N Z ; B 62 0 637 562 ;
C 91 ; WX 600 ; N bracketleft ; B 223 -102 606 616 ;
C 92 ; WX 600 ; N backslash ; B 222 -77 496 626 ;
C 93 ; WX 600 ; N bracketright ; B 103 -102 486 616 ;
C 94 ; WX 600 ; N asciicircum ; B 171 250 556 616 ;
C 95 ; WX 600 ; N underscore ; B -27 -125 585 -75 ;
C 96 ; WX 600 ; N quoteleft ; B 297 277 487 562 ;
C 97 ; WX 600 ; N a ; B 61 -15 593 454 ;
C 98 ; WX 600 ; N b ; B 13 -15 636 626 ;
C 99 ; WX 600 ; N c ; B 81 -15 631 459 ;
C 100 ; WX 600 ; N d ; B 60 -15 645 626 ;
C 101 ; WX 600 ; N e ; B 81 -15 605 454 ;
C 102 ; WX 600 ; N f ; B 83 0 677 626 ; L i fi ; L l fl ;
C 103 ; WX 600 ; N g ; B 40 -146 674 454 ;
C 104 ; WX 600 ; N h ; B 18 0 615 626 ;
C 105 ; WX 600 ; N i ; B 77 0 546 658 ;
C 106 ; WX 600 ; N j ; B 36 -146 580 658 ;
C 107 ; WX 600 ; N k ; B 33 0 643 626 ;
C 108 ; WX 600 ; N l ; B 77 0 546 626 ;
C 109 ; WX 600 ; N m ; B -22 0 649 454 ;
C 110 ; WX 600 ; N n ; B 18 0 615 454 ;
C 111 ; WX 600 ; N o ; B 71 -15 622 454 ;
C 112 ; WX 600 ; N p ; B -32 -142 622 454 ;
C 113 ; WX 600 ; N q ; B 60 -142 685 454 ;
C 114 ; WX 600 ; N r ; B 47 0 655 454 ;
C 115 ; WX 600 ; N s ; B 66 -17 608 459 ;
C 116 ; WX 600 ; N t ; B 118 -15 567 562 ;
C 117 ; WX 600 ; N u ; B 70 -15 592 439 ;
C 118 ; WX 600 ; N v ; B 70 0 695 439 ;
C 119 ; WX 600 ; N w ; B 53 0 712 439 ;
C 120 ; WX 600 ; N x ; B 6 0 671 439 ;
C 121 ; WX 600 ; N y ; B -21 -142 695 439 ;
C 122 ; WX 600 ; N z ; B 81 0 614 439 ;
C 123 ; WX 600 ; N braceleft ; B 203 -102 595 616 ;
C 124 ; WX 600 ; N bar ; B 201 -250 505 750 ;
C 125 ; WX 600 ; N braceright ; B 114 -102 506 616 ;
C 126 ; WX 600 ; N asciitilde ; B 120 153 590 356 ;
C 161 ; WX 600 ; N exclamdown ; B 196 -146 477 449 ;
C 162 ; WX 600 ; N cent ; B 121 -49 605 614 ;
C 163 ; WX 600 ; N sterling ; B 106 -28 650 611 ;
C 164 ; WX 600 ; N fraction ; B 22 -60 708 661 ;
C 165 ; WX 600 ; N yen ; B 98 0 710 562 ;
C 166 ; WX 600 ; N florin ; B -57 -131 702 616 ;
C 167 ; WX 600 ; N section ; B 74 -70 620 580 ;
C 168 ; WX 600 ; N currency ; B 77 49 644 517 ;
C 169 ; WX 600 ; N quotesingle ; B 303 277 493 562 ;
C 170 ; WX 600 ; N quotedblleft ; B 190 277 594 562 ;
C 171 ; WX 600 ; N guillemotleft ; B 62 70 639 446 ;
C 172 ; WX 600 ; N guilsinglleft ; B 195 70 545 446 ;
C 173 ; WX 600 ; N guilsinglright ; B 165 70 514 446 ;
C 174 ; WX 600 ; N fi ; B 12 0 644 626 ;
C 175 ; WX 600 ; N fl ; B 12 0 644 626 ;
C 177 ; WX 600 ; N endash ; B 108 203 602 313 ;
C 178 ; WX 600 ; N dagger ; B 175 -70 586 580 ;
C 179 ; WX 600 ; N daggerdbl ; B 121 -70 587 580 ;
C 180 ; WX 600 ; N periodcentered ; B 248 165 461 351 ;
C 182 ; WX 600 ; N paragraph ; B 61 -70 700 580 ;
C 183 ; WX 600 ; N bullet ; B 196 132 523 430 ;
C 184 ; WX 600 ; N quotesinglbase ; B 144 -142 458 143 ;
C 185 ; WX 600 ; N quotedblbase ; B 34 -142 560 143 ;
C 186 ; WX 600 ; N quotedblright ; B 119 277 645 562 ;
C 187 ; WX 600 ; N guillemotright ; B 71 70 647 446 ;
C 188 ; WX 600 ; N ellipsis ; B 35 -15 587 116 ;
C 189 ; WX 600 ; N perthousand ; B -45 -15 743 616 ;
C 191 ; WX 600 ; N questiondown ; B 100 -146 509 449 ;
C 193 ; WX 600 ; N grave ; B 272 508 503 661 ;
C 194 ; WX 600 ; N acute ; B 312 508 609 661 ;
C 195 ; WX 600 ; N circumflex ; B 212 483 607 657 ;
C 196 ; WX 600 ; N tilde ; B 199 493 643 636 ;
C 197 ; WX 600 ; N macron ; B 195 505 637 585 ;
C 198 ; WX 600 ; N breve ; B 217 468 652 631 ;
C 199 ; WX 600 ; N dotaccent ; B 348 498 493 638 ;
C 200 ; WX 600 ; N dieresis ; B 246 498 595 638 ;
C 202 ; WX 600 ; N ring ; B 319 481 528 678 ;
C 203 ; WX 600 ; N cedilla ; B 168 -206 368 0 ;
C 205 ; WX 600 ; N hungarumlaut ; B 171 488 729 661 ;
C 206 ; WX 600 ; N ogonek ; B 143 -199 367 0 ;
C 207 ; WX 600 ; N caron ; B 238 493 633 667 ;
C 208 ; WX 600 ; N emdash ; B 33 203 677 313 ;
C 225 ; WX 600 ; N AE ; B -29 0 708 562 ;
C 227 ; WX 600 ; N ordfeminine ; B 188 196 526 580 ;
C 232 ; WX 600 ; N Lslash ; B 39 0 636 562 ;
C 233 ; WX 600 ; N Oslash ; B 48 -22 673 584 ;
C 234 ; WX 600 ; N OE ; B 26 0 701 562 ;
C 235 ; WX 600 ; N ordmasculine ; B 188 196 543 580 ;
C 241 ; WX 600 ; N ae ; B 21 -15 652 454 ;
C 245 ; WX 600 ; N dotlessi ; B 77 0 546 439 ;
C 248 ; WX 600 ; N lslash ; B 77 0 587 626 ;
C 249 ; WX 600 ; N oslash ; B 54 -24 638 463 ;
C 250 ; WX 600 ; N oe ; B 18 -15 662 454 ;
C 251 ; WX 600 ; N germandbls ; B 22 -15 629 626 ;
C -1 ; WX 600 ; N Idieresis ; B 77 0 643 761 ;
C -1 ; WX 600 ; N eacute ; B 81 -15 609 661 ;
C -1 ; WX 600 ; N abreve ; B 61 -15 658 661 ;
C -1 ; WX 600 ; N uhungarumlaut ; B 70 -15 769 661 ;
C -1 ; WX 600 ; N ecaron ; B 81 -15 633 667 ;
C -1 ; WX 600 ; N Ydieresis ; B 109 0 709 761 ;
C -1 ; WX 600 ; N divide ; B 114 16 596 500 ;
C -1 ; WX 600 ; N Yacute ; B 109 0 709 784 ;
C -1 ; WX 600 ; N Acircumflex ; B -9 0 632 780 ;
C -1 ; WX 600 ; N aacute ; B 61 -15 609 661 ;
C -1 ; WX 600 ; N Ucircumflex ; B 101 -18 716 780 ;
C -1 ; WX 600 ; N yacute ; B -21 -142 695 661 ;
C -1 ; WX 600 ; N scommaaccent ; B 66 -250 608 459 ;
C -1 ; WX 600 ; N ecircumflex ; B 81 -15 607 657 ;
C -1 ; WX 600 ; N Uring ; B 101 -18 716 801 ;
C -1 ; WX 600 ; N Udieresis ; B 101 -18 716 761 ;
C -1 ; WX 600 ; N aogonek ; B 61 -199 593 454 ;
C -1 ; WX 600 ; N Uacute ; B 101 -18 716 784 ;
C -1 ; WX 600 ; N uogonek ; B 70 -199 592 439 ;
C -1 ; WX 600 ; N Edieresis ; B 25 0 670 761 ;
C -1 ; WX 600 ; N Dcroat ; B 30 0 664 562 ;
C -1 ; WX 600 ; N commaaccent ; B 151 -250 385 -57 ;
C -1 ; WX 600 ; N copyright ; B 53 -18 667 580 ;
C -1 ; WX 600 ; N Emacron ; B 25 0 670 708 ;
C -1 ; WX 600 ; N ccaron ; B 81 -15 633 667 ;
C -1 ; WX 600 ; N aring ; B 61 -15 593 678 ;
C -1 ; WX 600 ; N Ncommaaccent ; B 8 -250 730 562 ;
C -1 ; WX 600 ; N lacute ; B 77 0 639 801 ;
C -1 ; WX 600 ; N agrave ; B 61 -15 593 661 ;
C -1 ; WX 600 ; N Tcommaaccent ; B 86 -250 679 562 ;
C -1 ; WX 600 ; N Cacute ; B 74 -18 675 784 ;
C -1 ; WX 600 ; N atilde ; B 61 -15 643 636 ;
C -1 ; WX 600 ; N Edotaccent ; B 25 0 670 761 ;
C -1 ; WX 600 ; N scaron ; B 66 -17 633 667 ;
C -1 ; WX 600 ; N scedilla ; B 66 -206 608 459 ;
C -1 ; WX 600 ; N iacute ; B 77 0 609 661 ;
C -1 ; WX 600 ; N lozenge ; B 145 0 614 740 ;
C -1 ; WX 600 ; N Rcaron ; B 24 0 659 790 ;
C -1 ; WX 600 ; N Gcommaaccent ; B 74 -250 675 580 ;
C -1 ; WX 600 ; N ucircumflex ; B 70 -15 597 657 ;
C -1 ; WX 600 ; N acircumflex ; B 61 -15 607 657 ;
C -1 ; WX 600 ; N Amacron ; B -9 0 633 708 ;
C -1 ; WX 600 ; N rcaron ; B 47 0 655 667 ;
C -1 ; WX 600 ; N ccedilla ; B 81 -206 631 459 ;
C -1 ; WX 600 ; N Zdotaccent ; B 62 0 637 761 ;
C -1 ; WX 600 ; N Thorn ; B 48 0 620 562 ;
C -1 ; WX 600 ; N Omacron ; B 74 -18 663 708 ;
C -1 ; WX 600 ; N Racute ; B 24 0 665 784 ;
C -1 ; WX 600 ; N Sacute ; B 54 -22 673 784 ;
C -1 ; WX 600 ; N dcaron ; B 60 -15 861 626 ;
C -1 ; WX 600 ; N Umacron ; B 101 -18 716 708 ;
C -1 ; WX 600 ; N uring ; B 70 -15 592 678 ;
C -1 ; WX 600 ; N threesuperior ; B 193 222 526 616 ;
C -1 ; WX 600 ; N Ograve ; B 74 -18 645 784 ;
C -1 ; WX 600 ; N Agrave ; B -9 0 632 784 ;
C -1 ; WX 600 ; N Abreve ; B -9 0 684 784 ;
C -1 ; WX 600 ; N multiply ; B 104 39 606 478 ;
C -1 ; WX 600 ; N uacute ; B 70 -15 599 661 ;
C -1 ; WX 600 ; N Tcaron ; B 86 0 679 790 ;
C -1 ; WX 600 ; N partialdiff ; B 91 -38 627 728 ;
C -1 ; WX 600 ; N ydieresis ; B -21 -142 695 638 ;
C -1 ; WX 600 ; N Nacute ; B 8 -12 730 784 ;
C -1 ; WX 600 ; N icircumflex ; B 77 0 577 657 ;
C -1 ; WX 600 ; N Ecircumflex ; B 25 0 670 780 ;
C -1 ; WX 600 ; N adieresis ; B 61 -15 595 638 ;
C -1 ; WX 600 ; N edieresis ; B 81 -15 605 638 ;
C -1 ; WX 600 ; N cacute ; B 81 -15 649 661 ;
C -1 ; WX 600 ; N nacute ; B 18 0 639 661 ;
C -1 ; WX 600 ; N umacron ; B 70 -15 637 585 ;
C -1 ; WX 600 ; N Ncaron ; B 8 -12 730 790 ;
C -1 ; WX 600 ; N Iacute ; B 77 0 643 784 ;
C -1 ; WX 600 ; N plusminus ; B 76 24 614 515 ;
C -1 ; WX 600 ; N brokenbar ; B 217 -175 489 675 ;
C -1 ; WX 600 ; N registered ; B 53 -18 667 580 ;
C -1 ; WX 600 ; N Gbreve ; B 74 -18 684 784 ;
C -1 ; WX 600 ; N Idotaccent ; B 77 0 643 761 ;
C -1 ; WX 600 ; N summation ; B 15 -10 672 706 ;
C -1 ; WX 600 ; N Egrave ; B 25 0 670 784 ;
C -1 ; WX 600 ; N racute ; B 47 0 655 661 ;
C -1 ; WX 600 ; N omacron ; B 71 -15 637 585 ;
C -1 ; WX 600 ; N Zacute ; B 62 0 665 784 ;
C -1 ; WX 600 ; N Zcaron ; B 62 0 659 790 ;
C -1 ; WX 600 ; N greaterequal ; B 26 0 627 696 ;
C -1 ; WX 600 ; N Eth ; B 30 0 664 562 ;
C -1 ; WX 600 ; N Ccedilla ; B 74 -206 675 580 ;
C -1 ; WX 600 ; N lcommaaccent ; B 77 -250 546 626 ;
C -1 ; WX 600 ; N tcaron ; B 118 -15 627 703 ;
C -1 ; WX 600 ; N eogonek ; B 81 -199 605 454 ;
C -1 ; WX 600 ; N Uogonek ; B 101 -199 716 562 ;
C -1 ; WX 600 ; N Aacute ; B -9 0 655 784 ;
C -1 ; WX 600 ; N Adieresis ; B -9 0 632 761 ;
C -1 ; WX 600 ; N egrave ; B 81 -15 605 661 ;
C -1 ; WX 600 ; N zacute ; B 81 0 614 661 ;
C -1 ; WX 600 ; N iogonek ; B 77 -199 546 658 ;
C -1 ; WX 600 ; N Oacute ; B 74 -18 645 784 ;
C -1 ; WX 600 ; N oacute ; B 71 -15 649 661 ;
C -1 ; WX 600 ; N amacron ; B 61 -15 637 585 ;
C -1 ; WX 600 ; N sacute ; B 66 -17 609 661 ;
C -1 ; WX 600 ; N idieresis ; B 77 0 561 618 ;
C -1 ; WX 600 ; N Ocircumflex ; B 74 -18 645 780 ;
C -1 ; WX 600 ; N Ugrave ; B 101 -18 716 784 ;
C -1 ; WX 600 ; N Delta ; B 6 0 594 688 ;
C -1 ; WX 600 ; N thorn ; B -32 -142 622 626 ;
C -1 ; WX 600 ; N twosuperior ; B 191 230 542 616 ;
C -1 ; WX 600 ; N Odieresis ; B 74 -18 645 761 ;
C -1 ; WX 600 ; N mu ; B 49 -142 592 439 ;
C -1 ; WX 600 ; N igrave ; B 77 0 546 661 ;
C -1 ; WX 600 ; N ohungarumlaut ; B 71 -15 809 661 ;
C -1 ; WX 600 ; N Eogonek ; B 25 -199 670 562 ;
C -1 ; WX 600 ; N dcroat ; B 60 -15 712 626 ;
C -1 ; WX 600 ; N threequarters ; B 8 -60 699 661 ;
C -1 ; WX 600 ; N Scedilla ; B 54 -206 673 582 ;
C -1 ; WX 600 ; N lcaron ; B 77 0 731 626 ;
C -1 ; WX 600 ; N Kcommaaccent ; B 21 -250 692 562 ;
C -1 ; WX 600 ; N Lacute ; B 39 0 636 784 ;
C -1 ; WX 600 ; N trademark ; B 86 230 869 562 ;
C -1 ; WX 600 ; N edotaccent ; B 81 -15 605 638 ;
C -1 ; WX 600 ; N Igrave ; B 77 0 643 784 ;
C -1 ; WX 600 ; N Imacron ; B 77 0 663 708 ;
C -1 ; WX 600 ; N Lcaron ; B 39 0 757 562 ;
C -1 ; WX 600 ; N onehalf ; B 22 -60 716 661 ;
C -1 ; WX 600 ; N lessequal ; B 26 0 671 696 ;
C -1 ; WX 600 ; N ocircumflex ; B 71 -15 622 657 ;
C -1 ; WX 600 ; N ntilde ; B 18 0 643 636 ;
C -1 ; WX 600 ; N Uhungarumlaut ; B 101 -18 805 784 ;
C -1 ; WX 600 ; N Eacute ; B 25 0 670 784 ;
C -1 ; WX 600 ; N emacron ; B 81 -15 637 585 ;
C -1 ; WX 600 ; N gbreve ; B 40 -146 674 661 ;
C -1 ; WX 600 ; N onequarter ; B 13 -60 707 661 ;
C -1 ; WX 600 ; N Scaron ; B 54 -22 689 790 ;
C -1 ; WX 600 ; N Scommaaccent ; B 54 -250 673 582 ;
C -1 ; WX 600 ; N Ohungarumlaut ; B 74 -18 795 784 ;
C -1 ; WX 600 ; N degree ; B 173 243 570 616 ;
C -1 ; WX 600 ; N ograve ; B 71 -15 622 661 ;
C -1 ; WX 600 ; N Ccaron ; B 74 -18 689 790 ;
C -1 ; WX 600 ; N ugrave ; B 70 -15 592 661 ;
C -1 ; WX 600 ; N radical ; B 67 -104 635 778 ;
C -1 ; WX 600 ; N Dcaron ; B 30 0 664 790 ;
C -1 ; WX 600 ; N rcommaaccent ; B 47 -250 655 454 ;
C -1 ; WX 600 ; N Ntilde ; B 8 -12 730 759 ;
C -1 ; WX 600 ; N otilde ; B 71 -15 643 636 ;
C -1 ; WX 600 ; N Rcommaaccent ; B 24 -250 617 562 ;
C -1 ; WX 600 ; N Lcommaaccent ; B 39 -250 636 562 ;
C -1 ; WX 600 ; N Atilde ; B -9 0 669 759 ;
C -1 ; WX 600 ; N Aogonek ; B -9 -199 632 562 ;
C -1 ; WX 600 ; N Aring ; B -9 0 632 801 ;
C -1 ; WX 600 ; N Otilde ; B 74 -18 669 759 ;
C -1 ; WX 600 ; N zdotaccent ; B 81 0 614 638 ;
C -1 ; WX 600 ; N Ecaron ; B 25 0 670 790 ;
C -1 ; WX 600 ; N Iogonek ; B 77 -199 643 562 ;
C -1 ; WX 600 ; N kcommaaccent ; B 33 -250 643 626 ;
C -1 ; WX 600 ; N minus ; B 114 203 596 313 ;
C -1 ; WX 600 ; N Icircumflex ; B 77 0 643 780 ;
C -1 ; WX 600 ; N ncaron ; B 18 0 633 667 ;
C -1 ; WX 600 ; N tcommaaccent ; B 118 -250 567 562 ;
C -1 ; WX 600 ; N logicalnot ; B 135 103 617 413 ;
C -1 ; WX 600 ; N odieresis ; B 71 -15 622 638 ;
C -1 ; WX 600 ; N udieresis ; B 70 -15 595 638 ;
C -1 ; WX 600 ; N notequal ; B 30 -47 626 563 ;
C -1 ; WX 600 ; N gcommaaccent ; B 40 -146 674 714 ;
C -1 ; WX 600 ; N eth ; B 93 -27 661 626 ;
C -1 ; WX 600 ; N zcaron ; B 81 0 643 667 ;
C -1 ; WX 600 ; N ncommaaccent ; B 18 -250 615 454 ;
C -1 ; WX 600 ; N onesuperior ; B 212 230 514 616 ;
C -1 ; WX 600 ; N imacron ; B 77 0 575 585 ;
C -1 ; WX 600 ; N Euro ; B 0 0 0 0 ;
EndCharMetrics
EndFontMetrics

View File

@@ -0,0 +1,342 @@
StartFontMetrics 4.1
Comment Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
Comment Creation Date: Thu May 1 17:37:52 1997
Comment UniqueID 43051
Comment VMusage 16248 75829
FontName Courier-Oblique
FullName Courier Oblique
FamilyName Courier
Weight Medium
ItalicAngle -12
IsFixedPitch true
CharacterSet ExtendedRoman
FontBBox -27 -250 849 805
UnderlinePosition -100
UnderlineThickness 50
Version 003.000
Notice Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
EncodingScheme AdobeStandardEncoding
CapHeight 562
XHeight 426
Ascender 629
Descender -157
StdHW 51
StdVW 51
StartCharMetrics 315
C 32 ; WX 600 ; N space ; B 0 0 0 0 ;
C 33 ; WX 600 ; N exclam ; B 243 -15 464 572 ;
C 34 ; WX 600 ; N quotedbl ; B 273 328 532 562 ;
C 35 ; WX 600 ; N numbersign ; B 133 -32 596 639 ;
C 36 ; WX 600 ; N dollar ; B 108 -126 596 662 ;
C 37 ; WX 600 ; N percent ; B 134 -15 599 622 ;
C 38 ; WX 600 ; N ampersand ; B 87 -15 580 543 ;
C 39 ; WX 600 ; N quoteright ; B 283 328 495 562 ;
C 40 ; WX 600 ; N parenleft ; B 313 -108 572 622 ;
C 41 ; WX 600 ; N parenright ; B 137 -108 396 622 ;
C 42 ; WX 600 ; N asterisk ; B 212 257 580 607 ;
C 43 ; WX 600 ; N plus ; B 129 44 580 470 ;
C 44 ; WX 600 ; N comma ; B 157 -112 370 122 ;
C 45 ; WX 600 ; N hyphen ; B 152 231 558 285 ;
C 46 ; WX 600 ; N period ; B 238 -15 382 109 ;
C 47 ; WX 600 ; N slash ; B 112 -80 604 629 ;
C 48 ; WX 600 ; N zero ; B 154 -15 575 622 ;
C 49 ; WX 600 ; N one ; B 98 0 515 622 ;
C 50 ; WX 600 ; N two ; B 70 0 568 622 ;
C 51 ; WX 600 ; N three ; B 82 -15 538 622 ;
C 52 ; WX 600 ; N four ; B 108 0 541 622 ;
C 53 ; WX 600 ; N five ; B 99 -15 589 607 ;
C 54 ; WX 600 ; N six ; B 155 -15 629 622 ;
C 55 ; WX 600 ; N seven ; B 182 0 612 607 ;
C 56 ; WX 600 ; N eight ; B 132 -15 588 622 ;
C 57 ; WX 600 ; N nine ; B 93 -15 574 622 ;
C 58 ; WX 600 ; N colon ; B 238 -15 441 385 ;
C 59 ; WX 600 ; N semicolon ; B 157 -112 441 385 ;
C 60 ; WX 600 ; N less ; B 96 42 610 472 ;
C 61 ; WX 600 ; N equal ; B 109 138 600 376 ;
C 62 ; WX 600 ; N greater ; B 85 42 599 472 ;
C 63 ; WX 600 ; N question ; B 222 -15 583 572 ;
C 64 ; WX 600 ; N at ; B 127 -15 582 622 ;
C 65 ; WX 600 ; N A ; B 3 0 607 562 ;
C 66 ; WX 600 ; N B ; B 43 0 616 562 ;
C 67 ; WX 600 ; N C ; B 93 -18 655 580 ;
C 68 ; WX 600 ; N D ; B 43 0 645 562 ;
C 69 ; WX 600 ; N E ; B 53 0 660 562 ;
C 70 ; WX 600 ; N F ; B 53 0 660 562 ;
C 71 ; WX 600 ; N G ; B 83 -18 645 580 ;
C 72 ; WX 600 ; N H ; B 32 0 687 562 ;
C 73 ; WX 600 ; N I ; B 96 0 623 562 ;
C 74 ; WX 600 ; N J ; B 52 -18 685 562 ;
C 75 ; WX 600 ; N K ; B 38 0 671 562 ;
C 76 ; WX 600 ; N L ; B 47 0 607 562 ;
C 77 ; WX 600 ; N M ; B 4 0 715 562 ;
C 78 ; WX 600 ; N N ; B 7 -13 712 562 ;
C 79 ; WX 600 ; N O ; B 94 -18 625 580 ;
C 80 ; WX 600 ; N P ; B 79 0 644 562 ;
C 81 ; WX 600 ; N Q ; B 95 -138 625 580 ;
C 82 ; WX 600 ; N R ; B 38 0 598 562 ;
C 83 ; WX 600 ; N S ; B 76 -20 650 580 ;
C 84 ; WX 600 ; N T ; B 108 0 665 562 ;
C 85 ; WX 600 ; N U ; B 125 -18 702 562 ;
C 86 ; WX 600 ; N V ; B 105 -13 723 562 ;
C 87 ; WX 600 ; N W ; B 106 -13 722 562 ;
C 88 ; WX 600 ; N X ; B 23 0 675 562 ;
C 89 ; WX 600 ; N Y ; B 133 0 695 562 ;
C 90 ; WX 600 ; N Z ; B 86 0 610 562 ;
C 91 ; WX 600 ; N bracketleft ; B 246 -108 574 622 ;
C 92 ; WX 600 ; N backslash ; B 249 -80 468 629 ;
C 93 ; WX 600 ; N bracketright ; B 135 -108 463 622 ;
C 94 ; WX 600 ; N asciicircum ; B 175 354 587 622 ;
C 95 ; WX 600 ; N underscore ; B -27 -125 584 -75 ;
C 96 ; WX 600 ; N quoteleft ; B 343 328 457 562 ;
C 97 ; WX 600 ; N a ; B 76 -15 569 441 ;
C 98 ; WX 600 ; N b ; B 29 -15 625 629 ;
C 99 ; WX 600 ; N c ; B 106 -15 608 441 ;
C 100 ; WX 600 ; N d ; B 85 -15 640 629 ;
C 101 ; WX 600 ; N e ; B 106 -15 598 441 ;
C 102 ; WX 600 ; N f ; B 114 0 662 629 ; L i fi ; L l fl ;
C 103 ; WX 600 ; N g ; B 61 -157 657 441 ;
C 104 ; WX 600 ; N h ; B 33 0 592 629 ;
C 105 ; WX 600 ; N i ; B 95 0 515 657 ;
C 106 ; WX 600 ; N j ; B 52 -157 550 657 ;
C 107 ; WX 600 ; N k ; B 58 0 633 629 ;
C 108 ; WX 600 ; N l ; B 95 0 515 629 ;
C 109 ; WX 600 ; N m ; B -5 0 615 441 ;
C 110 ; WX 600 ; N n ; B 26 0 585 441 ;
C 111 ; WX 600 ; N o ; B 102 -15 588 441 ;
C 112 ; WX 600 ; N p ; B -24 -157 605 441 ;
C 113 ; WX 600 ; N q ; B 85 -157 682 441 ;
C 114 ; WX 600 ; N r ; B 60 0 636 441 ;
C 115 ; WX 600 ; N s ; B 78 -15 584 441 ;
C 116 ; WX 600 ; N t ; B 167 -15 561 561 ;
C 117 ; WX 600 ; N u ; B 101 -15 572 426 ;
C 118 ; WX 600 ; N v ; B 90 -10 681 426 ;
C 119 ; WX 600 ; N w ; B 76 -10 695 426 ;
C 120 ; WX 600 ; N x ; B 20 0 655 426 ;
C 121 ; WX 600 ; N y ; B -4 -157 683 426 ;
C 122 ; WX 600 ; N z ; B 99 0 593 426 ;
C 123 ; WX 600 ; N braceleft ; B 233 -108 569 622 ;
C 124 ; WX 600 ; N bar ; B 222 -250 485 750 ;
C 125 ; WX 600 ; N braceright ; B 140 -108 477 622 ;
C 126 ; WX 600 ; N asciitilde ; B 116 197 600 320 ;
C 161 ; WX 600 ; N exclamdown ; B 225 -157 445 430 ;
C 162 ; WX 600 ; N cent ; B 151 -49 588 614 ;
C 163 ; WX 600 ; N sterling ; B 124 -21 621 611 ;
C 164 ; WX 600 ; N fraction ; B 84 -57 646 665 ;
C 165 ; WX 600 ; N yen ; B 120 0 693 562 ;
C 166 ; WX 600 ; N florin ; B -26 -143 671 622 ;
C 167 ; WX 600 ; N section ; B 104 -78 590 580 ;
C 168 ; WX 600 ; N currency ; B 94 58 628 506 ;
C 169 ; WX 600 ; N quotesingle ; B 345 328 460 562 ;
C 170 ; WX 600 ; N quotedblleft ; B 262 328 541 562 ;
C 171 ; WX 600 ; N guillemotleft ; B 92 70 652 446 ;
C 172 ; WX 600 ; N guilsinglleft ; B 204 70 540 446 ;
C 173 ; WX 600 ; N guilsinglright ; B 170 70 506 446 ;
C 174 ; WX 600 ; N fi ; B 3 0 619 629 ;
C 175 ; WX 600 ; N fl ; B 3 0 619 629 ;
C 177 ; WX 600 ; N endash ; B 124 231 586 285 ;
C 178 ; WX 600 ; N dagger ; B 217 -78 546 580 ;
C 179 ; WX 600 ; N daggerdbl ; B 163 -78 546 580 ;
C 180 ; WX 600 ; N periodcentered ; B 275 189 434 327 ;
C 182 ; WX 600 ; N paragraph ; B 100 -78 630 562 ;
C 183 ; WX 600 ; N bullet ; B 224 130 485 383 ;
C 184 ; WX 600 ; N quotesinglbase ; B 185 -134 397 100 ;
C 185 ; WX 600 ; N quotedblbase ; B 115 -134 478 100 ;
C 186 ; WX 600 ; N quotedblright ; B 213 328 576 562 ;
C 187 ; WX 600 ; N guillemotright ; B 58 70 618 446 ;
C 188 ; WX 600 ; N ellipsis ; B 46 -15 575 111 ;
C 189 ; WX 600 ; N perthousand ; B 59 -15 627 622 ;
C 191 ; WX 600 ; N questiondown ; B 105 -157 466 430 ;
C 193 ; WX 600 ; N grave ; B 294 497 484 672 ;
C 194 ; WX 600 ; N acute ; B 348 497 612 672 ;
C 195 ; WX 600 ; N circumflex ; B 229 477 581 654 ;
C 196 ; WX 600 ; N tilde ; B 212 489 629 606 ;
C 197 ; WX 600 ; N macron ; B 232 525 600 565 ;
C 198 ; WX 600 ; N breve ; B 279 501 576 609 ;
C 199 ; WX 600 ; N dotaccent ; B 373 537 478 640 ;
C 200 ; WX 600 ; N dieresis ; B 272 537 579 640 ;
C 202 ; WX 600 ; N ring ; B 332 463 500 627 ;
C 203 ; WX 600 ; N cedilla ; B 197 -151 344 10 ;
C 205 ; WX 600 ; N hungarumlaut ; B 239 497 683 672 ;
C 206 ; WX 600 ; N ogonek ; B 189 -172 377 4 ;
C 207 ; WX 600 ; N caron ; B 262 492 614 669 ;
C 208 ; WX 600 ; N emdash ; B 49 231 661 285 ;
C 225 ; WX 600 ; N AE ; B 3 0 655 562 ;
C 227 ; WX 600 ; N ordfeminine ; B 209 249 512 580 ;
C 232 ; WX 600 ; N Lslash ; B 47 0 607 562 ;
C 233 ; WX 600 ; N Oslash ; B 94 -80 625 629 ;
C 234 ; WX 600 ; N OE ; B 59 0 672 562 ;
C 235 ; WX 600 ; N ordmasculine ; B 210 249 535 580 ;
C 241 ; WX 600 ; N ae ; B 41 -15 626 441 ;
C 245 ; WX 600 ; N dotlessi ; B 95 0 515 426 ;
C 248 ; WX 600 ; N lslash ; B 95 0 587 629 ;
C 249 ; WX 600 ; N oslash ; B 102 -80 588 506 ;
C 250 ; WX 600 ; N oe ; B 54 -15 615 441 ;
C 251 ; WX 600 ; N germandbls ; B 48 -15 617 629 ;
C -1 ; WX 600 ; N Idieresis ; B 96 0 623 753 ;
C -1 ; WX 600 ; N eacute ; B 106 -15 612 672 ;
C -1 ; WX 600 ; N abreve ; B 76 -15 576 609 ;
C -1 ; WX 600 ; N uhungarumlaut ; B 101 -15 723 672 ;
C -1 ; WX 600 ; N ecaron ; B 106 -15 614 669 ;
C -1 ; WX 600 ; N Ydieresis ; B 133 0 695 753 ;
C -1 ; WX 600 ; N divide ; B 136 48 573 467 ;
C -1 ; WX 600 ; N Yacute ; B 133 0 695 805 ;
C -1 ; WX 600 ; N Acircumflex ; B 3 0 607 787 ;
C -1 ; WX 600 ; N aacute ; B 76 -15 612 672 ;
C -1 ; WX 600 ; N Ucircumflex ; B 125 -18 702 787 ;
C -1 ; WX 600 ; N yacute ; B -4 -157 683 672 ;
C -1 ; WX 600 ; N scommaaccent ; B 78 -250 584 441 ;
C -1 ; WX 600 ; N ecircumflex ; B 106 -15 598 654 ;
C -1 ; WX 600 ; N Uring ; B 125 -18 702 760 ;
C -1 ; WX 600 ; N Udieresis ; B 125 -18 702 753 ;
C -1 ; WX 600 ; N aogonek ; B 76 -172 569 441 ;
C -1 ; WX 600 ; N Uacute ; B 125 -18 702 805 ;
C -1 ; WX 600 ; N uogonek ; B 101 -172 572 426 ;
C -1 ; WX 600 ; N Edieresis ; B 53 0 660 753 ;
C -1 ; WX 600 ; N Dcroat ; B 43 0 645 562 ;
C -1 ; WX 600 ; N commaaccent ; B 145 -250 323 -58 ;
C -1 ; WX 600 ; N copyright ; B 53 -18 667 580 ;
C -1 ; WX 600 ; N Emacron ; B 53 0 660 698 ;
C -1 ; WX 600 ; N ccaron ; B 106 -15 614 669 ;
C -1 ; WX 600 ; N aring ; B 76 -15 569 627 ;
C -1 ; WX 600 ; N Ncommaaccent ; B 7 -250 712 562 ;
C -1 ; WX 600 ; N lacute ; B 95 0 640 805 ;
C -1 ; WX 600 ; N agrave ; B 76 -15 569 672 ;
C -1 ; WX 600 ; N Tcommaaccent ; B 108 -250 665 562 ;
C -1 ; WX 600 ; N Cacute ; B 93 -18 655 805 ;
C -1 ; WX 600 ; N atilde ; B 76 -15 629 606 ;
C -1 ; WX 600 ; N Edotaccent ; B 53 0 660 753 ;
C -1 ; WX 600 ; N scaron ; B 78 -15 614 669 ;
C -1 ; WX 600 ; N scedilla ; B 78 -151 584 441 ;
C -1 ; WX 600 ; N iacute ; B 95 0 612 672 ;
C -1 ; WX 600 ; N lozenge ; B 94 0 519 706 ;
C -1 ; WX 600 ; N Rcaron ; B 38 0 642 802 ;
C -1 ; WX 600 ; N Gcommaaccent ; B 83 -250 645 580 ;
C -1 ; WX 600 ; N ucircumflex ; B 101 -15 572 654 ;
C -1 ; WX 600 ; N acircumflex ; B 76 -15 581 654 ;
C -1 ; WX 600 ; N Amacron ; B 3 0 607 698 ;
C -1 ; WX 600 ; N rcaron ; B 60 0 636 669 ;
C -1 ; WX 600 ; N ccedilla ; B 106 -151 614 441 ;
C -1 ; WX 600 ; N Zdotaccent ; B 86 0 610 753 ;
C -1 ; WX 600 ; N Thorn ; B 79 0 606 562 ;
C -1 ; WX 600 ; N Omacron ; B 94 -18 628 698 ;
C -1 ; WX 600 ; N Racute ; B 38 0 670 805 ;
C -1 ; WX 600 ; N Sacute ; B 76 -20 650 805 ;
C -1 ; WX 600 ; N dcaron ; B 85 -15 849 629 ;
C -1 ; WX 600 ; N Umacron ; B 125 -18 702 698 ;
C -1 ; WX 600 ; N uring ; B 101 -15 572 627 ;
C -1 ; WX 600 ; N threesuperior ; B 213 240 501 622 ;
C -1 ; WX 600 ; N Ograve ; B 94 -18 625 805 ;
C -1 ; WX 600 ; N Agrave ; B 3 0 607 805 ;
C -1 ; WX 600 ; N Abreve ; B 3 0 607 732 ;
C -1 ; WX 600 ; N multiply ; B 103 43 607 470 ;
C -1 ; WX 600 ; N uacute ; B 101 -15 602 672 ;
C -1 ; WX 600 ; N Tcaron ; B 108 0 665 802 ;
C -1 ; WX 600 ; N partialdiff ; B 45 -38 546 710 ;
C -1 ; WX 600 ; N ydieresis ; B -4 -157 683 620 ;
C -1 ; WX 600 ; N Nacute ; B 7 -13 712 805 ;
C -1 ; WX 600 ; N icircumflex ; B 95 0 551 654 ;
C -1 ; WX 600 ; N Ecircumflex ; B 53 0 660 787 ;
C -1 ; WX 600 ; N adieresis ; B 76 -15 575 620 ;
C -1 ; WX 600 ; N edieresis ; B 106 -15 598 620 ;
C -1 ; WX 600 ; N cacute ; B 106 -15 612 672 ;
C -1 ; WX 600 ; N nacute ; B 26 0 602 672 ;
C -1 ; WX 600 ; N umacron ; B 101 -15 600 565 ;
C -1 ; WX 600 ; N Ncaron ; B 7 -13 712 802 ;
C -1 ; WX 600 ; N Iacute ; B 96 0 640 805 ;
C -1 ; WX 600 ; N plusminus ; B 96 44 594 558 ;
C -1 ; WX 600 ; N brokenbar ; B 238 -175 469 675 ;
C -1 ; WX 600 ; N registered ; B 53 -18 667 580 ;
C -1 ; WX 600 ; N Gbreve ; B 83 -18 645 732 ;
C -1 ; WX 600 ; N Idotaccent ; B 96 0 623 753 ;
C -1 ; WX 600 ; N summation ; B 15 -10 670 706 ;
C -1 ; WX 600 ; N Egrave ; B 53 0 660 805 ;
C -1 ; WX 600 ; N racute ; B 60 0 636 672 ;
C -1 ; WX 600 ; N omacron ; B 102 -15 600 565 ;
C -1 ; WX 600 ; N Zacute ; B 86 0 670 805 ;
C -1 ; WX 600 ; N Zcaron ; B 86 0 642 802 ;
C -1 ; WX 600 ; N greaterequal ; B 98 0 594 710 ;
C -1 ; WX 600 ; N Eth ; B 43 0 645 562 ;
C -1 ; WX 600 ; N Ccedilla ; B 93 -151 658 580 ;
C -1 ; WX 600 ; N lcommaaccent ; B 95 -250 515 629 ;
C -1 ; WX 600 ; N tcaron ; B 167 -15 587 717 ;
C -1 ; WX 600 ; N eogonek ; B 106 -172 598 441 ;
C -1 ; WX 600 ; N Uogonek ; B 124 -172 702 562 ;
C -1 ; WX 600 ; N Aacute ; B 3 0 660 805 ;
C -1 ; WX 600 ; N Adieresis ; B 3 0 607 753 ;
C -1 ; WX 600 ; N egrave ; B 106 -15 598 672 ;
C -1 ; WX 600 ; N zacute ; B 99 0 612 672 ;
C -1 ; WX 600 ; N iogonek ; B 95 -172 515 657 ;
C -1 ; WX 600 ; N Oacute ; B 94 -18 640 805 ;
C -1 ; WX 600 ; N oacute ; B 102 -15 612 672 ;
C -1 ; WX 600 ; N amacron ; B 76 -15 600 565 ;
C -1 ; WX 600 ; N sacute ; B 78 -15 612 672 ;
C -1 ; WX 600 ; N idieresis ; B 95 0 545 620 ;
C -1 ; WX 600 ; N Ocircumflex ; B 94 -18 625 787 ;
C -1 ; WX 600 ; N Ugrave ; B 125 -18 702 805 ;
C -1 ; WX 600 ; N Delta ; B 6 0 598 688 ;
C -1 ; WX 600 ; N thorn ; B -24 -157 605 629 ;
C -1 ; WX 600 ; N twosuperior ; B 230 249 535 622 ;
C -1 ; WX 600 ; N Odieresis ; B 94 -18 625 753 ;
C -1 ; WX 600 ; N mu ; B 72 -157 572 426 ;
C -1 ; WX 600 ; N igrave ; B 95 0 515 672 ;
C -1 ; WX 600 ; N ohungarumlaut ; B 102 -15 723 672 ;
C -1 ; WX 600 ; N Eogonek ; B 53 -172 660 562 ;
C -1 ; WX 600 ; N dcroat ; B 85 -15 704 629 ;
C -1 ; WX 600 ; N threequarters ; B 73 -56 659 666 ;
C -1 ; WX 600 ; N Scedilla ; B 76 -151 650 580 ;
C -1 ; WX 600 ; N lcaron ; B 95 0 667 629 ;
C -1 ; WX 600 ; N Kcommaaccent ; B 38 -250 671 562 ;
C -1 ; WX 600 ; N Lacute ; B 47 0 607 805 ;
C -1 ; WX 600 ; N trademark ; B 75 263 742 562 ;
C -1 ; WX 600 ; N edotaccent ; B 106 -15 598 620 ;
C -1 ; WX 600 ; N Igrave ; B 96 0 623 805 ;
C -1 ; WX 600 ; N Imacron ; B 96 0 628 698 ;
C -1 ; WX 600 ; N Lcaron ; B 47 0 632 562 ;
C -1 ; WX 600 ; N onehalf ; B 65 -57 669 665 ;
C -1 ; WX 600 ; N lessequal ; B 98 0 645 710 ;
C -1 ; WX 600 ; N ocircumflex ; B 102 -15 588 654 ;
C -1 ; WX 600 ; N ntilde ; B 26 0 629 606 ;
C -1 ; WX 600 ; N Uhungarumlaut ; B 125 -18 761 805 ;
C -1 ; WX 600 ; N Eacute ; B 53 0 670 805 ;
C -1 ; WX 600 ; N emacron ; B 106 -15 600 565 ;
C -1 ; WX 600 ; N gbreve ; B 61 -157 657 609 ;
C -1 ; WX 600 ; N onequarter ; B 65 -57 674 665 ;
C -1 ; WX 600 ; N Scaron ; B 76 -20 672 802 ;
C -1 ; WX 600 ; N Scommaaccent ; B 76 -250 650 580 ;
C -1 ; WX 600 ; N Ohungarumlaut ; B 94 -18 751 805 ;
C -1 ; WX 600 ; N degree ; B 214 269 576 622 ;
C -1 ; WX 600 ; N ograve ; B 102 -15 588 672 ;
C -1 ; WX 600 ; N Ccaron ; B 93 -18 672 802 ;
C -1 ; WX 600 ; N ugrave ; B 101 -15 572 672 ;
C -1 ; WX 600 ; N radical ; B 85 -15 765 792 ;
C -1 ; WX 600 ; N Dcaron ; B 43 0 645 802 ;
C -1 ; WX 600 ; N rcommaaccent ; B 60 -250 636 441 ;
C -1 ; WX 600 ; N Ntilde ; B 7 -13 712 729 ;
C -1 ; WX 600 ; N otilde ; B 102 -15 629 606 ;
C -1 ; WX 600 ; N Rcommaaccent ; B 38 -250 598 562 ;
C -1 ; WX 600 ; N Lcommaaccent ; B 47 -250 607 562 ;
C -1 ; WX 600 ; N Atilde ; B 3 0 655 729 ;
C -1 ; WX 600 ; N Aogonek ; B 3 -172 607 562 ;
C -1 ; WX 600 ; N Aring ; B 3 0 607 750 ;
C -1 ; WX 600 ; N Otilde ; B 94 -18 655 729 ;
C -1 ; WX 600 ; N zdotaccent ; B 99 0 593 620 ;
C -1 ; WX 600 ; N Ecaron ; B 53 0 660 802 ;
C -1 ; WX 600 ; N Iogonek ; B 96 -172 623 562 ;
C -1 ; WX 600 ; N kcommaaccent ; B 58 -250 633 629 ;
C -1 ; WX 600 ; N minus ; B 129 232 580 283 ;
C -1 ; WX 600 ; N Icircumflex ; B 96 0 623 787 ;
C -1 ; WX 600 ; N ncaron ; B 26 0 614 669 ;
C -1 ; WX 600 ; N tcommaaccent ; B 165 -250 561 561 ;
C -1 ; WX 600 ; N logicalnot ; B 155 108 591 369 ;
C -1 ; WX 600 ; N odieresis ; B 102 -15 588 620 ;
C -1 ; WX 600 ; N udieresis ; B 101 -15 575 620 ;
C -1 ; WX 600 ; N notequal ; B 43 -16 621 529 ;
C -1 ; WX 600 ; N gcommaaccent ; B 61 -157 657 708 ;
C -1 ; WX 600 ; N eth ; B 102 -15 639 629 ;
C -1 ; WX 600 ; N zcaron ; B 99 0 624 669 ;
C -1 ; WX 600 ; N ncommaaccent ; B 26 -250 585 441 ;
C -1 ; WX 600 ; N onesuperior ; B 231 249 491 622 ;
C -1 ; WX 600 ; N imacron ; B 95 0 543 565 ;
C -1 ; WX 600 ; N Euro ; B 0 0 0 0 ;
EndCharMetrics
EndFontMetrics

View File

@@ -0,0 +1,342 @@
StartFontMetrics 4.1
Comment Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
Comment Creation Date: Thu May 1 17:27:09 1997
Comment UniqueID 43050
Comment VMusage 39754 50779
FontName Courier
FullName Courier
FamilyName Courier
Weight Medium
ItalicAngle 0
IsFixedPitch true
CharacterSet ExtendedRoman
FontBBox -23 -250 715 805
UnderlinePosition -100
UnderlineThickness 50
Version 003.000
Notice Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
EncodingScheme AdobeStandardEncoding
CapHeight 562
XHeight 426
Ascender 629
Descender -157
StdHW 51
StdVW 51
StartCharMetrics 315
C 32 ; WX 600 ; N space ; B 0 0 0 0 ;
C 33 ; WX 600 ; N exclam ; B 236 -15 364 572 ;
C 34 ; WX 600 ; N quotedbl ; B 187 328 413 562 ;
C 35 ; WX 600 ; N numbersign ; B 93 -32 507 639 ;
C 36 ; WX 600 ; N dollar ; B 105 -126 496 662 ;
C 37 ; WX 600 ; N percent ; B 81 -15 518 622 ;
C 38 ; WX 600 ; N ampersand ; B 63 -15 538 543 ;
C 39 ; WX 600 ; N quoteright ; B 213 328 376 562 ;
C 40 ; WX 600 ; N parenleft ; B 269 -108 440 622 ;
C 41 ; WX 600 ; N parenright ; B 160 -108 331 622 ;
C 42 ; WX 600 ; N asterisk ; B 116 257 484 607 ;
C 43 ; WX 600 ; N plus ; B 80 44 520 470 ;
C 44 ; WX 600 ; N comma ; B 181 -112 344 122 ;
C 45 ; WX 600 ; N hyphen ; B 103 231 497 285 ;
C 46 ; WX 600 ; N period ; B 229 -15 371 109 ;
C 47 ; WX 600 ; N slash ; B 125 -80 475 629 ;
C 48 ; WX 600 ; N zero ; B 106 -15 494 622 ;
C 49 ; WX 600 ; N one ; B 96 0 505 622 ;
C 50 ; WX 600 ; N two ; B 70 0 471 622 ;
C 51 ; WX 600 ; N three ; B 75 -15 466 622 ;
C 52 ; WX 600 ; N four ; B 78 0 500 622 ;
C 53 ; WX 600 ; N five ; B 92 -15 497 607 ;
C 54 ; WX 600 ; N six ; B 111 -15 497 622 ;
C 55 ; WX 600 ; N seven ; B 82 0 483 607 ;
C 56 ; WX 600 ; N eight ; B 102 -15 498 622 ;
C 57 ; WX 600 ; N nine ; B 96 -15 489 622 ;
C 58 ; WX 600 ; N colon ; B 229 -15 371 385 ;
C 59 ; WX 600 ; N semicolon ; B 181 -112 371 385 ;
C 60 ; WX 600 ; N less ; B 41 42 519 472 ;
C 61 ; WX 600 ; N equal ; B 80 138 520 376 ;
C 62 ; WX 600 ; N greater ; B 66 42 544 472 ;
C 63 ; WX 600 ; N question ; B 129 -15 492 572 ;
C 64 ; WX 600 ; N at ; B 77 -15 533 622 ;
C 65 ; WX 600 ; N A ; B 3 0 597 562 ;
C 66 ; WX 600 ; N B ; B 43 0 559 562 ;
C 67 ; WX 600 ; N C ; B 41 -18 540 580 ;
C 68 ; WX 600 ; N D ; B 43 0 574 562 ;
C 69 ; WX 600 ; N E ; B 53 0 550 562 ;
C 70 ; WX 600 ; N F ; B 53 0 545 562 ;
C 71 ; WX 600 ; N G ; B 31 -18 575 580 ;
C 72 ; WX 600 ; N H ; B 32 0 568 562 ;
C 73 ; WX 600 ; N I ; B 96 0 504 562 ;
C 74 ; WX 600 ; N J ; B 34 -18 566 562 ;
C 75 ; WX 600 ; N K ; B 38 0 582 562 ;
C 76 ; WX 600 ; N L ; B 47 0 554 562 ;
C 77 ; WX 600 ; N M ; B 4 0 596 562 ;
C 78 ; WX 600 ; N N ; B 7 -13 593 562 ;
C 79 ; WX 600 ; N O ; B 43 -18 557 580 ;
C 80 ; WX 600 ; N P ; B 79 0 558 562 ;
C 81 ; WX 600 ; N Q ; B 43 -138 557 580 ;
C 82 ; WX 600 ; N R ; B 38 0 588 562 ;
C 83 ; WX 600 ; N S ; B 72 -20 529 580 ;
C 84 ; WX 600 ; N T ; B 38 0 563 562 ;
C 85 ; WX 600 ; N U ; B 17 -18 583 562 ;
C 86 ; WX 600 ; N V ; B -4 -13 604 562 ;
C 87 ; WX 600 ; N W ; B -3 -13 603 562 ;
C 88 ; WX 600 ; N X ; B 23 0 577 562 ;
C 89 ; WX 600 ; N Y ; B 24 0 576 562 ;
C 90 ; WX 600 ; N Z ; B 86 0 514 562 ;
C 91 ; WX 600 ; N bracketleft ; B 269 -108 442 622 ;
C 92 ; WX 600 ; N backslash ; B 118 -80 482 629 ;
C 93 ; WX 600 ; N bracketright ; B 158 -108 331 622 ;
C 94 ; WX 600 ; N asciicircum ; B 94 354 506 622 ;
C 95 ; WX 600 ; N underscore ; B 0 -125 600 -75 ;
C 96 ; WX 600 ; N quoteleft ; B 224 328 387 562 ;
C 97 ; WX 600 ; N a ; B 53 -15 559 441 ;
C 98 ; WX 600 ; N b ; B 14 -15 575 629 ;
C 99 ; WX 600 ; N c ; B 66 -15 529 441 ;
C 100 ; WX 600 ; N d ; B 45 -15 591 629 ;
C 101 ; WX 600 ; N e ; B 66 -15 548 441 ;
C 102 ; WX 600 ; N f ; B 114 0 531 629 ; L i fi ; L l fl ;
C 103 ; WX 600 ; N g ; B 45 -157 566 441 ;
C 104 ; WX 600 ; N h ; B 18 0 582 629 ;
C 105 ; WX 600 ; N i ; B 95 0 505 657 ;
C 106 ; WX 600 ; N j ; B 82 -157 410 657 ;
C 107 ; WX 600 ; N k ; B 43 0 580 629 ;
C 108 ; WX 600 ; N l ; B 95 0 505 629 ;
C 109 ; WX 600 ; N m ; B -5 0 605 441 ;
C 110 ; WX 600 ; N n ; B 26 0 575 441 ;
C 111 ; WX 600 ; N o ; B 62 -15 538 441 ;
C 112 ; WX 600 ; N p ; B 9 -157 555 441 ;
C 113 ; WX 600 ; N q ; B 45 -157 591 441 ;
C 114 ; WX 600 ; N r ; B 60 0 559 441 ;
C 115 ; WX 600 ; N s ; B 80 -15 513 441 ;
C 116 ; WX 600 ; N t ; B 87 -15 530 561 ;
C 117 ; WX 600 ; N u ; B 21 -15 562 426 ;
C 118 ; WX 600 ; N v ; B 10 -10 590 426 ;
C 119 ; WX 600 ; N w ; B -4 -10 604 426 ;
C 120 ; WX 600 ; N x ; B 20 0 580 426 ;
C 121 ; WX 600 ; N y ; B 7 -157 592 426 ;
C 122 ; WX 600 ; N z ; B 99 0 502 426 ;
C 123 ; WX 600 ; N braceleft ; B 182 -108 437 622 ;
C 124 ; WX 600 ; N bar ; B 275 -250 326 750 ;
C 125 ; WX 600 ; N braceright ; B 163 -108 418 622 ;
C 126 ; WX 600 ; N asciitilde ; B 63 197 540 320 ;
C 161 ; WX 600 ; N exclamdown ; B 236 -157 364 430 ;
C 162 ; WX 600 ; N cent ; B 96 -49 500 614 ;
C 163 ; WX 600 ; N sterling ; B 84 -21 521 611 ;
C 164 ; WX 600 ; N fraction ; B 92 -57 509 665 ;
C 165 ; WX 600 ; N yen ; B 26 0 574 562 ;
C 166 ; WX 600 ; N florin ; B 4 -143 539 622 ;
C 167 ; WX 600 ; N section ; B 113 -78 488 580 ;
C 168 ; WX 600 ; N currency ; B 73 58 527 506 ;
C 169 ; WX 600 ; N quotesingle ; B 259 328 341 562 ;
C 170 ; WX 600 ; N quotedblleft ; B 143 328 471 562 ;
C 171 ; WX 600 ; N guillemotleft ; B 37 70 563 446 ;
C 172 ; WX 600 ; N guilsinglleft ; B 149 70 451 446 ;
C 173 ; WX 600 ; N guilsinglright ; B 149 70 451 446 ;
C 174 ; WX 600 ; N fi ; B 3 0 597 629 ;
C 175 ; WX 600 ; N fl ; B 3 0 597 629 ;
C 177 ; WX 600 ; N endash ; B 75 231 525 285 ;
C 178 ; WX 600 ; N dagger ; B 141 -78 459 580 ;
C 179 ; WX 600 ; N daggerdbl ; B 141 -78 459 580 ;
C 180 ; WX 600 ; N periodcentered ; B 222 189 378 327 ;
C 182 ; WX 600 ; N paragraph ; B 50 -78 511 562 ;
C 183 ; WX 600 ; N bullet ; B 172 130 428 383 ;
C 184 ; WX 600 ; N quotesinglbase ; B 213 -134 376 100 ;
C 185 ; WX 600 ; N quotedblbase ; B 143 -134 457 100 ;
C 186 ; WX 600 ; N quotedblright ; B 143 328 457 562 ;
C 187 ; WX 600 ; N guillemotright ; B 37 70 563 446 ;
C 188 ; WX 600 ; N ellipsis ; B 37 -15 563 111 ;
C 189 ; WX 600 ; N perthousand ; B 3 -15 600 622 ;
C 191 ; WX 600 ; N questiondown ; B 108 -157 471 430 ;
C 193 ; WX 600 ; N grave ; B 151 497 378 672 ;
C 194 ; WX 600 ; N acute ; B 242 497 469 672 ;
C 195 ; WX 600 ; N circumflex ; B 124 477 476 654 ;
C 196 ; WX 600 ; N tilde ; B 105 489 503 606 ;
C 197 ; WX 600 ; N macron ; B 120 525 480 565 ;
C 198 ; WX 600 ; N breve ; B 153 501 447 609 ;
C 199 ; WX 600 ; N dotaccent ; B 249 537 352 640 ;
C 200 ; WX 600 ; N dieresis ; B 148 537 453 640 ;
C 202 ; WX 600 ; N ring ; B 218 463 382 627 ;
C 203 ; WX 600 ; N cedilla ; B 224 -151 362 10 ;
C 205 ; WX 600 ; N hungarumlaut ; B 133 497 540 672 ;
C 206 ; WX 600 ; N ogonek ; B 211 -172 407 4 ;
C 207 ; WX 600 ; N caron ; B 124 492 476 669 ;
C 208 ; WX 600 ; N emdash ; B 0 231 600 285 ;
C 225 ; WX 600 ; N AE ; B 3 0 550 562 ;
C 227 ; WX 600 ; N ordfeminine ; B 156 249 442 580 ;
C 232 ; WX 600 ; N Lslash ; B 47 0 554 562 ;
C 233 ; WX 600 ; N Oslash ; B 43 -80 557 629 ;
C 234 ; WX 600 ; N OE ; B 7 0 567 562 ;
C 235 ; WX 600 ; N ordmasculine ; B 157 249 443 580 ;
C 241 ; WX 600 ; N ae ; B 19 -15 570 441 ;
C 245 ; WX 600 ; N dotlessi ; B 95 0 505 426 ;
C 248 ; WX 600 ; N lslash ; B 95 0 505 629 ;
C 249 ; WX 600 ; N oslash ; B 62 -80 538 506 ;
C 250 ; WX 600 ; N oe ; B 19 -15 559 441 ;
C 251 ; WX 600 ; N germandbls ; B 48 -15 588 629 ;
C -1 ; WX 600 ; N Idieresis ; B 96 0 504 753 ;
C -1 ; WX 600 ; N eacute ; B 66 -15 548 672 ;
C -1 ; WX 600 ; N abreve ; B 53 -15 559 609 ;
C -1 ; WX 600 ; N uhungarumlaut ; B 21 -15 580 672 ;
C -1 ; WX 600 ; N ecaron ; B 66 -15 548 669 ;
C -1 ; WX 600 ; N Ydieresis ; B 24 0 576 753 ;
C -1 ; WX 600 ; N divide ; B 87 48 513 467 ;
C -1 ; WX 600 ; N Yacute ; B 24 0 576 805 ;
C -1 ; WX 600 ; N Acircumflex ; B 3 0 597 787 ;
C -1 ; WX 600 ; N aacute ; B 53 -15 559 672 ;
C -1 ; WX 600 ; N Ucircumflex ; B 17 -18 583 787 ;
C -1 ; WX 600 ; N yacute ; B 7 -157 592 672 ;
C -1 ; WX 600 ; N scommaaccent ; B 80 -250 513 441 ;
C -1 ; WX 600 ; N ecircumflex ; B 66 -15 548 654 ;
C -1 ; WX 600 ; N Uring ; B 17 -18 583 760 ;
C -1 ; WX 600 ; N Udieresis ; B 17 -18 583 753 ;
C -1 ; WX 600 ; N aogonek ; B 53 -172 587 441 ;
C -1 ; WX 600 ; N Uacute ; B 17 -18 583 805 ;
C -1 ; WX 600 ; N uogonek ; B 21 -172 590 426 ;
C -1 ; WX 600 ; N Edieresis ; B 53 0 550 753 ;
C -1 ; WX 600 ; N Dcroat ; B 30 0 574 562 ;
C -1 ; WX 600 ; N commaaccent ; B 198 -250 335 -58 ;
C -1 ; WX 600 ; N copyright ; B 0 -18 600 580 ;
C -1 ; WX 600 ; N Emacron ; B 53 0 550 698 ;
C -1 ; WX 600 ; N ccaron ; B 66 -15 529 669 ;
C -1 ; WX 600 ; N aring ; B 53 -15 559 627 ;
C -1 ; WX 600 ; N Ncommaaccent ; B 7 -250 593 562 ;
C -1 ; WX 600 ; N lacute ; B 95 0 505 805 ;
C -1 ; WX 600 ; N agrave ; B 53 -15 559 672 ;
C -1 ; WX 600 ; N Tcommaaccent ; B 38 -250 563 562 ;
C -1 ; WX 600 ; N Cacute ; B 41 -18 540 805 ;
C -1 ; WX 600 ; N atilde ; B 53 -15 559 606 ;
C -1 ; WX 600 ; N Edotaccent ; B 53 0 550 753 ;
C -1 ; WX 600 ; N scaron ; B 80 -15 513 669 ;
C -1 ; WX 600 ; N scedilla ; B 80 -151 513 441 ;
C -1 ; WX 600 ; N iacute ; B 95 0 505 672 ;
C -1 ; WX 600 ; N lozenge ; B 18 0 443 706 ;
C -1 ; WX 600 ; N Rcaron ; B 38 0 588 802 ;
C -1 ; WX 600 ; N Gcommaaccent ; B 31 -250 575 580 ;
C -1 ; WX 600 ; N ucircumflex ; B 21 -15 562 654 ;
C -1 ; WX 600 ; N acircumflex ; B 53 -15 559 654 ;
C -1 ; WX 600 ; N Amacron ; B 3 0 597 698 ;
C -1 ; WX 600 ; N rcaron ; B 60 0 559 669 ;
C -1 ; WX 600 ; N ccedilla ; B 66 -151 529 441 ;
C -1 ; WX 600 ; N Zdotaccent ; B 86 0 514 753 ;
C -1 ; WX 600 ; N Thorn ; B 79 0 538 562 ;
C -1 ; WX 600 ; N Omacron ; B 43 -18 557 698 ;
C -1 ; WX 600 ; N Racute ; B 38 0 588 805 ;
C -1 ; WX 600 ; N Sacute ; B 72 -20 529 805 ;
C -1 ; WX 600 ; N dcaron ; B 45 -15 715 629 ;
C -1 ; WX 600 ; N Umacron ; B 17 -18 583 698 ;
C -1 ; WX 600 ; N uring ; B 21 -15 562 627 ;
C -1 ; WX 600 ; N threesuperior ; B 155 240 406 622 ;
C -1 ; WX 600 ; N Ograve ; B 43 -18 557 805 ;
C -1 ; WX 600 ; N Agrave ; B 3 0 597 805 ;
C -1 ; WX 600 ; N Abreve ; B 3 0 597 732 ;
C -1 ; WX 600 ; N multiply ; B 87 43 515 470 ;
C -1 ; WX 600 ; N uacute ; B 21 -15 562 672 ;
C -1 ; WX 600 ; N Tcaron ; B 38 0 563 802 ;
C -1 ; WX 600 ; N partialdiff ; B 17 -38 459 710 ;
C -1 ; WX 600 ; N ydieresis ; B 7 -157 592 620 ;
C -1 ; WX 600 ; N Nacute ; B 7 -13 593 805 ;
C -1 ; WX 600 ; N icircumflex ; B 94 0 505 654 ;
C -1 ; WX 600 ; N Ecircumflex ; B 53 0 550 787 ;
C -1 ; WX 600 ; N adieresis ; B 53 -15 559 620 ;
C -1 ; WX 600 ; N edieresis ; B 66 -15 548 620 ;
C -1 ; WX 600 ; N cacute ; B 66 -15 529 672 ;
C -1 ; WX 600 ; N nacute ; B 26 0 575 672 ;
C -1 ; WX 600 ; N umacron ; B 21 -15 562 565 ;
C -1 ; WX 600 ; N Ncaron ; B 7 -13 593 802 ;
C -1 ; WX 600 ; N Iacute ; B 96 0 504 805 ;
C -1 ; WX 600 ; N plusminus ; B 87 44 513 558 ;
C -1 ; WX 600 ; N brokenbar ; B 275 -175 326 675 ;
C -1 ; WX 600 ; N registered ; B 0 -18 600 580 ;
C -1 ; WX 600 ; N Gbreve ; B 31 -18 575 732 ;
C -1 ; WX 600 ; N Idotaccent ; B 96 0 504 753 ;
C -1 ; WX 600 ; N summation ; B 15 -10 585 706 ;
C -1 ; WX 600 ; N Egrave ; B 53 0 550 805 ;
C -1 ; WX 600 ; N racute ; B 60 0 559 672 ;
C -1 ; WX 600 ; N omacron ; B 62 -15 538 565 ;
C -1 ; WX 600 ; N Zacute ; B 86 0 514 805 ;
C -1 ; WX 600 ; N Zcaron ; B 86 0 514 802 ;
C -1 ; WX 600 ; N greaterequal ; B 98 0 502 710 ;
C -1 ; WX 600 ; N Eth ; B 30 0 574 562 ;
C -1 ; WX 600 ; N Ccedilla ; B 41 -151 540 580 ;
C -1 ; WX 600 ; N lcommaaccent ; B 95 -250 505 629 ;
C -1 ; WX 600 ; N tcaron ; B 87 -15 530 717 ;
C -1 ; WX 600 ; N eogonek ; B 66 -172 548 441 ;
C -1 ; WX 600 ; N Uogonek ; B 17 -172 583 562 ;
C -1 ; WX 600 ; N Aacute ; B 3 0 597 805 ;
C -1 ; WX 600 ; N Adieresis ; B 3 0 597 753 ;
C -1 ; WX 600 ; N egrave ; B 66 -15 548 672 ;
C -1 ; WX 600 ; N zacute ; B 99 0 502 672 ;
C -1 ; WX 600 ; N iogonek ; B 95 -172 505 657 ;
C -1 ; WX 600 ; N Oacute ; B 43 -18 557 805 ;
C -1 ; WX 600 ; N oacute ; B 62 -15 538 672 ;
C -1 ; WX 600 ; N amacron ; B 53 -15 559 565 ;
C -1 ; WX 600 ; N sacute ; B 80 -15 513 672 ;
C -1 ; WX 600 ; N idieresis ; B 95 0 505 620 ;
C -1 ; WX 600 ; N Ocircumflex ; B 43 -18 557 787 ;
C -1 ; WX 600 ; N Ugrave ; B 17 -18 583 805 ;
C -1 ; WX 600 ; N Delta ; B 6 0 598 688 ;
C -1 ; WX 600 ; N thorn ; B -6 -157 555 629 ;
C -1 ; WX 600 ; N twosuperior ; B 177 249 424 622 ;
C -1 ; WX 600 ; N Odieresis ; B 43 -18 557 753 ;
C -1 ; WX 600 ; N mu ; B 21 -157 562 426 ;
C -1 ; WX 600 ; N igrave ; B 95 0 505 672 ;
C -1 ; WX 600 ; N ohungarumlaut ; B 62 -15 580 672 ;
C -1 ; WX 600 ; N Eogonek ; B 53 -172 561 562 ;
C -1 ; WX 600 ; N dcroat ; B 45 -15 591 629 ;
C -1 ; WX 600 ; N threequarters ; B 8 -56 593 666 ;
C -1 ; WX 600 ; N Scedilla ; B 72 -151 529 580 ;
C -1 ; WX 600 ; N lcaron ; B 95 0 533 629 ;
C -1 ; WX 600 ; N Kcommaaccent ; B 38 -250 582 562 ;
C -1 ; WX 600 ; N Lacute ; B 47 0 554 805 ;
C -1 ; WX 600 ; N trademark ; B -23 263 623 562 ;
C -1 ; WX 600 ; N edotaccent ; B 66 -15 548 620 ;
C -1 ; WX 600 ; N Igrave ; B 96 0 504 805 ;
C -1 ; WX 600 ; N Imacron ; B 96 0 504 698 ;
C -1 ; WX 600 ; N Lcaron ; B 47 0 554 562 ;
C -1 ; WX 600 ; N onehalf ; B 0 -57 611 665 ;
C -1 ; WX 600 ; N lessequal ; B 98 0 502 710 ;
C -1 ; WX 600 ; N ocircumflex ; B 62 -15 538 654 ;
C -1 ; WX 600 ; N ntilde ; B 26 0 575 606 ;
C -1 ; WX 600 ; N Uhungarumlaut ; B 17 -18 590 805 ;
C -1 ; WX 600 ; N Eacute ; B 53 0 550 805 ;
C -1 ; WX 600 ; N emacron ; B 66 -15 548 565 ;
C -1 ; WX 600 ; N gbreve ; B 45 -157 566 609 ;
C -1 ; WX 600 ; N onequarter ; B 0 -57 600 665 ;
C -1 ; WX 600 ; N Scaron ; B 72 -20 529 802 ;
C -1 ; WX 600 ; N Scommaaccent ; B 72 -250 529 580 ;
C -1 ; WX 600 ; N Ohungarumlaut ; B 43 -18 580 805 ;
C -1 ; WX 600 ; N degree ; B 123 269 477 622 ;
C -1 ; WX 600 ; N ograve ; B 62 -15 538 672 ;
C -1 ; WX 600 ; N Ccaron ; B 41 -18 540 802 ;
C -1 ; WX 600 ; N ugrave ; B 21 -15 562 672 ;
C -1 ; WX 600 ; N radical ; B 3 -15 597 792 ;
C -1 ; WX 600 ; N Dcaron ; B 43 0 574 802 ;
C -1 ; WX 600 ; N rcommaaccent ; B 60 -250 559 441 ;
C -1 ; WX 600 ; N Ntilde ; B 7 -13 593 729 ;
C -1 ; WX 600 ; N otilde ; B 62 -15 538 606 ;
C -1 ; WX 600 ; N Rcommaaccent ; B 38 -250 588 562 ;
C -1 ; WX 600 ; N Lcommaaccent ; B 47 -250 554 562 ;
C -1 ; WX 600 ; N Atilde ; B 3 0 597 729 ;
C -1 ; WX 600 ; N Aogonek ; B 3 -172 608 562 ;
C -1 ; WX 600 ; N Aring ; B 3 0 597 750 ;
C -1 ; WX 600 ; N Otilde ; B 43 -18 557 729 ;
C -1 ; WX 600 ; N zdotaccent ; B 99 0 502 620 ;
C -1 ; WX 600 ; N Ecaron ; B 53 0 550 802 ;
C -1 ; WX 600 ; N Iogonek ; B 96 -172 504 562 ;
C -1 ; WX 600 ; N kcommaaccent ; B 43 -250 580 629 ;
C -1 ; WX 600 ; N minus ; B 80 232 520 283 ;
C -1 ; WX 600 ; N Icircumflex ; B 96 0 504 787 ;
C -1 ; WX 600 ; N ncaron ; B 26 0 575 669 ;
C -1 ; WX 600 ; N tcommaaccent ; B 87 -250 530 561 ;
C -1 ; WX 600 ; N logicalnot ; B 87 108 513 369 ;
C -1 ; WX 600 ; N odieresis ; B 62 -15 538 620 ;
C -1 ; WX 600 ; N udieresis ; B 21 -15 562 620 ;
C -1 ; WX 600 ; N notequal ; B 15 -16 540 529 ;
C -1 ; WX 600 ; N gcommaaccent ; B 45 -157 566 708 ;
C -1 ; WX 600 ; N eth ; B 62 -15 538 629 ;
C -1 ; WX 600 ; N zcaron ; B 99 0 502 669 ;
C -1 ; WX 600 ; N ncommaaccent ; B 26 -250 575 441 ;
C -1 ; WX 600 ; N onesuperior ; B 172 249 428 622 ;
C -1 ; WX 600 ; N imacron ; B 95 0 505 565 ;
C -1 ; WX 600 ; N Euro ; B 0 0 0 0 ;
EndCharMetrics
EndFontMetrics

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
<html>

View File

@@ -0,0 +1,213 @@
StartFontMetrics 4.1
Comment Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All rights reserved.
Comment Creation Date: Thu May 1 15:12:25 1997
Comment UniqueID 43064
Comment VMusage 30820 39997
FontName Symbol
FullName Symbol
FamilyName Symbol
Weight Medium
ItalicAngle 0
IsFixedPitch false
CharacterSet Special
FontBBox -180 -293 1090 1010
UnderlinePosition -100
UnderlineThickness 50
Version 001.008
Notice Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All rights reserved.
EncodingScheme FontSpecific
StdHW 92
StdVW 85
StartCharMetrics 190
C 32 ; WX 250 ; N space ; B 0 0 0 0 ;
C 33 ; WX 333 ; N exclam ; B 128 -17 240 672 ;
C 34 ; WX 713 ; N universal ; B 31 0 681 705 ;
C 35 ; WX 500 ; N numbersign ; B 20 -16 481 673 ;
C 36 ; WX 549 ; N existential ; B 25 0 478 707 ;
C 37 ; WX 833 ; N percent ; B 63 -36 771 655 ;
C 38 ; WX 778 ; N ampersand ; B 41 -18 750 661 ;
C 39 ; WX 439 ; N suchthat ; B 48 -17 414 500 ;
C 40 ; WX 333 ; N parenleft ; B 53 -191 300 673 ;
C 41 ; WX 333 ; N parenright ; B 30 -191 277 673 ;
C 42 ; WX 500 ; N asteriskmath ; B 65 134 427 551 ;
C 43 ; WX 549 ; N plus ; B 10 0 539 533 ;
C 44 ; WX 250 ; N comma ; B 56 -152 194 104 ;
C 45 ; WX 549 ; N minus ; B 11 233 535 288 ;
C 46 ; WX 250 ; N period ; B 69 -17 181 95 ;
C 47 ; WX 278 ; N slash ; B 0 -18 254 646 ;
C 48 ; WX 500 ; N zero ; B 24 -14 476 685 ;
C 49 ; WX 500 ; N one ; B 117 0 390 673 ;
C 50 ; WX 500 ; N two ; B 25 0 475 685 ;
C 51 ; WX 500 ; N three ; B 43 -14 435 685 ;
C 52 ; WX 500 ; N four ; B 15 0 469 685 ;
C 53 ; WX 500 ; N five ; B 32 -14 445 690 ;
C 54 ; WX 500 ; N six ; B 34 -14 468 685 ;
C 55 ; WX 500 ; N seven ; B 24 -16 448 673 ;
C 56 ; WX 500 ; N eight ; B 56 -14 445 685 ;
C 57 ; WX 500 ; N nine ; B 30 -18 459 685 ;
C 58 ; WX 278 ; N colon ; B 81 -17 193 460 ;
C 59 ; WX 278 ; N semicolon ; B 83 -152 221 460 ;
C 60 ; WX 549 ; N less ; B 26 0 523 522 ;
C 61 ; WX 549 ; N equal ; B 11 141 537 390 ;
C 62 ; WX 549 ; N greater ; B 26 0 523 522 ;
C 63 ; WX 444 ; N question ; B 70 -17 412 686 ;
C 64 ; WX 549 ; N congruent ; B 11 0 537 475 ;
C 65 ; WX 722 ; N Alpha ; B 4 0 684 673 ;
C 66 ; WX 667 ; N Beta ; B 29 0 592 673 ;
C 67 ; WX 722 ; N Chi ; B -9 0 704 673 ;
C 68 ; WX 612 ; N Delta ; B 6 0 608 688 ;
C 69 ; WX 611 ; N Epsilon ; B 32 0 617 673 ;
C 70 ; WX 763 ; N Phi ; B 26 0 741 673 ;
C 71 ; WX 603 ; N Gamma ; B 24 0 609 673 ;
C 72 ; WX 722 ; N Eta ; B 39 0 729 673 ;
C 73 ; WX 333 ; N Iota ; B 32 0 316 673 ;
C 74 ; WX 631 ; N theta1 ; B 18 -18 623 689 ;
C 75 ; WX 722 ; N Kappa ; B 35 0 722 673 ;
C 76 ; WX 686 ; N Lambda ; B 6 0 680 688 ;
C 77 ; WX 889 ; N Mu ; B 28 0 887 673 ;
C 78 ; WX 722 ; N Nu ; B 29 -8 720 673 ;
C 79 ; WX 722 ; N Omicron ; B 41 -17 715 685 ;
C 80 ; WX 768 ; N Pi ; B 25 0 745 673 ;
C 81 ; WX 741 ; N Theta ; B 41 -17 715 685 ;
C 82 ; WX 556 ; N Rho ; B 28 0 563 673 ;
C 83 ; WX 592 ; N Sigma ; B 5 0 589 673 ;
C 84 ; WX 611 ; N Tau ; B 33 0 607 673 ;
C 85 ; WX 690 ; N Upsilon ; B -8 0 694 673 ;
C 86 ; WX 439 ; N sigma1 ; B 40 -233 436 500 ;
C 87 ; WX 768 ; N Omega ; B 34 0 736 688 ;
C 88 ; WX 645 ; N Xi ; B 40 0 599 673 ;
C 89 ; WX 795 ; N Psi ; B 15 0 781 684 ;
C 90 ; WX 611 ; N Zeta ; B 44 0 636 673 ;
C 91 ; WX 333 ; N bracketleft ; B 86 -155 299 674 ;
C 92 ; WX 863 ; N therefore ; B 163 0 701 487 ;
C 93 ; WX 333 ; N bracketright ; B 33 -155 246 674 ;
C 94 ; WX 658 ; N perpendicular ; B 15 0 652 674 ;
C 95 ; WX 500 ; N underscore ; B -2 -125 502 -75 ;
C 96 ; WX 500 ; N radicalex ; B 480 881 1090 917 ;
C 97 ; WX 631 ; N alpha ; B 41 -18 622 500 ;
C 98 ; WX 549 ; N beta ; B 61 -223 515 741 ;
C 99 ; WX 549 ; N chi ; B 12 -231 522 499 ;
C 100 ; WX 494 ; N delta ; B 40 -19 481 740 ;
C 101 ; WX 439 ; N epsilon ; B 22 -19 427 502 ;
C 102 ; WX 521 ; N phi ; B 28 -224 492 673 ;
C 103 ; WX 411 ; N gamma ; B 5 -225 484 499 ;
C 104 ; WX 603 ; N eta ; B 0 -202 527 514 ;
C 105 ; WX 329 ; N iota ; B 0 -17 301 503 ;
C 106 ; WX 603 ; N phi1 ; B 36 -224 587 499 ;
C 107 ; WX 549 ; N kappa ; B 33 0 558 501 ;
C 108 ; WX 549 ; N lambda ; B 24 -17 548 739 ;
C 109 ; WX 576 ; N mu ; B 33 -223 567 500 ;
C 110 ; WX 521 ; N nu ; B -9 -16 475 507 ;
C 111 ; WX 549 ; N omicron ; B 35 -19 501 499 ;
C 112 ; WX 549 ; N pi ; B 10 -19 530 487 ;
C 113 ; WX 521 ; N theta ; B 43 -17 485 690 ;
C 114 ; WX 549 ; N rho ; B 50 -230 490 499 ;
C 115 ; WX 603 ; N sigma ; B 30 -21 588 500 ;
C 116 ; WX 439 ; N tau ; B 10 -19 418 500 ;
C 117 ; WX 576 ; N upsilon ; B 7 -18 535 507 ;
C 118 ; WX 713 ; N omega1 ; B 12 -18 671 583 ;
C 119 ; WX 686 ; N omega ; B 42 -17 684 500 ;
C 120 ; WX 493 ; N xi ; B 27 -224 469 766 ;
C 121 ; WX 686 ; N psi ; B 12 -228 701 500 ;
C 122 ; WX 494 ; N zeta ; B 60 -225 467 756 ;
C 123 ; WX 480 ; N braceleft ; B 58 -183 397 673 ;
C 124 ; WX 200 ; N bar ; B 65 -293 135 707 ;
C 125 ; WX 480 ; N braceright ; B 79 -183 418 673 ;
C 126 ; WX 549 ; N similar ; B 17 203 529 307 ;
C 160 ; WX 750 ; N Euro ; B 20 -12 714 685 ;
C 161 ; WX 620 ; N Upsilon1 ; B -2 0 610 685 ;
C 162 ; WX 247 ; N minute ; B 27 459 228 735 ;
C 163 ; WX 549 ; N lessequal ; B 29 0 526 639 ;
C 164 ; WX 167 ; N fraction ; B -180 -12 340 677 ;
C 165 ; WX 713 ; N infinity ; B 26 124 688 404 ;
C 166 ; WX 500 ; N florin ; B 2 -193 494 686 ;
C 167 ; WX 753 ; N club ; B 86 -26 660 533 ;
C 168 ; WX 753 ; N diamond ; B 142 -36 600 550 ;
C 169 ; WX 753 ; N heart ; B 117 -33 631 532 ;
C 170 ; WX 753 ; N spade ; B 113 -36 629 548 ;
C 171 ; WX 1042 ; N arrowboth ; B 24 -15 1024 511 ;
C 172 ; WX 987 ; N arrowleft ; B 32 -15 942 511 ;
C 173 ; WX 603 ; N arrowup ; B 45 0 571 910 ;
C 174 ; WX 987 ; N arrowright ; B 49 -15 959 511 ;
C 175 ; WX 603 ; N arrowdown ; B 45 -22 571 888 ;
C 176 ; WX 400 ; N degree ; B 50 385 350 685 ;
C 177 ; WX 549 ; N plusminus ; B 10 0 539 645 ;
C 178 ; WX 411 ; N second ; B 20 459 413 737 ;
C 179 ; WX 549 ; N greaterequal ; B 29 0 526 639 ;
C 180 ; WX 549 ; N multiply ; B 17 8 533 524 ;
C 181 ; WX 713 ; N proportional ; B 27 123 639 404 ;
C 182 ; WX 494 ; N partialdiff ; B 26 -20 462 746 ;
C 183 ; WX 460 ; N bullet ; B 50 113 410 473 ;
C 184 ; WX 549 ; N divide ; B 10 71 536 456 ;
C 185 ; WX 549 ; N notequal ; B 15 -25 540 549 ;
C 186 ; WX 549 ; N equivalence ; B 14 82 538 443 ;
C 187 ; WX 549 ; N approxequal ; B 14 135 527 394 ;
C 188 ; WX 1000 ; N ellipsis ; B 111 -17 889 95 ;
C 189 ; WX 603 ; N arrowvertex ; B 280 -120 336 1010 ;
C 190 ; WX 1000 ; N arrowhorizex ; B -60 220 1050 276 ;
C 191 ; WX 658 ; N carriagereturn ; B 15 -16 602 629 ;
C 192 ; WX 823 ; N aleph ; B 175 -18 661 658 ;
C 193 ; WX 686 ; N Ifraktur ; B 10 -53 578 740 ;
C 194 ; WX 795 ; N Rfraktur ; B 26 -15 759 734 ;
C 195 ; WX 987 ; N weierstrass ; B 159 -211 870 573 ;
C 196 ; WX 768 ; N circlemultiply ; B 43 -17 733 673 ;
C 197 ; WX 768 ; N circleplus ; B 43 -15 733 675 ;
C 198 ; WX 823 ; N emptyset ; B 39 -24 781 719 ;
C 199 ; WX 768 ; N intersection ; B 40 0 732 509 ;
C 200 ; WX 768 ; N union ; B 40 -17 732 492 ;
C 201 ; WX 713 ; N propersuperset ; B 20 0 673 470 ;
C 202 ; WX 713 ; N reflexsuperset ; B 20 -125 673 470 ;
C 203 ; WX 713 ; N notsubset ; B 36 -70 690 540 ;
C 204 ; WX 713 ; N propersubset ; B 37 0 690 470 ;
C 205 ; WX 713 ; N reflexsubset ; B 37 -125 690 470 ;
C 206 ; WX 713 ; N element ; B 45 0 505 468 ;
C 207 ; WX 713 ; N notelement ; B 45 -58 505 555 ;
C 208 ; WX 768 ; N angle ; B 26 0 738 673 ;
C 209 ; WX 713 ; N gradient ; B 36 -19 681 718 ;
C 210 ; WX 790 ; N registerserif ; B 50 -17 740 673 ;
C 211 ; WX 790 ; N copyrightserif ; B 51 -15 741 675 ;
C 212 ; WX 890 ; N trademarkserif ; B 18 293 855 673 ;
C 213 ; WX 823 ; N product ; B 25 -101 803 751 ;
C 214 ; WX 549 ; N radical ; B 10 -38 515 917 ;
C 215 ; WX 250 ; N dotmath ; B 69 210 169 310 ;
C 216 ; WX 713 ; N logicalnot ; B 15 0 680 288 ;
C 217 ; WX 603 ; N logicaland ; B 23 0 583 454 ;
C 218 ; WX 603 ; N logicalor ; B 30 0 578 477 ;
C 219 ; WX 1042 ; N arrowdblboth ; B 27 -20 1023 510 ;
C 220 ; WX 987 ; N arrowdblleft ; B 30 -15 939 513 ;
C 221 ; WX 603 ; N arrowdblup ; B 39 2 567 911 ;
C 222 ; WX 987 ; N arrowdblright ; B 45 -20 954 508 ;
C 223 ; WX 603 ; N arrowdbldown ; B 44 -19 572 890 ;
C 224 ; WX 494 ; N lozenge ; B 18 0 466 745 ;
C 225 ; WX 329 ; N angleleft ; B 25 -198 306 746 ;
C 226 ; WX 790 ; N registersans ; B 50 -20 740 670 ;
C 227 ; WX 790 ; N copyrightsans ; B 49 -15 739 675 ;
C 228 ; WX 786 ; N trademarksans ; B 5 293 725 673 ;
C 229 ; WX 713 ; N summation ; B 14 -108 695 752 ;
C 230 ; WX 384 ; N parenlefttp ; B 24 -293 436 926 ;
C 231 ; WX 384 ; N parenleftex ; B 24 -85 108 925 ;
C 232 ; WX 384 ; N parenleftbt ; B 24 -293 436 926 ;
C 233 ; WX 384 ; N bracketlefttp ; B 0 -80 349 926 ;
C 234 ; WX 384 ; N bracketleftex ; B 0 -79 77 925 ;
C 235 ; WX 384 ; N bracketleftbt ; B 0 -80 349 926 ;
C 236 ; WX 494 ; N bracelefttp ; B 209 -85 445 925 ;
C 237 ; WX 494 ; N braceleftmid ; B 20 -85 284 935 ;
C 238 ; WX 494 ; N braceleftbt ; B 209 -75 445 935 ;
C 239 ; WX 494 ; N braceex ; B 209 -85 284 935 ;
C 241 ; WX 329 ; N angleright ; B 21 -198 302 746 ;
C 242 ; WX 274 ; N integral ; B 2 -107 291 916 ;
C 243 ; WX 686 ; N integraltp ; B 308 -88 675 920 ;
C 244 ; WX 686 ; N integralex ; B 308 -88 378 975 ;
C 245 ; WX 686 ; N integralbt ; B 11 -87 378 921 ;
C 246 ; WX 384 ; N parenrighttp ; B 54 -293 466 926 ;
C 247 ; WX 384 ; N parenrightex ; B 382 -85 466 925 ;
C 248 ; WX 384 ; N parenrightbt ; B 54 -293 466 926 ;
C 249 ; WX 384 ; N bracketrighttp ; B 22 -80 371 926 ;
C 250 ; WX 384 ; N bracketrightex ; B 294 -79 371 925 ;
C 251 ; WX 384 ; N bracketrightbt ; B 22 -80 371 926 ;
C 252 ; WX 494 ; N bracerighttp ; B 48 -85 284 925 ;
C 253 ; WX 494 ; N bracerightmid ; B 209 -85 473 935 ;
C 254 ; WX 494 ; N bracerightbt ; B 48 -75 284 935 ;
C -1 ; WX 790 ; N apple ; B 56 -3 733 808 ;
EndCharMetrics
EndFontMetrics

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,225 @@
StartFontMetrics 4.1
Comment Copyright (c) 1985, 1987, 1988, 1989, 1997 Adobe Systems Incorporated. All Rights Reserved.
Comment Creation Date: Thu May 1 15:14:13 1997
Comment UniqueID 43082
Comment VMusage 45775 55535
FontName ZapfDingbats
FullName ITC Zapf Dingbats
FamilyName ZapfDingbats
Weight Medium
ItalicAngle 0
IsFixedPitch false
CharacterSet Special
FontBBox -1 -143 981 820
UnderlinePosition -100
UnderlineThickness 50
Version 002.000
Notice Copyright (c) 1985, 1987, 1988, 1989, 1997 Adobe Systems Incorporated. All Rights Reserved.ITC Zapf Dingbats is a registered trademark of International Typeface Corporation.
EncodingScheme FontSpecific
StdHW 28
StdVW 90
StartCharMetrics 202
C 32 ; WX 278 ; N space ; B 0 0 0 0 ;
C 33 ; WX 974 ; N a1 ; B 35 72 939 621 ;
C 34 ; WX 961 ; N a2 ; B 35 81 927 611 ;
C 35 ; WX 974 ; N a202 ; B 35 72 939 621 ;
C 36 ; WX 980 ; N a3 ; B 35 0 945 692 ;
C 37 ; WX 719 ; N a4 ; B 34 139 685 566 ;
C 38 ; WX 789 ; N a5 ; B 35 -14 755 705 ;
C 39 ; WX 790 ; N a119 ; B 35 -14 755 705 ;
C 40 ; WX 791 ; N a118 ; B 35 -13 761 705 ;
C 41 ; WX 690 ; N a117 ; B 34 138 655 553 ;
C 42 ; WX 960 ; N a11 ; B 35 123 925 568 ;
C 43 ; WX 939 ; N a12 ; B 35 134 904 559 ;
C 44 ; WX 549 ; N a13 ; B 29 -11 516 705 ;
C 45 ; WX 855 ; N a14 ; B 34 59 820 632 ;
C 46 ; WX 911 ; N a15 ; B 35 50 876 642 ;
C 47 ; WX 933 ; N a16 ; B 35 139 899 550 ;
C 48 ; WX 911 ; N a105 ; B 35 50 876 642 ;
C 49 ; WX 945 ; N a17 ; B 35 139 909 553 ;
C 50 ; WX 974 ; N a18 ; B 35 104 938 587 ;
C 51 ; WX 755 ; N a19 ; B 34 -13 721 705 ;
C 52 ; WX 846 ; N a20 ; B 36 -14 811 705 ;
C 53 ; WX 762 ; N a21 ; B 35 0 727 692 ;
C 54 ; WX 761 ; N a22 ; B 35 0 727 692 ;
C 55 ; WX 571 ; N a23 ; B -1 -68 571 661 ;
C 56 ; WX 677 ; N a24 ; B 36 -13 642 705 ;
C 57 ; WX 763 ; N a25 ; B 35 0 728 692 ;
C 58 ; WX 760 ; N a26 ; B 35 0 726 692 ;
C 59 ; WX 759 ; N a27 ; B 35 0 725 692 ;
C 60 ; WX 754 ; N a28 ; B 35 0 720 692 ;
C 61 ; WX 494 ; N a6 ; B 35 0 460 692 ;
C 62 ; WX 552 ; N a7 ; B 35 0 517 692 ;
C 63 ; WX 537 ; N a8 ; B 35 0 503 692 ;
C 64 ; WX 577 ; N a9 ; B 35 96 542 596 ;
C 65 ; WX 692 ; N a10 ; B 35 -14 657 705 ;
C 66 ; WX 786 ; N a29 ; B 35 -14 751 705 ;
C 67 ; WX 788 ; N a30 ; B 35 -14 752 705 ;
C 68 ; WX 788 ; N a31 ; B 35 -14 753 705 ;
C 69 ; WX 790 ; N a32 ; B 35 -14 756 705 ;
C 70 ; WX 793 ; N a33 ; B 35 -13 759 705 ;
C 71 ; WX 794 ; N a34 ; B 35 -13 759 705 ;
C 72 ; WX 816 ; N a35 ; B 35 -14 782 705 ;
C 73 ; WX 823 ; N a36 ; B 35 -14 787 705 ;
C 74 ; WX 789 ; N a37 ; B 35 -14 754 705 ;
C 75 ; WX 841 ; N a38 ; B 35 -14 807 705 ;
C 76 ; WX 823 ; N a39 ; B 35 -14 789 705 ;
C 77 ; WX 833 ; N a40 ; B 35 -14 798 705 ;
C 78 ; WX 816 ; N a41 ; B 35 -13 782 705 ;
C 79 ; WX 831 ; N a42 ; B 35 -14 796 705 ;
C 80 ; WX 923 ; N a43 ; B 35 -14 888 705 ;
C 81 ; WX 744 ; N a44 ; B 35 0 710 692 ;
C 82 ; WX 723 ; N a45 ; B 35 0 688 692 ;
C 83 ; WX 749 ; N a46 ; B 35 0 714 692 ;
C 84 ; WX 790 ; N a47 ; B 34 -14 756 705 ;
C 85 ; WX 792 ; N a48 ; B 35 -14 758 705 ;
C 86 ; WX 695 ; N a49 ; B 35 -14 661 706 ;
C 87 ; WX 776 ; N a50 ; B 35 -6 741 699 ;
C 88 ; WX 768 ; N a51 ; B 35 -7 734 699 ;
C 89 ; WX 792 ; N a52 ; B 35 -14 757 705 ;
C 90 ; WX 759 ; N a53 ; B 35 0 725 692 ;
C 91 ; WX 707 ; N a54 ; B 35 -13 672 704 ;
C 92 ; WX 708 ; N a55 ; B 35 -14 672 705 ;
C 93 ; WX 682 ; N a56 ; B 35 -14 647 705 ;
C 94 ; WX 701 ; N a57 ; B 35 -14 666 705 ;
C 95 ; WX 826 ; N a58 ; B 35 -14 791 705 ;
C 96 ; WX 815 ; N a59 ; B 35 -14 780 705 ;
C 97 ; WX 789 ; N a60 ; B 35 -14 754 705 ;
C 98 ; WX 789 ; N a61 ; B 35 -14 754 705 ;
C 99 ; WX 707 ; N a62 ; B 34 -14 673 705 ;
C 100 ; WX 687 ; N a63 ; B 36 0 651 692 ;
C 101 ; WX 696 ; N a64 ; B 35 0 661 691 ;
C 102 ; WX 689 ; N a65 ; B 35 0 655 692 ;
C 103 ; WX 786 ; N a66 ; B 34 -14 751 705 ;
C 104 ; WX 787 ; N a67 ; B 35 -14 752 705 ;
C 105 ; WX 713 ; N a68 ; B 35 -14 678 705 ;
C 106 ; WX 791 ; N a69 ; B 35 -14 756 705 ;
C 107 ; WX 785 ; N a70 ; B 36 -14 751 705 ;
C 108 ; WX 791 ; N a71 ; B 35 -14 757 705 ;
C 109 ; WX 873 ; N a72 ; B 35 -14 838 705 ;
C 110 ; WX 761 ; N a73 ; B 35 0 726 692 ;
C 111 ; WX 762 ; N a74 ; B 35 0 727 692 ;
C 112 ; WX 762 ; N a203 ; B 35 0 727 692 ;
C 113 ; WX 759 ; N a75 ; B 35 0 725 692 ;
C 114 ; WX 759 ; N a204 ; B 35 0 725 692 ;
C 115 ; WX 892 ; N a76 ; B 35 0 858 705 ;
C 116 ; WX 892 ; N a77 ; B 35 -14 858 692 ;
C 117 ; WX 788 ; N a78 ; B 35 -14 754 705 ;
C 118 ; WX 784 ; N a79 ; B 35 -14 749 705 ;
C 119 ; WX 438 ; N a81 ; B 35 -14 403 705 ;
C 120 ; WX 138 ; N a82 ; B 35 0 104 692 ;
C 121 ; WX 277 ; N a83 ; B 35 0 242 692 ;
C 122 ; WX 415 ; N a84 ; B 35 0 380 692 ;
C 123 ; WX 392 ; N a97 ; B 35 263 357 705 ;
C 124 ; WX 392 ; N a98 ; B 34 263 357 705 ;
C 125 ; WX 668 ; N a99 ; B 35 263 633 705 ;
C 126 ; WX 668 ; N a100 ; B 36 263 634 705 ;
C 128 ; WX 390 ; N a89 ; B 35 -14 356 705 ;
C 129 ; WX 390 ; N a90 ; B 35 -14 355 705 ;
C 130 ; WX 317 ; N a93 ; B 35 0 283 692 ;
C 131 ; WX 317 ; N a94 ; B 35 0 283 692 ;
C 132 ; WX 276 ; N a91 ; B 35 0 242 692 ;
C 133 ; WX 276 ; N a92 ; B 35 0 242 692 ;
C 134 ; WX 509 ; N a205 ; B 35 0 475 692 ;
C 135 ; WX 509 ; N a85 ; B 35 0 475 692 ;
C 136 ; WX 410 ; N a206 ; B 35 0 375 692 ;
C 137 ; WX 410 ; N a86 ; B 35 0 375 692 ;
C 138 ; WX 234 ; N a87 ; B 35 -14 199 705 ;
C 139 ; WX 234 ; N a88 ; B 35 -14 199 705 ;
C 140 ; WX 334 ; N a95 ; B 35 0 299 692 ;
C 141 ; WX 334 ; N a96 ; B 35 0 299 692 ;
C 161 ; WX 732 ; N a101 ; B 35 -143 697 806 ;
C 162 ; WX 544 ; N a102 ; B 56 -14 488 706 ;
C 163 ; WX 544 ; N a103 ; B 34 -14 508 705 ;
C 164 ; WX 910 ; N a104 ; B 35 40 875 651 ;
C 165 ; WX 667 ; N a106 ; B 35 -14 633 705 ;
C 166 ; WX 760 ; N a107 ; B 35 -14 726 705 ;
C 167 ; WX 760 ; N a108 ; B 0 121 758 569 ;
C 168 ; WX 776 ; N a112 ; B 35 0 741 705 ;
C 169 ; WX 595 ; N a111 ; B 34 -14 560 705 ;
C 170 ; WX 694 ; N a110 ; B 35 -14 659 705 ;
C 171 ; WX 626 ; N a109 ; B 34 0 591 705 ;
C 172 ; WX 788 ; N a120 ; B 35 -14 754 705 ;
C 173 ; WX 788 ; N a121 ; B 35 -14 754 705 ;
C 174 ; WX 788 ; N a122 ; B 35 -14 754 705 ;
C 175 ; WX 788 ; N a123 ; B 35 -14 754 705 ;
C 176 ; WX 788 ; N a124 ; B 35 -14 754 705 ;
C 177 ; WX 788 ; N a125 ; B 35 -14 754 705 ;
C 178 ; WX 788 ; N a126 ; B 35 -14 754 705 ;
C 179 ; WX 788 ; N a127 ; B 35 -14 754 705 ;
C 180 ; WX 788 ; N a128 ; B 35 -14 754 705 ;
C 181 ; WX 788 ; N a129 ; B 35 -14 754 705 ;
C 182 ; WX 788 ; N a130 ; B 35 -14 754 705 ;
C 183 ; WX 788 ; N a131 ; B 35 -14 754 705 ;
C 184 ; WX 788 ; N a132 ; B 35 -14 754 705 ;
C 185 ; WX 788 ; N a133 ; B 35 -14 754 705 ;
C 186 ; WX 788 ; N a134 ; B 35 -14 754 705 ;
C 187 ; WX 788 ; N a135 ; B 35 -14 754 705 ;
C 188 ; WX 788 ; N a136 ; B 35 -14 754 705 ;
C 189 ; WX 788 ; N a137 ; B 35 -14 754 705 ;
C 190 ; WX 788 ; N a138 ; B 35 -14 754 705 ;
C 191 ; WX 788 ; N a139 ; B 35 -14 754 705 ;
C 192 ; WX 788 ; N a140 ; B 35 -14 754 705 ;
C 193 ; WX 788 ; N a141 ; B 35 -14 754 705 ;
C 194 ; WX 788 ; N a142 ; B 35 -14 754 705 ;
C 195 ; WX 788 ; N a143 ; B 35 -14 754 705 ;
C 196 ; WX 788 ; N a144 ; B 35 -14 754 705 ;
C 197 ; WX 788 ; N a145 ; B 35 -14 754 705 ;
C 198 ; WX 788 ; N a146 ; B 35 -14 754 705 ;
C 199 ; WX 788 ; N a147 ; B 35 -14 754 705 ;
C 200 ; WX 788 ; N a148 ; B 35 -14 754 705 ;
C 201 ; WX 788 ; N a149 ; B 35 -14 754 705 ;
C 202 ; WX 788 ; N a150 ; B 35 -14 754 705 ;
C 203 ; WX 788 ; N a151 ; B 35 -14 754 705 ;
C 204 ; WX 788 ; N a152 ; B 35 -14 754 705 ;
C 205 ; WX 788 ; N a153 ; B 35 -14 754 705 ;
C 206 ; WX 788 ; N a154 ; B 35 -14 754 705 ;
C 207 ; WX 788 ; N a155 ; B 35 -14 754 705 ;
C 208 ; WX 788 ; N a156 ; B 35 -14 754 705 ;
C 209 ; WX 788 ; N a157 ; B 35 -14 754 705 ;
C 210 ; WX 788 ; N a158 ; B 35 -14 754 705 ;
C 211 ; WX 788 ; N a159 ; B 35 -14 754 705 ;
C 212 ; WX 894 ; N a160 ; B 35 58 860 634 ;
C 213 ; WX 838 ; N a161 ; B 35 152 803 540 ;
C 214 ; WX 1016 ; N a163 ; B 34 152 981 540 ;
C 215 ; WX 458 ; N a164 ; B 35 -127 422 820 ;
C 216 ; WX 748 ; N a196 ; B 35 94 698 597 ;
C 217 ; WX 924 ; N a165 ; B 35 140 890 552 ;
C 218 ; WX 748 ; N a192 ; B 35 94 698 597 ;
C 219 ; WX 918 ; N a166 ; B 35 166 884 526 ;
C 220 ; WX 927 ; N a167 ; B 35 32 892 660 ;
C 221 ; WX 928 ; N a168 ; B 35 129 891 562 ;
C 222 ; WX 928 ; N a169 ; B 35 128 893 563 ;
C 223 ; WX 834 ; N a170 ; B 35 155 799 537 ;
C 224 ; WX 873 ; N a171 ; B 35 93 838 599 ;
C 225 ; WX 828 ; N a172 ; B 35 104 791 588 ;
C 226 ; WX 924 ; N a173 ; B 35 98 889 594 ;
C 227 ; WX 924 ; N a162 ; B 35 98 889 594 ;
C 228 ; WX 917 ; N a174 ; B 35 0 882 692 ;
C 229 ; WX 930 ; N a175 ; B 35 84 896 608 ;
C 230 ; WX 931 ; N a176 ; B 35 84 896 608 ;
C 231 ; WX 463 ; N a177 ; B 35 -99 429 791 ;
C 232 ; WX 883 ; N a178 ; B 35 71 848 623 ;
C 233 ; WX 836 ; N a179 ; B 35 44 802 648 ;
C 234 ; WX 836 ; N a193 ; B 35 44 802 648 ;
C 235 ; WX 867 ; N a180 ; B 35 101 832 591 ;
C 236 ; WX 867 ; N a199 ; B 35 101 832 591 ;
C 237 ; WX 696 ; N a181 ; B 35 44 661 648 ;
C 238 ; WX 696 ; N a200 ; B 35 44 661 648 ;
C 239 ; WX 874 ; N a182 ; B 35 77 840 619 ;
C 241 ; WX 874 ; N a201 ; B 35 73 840 615 ;
C 242 ; WX 760 ; N a183 ; B 35 0 725 692 ;
C 243 ; WX 946 ; N a184 ; B 35 160 911 533 ;
C 244 ; WX 771 ; N a197 ; B 34 37 736 655 ;
C 245 ; WX 865 ; N a185 ; B 35 207 830 481 ;
C 246 ; WX 771 ; N a194 ; B 34 37 736 655 ;
C 247 ; WX 888 ; N a198 ; B 34 -19 853 712 ;
C 248 ; WX 967 ; N a186 ; B 35 124 932 568 ;
C 249 ; WX 888 ; N a195 ; B 34 -19 853 712 ;
C 250 ; WX 831 ; N a187 ; B 35 113 796 579 ;
C 251 ; WX 873 ; N a188 ; B 36 118 838 578 ;
C 252 ; WX 927 ; N a189 ; B 35 150 891 542 ;
C 253 ; WX 970 ; N a190 ; B 35 76 931 616 ;
C 254 ; WX 918 ; N a191 ; B 34 99 884 593 ;
EndCharMetrics
EndFontMetrics

View File

@@ -0,0 +1,362 @@
package fonts
import (
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model/textencoding"
)
// Font Courier. Implements Font interface.
// This is a built-in font and it is assumed that every reader has access to it.
type fontCourier struct {
encoder textencoding.TextEncoder
}
func NewFontCourier() fontCourier {
font := fontCourier{}
font.encoder = textencoding.NewWinAnsiTextEncoder() // Default
return font
}
func (font fontCourier) SetEncoder(encoder textencoding.TextEncoder) {
font.encoder = encoder
}
func (font fontCourier) GetGlyphCharMetrics(glyph string) (CharMetrics, bool) {
metrics, has := courierCharMetrics[glyph]
if !has {
return metrics, false
}
return metrics, true
}
func (font fontCourier) ToPdfObject() core.PdfObject {
obj := &core.PdfIndirectObject{}
fontDict := core.MakeDict()
fontDict.Set("Type", core.MakeName("Font"))
fontDict.Set("Subtype", core.MakeName("Type1"))
fontDict.Set("BaseFont", core.MakeName("Courier"))
fontDict.Set("Encoding", font.encoder.ToPdfObject())
obj.PdfObject = fontDict
return obj
}
var courierCharMetrics map[string]CharMetrics = map[string]CharMetrics{
"A": {GlyphName: "A", Wx: 600.000000, Wy: 0.000000},
"AE": {GlyphName: "AE", Wx: 600.000000, Wy: 0.000000},
"Aacute": {GlyphName: "Aacute", Wx: 600.000000, Wy: 0.000000},
"Abreve": {GlyphName: "Abreve", Wx: 600.000000, Wy: 0.000000},
"Acircumflex": {GlyphName: "Acircumflex", Wx: 600.000000, Wy: 0.000000},
"Adieresis": {GlyphName: "Adieresis", Wx: 600.000000, Wy: 0.000000},
"Agrave": {GlyphName: "Agrave", Wx: 600.000000, Wy: 0.000000},
"Amacron": {GlyphName: "Amacron", Wx: 600.000000, Wy: 0.000000},
"Aogonek": {GlyphName: "Aogonek", Wx: 600.000000, Wy: 0.000000},
"Aring": {GlyphName: "Aring", Wx: 600.000000, Wy: 0.000000},
"Atilde": {GlyphName: "Atilde", Wx: 600.000000, Wy: 0.000000},
"B": {GlyphName: "B", Wx: 600.000000, Wy: 0.000000},
"C": {GlyphName: "C", Wx: 600.000000, Wy: 0.000000},
"Cacute": {GlyphName: "Cacute", Wx: 600.000000, Wy: 0.000000},
"Ccaron": {GlyphName: "Ccaron", Wx: 600.000000, Wy: 0.000000},
"Ccedilla": {GlyphName: "Ccedilla", Wx: 600.000000, Wy: 0.000000},
"D": {GlyphName: "D", Wx: 600.000000, Wy: 0.000000},
"Dcaron": {GlyphName: "Dcaron", Wx: 600.000000, Wy: 0.000000},
"Dcroat": {GlyphName: "Dcroat", Wx: 600.000000, Wy: 0.000000},
"Delta": {GlyphName: "Delta", Wx: 600.000000, Wy: 0.000000},
"E": {GlyphName: "E", Wx: 600.000000, Wy: 0.000000},
"Eacute": {GlyphName: "Eacute", Wx: 600.000000, Wy: 0.000000},
"Ecaron": {GlyphName: "Ecaron", Wx: 600.000000, Wy: 0.000000},
"Ecircumflex": {GlyphName: "Ecircumflex", Wx: 600.000000, Wy: 0.000000},
"Edieresis": {GlyphName: "Edieresis", Wx: 600.000000, Wy: 0.000000},
"Edotaccent": {GlyphName: "Edotaccent", Wx: 600.000000, Wy: 0.000000},
"Egrave": {GlyphName: "Egrave", Wx: 600.000000, Wy: 0.000000},
"Emacron": {GlyphName: "Emacron", Wx: 600.000000, Wy: 0.000000},
"Eogonek": {GlyphName: "Eogonek", Wx: 600.000000, Wy: 0.000000},
"Eth": {GlyphName: "Eth", Wx: 600.000000, Wy: 0.000000},
"Euro": {GlyphName: "Euro", Wx: 600.000000, Wy: 0.000000},
"F": {GlyphName: "F", Wx: 600.000000, Wy: 0.000000},
"G": {GlyphName: "G", Wx: 600.000000, Wy: 0.000000},
"Gbreve": {GlyphName: "Gbreve", Wx: 600.000000, Wy: 0.000000},
"Gcommaaccent": {GlyphName: "Gcommaaccent", Wx: 600.000000, Wy: 0.000000},
"H": {GlyphName: "H", Wx: 600.000000, Wy: 0.000000},
"I": {GlyphName: "I", Wx: 600.000000, Wy: 0.000000},
"Iacute": {GlyphName: "Iacute", Wx: 600.000000, Wy: 0.000000},
"Icircumflex": {GlyphName: "Icircumflex", Wx: 600.000000, Wy: 0.000000},
"Idieresis": {GlyphName: "Idieresis", Wx: 600.000000, Wy: 0.000000},
"Idotaccent": {GlyphName: "Idotaccent", Wx: 600.000000, Wy: 0.000000},
"Igrave": {GlyphName: "Igrave", Wx: 600.000000, Wy: 0.000000},
"Imacron": {GlyphName: "Imacron", Wx: 600.000000, Wy: 0.000000},
"Iogonek": {GlyphName: "Iogonek", Wx: 600.000000, Wy: 0.000000},
"J": {GlyphName: "J", Wx: 600.000000, Wy: 0.000000},
"K": {GlyphName: "K", Wx: 600.000000, Wy: 0.000000},
"Kcommaaccent": {GlyphName: "Kcommaaccent", Wx: 600.000000, Wy: 0.000000},
"L": {GlyphName: "L", Wx: 600.000000, Wy: 0.000000},
"Lacute": {GlyphName: "Lacute", Wx: 600.000000, Wy: 0.000000},
"Lcaron": {GlyphName: "Lcaron", Wx: 600.000000, Wy: 0.000000},
"Lcommaaccent": {GlyphName: "Lcommaaccent", Wx: 600.000000, Wy: 0.000000},
"Lslash": {GlyphName: "Lslash", Wx: 600.000000, Wy: 0.000000},
"M": {GlyphName: "M", Wx: 600.000000, Wy: 0.000000},
"N": {GlyphName: "N", Wx: 600.000000, Wy: 0.000000},
"Nacute": {GlyphName: "Nacute", Wx: 600.000000, Wy: 0.000000},
"Ncaron": {GlyphName: "Ncaron", Wx: 600.000000, Wy: 0.000000},
"Ncommaaccent": {GlyphName: "Ncommaaccent", Wx: 600.000000, Wy: 0.000000},
"Ntilde": {GlyphName: "Ntilde", Wx: 600.000000, Wy: 0.000000},
"O": {GlyphName: "O", Wx: 600.000000, Wy: 0.000000},
"OE": {GlyphName: "OE", Wx: 600.000000, Wy: 0.000000},
"Oacute": {GlyphName: "Oacute", Wx: 600.000000, Wy: 0.000000},
"Ocircumflex": {GlyphName: "Ocircumflex", Wx: 600.000000, Wy: 0.000000},
"Odieresis": {GlyphName: "Odieresis", Wx: 600.000000, Wy: 0.000000},
"Ograve": {GlyphName: "Ograve", Wx: 600.000000, Wy: 0.000000},
"Ohungarumlaut": {GlyphName: "Ohungarumlaut", Wx: 600.000000, Wy: 0.000000},
"Omacron": {GlyphName: "Omacron", Wx: 600.000000, Wy: 0.000000},
"Oslash": {GlyphName: "Oslash", Wx: 600.000000, Wy: 0.000000},
"Otilde": {GlyphName: "Otilde", Wx: 600.000000, Wy: 0.000000},
"P": {GlyphName: "P", Wx: 600.000000, Wy: 0.000000},
"Q": {GlyphName: "Q", Wx: 600.000000, Wy: 0.000000},
"R": {GlyphName: "R", Wx: 600.000000, Wy: 0.000000},
"Racute": {GlyphName: "Racute", Wx: 600.000000, Wy: 0.000000},
"Rcaron": {GlyphName: "Rcaron", Wx: 600.000000, Wy: 0.000000},
"Rcommaaccent": {GlyphName: "Rcommaaccent", Wx: 600.000000, Wy: 0.000000},
"S": {GlyphName: "S", Wx: 600.000000, Wy: 0.000000},
"Sacute": {GlyphName: "Sacute", Wx: 600.000000, Wy: 0.000000},
"Scaron": {GlyphName: "Scaron", Wx: 600.000000, Wy: 0.000000},
"Scedilla": {GlyphName: "Scedilla", Wx: 600.000000, Wy: 0.000000},
"Scommaaccent": {GlyphName: "Scommaaccent", Wx: 600.000000, Wy: 0.000000},
"T": {GlyphName: "T", Wx: 600.000000, Wy: 0.000000},
"Tcaron": {GlyphName: "Tcaron", Wx: 600.000000, Wy: 0.000000},
"Tcommaaccent": {GlyphName: "Tcommaaccent", Wx: 600.000000, Wy: 0.000000},
"Thorn": {GlyphName: "Thorn", Wx: 600.000000, Wy: 0.000000},
"U": {GlyphName: "U", Wx: 600.000000, Wy: 0.000000},
"Uacute": {GlyphName: "Uacute", Wx: 600.000000, Wy: 0.000000},
"Ucircumflex": {GlyphName: "Ucircumflex", Wx: 600.000000, Wy: 0.000000},
"Udieresis": {GlyphName: "Udieresis", Wx: 600.000000, Wy: 0.000000},
"Ugrave": {GlyphName: "Ugrave", Wx: 600.000000, Wy: 0.000000},
"Uhungarumlaut": {GlyphName: "Uhungarumlaut", Wx: 600.000000, Wy: 0.000000},
"Umacron": {GlyphName: "Umacron", Wx: 600.000000, Wy: 0.000000},
"Uogonek": {GlyphName: "Uogonek", Wx: 600.000000, Wy: 0.000000},
"Uring": {GlyphName: "Uring", Wx: 600.000000, Wy: 0.000000},
"V": {GlyphName: "V", Wx: 600.000000, Wy: 0.000000},
"W": {GlyphName: "W", Wx: 600.000000, Wy: 0.000000},
"X": {GlyphName: "X", Wx: 600.000000, Wy: 0.000000},
"Y": {GlyphName: "Y", Wx: 600.000000, Wy: 0.000000},
"Yacute": {GlyphName: "Yacute", Wx: 600.000000, Wy: 0.000000},
"Ydieresis": {GlyphName: "Ydieresis", Wx: 600.000000, Wy: 0.000000},
"Z": {GlyphName: "Z", Wx: 600.000000, Wy: 0.000000},
"Zacute": {GlyphName: "Zacute", Wx: 600.000000, Wy: 0.000000},
"Zcaron": {GlyphName: "Zcaron", Wx: 600.000000, Wy: 0.000000},
"Zdotaccent": {GlyphName: "Zdotaccent", Wx: 600.000000, Wy: 0.000000},
"a": {GlyphName: "a", Wx: 600.000000, Wy: 0.000000},
"aacute": {GlyphName: "aacute", Wx: 600.000000, Wy: 0.000000},
"abreve": {GlyphName: "abreve", Wx: 600.000000, Wy: 0.000000},
"acircumflex": {GlyphName: "acircumflex", Wx: 600.000000, Wy: 0.000000},
"acute": {GlyphName: "acute", Wx: 600.000000, Wy: 0.000000},
"adieresis": {GlyphName: "adieresis", Wx: 600.000000, Wy: 0.000000},
"ae": {GlyphName: "ae", Wx: 600.000000, Wy: 0.000000},
"agrave": {GlyphName: "agrave", Wx: 600.000000, Wy: 0.000000},
"amacron": {GlyphName: "amacron", Wx: 600.000000, Wy: 0.000000},
"ampersand": {GlyphName: "ampersand", Wx: 600.000000, Wy: 0.000000},
"aogonek": {GlyphName: "aogonek", Wx: 600.000000, Wy: 0.000000},
"aring": {GlyphName: "aring", Wx: 600.000000, Wy: 0.000000},
"asciicircum": {GlyphName: "asciicircum", Wx: 600.000000, Wy: 0.000000},
"asciitilde": {GlyphName: "asciitilde", Wx: 600.000000, Wy: 0.000000},
"asterisk": {GlyphName: "asterisk", Wx: 600.000000, Wy: 0.000000},
"at": {GlyphName: "at", Wx: 600.000000, Wy: 0.000000},
"atilde": {GlyphName: "atilde", Wx: 600.000000, Wy: 0.000000},
"b": {GlyphName: "b", Wx: 600.000000, Wy: 0.000000},
"backslash": {GlyphName: "backslash", Wx: 600.000000, Wy: 0.000000},
"bar": {GlyphName: "bar", Wx: 600.000000, Wy: 0.000000},
"braceleft": {GlyphName: "braceleft", Wx: 600.000000, Wy: 0.000000},
"braceright": {GlyphName: "braceright", Wx: 600.000000, Wy: 0.000000},
"bracketleft": {GlyphName: "bracketleft", Wx: 600.000000, Wy: 0.000000},
"bracketright": {GlyphName: "bracketright", Wx: 600.000000, Wy: 0.000000},
"breve": {GlyphName: "breve", Wx: 600.000000, Wy: 0.000000},
"brokenbar": {GlyphName: "brokenbar", Wx: 600.000000, Wy: 0.000000},
"bullet": {GlyphName: "bullet", Wx: 600.000000, Wy: 0.000000},
"c": {GlyphName: "c", Wx: 600.000000, Wy: 0.000000},
"cacute": {GlyphName: "cacute", Wx: 600.000000, Wy: 0.000000},
"caron": {GlyphName: "caron", Wx: 600.000000, Wy: 0.000000},
"ccaron": {GlyphName: "ccaron", Wx: 600.000000, Wy: 0.000000},
"ccedilla": {GlyphName: "ccedilla", Wx: 600.000000, Wy: 0.000000},
"cedilla": {GlyphName: "cedilla", Wx: 600.000000, Wy: 0.000000},
"cent": {GlyphName: "cent", Wx: 600.000000, Wy: 0.000000},
"circumflex": {GlyphName: "circumflex", Wx: 600.000000, Wy: 0.000000},
"colon": {GlyphName: "colon", Wx: 600.000000, Wy: 0.000000},
"comma": {GlyphName: "comma", Wx: 600.000000, Wy: 0.000000},
"commaaccent": {GlyphName: "commaaccent", Wx: 600.000000, Wy: 0.000000},
"copyright": {GlyphName: "copyright", Wx: 600.000000, Wy: 0.000000},
"currency": {GlyphName: "currency", Wx: 600.000000, Wy: 0.000000},
"d": {GlyphName: "d", Wx: 600.000000, Wy: 0.000000},
"dagger": {GlyphName: "dagger", Wx: 600.000000, Wy: 0.000000},
"daggerdbl": {GlyphName: "daggerdbl", Wx: 600.000000, Wy: 0.000000},
"dcaron": {GlyphName: "dcaron", Wx: 600.000000, Wy: 0.000000},
"dcroat": {GlyphName: "dcroat", Wx: 600.000000, Wy: 0.000000},
"degree": {GlyphName: "degree", Wx: 600.000000, Wy: 0.000000},
"dieresis": {GlyphName: "dieresis", Wx: 600.000000, Wy: 0.000000},
"divide": {GlyphName: "divide", Wx: 600.000000, Wy: 0.000000},
"dollar": {GlyphName: "dollar", Wx: 600.000000, Wy: 0.000000},
"dotaccent": {GlyphName: "dotaccent", Wx: 600.000000, Wy: 0.000000},
"dotlessi": {GlyphName: "dotlessi", Wx: 600.000000, Wy: 0.000000},
"e": {GlyphName: "e", Wx: 600.000000, Wy: 0.000000},
"eacute": {GlyphName: "eacute", Wx: 600.000000, Wy: 0.000000},
"ecaron": {GlyphName: "ecaron", Wx: 600.000000, Wy: 0.000000},
"ecircumflex": {GlyphName: "ecircumflex", Wx: 600.000000, Wy: 0.000000},
"edieresis": {GlyphName: "edieresis", Wx: 600.000000, Wy: 0.000000},
"edotaccent": {GlyphName: "edotaccent", Wx: 600.000000, Wy: 0.000000},
"egrave": {GlyphName: "egrave", Wx: 600.000000, Wy: 0.000000},
"eight": {GlyphName: "eight", Wx: 600.000000, Wy: 0.000000},
"ellipsis": {GlyphName: "ellipsis", Wx: 600.000000, Wy: 0.000000},
"emacron": {GlyphName: "emacron", Wx: 600.000000, Wy: 0.000000},
"emdash": {GlyphName: "emdash", Wx: 600.000000, Wy: 0.000000},
"endash": {GlyphName: "endash", Wx: 600.000000, Wy: 0.000000},
"eogonek": {GlyphName: "eogonek", Wx: 600.000000, Wy: 0.000000},
"equal": {GlyphName: "equal", Wx: 600.000000, Wy: 0.000000},
"eth": {GlyphName: "eth", Wx: 600.000000, Wy: 0.000000},
"exclam": {GlyphName: "exclam", Wx: 600.000000, Wy: 0.000000},
"exclamdown": {GlyphName: "exclamdown", Wx: 600.000000, Wy: 0.000000},
"f": {GlyphName: "f", Wx: 600.000000, Wy: 0.000000},
"fi": {GlyphName: "fi", Wx: 600.000000, Wy: 0.000000},
"five": {GlyphName: "five", Wx: 600.000000, Wy: 0.000000},
"fl": {GlyphName: "fl", Wx: 600.000000, Wy: 0.000000},
"florin": {GlyphName: "florin", Wx: 600.000000, Wy: 0.000000},
"four": {GlyphName: "four", Wx: 600.000000, Wy: 0.000000},
"fraction": {GlyphName: "fraction", Wx: 600.000000, Wy: 0.000000},
"g": {GlyphName: "g", Wx: 600.000000, Wy: 0.000000},
"gbreve": {GlyphName: "gbreve", Wx: 600.000000, Wy: 0.000000},
"gcommaaccent": {GlyphName: "gcommaaccent", Wx: 600.000000, Wy: 0.000000},
"germandbls": {GlyphName: "germandbls", Wx: 600.000000, Wy: 0.000000},
"grave": {GlyphName: "grave", Wx: 600.000000, Wy: 0.000000},
"greater": {GlyphName: "greater", Wx: 600.000000, Wy: 0.000000},
"greaterequal": {GlyphName: "greaterequal", Wx: 600.000000, Wy: 0.000000},
"guillemotleft": {GlyphName: "guillemotleft", Wx: 600.000000, Wy: 0.000000},
"guillemotright": {GlyphName: "guillemotright", Wx: 600.000000, Wy: 0.000000},
"guilsinglleft": {GlyphName: "guilsinglleft", Wx: 600.000000, Wy: 0.000000},
"guilsinglright": {GlyphName: "guilsinglright", Wx: 600.000000, Wy: 0.000000},
"h": {GlyphName: "h", Wx: 600.000000, Wy: 0.000000},
"hungarumlaut": {GlyphName: "hungarumlaut", Wx: 600.000000, Wy: 0.000000},
"hyphen": {GlyphName: "hyphen", Wx: 600.000000, Wy: 0.000000},
"i": {GlyphName: "i", Wx: 600.000000, Wy: 0.000000},
"iacute": {GlyphName: "iacute", Wx: 600.000000, Wy: 0.000000},
"icircumflex": {GlyphName: "icircumflex", Wx: 600.000000, Wy: 0.000000},
"idieresis": {GlyphName: "idieresis", Wx: 600.000000, Wy: 0.000000},
"igrave": {GlyphName: "igrave", Wx: 600.000000, Wy: 0.000000},
"imacron": {GlyphName: "imacron", Wx: 600.000000, Wy: 0.000000},
"iogonek": {GlyphName: "iogonek", Wx: 600.000000, Wy: 0.000000},
"j": {GlyphName: "j", Wx: 600.000000, Wy: 0.000000},
"k": {GlyphName: "k", Wx: 600.000000, Wy: 0.000000},
"kcommaaccent": {GlyphName: "kcommaaccent", Wx: 600.000000, Wy: 0.000000},
"l": {GlyphName: "l", Wx: 600.000000, Wy: 0.000000},
"lacute": {GlyphName: "lacute", Wx: 600.000000, Wy: 0.000000},
"lcaron": {GlyphName: "lcaron", Wx: 600.000000, Wy: 0.000000},
"lcommaaccent": {GlyphName: "lcommaaccent", Wx: 600.000000, Wy: 0.000000},
"less": {GlyphName: "less", Wx: 600.000000, Wy: 0.000000},
"lessequal": {GlyphName: "lessequal", Wx: 600.000000, Wy: 0.000000},
"logicalnot": {GlyphName: "logicalnot", Wx: 600.000000, Wy: 0.000000},
"lozenge": {GlyphName: "lozenge", Wx: 600.000000, Wy: 0.000000},
"lslash": {GlyphName: "lslash", Wx: 600.000000, Wy: 0.000000},
"m": {GlyphName: "m", Wx: 600.000000, Wy: 0.000000},
"macron": {GlyphName: "macron", Wx: 600.000000, Wy: 0.000000},
"minus": {GlyphName: "minus", Wx: 600.000000, Wy: 0.000000},
"mu": {GlyphName: "mu", Wx: 600.000000, Wy: 0.000000},
"multiply": {GlyphName: "multiply", Wx: 600.000000, Wy: 0.000000},
"n": {GlyphName: "n", Wx: 600.000000, Wy: 0.000000},
"nacute": {GlyphName: "nacute", Wx: 600.000000, Wy: 0.000000},
"ncaron": {GlyphName: "ncaron", Wx: 600.000000, Wy: 0.000000},
"ncommaaccent": {GlyphName: "ncommaaccent", Wx: 600.000000, Wy: 0.000000},
"nine": {GlyphName: "nine", Wx: 600.000000, Wy: 0.000000},
"notequal": {GlyphName: "notequal", Wx: 600.000000, Wy: 0.000000},
"ntilde": {GlyphName: "ntilde", Wx: 600.000000, Wy: 0.000000},
"numbersign": {GlyphName: "numbersign", Wx: 600.000000, Wy: 0.000000},
"o": {GlyphName: "o", Wx: 600.000000, Wy: 0.000000},
"oacute": {GlyphName: "oacute", Wx: 600.000000, Wy: 0.000000},
"ocircumflex": {GlyphName: "ocircumflex", Wx: 600.000000, Wy: 0.000000},
"odieresis": {GlyphName: "odieresis", Wx: 600.000000, Wy: 0.000000},
"oe": {GlyphName: "oe", Wx: 600.000000, Wy: 0.000000},
"ogonek": {GlyphName: "ogonek", Wx: 600.000000, Wy: 0.000000},
"ograve": {GlyphName: "ograve", Wx: 600.000000, Wy: 0.000000},
"ohungarumlaut": {GlyphName: "ohungarumlaut", Wx: 600.000000, Wy: 0.000000},
"omacron": {GlyphName: "omacron", Wx: 600.000000, Wy: 0.000000},
"one": {GlyphName: "one", Wx: 600.000000, Wy: 0.000000},
"onehalf": {GlyphName: "onehalf", Wx: 600.000000, Wy: 0.000000},
"onequarter": {GlyphName: "onequarter", Wx: 600.000000, Wy: 0.000000},
"onesuperior": {GlyphName: "onesuperior", Wx: 600.000000, Wy: 0.000000},
"ordfeminine": {GlyphName: "ordfeminine", Wx: 600.000000, Wy: 0.000000},
"ordmasculine": {GlyphName: "ordmasculine", Wx: 600.000000, Wy: 0.000000},
"oslash": {GlyphName: "oslash", Wx: 600.000000, Wy: 0.000000},
"otilde": {GlyphName: "otilde", Wx: 600.000000, Wy: 0.000000},
"p": {GlyphName: "p", Wx: 600.000000, Wy: 0.000000},
"paragraph": {GlyphName: "paragraph", Wx: 600.000000, Wy: 0.000000},
"parenleft": {GlyphName: "parenleft", Wx: 600.000000, Wy: 0.000000},
"parenright": {GlyphName: "parenright", Wx: 600.000000, Wy: 0.000000},
"partialdiff": {GlyphName: "partialdiff", Wx: 600.000000, Wy: 0.000000},
"percent": {GlyphName: "percent", Wx: 600.000000, Wy: 0.000000},
"period": {GlyphName: "period", Wx: 600.000000, Wy: 0.000000},
"periodcentered": {GlyphName: "periodcentered", Wx: 600.000000, Wy: 0.000000},
"perthousand": {GlyphName: "perthousand", Wx: 600.000000, Wy: 0.000000},
"plus": {GlyphName: "plus", Wx: 600.000000, Wy: 0.000000},
"plusminus": {GlyphName: "plusminus", Wx: 600.000000, Wy: 0.000000},
"q": {GlyphName: "q", Wx: 600.000000, Wy: 0.000000},
"question": {GlyphName: "question", Wx: 600.000000, Wy: 0.000000},
"questiondown": {GlyphName: "questiondown", Wx: 600.000000, Wy: 0.000000},
"quotedbl": {GlyphName: "quotedbl", Wx: 600.000000, Wy: 0.000000},
"quotedblbase": {GlyphName: "quotedblbase", Wx: 600.000000, Wy: 0.000000},
"quotedblleft": {GlyphName: "quotedblleft", Wx: 600.000000, Wy: 0.000000},
"quotedblright": {GlyphName: "quotedblright", Wx: 600.000000, Wy: 0.000000},
"quoteleft": {GlyphName: "quoteleft", Wx: 600.000000, Wy: 0.000000},
"quoteright": {GlyphName: "quoteright", Wx: 600.000000, Wy: 0.000000},
"quotesinglbase": {GlyphName: "quotesinglbase", Wx: 600.000000, Wy: 0.000000},
"quotesingle": {GlyphName: "quotesingle", Wx: 600.000000, Wy: 0.000000},
"r": {GlyphName: "r", Wx: 600.000000, Wy: 0.000000},
"racute": {GlyphName: "racute", Wx: 600.000000, Wy: 0.000000},
"radical": {GlyphName: "radical", Wx: 600.000000, Wy: 0.000000},
"rcaron": {GlyphName: "rcaron", Wx: 600.000000, Wy: 0.000000},
"rcommaaccent": {GlyphName: "rcommaaccent", Wx: 600.000000, Wy: 0.000000},
"registered": {GlyphName: "registered", Wx: 600.000000, Wy: 0.000000},
"ring": {GlyphName: "ring", Wx: 600.000000, Wy: 0.000000},
"s": {GlyphName: "s", Wx: 600.000000, Wy: 0.000000},
"sacute": {GlyphName: "sacute", Wx: 600.000000, Wy: 0.000000},
"scaron": {GlyphName: "scaron", Wx: 600.000000, Wy: 0.000000},
"scedilla": {GlyphName: "scedilla", Wx: 600.000000, Wy: 0.000000},
"scommaaccent": {GlyphName: "scommaaccent", Wx: 600.000000, Wy: 0.000000},
"section": {GlyphName: "section", Wx: 600.000000, Wy: 0.000000},
"semicolon": {GlyphName: "semicolon", Wx: 600.000000, Wy: 0.000000},
"seven": {GlyphName: "seven", Wx: 600.000000, Wy: 0.000000},
"six": {GlyphName: "six", Wx: 600.000000, Wy: 0.000000},
"slash": {GlyphName: "slash", Wx: 600.000000, Wy: 0.000000},
"space": {GlyphName: "space", Wx: 600.000000, Wy: 0.000000},
"sterling": {GlyphName: "sterling", Wx: 600.000000, Wy: 0.000000},
"summation": {GlyphName: "summation", Wx: 600.000000, Wy: 0.000000},
"t": {GlyphName: "t", Wx: 600.000000, Wy: 0.000000},
"tcaron": {GlyphName: "tcaron", Wx: 600.000000, Wy: 0.000000},
"tcommaaccent": {GlyphName: "tcommaaccent", Wx: 600.000000, Wy: 0.000000},
"thorn": {GlyphName: "thorn", Wx: 600.000000, Wy: 0.000000},
"three": {GlyphName: "three", Wx: 600.000000, Wy: 0.000000},
"threequarters": {GlyphName: "threequarters", Wx: 600.000000, Wy: 0.000000},
"threesuperior": {GlyphName: "threesuperior", Wx: 600.000000, Wy: 0.000000},
"tilde": {GlyphName: "tilde", Wx: 600.000000, Wy: 0.000000},
"trademark": {GlyphName: "trademark", Wx: 600.000000, Wy: 0.000000},
"two": {GlyphName: "two", Wx: 600.000000, Wy: 0.000000},
"twosuperior": {GlyphName: "twosuperior", Wx: 600.000000, Wy: 0.000000},
"u": {GlyphName: "u", Wx: 600.000000, Wy: 0.000000},
"uacute": {GlyphName: "uacute", Wx: 600.000000, Wy: 0.000000},
"ucircumflex": {GlyphName: "ucircumflex", Wx: 600.000000, Wy: 0.000000},
"udieresis": {GlyphName: "udieresis", Wx: 600.000000, Wy: 0.000000},
"ugrave": {GlyphName: "ugrave", Wx: 600.000000, Wy: 0.000000},
"uhungarumlaut": {GlyphName: "uhungarumlaut", Wx: 600.000000, Wy: 0.000000},
"umacron": {GlyphName: "umacron", Wx: 600.000000, Wy: 0.000000},
"underscore": {GlyphName: "underscore", Wx: 600.000000, Wy: 0.000000},
"uogonek": {GlyphName: "uogonek", Wx: 600.000000, Wy: 0.000000},
"uring": {GlyphName: "uring", Wx: 600.000000, Wy: 0.000000},
"v": {GlyphName: "v", Wx: 600.000000, Wy: 0.000000},
"w": {GlyphName: "w", Wx: 600.000000, Wy: 0.000000},
"x": {GlyphName: "x", Wx: 600.000000, Wy: 0.000000},
"y": {GlyphName: "y", Wx: 600.000000, Wy: 0.000000},
"yacute": {GlyphName: "yacute", Wx: 600.000000, Wy: 0.000000},
"ydieresis": {GlyphName: "ydieresis", Wx: 600.000000, Wy: 0.000000},
"yen": {GlyphName: "yen", Wx: 600.000000, Wy: 0.000000},
"z": {GlyphName: "z", Wx: 600.000000, Wy: 0.000000},
"zacute": {GlyphName: "zacute", Wx: 600.000000, Wy: 0.000000},
"zcaron": {GlyphName: "zcaron", Wx: 600.000000, Wy: 0.000000},
"zdotaccent": {GlyphName: "zdotaccent", Wx: 600.000000, Wy: 0.000000},
"zero": {GlyphName: "zero", Wx: 600.000000, Wy: 0.000000},
}

View File

@@ -0,0 +1,362 @@
package fonts
import (
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model/textencoding"
)
// Font Courier-Bold. Implements Font interface.
// This is a built-in font and it is assumed that every reader has access to it.
type fontCourierBold struct {
encoder textencoding.TextEncoder
}
func NewFontCourierBold() fontCourierBold {
font := fontCourierBold{}
font.encoder = textencoding.NewWinAnsiTextEncoder() // Default
return font
}
func (font fontCourierBold) SetEncoder(encoder textencoding.TextEncoder) {
font.encoder = encoder
}
func (font fontCourierBold) GetGlyphCharMetrics(glyph string) (CharMetrics, bool) {
metrics, has := courierBoldCharMetrics[glyph]
if !has {
return metrics, false
}
return metrics, true
}
func (font fontCourierBold) ToPdfObject() core.PdfObject {
obj := &core.PdfIndirectObject{}
fontDict := core.MakeDict()
fontDict.Set("Type", core.MakeName("Font"))
fontDict.Set("Subtype", core.MakeName("Type1"))
fontDict.Set("BaseFont", core.MakeName("Courier-Bold"))
fontDict.Set("Encoding", font.encoder.ToPdfObject())
obj.PdfObject = fontDict
return obj
}
var courierBoldCharMetrics map[string]CharMetrics = map[string]CharMetrics{
"A": {GlyphName: "A", Wx: 600.000000, Wy: 0.000000},
"AE": {GlyphName: "AE", Wx: 600.000000, Wy: 0.000000},
"Aacute": {GlyphName: "Aacute", Wx: 600.000000, Wy: 0.000000},
"Abreve": {GlyphName: "Abreve", Wx: 600.000000, Wy: 0.000000},
"Acircumflex": {GlyphName: "Acircumflex", Wx: 600.000000, Wy: 0.000000},
"Adieresis": {GlyphName: "Adieresis", Wx: 600.000000, Wy: 0.000000},
"Agrave": {GlyphName: "Agrave", Wx: 600.000000, Wy: 0.000000},
"Amacron": {GlyphName: "Amacron", Wx: 600.000000, Wy: 0.000000},
"Aogonek": {GlyphName: "Aogonek", Wx: 600.000000, Wy: 0.000000},
"Aring": {GlyphName: "Aring", Wx: 600.000000, Wy: 0.000000},
"Atilde": {GlyphName: "Atilde", Wx: 600.000000, Wy: 0.000000},
"B": {GlyphName: "B", Wx: 600.000000, Wy: 0.000000},
"C": {GlyphName: "C", Wx: 600.000000, Wy: 0.000000},
"Cacute": {GlyphName: "Cacute", Wx: 600.000000, Wy: 0.000000},
"Ccaron": {GlyphName: "Ccaron", Wx: 600.000000, Wy: 0.000000},
"Ccedilla": {GlyphName: "Ccedilla", Wx: 600.000000, Wy: 0.000000},
"D": {GlyphName: "D", Wx: 600.000000, Wy: 0.000000},
"Dcaron": {GlyphName: "Dcaron", Wx: 600.000000, Wy: 0.000000},
"Dcroat": {GlyphName: "Dcroat", Wx: 600.000000, Wy: 0.000000},
"Delta": {GlyphName: "Delta", Wx: 600.000000, Wy: 0.000000},
"E": {GlyphName: "E", Wx: 600.000000, Wy: 0.000000},
"Eacute": {GlyphName: "Eacute", Wx: 600.000000, Wy: 0.000000},
"Ecaron": {GlyphName: "Ecaron", Wx: 600.000000, Wy: 0.000000},
"Ecircumflex": {GlyphName: "Ecircumflex", Wx: 600.000000, Wy: 0.000000},
"Edieresis": {GlyphName: "Edieresis", Wx: 600.000000, Wy: 0.000000},
"Edotaccent": {GlyphName: "Edotaccent", Wx: 600.000000, Wy: 0.000000},
"Egrave": {GlyphName: "Egrave", Wx: 600.000000, Wy: 0.000000},
"Emacron": {GlyphName: "Emacron", Wx: 600.000000, Wy: 0.000000},
"Eogonek": {GlyphName: "Eogonek", Wx: 600.000000, Wy: 0.000000},
"Eth": {GlyphName: "Eth", Wx: 600.000000, Wy: 0.000000},
"Euro": {GlyphName: "Euro", Wx: 600.000000, Wy: 0.000000},
"F": {GlyphName: "F", Wx: 600.000000, Wy: 0.000000},
"G": {GlyphName: "G", Wx: 600.000000, Wy: 0.000000},
"Gbreve": {GlyphName: "Gbreve", Wx: 600.000000, Wy: 0.000000},
"Gcommaaccent": {GlyphName: "Gcommaaccent", Wx: 600.000000, Wy: 0.000000},
"H": {GlyphName: "H", Wx: 600.000000, Wy: 0.000000},
"I": {GlyphName: "I", Wx: 600.000000, Wy: 0.000000},
"Iacute": {GlyphName: "Iacute", Wx: 600.000000, Wy: 0.000000},
"Icircumflex": {GlyphName: "Icircumflex", Wx: 600.000000, Wy: 0.000000},
"Idieresis": {GlyphName: "Idieresis", Wx: 600.000000, Wy: 0.000000},
"Idotaccent": {GlyphName: "Idotaccent", Wx: 600.000000, Wy: 0.000000},
"Igrave": {GlyphName: "Igrave", Wx: 600.000000, Wy: 0.000000},
"Imacron": {GlyphName: "Imacron", Wx: 600.000000, Wy: 0.000000},
"Iogonek": {GlyphName: "Iogonek", Wx: 600.000000, Wy: 0.000000},
"J": {GlyphName: "J", Wx: 600.000000, Wy: 0.000000},
"K": {GlyphName: "K", Wx: 600.000000, Wy: 0.000000},
"Kcommaaccent": {GlyphName: "Kcommaaccent", Wx: 600.000000, Wy: 0.000000},
"L": {GlyphName: "L", Wx: 600.000000, Wy: 0.000000},
"Lacute": {GlyphName: "Lacute", Wx: 600.000000, Wy: 0.000000},
"Lcaron": {GlyphName: "Lcaron", Wx: 600.000000, Wy: 0.000000},
"Lcommaaccent": {GlyphName: "Lcommaaccent", Wx: 600.000000, Wy: 0.000000},
"Lslash": {GlyphName: "Lslash", Wx: 600.000000, Wy: 0.000000},
"M": {GlyphName: "M", Wx: 600.000000, Wy: 0.000000},
"N": {GlyphName: "N", Wx: 600.000000, Wy: 0.000000},
"Nacute": {GlyphName: "Nacute", Wx: 600.000000, Wy: 0.000000},
"Ncaron": {GlyphName: "Ncaron", Wx: 600.000000, Wy: 0.000000},
"Ncommaaccent": {GlyphName: "Ncommaaccent", Wx: 600.000000, Wy: 0.000000},
"Ntilde": {GlyphName: "Ntilde", Wx: 600.000000, Wy: 0.000000},
"O": {GlyphName: "O", Wx: 600.000000, Wy: 0.000000},
"OE": {GlyphName: "OE", Wx: 600.000000, Wy: 0.000000},
"Oacute": {GlyphName: "Oacute", Wx: 600.000000, Wy: 0.000000},
"Ocircumflex": {GlyphName: "Ocircumflex", Wx: 600.000000, Wy: 0.000000},
"Odieresis": {GlyphName: "Odieresis", Wx: 600.000000, Wy: 0.000000},
"Ograve": {GlyphName: "Ograve", Wx: 600.000000, Wy: 0.000000},
"Ohungarumlaut": {GlyphName: "Ohungarumlaut", Wx: 600.000000, Wy: 0.000000},
"Omacron": {GlyphName: "Omacron", Wx: 600.000000, Wy: 0.000000},
"Oslash": {GlyphName: "Oslash", Wx: 600.000000, Wy: 0.000000},
"Otilde": {GlyphName: "Otilde", Wx: 600.000000, Wy: 0.000000},
"P": {GlyphName: "P", Wx: 600.000000, Wy: 0.000000},
"Q": {GlyphName: "Q", Wx: 600.000000, Wy: 0.000000},
"R": {GlyphName: "R", Wx: 600.000000, Wy: 0.000000},
"Racute": {GlyphName: "Racute", Wx: 600.000000, Wy: 0.000000},
"Rcaron": {GlyphName: "Rcaron", Wx: 600.000000, Wy: 0.000000},
"Rcommaaccent": {GlyphName: "Rcommaaccent", Wx: 600.000000, Wy: 0.000000},
"S": {GlyphName: "S", Wx: 600.000000, Wy: 0.000000},
"Sacute": {GlyphName: "Sacute", Wx: 600.000000, Wy: 0.000000},
"Scaron": {GlyphName: "Scaron", Wx: 600.000000, Wy: 0.000000},
"Scedilla": {GlyphName: "Scedilla", Wx: 600.000000, Wy: 0.000000},
"Scommaaccent": {GlyphName: "Scommaaccent", Wx: 600.000000, Wy: 0.000000},
"T": {GlyphName: "T", Wx: 600.000000, Wy: 0.000000},
"Tcaron": {GlyphName: "Tcaron", Wx: 600.000000, Wy: 0.000000},
"Tcommaaccent": {GlyphName: "Tcommaaccent", Wx: 600.000000, Wy: 0.000000},
"Thorn": {GlyphName: "Thorn", Wx: 600.000000, Wy: 0.000000},
"U": {GlyphName: "U", Wx: 600.000000, Wy: 0.000000},
"Uacute": {GlyphName: "Uacute", Wx: 600.000000, Wy: 0.000000},
"Ucircumflex": {GlyphName: "Ucircumflex", Wx: 600.000000, Wy: 0.000000},
"Udieresis": {GlyphName: "Udieresis", Wx: 600.000000, Wy: 0.000000},
"Ugrave": {GlyphName: "Ugrave", Wx: 600.000000, Wy: 0.000000},
"Uhungarumlaut": {GlyphName: "Uhungarumlaut", Wx: 600.000000, Wy: 0.000000},
"Umacron": {GlyphName: "Umacron", Wx: 600.000000, Wy: 0.000000},
"Uogonek": {GlyphName: "Uogonek", Wx: 600.000000, Wy: 0.000000},
"Uring": {GlyphName: "Uring", Wx: 600.000000, Wy: 0.000000},
"V": {GlyphName: "V", Wx: 600.000000, Wy: 0.000000},
"W": {GlyphName: "W", Wx: 600.000000, Wy: 0.000000},
"X": {GlyphName: "X", Wx: 600.000000, Wy: 0.000000},
"Y": {GlyphName: "Y", Wx: 600.000000, Wy: 0.000000},
"Yacute": {GlyphName: "Yacute", Wx: 600.000000, Wy: 0.000000},
"Ydieresis": {GlyphName: "Ydieresis", Wx: 600.000000, Wy: 0.000000},
"Z": {GlyphName: "Z", Wx: 600.000000, Wy: 0.000000},
"Zacute": {GlyphName: "Zacute", Wx: 600.000000, Wy: 0.000000},
"Zcaron": {GlyphName: "Zcaron", Wx: 600.000000, Wy: 0.000000},
"Zdotaccent": {GlyphName: "Zdotaccent", Wx: 600.000000, Wy: 0.000000},
"a": {GlyphName: "a", Wx: 600.000000, Wy: 0.000000},
"aacute": {GlyphName: "aacute", Wx: 600.000000, Wy: 0.000000},
"abreve": {GlyphName: "abreve", Wx: 600.000000, Wy: 0.000000},
"acircumflex": {GlyphName: "acircumflex", Wx: 600.000000, Wy: 0.000000},
"acute": {GlyphName: "acute", Wx: 600.000000, Wy: 0.000000},
"adieresis": {GlyphName: "adieresis", Wx: 600.000000, Wy: 0.000000},
"ae": {GlyphName: "ae", Wx: 600.000000, Wy: 0.000000},
"agrave": {GlyphName: "agrave", Wx: 600.000000, Wy: 0.000000},
"amacron": {GlyphName: "amacron", Wx: 600.000000, Wy: 0.000000},
"ampersand": {GlyphName: "ampersand", Wx: 600.000000, Wy: 0.000000},
"aogonek": {GlyphName: "aogonek", Wx: 600.000000, Wy: 0.000000},
"aring": {GlyphName: "aring", Wx: 600.000000, Wy: 0.000000},
"asciicircum": {GlyphName: "asciicircum", Wx: 600.000000, Wy: 0.000000},
"asciitilde": {GlyphName: "asciitilde", Wx: 600.000000, Wy: 0.000000},
"asterisk": {GlyphName: "asterisk", Wx: 600.000000, Wy: 0.000000},
"at": {GlyphName: "at", Wx: 600.000000, Wy: 0.000000},
"atilde": {GlyphName: "atilde", Wx: 600.000000, Wy: 0.000000},
"b": {GlyphName: "b", Wx: 600.000000, Wy: 0.000000},
"backslash": {GlyphName: "backslash", Wx: 600.000000, Wy: 0.000000},
"bar": {GlyphName: "bar", Wx: 600.000000, Wy: 0.000000},
"braceleft": {GlyphName: "braceleft", Wx: 600.000000, Wy: 0.000000},
"braceright": {GlyphName: "braceright", Wx: 600.000000, Wy: 0.000000},
"bracketleft": {GlyphName: "bracketleft", Wx: 600.000000, Wy: 0.000000},
"bracketright": {GlyphName: "bracketright", Wx: 600.000000, Wy: 0.000000},
"breve": {GlyphName: "breve", Wx: 600.000000, Wy: 0.000000},
"brokenbar": {GlyphName: "brokenbar", Wx: 600.000000, Wy: 0.000000},
"bullet": {GlyphName: "bullet", Wx: 600.000000, Wy: 0.000000},
"c": {GlyphName: "c", Wx: 600.000000, Wy: 0.000000},
"cacute": {GlyphName: "cacute", Wx: 600.000000, Wy: 0.000000},
"caron": {GlyphName: "caron", Wx: 600.000000, Wy: 0.000000},
"ccaron": {GlyphName: "ccaron", Wx: 600.000000, Wy: 0.000000},
"ccedilla": {GlyphName: "ccedilla", Wx: 600.000000, Wy: 0.000000},
"cedilla": {GlyphName: "cedilla", Wx: 600.000000, Wy: 0.000000},
"cent": {GlyphName: "cent", Wx: 600.000000, Wy: 0.000000},
"circumflex": {GlyphName: "circumflex", Wx: 600.000000, Wy: 0.000000},
"colon": {GlyphName: "colon", Wx: 600.000000, Wy: 0.000000},
"comma": {GlyphName: "comma", Wx: 600.000000, Wy: 0.000000},
"commaaccent": {GlyphName: "commaaccent", Wx: 600.000000, Wy: 0.000000},
"copyright": {GlyphName: "copyright", Wx: 600.000000, Wy: 0.000000},
"currency": {GlyphName: "currency", Wx: 600.000000, Wy: 0.000000},
"d": {GlyphName: "d", Wx: 600.000000, Wy: 0.000000},
"dagger": {GlyphName: "dagger", Wx: 600.000000, Wy: 0.000000},
"daggerdbl": {GlyphName: "daggerdbl", Wx: 600.000000, Wy: 0.000000},
"dcaron": {GlyphName: "dcaron", Wx: 600.000000, Wy: 0.000000},
"dcroat": {GlyphName: "dcroat", Wx: 600.000000, Wy: 0.000000},
"degree": {GlyphName: "degree", Wx: 600.000000, Wy: 0.000000},
"dieresis": {GlyphName: "dieresis", Wx: 600.000000, Wy: 0.000000},
"divide": {GlyphName: "divide", Wx: 600.000000, Wy: 0.000000},
"dollar": {GlyphName: "dollar", Wx: 600.000000, Wy: 0.000000},
"dotaccent": {GlyphName: "dotaccent", Wx: 600.000000, Wy: 0.000000},
"dotlessi": {GlyphName: "dotlessi", Wx: 600.000000, Wy: 0.000000},
"e": {GlyphName: "e", Wx: 600.000000, Wy: 0.000000},
"eacute": {GlyphName: "eacute", Wx: 600.000000, Wy: 0.000000},
"ecaron": {GlyphName: "ecaron", Wx: 600.000000, Wy: 0.000000},
"ecircumflex": {GlyphName: "ecircumflex", Wx: 600.000000, Wy: 0.000000},
"edieresis": {GlyphName: "edieresis", Wx: 600.000000, Wy: 0.000000},
"edotaccent": {GlyphName: "edotaccent", Wx: 600.000000, Wy: 0.000000},
"egrave": {GlyphName: "egrave", Wx: 600.000000, Wy: 0.000000},
"eight": {GlyphName: "eight", Wx: 600.000000, Wy: 0.000000},
"ellipsis": {GlyphName: "ellipsis", Wx: 600.000000, Wy: 0.000000},
"emacron": {GlyphName: "emacron", Wx: 600.000000, Wy: 0.000000},
"emdash": {GlyphName: "emdash", Wx: 600.000000, Wy: 0.000000},
"endash": {GlyphName: "endash", Wx: 600.000000, Wy: 0.000000},
"eogonek": {GlyphName: "eogonek", Wx: 600.000000, Wy: 0.000000},
"equal": {GlyphName: "equal", Wx: 600.000000, Wy: 0.000000},
"eth": {GlyphName: "eth", Wx: 600.000000, Wy: 0.000000},
"exclam": {GlyphName: "exclam", Wx: 600.000000, Wy: 0.000000},
"exclamdown": {GlyphName: "exclamdown", Wx: 600.000000, Wy: 0.000000},
"f": {GlyphName: "f", Wx: 600.000000, Wy: 0.000000},
"fi": {GlyphName: "fi", Wx: 600.000000, Wy: 0.000000},
"five": {GlyphName: "five", Wx: 600.000000, Wy: 0.000000},
"fl": {GlyphName: "fl", Wx: 600.000000, Wy: 0.000000},
"florin": {GlyphName: "florin", Wx: 600.000000, Wy: 0.000000},
"four": {GlyphName: "four", Wx: 600.000000, Wy: 0.000000},
"fraction": {GlyphName: "fraction", Wx: 600.000000, Wy: 0.000000},
"g": {GlyphName: "g", Wx: 600.000000, Wy: 0.000000},
"gbreve": {GlyphName: "gbreve", Wx: 600.000000, Wy: 0.000000},
"gcommaaccent": {GlyphName: "gcommaaccent", Wx: 600.000000, Wy: 0.000000},
"germandbls": {GlyphName: "germandbls", Wx: 600.000000, Wy: 0.000000},
"grave": {GlyphName: "grave", Wx: 600.000000, Wy: 0.000000},
"greater": {GlyphName: "greater", Wx: 600.000000, Wy: 0.000000},
"greaterequal": {GlyphName: "greaterequal", Wx: 600.000000, Wy: 0.000000},
"guillemotleft": {GlyphName: "guillemotleft", Wx: 600.000000, Wy: 0.000000},
"guillemotright": {GlyphName: "guillemotright", Wx: 600.000000, Wy: 0.000000},
"guilsinglleft": {GlyphName: "guilsinglleft", Wx: 600.000000, Wy: 0.000000},
"guilsinglright": {GlyphName: "guilsinglright", Wx: 600.000000, Wy: 0.000000},
"h": {GlyphName: "h", Wx: 600.000000, Wy: 0.000000},
"hungarumlaut": {GlyphName: "hungarumlaut", Wx: 600.000000, Wy: 0.000000},
"hyphen": {GlyphName: "hyphen", Wx: 600.000000, Wy: 0.000000},
"i": {GlyphName: "i", Wx: 600.000000, Wy: 0.000000},
"iacute": {GlyphName: "iacute", Wx: 600.000000, Wy: 0.000000},
"icircumflex": {GlyphName: "icircumflex", Wx: 600.000000, Wy: 0.000000},
"idieresis": {GlyphName: "idieresis", Wx: 600.000000, Wy: 0.000000},
"igrave": {GlyphName: "igrave", Wx: 600.000000, Wy: 0.000000},
"imacron": {GlyphName: "imacron", Wx: 600.000000, Wy: 0.000000},
"iogonek": {GlyphName: "iogonek", Wx: 600.000000, Wy: 0.000000},
"j": {GlyphName: "j", Wx: 600.000000, Wy: 0.000000},
"k": {GlyphName: "k", Wx: 600.000000, Wy: 0.000000},
"kcommaaccent": {GlyphName: "kcommaaccent", Wx: 600.000000, Wy: 0.000000},
"l": {GlyphName: "l", Wx: 600.000000, Wy: 0.000000},
"lacute": {GlyphName: "lacute", Wx: 600.000000, Wy: 0.000000},
"lcaron": {GlyphName: "lcaron", Wx: 600.000000, Wy: 0.000000},
"lcommaaccent": {GlyphName: "lcommaaccent", Wx: 600.000000, Wy: 0.000000},
"less": {GlyphName: "less", Wx: 600.000000, Wy: 0.000000},
"lessequal": {GlyphName: "lessequal", Wx: 600.000000, Wy: 0.000000},
"logicalnot": {GlyphName: "logicalnot", Wx: 600.000000, Wy: 0.000000},
"lozenge": {GlyphName: "lozenge", Wx: 600.000000, Wy: 0.000000},
"lslash": {GlyphName: "lslash", Wx: 600.000000, Wy: 0.000000},
"m": {GlyphName: "m", Wx: 600.000000, Wy: 0.000000},
"macron": {GlyphName: "macron", Wx: 600.000000, Wy: 0.000000},
"minus": {GlyphName: "minus", Wx: 600.000000, Wy: 0.000000},
"mu": {GlyphName: "mu", Wx: 600.000000, Wy: 0.000000},
"multiply": {GlyphName: "multiply", Wx: 600.000000, Wy: 0.000000},
"n": {GlyphName: "n", Wx: 600.000000, Wy: 0.000000},
"nacute": {GlyphName: "nacute", Wx: 600.000000, Wy: 0.000000},
"ncaron": {GlyphName: "ncaron", Wx: 600.000000, Wy: 0.000000},
"ncommaaccent": {GlyphName: "ncommaaccent", Wx: 600.000000, Wy: 0.000000},
"nine": {GlyphName: "nine", Wx: 600.000000, Wy: 0.000000},
"notequal": {GlyphName: "notequal", Wx: 600.000000, Wy: 0.000000},
"ntilde": {GlyphName: "ntilde", Wx: 600.000000, Wy: 0.000000},
"numbersign": {GlyphName: "numbersign", Wx: 600.000000, Wy: 0.000000},
"o": {GlyphName: "o", Wx: 600.000000, Wy: 0.000000},
"oacute": {GlyphName: "oacute", Wx: 600.000000, Wy: 0.000000},
"ocircumflex": {GlyphName: "ocircumflex", Wx: 600.000000, Wy: 0.000000},
"odieresis": {GlyphName: "odieresis", Wx: 600.000000, Wy: 0.000000},
"oe": {GlyphName: "oe", Wx: 600.000000, Wy: 0.000000},
"ogonek": {GlyphName: "ogonek", Wx: 600.000000, Wy: 0.000000},
"ograve": {GlyphName: "ograve", Wx: 600.000000, Wy: 0.000000},
"ohungarumlaut": {GlyphName: "ohungarumlaut", Wx: 600.000000, Wy: 0.000000},
"omacron": {GlyphName: "omacron", Wx: 600.000000, Wy: 0.000000},
"one": {GlyphName: "one", Wx: 600.000000, Wy: 0.000000},
"onehalf": {GlyphName: "onehalf", Wx: 600.000000, Wy: 0.000000},
"onequarter": {GlyphName: "onequarter", Wx: 600.000000, Wy: 0.000000},
"onesuperior": {GlyphName: "onesuperior", Wx: 600.000000, Wy: 0.000000},
"ordfeminine": {GlyphName: "ordfeminine", Wx: 600.000000, Wy: 0.000000},
"ordmasculine": {GlyphName: "ordmasculine", Wx: 600.000000, Wy: 0.000000},
"oslash": {GlyphName: "oslash", Wx: 600.000000, Wy: 0.000000},
"otilde": {GlyphName: "otilde", Wx: 600.000000, Wy: 0.000000},
"p": {GlyphName: "p", Wx: 600.000000, Wy: 0.000000},
"paragraph": {GlyphName: "paragraph", Wx: 600.000000, Wy: 0.000000},
"parenleft": {GlyphName: "parenleft", Wx: 600.000000, Wy: 0.000000},
"parenright": {GlyphName: "parenright", Wx: 600.000000, Wy: 0.000000},
"partialdiff": {GlyphName: "partialdiff", Wx: 600.000000, Wy: 0.000000},
"percent": {GlyphName: "percent", Wx: 600.000000, Wy: 0.000000},
"period": {GlyphName: "period", Wx: 600.000000, Wy: 0.000000},
"periodcentered": {GlyphName: "periodcentered", Wx: 600.000000, Wy: 0.000000},
"perthousand": {GlyphName: "perthousand", Wx: 600.000000, Wy: 0.000000},
"plus": {GlyphName: "plus", Wx: 600.000000, Wy: 0.000000},
"plusminus": {GlyphName: "plusminus", Wx: 600.000000, Wy: 0.000000},
"q": {GlyphName: "q", Wx: 600.000000, Wy: 0.000000},
"question": {GlyphName: "question", Wx: 600.000000, Wy: 0.000000},
"questiondown": {GlyphName: "questiondown", Wx: 600.000000, Wy: 0.000000},
"quotedbl": {GlyphName: "quotedbl", Wx: 600.000000, Wy: 0.000000},
"quotedblbase": {GlyphName: "quotedblbase", Wx: 600.000000, Wy: 0.000000},
"quotedblleft": {GlyphName: "quotedblleft", Wx: 600.000000, Wy: 0.000000},
"quotedblright": {GlyphName: "quotedblright", Wx: 600.000000, Wy: 0.000000},
"quoteleft": {GlyphName: "quoteleft", Wx: 600.000000, Wy: 0.000000},
"quoteright": {GlyphName: "quoteright", Wx: 600.000000, Wy: 0.000000},
"quotesinglbase": {GlyphName: "quotesinglbase", Wx: 600.000000, Wy: 0.000000},
"quotesingle": {GlyphName: "quotesingle", Wx: 600.000000, Wy: 0.000000},
"r": {GlyphName: "r", Wx: 600.000000, Wy: 0.000000},
"racute": {GlyphName: "racute", Wx: 600.000000, Wy: 0.000000},
"radical": {GlyphName: "radical", Wx: 600.000000, Wy: 0.000000},
"rcaron": {GlyphName: "rcaron", Wx: 600.000000, Wy: 0.000000},
"rcommaaccent": {GlyphName: "rcommaaccent", Wx: 600.000000, Wy: 0.000000},
"registered": {GlyphName: "registered", Wx: 600.000000, Wy: 0.000000},
"ring": {GlyphName: "ring", Wx: 600.000000, Wy: 0.000000},
"s": {GlyphName: "s", Wx: 600.000000, Wy: 0.000000},
"sacute": {GlyphName: "sacute", Wx: 600.000000, Wy: 0.000000},
"scaron": {GlyphName: "scaron", Wx: 600.000000, Wy: 0.000000},
"scedilla": {GlyphName: "scedilla", Wx: 600.000000, Wy: 0.000000},
"scommaaccent": {GlyphName: "scommaaccent", Wx: 600.000000, Wy: 0.000000},
"section": {GlyphName: "section", Wx: 600.000000, Wy: 0.000000},
"semicolon": {GlyphName: "semicolon", Wx: 600.000000, Wy: 0.000000},
"seven": {GlyphName: "seven", Wx: 600.000000, Wy: 0.000000},
"six": {GlyphName: "six", Wx: 600.000000, Wy: 0.000000},
"slash": {GlyphName: "slash", Wx: 600.000000, Wy: 0.000000},
"space": {GlyphName: "space", Wx: 600.000000, Wy: 0.000000},
"sterling": {GlyphName: "sterling", Wx: 600.000000, Wy: 0.000000},
"summation": {GlyphName: "summation", Wx: 600.000000, Wy: 0.000000},
"t": {GlyphName: "t", Wx: 600.000000, Wy: 0.000000},
"tcaron": {GlyphName: "tcaron", Wx: 600.000000, Wy: 0.000000},
"tcommaaccent": {GlyphName: "tcommaaccent", Wx: 600.000000, Wy: 0.000000},
"thorn": {GlyphName: "thorn", Wx: 600.000000, Wy: 0.000000},
"three": {GlyphName: "three", Wx: 600.000000, Wy: 0.000000},
"threequarters": {GlyphName: "threequarters", Wx: 600.000000, Wy: 0.000000},
"threesuperior": {GlyphName: "threesuperior", Wx: 600.000000, Wy: 0.000000},
"tilde": {GlyphName: "tilde", Wx: 600.000000, Wy: 0.000000},
"trademark": {GlyphName: "trademark", Wx: 600.000000, Wy: 0.000000},
"two": {GlyphName: "two", Wx: 600.000000, Wy: 0.000000},
"twosuperior": {GlyphName: "twosuperior", Wx: 600.000000, Wy: 0.000000},
"u": {GlyphName: "u", Wx: 600.000000, Wy: 0.000000},
"uacute": {GlyphName: "uacute", Wx: 600.000000, Wy: 0.000000},
"ucircumflex": {GlyphName: "ucircumflex", Wx: 600.000000, Wy: 0.000000},
"udieresis": {GlyphName: "udieresis", Wx: 600.000000, Wy: 0.000000},
"ugrave": {GlyphName: "ugrave", Wx: 600.000000, Wy: 0.000000},
"uhungarumlaut": {GlyphName: "uhungarumlaut", Wx: 600.000000, Wy: 0.000000},
"umacron": {GlyphName: "umacron", Wx: 600.000000, Wy: 0.000000},
"underscore": {GlyphName: "underscore", Wx: 600.000000, Wy: 0.000000},
"uogonek": {GlyphName: "uogonek", Wx: 600.000000, Wy: 0.000000},
"uring": {GlyphName: "uring", Wx: 600.000000, Wy: 0.000000},
"v": {GlyphName: "v", Wx: 600.000000, Wy: 0.000000},
"w": {GlyphName: "w", Wx: 600.000000, Wy: 0.000000},
"x": {GlyphName: "x", Wx: 600.000000, Wy: 0.000000},
"y": {GlyphName: "y", Wx: 600.000000, Wy: 0.000000},
"yacute": {GlyphName: "yacute", Wx: 600.000000, Wy: 0.000000},
"ydieresis": {GlyphName: "ydieresis", Wx: 600.000000, Wy: 0.000000},
"yen": {GlyphName: "yen", Wx: 600.000000, Wy: 0.000000},
"z": {GlyphName: "z", Wx: 600.000000, Wy: 0.000000},
"zacute": {GlyphName: "zacute", Wx: 600.000000, Wy: 0.000000},
"zcaron": {GlyphName: "zcaron", Wx: 600.000000, Wy: 0.000000},
"zdotaccent": {GlyphName: "zdotaccent", Wx: 600.000000, Wy: 0.000000},
"zero": {GlyphName: "zero", Wx: 600.000000, Wy: 0.000000},
}

View File

@@ -0,0 +1,362 @@
package fonts
import (
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model/textencoding"
)
// Font Courier-BoldOblique. Implements Font interface.
// This is a built-in font and it is assumed that every reader has access to it.
type fontCourierBoldOblique struct {
encoder textencoding.TextEncoder
}
func NewFontCourierBoldOblique() fontCourierBoldOblique {
font := fontCourierBoldOblique{}
font.encoder = textencoding.NewWinAnsiTextEncoder() // Default
return font
}
func (font fontCourierBoldOblique) SetEncoder(encoder textencoding.TextEncoder) {
font.encoder = encoder
}
func (font fontCourierBoldOblique) GetGlyphCharMetrics(glyph string) (CharMetrics, bool) {
metrics, has := courierBoldObliqueCharMetrics[glyph]
if !has {
return metrics, false
}
return metrics, true
}
func (font fontCourierBoldOblique) ToPdfObject() core.PdfObject {
obj := &core.PdfIndirectObject{}
fontDict := core.MakeDict()
fontDict.Set("Type", core.MakeName("Font"))
fontDict.Set("Subtype", core.MakeName("Type1"))
fontDict.Set("BaseFont", core.MakeName("Courier-BoldOblique"))
fontDict.Set("Encoding", font.encoder.ToPdfObject())
obj.PdfObject = fontDict
return obj
}
var courierBoldObliqueCharMetrics map[string]CharMetrics = map[string]CharMetrics{
"A": {GlyphName: "A", Wx: 600.000000, Wy: 0.000000},
"AE": {GlyphName: "AE", Wx: 600.000000, Wy: 0.000000},
"Aacute": {GlyphName: "Aacute", Wx: 600.000000, Wy: 0.000000},
"Abreve": {GlyphName: "Abreve", Wx: 600.000000, Wy: 0.000000},
"Acircumflex": {GlyphName: "Acircumflex", Wx: 600.000000, Wy: 0.000000},
"Adieresis": {GlyphName: "Adieresis", Wx: 600.000000, Wy: 0.000000},
"Agrave": {GlyphName: "Agrave", Wx: 600.000000, Wy: 0.000000},
"Amacron": {GlyphName: "Amacron", Wx: 600.000000, Wy: 0.000000},
"Aogonek": {GlyphName: "Aogonek", Wx: 600.000000, Wy: 0.000000},
"Aring": {GlyphName: "Aring", Wx: 600.000000, Wy: 0.000000},
"Atilde": {GlyphName: "Atilde", Wx: 600.000000, Wy: 0.000000},
"B": {GlyphName: "B", Wx: 600.000000, Wy: 0.000000},
"C": {GlyphName: "C", Wx: 600.000000, Wy: 0.000000},
"Cacute": {GlyphName: "Cacute", Wx: 600.000000, Wy: 0.000000},
"Ccaron": {GlyphName: "Ccaron", Wx: 600.000000, Wy: 0.000000},
"Ccedilla": {GlyphName: "Ccedilla", Wx: 600.000000, Wy: 0.000000},
"D": {GlyphName: "D", Wx: 600.000000, Wy: 0.000000},
"Dcaron": {GlyphName: "Dcaron", Wx: 600.000000, Wy: 0.000000},
"Dcroat": {GlyphName: "Dcroat", Wx: 600.000000, Wy: 0.000000},
"Delta": {GlyphName: "Delta", Wx: 600.000000, Wy: 0.000000},
"E": {GlyphName: "E", Wx: 600.000000, Wy: 0.000000},
"Eacute": {GlyphName: "Eacute", Wx: 600.000000, Wy: 0.000000},
"Ecaron": {GlyphName: "Ecaron", Wx: 600.000000, Wy: 0.000000},
"Ecircumflex": {GlyphName: "Ecircumflex", Wx: 600.000000, Wy: 0.000000},
"Edieresis": {GlyphName: "Edieresis", Wx: 600.000000, Wy: 0.000000},
"Edotaccent": {GlyphName: "Edotaccent", Wx: 600.000000, Wy: 0.000000},
"Egrave": {GlyphName: "Egrave", Wx: 600.000000, Wy: 0.000000},
"Emacron": {GlyphName: "Emacron", Wx: 600.000000, Wy: 0.000000},
"Eogonek": {GlyphName: "Eogonek", Wx: 600.000000, Wy: 0.000000},
"Eth": {GlyphName: "Eth", Wx: 600.000000, Wy: 0.000000},
"Euro": {GlyphName: "Euro", Wx: 600.000000, Wy: 0.000000},
"F": {GlyphName: "F", Wx: 600.000000, Wy: 0.000000},
"G": {GlyphName: "G", Wx: 600.000000, Wy: 0.000000},
"Gbreve": {GlyphName: "Gbreve", Wx: 600.000000, Wy: 0.000000},
"Gcommaaccent": {GlyphName: "Gcommaaccent", Wx: 600.000000, Wy: 0.000000},
"H": {GlyphName: "H", Wx: 600.000000, Wy: 0.000000},
"I": {GlyphName: "I", Wx: 600.000000, Wy: 0.000000},
"Iacute": {GlyphName: "Iacute", Wx: 600.000000, Wy: 0.000000},
"Icircumflex": {GlyphName: "Icircumflex", Wx: 600.000000, Wy: 0.000000},
"Idieresis": {GlyphName: "Idieresis", Wx: 600.000000, Wy: 0.000000},
"Idotaccent": {GlyphName: "Idotaccent", Wx: 600.000000, Wy: 0.000000},
"Igrave": {GlyphName: "Igrave", Wx: 600.000000, Wy: 0.000000},
"Imacron": {GlyphName: "Imacron", Wx: 600.000000, Wy: 0.000000},
"Iogonek": {GlyphName: "Iogonek", Wx: 600.000000, Wy: 0.000000},
"J": {GlyphName: "J", Wx: 600.000000, Wy: 0.000000},
"K": {GlyphName: "K", Wx: 600.000000, Wy: 0.000000},
"Kcommaaccent": {GlyphName: "Kcommaaccent", Wx: 600.000000, Wy: 0.000000},
"L": {GlyphName: "L", Wx: 600.000000, Wy: 0.000000},
"Lacute": {GlyphName: "Lacute", Wx: 600.000000, Wy: 0.000000},
"Lcaron": {GlyphName: "Lcaron", Wx: 600.000000, Wy: 0.000000},
"Lcommaaccent": {GlyphName: "Lcommaaccent", Wx: 600.000000, Wy: 0.000000},
"Lslash": {GlyphName: "Lslash", Wx: 600.000000, Wy: 0.000000},
"M": {GlyphName: "M", Wx: 600.000000, Wy: 0.000000},
"N": {GlyphName: "N", Wx: 600.000000, Wy: 0.000000},
"Nacute": {GlyphName: "Nacute", Wx: 600.000000, Wy: 0.000000},
"Ncaron": {GlyphName: "Ncaron", Wx: 600.000000, Wy: 0.000000},
"Ncommaaccent": {GlyphName: "Ncommaaccent", Wx: 600.000000, Wy: 0.000000},
"Ntilde": {GlyphName: "Ntilde", Wx: 600.000000, Wy: 0.000000},
"O": {GlyphName: "O", Wx: 600.000000, Wy: 0.000000},
"OE": {GlyphName: "OE", Wx: 600.000000, Wy: 0.000000},
"Oacute": {GlyphName: "Oacute", Wx: 600.000000, Wy: 0.000000},
"Ocircumflex": {GlyphName: "Ocircumflex", Wx: 600.000000, Wy: 0.000000},
"Odieresis": {GlyphName: "Odieresis", Wx: 600.000000, Wy: 0.000000},
"Ograve": {GlyphName: "Ograve", Wx: 600.000000, Wy: 0.000000},
"Ohungarumlaut": {GlyphName: "Ohungarumlaut", Wx: 600.000000, Wy: 0.000000},
"Omacron": {GlyphName: "Omacron", Wx: 600.000000, Wy: 0.000000},
"Oslash": {GlyphName: "Oslash", Wx: 600.000000, Wy: 0.000000},
"Otilde": {GlyphName: "Otilde", Wx: 600.000000, Wy: 0.000000},
"P": {GlyphName: "P", Wx: 600.000000, Wy: 0.000000},
"Q": {GlyphName: "Q", Wx: 600.000000, Wy: 0.000000},
"R": {GlyphName: "R", Wx: 600.000000, Wy: 0.000000},
"Racute": {GlyphName: "Racute", Wx: 600.000000, Wy: 0.000000},
"Rcaron": {GlyphName: "Rcaron", Wx: 600.000000, Wy: 0.000000},
"Rcommaaccent": {GlyphName: "Rcommaaccent", Wx: 600.000000, Wy: 0.000000},
"S": {GlyphName: "S", Wx: 600.000000, Wy: 0.000000},
"Sacute": {GlyphName: "Sacute", Wx: 600.000000, Wy: 0.000000},
"Scaron": {GlyphName: "Scaron", Wx: 600.000000, Wy: 0.000000},
"Scedilla": {GlyphName: "Scedilla", Wx: 600.000000, Wy: 0.000000},
"Scommaaccent": {GlyphName: "Scommaaccent", Wx: 600.000000, Wy: 0.000000},
"T": {GlyphName: "T", Wx: 600.000000, Wy: 0.000000},
"Tcaron": {GlyphName: "Tcaron", Wx: 600.000000, Wy: 0.000000},
"Tcommaaccent": {GlyphName: "Tcommaaccent", Wx: 600.000000, Wy: 0.000000},
"Thorn": {GlyphName: "Thorn", Wx: 600.000000, Wy: 0.000000},
"U": {GlyphName: "U", Wx: 600.000000, Wy: 0.000000},
"Uacute": {GlyphName: "Uacute", Wx: 600.000000, Wy: 0.000000},
"Ucircumflex": {GlyphName: "Ucircumflex", Wx: 600.000000, Wy: 0.000000},
"Udieresis": {GlyphName: "Udieresis", Wx: 600.000000, Wy: 0.000000},
"Ugrave": {GlyphName: "Ugrave", Wx: 600.000000, Wy: 0.000000},
"Uhungarumlaut": {GlyphName: "Uhungarumlaut", Wx: 600.000000, Wy: 0.000000},
"Umacron": {GlyphName: "Umacron", Wx: 600.000000, Wy: 0.000000},
"Uogonek": {GlyphName: "Uogonek", Wx: 600.000000, Wy: 0.000000},
"Uring": {GlyphName: "Uring", Wx: 600.000000, Wy: 0.000000},
"V": {GlyphName: "V", Wx: 600.000000, Wy: 0.000000},
"W": {GlyphName: "W", Wx: 600.000000, Wy: 0.000000},
"X": {GlyphName: "X", Wx: 600.000000, Wy: 0.000000},
"Y": {GlyphName: "Y", Wx: 600.000000, Wy: 0.000000},
"Yacute": {GlyphName: "Yacute", Wx: 600.000000, Wy: 0.000000},
"Ydieresis": {GlyphName: "Ydieresis", Wx: 600.000000, Wy: 0.000000},
"Z": {GlyphName: "Z", Wx: 600.000000, Wy: 0.000000},
"Zacute": {GlyphName: "Zacute", Wx: 600.000000, Wy: 0.000000},
"Zcaron": {GlyphName: "Zcaron", Wx: 600.000000, Wy: 0.000000},
"Zdotaccent": {GlyphName: "Zdotaccent", Wx: 600.000000, Wy: 0.000000},
"a": {GlyphName: "a", Wx: 600.000000, Wy: 0.000000},
"aacute": {GlyphName: "aacute", Wx: 600.000000, Wy: 0.000000},
"abreve": {GlyphName: "abreve", Wx: 600.000000, Wy: 0.000000},
"acircumflex": {GlyphName: "acircumflex", Wx: 600.000000, Wy: 0.000000},
"acute": {GlyphName: "acute", Wx: 600.000000, Wy: 0.000000},
"adieresis": {GlyphName: "adieresis", Wx: 600.000000, Wy: 0.000000},
"ae": {GlyphName: "ae", Wx: 600.000000, Wy: 0.000000},
"agrave": {GlyphName: "agrave", Wx: 600.000000, Wy: 0.000000},
"amacron": {GlyphName: "amacron", Wx: 600.000000, Wy: 0.000000},
"ampersand": {GlyphName: "ampersand", Wx: 600.000000, Wy: 0.000000},
"aogonek": {GlyphName: "aogonek", Wx: 600.000000, Wy: 0.000000},
"aring": {GlyphName: "aring", Wx: 600.000000, Wy: 0.000000},
"asciicircum": {GlyphName: "asciicircum", Wx: 600.000000, Wy: 0.000000},
"asciitilde": {GlyphName: "asciitilde", Wx: 600.000000, Wy: 0.000000},
"asterisk": {GlyphName: "asterisk", Wx: 600.000000, Wy: 0.000000},
"at": {GlyphName: "at", Wx: 600.000000, Wy: 0.000000},
"atilde": {GlyphName: "atilde", Wx: 600.000000, Wy: 0.000000},
"b": {GlyphName: "b", Wx: 600.000000, Wy: 0.000000},
"backslash": {GlyphName: "backslash", Wx: 600.000000, Wy: 0.000000},
"bar": {GlyphName: "bar", Wx: 600.000000, Wy: 0.000000},
"braceleft": {GlyphName: "braceleft", Wx: 600.000000, Wy: 0.000000},
"braceright": {GlyphName: "braceright", Wx: 600.000000, Wy: 0.000000},
"bracketleft": {GlyphName: "bracketleft", Wx: 600.000000, Wy: 0.000000},
"bracketright": {GlyphName: "bracketright", Wx: 600.000000, Wy: 0.000000},
"breve": {GlyphName: "breve", Wx: 600.000000, Wy: 0.000000},
"brokenbar": {GlyphName: "brokenbar", Wx: 600.000000, Wy: 0.000000},
"bullet": {GlyphName: "bullet", Wx: 600.000000, Wy: 0.000000},
"c": {GlyphName: "c", Wx: 600.000000, Wy: 0.000000},
"cacute": {GlyphName: "cacute", Wx: 600.000000, Wy: 0.000000},
"caron": {GlyphName: "caron", Wx: 600.000000, Wy: 0.000000},
"ccaron": {GlyphName: "ccaron", Wx: 600.000000, Wy: 0.000000},
"ccedilla": {GlyphName: "ccedilla", Wx: 600.000000, Wy: 0.000000},
"cedilla": {GlyphName: "cedilla", Wx: 600.000000, Wy: 0.000000},
"cent": {GlyphName: "cent", Wx: 600.000000, Wy: 0.000000},
"circumflex": {GlyphName: "circumflex", Wx: 600.000000, Wy: 0.000000},
"colon": {GlyphName: "colon", Wx: 600.000000, Wy: 0.000000},
"comma": {GlyphName: "comma", Wx: 600.000000, Wy: 0.000000},
"commaaccent": {GlyphName: "commaaccent", Wx: 600.000000, Wy: 0.000000},
"copyright": {GlyphName: "copyright", Wx: 600.000000, Wy: 0.000000},
"currency": {GlyphName: "currency", Wx: 600.000000, Wy: 0.000000},
"d": {GlyphName: "d", Wx: 600.000000, Wy: 0.000000},
"dagger": {GlyphName: "dagger", Wx: 600.000000, Wy: 0.000000},
"daggerdbl": {GlyphName: "daggerdbl", Wx: 600.000000, Wy: 0.000000},
"dcaron": {GlyphName: "dcaron", Wx: 600.000000, Wy: 0.000000},
"dcroat": {GlyphName: "dcroat", Wx: 600.000000, Wy: 0.000000},
"degree": {GlyphName: "degree", Wx: 600.000000, Wy: 0.000000},
"dieresis": {GlyphName: "dieresis", Wx: 600.000000, Wy: 0.000000},
"divide": {GlyphName: "divide", Wx: 600.000000, Wy: 0.000000},
"dollar": {GlyphName: "dollar", Wx: 600.000000, Wy: 0.000000},
"dotaccent": {GlyphName: "dotaccent", Wx: 600.000000, Wy: 0.000000},
"dotlessi": {GlyphName: "dotlessi", Wx: 600.000000, Wy: 0.000000},
"e": {GlyphName: "e", Wx: 600.000000, Wy: 0.000000},
"eacute": {GlyphName: "eacute", Wx: 600.000000, Wy: 0.000000},
"ecaron": {GlyphName: "ecaron", Wx: 600.000000, Wy: 0.000000},
"ecircumflex": {GlyphName: "ecircumflex", Wx: 600.000000, Wy: 0.000000},
"edieresis": {GlyphName: "edieresis", Wx: 600.000000, Wy: 0.000000},
"edotaccent": {GlyphName: "edotaccent", Wx: 600.000000, Wy: 0.000000},
"egrave": {GlyphName: "egrave", Wx: 600.000000, Wy: 0.000000},
"eight": {GlyphName: "eight", Wx: 600.000000, Wy: 0.000000},
"ellipsis": {GlyphName: "ellipsis", Wx: 600.000000, Wy: 0.000000},
"emacron": {GlyphName: "emacron", Wx: 600.000000, Wy: 0.000000},
"emdash": {GlyphName: "emdash", Wx: 600.000000, Wy: 0.000000},
"endash": {GlyphName: "endash", Wx: 600.000000, Wy: 0.000000},
"eogonek": {GlyphName: "eogonek", Wx: 600.000000, Wy: 0.000000},
"equal": {GlyphName: "equal", Wx: 600.000000, Wy: 0.000000},
"eth": {GlyphName: "eth", Wx: 600.000000, Wy: 0.000000},
"exclam": {GlyphName: "exclam", Wx: 600.000000, Wy: 0.000000},
"exclamdown": {GlyphName: "exclamdown", Wx: 600.000000, Wy: 0.000000},
"f": {GlyphName: "f", Wx: 600.000000, Wy: 0.000000},
"fi": {GlyphName: "fi", Wx: 600.000000, Wy: 0.000000},
"five": {GlyphName: "five", Wx: 600.000000, Wy: 0.000000},
"fl": {GlyphName: "fl", Wx: 600.000000, Wy: 0.000000},
"florin": {GlyphName: "florin", Wx: 600.000000, Wy: 0.000000},
"four": {GlyphName: "four", Wx: 600.000000, Wy: 0.000000},
"fraction": {GlyphName: "fraction", Wx: 600.000000, Wy: 0.000000},
"g": {GlyphName: "g", Wx: 600.000000, Wy: 0.000000},
"gbreve": {GlyphName: "gbreve", Wx: 600.000000, Wy: 0.000000},
"gcommaaccent": {GlyphName: "gcommaaccent", Wx: 600.000000, Wy: 0.000000},
"germandbls": {GlyphName: "germandbls", Wx: 600.000000, Wy: 0.000000},
"grave": {GlyphName: "grave", Wx: 600.000000, Wy: 0.000000},
"greater": {GlyphName: "greater", Wx: 600.000000, Wy: 0.000000},
"greaterequal": {GlyphName: "greaterequal", Wx: 600.000000, Wy: 0.000000},
"guillemotleft": {GlyphName: "guillemotleft", Wx: 600.000000, Wy: 0.000000},
"guillemotright": {GlyphName: "guillemotright", Wx: 600.000000, Wy: 0.000000},
"guilsinglleft": {GlyphName: "guilsinglleft", Wx: 600.000000, Wy: 0.000000},
"guilsinglright": {GlyphName: "guilsinglright", Wx: 600.000000, Wy: 0.000000},
"h": {GlyphName: "h", Wx: 600.000000, Wy: 0.000000},
"hungarumlaut": {GlyphName: "hungarumlaut", Wx: 600.000000, Wy: 0.000000},
"hyphen": {GlyphName: "hyphen", Wx: 600.000000, Wy: 0.000000},
"i": {GlyphName: "i", Wx: 600.000000, Wy: 0.000000},
"iacute": {GlyphName: "iacute", Wx: 600.000000, Wy: 0.000000},
"icircumflex": {GlyphName: "icircumflex", Wx: 600.000000, Wy: 0.000000},
"idieresis": {GlyphName: "idieresis", Wx: 600.000000, Wy: 0.000000},
"igrave": {GlyphName: "igrave", Wx: 600.000000, Wy: 0.000000},
"imacron": {GlyphName: "imacron", Wx: 600.000000, Wy: 0.000000},
"iogonek": {GlyphName: "iogonek", Wx: 600.000000, Wy: 0.000000},
"j": {GlyphName: "j", Wx: 600.000000, Wy: 0.000000},
"k": {GlyphName: "k", Wx: 600.000000, Wy: 0.000000},
"kcommaaccent": {GlyphName: "kcommaaccent", Wx: 600.000000, Wy: 0.000000},
"l": {GlyphName: "l", Wx: 600.000000, Wy: 0.000000},
"lacute": {GlyphName: "lacute", Wx: 600.000000, Wy: 0.000000},
"lcaron": {GlyphName: "lcaron", Wx: 600.000000, Wy: 0.000000},
"lcommaaccent": {GlyphName: "lcommaaccent", Wx: 600.000000, Wy: 0.000000},
"less": {GlyphName: "less", Wx: 600.000000, Wy: 0.000000},
"lessequal": {GlyphName: "lessequal", Wx: 600.000000, Wy: 0.000000},
"logicalnot": {GlyphName: "logicalnot", Wx: 600.000000, Wy: 0.000000},
"lozenge": {GlyphName: "lozenge", Wx: 600.000000, Wy: 0.000000},
"lslash": {GlyphName: "lslash", Wx: 600.000000, Wy: 0.000000},
"m": {GlyphName: "m", Wx: 600.000000, Wy: 0.000000},
"macron": {GlyphName: "macron", Wx: 600.000000, Wy: 0.000000},
"minus": {GlyphName: "minus", Wx: 600.000000, Wy: 0.000000},
"mu": {GlyphName: "mu", Wx: 600.000000, Wy: 0.000000},
"multiply": {GlyphName: "multiply", Wx: 600.000000, Wy: 0.000000},
"n": {GlyphName: "n", Wx: 600.000000, Wy: 0.000000},
"nacute": {GlyphName: "nacute", Wx: 600.000000, Wy: 0.000000},
"ncaron": {GlyphName: "ncaron", Wx: 600.000000, Wy: 0.000000},
"ncommaaccent": {GlyphName: "ncommaaccent", Wx: 600.000000, Wy: 0.000000},
"nine": {GlyphName: "nine", Wx: 600.000000, Wy: 0.000000},
"notequal": {GlyphName: "notequal", Wx: 600.000000, Wy: 0.000000},
"ntilde": {GlyphName: "ntilde", Wx: 600.000000, Wy: 0.000000},
"numbersign": {GlyphName: "numbersign", Wx: 600.000000, Wy: 0.000000},
"o": {GlyphName: "o", Wx: 600.000000, Wy: 0.000000},
"oacute": {GlyphName: "oacute", Wx: 600.000000, Wy: 0.000000},
"ocircumflex": {GlyphName: "ocircumflex", Wx: 600.000000, Wy: 0.000000},
"odieresis": {GlyphName: "odieresis", Wx: 600.000000, Wy: 0.000000},
"oe": {GlyphName: "oe", Wx: 600.000000, Wy: 0.000000},
"ogonek": {GlyphName: "ogonek", Wx: 600.000000, Wy: 0.000000},
"ograve": {GlyphName: "ograve", Wx: 600.000000, Wy: 0.000000},
"ohungarumlaut": {GlyphName: "ohungarumlaut", Wx: 600.000000, Wy: 0.000000},
"omacron": {GlyphName: "omacron", Wx: 600.000000, Wy: 0.000000},
"one": {GlyphName: "one", Wx: 600.000000, Wy: 0.000000},
"onehalf": {GlyphName: "onehalf", Wx: 600.000000, Wy: 0.000000},
"onequarter": {GlyphName: "onequarter", Wx: 600.000000, Wy: 0.000000},
"onesuperior": {GlyphName: "onesuperior", Wx: 600.000000, Wy: 0.000000},
"ordfeminine": {GlyphName: "ordfeminine", Wx: 600.000000, Wy: 0.000000},
"ordmasculine": {GlyphName: "ordmasculine", Wx: 600.000000, Wy: 0.000000},
"oslash": {GlyphName: "oslash", Wx: 600.000000, Wy: 0.000000},
"otilde": {GlyphName: "otilde", Wx: 600.000000, Wy: 0.000000},
"p": {GlyphName: "p", Wx: 600.000000, Wy: 0.000000},
"paragraph": {GlyphName: "paragraph", Wx: 600.000000, Wy: 0.000000},
"parenleft": {GlyphName: "parenleft", Wx: 600.000000, Wy: 0.000000},
"parenright": {GlyphName: "parenright", Wx: 600.000000, Wy: 0.000000},
"partialdiff": {GlyphName: "partialdiff", Wx: 600.000000, Wy: 0.000000},
"percent": {GlyphName: "percent", Wx: 600.000000, Wy: 0.000000},
"period": {GlyphName: "period", Wx: 600.000000, Wy: 0.000000},
"periodcentered": {GlyphName: "periodcentered", Wx: 600.000000, Wy: 0.000000},
"perthousand": {GlyphName: "perthousand", Wx: 600.000000, Wy: 0.000000},
"plus": {GlyphName: "plus", Wx: 600.000000, Wy: 0.000000},
"plusminus": {GlyphName: "plusminus", Wx: 600.000000, Wy: 0.000000},
"q": {GlyphName: "q", Wx: 600.000000, Wy: 0.000000},
"question": {GlyphName: "question", Wx: 600.000000, Wy: 0.000000},
"questiondown": {GlyphName: "questiondown", Wx: 600.000000, Wy: 0.000000},
"quotedbl": {GlyphName: "quotedbl", Wx: 600.000000, Wy: 0.000000},
"quotedblbase": {GlyphName: "quotedblbase", Wx: 600.000000, Wy: 0.000000},
"quotedblleft": {GlyphName: "quotedblleft", Wx: 600.000000, Wy: 0.000000},
"quotedblright": {GlyphName: "quotedblright", Wx: 600.000000, Wy: 0.000000},
"quoteleft": {GlyphName: "quoteleft", Wx: 600.000000, Wy: 0.000000},
"quoteright": {GlyphName: "quoteright", Wx: 600.000000, Wy: 0.000000},
"quotesinglbase": {GlyphName: "quotesinglbase", Wx: 600.000000, Wy: 0.000000},
"quotesingle": {GlyphName: "quotesingle", Wx: 600.000000, Wy: 0.000000},
"r": {GlyphName: "r", Wx: 600.000000, Wy: 0.000000},
"racute": {GlyphName: "racute", Wx: 600.000000, Wy: 0.000000},
"radical": {GlyphName: "radical", Wx: 600.000000, Wy: 0.000000},
"rcaron": {GlyphName: "rcaron", Wx: 600.000000, Wy: 0.000000},
"rcommaaccent": {GlyphName: "rcommaaccent", Wx: 600.000000, Wy: 0.000000},
"registered": {GlyphName: "registered", Wx: 600.000000, Wy: 0.000000},
"ring": {GlyphName: "ring", Wx: 600.000000, Wy: 0.000000},
"s": {GlyphName: "s", Wx: 600.000000, Wy: 0.000000},
"sacute": {GlyphName: "sacute", Wx: 600.000000, Wy: 0.000000},
"scaron": {GlyphName: "scaron", Wx: 600.000000, Wy: 0.000000},
"scedilla": {GlyphName: "scedilla", Wx: 600.000000, Wy: 0.000000},
"scommaaccent": {GlyphName: "scommaaccent", Wx: 600.000000, Wy: 0.000000},
"section": {GlyphName: "section", Wx: 600.000000, Wy: 0.000000},
"semicolon": {GlyphName: "semicolon", Wx: 600.000000, Wy: 0.000000},
"seven": {GlyphName: "seven", Wx: 600.000000, Wy: 0.000000},
"six": {GlyphName: "six", Wx: 600.000000, Wy: 0.000000},
"slash": {GlyphName: "slash", Wx: 600.000000, Wy: 0.000000},
"space": {GlyphName: "space", Wx: 600.000000, Wy: 0.000000},
"sterling": {GlyphName: "sterling", Wx: 600.000000, Wy: 0.000000},
"summation": {GlyphName: "summation", Wx: 600.000000, Wy: 0.000000},
"t": {GlyphName: "t", Wx: 600.000000, Wy: 0.000000},
"tcaron": {GlyphName: "tcaron", Wx: 600.000000, Wy: 0.000000},
"tcommaaccent": {GlyphName: "tcommaaccent", Wx: 600.000000, Wy: 0.000000},
"thorn": {GlyphName: "thorn", Wx: 600.000000, Wy: 0.000000},
"three": {GlyphName: "three", Wx: 600.000000, Wy: 0.000000},
"threequarters": {GlyphName: "threequarters", Wx: 600.000000, Wy: 0.000000},
"threesuperior": {GlyphName: "threesuperior", Wx: 600.000000, Wy: 0.000000},
"tilde": {GlyphName: "tilde", Wx: 600.000000, Wy: 0.000000},
"trademark": {GlyphName: "trademark", Wx: 600.000000, Wy: 0.000000},
"two": {GlyphName: "two", Wx: 600.000000, Wy: 0.000000},
"twosuperior": {GlyphName: "twosuperior", Wx: 600.000000, Wy: 0.000000},
"u": {GlyphName: "u", Wx: 600.000000, Wy: 0.000000},
"uacute": {GlyphName: "uacute", Wx: 600.000000, Wy: 0.000000},
"ucircumflex": {GlyphName: "ucircumflex", Wx: 600.000000, Wy: 0.000000},
"udieresis": {GlyphName: "udieresis", Wx: 600.000000, Wy: 0.000000},
"ugrave": {GlyphName: "ugrave", Wx: 600.000000, Wy: 0.000000},
"uhungarumlaut": {GlyphName: "uhungarumlaut", Wx: 600.000000, Wy: 0.000000},
"umacron": {GlyphName: "umacron", Wx: 600.000000, Wy: 0.000000},
"underscore": {GlyphName: "underscore", Wx: 600.000000, Wy: 0.000000},
"uogonek": {GlyphName: "uogonek", Wx: 600.000000, Wy: 0.000000},
"uring": {GlyphName: "uring", Wx: 600.000000, Wy: 0.000000},
"v": {GlyphName: "v", Wx: 600.000000, Wy: 0.000000},
"w": {GlyphName: "w", Wx: 600.000000, Wy: 0.000000},
"x": {GlyphName: "x", Wx: 600.000000, Wy: 0.000000},
"y": {GlyphName: "y", Wx: 600.000000, Wy: 0.000000},
"yacute": {GlyphName: "yacute", Wx: 600.000000, Wy: 0.000000},
"ydieresis": {GlyphName: "ydieresis", Wx: 600.000000, Wy: 0.000000},
"yen": {GlyphName: "yen", Wx: 600.000000, Wy: 0.000000},
"z": {GlyphName: "z", Wx: 600.000000, Wy: 0.000000},
"zacute": {GlyphName: "zacute", Wx: 600.000000, Wy: 0.000000},
"zcaron": {GlyphName: "zcaron", Wx: 600.000000, Wy: 0.000000},
"zdotaccent": {GlyphName: "zdotaccent", Wx: 600.000000, Wy: 0.000000},
"zero": {GlyphName: "zero", Wx: 600.000000, Wy: 0.000000},
}

View File

@@ -0,0 +1,362 @@
package fonts
import (
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model/textencoding"
)
// Font Courier-Oblique. Implements Font interface.
// This is a built-in font and it is assumed that every reader has access to it.
type fontCourierOblique struct {
encoder textencoding.TextEncoder
}
func NewFontCourierOblique() fontCourierOblique {
font := fontCourierOblique{}
font.encoder = textencoding.NewWinAnsiTextEncoder() // Default
return font
}
func (font fontCourierOblique) SetEncoder(encoder textencoding.TextEncoder) {
font.encoder = encoder
}
func (font fontCourierOblique) GetGlyphCharMetrics(glyph string) (CharMetrics, bool) {
metrics, has := courierObliqueCharMetrics[glyph]
if !has {
return metrics, false
}
return metrics, true
}
func (font fontCourierOblique) ToPdfObject() core.PdfObject {
obj := &core.PdfIndirectObject{}
fontDict := core.MakeDict()
fontDict.Set("Type", core.MakeName("Font"))
fontDict.Set("Subtype", core.MakeName("Type1"))
fontDict.Set("BaseFont", core.MakeName("Courier-Oblique"))
fontDict.Set("Encoding", font.encoder.ToPdfObject())
obj.PdfObject = fontDict
return obj
}
var courierObliqueCharMetrics map[string]CharMetrics = map[string]CharMetrics{
"A": {GlyphName: "A", Wx: 600.000000, Wy: 0.000000},
"AE": {GlyphName: "AE", Wx: 600.000000, Wy: 0.000000},
"Aacute": {GlyphName: "Aacute", Wx: 600.000000, Wy: 0.000000},
"Abreve": {GlyphName: "Abreve", Wx: 600.000000, Wy: 0.000000},
"Acircumflex": {GlyphName: "Acircumflex", Wx: 600.000000, Wy: 0.000000},
"Adieresis": {GlyphName: "Adieresis", Wx: 600.000000, Wy: 0.000000},
"Agrave": {GlyphName: "Agrave", Wx: 600.000000, Wy: 0.000000},
"Amacron": {GlyphName: "Amacron", Wx: 600.000000, Wy: 0.000000},
"Aogonek": {GlyphName: "Aogonek", Wx: 600.000000, Wy: 0.000000},
"Aring": {GlyphName: "Aring", Wx: 600.000000, Wy: 0.000000},
"Atilde": {GlyphName: "Atilde", Wx: 600.000000, Wy: 0.000000},
"B": {GlyphName: "B", Wx: 600.000000, Wy: 0.000000},
"C": {GlyphName: "C", Wx: 600.000000, Wy: 0.000000},
"Cacute": {GlyphName: "Cacute", Wx: 600.000000, Wy: 0.000000},
"Ccaron": {GlyphName: "Ccaron", Wx: 600.000000, Wy: 0.000000},
"Ccedilla": {GlyphName: "Ccedilla", Wx: 600.000000, Wy: 0.000000},
"D": {GlyphName: "D", Wx: 600.000000, Wy: 0.000000},
"Dcaron": {GlyphName: "Dcaron", Wx: 600.000000, Wy: 0.000000},
"Dcroat": {GlyphName: "Dcroat", Wx: 600.000000, Wy: 0.000000},
"Delta": {GlyphName: "Delta", Wx: 600.000000, Wy: 0.000000},
"E": {GlyphName: "E", Wx: 600.000000, Wy: 0.000000},
"Eacute": {GlyphName: "Eacute", Wx: 600.000000, Wy: 0.000000},
"Ecaron": {GlyphName: "Ecaron", Wx: 600.000000, Wy: 0.000000},
"Ecircumflex": {GlyphName: "Ecircumflex", Wx: 600.000000, Wy: 0.000000},
"Edieresis": {GlyphName: "Edieresis", Wx: 600.000000, Wy: 0.000000},
"Edotaccent": {GlyphName: "Edotaccent", Wx: 600.000000, Wy: 0.000000},
"Egrave": {GlyphName: "Egrave", Wx: 600.000000, Wy: 0.000000},
"Emacron": {GlyphName: "Emacron", Wx: 600.000000, Wy: 0.000000},
"Eogonek": {GlyphName: "Eogonek", Wx: 600.000000, Wy: 0.000000},
"Eth": {GlyphName: "Eth", Wx: 600.000000, Wy: 0.000000},
"Euro": {GlyphName: "Euro", Wx: 600.000000, Wy: 0.000000},
"F": {GlyphName: "F", Wx: 600.000000, Wy: 0.000000},
"G": {GlyphName: "G", Wx: 600.000000, Wy: 0.000000},
"Gbreve": {GlyphName: "Gbreve", Wx: 600.000000, Wy: 0.000000},
"Gcommaaccent": {GlyphName: "Gcommaaccent", Wx: 600.000000, Wy: 0.000000},
"H": {GlyphName: "H", Wx: 600.000000, Wy: 0.000000},
"I": {GlyphName: "I", Wx: 600.000000, Wy: 0.000000},
"Iacute": {GlyphName: "Iacute", Wx: 600.000000, Wy: 0.000000},
"Icircumflex": {GlyphName: "Icircumflex", Wx: 600.000000, Wy: 0.000000},
"Idieresis": {GlyphName: "Idieresis", Wx: 600.000000, Wy: 0.000000},
"Idotaccent": {GlyphName: "Idotaccent", Wx: 600.000000, Wy: 0.000000},
"Igrave": {GlyphName: "Igrave", Wx: 600.000000, Wy: 0.000000},
"Imacron": {GlyphName: "Imacron", Wx: 600.000000, Wy: 0.000000},
"Iogonek": {GlyphName: "Iogonek", Wx: 600.000000, Wy: 0.000000},
"J": {GlyphName: "J", Wx: 600.000000, Wy: 0.000000},
"K": {GlyphName: "K", Wx: 600.000000, Wy: 0.000000},
"Kcommaaccent": {GlyphName: "Kcommaaccent", Wx: 600.000000, Wy: 0.000000},
"L": {GlyphName: "L", Wx: 600.000000, Wy: 0.000000},
"Lacute": {GlyphName: "Lacute", Wx: 600.000000, Wy: 0.000000},
"Lcaron": {GlyphName: "Lcaron", Wx: 600.000000, Wy: 0.000000},
"Lcommaaccent": {GlyphName: "Lcommaaccent", Wx: 600.000000, Wy: 0.000000},
"Lslash": {GlyphName: "Lslash", Wx: 600.000000, Wy: 0.000000},
"M": {GlyphName: "M", Wx: 600.000000, Wy: 0.000000},
"N": {GlyphName: "N", Wx: 600.000000, Wy: 0.000000},
"Nacute": {GlyphName: "Nacute", Wx: 600.000000, Wy: 0.000000},
"Ncaron": {GlyphName: "Ncaron", Wx: 600.000000, Wy: 0.000000},
"Ncommaaccent": {GlyphName: "Ncommaaccent", Wx: 600.000000, Wy: 0.000000},
"Ntilde": {GlyphName: "Ntilde", Wx: 600.000000, Wy: 0.000000},
"O": {GlyphName: "O", Wx: 600.000000, Wy: 0.000000},
"OE": {GlyphName: "OE", Wx: 600.000000, Wy: 0.000000},
"Oacute": {GlyphName: "Oacute", Wx: 600.000000, Wy: 0.000000},
"Ocircumflex": {GlyphName: "Ocircumflex", Wx: 600.000000, Wy: 0.000000},
"Odieresis": {GlyphName: "Odieresis", Wx: 600.000000, Wy: 0.000000},
"Ograve": {GlyphName: "Ograve", Wx: 600.000000, Wy: 0.000000},
"Ohungarumlaut": {GlyphName: "Ohungarumlaut", Wx: 600.000000, Wy: 0.000000},
"Omacron": {GlyphName: "Omacron", Wx: 600.000000, Wy: 0.000000},
"Oslash": {GlyphName: "Oslash", Wx: 600.000000, Wy: 0.000000},
"Otilde": {GlyphName: "Otilde", Wx: 600.000000, Wy: 0.000000},
"P": {GlyphName: "P", Wx: 600.000000, Wy: 0.000000},
"Q": {GlyphName: "Q", Wx: 600.000000, Wy: 0.000000},
"R": {GlyphName: "R", Wx: 600.000000, Wy: 0.000000},
"Racute": {GlyphName: "Racute", Wx: 600.000000, Wy: 0.000000},
"Rcaron": {GlyphName: "Rcaron", Wx: 600.000000, Wy: 0.000000},
"Rcommaaccent": {GlyphName: "Rcommaaccent", Wx: 600.000000, Wy: 0.000000},
"S": {GlyphName: "S", Wx: 600.000000, Wy: 0.000000},
"Sacute": {GlyphName: "Sacute", Wx: 600.000000, Wy: 0.000000},
"Scaron": {GlyphName: "Scaron", Wx: 600.000000, Wy: 0.000000},
"Scedilla": {GlyphName: "Scedilla", Wx: 600.000000, Wy: 0.000000},
"Scommaaccent": {GlyphName: "Scommaaccent", Wx: 600.000000, Wy: 0.000000},
"T": {GlyphName: "T", Wx: 600.000000, Wy: 0.000000},
"Tcaron": {GlyphName: "Tcaron", Wx: 600.000000, Wy: 0.000000},
"Tcommaaccent": {GlyphName: "Tcommaaccent", Wx: 600.000000, Wy: 0.000000},
"Thorn": {GlyphName: "Thorn", Wx: 600.000000, Wy: 0.000000},
"U": {GlyphName: "U", Wx: 600.000000, Wy: 0.000000},
"Uacute": {GlyphName: "Uacute", Wx: 600.000000, Wy: 0.000000},
"Ucircumflex": {GlyphName: "Ucircumflex", Wx: 600.000000, Wy: 0.000000},
"Udieresis": {GlyphName: "Udieresis", Wx: 600.000000, Wy: 0.000000},
"Ugrave": {GlyphName: "Ugrave", Wx: 600.000000, Wy: 0.000000},
"Uhungarumlaut": {GlyphName: "Uhungarumlaut", Wx: 600.000000, Wy: 0.000000},
"Umacron": {GlyphName: "Umacron", Wx: 600.000000, Wy: 0.000000},
"Uogonek": {GlyphName: "Uogonek", Wx: 600.000000, Wy: 0.000000},
"Uring": {GlyphName: "Uring", Wx: 600.000000, Wy: 0.000000},
"V": {GlyphName: "V", Wx: 600.000000, Wy: 0.000000},
"W": {GlyphName: "W", Wx: 600.000000, Wy: 0.000000},
"X": {GlyphName: "X", Wx: 600.000000, Wy: 0.000000},
"Y": {GlyphName: "Y", Wx: 600.000000, Wy: 0.000000},
"Yacute": {GlyphName: "Yacute", Wx: 600.000000, Wy: 0.000000},
"Ydieresis": {GlyphName: "Ydieresis", Wx: 600.000000, Wy: 0.000000},
"Z": {GlyphName: "Z", Wx: 600.000000, Wy: 0.000000},
"Zacute": {GlyphName: "Zacute", Wx: 600.000000, Wy: 0.000000},
"Zcaron": {GlyphName: "Zcaron", Wx: 600.000000, Wy: 0.000000},
"Zdotaccent": {GlyphName: "Zdotaccent", Wx: 600.000000, Wy: 0.000000},
"a": {GlyphName: "a", Wx: 600.000000, Wy: 0.000000},
"aacute": {GlyphName: "aacute", Wx: 600.000000, Wy: 0.000000},
"abreve": {GlyphName: "abreve", Wx: 600.000000, Wy: 0.000000},
"acircumflex": {GlyphName: "acircumflex", Wx: 600.000000, Wy: 0.000000},
"acute": {GlyphName: "acute", Wx: 600.000000, Wy: 0.000000},
"adieresis": {GlyphName: "adieresis", Wx: 600.000000, Wy: 0.000000},
"ae": {GlyphName: "ae", Wx: 600.000000, Wy: 0.000000},
"agrave": {GlyphName: "agrave", Wx: 600.000000, Wy: 0.000000},
"amacron": {GlyphName: "amacron", Wx: 600.000000, Wy: 0.000000},
"ampersand": {GlyphName: "ampersand", Wx: 600.000000, Wy: 0.000000},
"aogonek": {GlyphName: "aogonek", Wx: 600.000000, Wy: 0.000000},
"aring": {GlyphName: "aring", Wx: 600.000000, Wy: 0.000000},
"asciicircum": {GlyphName: "asciicircum", Wx: 600.000000, Wy: 0.000000},
"asciitilde": {GlyphName: "asciitilde", Wx: 600.000000, Wy: 0.000000},
"asterisk": {GlyphName: "asterisk", Wx: 600.000000, Wy: 0.000000},
"at": {GlyphName: "at", Wx: 600.000000, Wy: 0.000000},
"atilde": {GlyphName: "atilde", Wx: 600.000000, Wy: 0.000000},
"b": {GlyphName: "b", Wx: 600.000000, Wy: 0.000000},
"backslash": {GlyphName: "backslash", Wx: 600.000000, Wy: 0.000000},
"bar": {GlyphName: "bar", Wx: 600.000000, Wy: 0.000000},
"braceleft": {GlyphName: "braceleft", Wx: 600.000000, Wy: 0.000000},
"braceright": {GlyphName: "braceright", Wx: 600.000000, Wy: 0.000000},
"bracketleft": {GlyphName: "bracketleft", Wx: 600.000000, Wy: 0.000000},
"bracketright": {GlyphName: "bracketright", Wx: 600.000000, Wy: 0.000000},
"breve": {GlyphName: "breve", Wx: 600.000000, Wy: 0.000000},
"brokenbar": {GlyphName: "brokenbar", Wx: 600.000000, Wy: 0.000000},
"bullet": {GlyphName: "bullet", Wx: 600.000000, Wy: 0.000000},
"c": {GlyphName: "c", Wx: 600.000000, Wy: 0.000000},
"cacute": {GlyphName: "cacute", Wx: 600.000000, Wy: 0.000000},
"caron": {GlyphName: "caron", Wx: 600.000000, Wy: 0.000000},
"ccaron": {GlyphName: "ccaron", Wx: 600.000000, Wy: 0.000000},
"ccedilla": {GlyphName: "ccedilla", Wx: 600.000000, Wy: 0.000000},
"cedilla": {GlyphName: "cedilla", Wx: 600.000000, Wy: 0.000000},
"cent": {GlyphName: "cent", Wx: 600.000000, Wy: 0.000000},
"circumflex": {GlyphName: "circumflex", Wx: 600.000000, Wy: 0.000000},
"colon": {GlyphName: "colon", Wx: 600.000000, Wy: 0.000000},
"comma": {GlyphName: "comma", Wx: 600.000000, Wy: 0.000000},
"commaaccent": {GlyphName: "commaaccent", Wx: 600.000000, Wy: 0.000000},
"copyright": {GlyphName: "copyright", Wx: 600.000000, Wy: 0.000000},
"currency": {GlyphName: "currency", Wx: 600.000000, Wy: 0.000000},
"d": {GlyphName: "d", Wx: 600.000000, Wy: 0.000000},
"dagger": {GlyphName: "dagger", Wx: 600.000000, Wy: 0.000000},
"daggerdbl": {GlyphName: "daggerdbl", Wx: 600.000000, Wy: 0.000000},
"dcaron": {GlyphName: "dcaron", Wx: 600.000000, Wy: 0.000000},
"dcroat": {GlyphName: "dcroat", Wx: 600.000000, Wy: 0.000000},
"degree": {GlyphName: "degree", Wx: 600.000000, Wy: 0.000000},
"dieresis": {GlyphName: "dieresis", Wx: 600.000000, Wy: 0.000000},
"divide": {GlyphName: "divide", Wx: 600.000000, Wy: 0.000000},
"dollar": {GlyphName: "dollar", Wx: 600.000000, Wy: 0.000000},
"dotaccent": {GlyphName: "dotaccent", Wx: 600.000000, Wy: 0.000000},
"dotlessi": {GlyphName: "dotlessi", Wx: 600.000000, Wy: 0.000000},
"e": {GlyphName: "e", Wx: 600.000000, Wy: 0.000000},
"eacute": {GlyphName: "eacute", Wx: 600.000000, Wy: 0.000000},
"ecaron": {GlyphName: "ecaron", Wx: 600.000000, Wy: 0.000000},
"ecircumflex": {GlyphName: "ecircumflex", Wx: 600.000000, Wy: 0.000000},
"edieresis": {GlyphName: "edieresis", Wx: 600.000000, Wy: 0.000000},
"edotaccent": {GlyphName: "edotaccent", Wx: 600.000000, Wy: 0.000000},
"egrave": {GlyphName: "egrave", Wx: 600.000000, Wy: 0.000000},
"eight": {GlyphName: "eight", Wx: 600.000000, Wy: 0.000000},
"ellipsis": {GlyphName: "ellipsis", Wx: 600.000000, Wy: 0.000000},
"emacron": {GlyphName: "emacron", Wx: 600.000000, Wy: 0.000000},
"emdash": {GlyphName: "emdash", Wx: 600.000000, Wy: 0.000000},
"endash": {GlyphName: "endash", Wx: 600.000000, Wy: 0.000000},
"eogonek": {GlyphName: "eogonek", Wx: 600.000000, Wy: 0.000000},
"equal": {GlyphName: "equal", Wx: 600.000000, Wy: 0.000000},
"eth": {GlyphName: "eth", Wx: 600.000000, Wy: 0.000000},
"exclam": {GlyphName: "exclam", Wx: 600.000000, Wy: 0.000000},
"exclamdown": {GlyphName: "exclamdown", Wx: 600.000000, Wy: 0.000000},
"f": {GlyphName: "f", Wx: 600.000000, Wy: 0.000000},
"fi": {GlyphName: "fi", Wx: 600.000000, Wy: 0.000000},
"five": {GlyphName: "five", Wx: 600.000000, Wy: 0.000000},
"fl": {GlyphName: "fl", Wx: 600.000000, Wy: 0.000000},
"florin": {GlyphName: "florin", Wx: 600.000000, Wy: 0.000000},
"four": {GlyphName: "four", Wx: 600.000000, Wy: 0.000000},
"fraction": {GlyphName: "fraction", Wx: 600.000000, Wy: 0.000000},
"g": {GlyphName: "g", Wx: 600.000000, Wy: 0.000000},
"gbreve": {GlyphName: "gbreve", Wx: 600.000000, Wy: 0.000000},
"gcommaaccent": {GlyphName: "gcommaaccent", Wx: 600.000000, Wy: 0.000000},
"germandbls": {GlyphName: "germandbls", Wx: 600.000000, Wy: 0.000000},
"grave": {GlyphName: "grave", Wx: 600.000000, Wy: 0.000000},
"greater": {GlyphName: "greater", Wx: 600.000000, Wy: 0.000000},
"greaterequal": {GlyphName: "greaterequal", Wx: 600.000000, Wy: 0.000000},
"guillemotleft": {GlyphName: "guillemotleft", Wx: 600.000000, Wy: 0.000000},
"guillemotright": {GlyphName: "guillemotright", Wx: 600.000000, Wy: 0.000000},
"guilsinglleft": {GlyphName: "guilsinglleft", Wx: 600.000000, Wy: 0.000000},
"guilsinglright": {GlyphName: "guilsinglright", Wx: 600.000000, Wy: 0.000000},
"h": {GlyphName: "h", Wx: 600.000000, Wy: 0.000000},
"hungarumlaut": {GlyphName: "hungarumlaut", Wx: 600.000000, Wy: 0.000000},
"hyphen": {GlyphName: "hyphen", Wx: 600.000000, Wy: 0.000000},
"i": {GlyphName: "i", Wx: 600.000000, Wy: 0.000000},
"iacute": {GlyphName: "iacute", Wx: 600.000000, Wy: 0.000000},
"icircumflex": {GlyphName: "icircumflex", Wx: 600.000000, Wy: 0.000000},
"idieresis": {GlyphName: "idieresis", Wx: 600.000000, Wy: 0.000000},
"igrave": {GlyphName: "igrave", Wx: 600.000000, Wy: 0.000000},
"imacron": {GlyphName: "imacron", Wx: 600.000000, Wy: 0.000000},
"iogonek": {GlyphName: "iogonek", Wx: 600.000000, Wy: 0.000000},
"j": {GlyphName: "j", Wx: 600.000000, Wy: 0.000000},
"k": {GlyphName: "k", Wx: 600.000000, Wy: 0.000000},
"kcommaaccent": {GlyphName: "kcommaaccent", Wx: 600.000000, Wy: 0.000000},
"l": {GlyphName: "l", Wx: 600.000000, Wy: 0.000000},
"lacute": {GlyphName: "lacute", Wx: 600.000000, Wy: 0.000000},
"lcaron": {GlyphName: "lcaron", Wx: 600.000000, Wy: 0.000000},
"lcommaaccent": {GlyphName: "lcommaaccent", Wx: 600.000000, Wy: 0.000000},
"less": {GlyphName: "less", Wx: 600.000000, Wy: 0.000000},
"lessequal": {GlyphName: "lessequal", Wx: 600.000000, Wy: 0.000000},
"logicalnot": {GlyphName: "logicalnot", Wx: 600.000000, Wy: 0.000000},
"lozenge": {GlyphName: "lozenge", Wx: 600.000000, Wy: 0.000000},
"lslash": {GlyphName: "lslash", Wx: 600.000000, Wy: 0.000000},
"m": {GlyphName: "m", Wx: 600.000000, Wy: 0.000000},
"macron": {GlyphName: "macron", Wx: 600.000000, Wy: 0.000000},
"minus": {GlyphName: "minus", Wx: 600.000000, Wy: 0.000000},
"mu": {GlyphName: "mu", Wx: 600.000000, Wy: 0.000000},
"multiply": {GlyphName: "multiply", Wx: 600.000000, Wy: 0.000000},
"n": {GlyphName: "n", Wx: 600.000000, Wy: 0.000000},
"nacute": {GlyphName: "nacute", Wx: 600.000000, Wy: 0.000000},
"ncaron": {GlyphName: "ncaron", Wx: 600.000000, Wy: 0.000000},
"ncommaaccent": {GlyphName: "ncommaaccent", Wx: 600.000000, Wy: 0.000000},
"nine": {GlyphName: "nine", Wx: 600.000000, Wy: 0.000000},
"notequal": {GlyphName: "notequal", Wx: 600.000000, Wy: 0.000000},
"ntilde": {GlyphName: "ntilde", Wx: 600.000000, Wy: 0.000000},
"numbersign": {GlyphName: "numbersign", Wx: 600.000000, Wy: 0.000000},
"o": {GlyphName: "o", Wx: 600.000000, Wy: 0.000000},
"oacute": {GlyphName: "oacute", Wx: 600.000000, Wy: 0.000000},
"ocircumflex": {GlyphName: "ocircumflex", Wx: 600.000000, Wy: 0.000000},
"odieresis": {GlyphName: "odieresis", Wx: 600.000000, Wy: 0.000000},
"oe": {GlyphName: "oe", Wx: 600.000000, Wy: 0.000000},
"ogonek": {GlyphName: "ogonek", Wx: 600.000000, Wy: 0.000000},
"ograve": {GlyphName: "ograve", Wx: 600.000000, Wy: 0.000000},
"ohungarumlaut": {GlyphName: "ohungarumlaut", Wx: 600.000000, Wy: 0.000000},
"omacron": {GlyphName: "omacron", Wx: 600.000000, Wy: 0.000000},
"one": {GlyphName: "one", Wx: 600.000000, Wy: 0.000000},
"onehalf": {GlyphName: "onehalf", Wx: 600.000000, Wy: 0.000000},
"onequarter": {GlyphName: "onequarter", Wx: 600.000000, Wy: 0.000000},
"onesuperior": {GlyphName: "onesuperior", Wx: 600.000000, Wy: 0.000000},
"ordfeminine": {GlyphName: "ordfeminine", Wx: 600.000000, Wy: 0.000000},
"ordmasculine": {GlyphName: "ordmasculine", Wx: 600.000000, Wy: 0.000000},
"oslash": {GlyphName: "oslash", Wx: 600.000000, Wy: 0.000000},
"otilde": {GlyphName: "otilde", Wx: 600.000000, Wy: 0.000000},
"p": {GlyphName: "p", Wx: 600.000000, Wy: 0.000000},
"paragraph": {GlyphName: "paragraph", Wx: 600.000000, Wy: 0.000000},
"parenleft": {GlyphName: "parenleft", Wx: 600.000000, Wy: 0.000000},
"parenright": {GlyphName: "parenright", Wx: 600.000000, Wy: 0.000000},
"partialdiff": {GlyphName: "partialdiff", Wx: 600.000000, Wy: 0.000000},
"percent": {GlyphName: "percent", Wx: 600.000000, Wy: 0.000000},
"period": {GlyphName: "period", Wx: 600.000000, Wy: 0.000000},
"periodcentered": {GlyphName: "periodcentered", Wx: 600.000000, Wy: 0.000000},
"perthousand": {GlyphName: "perthousand", Wx: 600.000000, Wy: 0.000000},
"plus": {GlyphName: "plus", Wx: 600.000000, Wy: 0.000000},
"plusminus": {GlyphName: "plusminus", Wx: 600.000000, Wy: 0.000000},
"q": {GlyphName: "q", Wx: 600.000000, Wy: 0.000000},
"question": {GlyphName: "question", Wx: 600.000000, Wy: 0.000000},
"questiondown": {GlyphName: "questiondown", Wx: 600.000000, Wy: 0.000000},
"quotedbl": {GlyphName: "quotedbl", Wx: 600.000000, Wy: 0.000000},
"quotedblbase": {GlyphName: "quotedblbase", Wx: 600.000000, Wy: 0.000000},
"quotedblleft": {GlyphName: "quotedblleft", Wx: 600.000000, Wy: 0.000000},
"quotedblright": {GlyphName: "quotedblright", Wx: 600.000000, Wy: 0.000000},
"quoteleft": {GlyphName: "quoteleft", Wx: 600.000000, Wy: 0.000000},
"quoteright": {GlyphName: "quoteright", Wx: 600.000000, Wy: 0.000000},
"quotesinglbase": {GlyphName: "quotesinglbase", Wx: 600.000000, Wy: 0.000000},
"quotesingle": {GlyphName: "quotesingle", Wx: 600.000000, Wy: 0.000000},
"r": {GlyphName: "r", Wx: 600.000000, Wy: 0.000000},
"racute": {GlyphName: "racute", Wx: 600.000000, Wy: 0.000000},
"radical": {GlyphName: "radical", Wx: 600.000000, Wy: 0.000000},
"rcaron": {GlyphName: "rcaron", Wx: 600.000000, Wy: 0.000000},
"rcommaaccent": {GlyphName: "rcommaaccent", Wx: 600.000000, Wy: 0.000000},
"registered": {GlyphName: "registered", Wx: 600.000000, Wy: 0.000000},
"ring": {GlyphName: "ring", Wx: 600.000000, Wy: 0.000000},
"s": {GlyphName: "s", Wx: 600.000000, Wy: 0.000000},
"sacute": {GlyphName: "sacute", Wx: 600.000000, Wy: 0.000000},
"scaron": {GlyphName: "scaron", Wx: 600.000000, Wy: 0.000000},
"scedilla": {GlyphName: "scedilla", Wx: 600.000000, Wy: 0.000000},
"scommaaccent": {GlyphName: "scommaaccent", Wx: 600.000000, Wy: 0.000000},
"section": {GlyphName: "section", Wx: 600.000000, Wy: 0.000000},
"semicolon": {GlyphName: "semicolon", Wx: 600.000000, Wy: 0.000000},
"seven": {GlyphName: "seven", Wx: 600.000000, Wy: 0.000000},
"six": {GlyphName: "six", Wx: 600.000000, Wy: 0.000000},
"slash": {GlyphName: "slash", Wx: 600.000000, Wy: 0.000000},
"space": {GlyphName: "space", Wx: 600.000000, Wy: 0.000000},
"sterling": {GlyphName: "sterling", Wx: 600.000000, Wy: 0.000000},
"summation": {GlyphName: "summation", Wx: 600.000000, Wy: 0.000000},
"t": {GlyphName: "t", Wx: 600.000000, Wy: 0.000000},
"tcaron": {GlyphName: "tcaron", Wx: 600.000000, Wy: 0.000000},
"tcommaaccent": {GlyphName: "tcommaaccent", Wx: 600.000000, Wy: 0.000000},
"thorn": {GlyphName: "thorn", Wx: 600.000000, Wy: 0.000000},
"three": {GlyphName: "three", Wx: 600.000000, Wy: 0.000000},
"threequarters": {GlyphName: "threequarters", Wx: 600.000000, Wy: 0.000000},
"threesuperior": {GlyphName: "threesuperior", Wx: 600.000000, Wy: 0.000000},
"tilde": {GlyphName: "tilde", Wx: 600.000000, Wy: 0.000000},
"trademark": {GlyphName: "trademark", Wx: 600.000000, Wy: 0.000000},
"two": {GlyphName: "two", Wx: 600.000000, Wy: 0.000000},
"twosuperior": {GlyphName: "twosuperior", Wx: 600.000000, Wy: 0.000000},
"u": {GlyphName: "u", Wx: 600.000000, Wy: 0.000000},
"uacute": {GlyphName: "uacute", Wx: 600.000000, Wy: 0.000000},
"ucircumflex": {GlyphName: "ucircumflex", Wx: 600.000000, Wy: 0.000000},
"udieresis": {GlyphName: "udieresis", Wx: 600.000000, Wy: 0.000000},
"ugrave": {GlyphName: "ugrave", Wx: 600.000000, Wy: 0.000000},
"uhungarumlaut": {GlyphName: "uhungarumlaut", Wx: 600.000000, Wy: 0.000000},
"umacron": {GlyphName: "umacron", Wx: 600.000000, Wy: 0.000000},
"underscore": {GlyphName: "underscore", Wx: 600.000000, Wy: 0.000000},
"uogonek": {GlyphName: "uogonek", Wx: 600.000000, Wy: 0.000000},
"uring": {GlyphName: "uring", Wx: 600.000000, Wy: 0.000000},
"v": {GlyphName: "v", Wx: 600.000000, Wy: 0.000000},
"w": {GlyphName: "w", Wx: 600.000000, Wy: 0.000000},
"x": {GlyphName: "x", Wx: 600.000000, Wy: 0.000000},
"y": {GlyphName: "y", Wx: 600.000000, Wy: 0.000000},
"yacute": {GlyphName: "yacute", Wx: 600.000000, Wy: 0.000000},
"ydieresis": {GlyphName: "ydieresis", Wx: 600.000000, Wy: 0.000000},
"yen": {GlyphName: "yen", Wx: 600.000000, Wy: 0.000000},
"z": {GlyphName: "z", Wx: 600.000000, Wy: 0.000000},
"zacute": {GlyphName: "zacute", Wx: 600.000000, Wy: 0.000000},
"zcaron": {GlyphName: "zcaron", Wx: 600.000000, Wy: 0.000000},
"zdotaccent": {GlyphName: "zdotaccent", Wx: 600.000000, Wy: 0.000000},
"zero": {GlyphName: "zero", Wx: 600.000000, Wy: 0.000000},
}

View File

@@ -0,0 +1,18 @@
package fonts
import (
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model/textencoding"
)
type Font interface {
SetEncoder(encoder textencoding.TextEncoder)
GetGlyphCharMetrics(glyph string) (CharMetrics, bool)
ToPdfObject() core.PdfObject
}
type CharMetrics struct {
GlyphName string
Wx float64
Wy float64
}

View File

@@ -0,0 +1,362 @@
package fonts
import (
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model/textencoding"
)
// Font Helvetica. Implements Font interface.
// This is a built-in font and it is assumed that every reader has access to it.
type fontHelvetica struct {
encoder textencoding.TextEncoder
}
func NewFontHelvetica() fontHelvetica {
font := fontHelvetica{}
font.encoder = textencoding.NewWinAnsiTextEncoder() // Default
return font
}
func (font fontHelvetica) SetEncoder(encoder textencoding.TextEncoder) {
font.encoder = encoder
}
func (font fontHelvetica) GetGlyphCharMetrics(glyph string) (CharMetrics, bool) {
metrics, has := helveticaCharMetrics[glyph]
if !has {
return metrics, false
}
return metrics, true
}
func (font fontHelvetica) ToPdfObject() core.PdfObject {
obj := &core.PdfIndirectObject{}
fontDict := core.MakeDict()
fontDict.Set("Type", core.MakeName("Font"))
fontDict.Set("Subtype", core.MakeName("Type1"))
fontDict.Set("BaseFont", core.MakeName("Helvetica"))
fontDict.Set("Encoding", font.encoder.ToPdfObject())
obj.PdfObject = fontDict
return obj
}
var helveticaCharMetrics map[string]CharMetrics = map[string]CharMetrics{
"A": {GlyphName: "A", Wx: 667.000000, Wy: 0.000000},
"AE": {GlyphName: "AE", Wx: 1000.000000, Wy: 0.000000},
"Aacute": {GlyphName: "Aacute", Wx: 667.000000, Wy: 0.000000},
"Abreve": {GlyphName: "Abreve", Wx: 667.000000, Wy: 0.000000},
"Acircumflex": {GlyphName: "Acircumflex", Wx: 667.000000, Wy: 0.000000},
"Adieresis": {GlyphName: "Adieresis", Wx: 667.000000, Wy: 0.000000},
"Agrave": {GlyphName: "Agrave", Wx: 667.000000, Wy: 0.000000},
"Amacron": {GlyphName: "Amacron", Wx: 667.000000, Wy: 0.000000},
"Aogonek": {GlyphName: "Aogonek", Wx: 667.000000, Wy: 0.000000},
"Aring": {GlyphName: "Aring", Wx: 667.000000, Wy: 0.000000},
"Atilde": {GlyphName: "Atilde", Wx: 667.000000, Wy: 0.000000},
"B": {GlyphName: "B", Wx: 667.000000, Wy: 0.000000},
"C": {GlyphName: "C", Wx: 722.000000, Wy: 0.000000},
"Cacute": {GlyphName: "Cacute", Wx: 722.000000, Wy: 0.000000},
"Ccaron": {GlyphName: "Ccaron", Wx: 722.000000, Wy: 0.000000},
"Ccedilla": {GlyphName: "Ccedilla", Wx: 722.000000, Wy: 0.000000},
"D": {GlyphName: "D", Wx: 722.000000, Wy: 0.000000},
"Dcaron": {GlyphName: "Dcaron", Wx: 722.000000, Wy: 0.000000},
"Dcroat": {GlyphName: "Dcroat", Wx: 722.000000, Wy: 0.000000},
"Delta": {GlyphName: "Delta", Wx: 612.000000, Wy: 0.000000},
"E": {GlyphName: "E", Wx: 667.000000, Wy: 0.000000},
"Eacute": {GlyphName: "Eacute", Wx: 667.000000, Wy: 0.000000},
"Ecaron": {GlyphName: "Ecaron", Wx: 667.000000, Wy: 0.000000},
"Ecircumflex": {GlyphName: "Ecircumflex", Wx: 667.000000, Wy: 0.000000},
"Edieresis": {GlyphName: "Edieresis", Wx: 667.000000, Wy: 0.000000},
"Edotaccent": {GlyphName: "Edotaccent", Wx: 667.000000, Wy: 0.000000},
"Egrave": {GlyphName: "Egrave", Wx: 667.000000, Wy: 0.000000},
"Emacron": {GlyphName: "Emacron", Wx: 667.000000, Wy: 0.000000},
"Eogonek": {GlyphName: "Eogonek", Wx: 667.000000, Wy: 0.000000},
"Eth": {GlyphName: "Eth", Wx: 722.000000, Wy: 0.000000},
"Euro": {GlyphName: "Euro", Wx: 556.000000, Wy: 0.000000},
"F": {GlyphName: "F", Wx: 611.000000, Wy: 0.000000},
"G": {GlyphName: "G", Wx: 778.000000, Wy: 0.000000},
"Gbreve": {GlyphName: "Gbreve", Wx: 778.000000, Wy: 0.000000},
"Gcommaaccent": {GlyphName: "Gcommaaccent", Wx: 778.000000, Wy: 0.000000},
"H": {GlyphName: "H", Wx: 722.000000, Wy: 0.000000},
"I": {GlyphName: "I", Wx: 278.000000, Wy: 0.000000},
"Iacute": {GlyphName: "Iacute", Wx: 278.000000, Wy: 0.000000},
"Icircumflex": {GlyphName: "Icircumflex", Wx: 278.000000, Wy: 0.000000},
"Idieresis": {GlyphName: "Idieresis", Wx: 278.000000, Wy: 0.000000},
"Idotaccent": {GlyphName: "Idotaccent", Wx: 278.000000, Wy: 0.000000},
"Igrave": {GlyphName: "Igrave", Wx: 278.000000, Wy: 0.000000},
"Imacron": {GlyphName: "Imacron", Wx: 278.000000, Wy: 0.000000},
"Iogonek": {GlyphName: "Iogonek", Wx: 278.000000, Wy: 0.000000},
"J": {GlyphName: "J", Wx: 500.000000, Wy: 0.000000},
"K": {GlyphName: "K", Wx: 667.000000, Wy: 0.000000},
"Kcommaaccent": {GlyphName: "Kcommaaccent", Wx: 667.000000, Wy: 0.000000},
"L": {GlyphName: "L", Wx: 556.000000, Wy: 0.000000},
"Lacute": {GlyphName: "Lacute", Wx: 556.000000, Wy: 0.000000},
"Lcaron": {GlyphName: "Lcaron", Wx: 556.000000, Wy: 0.000000},
"Lcommaaccent": {GlyphName: "Lcommaaccent", Wx: 556.000000, Wy: 0.000000},
"Lslash": {GlyphName: "Lslash", Wx: 556.000000, Wy: 0.000000},
"M": {GlyphName: "M", Wx: 833.000000, Wy: 0.000000},
"N": {GlyphName: "N", Wx: 722.000000, Wy: 0.000000},
"Nacute": {GlyphName: "Nacute", Wx: 722.000000, Wy: 0.000000},
"Ncaron": {GlyphName: "Ncaron", Wx: 722.000000, Wy: 0.000000},
"Ncommaaccent": {GlyphName: "Ncommaaccent", Wx: 722.000000, Wy: 0.000000},
"Ntilde": {GlyphName: "Ntilde", Wx: 722.000000, Wy: 0.000000},
"O": {GlyphName: "O", Wx: 778.000000, Wy: 0.000000},
"OE": {GlyphName: "OE", Wx: 1000.000000, Wy: 0.000000},
"Oacute": {GlyphName: "Oacute", Wx: 778.000000, Wy: 0.000000},
"Ocircumflex": {GlyphName: "Ocircumflex", Wx: 778.000000, Wy: 0.000000},
"Odieresis": {GlyphName: "Odieresis", Wx: 778.000000, Wy: 0.000000},
"Ograve": {GlyphName: "Ograve", Wx: 778.000000, Wy: 0.000000},
"Ohungarumlaut": {GlyphName: "Ohungarumlaut", Wx: 778.000000, Wy: 0.000000},
"Omacron": {GlyphName: "Omacron", Wx: 778.000000, Wy: 0.000000},
"Oslash": {GlyphName: "Oslash", Wx: 778.000000, Wy: 0.000000},
"Otilde": {GlyphName: "Otilde", Wx: 778.000000, Wy: 0.000000},
"P": {GlyphName: "P", Wx: 667.000000, Wy: 0.000000},
"Q": {GlyphName: "Q", Wx: 778.000000, Wy: 0.000000},
"R": {GlyphName: "R", Wx: 722.000000, Wy: 0.000000},
"Racute": {GlyphName: "Racute", Wx: 722.000000, Wy: 0.000000},
"Rcaron": {GlyphName: "Rcaron", Wx: 722.000000, Wy: 0.000000},
"Rcommaaccent": {GlyphName: "Rcommaaccent", Wx: 722.000000, Wy: 0.000000},
"S": {GlyphName: "S", Wx: 667.000000, Wy: 0.000000},
"Sacute": {GlyphName: "Sacute", Wx: 667.000000, Wy: 0.000000},
"Scaron": {GlyphName: "Scaron", Wx: 667.000000, Wy: 0.000000},
"Scedilla": {GlyphName: "Scedilla", Wx: 667.000000, Wy: 0.000000},
"Scommaaccent": {GlyphName: "Scommaaccent", Wx: 667.000000, Wy: 0.000000},
"T": {GlyphName: "T", Wx: 611.000000, Wy: 0.000000},
"Tcaron": {GlyphName: "Tcaron", Wx: 611.000000, Wy: 0.000000},
"Tcommaaccent": {GlyphName: "Tcommaaccent", Wx: 611.000000, Wy: 0.000000},
"Thorn": {GlyphName: "Thorn", Wx: 667.000000, Wy: 0.000000},
"U": {GlyphName: "U", Wx: 722.000000, Wy: 0.000000},
"Uacute": {GlyphName: "Uacute", Wx: 722.000000, Wy: 0.000000},
"Ucircumflex": {GlyphName: "Ucircumflex", Wx: 722.000000, Wy: 0.000000},
"Udieresis": {GlyphName: "Udieresis", Wx: 722.000000, Wy: 0.000000},
"Ugrave": {GlyphName: "Ugrave", Wx: 722.000000, Wy: 0.000000},
"Uhungarumlaut": {GlyphName: "Uhungarumlaut", Wx: 722.000000, Wy: 0.000000},
"Umacron": {GlyphName: "Umacron", Wx: 722.000000, Wy: 0.000000},
"Uogonek": {GlyphName: "Uogonek", Wx: 722.000000, Wy: 0.000000},
"Uring": {GlyphName: "Uring", Wx: 722.000000, Wy: 0.000000},
"V": {GlyphName: "V", Wx: 667.000000, Wy: 0.000000},
"W": {GlyphName: "W", Wx: 944.000000, Wy: 0.000000},
"X": {GlyphName: "X", Wx: 667.000000, Wy: 0.000000},
"Y": {GlyphName: "Y", Wx: 667.000000, Wy: 0.000000},
"Yacute": {GlyphName: "Yacute", Wx: 667.000000, Wy: 0.000000},
"Ydieresis": {GlyphName: "Ydieresis", Wx: 667.000000, Wy: 0.000000},
"Z": {GlyphName: "Z", Wx: 611.000000, Wy: 0.000000},
"Zacute": {GlyphName: "Zacute", Wx: 611.000000, Wy: 0.000000},
"Zcaron": {GlyphName: "Zcaron", Wx: 611.000000, Wy: 0.000000},
"Zdotaccent": {GlyphName: "Zdotaccent", Wx: 611.000000, Wy: 0.000000},
"a": {GlyphName: "a", Wx: 556.000000, Wy: 0.000000},
"aacute": {GlyphName: "aacute", Wx: 556.000000, Wy: 0.000000},
"abreve": {GlyphName: "abreve", Wx: 556.000000, Wy: 0.000000},
"acircumflex": {GlyphName: "acircumflex", Wx: 556.000000, Wy: 0.000000},
"acute": {GlyphName: "acute", Wx: 333.000000, Wy: 0.000000},
"adieresis": {GlyphName: "adieresis", Wx: 556.000000, Wy: 0.000000},
"ae": {GlyphName: "ae", Wx: 889.000000, Wy: 0.000000},
"agrave": {GlyphName: "agrave", Wx: 556.000000, Wy: 0.000000},
"amacron": {GlyphName: "amacron", Wx: 556.000000, Wy: 0.000000},
"ampersand": {GlyphName: "ampersand", Wx: 667.000000, Wy: 0.000000},
"aogonek": {GlyphName: "aogonek", Wx: 556.000000, Wy: 0.000000},
"aring": {GlyphName: "aring", Wx: 556.000000, Wy: 0.000000},
"asciicircum": {GlyphName: "asciicircum", Wx: 469.000000, Wy: 0.000000},
"asciitilde": {GlyphName: "asciitilde", Wx: 584.000000, Wy: 0.000000},
"asterisk": {GlyphName: "asterisk", Wx: 389.000000, Wy: 0.000000},
"at": {GlyphName: "at", Wx: 1015.000000, Wy: 0.000000},
"atilde": {GlyphName: "atilde", Wx: 556.000000, Wy: 0.000000},
"b": {GlyphName: "b", Wx: 556.000000, Wy: 0.000000},
"backslash": {GlyphName: "backslash", Wx: 278.000000, Wy: 0.000000},
"bar": {GlyphName: "bar", Wx: 260.000000, Wy: 0.000000},
"braceleft": {GlyphName: "braceleft", Wx: 334.000000, Wy: 0.000000},
"braceright": {GlyphName: "braceright", Wx: 334.000000, Wy: 0.000000},
"bracketleft": {GlyphName: "bracketleft", Wx: 278.000000, Wy: 0.000000},
"bracketright": {GlyphName: "bracketright", Wx: 278.000000, Wy: 0.000000},
"breve": {GlyphName: "breve", Wx: 333.000000, Wy: 0.000000},
"brokenbar": {GlyphName: "brokenbar", Wx: 260.000000, Wy: 0.000000},
"bullet": {GlyphName: "bullet", Wx: 350.000000, Wy: 0.000000},
"c": {GlyphName: "c", Wx: 500.000000, Wy: 0.000000},
"cacute": {GlyphName: "cacute", Wx: 500.000000, Wy: 0.000000},
"caron": {GlyphName: "caron", Wx: 333.000000, Wy: 0.000000},
"ccaron": {GlyphName: "ccaron", Wx: 500.000000, Wy: 0.000000},
"ccedilla": {GlyphName: "ccedilla", Wx: 500.000000, Wy: 0.000000},
"cedilla": {GlyphName: "cedilla", Wx: 333.000000, Wy: 0.000000},
"cent": {GlyphName: "cent", Wx: 556.000000, Wy: 0.000000},
"circumflex": {GlyphName: "circumflex", Wx: 333.000000, Wy: 0.000000},
"colon": {GlyphName: "colon", Wx: 278.000000, Wy: 0.000000},
"comma": {GlyphName: "comma", Wx: 278.000000, Wy: 0.000000},
"commaaccent": {GlyphName: "commaaccent", Wx: 250.000000, Wy: 0.000000},
"copyright": {GlyphName: "copyright", Wx: 737.000000, Wy: 0.000000},
"currency": {GlyphName: "currency", Wx: 556.000000, Wy: 0.000000},
"d": {GlyphName: "d", Wx: 556.000000, Wy: 0.000000},
"dagger": {GlyphName: "dagger", Wx: 556.000000, Wy: 0.000000},
"daggerdbl": {GlyphName: "daggerdbl", Wx: 556.000000, Wy: 0.000000},
"dcaron": {GlyphName: "dcaron", Wx: 643.000000, Wy: 0.000000},
"dcroat": {GlyphName: "dcroat", Wx: 556.000000, Wy: 0.000000},
"degree": {GlyphName: "degree", Wx: 400.000000, Wy: 0.000000},
"dieresis": {GlyphName: "dieresis", Wx: 333.000000, Wy: 0.000000},
"divide": {GlyphName: "divide", Wx: 584.000000, Wy: 0.000000},
"dollar": {GlyphName: "dollar", Wx: 556.000000, Wy: 0.000000},
"dotaccent": {GlyphName: "dotaccent", Wx: 333.000000, Wy: 0.000000},
"dotlessi": {GlyphName: "dotlessi", Wx: 278.000000, Wy: 0.000000},
"e": {GlyphName: "e", Wx: 556.000000, Wy: 0.000000},
"eacute": {GlyphName: "eacute", Wx: 556.000000, Wy: 0.000000},
"ecaron": {GlyphName: "ecaron", Wx: 556.000000, Wy: 0.000000},
"ecircumflex": {GlyphName: "ecircumflex", Wx: 556.000000, Wy: 0.000000},
"edieresis": {GlyphName: "edieresis", Wx: 556.000000, Wy: 0.000000},
"edotaccent": {GlyphName: "edotaccent", Wx: 556.000000, Wy: 0.000000},
"egrave": {GlyphName: "egrave", Wx: 556.000000, Wy: 0.000000},
"eight": {GlyphName: "eight", Wx: 556.000000, Wy: 0.000000},
"ellipsis": {GlyphName: "ellipsis", Wx: 1000.000000, Wy: 0.000000},
"emacron": {GlyphName: "emacron", Wx: 556.000000, Wy: 0.000000},
"emdash": {GlyphName: "emdash", Wx: 1000.000000, Wy: 0.000000},
"endash": {GlyphName: "endash", Wx: 556.000000, Wy: 0.000000},
"eogonek": {GlyphName: "eogonek", Wx: 556.000000, Wy: 0.000000},
"equal": {GlyphName: "equal", Wx: 584.000000, Wy: 0.000000},
"eth": {GlyphName: "eth", Wx: 556.000000, Wy: 0.000000},
"exclam": {GlyphName: "exclam", Wx: 278.000000, Wy: 0.000000},
"exclamdown": {GlyphName: "exclamdown", Wx: 333.000000, Wy: 0.000000},
"f": {GlyphName: "f", Wx: 278.000000, Wy: 0.000000},
"fi": {GlyphName: "fi", Wx: 500.000000, Wy: 0.000000},
"five": {GlyphName: "five", Wx: 556.000000, Wy: 0.000000},
"fl": {GlyphName: "fl", Wx: 500.000000, Wy: 0.000000},
"florin": {GlyphName: "florin", Wx: 556.000000, Wy: 0.000000},
"four": {GlyphName: "four", Wx: 556.000000, Wy: 0.000000},
"fraction": {GlyphName: "fraction", Wx: 167.000000, Wy: 0.000000},
"g": {GlyphName: "g", Wx: 556.000000, Wy: 0.000000},
"gbreve": {GlyphName: "gbreve", Wx: 556.000000, Wy: 0.000000},
"gcommaaccent": {GlyphName: "gcommaaccent", Wx: 556.000000, Wy: 0.000000},
"germandbls": {GlyphName: "germandbls", Wx: 611.000000, Wy: 0.000000},
"grave": {GlyphName: "grave", Wx: 333.000000, Wy: 0.000000},
"greater": {GlyphName: "greater", Wx: 584.000000, Wy: 0.000000},
"greaterequal": {GlyphName: "greaterequal", Wx: 549.000000, Wy: 0.000000},
"guillemotleft": {GlyphName: "guillemotleft", Wx: 556.000000, Wy: 0.000000},
"guillemotright": {GlyphName: "guillemotright", Wx: 556.000000, Wy: 0.000000},
"guilsinglleft": {GlyphName: "guilsinglleft", Wx: 333.000000, Wy: 0.000000},
"guilsinglright": {GlyphName: "guilsinglright", Wx: 333.000000, Wy: 0.000000},
"h": {GlyphName: "h", Wx: 556.000000, Wy: 0.000000},
"hungarumlaut": {GlyphName: "hungarumlaut", Wx: 333.000000, Wy: 0.000000},
"hyphen": {GlyphName: "hyphen", Wx: 333.000000, Wy: 0.000000},
"i": {GlyphName: "i", Wx: 222.000000, Wy: 0.000000},
"iacute": {GlyphName: "iacute", Wx: 278.000000, Wy: 0.000000},
"icircumflex": {GlyphName: "icircumflex", Wx: 278.000000, Wy: 0.000000},
"idieresis": {GlyphName: "idieresis", Wx: 278.000000, Wy: 0.000000},
"igrave": {GlyphName: "igrave", Wx: 278.000000, Wy: 0.000000},
"imacron": {GlyphName: "imacron", Wx: 278.000000, Wy: 0.000000},
"iogonek": {GlyphName: "iogonek", Wx: 222.000000, Wy: 0.000000},
"j": {GlyphName: "j", Wx: 222.000000, Wy: 0.000000},
"k": {GlyphName: "k", Wx: 500.000000, Wy: 0.000000},
"kcommaaccent": {GlyphName: "kcommaaccent", Wx: 500.000000, Wy: 0.000000},
"l": {GlyphName: "l", Wx: 222.000000, Wy: 0.000000},
"lacute": {GlyphName: "lacute", Wx: 222.000000, Wy: 0.000000},
"lcaron": {GlyphName: "lcaron", Wx: 299.000000, Wy: 0.000000},
"lcommaaccent": {GlyphName: "lcommaaccent", Wx: 222.000000, Wy: 0.000000},
"less": {GlyphName: "less", Wx: 584.000000, Wy: 0.000000},
"lessequal": {GlyphName: "lessequal", Wx: 549.000000, Wy: 0.000000},
"logicalnot": {GlyphName: "logicalnot", Wx: 584.000000, Wy: 0.000000},
"lozenge": {GlyphName: "lozenge", Wx: 471.000000, Wy: 0.000000},
"lslash": {GlyphName: "lslash", Wx: 222.000000, Wy: 0.000000},
"m": {GlyphName: "m", Wx: 833.000000, Wy: 0.000000},
"macron": {GlyphName: "macron", Wx: 333.000000, Wy: 0.000000},
"minus": {GlyphName: "minus", Wx: 584.000000, Wy: 0.000000},
"mu": {GlyphName: "mu", Wx: 556.000000, Wy: 0.000000},
"multiply": {GlyphName: "multiply", Wx: 584.000000, Wy: 0.000000},
"n": {GlyphName: "n", Wx: 556.000000, Wy: 0.000000},
"nacute": {GlyphName: "nacute", Wx: 556.000000, Wy: 0.000000},
"ncaron": {GlyphName: "ncaron", Wx: 556.000000, Wy: 0.000000},
"ncommaaccent": {GlyphName: "ncommaaccent", Wx: 556.000000, Wy: 0.000000},
"nine": {GlyphName: "nine", Wx: 556.000000, Wy: 0.000000},
"notequal": {GlyphName: "notequal", Wx: 549.000000, Wy: 0.000000},
"ntilde": {GlyphName: "ntilde", Wx: 556.000000, Wy: 0.000000},
"numbersign": {GlyphName: "numbersign", Wx: 556.000000, Wy: 0.000000},
"o": {GlyphName: "o", Wx: 556.000000, Wy: 0.000000},
"oacute": {GlyphName: "oacute", Wx: 556.000000, Wy: 0.000000},
"ocircumflex": {GlyphName: "ocircumflex", Wx: 556.000000, Wy: 0.000000},
"odieresis": {GlyphName: "odieresis", Wx: 556.000000, Wy: 0.000000},
"oe": {GlyphName: "oe", Wx: 944.000000, Wy: 0.000000},
"ogonek": {GlyphName: "ogonek", Wx: 333.000000, Wy: 0.000000},
"ograve": {GlyphName: "ograve", Wx: 556.000000, Wy: 0.000000},
"ohungarumlaut": {GlyphName: "ohungarumlaut", Wx: 556.000000, Wy: 0.000000},
"omacron": {GlyphName: "omacron", Wx: 556.000000, Wy: 0.000000},
"one": {GlyphName: "one", Wx: 556.000000, Wy: 0.000000},
"onehalf": {GlyphName: "onehalf", Wx: 834.000000, Wy: 0.000000},
"onequarter": {GlyphName: "onequarter", Wx: 834.000000, Wy: 0.000000},
"onesuperior": {GlyphName: "onesuperior", Wx: 333.000000, Wy: 0.000000},
"ordfeminine": {GlyphName: "ordfeminine", Wx: 370.000000, Wy: 0.000000},
"ordmasculine": {GlyphName: "ordmasculine", Wx: 365.000000, Wy: 0.000000},
"oslash": {GlyphName: "oslash", Wx: 611.000000, Wy: 0.000000},
"otilde": {GlyphName: "otilde", Wx: 556.000000, Wy: 0.000000},
"p": {GlyphName: "p", Wx: 556.000000, Wy: 0.000000},
"paragraph": {GlyphName: "paragraph", Wx: 537.000000, Wy: 0.000000},
"parenleft": {GlyphName: "parenleft", Wx: 333.000000, Wy: 0.000000},
"parenright": {GlyphName: "parenright", Wx: 333.000000, Wy: 0.000000},
"partialdiff": {GlyphName: "partialdiff", Wx: 476.000000, Wy: 0.000000},
"percent": {GlyphName: "percent", Wx: 889.000000, Wy: 0.000000},
"period": {GlyphName: "period", Wx: 278.000000, Wy: 0.000000},
"periodcentered": {GlyphName: "periodcentered", Wx: 278.000000, Wy: 0.000000},
"perthousand": {GlyphName: "perthousand", Wx: 1000.000000, Wy: 0.000000},
"plus": {GlyphName: "plus", Wx: 584.000000, Wy: 0.000000},
"plusminus": {GlyphName: "plusminus", Wx: 584.000000, Wy: 0.000000},
"q": {GlyphName: "q", Wx: 556.000000, Wy: 0.000000},
"question": {GlyphName: "question", Wx: 556.000000, Wy: 0.000000},
"questiondown": {GlyphName: "questiondown", Wx: 611.000000, Wy: 0.000000},
"quotedbl": {GlyphName: "quotedbl", Wx: 355.000000, Wy: 0.000000},
"quotedblbase": {GlyphName: "quotedblbase", Wx: 333.000000, Wy: 0.000000},
"quotedblleft": {GlyphName: "quotedblleft", Wx: 333.000000, Wy: 0.000000},
"quotedblright": {GlyphName: "quotedblright", Wx: 333.000000, Wy: 0.000000},
"quoteleft": {GlyphName: "quoteleft", Wx: 222.000000, Wy: 0.000000},
"quoteright": {GlyphName: "quoteright", Wx: 222.000000, Wy: 0.000000},
"quotesinglbase": {GlyphName: "quotesinglbase", Wx: 222.000000, Wy: 0.000000},
"quotesingle": {GlyphName: "quotesingle", Wx: 191.000000, Wy: 0.000000},
"r": {GlyphName: "r", Wx: 333.000000, Wy: 0.000000},
"racute": {GlyphName: "racute", Wx: 333.000000, Wy: 0.000000},
"radical": {GlyphName: "radical", Wx: 453.000000, Wy: 0.000000},
"rcaron": {GlyphName: "rcaron", Wx: 333.000000, Wy: 0.000000},
"rcommaaccent": {GlyphName: "rcommaaccent", Wx: 333.000000, Wy: 0.000000},
"registered": {GlyphName: "registered", Wx: 737.000000, Wy: 0.000000},
"ring": {GlyphName: "ring", Wx: 333.000000, Wy: 0.000000},
"s": {GlyphName: "s", Wx: 500.000000, Wy: 0.000000},
"sacute": {GlyphName: "sacute", Wx: 500.000000, Wy: 0.000000},
"scaron": {GlyphName: "scaron", Wx: 500.000000, Wy: 0.000000},
"scedilla": {GlyphName: "scedilla", Wx: 500.000000, Wy: 0.000000},
"scommaaccent": {GlyphName: "scommaaccent", Wx: 500.000000, Wy: 0.000000},
"section": {GlyphName: "section", Wx: 556.000000, Wy: 0.000000},
"semicolon": {GlyphName: "semicolon", Wx: 278.000000, Wy: 0.000000},
"seven": {GlyphName: "seven", Wx: 556.000000, Wy: 0.000000},
"six": {GlyphName: "six", Wx: 556.000000, Wy: 0.000000},
"slash": {GlyphName: "slash", Wx: 278.000000, Wy: 0.000000},
"space": {GlyphName: "space", Wx: 278.000000, Wy: 0.000000},
"sterling": {GlyphName: "sterling", Wx: 556.000000, Wy: 0.000000},
"summation": {GlyphName: "summation", Wx: 600.000000, Wy: 0.000000},
"t": {GlyphName: "t", Wx: 278.000000, Wy: 0.000000},
"tcaron": {GlyphName: "tcaron", Wx: 317.000000, Wy: 0.000000},
"tcommaaccent": {GlyphName: "tcommaaccent", Wx: 278.000000, Wy: 0.000000},
"thorn": {GlyphName: "thorn", Wx: 556.000000, Wy: 0.000000},
"three": {GlyphName: "three", Wx: 556.000000, Wy: 0.000000},
"threequarters": {GlyphName: "threequarters", Wx: 834.000000, Wy: 0.000000},
"threesuperior": {GlyphName: "threesuperior", Wx: 333.000000, Wy: 0.000000},
"tilde": {GlyphName: "tilde", Wx: 333.000000, Wy: 0.000000},
"trademark": {GlyphName: "trademark", Wx: 1000.000000, Wy: 0.000000},
"two": {GlyphName: "two", Wx: 556.000000, Wy: 0.000000},
"twosuperior": {GlyphName: "twosuperior", Wx: 333.000000, Wy: 0.000000},
"u": {GlyphName: "u", Wx: 556.000000, Wy: 0.000000},
"uacute": {GlyphName: "uacute", Wx: 556.000000, Wy: 0.000000},
"ucircumflex": {GlyphName: "ucircumflex", Wx: 556.000000, Wy: 0.000000},
"udieresis": {GlyphName: "udieresis", Wx: 556.000000, Wy: 0.000000},
"ugrave": {GlyphName: "ugrave", Wx: 556.000000, Wy: 0.000000},
"uhungarumlaut": {GlyphName: "uhungarumlaut", Wx: 556.000000, Wy: 0.000000},
"umacron": {GlyphName: "umacron", Wx: 556.000000, Wy: 0.000000},
"underscore": {GlyphName: "underscore", Wx: 556.000000, Wy: 0.000000},
"uogonek": {GlyphName: "uogonek", Wx: 556.000000, Wy: 0.000000},
"uring": {GlyphName: "uring", Wx: 556.000000, Wy: 0.000000},
"v": {GlyphName: "v", Wx: 500.000000, Wy: 0.000000},
"w": {GlyphName: "w", Wx: 722.000000, Wy: 0.000000},
"x": {GlyphName: "x", Wx: 500.000000, Wy: 0.000000},
"y": {GlyphName: "y", Wx: 500.000000, Wy: 0.000000},
"yacute": {GlyphName: "yacute", Wx: 500.000000, Wy: 0.000000},
"ydieresis": {GlyphName: "ydieresis", Wx: 500.000000, Wy: 0.000000},
"yen": {GlyphName: "yen", Wx: 556.000000, Wy: 0.000000},
"z": {GlyphName: "z", Wx: 500.000000, Wy: 0.000000},
"zacute": {GlyphName: "zacute", Wx: 500.000000, Wy: 0.000000},
"zcaron": {GlyphName: "zcaron", Wx: 500.000000, Wy: 0.000000},
"zdotaccent": {GlyphName: "zdotaccent", Wx: 500.000000, Wy: 0.000000},
"zero": {GlyphName: "zero", Wx: 556.000000, Wy: 0.000000},
}

View File

@@ -0,0 +1,362 @@
package fonts
import (
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model/textencoding"
)
// Font Helvetica-Bold. Implements Font interface.
// This is a built-in font and it is assumed that every reader has access to it.
type fontHelveticaBold struct {
encoder textencoding.TextEncoder
}
func NewFontHelveticaBold() fontHelveticaBold {
font := fontHelveticaBold{}
font.encoder = textencoding.NewWinAnsiTextEncoder() // Default
return font
}
func (font fontHelveticaBold) SetEncoder(encoder textencoding.TextEncoder) {
font.encoder = encoder
}
func (font fontHelveticaBold) GetGlyphCharMetrics(glyph string) (CharMetrics, bool) {
metrics, has := helveticaBoldCharMetrics[glyph]
if !has {
return metrics, false
}
return metrics, true
}
func (font fontHelveticaBold) ToPdfObject() core.PdfObject {
obj := &core.PdfIndirectObject{}
fontDict := core.MakeDict()
fontDict.Set("Type", core.MakeName("Font"))
fontDict.Set("Subtype", core.MakeName("Type1"))
fontDict.Set("BaseFont", core.MakeName("Helvetica-Bold"))
fontDict.Set("Encoding", font.encoder.ToPdfObject())
obj.PdfObject = fontDict
return obj
}
var helveticaBoldCharMetrics map[string]CharMetrics = map[string]CharMetrics{
"A": {GlyphName: "A", Wx: 722.000000, Wy: 0.000000},
"AE": {GlyphName: "AE", Wx: 1000.000000, Wy: 0.000000},
"Aacute": {GlyphName: "Aacute", Wx: 722.000000, Wy: 0.000000},
"Abreve": {GlyphName: "Abreve", Wx: 722.000000, Wy: 0.000000},
"Acircumflex": {GlyphName: "Acircumflex", Wx: 722.000000, Wy: 0.000000},
"Adieresis": {GlyphName: "Adieresis", Wx: 722.000000, Wy: 0.000000},
"Agrave": {GlyphName: "Agrave", Wx: 722.000000, Wy: 0.000000},
"Amacron": {GlyphName: "Amacron", Wx: 722.000000, Wy: 0.000000},
"Aogonek": {GlyphName: "Aogonek", Wx: 722.000000, Wy: 0.000000},
"Aring": {GlyphName: "Aring", Wx: 722.000000, Wy: 0.000000},
"Atilde": {GlyphName: "Atilde", Wx: 722.000000, Wy: 0.000000},
"B": {GlyphName: "B", Wx: 722.000000, Wy: 0.000000},
"C": {GlyphName: "C", Wx: 722.000000, Wy: 0.000000},
"Cacute": {GlyphName: "Cacute", Wx: 722.000000, Wy: 0.000000},
"Ccaron": {GlyphName: "Ccaron", Wx: 722.000000, Wy: 0.000000},
"Ccedilla": {GlyphName: "Ccedilla", Wx: 722.000000, Wy: 0.000000},
"D": {GlyphName: "D", Wx: 722.000000, Wy: 0.000000},
"Dcaron": {GlyphName: "Dcaron", Wx: 722.000000, Wy: 0.000000},
"Dcroat": {GlyphName: "Dcroat", Wx: 722.000000, Wy: 0.000000},
"Delta": {GlyphName: "Delta", Wx: 612.000000, Wy: 0.000000},
"E": {GlyphName: "E", Wx: 667.000000, Wy: 0.000000},
"Eacute": {GlyphName: "Eacute", Wx: 667.000000, Wy: 0.000000},
"Ecaron": {GlyphName: "Ecaron", Wx: 667.000000, Wy: 0.000000},
"Ecircumflex": {GlyphName: "Ecircumflex", Wx: 667.000000, Wy: 0.000000},
"Edieresis": {GlyphName: "Edieresis", Wx: 667.000000, Wy: 0.000000},
"Edotaccent": {GlyphName: "Edotaccent", Wx: 667.000000, Wy: 0.000000},
"Egrave": {GlyphName: "Egrave", Wx: 667.000000, Wy: 0.000000},
"Emacron": {GlyphName: "Emacron", Wx: 667.000000, Wy: 0.000000},
"Eogonek": {GlyphName: "Eogonek", Wx: 667.000000, Wy: 0.000000},
"Eth": {GlyphName: "Eth", Wx: 722.000000, Wy: 0.000000},
"Euro": {GlyphName: "Euro", Wx: 556.000000, Wy: 0.000000},
"F": {GlyphName: "F", Wx: 611.000000, Wy: 0.000000},
"G": {GlyphName: "G", Wx: 778.000000, Wy: 0.000000},
"Gbreve": {GlyphName: "Gbreve", Wx: 778.000000, Wy: 0.000000},
"Gcommaaccent": {GlyphName: "Gcommaaccent", Wx: 778.000000, Wy: 0.000000},
"H": {GlyphName: "H", Wx: 722.000000, Wy: 0.000000},
"I": {GlyphName: "I", Wx: 278.000000, Wy: 0.000000},
"Iacute": {GlyphName: "Iacute", Wx: 278.000000, Wy: 0.000000},
"Icircumflex": {GlyphName: "Icircumflex", Wx: 278.000000, Wy: 0.000000},
"Idieresis": {GlyphName: "Idieresis", Wx: 278.000000, Wy: 0.000000},
"Idotaccent": {GlyphName: "Idotaccent", Wx: 278.000000, Wy: 0.000000},
"Igrave": {GlyphName: "Igrave", Wx: 278.000000, Wy: 0.000000},
"Imacron": {GlyphName: "Imacron", Wx: 278.000000, Wy: 0.000000},
"Iogonek": {GlyphName: "Iogonek", Wx: 278.000000, Wy: 0.000000},
"J": {GlyphName: "J", Wx: 556.000000, Wy: 0.000000},
"K": {GlyphName: "K", Wx: 722.000000, Wy: 0.000000},
"Kcommaaccent": {GlyphName: "Kcommaaccent", Wx: 722.000000, Wy: 0.000000},
"L": {GlyphName: "L", Wx: 611.000000, Wy: 0.000000},
"Lacute": {GlyphName: "Lacute", Wx: 611.000000, Wy: 0.000000},
"Lcaron": {GlyphName: "Lcaron", Wx: 611.000000, Wy: 0.000000},
"Lcommaaccent": {GlyphName: "Lcommaaccent", Wx: 611.000000, Wy: 0.000000},
"Lslash": {GlyphName: "Lslash", Wx: 611.000000, Wy: 0.000000},
"M": {GlyphName: "M", Wx: 833.000000, Wy: 0.000000},
"N": {GlyphName: "N", Wx: 722.000000, Wy: 0.000000},
"Nacute": {GlyphName: "Nacute", Wx: 722.000000, Wy: 0.000000},
"Ncaron": {GlyphName: "Ncaron", Wx: 722.000000, Wy: 0.000000},
"Ncommaaccent": {GlyphName: "Ncommaaccent", Wx: 722.000000, Wy: 0.000000},
"Ntilde": {GlyphName: "Ntilde", Wx: 722.000000, Wy: 0.000000},
"O": {GlyphName: "O", Wx: 778.000000, Wy: 0.000000},
"OE": {GlyphName: "OE", Wx: 1000.000000, Wy: 0.000000},
"Oacute": {GlyphName: "Oacute", Wx: 778.000000, Wy: 0.000000},
"Ocircumflex": {GlyphName: "Ocircumflex", Wx: 778.000000, Wy: 0.000000},
"Odieresis": {GlyphName: "Odieresis", Wx: 778.000000, Wy: 0.000000},
"Ograve": {GlyphName: "Ograve", Wx: 778.000000, Wy: 0.000000},
"Ohungarumlaut": {GlyphName: "Ohungarumlaut", Wx: 778.000000, Wy: 0.000000},
"Omacron": {GlyphName: "Omacron", Wx: 778.000000, Wy: 0.000000},
"Oslash": {GlyphName: "Oslash", Wx: 778.000000, Wy: 0.000000},
"Otilde": {GlyphName: "Otilde", Wx: 778.000000, Wy: 0.000000},
"P": {GlyphName: "P", Wx: 667.000000, Wy: 0.000000},
"Q": {GlyphName: "Q", Wx: 778.000000, Wy: 0.000000},
"R": {GlyphName: "R", Wx: 722.000000, Wy: 0.000000},
"Racute": {GlyphName: "Racute", Wx: 722.000000, Wy: 0.000000},
"Rcaron": {GlyphName: "Rcaron", Wx: 722.000000, Wy: 0.000000},
"Rcommaaccent": {GlyphName: "Rcommaaccent", Wx: 722.000000, Wy: 0.000000},
"S": {GlyphName: "S", Wx: 667.000000, Wy: 0.000000},
"Sacute": {GlyphName: "Sacute", Wx: 667.000000, Wy: 0.000000},
"Scaron": {GlyphName: "Scaron", Wx: 667.000000, Wy: 0.000000},
"Scedilla": {GlyphName: "Scedilla", Wx: 667.000000, Wy: 0.000000},
"Scommaaccent": {GlyphName: "Scommaaccent", Wx: 667.000000, Wy: 0.000000},
"T": {GlyphName: "T", Wx: 611.000000, Wy: 0.000000},
"Tcaron": {GlyphName: "Tcaron", Wx: 611.000000, Wy: 0.000000},
"Tcommaaccent": {GlyphName: "Tcommaaccent", Wx: 611.000000, Wy: 0.000000},
"Thorn": {GlyphName: "Thorn", Wx: 667.000000, Wy: 0.000000},
"U": {GlyphName: "U", Wx: 722.000000, Wy: 0.000000},
"Uacute": {GlyphName: "Uacute", Wx: 722.000000, Wy: 0.000000},
"Ucircumflex": {GlyphName: "Ucircumflex", Wx: 722.000000, Wy: 0.000000},
"Udieresis": {GlyphName: "Udieresis", Wx: 722.000000, Wy: 0.000000},
"Ugrave": {GlyphName: "Ugrave", Wx: 722.000000, Wy: 0.000000},
"Uhungarumlaut": {GlyphName: "Uhungarumlaut", Wx: 722.000000, Wy: 0.000000},
"Umacron": {GlyphName: "Umacron", Wx: 722.000000, Wy: 0.000000},
"Uogonek": {GlyphName: "Uogonek", Wx: 722.000000, Wy: 0.000000},
"Uring": {GlyphName: "Uring", Wx: 722.000000, Wy: 0.000000},
"V": {GlyphName: "V", Wx: 667.000000, Wy: 0.000000},
"W": {GlyphName: "W", Wx: 944.000000, Wy: 0.000000},
"X": {GlyphName: "X", Wx: 667.000000, Wy: 0.000000},
"Y": {GlyphName: "Y", Wx: 667.000000, Wy: 0.000000},
"Yacute": {GlyphName: "Yacute", Wx: 667.000000, Wy: 0.000000},
"Ydieresis": {GlyphName: "Ydieresis", Wx: 667.000000, Wy: 0.000000},
"Z": {GlyphName: "Z", Wx: 611.000000, Wy: 0.000000},
"Zacute": {GlyphName: "Zacute", Wx: 611.000000, Wy: 0.000000},
"Zcaron": {GlyphName: "Zcaron", Wx: 611.000000, Wy: 0.000000},
"Zdotaccent": {GlyphName: "Zdotaccent", Wx: 611.000000, Wy: 0.000000},
"a": {GlyphName: "a", Wx: 556.000000, Wy: 0.000000},
"aacute": {GlyphName: "aacute", Wx: 556.000000, Wy: 0.000000},
"abreve": {GlyphName: "abreve", Wx: 556.000000, Wy: 0.000000},
"acircumflex": {GlyphName: "acircumflex", Wx: 556.000000, Wy: 0.000000},
"acute": {GlyphName: "acute", Wx: 333.000000, Wy: 0.000000},
"adieresis": {GlyphName: "adieresis", Wx: 556.000000, Wy: 0.000000},
"ae": {GlyphName: "ae", Wx: 889.000000, Wy: 0.000000},
"agrave": {GlyphName: "agrave", Wx: 556.000000, Wy: 0.000000},
"amacron": {GlyphName: "amacron", Wx: 556.000000, Wy: 0.000000},
"ampersand": {GlyphName: "ampersand", Wx: 722.000000, Wy: 0.000000},
"aogonek": {GlyphName: "aogonek", Wx: 556.000000, Wy: 0.000000},
"aring": {GlyphName: "aring", Wx: 556.000000, Wy: 0.000000},
"asciicircum": {GlyphName: "asciicircum", Wx: 584.000000, Wy: 0.000000},
"asciitilde": {GlyphName: "asciitilde", Wx: 584.000000, Wy: 0.000000},
"asterisk": {GlyphName: "asterisk", Wx: 389.000000, Wy: 0.000000},
"at": {GlyphName: "at", Wx: 975.000000, Wy: 0.000000},
"atilde": {GlyphName: "atilde", Wx: 556.000000, Wy: 0.000000},
"b": {GlyphName: "b", Wx: 611.000000, Wy: 0.000000},
"backslash": {GlyphName: "backslash", Wx: 278.000000, Wy: 0.000000},
"bar": {GlyphName: "bar", Wx: 280.000000, Wy: 0.000000},
"braceleft": {GlyphName: "braceleft", Wx: 389.000000, Wy: 0.000000},
"braceright": {GlyphName: "braceright", Wx: 389.000000, Wy: 0.000000},
"bracketleft": {GlyphName: "bracketleft", Wx: 333.000000, Wy: 0.000000},
"bracketright": {GlyphName: "bracketright", Wx: 333.000000, Wy: 0.000000},
"breve": {GlyphName: "breve", Wx: 333.000000, Wy: 0.000000},
"brokenbar": {GlyphName: "brokenbar", Wx: 280.000000, Wy: 0.000000},
"bullet": {GlyphName: "bullet", Wx: 350.000000, Wy: 0.000000},
"c": {GlyphName: "c", Wx: 556.000000, Wy: 0.000000},
"cacute": {GlyphName: "cacute", Wx: 556.000000, Wy: 0.000000},
"caron": {GlyphName: "caron", Wx: 333.000000, Wy: 0.000000},
"ccaron": {GlyphName: "ccaron", Wx: 556.000000, Wy: 0.000000},
"ccedilla": {GlyphName: "ccedilla", Wx: 556.000000, Wy: 0.000000},
"cedilla": {GlyphName: "cedilla", Wx: 333.000000, Wy: 0.000000},
"cent": {GlyphName: "cent", Wx: 556.000000, Wy: 0.000000},
"circumflex": {GlyphName: "circumflex", Wx: 333.000000, Wy: 0.000000},
"colon": {GlyphName: "colon", Wx: 333.000000, Wy: 0.000000},
"comma": {GlyphName: "comma", Wx: 278.000000, Wy: 0.000000},
"commaaccent": {GlyphName: "commaaccent", Wx: 250.000000, Wy: 0.000000},
"copyright": {GlyphName: "copyright", Wx: 737.000000, Wy: 0.000000},
"currency": {GlyphName: "currency", Wx: 556.000000, Wy: 0.000000},
"d": {GlyphName: "d", Wx: 611.000000, Wy: 0.000000},
"dagger": {GlyphName: "dagger", Wx: 556.000000, Wy: 0.000000},
"daggerdbl": {GlyphName: "daggerdbl", Wx: 556.000000, Wy: 0.000000},
"dcaron": {GlyphName: "dcaron", Wx: 743.000000, Wy: 0.000000},
"dcroat": {GlyphName: "dcroat", Wx: 611.000000, Wy: 0.000000},
"degree": {GlyphName: "degree", Wx: 400.000000, Wy: 0.000000},
"dieresis": {GlyphName: "dieresis", Wx: 333.000000, Wy: 0.000000},
"divide": {GlyphName: "divide", Wx: 584.000000, Wy: 0.000000},
"dollar": {GlyphName: "dollar", Wx: 556.000000, Wy: 0.000000},
"dotaccent": {GlyphName: "dotaccent", Wx: 333.000000, Wy: 0.000000},
"dotlessi": {GlyphName: "dotlessi", Wx: 278.000000, Wy: 0.000000},
"e": {GlyphName: "e", Wx: 556.000000, Wy: 0.000000},
"eacute": {GlyphName: "eacute", Wx: 556.000000, Wy: 0.000000},
"ecaron": {GlyphName: "ecaron", Wx: 556.000000, Wy: 0.000000},
"ecircumflex": {GlyphName: "ecircumflex", Wx: 556.000000, Wy: 0.000000},
"edieresis": {GlyphName: "edieresis", Wx: 556.000000, Wy: 0.000000},
"edotaccent": {GlyphName: "edotaccent", Wx: 556.000000, Wy: 0.000000},
"egrave": {GlyphName: "egrave", Wx: 556.000000, Wy: 0.000000},
"eight": {GlyphName: "eight", Wx: 556.000000, Wy: 0.000000},
"ellipsis": {GlyphName: "ellipsis", Wx: 1000.000000, Wy: 0.000000},
"emacron": {GlyphName: "emacron", Wx: 556.000000, Wy: 0.000000},
"emdash": {GlyphName: "emdash", Wx: 1000.000000, Wy: 0.000000},
"endash": {GlyphName: "endash", Wx: 556.000000, Wy: 0.000000},
"eogonek": {GlyphName: "eogonek", Wx: 556.000000, Wy: 0.000000},
"equal": {GlyphName: "equal", Wx: 584.000000, Wy: 0.000000},
"eth": {GlyphName: "eth", Wx: 611.000000, Wy: 0.000000},
"exclam": {GlyphName: "exclam", Wx: 333.000000, Wy: 0.000000},
"exclamdown": {GlyphName: "exclamdown", Wx: 333.000000, Wy: 0.000000},
"f": {GlyphName: "f", Wx: 333.000000, Wy: 0.000000},
"fi": {GlyphName: "fi", Wx: 611.000000, Wy: 0.000000},
"five": {GlyphName: "five", Wx: 556.000000, Wy: 0.000000},
"fl": {GlyphName: "fl", Wx: 611.000000, Wy: 0.000000},
"florin": {GlyphName: "florin", Wx: 556.000000, Wy: 0.000000},
"four": {GlyphName: "four", Wx: 556.000000, Wy: 0.000000},
"fraction": {GlyphName: "fraction", Wx: 167.000000, Wy: 0.000000},
"g": {GlyphName: "g", Wx: 611.000000, Wy: 0.000000},
"gbreve": {GlyphName: "gbreve", Wx: 611.000000, Wy: 0.000000},
"gcommaaccent": {GlyphName: "gcommaaccent", Wx: 611.000000, Wy: 0.000000},
"germandbls": {GlyphName: "germandbls", Wx: 611.000000, Wy: 0.000000},
"grave": {GlyphName: "grave", Wx: 333.000000, Wy: 0.000000},
"greater": {GlyphName: "greater", Wx: 584.000000, Wy: 0.000000},
"greaterequal": {GlyphName: "greaterequal", Wx: 549.000000, Wy: 0.000000},
"guillemotleft": {GlyphName: "guillemotleft", Wx: 556.000000, Wy: 0.000000},
"guillemotright": {GlyphName: "guillemotright", Wx: 556.000000, Wy: 0.000000},
"guilsinglleft": {GlyphName: "guilsinglleft", Wx: 333.000000, Wy: 0.000000},
"guilsinglright": {GlyphName: "guilsinglright", Wx: 333.000000, Wy: 0.000000},
"h": {GlyphName: "h", Wx: 611.000000, Wy: 0.000000},
"hungarumlaut": {GlyphName: "hungarumlaut", Wx: 333.000000, Wy: 0.000000},
"hyphen": {GlyphName: "hyphen", Wx: 333.000000, Wy: 0.000000},
"i": {GlyphName: "i", Wx: 278.000000, Wy: 0.000000},
"iacute": {GlyphName: "iacute", Wx: 278.000000, Wy: 0.000000},
"icircumflex": {GlyphName: "icircumflex", Wx: 278.000000, Wy: 0.000000},
"idieresis": {GlyphName: "idieresis", Wx: 278.000000, Wy: 0.000000},
"igrave": {GlyphName: "igrave", Wx: 278.000000, Wy: 0.000000},
"imacron": {GlyphName: "imacron", Wx: 278.000000, Wy: 0.000000},
"iogonek": {GlyphName: "iogonek", Wx: 278.000000, Wy: 0.000000},
"j": {GlyphName: "j", Wx: 278.000000, Wy: 0.000000},
"k": {GlyphName: "k", Wx: 556.000000, Wy: 0.000000},
"kcommaaccent": {GlyphName: "kcommaaccent", Wx: 556.000000, Wy: 0.000000},
"l": {GlyphName: "l", Wx: 278.000000, Wy: 0.000000},
"lacute": {GlyphName: "lacute", Wx: 278.000000, Wy: 0.000000},
"lcaron": {GlyphName: "lcaron", Wx: 400.000000, Wy: 0.000000},
"lcommaaccent": {GlyphName: "lcommaaccent", Wx: 278.000000, Wy: 0.000000},
"less": {GlyphName: "less", Wx: 584.000000, Wy: 0.000000},
"lessequal": {GlyphName: "lessequal", Wx: 549.000000, Wy: 0.000000},
"logicalnot": {GlyphName: "logicalnot", Wx: 584.000000, Wy: 0.000000},
"lozenge": {GlyphName: "lozenge", Wx: 494.000000, Wy: 0.000000},
"lslash": {GlyphName: "lslash", Wx: 278.000000, Wy: 0.000000},
"m": {GlyphName: "m", Wx: 889.000000, Wy: 0.000000},
"macron": {GlyphName: "macron", Wx: 333.000000, Wy: 0.000000},
"minus": {GlyphName: "minus", Wx: 584.000000, Wy: 0.000000},
"mu": {GlyphName: "mu", Wx: 611.000000, Wy: 0.000000},
"multiply": {GlyphName: "multiply", Wx: 584.000000, Wy: 0.000000},
"n": {GlyphName: "n", Wx: 611.000000, Wy: 0.000000},
"nacute": {GlyphName: "nacute", Wx: 611.000000, Wy: 0.000000},
"ncaron": {GlyphName: "ncaron", Wx: 611.000000, Wy: 0.000000},
"ncommaaccent": {GlyphName: "ncommaaccent", Wx: 611.000000, Wy: 0.000000},
"nine": {GlyphName: "nine", Wx: 556.000000, Wy: 0.000000},
"notequal": {GlyphName: "notequal", Wx: 549.000000, Wy: 0.000000},
"ntilde": {GlyphName: "ntilde", Wx: 611.000000, Wy: 0.000000},
"numbersign": {GlyphName: "numbersign", Wx: 556.000000, Wy: 0.000000},
"o": {GlyphName: "o", Wx: 611.000000, Wy: 0.000000},
"oacute": {GlyphName: "oacute", Wx: 611.000000, Wy: 0.000000},
"ocircumflex": {GlyphName: "ocircumflex", Wx: 611.000000, Wy: 0.000000},
"odieresis": {GlyphName: "odieresis", Wx: 611.000000, Wy: 0.000000},
"oe": {GlyphName: "oe", Wx: 944.000000, Wy: 0.000000},
"ogonek": {GlyphName: "ogonek", Wx: 333.000000, Wy: 0.000000},
"ograve": {GlyphName: "ograve", Wx: 611.000000, Wy: 0.000000},
"ohungarumlaut": {GlyphName: "ohungarumlaut", Wx: 611.000000, Wy: 0.000000},
"omacron": {GlyphName: "omacron", Wx: 611.000000, Wy: 0.000000},
"one": {GlyphName: "one", Wx: 556.000000, Wy: 0.000000},
"onehalf": {GlyphName: "onehalf", Wx: 834.000000, Wy: 0.000000},
"onequarter": {GlyphName: "onequarter", Wx: 834.000000, Wy: 0.000000},
"onesuperior": {GlyphName: "onesuperior", Wx: 333.000000, Wy: 0.000000},
"ordfeminine": {GlyphName: "ordfeminine", Wx: 370.000000, Wy: 0.000000},
"ordmasculine": {GlyphName: "ordmasculine", Wx: 365.000000, Wy: 0.000000},
"oslash": {GlyphName: "oslash", Wx: 611.000000, Wy: 0.000000},
"otilde": {GlyphName: "otilde", Wx: 611.000000, Wy: 0.000000},
"p": {GlyphName: "p", Wx: 611.000000, Wy: 0.000000},
"paragraph": {GlyphName: "paragraph", Wx: 556.000000, Wy: 0.000000},
"parenleft": {GlyphName: "parenleft", Wx: 333.000000, Wy: 0.000000},
"parenright": {GlyphName: "parenright", Wx: 333.000000, Wy: 0.000000},
"partialdiff": {GlyphName: "partialdiff", Wx: 494.000000, Wy: 0.000000},
"percent": {GlyphName: "percent", Wx: 889.000000, Wy: 0.000000},
"period": {GlyphName: "period", Wx: 278.000000, Wy: 0.000000},
"periodcentered": {GlyphName: "periodcentered", Wx: 278.000000, Wy: 0.000000},
"perthousand": {GlyphName: "perthousand", Wx: 1000.000000, Wy: 0.000000},
"plus": {GlyphName: "plus", Wx: 584.000000, Wy: 0.000000},
"plusminus": {GlyphName: "plusminus", Wx: 584.000000, Wy: 0.000000},
"q": {GlyphName: "q", Wx: 611.000000, Wy: 0.000000},
"question": {GlyphName: "question", Wx: 611.000000, Wy: 0.000000},
"questiondown": {GlyphName: "questiondown", Wx: 611.000000, Wy: 0.000000},
"quotedbl": {GlyphName: "quotedbl", Wx: 474.000000, Wy: 0.000000},
"quotedblbase": {GlyphName: "quotedblbase", Wx: 500.000000, Wy: 0.000000},
"quotedblleft": {GlyphName: "quotedblleft", Wx: 500.000000, Wy: 0.000000},
"quotedblright": {GlyphName: "quotedblright", Wx: 500.000000, Wy: 0.000000},
"quoteleft": {GlyphName: "quoteleft", Wx: 278.000000, Wy: 0.000000},
"quoteright": {GlyphName: "quoteright", Wx: 278.000000, Wy: 0.000000},
"quotesinglbase": {GlyphName: "quotesinglbase", Wx: 278.000000, Wy: 0.000000},
"quotesingle": {GlyphName: "quotesingle", Wx: 238.000000, Wy: 0.000000},
"r": {GlyphName: "r", Wx: 389.000000, Wy: 0.000000},
"racute": {GlyphName: "racute", Wx: 389.000000, Wy: 0.000000},
"radical": {GlyphName: "radical", Wx: 549.000000, Wy: 0.000000},
"rcaron": {GlyphName: "rcaron", Wx: 389.000000, Wy: 0.000000},
"rcommaaccent": {GlyphName: "rcommaaccent", Wx: 389.000000, Wy: 0.000000},
"registered": {GlyphName: "registered", Wx: 737.000000, Wy: 0.000000},
"ring": {GlyphName: "ring", Wx: 333.000000, Wy: 0.000000},
"s": {GlyphName: "s", Wx: 556.000000, Wy: 0.000000},
"sacute": {GlyphName: "sacute", Wx: 556.000000, Wy: 0.000000},
"scaron": {GlyphName: "scaron", Wx: 556.000000, Wy: 0.000000},
"scedilla": {GlyphName: "scedilla", Wx: 556.000000, Wy: 0.000000},
"scommaaccent": {GlyphName: "scommaaccent", Wx: 556.000000, Wy: 0.000000},
"section": {GlyphName: "section", Wx: 556.000000, Wy: 0.000000},
"semicolon": {GlyphName: "semicolon", Wx: 333.000000, Wy: 0.000000},
"seven": {GlyphName: "seven", Wx: 556.000000, Wy: 0.000000},
"six": {GlyphName: "six", Wx: 556.000000, Wy: 0.000000},
"slash": {GlyphName: "slash", Wx: 278.000000, Wy: 0.000000},
"space": {GlyphName: "space", Wx: 278.000000, Wy: 0.000000},
"sterling": {GlyphName: "sterling", Wx: 556.000000, Wy: 0.000000},
"summation": {GlyphName: "summation", Wx: 600.000000, Wy: 0.000000},
"t": {GlyphName: "t", Wx: 333.000000, Wy: 0.000000},
"tcaron": {GlyphName: "tcaron", Wx: 389.000000, Wy: 0.000000},
"tcommaaccent": {GlyphName: "tcommaaccent", Wx: 333.000000, Wy: 0.000000},
"thorn": {GlyphName: "thorn", Wx: 611.000000, Wy: 0.000000},
"three": {GlyphName: "three", Wx: 556.000000, Wy: 0.000000},
"threequarters": {GlyphName: "threequarters", Wx: 834.000000, Wy: 0.000000},
"threesuperior": {GlyphName: "threesuperior", Wx: 333.000000, Wy: 0.000000},
"tilde": {GlyphName: "tilde", Wx: 333.000000, Wy: 0.000000},
"trademark": {GlyphName: "trademark", Wx: 1000.000000, Wy: 0.000000},
"two": {GlyphName: "two", Wx: 556.000000, Wy: 0.000000},
"twosuperior": {GlyphName: "twosuperior", Wx: 333.000000, Wy: 0.000000},
"u": {GlyphName: "u", Wx: 611.000000, Wy: 0.000000},
"uacute": {GlyphName: "uacute", Wx: 611.000000, Wy: 0.000000},
"ucircumflex": {GlyphName: "ucircumflex", Wx: 611.000000, Wy: 0.000000},
"udieresis": {GlyphName: "udieresis", Wx: 611.000000, Wy: 0.000000},
"ugrave": {GlyphName: "ugrave", Wx: 611.000000, Wy: 0.000000},
"uhungarumlaut": {GlyphName: "uhungarumlaut", Wx: 611.000000, Wy: 0.000000},
"umacron": {GlyphName: "umacron", Wx: 611.000000, Wy: 0.000000},
"underscore": {GlyphName: "underscore", Wx: 556.000000, Wy: 0.000000},
"uogonek": {GlyphName: "uogonek", Wx: 611.000000, Wy: 0.000000},
"uring": {GlyphName: "uring", Wx: 611.000000, Wy: 0.000000},
"v": {GlyphName: "v", Wx: 556.000000, Wy: 0.000000},
"w": {GlyphName: "w", Wx: 778.000000, Wy: 0.000000},
"x": {GlyphName: "x", Wx: 556.000000, Wy: 0.000000},
"y": {GlyphName: "y", Wx: 556.000000, Wy: 0.000000},
"yacute": {GlyphName: "yacute", Wx: 556.000000, Wy: 0.000000},
"ydieresis": {GlyphName: "ydieresis", Wx: 556.000000, Wy: 0.000000},
"yen": {GlyphName: "yen", Wx: 556.000000, Wy: 0.000000},
"z": {GlyphName: "z", Wx: 500.000000, Wy: 0.000000},
"zacute": {GlyphName: "zacute", Wx: 500.000000, Wy: 0.000000},
"zcaron": {GlyphName: "zcaron", Wx: 500.000000, Wy: 0.000000},
"zdotaccent": {GlyphName: "zdotaccent", Wx: 500.000000, Wy: 0.000000},
"zero": {GlyphName: "zero", Wx: 556.000000, Wy: 0.000000},
}

View File

@@ -0,0 +1,362 @@
package fonts
import (
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model/textencoding"
)
// Font Helvetica-BoldOblique. Implements Font interface.
// This is a built-in font and it is assumed that every reader has access to it.
type fontHelveticaBoldOblique struct {
encoder textencoding.TextEncoder
}
func NewFontHelveticaBoldOblique() fontHelveticaBoldOblique {
font := fontHelveticaBoldOblique{}
font.encoder = textencoding.NewWinAnsiTextEncoder() // Default
return font
}
func (font fontHelveticaBoldOblique) SetEncoder(encoder textencoding.TextEncoder) {
font.encoder = encoder
}
func (font fontHelveticaBoldOblique) GetGlyphCharMetrics(glyph string) (CharMetrics, bool) {
metrics, has := helveticaBoldObliqueCharMetrics[glyph]
if !has {
return metrics, false
}
return metrics, true
}
func (font fontHelveticaBoldOblique) ToPdfObject() core.PdfObject {
obj := &core.PdfIndirectObject{}
fontDict := core.MakeDict()
fontDict.Set("Type", core.MakeName("Font"))
fontDict.Set("Subtype", core.MakeName("Type1"))
fontDict.Set("BaseFont", core.MakeName("Helvetica-BoldOblique"))
fontDict.Set("Encoding", font.encoder.ToPdfObject())
obj.PdfObject = fontDict
return obj
}
var helveticaBoldObliqueCharMetrics map[string]CharMetrics = map[string]CharMetrics{
"A": {GlyphName: "A", Wx: 722.000000, Wy: 0.000000},
"AE": {GlyphName: "AE", Wx: 1000.000000, Wy: 0.000000},
"Aacute": {GlyphName: "Aacute", Wx: 722.000000, Wy: 0.000000},
"Abreve": {GlyphName: "Abreve", Wx: 722.000000, Wy: 0.000000},
"Acircumflex": {GlyphName: "Acircumflex", Wx: 722.000000, Wy: 0.000000},
"Adieresis": {GlyphName: "Adieresis", Wx: 722.000000, Wy: 0.000000},
"Agrave": {GlyphName: "Agrave", Wx: 722.000000, Wy: 0.000000},
"Amacron": {GlyphName: "Amacron", Wx: 722.000000, Wy: 0.000000},
"Aogonek": {GlyphName: "Aogonek", Wx: 722.000000, Wy: 0.000000},
"Aring": {GlyphName: "Aring", Wx: 722.000000, Wy: 0.000000},
"Atilde": {GlyphName: "Atilde", Wx: 722.000000, Wy: 0.000000},
"B": {GlyphName: "B", Wx: 722.000000, Wy: 0.000000},
"C": {GlyphName: "C", Wx: 722.000000, Wy: 0.000000},
"Cacute": {GlyphName: "Cacute", Wx: 722.000000, Wy: 0.000000},
"Ccaron": {GlyphName: "Ccaron", Wx: 722.000000, Wy: 0.000000},
"Ccedilla": {GlyphName: "Ccedilla", Wx: 722.000000, Wy: 0.000000},
"D": {GlyphName: "D", Wx: 722.000000, Wy: 0.000000},
"Dcaron": {GlyphName: "Dcaron", Wx: 722.000000, Wy: 0.000000},
"Dcroat": {GlyphName: "Dcroat", Wx: 722.000000, Wy: 0.000000},
"Delta": {GlyphName: "Delta", Wx: 612.000000, Wy: 0.000000},
"E": {GlyphName: "E", Wx: 667.000000, Wy: 0.000000},
"Eacute": {GlyphName: "Eacute", Wx: 667.000000, Wy: 0.000000},
"Ecaron": {GlyphName: "Ecaron", Wx: 667.000000, Wy: 0.000000},
"Ecircumflex": {GlyphName: "Ecircumflex", Wx: 667.000000, Wy: 0.000000},
"Edieresis": {GlyphName: "Edieresis", Wx: 667.000000, Wy: 0.000000},
"Edotaccent": {GlyphName: "Edotaccent", Wx: 667.000000, Wy: 0.000000},
"Egrave": {GlyphName: "Egrave", Wx: 667.000000, Wy: 0.000000},
"Emacron": {GlyphName: "Emacron", Wx: 667.000000, Wy: 0.000000},
"Eogonek": {GlyphName: "Eogonek", Wx: 667.000000, Wy: 0.000000},
"Eth": {GlyphName: "Eth", Wx: 722.000000, Wy: 0.000000},
"Euro": {GlyphName: "Euro", Wx: 556.000000, Wy: 0.000000},
"F": {GlyphName: "F", Wx: 611.000000, Wy: 0.000000},
"G": {GlyphName: "G", Wx: 778.000000, Wy: 0.000000},
"Gbreve": {GlyphName: "Gbreve", Wx: 778.000000, Wy: 0.000000},
"Gcommaaccent": {GlyphName: "Gcommaaccent", Wx: 778.000000, Wy: 0.000000},
"H": {GlyphName: "H", Wx: 722.000000, Wy: 0.000000},
"I": {GlyphName: "I", Wx: 278.000000, Wy: 0.000000},
"Iacute": {GlyphName: "Iacute", Wx: 278.000000, Wy: 0.000000},
"Icircumflex": {GlyphName: "Icircumflex", Wx: 278.000000, Wy: 0.000000},
"Idieresis": {GlyphName: "Idieresis", Wx: 278.000000, Wy: 0.000000},
"Idotaccent": {GlyphName: "Idotaccent", Wx: 278.000000, Wy: 0.000000},
"Igrave": {GlyphName: "Igrave", Wx: 278.000000, Wy: 0.000000},
"Imacron": {GlyphName: "Imacron", Wx: 278.000000, Wy: 0.000000},
"Iogonek": {GlyphName: "Iogonek", Wx: 278.000000, Wy: 0.000000},
"J": {GlyphName: "J", Wx: 556.000000, Wy: 0.000000},
"K": {GlyphName: "K", Wx: 722.000000, Wy: 0.000000},
"Kcommaaccent": {GlyphName: "Kcommaaccent", Wx: 722.000000, Wy: 0.000000},
"L": {GlyphName: "L", Wx: 611.000000, Wy: 0.000000},
"Lacute": {GlyphName: "Lacute", Wx: 611.000000, Wy: 0.000000},
"Lcaron": {GlyphName: "Lcaron", Wx: 611.000000, Wy: 0.000000},
"Lcommaaccent": {GlyphName: "Lcommaaccent", Wx: 611.000000, Wy: 0.000000},
"Lslash": {GlyphName: "Lslash", Wx: 611.000000, Wy: 0.000000},
"M": {GlyphName: "M", Wx: 833.000000, Wy: 0.000000},
"N": {GlyphName: "N", Wx: 722.000000, Wy: 0.000000},
"Nacute": {GlyphName: "Nacute", Wx: 722.000000, Wy: 0.000000},
"Ncaron": {GlyphName: "Ncaron", Wx: 722.000000, Wy: 0.000000},
"Ncommaaccent": {GlyphName: "Ncommaaccent", Wx: 722.000000, Wy: 0.000000},
"Ntilde": {GlyphName: "Ntilde", Wx: 722.000000, Wy: 0.000000},
"O": {GlyphName: "O", Wx: 778.000000, Wy: 0.000000},
"OE": {GlyphName: "OE", Wx: 1000.000000, Wy: 0.000000},
"Oacute": {GlyphName: "Oacute", Wx: 778.000000, Wy: 0.000000},
"Ocircumflex": {GlyphName: "Ocircumflex", Wx: 778.000000, Wy: 0.000000},
"Odieresis": {GlyphName: "Odieresis", Wx: 778.000000, Wy: 0.000000},
"Ograve": {GlyphName: "Ograve", Wx: 778.000000, Wy: 0.000000},
"Ohungarumlaut": {GlyphName: "Ohungarumlaut", Wx: 778.000000, Wy: 0.000000},
"Omacron": {GlyphName: "Omacron", Wx: 778.000000, Wy: 0.000000},
"Oslash": {GlyphName: "Oslash", Wx: 778.000000, Wy: 0.000000},
"Otilde": {GlyphName: "Otilde", Wx: 778.000000, Wy: 0.000000},
"P": {GlyphName: "P", Wx: 667.000000, Wy: 0.000000},
"Q": {GlyphName: "Q", Wx: 778.000000, Wy: 0.000000},
"R": {GlyphName: "R", Wx: 722.000000, Wy: 0.000000},
"Racute": {GlyphName: "Racute", Wx: 722.000000, Wy: 0.000000},
"Rcaron": {GlyphName: "Rcaron", Wx: 722.000000, Wy: 0.000000},
"Rcommaaccent": {GlyphName: "Rcommaaccent", Wx: 722.000000, Wy: 0.000000},
"S": {GlyphName: "S", Wx: 667.000000, Wy: 0.000000},
"Sacute": {GlyphName: "Sacute", Wx: 667.000000, Wy: 0.000000},
"Scaron": {GlyphName: "Scaron", Wx: 667.000000, Wy: 0.000000},
"Scedilla": {GlyphName: "Scedilla", Wx: 667.000000, Wy: 0.000000},
"Scommaaccent": {GlyphName: "Scommaaccent", Wx: 667.000000, Wy: 0.000000},
"T": {GlyphName: "T", Wx: 611.000000, Wy: 0.000000},
"Tcaron": {GlyphName: "Tcaron", Wx: 611.000000, Wy: 0.000000},
"Tcommaaccent": {GlyphName: "Tcommaaccent", Wx: 611.000000, Wy: 0.000000},
"Thorn": {GlyphName: "Thorn", Wx: 667.000000, Wy: 0.000000},
"U": {GlyphName: "U", Wx: 722.000000, Wy: 0.000000},
"Uacute": {GlyphName: "Uacute", Wx: 722.000000, Wy: 0.000000},
"Ucircumflex": {GlyphName: "Ucircumflex", Wx: 722.000000, Wy: 0.000000},
"Udieresis": {GlyphName: "Udieresis", Wx: 722.000000, Wy: 0.000000},
"Ugrave": {GlyphName: "Ugrave", Wx: 722.000000, Wy: 0.000000},
"Uhungarumlaut": {GlyphName: "Uhungarumlaut", Wx: 722.000000, Wy: 0.000000},
"Umacron": {GlyphName: "Umacron", Wx: 722.000000, Wy: 0.000000},
"Uogonek": {GlyphName: "Uogonek", Wx: 722.000000, Wy: 0.000000},
"Uring": {GlyphName: "Uring", Wx: 722.000000, Wy: 0.000000},
"V": {GlyphName: "V", Wx: 667.000000, Wy: 0.000000},
"W": {GlyphName: "W", Wx: 944.000000, Wy: 0.000000},
"X": {GlyphName: "X", Wx: 667.000000, Wy: 0.000000},
"Y": {GlyphName: "Y", Wx: 667.000000, Wy: 0.000000},
"Yacute": {GlyphName: "Yacute", Wx: 667.000000, Wy: 0.000000},
"Ydieresis": {GlyphName: "Ydieresis", Wx: 667.000000, Wy: 0.000000},
"Z": {GlyphName: "Z", Wx: 611.000000, Wy: 0.000000},
"Zacute": {GlyphName: "Zacute", Wx: 611.000000, Wy: 0.000000},
"Zcaron": {GlyphName: "Zcaron", Wx: 611.000000, Wy: 0.000000},
"Zdotaccent": {GlyphName: "Zdotaccent", Wx: 611.000000, Wy: 0.000000},
"a": {GlyphName: "a", Wx: 556.000000, Wy: 0.000000},
"aacute": {GlyphName: "aacute", Wx: 556.000000, Wy: 0.000000},
"abreve": {GlyphName: "abreve", Wx: 556.000000, Wy: 0.000000},
"acircumflex": {GlyphName: "acircumflex", Wx: 556.000000, Wy: 0.000000},
"acute": {GlyphName: "acute", Wx: 333.000000, Wy: 0.000000},
"adieresis": {GlyphName: "adieresis", Wx: 556.000000, Wy: 0.000000},
"ae": {GlyphName: "ae", Wx: 889.000000, Wy: 0.000000},
"agrave": {GlyphName: "agrave", Wx: 556.000000, Wy: 0.000000},
"amacron": {GlyphName: "amacron", Wx: 556.000000, Wy: 0.000000},
"ampersand": {GlyphName: "ampersand", Wx: 722.000000, Wy: 0.000000},
"aogonek": {GlyphName: "aogonek", Wx: 556.000000, Wy: 0.000000},
"aring": {GlyphName: "aring", Wx: 556.000000, Wy: 0.000000},
"asciicircum": {GlyphName: "asciicircum", Wx: 584.000000, Wy: 0.000000},
"asciitilde": {GlyphName: "asciitilde", Wx: 584.000000, Wy: 0.000000},
"asterisk": {GlyphName: "asterisk", Wx: 389.000000, Wy: 0.000000},
"at": {GlyphName: "at", Wx: 975.000000, Wy: 0.000000},
"atilde": {GlyphName: "atilde", Wx: 556.000000, Wy: 0.000000},
"b": {GlyphName: "b", Wx: 611.000000, Wy: 0.000000},
"backslash": {GlyphName: "backslash", Wx: 278.000000, Wy: 0.000000},
"bar": {GlyphName: "bar", Wx: 280.000000, Wy: 0.000000},
"braceleft": {GlyphName: "braceleft", Wx: 389.000000, Wy: 0.000000},
"braceright": {GlyphName: "braceright", Wx: 389.000000, Wy: 0.000000},
"bracketleft": {GlyphName: "bracketleft", Wx: 333.000000, Wy: 0.000000},
"bracketright": {GlyphName: "bracketright", Wx: 333.000000, Wy: 0.000000},
"breve": {GlyphName: "breve", Wx: 333.000000, Wy: 0.000000},
"brokenbar": {GlyphName: "brokenbar", Wx: 280.000000, Wy: 0.000000},
"bullet": {GlyphName: "bullet", Wx: 350.000000, Wy: 0.000000},
"c": {GlyphName: "c", Wx: 556.000000, Wy: 0.000000},
"cacute": {GlyphName: "cacute", Wx: 556.000000, Wy: 0.000000},
"caron": {GlyphName: "caron", Wx: 333.000000, Wy: 0.000000},
"ccaron": {GlyphName: "ccaron", Wx: 556.000000, Wy: 0.000000},
"ccedilla": {GlyphName: "ccedilla", Wx: 556.000000, Wy: 0.000000},
"cedilla": {GlyphName: "cedilla", Wx: 333.000000, Wy: 0.000000},
"cent": {GlyphName: "cent", Wx: 556.000000, Wy: 0.000000},
"circumflex": {GlyphName: "circumflex", Wx: 333.000000, Wy: 0.000000},
"colon": {GlyphName: "colon", Wx: 333.000000, Wy: 0.000000},
"comma": {GlyphName: "comma", Wx: 278.000000, Wy: 0.000000},
"commaaccent": {GlyphName: "commaaccent", Wx: 250.000000, Wy: 0.000000},
"copyright": {GlyphName: "copyright", Wx: 737.000000, Wy: 0.000000},
"currency": {GlyphName: "currency", Wx: 556.000000, Wy: 0.000000},
"d": {GlyphName: "d", Wx: 611.000000, Wy: 0.000000},
"dagger": {GlyphName: "dagger", Wx: 556.000000, Wy: 0.000000},
"daggerdbl": {GlyphName: "daggerdbl", Wx: 556.000000, Wy: 0.000000},
"dcaron": {GlyphName: "dcaron", Wx: 743.000000, Wy: 0.000000},
"dcroat": {GlyphName: "dcroat", Wx: 611.000000, Wy: 0.000000},
"degree": {GlyphName: "degree", Wx: 400.000000, Wy: 0.000000},
"dieresis": {GlyphName: "dieresis", Wx: 333.000000, Wy: 0.000000},
"divide": {GlyphName: "divide", Wx: 584.000000, Wy: 0.000000},
"dollar": {GlyphName: "dollar", Wx: 556.000000, Wy: 0.000000},
"dotaccent": {GlyphName: "dotaccent", Wx: 333.000000, Wy: 0.000000},
"dotlessi": {GlyphName: "dotlessi", Wx: 278.000000, Wy: 0.000000},
"e": {GlyphName: "e", Wx: 556.000000, Wy: 0.000000},
"eacute": {GlyphName: "eacute", Wx: 556.000000, Wy: 0.000000},
"ecaron": {GlyphName: "ecaron", Wx: 556.000000, Wy: 0.000000},
"ecircumflex": {GlyphName: "ecircumflex", Wx: 556.000000, Wy: 0.000000},
"edieresis": {GlyphName: "edieresis", Wx: 556.000000, Wy: 0.000000},
"edotaccent": {GlyphName: "edotaccent", Wx: 556.000000, Wy: 0.000000},
"egrave": {GlyphName: "egrave", Wx: 556.000000, Wy: 0.000000},
"eight": {GlyphName: "eight", Wx: 556.000000, Wy: 0.000000},
"ellipsis": {GlyphName: "ellipsis", Wx: 1000.000000, Wy: 0.000000},
"emacron": {GlyphName: "emacron", Wx: 556.000000, Wy: 0.000000},
"emdash": {GlyphName: "emdash", Wx: 1000.000000, Wy: 0.000000},
"endash": {GlyphName: "endash", Wx: 556.000000, Wy: 0.000000},
"eogonek": {GlyphName: "eogonek", Wx: 556.000000, Wy: 0.000000},
"equal": {GlyphName: "equal", Wx: 584.000000, Wy: 0.000000},
"eth": {GlyphName: "eth", Wx: 611.000000, Wy: 0.000000},
"exclam": {GlyphName: "exclam", Wx: 333.000000, Wy: 0.000000},
"exclamdown": {GlyphName: "exclamdown", Wx: 333.000000, Wy: 0.000000},
"f": {GlyphName: "f", Wx: 333.000000, Wy: 0.000000},
"fi": {GlyphName: "fi", Wx: 611.000000, Wy: 0.000000},
"five": {GlyphName: "five", Wx: 556.000000, Wy: 0.000000},
"fl": {GlyphName: "fl", Wx: 611.000000, Wy: 0.000000},
"florin": {GlyphName: "florin", Wx: 556.000000, Wy: 0.000000},
"four": {GlyphName: "four", Wx: 556.000000, Wy: 0.000000},
"fraction": {GlyphName: "fraction", Wx: 167.000000, Wy: 0.000000},
"g": {GlyphName: "g", Wx: 611.000000, Wy: 0.000000},
"gbreve": {GlyphName: "gbreve", Wx: 611.000000, Wy: 0.000000},
"gcommaaccent": {GlyphName: "gcommaaccent", Wx: 611.000000, Wy: 0.000000},
"germandbls": {GlyphName: "germandbls", Wx: 611.000000, Wy: 0.000000},
"grave": {GlyphName: "grave", Wx: 333.000000, Wy: 0.000000},
"greater": {GlyphName: "greater", Wx: 584.000000, Wy: 0.000000},
"greaterequal": {GlyphName: "greaterequal", Wx: 549.000000, Wy: 0.000000},
"guillemotleft": {GlyphName: "guillemotleft", Wx: 556.000000, Wy: 0.000000},
"guillemotright": {GlyphName: "guillemotright", Wx: 556.000000, Wy: 0.000000},
"guilsinglleft": {GlyphName: "guilsinglleft", Wx: 333.000000, Wy: 0.000000},
"guilsinglright": {GlyphName: "guilsinglright", Wx: 333.000000, Wy: 0.000000},
"h": {GlyphName: "h", Wx: 611.000000, Wy: 0.000000},
"hungarumlaut": {GlyphName: "hungarumlaut", Wx: 333.000000, Wy: 0.000000},
"hyphen": {GlyphName: "hyphen", Wx: 333.000000, Wy: 0.000000},
"i": {GlyphName: "i", Wx: 278.000000, Wy: 0.000000},
"iacute": {GlyphName: "iacute", Wx: 278.000000, Wy: 0.000000},
"icircumflex": {GlyphName: "icircumflex", Wx: 278.000000, Wy: 0.000000},
"idieresis": {GlyphName: "idieresis", Wx: 278.000000, Wy: 0.000000},
"igrave": {GlyphName: "igrave", Wx: 278.000000, Wy: 0.000000},
"imacron": {GlyphName: "imacron", Wx: 278.000000, Wy: 0.000000},
"iogonek": {GlyphName: "iogonek", Wx: 278.000000, Wy: 0.000000},
"j": {GlyphName: "j", Wx: 278.000000, Wy: 0.000000},
"k": {GlyphName: "k", Wx: 556.000000, Wy: 0.000000},
"kcommaaccent": {GlyphName: "kcommaaccent", Wx: 556.000000, Wy: 0.000000},
"l": {GlyphName: "l", Wx: 278.000000, Wy: 0.000000},
"lacute": {GlyphName: "lacute", Wx: 278.000000, Wy: 0.000000},
"lcaron": {GlyphName: "lcaron", Wx: 400.000000, Wy: 0.000000},
"lcommaaccent": {GlyphName: "lcommaaccent", Wx: 278.000000, Wy: 0.000000},
"less": {GlyphName: "less", Wx: 584.000000, Wy: 0.000000},
"lessequal": {GlyphName: "lessequal", Wx: 549.000000, Wy: 0.000000},
"logicalnot": {GlyphName: "logicalnot", Wx: 584.000000, Wy: 0.000000},
"lozenge": {GlyphName: "lozenge", Wx: 494.000000, Wy: 0.000000},
"lslash": {GlyphName: "lslash", Wx: 278.000000, Wy: 0.000000},
"m": {GlyphName: "m", Wx: 889.000000, Wy: 0.000000},
"macron": {GlyphName: "macron", Wx: 333.000000, Wy: 0.000000},
"minus": {GlyphName: "minus", Wx: 584.000000, Wy: 0.000000},
"mu": {GlyphName: "mu", Wx: 611.000000, Wy: 0.000000},
"multiply": {GlyphName: "multiply", Wx: 584.000000, Wy: 0.000000},
"n": {GlyphName: "n", Wx: 611.000000, Wy: 0.000000},
"nacute": {GlyphName: "nacute", Wx: 611.000000, Wy: 0.000000},
"ncaron": {GlyphName: "ncaron", Wx: 611.000000, Wy: 0.000000},
"ncommaaccent": {GlyphName: "ncommaaccent", Wx: 611.000000, Wy: 0.000000},
"nine": {GlyphName: "nine", Wx: 556.000000, Wy: 0.000000},
"notequal": {GlyphName: "notequal", Wx: 549.000000, Wy: 0.000000},
"ntilde": {GlyphName: "ntilde", Wx: 611.000000, Wy: 0.000000},
"numbersign": {GlyphName: "numbersign", Wx: 556.000000, Wy: 0.000000},
"o": {GlyphName: "o", Wx: 611.000000, Wy: 0.000000},
"oacute": {GlyphName: "oacute", Wx: 611.000000, Wy: 0.000000},
"ocircumflex": {GlyphName: "ocircumflex", Wx: 611.000000, Wy: 0.000000},
"odieresis": {GlyphName: "odieresis", Wx: 611.000000, Wy: 0.000000},
"oe": {GlyphName: "oe", Wx: 944.000000, Wy: 0.000000},
"ogonek": {GlyphName: "ogonek", Wx: 333.000000, Wy: 0.000000},
"ograve": {GlyphName: "ograve", Wx: 611.000000, Wy: 0.000000},
"ohungarumlaut": {GlyphName: "ohungarumlaut", Wx: 611.000000, Wy: 0.000000},
"omacron": {GlyphName: "omacron", Wx: 611.000000, Wy: 0.000000},
"one": {GlyphName: "one", Wx: 556.000000, Wy: 0.000000},
"onehalf": {GlyphName: "onehalf", Wx: 834.000000, Wy: 0.000000},
"onequarter": {GlyphName: "onequarter", Wx: 834.000000, Wy: 0.000000},
"onesuperior": {GlyphName: "onesuperior", Wx: 333.000000, Wy: 0.000000},
"ordfeminine": {GlyphName: "ordfeminine", Wx: 370.000000, Wy: 0.000000},
"ordmasculine": {GlyphName: "ordmasculine", Wx: 365.000000, Wy: 0.000000},
"oslash": {GlyphName: "oslash", Wx: 611.000000, Wy: 0.000000},
"otilde": {GlyphName: "otilde", Wx: 611.000000, Wy: 0.000000},
"p": {GlyphName: "p", Wx: 611.000000, Wy: 0.000000},
"paragraph": {GlyphName: "paragraph", Wx: 556.000000, Wy: 0.000000},
"parenleft": {GlyphName: "parenleft", Wx: 333.000000, Wy: 0.000000},
"parenright": {GlyphName: "parenright", Wx: 333.000000, Wy: 0.000000},
"partialdiff": {GlyphName: "partialdiff", Wx: 494.000000, Wy: 0.000000},
"percent": {GlyphName: "percent", Wx: 889.000000, Wy: 0.000000},
"period": {GlyphName: "period", Wx: 278.000000, Wy: 0.000000},
"periodcentered": {GlyphName: "periodcentered", Wx: 278.000000, Wy: 0.000000},
"perthousand": {GlyphName: "perthousand", Wx: 1000.000000, Wy: 0.000000},
"plus": {GlyphName: "plus", Wx: 584.000000, Wy: 0.000000},
"plusminus": {GlyphName: "plusminus", Wx: 584.000000, Wy: 0.000000},
"q": {GlyphName: "q", Wx: 611.000000, Wy: 0.000000},
"question": {GlyphName: "question", Wx: 611.000000, Wy: 0.000000},
"questiondown": {GlyphName: "questiondown", Wx: 611.000000, Wy: 0.000000},
"quotedbl": {GlyphName: "quotedbl", Wx: 474.000000, Wy: 0.000000},
"quotedblbase": {GlyphName: "quotedblbase", Wx: 500.000000, Wy: 0.000000},
"quotedblleft": {GlyphName: "quotedblleft", Wx: 500.000000, Wy: 0.000000},
"quotedblright": {GlyphName: "quotedblright", Wx: 500.000000, Wy: 0.000000},
"quoteleft": {GlyphName: "quoteleft", Wx: 278.000000, Wy: 0.000000},
"quoteright": {GlyphName: "quoteright", Wx: 278.000000, Wy: 0.000000},
"quotesinglbase": {GlyphName: "quotesinglbase", Wx: 278.000000, Wy: 0.000000},
"quotesingle": {GlyphName: "quotesingle", Wx: 238.000000, Wy: 0.000000},
"r": {GlyphName: "r", Wx: 389.000000, Wy: 0.000000},
"racute": {GlyphName: "racute", Wx: 389.000000, Wy: 0.000000},
"radical": {GlyphName: "radical", Wx: 549.000000, Wy: 0.000000},
"rcaron": {GlyphName: "rcaron", Wx: 389.000000, Wy: 0.000000},
"rcommaaccent": {GlyphName: "rcommaaccent", Wx: 389.000000, Wy: 0.000000},
"registered": {GlyphName: "registered", Wx: 737.000000, Wy: 0.000000},
"ring": {GlyphName: "ring", Wx: 333.000000, Wy: 0.000000},
"s": {GlyphName: "s", Wx: 556.000000, Wy: 0.000000},
"sacute": {GlyphName: "sacute", Wx: 556.000000, Wy: 0.000000},
"scaron": {GlyphName: "scaron", Wx: 556.000000, Wy: 0.000000},
"scedilla": {GlyphName: "scedilla", Wx: 556.000000, Wy: 0.000000},
"scommaaccent": {GlyphName: "scommaaccent", Wx: 556.000000, Wy: 0.000000},
"section": {GlyphName: "section", Wx: 556.000000, Wy: 0.000000},
"semicolon": {GlyphName: "semicolon", Wx: 333.000000, Wy: 0.000000},
"seven": {GlyphName: "seven", Wx: 556.000000, Wy: 0.000000},
"six": {GlyphName: "six", Wx: 556.000000, Wy: 0.000000},
"slash": {GlyphName: "slash", Wx: 278.000000, Wy: 0.000000},
"space": {GlyphName: "space", Wx: 278.000000, Wy: 0.000000},
"sterling": {GlyphName: "sterling", Wx: 556.000000, Wy: 0.000000},
"summation": {GlyphName: "summation", Wx: 600.000000, Wy: 0.000000},
"t": {GlyphName: "t", Wx: 333.000000, Wy: 0.000000},
"tcaron": {GlyphName: "tcaron", Wx: 389.000000, Wy: 0.000000},
"tcommaaccent": {GlyphName: "tcommaaccent", Wx: 333.000000, Wy: 0.000000},
"thorn": {GlyphName: "thorn", Wx: 611.000000, Wy: 0.000000},
"three": {GlyphName: "three", Wx: 556.000000, Wy: 0.000000},
"threequarters": {GlyphName: "threequarters", Wx: 834.000000, Wy: 0.000000},
"threesuperior": {GlyphName: "threesuperior", Wx: 333.000000, Wy: 0.000000},
"tilde": {GlyphName: "tilde", Wx: 333.000000, Wy: 0.000000},
"trademark": {GlyphName: "trademark", Wx: 1000.000000, Wy: 0.000000},
"two": {GlyphName: "two", Wx: 556.000000, Wy: 0.000000},
"twosuperior": {GlyphName: "twosuperior", Wx: 333.000000, Wy: 0.000000},
"u": {GlyphName: "u", Wx: 611.000000, Wy: 0.000000},
"uacute": {GlyphName: "uacute", Wx: 611.000000, Wy: 0.000000},
"ucircumflex": {GlyphName: "ucircumflex", Wx: 611.000000, Wy: 0.000000},
"udieresis": {GlyphName: "udieresis", Wx: 611.000000, Wy: 0.000000},
"ugrave": {GlyphName: "ugrave", Wx: 611.000000, Wy: 0.000000},
"uhungarumlaut": {GlyphName: "uhungarumlaut", Wx: 611.000000, Wy: 0.000000},
"umacron": {GlyphName: "umacron", Wx: 611.000000, Wy: 0.000000},
"underscore": {GlyphName: "underscore", Wx: 556.000000, Wy: 0.000000},
"uogonek": {GlyphName: "uogonek", Wx: 611.000000, Wy: 0.000000},
"uring": {GlyphName: "uring", Wx: 611.000000, Wy: 0.000000},
"v": {GlyphName: "v", Wx: 556.000000, Wy: 0.000000},
"w": {GlyphName: "w", Wx: 778.000000, Wy: 0.000000},
"x": {GlyphName: "x", Wx: 556.000000, Wy: 0.000000},
"y": {GlyphName: "y", Wx: 556.000000, Wy: 0.000000},
"yacute": {GlyphName: "yacute", Wx: 556.000000, Wy: 0.000000},
"ydieresis": {GlyphName: "ydieresis", Wx: 556.000000, Wy: 0.000000},
"yen": {GlyphName: "yen", Wx: 556.000000, Wy: 0.000000},
"z": {GlyphName: "z", Wx: 500.000000, Wy: 0.000000},
"zacute": {GlyphName: "zacute", Wx: 500.000000, Wy: 0.000000},
"zcaron": {GlyphName: "zcaron", Wx: 500.000000, Wy: 0.000000},
"zdotaccent": {GlyphName: "zdotaccent", Wx: 500.000000, Wy: 0.000000},
"zero": {GlyphName: "zero", Wx: 556.000000, Wy: 0.000000},
}

View File

@@ -0,0 +1,362 @@
package fonts
import (
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model/textencoding"
)
// Font Helvetica-Oblique. Implements Font interface.
// This is a built-in font and it is assumed that every reader has access to it.
type fontHelveticaOblique struct {
encoder textencoding.TextEncoder
}
func NewFontHelveticaOblique() fontHelveticaOblique {
font := fontHelveticaOblique{}
font.encoder = textencoding.NewWinAnsiTextEncoder() // Default
return font
}
func (font fontHelveticaOblique) SetEncoder(encoder textencoding.TextEncoder) {
font.encoder = encoder
}
func (font fontHelveticaOblique) GetGlyphCharMetrics(glyph string) (CharMetrics, bool) {
metrics, has := helveticaObliqueCharMetrics[glyph]
if !has {
return metrics, false
}
return metrics, true
}
func (font fontHelveticaOblique) ToPdfObject() core.PdfObject {
obj := &core.PdfIndirectObject{}
fontDict := core.MakeDict()
fontDict.Set("Type", core.MakeName("Font"))
fontDict.Set("Subtype", core.MakeName("Type1"))
fontDict.Set("BaseFont", core.MakeName("Helvetica-Oblique"))
fontDict.Set("Encoding", font.encoder.ToPdfObject())
obj.PdfObject = fontDict
return obj
}
var helveticaObliqueCharMetrics map[string]CharMetrics = map[string]CharMetrics{
"A": {GlyphName: "A", Wx: 667.000000, Wy: 0.000000},
"AE": {GlyphName: "AE", Wx: 1000.000000, Wy: 0.000000},
"Aacute": {GlyphName: "Aacute", Wx: 667.000000, Wy: 0.000000},
"Abreve": {GlyphName: "Abreve", Wx: 667.000000, Wy: 0.000000},
"Acircumflex": {GlyphName: "Acircumflex", Wx: 667.000000, Wy: 0.000000},
"Adieresis": {GlyphName: "Adieresis", Wx: 667.000000, Wy: 0.000000},
"Agrave": {GlyphName: "Agrave", Wx: 667.000000, Wy: 0.000000},
"Amacron": {GlyphName: "Amacron", Wx: 667.000000, Wy: 0.000000},
"Aogonek": {GlyphName: "Aogonek", Wx: 667.000000, Wy: 0.000000},
"Aring": {GlyphName: "Aring", Wx: 667.000000, Wy: 0.000000},
"Atilde": {GlyphName: "Atilde", Wx: 667.000000, Wy: 0.000000},
"B": {GlyphName: "B", Wx: 667.000000, Wy: 0.000000},
"C": {GlyphName: "C", Wx: 722.000000, Wy: 0.000000},
"Cacute": {GlyphName: "Cacute", Wx: 722.000000, Wy: 0.000000},
"Ccaron": {GlyphName: "Ccaron", Wx: 722.000000, Wy: 0.000000},
"Ccedilla": {GlyphName: "Ccedilla", Wx: 722.000000, Wy: 0.000000},
"D": {GlyphName: "D", Wx: 722.000000, Wy: 0.000000},
"Dcaron": {GlyphName: "Dcaron", Wx: 722.000000, Wy: 0.000000},
"Dcroat": {GlyphName: "Dcroat", Wx: 722.000000, Wy: 0.000000},
"Delta": {GlyphName: "Delta", Wx: 612.000000, Wy: 0.000000},
"E": {GlyphName: "E", Wx: 667.000000, Wy: 0.000000},
"Eacute": {GlyphName: "Eacute", Wx: 667.000000, Wy: 0.000000},
"Ecaron": {GlyphName: "Ecaron", Wx: 667.000000, Wy: 0.000000},
"Ecircumflex": {GlyphName: "Ecircumflex", Wx: 667.000000, Wy: 0.000000},
"Edieresis": {GlyphName: "Edieresis", Wx: 667.000000, Wy: 0.000000},
"Edotaccent": {GlyphName: "Edotaccent", Wx: 667.000000, Wy: 0.000000},
"Egrave": {GlyphName: "Egrave", Wx: 667.000000, Wy: 0.000000},
"Emacron": {GlyphName: "Emacron", Wx: 667.000000, Wy: 0.000000},
"Eogonek": {GlyphName: "Eogonek", Wx: 667.000000, Wy: 0.000000},
"Eth": {GlyphName: "Eth", Wx: 722.000000, Wy: 0.000000},
"Euro": {GlyphName: "Euro", Wx: 556.000000, Wy: 0.000000},
"F": {GlyphName: "F", Wx: 611.000000, Wy: 0.000000},
"G": {GlyphName: "G", Wx: 778.000000, Wy: 0.000000},
"Gbreve": {GlyphName: "Gbreve", Wx: 778.000000, Wy: 0.000000},
"Gcommaaccent": {GlyphName: "Gcommaaccent", Wx: 778.000000, Wy: 0.000000},
"H": {GlyphName: "H", Wx: 722.000000, Wy: 0.000000},
"I": {GlyphName: "I", Wx: 278.000000, Wy: 0.000000},
"Iacute": {GlyphName: "Iacute", Wx: 278.000000, Wy: 0.000000},
"Icircumflex": {GlyphName: "Icircumflex", Wx: 278.000000, Wy: 0.000000},
"Idieresis": {GlyphName: "Idieresis", Wx: 278.000000, Wy: 0.000000},
"Idotaccent": {GlyphName: "Idotaccent", Wx: 278.000000, Wy: 0.000000},
"Igrave": {GlyphName: "Igrave", Wx: 278.000000, Wy: 0.000000},
"Imacron": {GlyphName: "Imacron", Wx: 278.000000, Wy: 0.000000},
"Iogonek": {GlyphName: "Iogonek", Wx: 278.000000, Wy: 0.000000},
"J": {GlyphName: "J", Wx: 500.000000, Wy: 0.000000},
"K": {GlyphName: "K", Wx: 667.000000, Wy: 0.000000},
"Kcommaaccent": {GlyphName: "Kcommaaccent", Wx: 667.000000, Wy: 0.000000},
"L": {GlyphName: "L", Wx: 556.000000, Wy: 0.000000},
"Lacute": {GlyphName: "Lacute", Wx: 556.000000, Wy: 0.000000},
"Lcaron": {GlyphName: "Lcaron", Wx: 556.000000, Wy: 0.000000},
"Lcommaaccent": {GlyphName: "Lcommaaccent", Wx: 556.000000, Wy: 0.000000},
"Lslash": {GlyphName: "Lslash", Wx: 556.000000, Wy: 0.000000},
"M": {GlyphName: "M", Wx: 833.000000, Wy: 0.000000},
"N": {GlyphName: "N", Wx: 722.000000, Wy: 0.000000},
"Nacute": {GlyphName: "Nacute", Wx: 722.000000, Wy: 0.000000},
"Ncaron": {GlyphName: "Ncaron", Wx: 722.000000, Wy: 0.000000},
"Ncommaaccent": {GlyphName: "Ncommaaccent", Wx: 722.000000, Wy: 0.000000},
"Ntilde": {GlyphName: "Ntilde", Wx: 722.000000, Wy: 0.000000},
"O": {GlyphName: "O", Wx: 778.000000, Wy: 0.000000},
"OE": {GlyphName: "OE", Wx: 1000.000000, Wy: 0.000000},
"Oacute": {GlyphName: "Oacute", Wx: 778.000000, Wy: 0.000000},
"Ocircumflex": {GlyphName: "Ocircumflex", Wx: 778.000000, Wy: 0.000000},
"Odieresis": {GlyphName: "Odieresis", Wx: 778.000000, Wy: 0.000000},
"Ograve": {GlyphName: "Ograve", Wx: 778.000000, Wy: 0.000000},
"Ohungarumlaut": {GlyphName: "Ohungarumlaut", Wx: 778.000000, Wy: 0.000000},
"Omacron": {GlyphName: "Omacron", Wx: 778.000000, Wy: 0.000000},
"Oslash": {GlyphName: "Oslash", Wx: 778.000000, Wy: 0.000000},
"Otilde": {GlyphName: "Otilde", Wx: 778.000000, Wy: 0.000000},
"P": {GlyphName: "P", Wx: 667.000000, Wy: 0.000000},
"Q": {GlyphName: "Q", Wx: 778.000000, Wy: 0.000000},
"R": {GlyphName: "R", Wx: 722.000000, Wy: 0.000000},
"Racute": {GlyphName: "Racute", Wx: 722.000000, Wy: 0.000000},
"Rcaron": {GlyphName: "Rcaron", Wx: 722.000000, Wy: 0.000000},
"Rcommaaccent": {GlyphName: "Rcommaaccent", Wx: 722.000000, Wy: 0.000000},
"S": {GlyphName: "S", Wx: 667.000000, Wy: 0.000000},
"Sacute": {GlyphName: "Sacute", Wx: 667.000000, Wy: 0.000000},
"Scaron": {GlyphName: "Scaron", Wx: 667.000000, Wy: 0.000000},
"Scedilla": {GlyphName: "Scedilla", Wx: 667.000000, Wy: 0.000000},
"Scommaaccent": {GlyphName: "Scommaaccent", Wx: 667.000000, Wy: 0.000000},
"T": {GlyphName: "T", Wx: 611.000000, Wy: 0.000000},
"Tcaron": {GlyphName: "Tcaron", Wx: 611.000000, Wy: 0.000000},
"Tcommaaccent": {GlyphName: "Tcommaaccent", Wx: 611.000000, Wy: 0.000000},
"Thorn": {GlyphName: "Thorn", Wx: 667.000000, Wy: 0.000000},
"U": {GlyphName: "U", Wx: 722.000000, Wy: 0.000000},
"Uacute": {GlyphName: "Uacute", Wx: 722.000000, Wy: 0.000000},
"Ucircumflex": {GlyphName: "Ucircumflex", Wx: 722.000000, Wy: 0.000000},
"Udieresis": {GlyphName: "Udieresis", Wx: 722.000000, Wy: 0.000000},
"Ugrave": {GlyphName: "Ugrave", Wx: 722.000000, Wy: 0.000000},
"Uhungarumlaut": {GlyphName: "Uhungarumlaut", Wx: 722.000000, Wy: 0.000000},
"Umacron": {GlyphName: "Umacron", Wx: 722.000000, Wy: 0.000000},
"Uogonek": {GlyphName: "Uogonek", Wx: 722.000000, Wy: 0.000000},
"Uring": {GlyphName: "Uring", Wx: 722.000000, Wy: 0.000000},
"V": {GlyphName: "V", Wx: 667.000000, Wy: 0.000000},
"W": {GlyphName: "W", Wx: 944.000000, Wy: 0.000000},
"X": {GlyphName: "X", Wx: 667.000000, Wy: 0.000000},
"Y": {GlyphName: "Y", Wx: 667.000000, Wy: 0.000000},
"Yacute": {GlyphName: "Yacute", Wx: 667.000000, Wy: 0.000000},
"Ydieresis": {GlyphName: "Ydieresis", Wx: 667.000000, Wy: 0.000000},
"Z": {GlyphName: "Z", Wx: 611.000000, Wy: 0.000000},
"Zacute": {GlyphName: "Zacute", Wx: 611.000000, Wy: 0.000000},
"Zcaron": {GlyphName: "Zcaron", Wx: 611.000000, Wy: 0.000000},
"Zdotaccent": {GlyphName: "Zdotaccent", Wx: 611.000000, Wy: 0.000000},
"a": {GlyphName: "a", Wx: 556.000000, Wy: 0.000000},
"aacute": {GlyphName: "aacute", Wx: 556.000000, Wy: 0.000000},
"abreve": {GlyphName: "abreve", Wx: 556.000000, Wy: 0.000000},
"acircumflex": {GlyphName: "acircumflex", Wx: 556.000000, Wy: 0.000000},
"acute": {GlyphName: "acute", Wx: 333.000000, Wy: 0.000000},
"adieresis": {GlyphName: "adieresis", Wx: 556.000000, Wy: 0.000000},
"ae": {GlyphName: "ae", Wx: 889.000000, Wy: 0.000000},
"agrave": {GlyphName: "agrave", Wx: 556.000000, Wy: 0.000000},
"amacron": {GlyphName: "amacron", Wx: 556.000000, Wy: 0.000000},
"ampersand": {GlyphName: "ampersand", Wx: 667.000000, Wy: 0.000000},
"aogonek": {GlyphName: "aogonek", Wx: 556.000000, Wy: 0.000000},
"aring": {GlyphName: "aring", Wx: 556.000000, Wy: 0.000000},
"asciicircum": {GlyphName: "asciicircum", Wx: 469.000000, Wy: 0.000000},
"asciitilde": {GlyphName: "asciitilde", Wx: 584.000000, Wy: 0.000000},
"asterisk": {GlyphName: "asterisk", Wx: 389.000000, Wy: 0.000000},
"at": {GlyphName: "at", Wx: 1015.000000, Wy: 0.000000},
"atilde": {GlyphName: "atilde", Wx: 556.000000, Wy: 0.000000},
"b": {GlyphName: "b", Wx: 556.000000, Wy: 0.000000},
"backslash": {GlyphName: "backslash", Wx: 278.000000, Wy: 0.000000},
"bar": {GlyphName: "bar", Wx: 260.000000, Wy: 0.000000},
"braceleft": {GlyphName: "braceleft", Wx: 334.000000, Wy: 0.000000},
"braceright": {GlyphName: "braceright", Wx: 334.000000, Wy: 0.000000},
"bracketleft": {GlyphName: "bracketleft", Wx: 278.000000, Wy: 0.000000},
"bracketright": {GlyphName: "bracketright", Wx: 278.000000, Wy: 0.000000},
"breve": {GlyphName: "breve", Wx: 333.000000, Wy: 0.000000},
"brokenbar": {GlyphName: "brokenbar", Wx: 260.000000, Wy: 0.000000},
"bullet": {GlyphName: "bullet", Wx: 350.000000, Wy: 0.000000},
"c": {GlyphName: "c", Wx: 500.000000, Wy: 0.000000},
"cacute": {GlyphName: "cacute", Wx: 500.000000, Wy: 0.000000},
"caron": {GlyphName: "caron", Wx: 333.000000, Wy: 0.000000},
"ccaron": {GlyphName: "ccaron", Wx: 500.000000, Wy: 0.000000},
"ccedilla": {GlyphName: "ccedilla", Wx: 500.000000, Wy: 0.000000},
"cedilla": {GlyphName: "cedilla", Wx: 333.000000, Wy: 0.000000},
"cent": {GlyphName: "cent", Wx: 556.000000, Wy: 0.000000},
"circumflex": {GlyphName: "circumflex", Wx: 333.000000, Wy: 0.000000},
"colon": {GlyphName: "colon", Wx: 278.000000, Wy: 0.000000},
"comma": {GlyphName: "comma", Wx: 278.000000, Wy: 0.000000},
"commaaccent": {GlyphName: "commaaccent", Wx: 250.000000, Wy: 0.000000},
"copyright": {GlyphName: "copyright", Wx: 737.000000, Wy: 0.000000},
"currency": {GlyphName: "currency", Wx: 556.000000, Wy: 0.000000},
"d": {GlyphName: "d", Wx: 556.000000, Wy: 0.000000},
"dagger": {GlyphName: "dagger", Wx: 556.000000, Wy: 0.000000},
"daggerdbl": {GlyphName: "daggerdbl", Wx: 556.000000, Wy: 0.000000},
"dcaron": {GlyphName: "dcaron", Wx: 643.000000, Wy: 0.000000},
"dcroat": {GlyphName: "dcroat", Wx: 556.000000, Wy: 0.000000},
"degree": {GlyphName: "degree", Wx: 400.000000, Wy: 0.000000},
"dieresis": {GlyphName: "dieresis", Wx: 333.000000, Wy: 0.000000},
"divide": {GlyphName: "divide", Wx: 584.000000, Wy: 0.000000},
"dollar": {GlyphName: "dollar", Wx: 556.000000, Wy: 0.000000},
"dotaccent": {GlyphName: "dotaccent", Wx: 333.000000, Wy: 0.000000},
"dotlessi": {GlyphName: "dotlessi", Wx: 278.000000, Wy: 0.000000},
"e": {GlyphName: "e", Wx: 556.000000, Wy: 0.000000},
"eacute": {GlyphName: "eacute", Wx: 556.000000, Wy: 0.000000},
"ecaron": {GlyphName: "ecaron", Wx: 556.000000, Wy: 0.000000},
"ecircumflex": {GlyphName: "ecircumflex", Wx: 556.000000, Wy: 0.000000},
"edieresis": {GlyphName: "edieresis", Wx: 556.000000, Wy: 0.000000},
"edotaccent": {GlyphName: "edotaccent", Wx: 556.000000, Wy: 0.000000},
"egrave": {GlyphName: "egrave", Wx: 556.000000, Wy: 0.000000},
"eight": {GlyphName: "eight", Wx: 556.000000, Wy: 0.000000},
"ellipsis": {GlyphName: "ellipsis", Wx: 1000.000000, Wy: 0.000000},
"emacron": {GlyphName: "emacron", Wx: 556.000000, Wy: 0.000000},
"emdash": {GlyphName: "emdash", Wx: 1000.000000, Wy: 0.000000},
"endash": {GlyphName: "endash", Wx: 556.000000, Wy: 0.000000},
"eogonek": {GlyphName: "eogonek", Wx: 556.000000, Wy: 0.000000},
"equal": {GlyphName: "equal", Wx: 584.000000, Wy: 0.000000},
"eth": {GlyphName: "eth", Wx: 556.000000, Wy: 0.000000},
"exclam": {GlyphName: "exclam", Wx: 278.000000, Wy: 0.000000},
"exclamdown": {GlyphName: "exclamdown", Wx: 333.000000, Wy: 0.000000},
"f": {GlyphName: "f", Wx: 278.000000, Wy: 0.000000},
"fi": {GlyphName: "fi", Wx: 500.000000, Wy: 0.000000},
"five": {GlyphName: "five", Wx: 556.000000, Wy: 0.000000},
"fl": {GlyphName: "fl", Wx: 500.000000, Wy: 0.000000},
"florin": {GlyphName: "florin", Wx: 556.000000, Wy: 0.000000},
"four": {GlyphName: "four", Wx: 556.000000, Wy: 0.000000},
"fraction": {GlyphName: "fraction", Wx: 167.000000, Wy: 0.000000},
"g": {GlyphName: "g", Wx: 556.000000, Wy: 0.000000},
"gbreve": {GlyphName: "gbreve", Wx: 556.000000, Wy: 0.000000},
"gcommaaccent": {GlyphName: "gcommaaccent", Wx: 556.000000, Wy: 0.000000},
"germandbls": {GlyphName: "germandbls", Wx: 611.000000, Wy: 0.000000},
"grave": {GlyphName: "grave", Wx: 333.000000, Wy: 0.000000},
"greater": {GlyphName: "greater", Wx: 584.000000, Wy: 0.000000},
"greaterequal": {GlyphName: "greaterequal", Wx: 549.000000, Wy: 0.000000},
"guillemotleft": {GlyphName: "guillemotleft", Wx: 556.000000, Wy: 0.000000},
"guillemotright": {GlyphName: "guillemotright", Wx: 556.000000, Wy: 0.000000},
"guilsinglleft": {GlyphName: "guilsinglleft", Wx: 333.000000, Wy: 0.000000},
"guilsinglright": {GlyphName: "guilsinglright", Wx: 333.000000, Wy: 0.000000},
"h": {GlyphName: "h", Wx: 556.000000, Wy: 0.000000},
"hungarumlaut": {GlyphName: "hungarumlaut", Wx: 333.000000, Wy: 0.000000},
"hyphen": {GlyphName: "hyphen", Wx: 333.000000, Wy: 0.000000},
"i": {GlyphName: "i", Wx: 222.000000, Wy: 0.000000},
"iacute": {GlyphName: "iacute", Wx: 278.000000, Wy: 0.000000},
"icircumflex": {GlyphName: "icircumflex", Wx: 278.000000, Wy: 0.000000},
"idieresis": {GlyphName: "idieresis", Wx: 278.000000, Wy: 0.000000},
"igrave": {GlyphName: "igrave", Wx: 278.000000, Wy: 0.000000},
"imacron": {GlyphName: "imacron", Wx: 278.000000, Wy: 0.000000},
"iogonek": {GlyphName: "iogonek", Wx: 222.000000, Wy: 0.000000},
"j": {GlyphName: "j", Wx: 222.000000, Wy: 0.000000},
"k": {GlyphName: "k", Wx: 500.000000, Wy: 0.000000},
"kcommaaccent": {GlyphName: "kcommaaccent", Wx: 500.000000, Wy: 0.000000},
"l": {GlyphName: "l", Wx: 222.000000, Wy: 0.000000},
"lacute": {GlyphName: "lacute", Wx: 222.000000, Wy: 0.000000},
"lcaron": {GlyphName: "lcaron", Wx: 299.000000, Wy: 0.000000},
"lcommaaccent": {GlyphName: "lcommaaccent", Wx: 222.000000, Wy: 0.000000},
"less": {GlyphName: "less", Wx: 584.000000, Wy: 0.000000},
"lessequal": {GlyphName: "lessequal", Wx: 549.000000, Wy: 0.000000},
"logicalnot": {GlyphName: "logicalnot", Wx: 584.000000, Wy: 0.000000},
"lozenge": {GlyphName: "lozenge", Wx: 471.000000, Wy: 0.000000},
"lslash": {GlyphName: "lslash", Wx: 222.000000, Wy: 0.000000},
"m": {GlyphName: "m", Wx: 833.000000, Wy: 0.000000},
"macron": {GlyphName: "macron", Wx: 333.000000, Wy: 0.000000},
"minus": {GlyphName: "minus", Wx: 584.000000, Wy: 0.000000},
"mu": {GlyphName: "mu", Wx: 556.000000, Wy: 0.000000},
"multiply": {GlyphName: "multiply", Wx: 584.000000, Wy: 0.000000},
"n": {GlyphName: "n", Wx: 556.000000, Wy: 0.000000},
"nacute": {GlyphName: "nacute", Wx: 556.000000, Wy: 0.000000},
"ncaron": {GlyphName: "ncaron", Wx: 556.000000, Wy: 0.000000},
"ncommaaccent": {GlyphName: "ncommaaccent", Wx: 556.000000, Wy: 0.000000},
"nine": {GlyphName: "nine", Wx: 556.000000, Wy: 0.000000},
"notequal": {GlyphName: "notequal", Wx: 549.000000, Wy: 0.000000},
"ntilde": {GlyphName: "ntilde", Wx: 556.000000, Wy: 0.000000},
"numbersign": {GlyphName: "numbersign", Wx: 556.000000, Wy: 0.000000},
"o": {GlyphName: "o", Wx: 556.000000, Wy: 0.000000},
"oacute": {GlyphName: "oacute", Wx: 556.000000, Wy: 0.000000},
"ocircumflex": {GlyphName: "ocircumflex", Wx: 556.000000, Wy: 0.000000},
"odieresis": {GlyphName: "odieresis", Wx: 556.000000, Wy: 0.000000},
"oe": {GlyphName: "oe", Wx: 944.000000, Wy: 0.000000},
"ogonek": {GlyphName: "ogonek", Wx: 333.000000, Wy: 0.000000},
"ograve": {GlyphName: "ograve", Wx: 556.000000, Wy: 0.000000},
"ohungarumlaut": {GlyphName: "ohungarumlaut", Wx: 556.000000, Wy: 0.000000},
"omacron": {GlyphName: "omacron", Wx: 556.000000, Wy: 0.000000},
"one": {GlyphName: "one", Wx: 556.000000, Wy: 0.000000},
"onehalf": {GlyphName: "onehalf", Wx: 834.000000, Wy: 0.000000},
"onequarter": {GlyphName: "onequarter", Wx: 834.000000, Wy: 0.000000},
"onesuperior": {GlyphName: "onesuperior", Wx: 333.000000, Wy: 0.000000},
"ordfeminine": {GlyphName: "ordfeminine", Wx: 370.000000, Wy: 0.000000},
"ordmasculine": {GlyphName: "ordmasculine", Wx: 365.000000, Wy: 0.000000},
"oslash": {GlyphName: "oslash", Wx: 611.000000, Wy: 0.000000},
"otilde": {GlyphName: "otilde", Wx: 556.000000, Wy: 0.000000},
"p": {GlyphName: "p", Wx: 556.000000, Wy: 0.000000},
"paragraph": {GlyphName: "paragraph", Wx: 537.000000, Wy: 0.000000},
"parenleft": {GlyphName: "parenleft", Wx: 333.000000, Wy: 0.000000},
"parenright": {GlyphName: "parenright", Wx: 333.000000, Wy: 0.000000},
"partialdiff": {GlyphName: "partialdiff", Wx: 476.000000, Wy: 0.000000},
"percent": {GlyphName: "percent", Wx: 889.000000, Wy: 0.000000},
"period": {GlyphName: "period", Wx: 278.000000, Wy: 0.000000},
"periodcentered": {GlyphName: "periodcentered", Wx: 278.000000, Wy: 0.000000},
"perthousand": {GlyphName: "perthousand", Wx: 1000.000000, Wy: 0.000000},
"plus": {GlyphName: "plus", Wx: 584.000000, Wy: 0.000000},
"plusminus": {GlyphName: "plusminus", Wx: 584.000000, Wy: 0.000000},
"q": {GlyphName: "q", Wx: 556.000000, Wy: 0.000000},
"question": {GlyphName: "question", Wx: 556.000000, Wy: 0.000000},
"questiondown": {GlyphName: "questiondown", Wx: 611.000000, Wy: 0.000000},
"quotedbl": {GlyphName: "quotedbl", Wx: 355.000000, Wy: 0.000000},
"quotedblbase": {GlyphName: "quotedblbase", Wx: 333.000000, Wy: 0.000000},
"quotedblleft": {GlyphName: "quotedblleft", Wx: 333.000000, Wy: 0.000000},
"quotedblright": {GlyphName: "quotedblright", Wx: 333.000000, Wy: 0.000000},
"quoteleft": {GlyphName: "quoteleft", Wx: 222.000000, Wy: 0.000000},
"quoteright": {GlyphName: "quoteright", Wx: 222.000000, Wy: 0.000000},
"quotesinglbase": {GlyphName: "quotesinglbase", Wx: 222.000000, Wy: 0.000000},
"quotesingle": {GlyphName: "quotesingle", Wx: 191.000000, Wy: 0.000000},
"r": {GlyphName: "r", Wx: 333.000000, Wy: 0.000000},
"racute": {GlyphName: "racute", Wx: 333.000000, Wy: 0.000000},
"radical": {GlyphName: "radical", Wx: 453.000000, Wy: 0.000000},
"rcaron": {GlyphName: "rcaron", Wx: 333.000000, Wy: 0.000000},
"rcommaaccent": {GlyphName: "rcommaaccent", Wx: 333.000000, Wy: 0.000000},
"registered": {GlyphName: "registered", Wx: 737.000000, Wy: 0.000000},
"ring": {GlyphName: "ring", Wx: 333.000000, Wy: 0.000000},
"s": {GlyphName: "s", Wx: 500.000000, Wy: 0.000000},
"sacute": {GlyphName: "sacute", Wx: 500.000000, Wy: 0.000000},
"scaron": {GlyphName: "scaron", Wx: 500.000000, Wy: 0.000000},
"scedilla": {GlyphName: "scedilla", Wx: 500.000000, Wy: 0.000000},
"scommaaccent": {GlyphName: "scommaaccent", Wx: 500.000000, Wy: 0.000000},
"section": {GlyphName: "section", Wx: 556.000000, Wy: 0.000000},
"semicolon": {GlyphName: "semicolon", Wx: 278.000000, Wy: 0.000000},
"seven": {GlyphName: "seven", Wx: 556.000000, Wy: 0.000000},
"six": {GlyphName: "six", Wx: 556.000000, Wy: 0.000000},
"slash": {GlyphName: "slash", Wx: 278.000000, Wy: 0.000000},
"space": {GlyphName: "space", Wx: 278.000000, Wy: 0.000000},
"sterling": {GlyphName: "sterling", Wx: 556.000000, Wy: 0.000000},
"summation": {GlyphName: "summation", Wx: 600.000000, Wy: 0.000000},
"t": {GlyphName: "t", Wx: 278.000000, Wy: 0.000000},
"tcaron": {GlyphName: "tcaron", Wx: 317.000000, Wy: 0.000000},
"tcommaaccent": {GlyphName: "tcommaaccent", Wx: 278.000000, Wy: 0.000000},
"thorn": {GlyphName: "thorn", Wx: 556.000000, Wy: 0.000000},
"three": {GlyphName: "three", Wx: 556.000000, Wy: 0.000000},
"threequarters": {GlyphName: "threequarters", Wx: 834.000000, Wy: 0.000000},
"threesuperior": {GlyphName: "threesuperior", Wx: 333.000000, Wy: 0.000000},
"tilde": {GlyphName: "tilde", Wx: 333.000000, Wy: 0.000000},
"trademark": {GlyphName: "trademark", Wx: 1000.000000, Wy: 0.000000},
"two": {GlyphName: "two", Wx: 556.000000, Wy: 0.000000},
"twosuperior": {GlyphName: "twosuperior", Wx: 333.000000, Wy: 0.000000},
"u": {GlyphName: "u", Wx: 556.000000, Wy: 0.000000},
"uacute": {GlyphName: "uacute", Wx: 556.000000, Wy: 0.000000},
"ucircumflex": {GlyphName: "ucircumflex", Wx: 556.000000, Wy: 0.000000},
"udieresis": {GlyphName: "udieresis", Wx: 556.000000, Wy: 0.000000},
"ugrave": {GlyphName: "ugrave", Wx: 556.000000, Wy: 0.000000},
"uhungarumlaut": {GlyphName: "uhungarumlaut", Wx: 556.000000, Wy: 0.000000},
"umacron": {GlyphName: "umacron", Wx: 556.000000, Wy: 0.000000},
"underscore": {GlyphName: "underscore", Wx: 556.000000, Wy: 0.000000},
"uogonek": {GlyphName: "uogonek", Wx: 556.000000, Wy: 0.000000},
"uring": {GlyphName: "uring", Wx: 556.000000, Wy: 0.000000},
"v": {GlyphName: "v", Wx: 500.000000, Wy: 0.000000},
"w": {GlyphName: "w", Wx: 722.000000, Wy: 0.000000},
"x": {GlyphName: "x", Wx: 500.000000, Wy: 0.000000},
"y": {GlyphName: "y", Wx: 500.000000, Wy: 0.000000},
"yacute": {GlyphName: "yacute", Wx: 500.000000, Wy: 0.000000},
"ydieresis": {GlyphName: "ydieresis", Wx: 500.000000, Wy: 0.000000},
"yen": {GlyphName: "yen", Wx: 556.000000, Wy: 0.000000},
"z": {GlyphName: "z", Wx: 500.000000, Wy: 0.000000},
"zacute": {GlyphName: "zacute", Wx: 500.000000, Wy: 0.000000},
"zcaron": {GlyphName: "zcaron", Wx: 500.000000, Wy: 0.000000},
"zdotaccent": {GlyphName: "zdotaccent", Wx: 500.000000, Wy: 0.000000},
"zero": {GlyphName: "zero", Wx: 556.000000, Wy: 0.000000},
}

View File

@@ -0,0 +1,239 @@
package fonts
import (
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model/textencoding"
)
// Font Symbol. Implements Font interface.
// This is a built-in font and it is assumed that every reader has access to it.
type fontSymbol struct {
// By default encoder is not set, which means that we use the font's built in encoding.
encoder textencoding.TextEncoder
}
func NewFontSymbol() fontSymbol {
font := fontSymbol{}
return font
}
func (font fontSymbol) SetEncoder(encoder textencoding.TextEncoder) {
font.encoder = encoder
}
func (font fontSymbol) GetGlyphCharMetrics(glyph string) (CharMetrics, bool) {
metrics, has := symbolCharMetrics[glyph]
if !has {
return metrics, false
}
return metrics, true
}
func (font fontSymbol) ToPdfObject() core.PdfObject {
obj := &core.PdfIndirectObject{}
fontDict := core.MakeDict()
fontDict.Set("Type", core.MakeName("Font"))
fontDict.Set("Subtype", core.MakeName("Type1"))
fontDict.Set("BaseFont", core.MakeName("Symbol"))
if font.encoder != nil {
fontDict.Set("Encoding", font.encoder.ToPdfObject())
}
obj.PdfObject = fontDict
return obj
}
var symbolCharMetrics map[string]CharMetrics = map[string]CharMetrics{
"Alpha": {GlyphName: "Alpha", Wx: 722.000000, Wy: 0.000000},
"Beta": {GlyphName: "Beta", Wx: 667.000000, Wy: 0.000000},
"Chi": {GlyphName: "Chi", Wx: 722.000000, Wy: 0.000000},
"Delta": {GlyphName: "Delta", Wx: 612.000000, Wy: 0.000000},
"Epsilon": {GlyphName: "Epsilon", Wx: 611.000000, Wy: 0.000000},
"Eta": {GlyphName: "Eta", Wx: 722.000000, Wy: 0.000000},
"Euro": {GlyphName: "Euro", Wx: 750.000000, Wy: 0.000000},
"Gamma": {GlyphName: "Gamma", Wx: 603.000000, Wy: 0.000000},
"Ifraktur": {GlyphName: "Ifraktur", Wx: 686.000000, Wy: 0.000000},
"Iota": {GlyphName: "Iota", Wx: 333.000000, Wy: 0.000000},
"Kappa": {GlyphName: "Kappa", Wx: 722.000000, Wy: 0.000000},
"Lambda": {GlyphName: "Lambda", Wx: 686.000000, Wy: 0.000000},
"Mu": {GlyphName: "Mu", Wx: 889.000000, Wy: 0.000000},
"Nu": {GlyphName: "Nu", Wx: 722.000000, Wy: 0.000000},
"Omega": {GlyphName: "Omega", Wx: 768.000000, Wy: 0.000000},
"Omicron": {GlyphName: "Omicron", Wx: 722.000000, Wy: 0.000000},
"Phi": {GlyphName: "Phi", Wx: 763.000000, Wy: 0.000000},
"Pi": {GlyphName: "Pi", Wx: 768.000000, Wy: 0.000000},
"Psi": {GlyphName: "Psi", Wx: 795.000000, Wy: 0.000000},
"Rfraktur": {GlyphName: "Rfraktur", Wx: 795.000000, Wy: 0.000000},
"Rho": {GlyphName: "Rho", Wx: 556.000000, Wy: 0.000000},
"Sigma": {GlyphName: "Sigma", Wx: 592.000000, Wy: 0.000000},
"Tau": {GlyphName: "Tau", Wx: 611.000000, Wy: 0.000000},
"Theta": {GlyphName: "Theta", Wx: 741.000000, Wy: 0.000000},
"Upsilon": {GlyphName: "Upsilon", Wx: 690.000000, Wy: 0.000000},
"Upsilon1": {GlyphName: "Upsilon1", Wx: 620.000000, Wy: 0.000000},
"Xi": {GlyphName: "Xi", Wx: 645.000000, Wy: 0.000000},
"Zeta": {GlyphName: "Zeta", Wx: 611.000000, Wy: 0.000000},
"aleph": {GlyphName: "aleph", Wx: 823.000000, Wy: 0.000000},
"alpha": {GlyphName: "alpha", Wx: 631.000000, Wy: 0.000000},
"ampersand": {GlyphName: "ampersand", Wx: 778.000000, Wy: 0.000000},
"angle": {GlyphName: "angle", Wx: 768.000000, Wy: 0.000000},
"angleleft": {GlyphName: "angleleft", Wx: 329.000000, Wy: 0.000000},
"angleright": {GlyphName: "angleright", Wx: 329.000000, Wy: 0.000000},
"apple": {GlyphName: "apple", Wx: 790.000000, Wy: 0.000000},
"approxequal": {GlyphName: "approxequal", Wx: 549.000000, Wy: 0.000000},
"arrowboth": {GlyphName: "arrowboth", Wx: 1042.000000, Wy: 0.000000},
"arrowdblboth": {GlyphName: "arrowdblboth", Wx: 1042.000000, Wy: 0.000000},
"arrowdbldown": {GlyphName: "arrowdbldown", Wx: 603.000000, Wy: 0.000000},
"arrowdblleft": {GlyphName: "arrowdblleft", Wx: 987.000000, Wy: 0.000000},
"arrowdblright": {GlyphName: "arrowdblright", Wx: 987.000000, Wy: 0.000000},
"arrowdblup": {GlyphName: "arrowdblup", Wx: 603.000000, Wy: 0.000000},
"arrowdown": {GlyphName: "arrowdown", Wx: 603.000000, Wy: 0.000000},
"arrowhorizex": {GlyphName: "arrowhorizex", Wx: 1000.000000, Wy: 0.000000},
"arrowleft": {GlyphName: "arrowleft", Wx: 987.000000, Wy: 0.000000},
"arrowright": {GlyphName: "arrowright", Wx: 987.000000, Wy: 0.000000},
"arrowup": {GlyphName: "arrowup", Wx: 603.000000, Wy: 0.000000},
"arrowvertex": {GlyphName: "arrowvertex", Wx: 603.000000, Wy: 0.000000},
"asteriskmath": {GlyphName: "asteriskmath", Wx: 500.000000, Wy: 0.000000},
"bar": {GlyphName: "bar", Wx: 200.000000, Wy: 0.000000},
"beta": {GlyphName: "beta", Wx: 549.000000, Wy: 0.000000},
"braceex": {GlyphName: "braceex", Wx: 494.000000, Wy: 0.000000},
"braceleft": {GlyphName: "braceleft", Wx: 480.000000, Wy: 0.000000},
"braceleftbt": {GlyphName: "braceleftbt", Wx: 494.000000, Wy: 0.000000},
"braceleftmid": {GlyphName: "braceleftmid", Wx: 494.000000, Wy: 0.000000},
"bracelefttp": {GlyphName: "bracelefttp", Wx: 494.000000, Wy: 0.000000},
"braceright": {GlyphName: "braceright", Wx: 480.000000, Wy: 0.000000},
"bracerightbt": {GlyphName: "bracerightbt", Wx: 494.000000, Wy: 0.000000},
"bracerightmid": {GlyphName: "bracerightmid", Wx: 494.000000, Wy: 0.000000},
"bracerighttp": {GlyphName: "bracerighttp", Wx: 494.000000, Wy: 0.000000},
"bracketleft": {GlyphName: "bracketleft", Wx: 333.000000, Wy: 0.000000},
"bracketleftbt": {GlyphName: "bracketleftbt", Wx: 384.000000, Wy: 0.000000},
"bracketleftex": {GlyphName: "bracketleftex", Wx: 384.000000, Wy: 0.000000},
"bracketlefttp": {GlyphName: "bracketlefttp", Wx: 384.000000, Wy: 0.000000},
"bracketright": {GlyphName: "bracketright", Wx: 333.000000, Wy: 0.000000},
"bracketrightbt": {GlyphName: "bracketrightbt", Wx: 384.000000, Wy: 0.000000},
"bracketrightex": {GlyphName: "bracketrightex", Wx: 384.000000, Wy: 0.000000},
"bracketrighttp": {GlyphName: "bracketrighttp", Wx: 384.000000, Wy: 0.000000},
"bullet": {GlyphName: "bullet", Wx: 460.000000, Wy: 0.000000},
"carriagereturn": {GlyphName: "carriagereturn", Wx: 658.000000, Wy: 0.000000},
"chi": {GlyphName: "chi", Wx: 549.000000, Wy: 0.000000},
"circlemultiply": {GlyphName: "circlemultiply", Wx: 768.000000, Wy: 0.000000},
"circleplus": {GlyphName: "circleplus", Wx: 768.000000, Wy: 0.000000},
"club": {GlyphName: "club", Wx: 753.000000, Wy: 0.000000},
"colon": {GlyphName: "colon", Wx: 278.000000, Wy: 0.000000},
"comma": {GlyphName: "comma", Wx: 250.000000, Wy: 0.000000},
"congruent": {GlyphName: "congruent", Wx: 549.000000, Wy: 0.000000},
"copyrightsans": {GlyphName: "copyrightsans", Wx: 790.000000, Wy: 0.000000},
"copyrightserif": {GlyphName: "copyrightserif", Wx: 790.000000, Wy: 0.000000},
"degree": {GlyphName: "degree", Wx: 400.000000, Wy: 0.000000},
"delta": {GlyphName: "delta", Wx: 494.000000, Wy: 0.000000},
"diamond": {GlyphName: "diamond", Wx: 753.000000, Wy: 0.000000},
"divide": {GlyphName: "divide", Wx: 549.000000, Wy: 0.000000},
"dotmath": {GlyphName: "dotmath", Wx: 250.000000, Wy: 0.000000},
"eight": {GlyphName: "eight", Wx: 500.000000, Wy: 0.000000},
"element": {GlyphName: "element", Wx: 713.000000, Wy: 0.000000},
"ellipsis": {GlyphName: "ellipsis", Wx: 1000.000000, Wy: 0.000000},
"emptyset": {GlyphName: "emptyset", Wx: 823.000000, Wy: 0.000000},
"epsilon": {GlyphName: "epsilon", Wx: 439.000000, Wy: 0.000000},
"equal": {GlyphName: "equal", Wx: 549.000000, Wy: 0.000000},
"equivalence": {GlyphName: "equivalence", Wx: 549.000000, Wy: 0.000000},
"eta": {GlyphName: "eta", Wx: 603.000000, Wy: 0.000000},
"exclam": {GlyphName: "exclam", Wx: 333.000000, Wy: 0.000000},
"existential": {GlyphName: "existential", Wx: 549.000000, Wy: 0.000000},
"five": {GlyphName: "five", Wx: 500.000000, Wy: 0.000000},
"florin": {GlyphName: "florin", Wx: 500.000000, Wy: 0.000000},
"four": {GlyphName: "four", Wx: 500.000000, Wy: 0.000000},
"fraction": {GlyphName: "fraction", Wx: 167.000000, Wy: 0.000000},
"gamma": {GlyphName: "gamma", Wx: 411.000000, Wy: 0.000000},
"gradient": {GlyphName: "gradient", Wx: 713.000000, Wy: 0.000000},
"greater": {GlyphName: "greater", Wx: 549.000000, Wy: 0.000000},
"greaterequal": {GlyphName: "greaterequal", Wx: 549.000000, Wy: 0.000000},
"heart": {GlyphName: "heart", Wx: 753.000000, Wy: 0.000000},
"infinity": {GlyphName: "infinity", Wx: 713.000000, Wy: 0.000000},
"integral": {GlyphName: "integral", Wx: 274.000000, Wy: 0.000000},
"integralbt": {GlyphName: "integralbt", Wx: 686.000000, Wy: 0.000000},
"integralex": {GlyphName: "integralex", Wx: 686.000000, Wy: 0.000000},
"integraltp": {GlyphName: "integraltp", Wx: 686.000000, Wy: 0.000000},
"intersection": {GlyphName: "intersection", Wx: 768.000000, Wy: 0.000000},
"iota": {GlyphName: "iota", Wx: 329.000000, Wy: 0.000000},
"kappa": {GlyphName: "kappa", Wx: 549.000000, Wy: 0.000000},
"lambda": {GlyphName: "lambda", Wx: 549.000000, Wy: 0.000000},
"less": {GlyphName: "less", Wx: 549.000000, Wy: 0.000000},
"lessequal": {GlyphName: "lessequal", Wx: 549.000000, Wy: 0.000000},
"logicaland": {GlyphName: "logicaland", Wx: 603.000000, Wy: 0.000000},
"logicalnot": {GlyphName: "logicalnot", Wx: 713.000000, Wy: 0.000000},
"logicalor": {GlyphName: "logicalor", Wx: 603.000000, Wy: 0.000000},
"lozenge": {GlyphName: "lozenge", Wx: 494.000000, Wy: 0.000000},
"minus": {GlyphName: "minus", Wx: 549.000000, Wy: 0.000000},
"minute": {GlyphName: "minute", Wx: 247.000000, Wy: 0.000000},
"mu": {GlyphName: "mu", Wx: 576.000000, Wy: 0.000000},
"multiply": {GlyphName: "multiply", Wx: 549.000000, Wy: 0.000000},
"nine": {GlyphName: "nine", Wx: 500.000000, Wy: 0.000000},
"notelement": {GlyphName: "notelement", Wx: 713.000000, Wy: 0.000000},
"notequal": {GlyphName: "notequal", Wx: 549.000000, Wy: 0.000000},
"notsubset": {GlyphName: "notsubset", Wx: 713.000000, Wy: 0.000000},
"nu": {GlyphName: "nu", Wx: 521.000000, Wy: 0.000000},
"numbersign": {GlyphName: "numbersign", Wx: 500.000000, Wy: 0.000000},
"omega": {GlyphName: "omega", Wx: 686.000000, Wy: 0.000000},
"omega1": {GlyphName: "omega1", Wx: 713.000000, Wy: 0.000000},
"omicron": {GlyphName: "omicron", Wx: 549.000000, Wy: 0.000000},
"one": {GlyphName: "one", Wx: 500.000000, Wy: 0.000000},
"parenleft": {GlyphName: "parenleft", Wx: 333.000000, Wy: 0.000000},
"parenleftbt": {GlyphName: "parenleftbt", Wx: 384.000000, Wy: 0.000000},
"parenleftex": {GlyphName: "parenleftex", Wx: 384.000000, Wy: 0.000000},
"parenlefttp": {GlyphName: "parenlefttp", Wx: 384.000000, Wy: 0.000000},
"parenright": {GlyphName: "parenright", Wx: 333.000000, Wy: 0.000000},
"parenrightbt": {GlyphName: "parenrightbt", Wx: 384.000000, Wy: 0.000000},
"parenrightex": {GlyphName: "parenrightex", Wx: 384.000000, Wy: 0.000000},
"parenrighttp": {GlyphName: "parenrighttp", Wx: 384.000000, Wy: 0.000000},
"partialdiff": {GlyphName: "partialdiff", Wx: 494.000000, Wy: 0.000000},
"percent": {GlyphName: "percent", Wx: 833.000000, Wy: 0.000000},
"period": {GlyphName: "period", Wx: 250.000000, Wy: 0.000000},
"perpendicular": {GlyphName: "perpendicular", Wx: 658.000000, Wy: 0.000000},
"phi": {GlyphName: "phi", Wx: 521.000000, Wy: 0.000000},
"phi1": {GlyphName: "phi1", Wx: 603.000000, Wy: 0.000000},
"pi": {GlyphName: "pi", Wx: 549.000000, Wy: 0.000000},
"plus": {GlyphName: "plus", Wx: 549.000000, Wy: 0.000000},
"plusminus": {GlyphName: "plusminus", Wx: 549.000000, Wy: 0.000000},
"product": {GlyphName: "product", Wx: 823.000000, Wy: 0.000000},
"propersubset": {GlyphName: "propersubset", Wx: 713.000000, Wy: 0.000000},
"propersuperset": {GlyphName: "propersuperset", Wx: 713.000000, Wy: 0.000000},
"proportional": {GlyphName: "proportional", Wx: 713.000000, Wy: 0.000000},
"psi": {GlyphName: "psi", Wx: 686.000000, Wy: 0.000000},
"question": {GlyphName: "question", Wx: 444.000000, Wy: 0.000000},
"radical": {GlyphName: "radical", Wx: 549.000000, Wy: 0.000000},
"radicalex": {GlyphName: "radicalex", Wx: 500.000000, Wy: 0.000000},
"reflexsubset": {GlyphName: "reflexsubset", Wx: 713.000000, Wy: 0.000000},
"reflexsuperset": {GlyphName: "reflexsuperset", Wx: 713.000000, Wy: 0.000000},
"registersans": {GlyphName: "registersans", Wx: 790.000000, Wy: 0.000000},
"registerserif": {GlyphName: "registerserif", Wx: 790.000000, Wy: 0.000000},
"rho": {GlyphName: "rho", Wx: 549.000000, Wy: 0.000000},
"second": {GlyphName: "second", Wx: 411.000000, Wy: 0.000000},
"semicolon": {GlyphName: "semicolon", Wx: 278.000000, Wy: 0.000000},
"seven": {GlyphName: "seven", Wx: 500.000000, Wy: 0.000000},
"sigma": {GlyphName: "sigma", Wx: 603.000000, Wy: 0.000000},
"sigma1": {GlyphName: "sigma1", Wx: 439.000000, Wy: 0.000000},
"similar": {GlyphName: "similar", Wx: 549.000000, Wy: 0.000000},
"six": {GlyphName: "six", Wx: 500.000000, Wy: 0.000000},
"slash": {GlyphName: "slash", Wx: 278.000000, Wy: 0.000000},
"space": {GlyphName: "space", Wx: 250.000000, Wy: 0.000000},
"spade": {GlyphName: "spade", Wx: 753.000000, Wy: 0.000000},
"suchthat": {GlyphName: "suchthat", Wx: 439.000000, Wy: 0.000000},
"summation": {GlyphName: "summation", Wx: 713.000000, Wy: 0.000000},
"tau": {GlyphName: "tau", Wx: 439.000000, Wy: 0.000000},
"therefore": {GlyphName: "therefore", Wx: 863.000000, Wy: 0.000000},
"theta": {GlyphName: "theta", Wx: 521.000000, Wy: 0.000000},
"theta1": {GlyphName: "theta1", Wx: 631.000000, Wy: 0.000000},
"three": {GlyphName: "three", Wx: 500.000000, Wy: 0.000000},
"trademarksans": {GlyphName: "trademarksans", Wx: 786.000000, Wy: 0.000000},
"trademarkserif": {GlyphName: "trademarkserif", Wx: 890.000000, Wy: 0.000000},
"two": {GlyphName: "two", Wx: 500.000000, Wy: 0.000000},
"underscore": {GlyphName: "underscore", Wx: 500.000000, Wy: 0.000000},
"union": {GlyphName: "union", Wx: 768.000000, Wy: 0.000000},
"universal": {GlyphName: "universal", Wx: 713.000000, Wy: 0.000000},
"upsilon": {GlyphName: "upsilon", Wx: 576.000000, Wy: 0.000000},
"weierstrass": {GlyphName: "weierstrass", Wx: 987.000000, Wy: 0.000000},
"xi": {GlyphName: "xi", Wx: 493.000000, Wy: 0.000000},
"zero": {GlyphName: "zero", Wx: 500.000000, Wy: 0.000000},
"zeta": {GlyphName: "zeta", Wx: 494.000000, Wy: 0.000000},
}

View File

@@ -0,0 +1,362 @@
package fonts
import (
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model/textencoding"
)
// Font Times-Bold. Implements Font interface.
// This is a built-in font and it is assumed that every reader has access to it.
type fontTimesBold struct {
encoder textencoding.TextEncoder
}
func NewFontTimesBold() fontTimesBold {
font := fontTimesBold{}
font.encoder = textencoding.NewWinAnsiTextEncoder() // Default
return font
}
func (font fontTimesBold) SetEncoder(encoder textencoding.TextEncoder) {
font.encoder = encoder
}
func (font fontTimesBold) GetGlyphCharMetrics(glyph string) (CharMetrics, bool) {
metrics, has := timesBoldCharMetrics[glyph]
if !has {
return metrics, false
}
return metrics, true
}
func (font fontTimesBold) ToPdfObject() core.PdfObject {
obj := &core.PdfIndirectObject{}
fontDict := core.MakeDict()
fontDict.Set("Type", core.MakeName("Font"))
fontDict.Set("Subtype", core.MakeName("Type1"))
fontDict.Set("BaseFont", core.MakeName("Times-Bold"))
fontDict.Set("Encoding", font.encoder.ToPdfObject())
obj.PdfObject = fontDict
return obj
}
var timesBoldCharMetrics map[string]CharMetrics = map[string]CharMetrics{
"A": {GlyphName: "A", Wx: 722.000000, Wy: 0.000000},
"AE": {GlyphName: "AE", Wx: 1000.000000, Wy: 0.000000},
"Aacute": {GlyphName: "Aacute", Wx: 722.000000, Wy: 0.000000},
"Abreve": {GlyphName: "Abreve", Wx: 722.000000, Wy: 0.000000},
"Acircumflex": {GlyphName: "Acircumflex", Wx: 722.000000, Wy: 0.000000},
"Adieresis": {GlyphName: "Adieresis", Wx: 722.000000, Wy: 0.000000},
"Agrave": {GlyphName: "Agrave", Wx: 722.000000, Wy: 0.000000},
"Amacron": {GlyphName: "Amacron", Wx: 722.000000, Wy: 0.000000},
"Aogonek": {GlyphName: "Aogonek", Wx: 722.000000, Wy: 0.000000},
"Aring": {GlyphName: "Aring", Wx: 722.000000, Wy: 0.000000},
"Atilde": {GlyphName: "Atilde", Wx: 722.000000, Wy: 0.000000},
"B": {GlyphName: "B", Wx: 667.000000, Wy: 0.000000},
"C": {GlyphName: "C", Wx: 722.000000, Wy: 0.000000},
"Cacute": {GlyphName: "Cacute", Wx: 722.000000, Wy: 0.000000},
"Ccaron": {GlyphName: "Ccaron", Wx: 722.000000, Wy: 0.000000},
"Ccedilla": {GlyphName: "Ccedilla", Wx: 722.000000, Wy: 0.000000},
"D": {GlyphName: "D", Wx: 722.000000, Wy: 0.000000},
"Dcaron": {GlyphName: "Dcaron", Wx: 722.000000, Wy: 0.000000},
"Dcroat": {GlyphName: "Dcroat", Wx: 722.000000, Wy: 0.000000},
"Delta": {GlyphName: "Delta", Wx: 612.000000, Wy: 0.000000},
"E": {GlyphName: "E", Wx: 667.000000, Wy: 0.000000},
"Eacute": {GlyphName: "Eacute", Wx: 667.000000, Wy: 0.000000},
"Ecaron": {GlyphName: "Ecaron", Wx: 667.000000, Wy: 0.000000},
"Ecircumflex": {GlyphName: "Ecircumflex", Wx: 667.000000, Wy: 0.000000},
"Edieresis": {GlyphName: "Edieresis", Wx: 667.000000, Wy: 0.000000},
"Edotaccent": {GlyphName: "Edotaccent", Wx: 667.000000, Wy: 0.000000},
"Egrave": {GlyphName: "Egrave", Wx: 667.000000, Wy: 0.000000},
"Emacron": {GlyphName: "Emacron", Wx: 667.000000, Wy: 0.000000},
"Eogonek": {GlyphName: "Eogonek", Wx: 667.000000, Wy: 0.000000},
"Eth": {GlyphName: "Eth", Wx: 722.000000, Wy: 0.000000},
"Euro": {GlyphName: "Euro", Wx: 500.000000, Wy: 0.000000},
"F": {GlyphName: "F", Wx: 611.000000, Wy: 0.000000},
"G": {GlyphName: "G", Wx: 778.000000, Wy: 0.000000},
"Gbreve": {GlyphName: "Gbreve", Wx: 778.000000, Wy: 0.000000},
"Gcommaaccent": {GlyphName: "Gcommaaccent", Wx: 778.000000, Wy: 0.000000},
"H": {GlyphName: "H", Wx: 778.000000, Wy: 0.000000},
"I": {GlyphName: "I", Wx: 389.000000, Wy: 0.000000},
"Iacute": {GlyphName: "Iacute", Wx: 389.000000, Wy: 0.000000},
"Icircumflex": {GlyphName: "Icircumflex", Wx: 389.000000, Wy: 0.000000},
"Idieresis": {GlyphName: "Idieresis", Wx: 389.000000, Wy: 0.000000},
"Idotaccent": {GlyphName: "Idotaccent", Wx: 389.000000, Wy: 0.000000},
"Igrave": {GlyphName: "Igrave", Wx: 389.000000, Wy: 0.000000},
"Imacron": {GlyphName: "Imacron", Wx: 389.000000, Wy: 0.000000},
"Iogonek": {GlyphName: "Iogonek", Wx: 389.000000, Wy: 0.000000},
"J": {GlyphName: "J", Wx: 500.000000, Wy: 0.000000},
"K": {GlyphName: "K", Wx: 778.000000, Wy: 0.000000},
"Kcommaaccent": {GlyphName: "Kcommaaccent", Wx: 778.000000, Wy: 0.000000},
"L": {GlyphName: "L", Wx: 667.000000, Wy: 0.000000},
"Lacute": {GlyphName: "Lacute", Wx: 667.000000, Wy: 0.000000},
"Lcaron": {GlyphName: "Lcaron", Wx: 667.000000, Wy: 0.000000},
"Lcommaaccent": {GlyphName: "Lcommaaccent", Wx: 667.000000, Wy: 0.000000},
"Lslash": {GlyphName: "Lslash", Wx: 667.000000, Wy: 0.000000},
"M": {GlyphName: "M", Wx: 944.000000, Wy: 0.000000},
"N": {GlyphName: "N", Wx: 722.000000, Wy: 0.000000},
"Nacute": {GlyphName: "Nacute", Wx: 722.000000, Wy: 0.000000},
"Ncaron": {GlyphName: "Ncaron", Wx: 722.000000, Wy: 0.000000},
"Ncommaaccent": {GlyphName: "Ncommaaccent", Wx: 722.000000, Wy: 0.000000},
"Ntilde": {GlyphName: "Ntilde", Wx: 722.000000, Wy: 0.000000},
"O": {GlyphName: "O", Wx: 778.000000, Wy: 0.000000},
"OE": {GlyphName: "OE", Wx: 1000.000000, Wy: 0.000000},
"Oacute": {GlyphName: "Oacute", Wx: 778.000000, Wy: 0.000000},
"Ocircumflex": {GlyphName: "Ocircumflex", Wx: 778.000000, Wy: 0.000000},
"Odieresis": {GlyphName: "Odieresis", Wx: 778.000000, Wy: 0.000000},
"Ograve": {GlyphName: "Ograve", Wx: 778.000000, Wy: 0.000000},
"Ohungarumlaut": {GlyphName: "Ohungarumlaut", Wx: 778.000000, Wy: 0.000000},
"Omacron": {GlyphName: "Omacron", Wx: 778.000000, Wy: 0.000000},
"Oslash": {GlyphName: "Oslash", Wx: 778.000000, Wy: 0.000000},
"Otilde": {GlyphName: "Otilde", Wx: 778.000000, Wy: 0.000000},
"P": {GlyphName: "P", Wx: 611.000000, Wy: 0.000000},
"Q": {GlyphName: "Q", Wx: 778.000000, Wy: 0.000000},
"R": {GlyphName: "R", Wx: 722.000000, Wy: 0.000000},
"Racute": {GlyphName: "Racute", Wx: 722.000000, Wy: 0.000000},
"Rcaron": {GlyphName: "Rcaron", Wx: 722.000000, Wy: 0.000000},
"Rcommaaccent": {GlyphName: "Rcommaaccent", Wx: 722.000000, Wy: 0.000000},
"S": {GlyphName: "S", Wx: 556.000000, Wy: 0.000000},
"Sacute": {GlyphName: "Sacute", Wx: 556.000000, Wy: 0.000000},
"Scaron": {GlyphName: "Scaron", Wx: 556.000000, Wy: 0.000000},
"Scedilla": {GlyphName: "Scedilla", Wx: 556.000000, Wy: 0.000000},
"Scommaaccent": {GlyphName: "Scommaaccent", Wx: 556.000000, Wy: 0.000000},
"T": {GlyphName: "T", Wx: 667.000000, Wy: 0.000000},
"Tcaron": {GlyphName: "Tcaron", Wx: 667.000000, Wy: 0.000000},
"Tcommaaccent": {GlyphName: "Tcommaaccent", Wx: 667.000000, Wy: 0.000000},
"Thorn": {GlyphName: "Thorn", Wx: 611.000000, Wy: 0.000000},
"U": {GlyphName: "U", Wx: 722.000000, Wy: 0.000000},
"Uacute": {GlyphName: "Uacute", Wx: 722.000000, Wy: 0.000000},
"Ucircumflex": {GlyphName: "Ucircumflex", Wx: 722.000000, Wy: 0.000000},
"Udieresis": {GlyphName: "Udieresis", Wx: 722.000000, Wy: 0.000000},
"Ugrave": {GlyphName: "Ugrave", Wx: 722.000000, Wy: 0.000000},
"Uhungarumlaut": {GlyphName: "Uhungarumlaut", Wx: 722.000000, Wy: 0.000000},
"Umacron": {GlyphName: "Umacron", Wx: 722.000000, Wy: 0.000000},
"Uogonek": {GlyphName: "Uogonek", Wx: 722.000000, Wy: 0.000000},
"Uring": {GlyphName: "Uring", Wx: 722.000000, Wy: 0.000000},
"V": {GlyphName: "V", Wx: 722.000000, Wy: 0.000000},
"W": {GlyphName: "W", Wx: 1000.000000, Wy: 0.000000},
"X": {GlyphName: "X", Wx: 722.000000, Wy: 0.000000},
"Y": {GlyphName: "Y", Wx: 722.000000, Wy: 0.000000},
"Yacute": {GlyphName: "Yacute", Wx: 722.000000, Wy: 0.000000},
"Ydieresis": {GlyphName: "Ydieresis", Wx: 722.000000, Wy: 0.000000},
"Z": {GlyphName: "Z", Wx: 667.000000, Wy: 0.000000},
"Zacute": {GlyphName: "Zacute", Wx: 667.000000, Wy: 0.000000},
"Zcaron": {GlyphName: "Zcaron", Wx: 667.000000, Wy: 0.000000},
"Zdotaccent": {GlyphName: "Zdotaccent", Wx: 667.000000, Wy: 0.000000},
"a": {GlyphName: "a", Wx: 500.000000, Wy: 0.000000},
"aacute": {GlyphName: "aacute", Wx: 500.000000, Wy: 0.000000},
"abreve": {GlyphName: "abreve", Wx: 500.000000, Wy: 0.000000},
"acircumflex": {GlyphName: "acircumflex", Wx: 500.000000, Wy: 0.000000},
"acute": {GlyphName: "acute", Wx: 333.000000, Wy: 0.000000},
"adieresis": {GlyphName: "adieresis", Wx: 500.000000, Wy: 0.000000},
"ae": {GlyphName: "ae", Wx: 722.000000, Wy: 0.000000},
"agrave": {GlyphName: "agrave", Wx: 500.000000, Wy: 0.000000},
"amacron": {GlyphName: "amacron", Wx: 500.000000, Wy: 0.000000},
"ampersand": {GlyphName: "ampersand", Wx: 833.000000, Wy: 0.000000},
"aogonek": {GlyphName: "aogonek", Wx: 500.000000, Wy: 0.000000},
"aring": {GlyphName: "aring", Wx: 500.000000, Wy: 0.000000},
"asciicircum": {GlyphName: "asciicircum", Wx: 581.000000, Wy: 0.000000},
"asciitilde": {GlyphName: "asciitilde", Wx: 520.000000, Wy: 0.000000},
"asterisk": {GlyphName: "asterisk", Wx: 500.000000, Wy: 0.000000},
"at": {GlyphName: "at", Wx: 930.000000, Wy: 0.000000},
"atilde": {GlyphName: "atilde", Wx: 500.000000, Wy: 0.000000},
"b": {GlyphName: "b", Wx: 556.000000, Wy: 0.000000},
"backslash": {GlyphName: "backslash", Wx: 278.000000, Wy: 0.000000},
"bar": {GlyphName: "bar", Wx: 220.000000, Wy: 0.000000},
"braceleft": {GlyphName: "braceleft", Wx: 394.000000, Wy: 0.000000},
"braceright": {GlyphName: "braceright", Wx: 394.000000, Wy: 0.000000},
"bracketleft": {GlyphName: "bracketleft", Wx: 333.000000, Wy: 0.000000},
"bracketright": {GlyphName: "bracketright", Wx: 333.000000, Wy: 0.000000},
"breve": {GlyphName: "breve", Wx: 333.000000, Wy: 0.000000},
"brokenbar": {GlyphName: "brokenbar", Wx: 220.000000, Wy: 0.000000},
"bullet": {GlyphName: "bullet", Wx: 350.000000, Wy: 0.000000},
"c": {GlyphName: "c", Wx: 444.000000, Wy: 0.000000},
"cacute": {GlyphName: "cacute", Wx: 444.000000, Wy: 0.000000},
"caron": {GlyphName: "caron", Wx: 333.000000, Wy: 0.000000},
"ccaron": {GlyphName: "ccaron", Wx: 444.000000, Wy: 0.000000},
"ccedilla": {GlyphName: "ccedilla", Wx: 444.000000, Wy: 0.000000},
"cedilla": {GlyphName: "cedilla", Wx: 333.000000, Wy: 0.000000},
"cent": {GlyphName: "cent", Wx: 500.000000, Wy: 0.000000},
"circumflex": {GlyphName: "circumflex", Wx: 333.000000, Wy: 0.000000},
"colon": {GlyphName: "colon", Wx: 333.000000, Wy: 0.000000},
"comma": {GlyphName: "comma", Wx: 250.000000, Wy: 0.000000},
"commaaccent": {GlyphName: "commaaccent", Wx: 250.000000, Wy: 0.000000},
"copyright": {GlyphName: "copyright", Wx: 747.000000, Wy: 0.000000},
"currency": {GlyphName: "currency", Wx: 500.000000, Wy: 0.000000},
"d": {GlyphName: "d", Wx: 556.000000, Wy: 0.000000},
"dagger": {GlyphName: "dagger", Wx: 500.000000, Wy: 0.000000},
"daggerdbl": {GlyphName: "daggerdbl", Wx: 500.000000, Wy: 0.000000},
"dcaron": {GlyphName: "dcaron", Wx: 672.000000, Wy: 0.000000},
"dcroat": {GlyphName: "dcroat", Wx: 556.000000, Wy: 0.000000},
"degree": {GlyphName: "degree", Wx: 400.000000, Wy: 0.000000},
"dieresis": {GlyphName: "dieresis", Wx: 333.000000, Wy: 0.000000},
"divide": {GlyphName: "divide", Wx: 570.000000, Wy: 0.000000},
"dollar": {GlyphName: "dollar", Wx: 500.000000, Wy: 0.000000},
"dotaccent": {GlyphName: "dotaccent", Wx: 333.000000, Wy: 0.000000},
"dotlessi": {GlyphName: "dotlessi", Wx: 278.000000, Wy: 0.000000},
"e": {GlyphName: "e", Wx: 444.000000, Wy: 0.000000},
"eacute": {GlyphName: "eacute", Wx: 444.000000, Wy: 0.000000},
"ecaron": {GlyphName: "ecaron", Wx: 444.000000, Wy: 0.000000},
"ecircumflex": {GlyphName: "ecircumflex", Wx: 444.000000, Wy: 0.000000},
"edieresis": {GlyphName: "edieresis", Wx: 444.000000, Wy: 0.000000},
"edotaccent": {GlyphName: "edotaccent", Wx: 444.000000, Wy: 0.000000},
"egrave": {GlyphName: "egrave", Wx: 444.000000, Wy: 0.000000},
"eight": {GlyphName: "eight", Wx: 500.000000, Wy: 0.000000},
"ellipsis": {GlyphName: "ellipsis", Wx: 1000.000000, Wy: 0.000000},
"emacron": {GlyphName: "emacron", Wx: 444.000000, Wy: 0.000000},
"emdash": {GlyphName: "emdash", Wx: 1000.000000, Wy: 0.000000},
"endash": {GlyphName: "endash", Wx: 500.000000, Wy: 0.000000},
"eogonek": {GlyphName: "eogonek", Wx: 444.000000, Wy: 0.000000},
"equal": {GlyphName: "equal", Wx: 570.000000, Wy: 0.000000},
"eth": {GlyphName: "eth", Wx: 500.000000, Wy: 0.000000},
"exclam": {GlyphName: "exclam", Wx: 333.000000, Wy: 0.000000},
"exclamdown": {GlyphName: "exclamdown", Wx: 333.000000, Wy: 0.000000},
"f": {GlyphName: "f", Wx: 333.000000, Wy: 0.000000},
"fi": {GlyphName: "fi", Wx: 556.000000, Wy: 0.000000},
"five": {GlyphName: "five", Wx: 500.000000, Wy: 0.000000},
"fl": {GlyphName: "fl", Wx: 556.000000, Wy: 0.000000},
"florin": {GlyphName: "florin", Wx: 500.000000, Wy: 0.000000},
"four": {GlyphName: "four", Wx: 500.000000, Wy: 0.000000},
"fraction": {GlyphName: "fraction", Wx: 167.000000, Wy: 0.000000},
"g": {GlyphName: "g", Wx: 500.000000, Wy: 0.000000},
"gbreve": {GlyphName: "gbreve", Wx: 500.000000, Wy: 0.000000},
"gcommaaccent": {GlyphName: "gcommaaccent", Wx: 500.000000, Wy: 0.000000},
"germandbls": {GlyphName: "germandbls", Wx: 556.000000, Wy: 0.000000},
"grave": {GlyphName: "grave", Wx: 333.000000, Wy: 0.000000},
"greater": {GlyphName: "greater", Wx: 570.000000, Wy: 0.000000},
"greaterequal": {GlyphName: "greaterequal", Wx: 549.000000, Wy: 0.000000},
"guillemotleft": {GlyphName: "guillemotleft", Wx: 500.000000, Wy: 0.000000},
"guillemotright": {GlyphName: "guillemotright", Wx: 500.000000, Wy: 0.000000},
"guilsinglleft": {GlyphName: "guilsinglleft", Wx: 333.000000, Wy: 0.000000},
"guilsinglright": {GlyphName: "guilsinglright", Wx: 333.000000, Wy: 0.000000},
"h": {GlyphName: "h", Wx: 556.000000, Wy: 0.000000},
"hungarumlaut": {GlyphName: "hungarumlaut", Wx: 333.000000, Wy: 0.000000},
"hyphen": {GlyphName: "hyphen", Wx: 333.000000, Wy: 0.000000},
"i": {GlyphName: "i", Wx: 278.000000, Wy: 0.000000},
"iacute": {GlyphName: "iacute", Wx: 278.000000, Wy: 0.000000},
"icircumflex": {GlyphName: "icircumflex", Wx: 278.000000, Wy: 0.000000},
"idieresis": {GlyphName: "idieresis", Wx: 278.000000, Wy: 0.000000},
"igrave": {GlyphName: "igrave", Wx: 278.000000, Wy: 0.000000},
"imacron": {GlyphName: "imacron", Wx: 278.000000, Wy: 0.000000},
"iogonek": {GlyphName: "iogonek", Wx: 278.000000, Wy: 0.000000},
"j": {GlyphName: "j", Wx: 333.000000, Wy: 0.000000},
"k": {GlyphName: "k", Wx: 556.000000, Wy: 0.000000},
"kcommaaccent": {GlyphName: "kcommaaccent", Wx: 556.000000, Wy: 0.000000},
"l": {GlyphName: "l", Wx: 278.000000, Wy: 0.000000},
"lacute": {GlyphName: "lacute", Wx: 278.000000, Wy: 0.000000},
"lcaron": {GlyphName: "lcaron", Wx: 394.000000, Wy: 0.000000},
"lcommaaccent": {GlyphName: "lcommaaccent", Wx: 278.000000, Wy: 0.000000},
"less": {GlyphName: "less", Wx: 570.000000, Wy: 0.000000},
"lessequal": {GlyphName: "lessequal", Wx: 549.000000, Wy: 0.000000},
"logicalnot": {GlyphName: "logicalnot", Wx: 570.000000, Wy: 0.000000},
"lozenge": {GlyphName: "lozenge", Wx: 494.000000, Wy: 0.000000},
"lslash": {GlyphName: "lslash", Wx: 278.000000, Wy: 0.000000},
"m": {GlyphName: "m", Wx: 833.000000, Wy: 0.000000},
"macron": {GlyphName: "macron", Wx: 333.000000, Wy: 0.000000},
"minus": {GlyphName: "minus", Wx: 570.000000, Wy: 0.000000},
"mu": {GlyphName: "mu", Wx: 556.000000, Wy: 0.000000},
"multiply": {GlyphName: "multiply", Wx: 570.000000, Wy: 0.000000},
"n": {GlyphName: "n", Wx: 556.000000, Wy: 0.000000},
"nacute": {GlyphName: "nacute", Wx: 556.000000, Wy: 0.000000},
"ncaron": {GlyphName: "ncaron", Wx: 556.000000, Wy: 0.000000},
"ncommaaccent": {GlyphName: "ncommaaccent", Wx: 556.000000, Wy: 0.000000},
"nine": {GlyphName: "nine", Wx: 500.000000, Wy: 0.000000},
"notequal": {GlyphName: "notequal", Wx: 549.000000, Wy: 0.000000},
"ntilde": {GlyphName: "ntilde", Wx: 556.000000, Wy: 0.000000},
"numbersign": {GlyphName: "numbersign", Wx: 500.000000, Wy: 0.000000},
"o": {GlyphName: "o", Wx: 500.000000, Wy: 0.000000},
"oacute": {GlyphName: "oacute", Wx: 500.000000, Wy: 0.000000},
"ocircumflex": {GlyphName: "ocircumflex", Wx: 500.000000, Wy: 0.000000},
"odieresis": {GlyphName: "odieresis", Wx: 500.000000, Wy: 0.000000},
"oe": {GlyphName: "oe", Wx: 722.000000, Wy: 0.000000},
"ogonek": {GlyphName: "ogonek", Wx: 333.000000, Wy: 0.000000},
"ograve": {GlyphName: "ograve", Wx: 500.000000, Wy: 0.000000},
"ohungarumlaut": {GlyphName: "ohungarumlaut", Wx: 500.000000, Wy: 0.000000},
"omacron": {GlyphName: "omacron", Wx: 500.000000, Wy: 0.000000},
"one": {GlyphName: "one", Wx: 500.000000, Wy: 0.000000},
"onehalf": {GlyphName: "onehalf", Wx: 750.000000, Wy: 0.000000},
"onequarter": {GlyphName: "onequarter", Wx: 750.000000, Wy: 0.000000},
"onesuperior": {GlyphName: "onesuperior", Wx: 300.000000, Wy: 0.000000},
"ordfeminine": {GlyphName: "ordfeminine", Wx: 300.000000, Wy: 0.000000},
"ordmasculine": {GlyphName: "ordmasculine", Wx: 330.000000, Wy: 0.000000},
"oslash": {GlyphName: "oslash", Wx: 500.000000, Wy: 0.000000},
"otilde": {GlyphName: "otilde", Wx: 500.000000, Wy: 0.000000},
"p": {GlyphName: "p", Wx: 556.000000, Wy: 0.000000},
"paragraph": {GlyphName: "paragraph", Wx: 540.000000, Wy: 0.000000},
"parenleft": {GlyphName: "parenleft", Wx: 333.000000, Wy: 0.000000},
"parenright": {GlyphName: "parenright", Wx: 333.000000, Wy: 0.000000},
"partialdiff": {GlyphName: "partialdiff", Wx: 494.000000, Wy: 0.000000},
"percent": {GlyphName: "percent", Wx: 1000.000000, Wy: 0.000000},
"period": {GlyphName: "period", Wx: 250.000000, Wy: 0.000000},
"periodcentered": {GlyphName: "periodcentered", Wx: 250.000000, Wy: 0.000000},
"perthousand": {GlyphName: "perthousand", Wx: 1000.000000, Wy: 0.000000},
"plus": {GlyphName: "plus", Wx: 570.000000, Wy: 0.000000},
"plusminus": {GlyphName: "plusminus", Wx: 570.000000, Wy: 0.000000},
"q": {GlyphName: "q", Wx: 556.000000, Wy: 0.000000},
"question": {GlyphName: "question", Wx: 500.000000, Wy: 0.000000},
"questiondown": {GlyphName: "questiondown", Wx: 500.000000, Wy: 0.000000},
"quotedbl": {GlyphName: "quotedbl", Wx: 555.000000, Wy: 0.000000},
"quotedblbase": {GlyphName: "quotedblbase", Wx: 500.000000, Wy: 0.000000},
"quotedblleft": {GlyphName: "quotedblleft", Wx: 500.000000, Wy: 0.000000},
"quotedblright": {GlyphName: "quotedblright", Wx: 500.000000, Wy: 0.000000},
"quoteleft": {GlyphName: "quoteleft", Wx: 333.000000, Wy: 0.000000},
"quoteright": {GlyphName: "quoteright", Wx: 333.000000, Wy: 0.000000},
"quotesinglbase": {GlyphName: "quotesinglbase", Wx: 333.000000, Wy: 0.000000},
"quotesingle": {GlyphName: "quotesingle", Wx: 278.000000, Wy: 0.000000},
"r": {GlyphName: "r", Wx: 444.000000, Wy: 0.000000},
"racute": {GlyphName: "racute", Wx: 444.000000, Wy: 0.000000},
"radical": {GlyphName: "radical", Wx: 549.000000, Wy: 0.000000},
"rcaron": {GlyphName: "rcaron", Wx: 444.000000, Wy: 0.000000},
"rcommaaccent": {GlyphName: "rcommaaccent", Wx: 444.000000, Wy: 0.000000},
"registered": {GlyphName: "registered", Wx: 747.000000, Wy: 0.000000},
"ring": {GlyphName: "ring", Wx: 333.000000, Wy: 0.000000},
"s": {GlyphName: "s", Wx: 389.000000, Wy: 0.000000},
"sacute": {GlyphName: "sacute", Wx: 389.000000, Wy: 0.000000},
"scaron": {GlyphName: "scaron", Wx: 389.000000, Wy: 0.000000},
"scedilla": {GlyphName: "scedilla", Wx: 389.000000, Wy: 0.000000},
"scommaaccent": {GlyphName: "scommaaccent", Wx: 389.000000, Wy: 0.000000},
"section": {GlyphName: "section", Wx: 500.000000, Wy: 0.000000},
"semicolon": {GlyphName: "semicolon", Wx: 333.000000, Wy: 0.000000},
"seven": {GlyphName: "seven", Wx: 500.000000, Wy: 0.000000},
"six": {GlyphName: "six", Wx: 500.000000, Wy: 0.000000},
"slash": {GlyphName: "slash", Wx: 278.000000, Wy: 0.000000},
"space": {GlyphName: "space", Wx: 250.000000, Wy: 0.000000},
"sterling": {GlyphName: "sterling", Wx: 500.000000, Wy: 0.000000},
"summation": {GlyphName: "summation", Wx: 600.000000, Wy: 0.000000},
"t": {GlyphName: "t", Wx: 333.000000, Wy: 0.000000},
"tcaron": {GlyphName: "tcaron", Wx: 416.000000, Wy: 0.000000},
"tcommaaccent": {GlyphName: "tcommaaccent", Wx: 333.000000, Wy: 0.000000},
"thorn": {GlyphName: "thorn", Wx: 556.000000, Wy: 0.000000},
"three": {GlyphName: "three", Wx: 500.000000, Wy: 0.000000},
"threequarters": {GlyphName: "threequarters", Wx: 750.000000, Wy: 0.000000},
"threesuperior": {GlyphName: "threesuperior", Wx: 300.000000, Wy: 0.000000},
"tilde": {GlyphName: "tilde", Wx: 333.000000, Wy: 0.000000},
"trademark": {GlyphName: "trademark", Wx: 1000.000000, Wy: 0.000000},
"two": {GlyphName: "two", Wx: 500.000000, Wy: 0.000000},
"twosuperior": {GlyphName: "twosuperior", Wx: 300.000000, Wy: 0.000000},
"u": {GlyphName: "u", Wx: 556.000000, Wy: 0.000000},
"uacute": {GlyphName: "uacute", Wx: 556.000000, Wy: 0.000000},
"ucircumflex": {GlyphName: "ucircumflex", Wx: 556.000000, Wy: 0.000000},
"udieresis": {GlyphName: "udieresis", Wx: 556.000000, Wy: 0.000000},
"ugrave": {GlyphName: "ugrave", Wx: 556.000000, Wy: 0.000000},
"uhungarumlaut": {GlyphName: "uhungarumlaut", Wx: 556.000000, Wy: 0.000000},
"umacron": {GlyphName: "umacron", Wx: 556.000000, Wy: 0.000000},
"underscore": {GlyphName: "underscore", Wx: 500.000000, Wy: 0.000000},
"uogonek": {GlyphName: "uogonek", Wx: 556.000000, Wy: 0.000000},
"uring": {GlyphName: "uring", Wx: 556.000000, Wy: 0.000000},
"v": {GlyphName: "v", Wx: 500.000000, Wy: 0.000000},
"w": {GlyphName: "w", Wx: 722.000000, Wy: 0.000000},
"x": {GlyphName: "x", Wx: 500.000000, Wy: 0.000000},
"y": {GlyphName: "y", Wx: 500.000000, Wy: 0.000000},
"yacute": {GlyphName: "yacute", Wx: 500.000000, Wy: 0.000000},
"ydieresis": {GlyphName: "ydieresis", Wx: 500.000000, Wy: 0.000000},
"yen": {GlyphName: "yen", Wx: 500.000000, Wy: 0.000000},
"z": {GlyphName: "z", Wx: 444.000000, Wy: 0.000000},
"zacute": {GlyphName: "zacute", Wx: 444.000000, Wy: 0.000000},
"zcaron": {GlyphName: "zcaron", Wx: 444.000000, Wy: 0.000000},
"zdotaccent": {GlyphName: "zdotaccent", Wx: 444.000000, Wy: 0.000000},
"zero": {GlyphName: "zero", Wx: 500.000000, Wy: 0.000000},
}

View File

@@ -0,0 +1,362 @@
package fonts
import (
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model/textencoding"
)
// Font Times-BoldItalic. Implements Font interface.
// This is a built-in font and it is assumed that every reader has access to it.
type fontTimesBoldItalic struct {
encoder textencoding.TextEncoder
}
func NewFontTimesBoldItalic() fontTimesBoldItalic {
font := fontTimesBoldItalic{}
font.encoder = textencoding.NewWinAnsiTextEncoder() // Default
return font
}
func (font fontTimesBoldItalic) SetEncoder(encoder textencoding.TextEncoder) {
font.encoder = encoder
}
func (font fontTimesBoldItalic) GetGlyphCharMetrics(glyph string) (CharMetrics, bool) {
metrics, has := timesBoldItalicCharMetrics[glyph]
if !has {
return metrics, false
}
return metrics, true
}
func (font fontTimesBoldItalic) ToPdfObject() core.PdfObject {
obj := &core.PdfIndirectObject{}
fontDict := core.MakeDict()
fontDict.Set("Type", core.MakeName("Font"))
fontDict.Set("Subtype", core.MakeName("Type1"))
fontDict.Set("BaseFont", core.MakeName("Times-BoldItalic"))
fontDict.Set("Encoding", font.encoder.ToPdfObject())
obj.PdfObject = fontDict
return obj
}
var timesBoldItalicCharMetrics map[string]CharMetrics = map[string]CharMetrics{
"A": {GlyphName: "A", Wx: 667.000000, Wy: 0.000000},
"AE": {GlyphName: "AE", Wx: 944.000000, Wy: 0.000000},
"Aacute": {GlyphName: "Aacute", Wx: 667.000000, Wy: 0.000000},
"Abreve": {GlyphName: "Abreve", Wx: 667.000000, Wy: 0.000000},
"Acircumflex": {GlyphName: "Acircumflex", Wx: 667.000000, Wy: 0.000000},
"Adieresis": {GlyphName: "Adieresis", Wx: 667.000000, Wy: 0.000000},
"Agrave": {GlyphName: "Agrave", Wx: 667.000000, Wy: 0.000000},
"Amacron": {GlyphName: "Amacron", Wx: 667.000000, Wy: 0.000000},
"Aogonek": {GlyphName: "Aogonek", Wx: 667.000000, Wy: 0.000000},
"Aring": {GlyphName: "Aring", Wx: 667.000000, Wy: 0.000000},
"Atilde": {GlyphName: "Atilde", Wx: 667.000000, Wy: 0.000000},
"B": {GlyphName: "B", Wx: 667.000000, Wy: 0.000000},
"C": {GlyphName: "C", Wx: 667.000000, Wy: 0.000000},
"Cacute": {GlyphName: "Cacute", Wx: 667.000000, Wy: 0.000000},
"Ccaron": {GlyphName: "Ccaron", Wx: 667.000000, Wy: 0.000000},
"Ccedilla": {GlyphName: "Ccedilla", Wx: 667.000000, Wy: 0.000000},
"D": {GlyphName: "D", Wx: 722.000000, Wy: 0.000000},
"Dcaron": {GlyphName: "Dcaron", Wx: 722.000000, Wy: 0.000000},
"Dcroat": {GlyphName: "Dcroat", Wx: 722.000000, Wy: 0.000000},
"Delta": {GlyphName: "Delta", Wx: 612.000000, Wy: 0.000000},
"E": {GlyphName: "E", Wx: 667.000000, Wy: 0.000000},
"Eacute": {GlyphName: "Eacute", Wx: 667.000000, Wy: 0.000000},
"Ecaron": {GlyphName: "Ecaron", Wx: 667.000000, Wy: 0.000000},
"Ecircumflex": {GlyphName: "Ecircumflex", Wx: 667.000000, Wy: 0.000000},
"Edieresis": {GlyphName: "Edieresis", Wx: 667.000000, Wy: 0.000000},
"Edotaccent": {GlyphName: "Edotaccent", Wx: 667.000000, Wy: 0.000000},
"Egrave": {GlyphName: "Egrave", Wx: 667.000000, Wy: 0.000000},
"Emacron": {GlyphName: "Emacron", Wx: 667.000000, Wy: 0.000000},
"Eogonek": {GlyphName: "Eogonek", Wx: 667.000000, Wy: 0.000000},
"Eth": {GlyphName: "Eth", Wx: 722.000000, Wy: 0.000000},
"Euro": {GlyphName: "Euro", Wx: 500.000000, Wy: 0.000000},
"F": {GlyphName: "F", Wx: 667.000000, Wy: 0.000000},
"G": {GlyphName: "G", Wx: 722.000000, Wy: 0.000000},
"Gbreve": {GlyphName: "Gbreve", Wx: 722.000000, Wy: 0.000000},
"Gcommaaccent": {GlyphName: "Gcommaaccent", Wx: 722.000000, Wy: 0.000000},
"H": {GlyphName: "H", Wx: 778.000000, Wy: 0.000000},
"I": {GlyphName: "I", Wx: 389.000000, Wy: 0.000000},
"Iacute": {GlyphName: "Iacute", Wx: 389.000000, Wy: 0.000000},
"Icircumflex": {GlyphName: "Icircumflex", Wx: 389.000000, Wy: 0.000000},
"Idieresis": {GlyphName: "Idieresis", Wx: 389.000000, Wy: 0.000000},
"Idotaccent": {GlyphName: "Idotaccent", Wx: 389.000000, Wy: 0.000000},
"Igrave": {GlyphName: "Igrave", Wx: 389.000000, Wy: 0.000000},
"Imacron": {GlyphName: "Imacron", Wx: 389.000000, Wy: 0.000000},
"Iogonek": {GlyphName: "Iogonek", Wx: 389.000000, Wy: 0.000000},
"J": {GlyphName: "J", Wx: 500.000000, Wy: 0.000000},
"K": {GlyphName: "K", Wx: 667.000000, Wy: 0.000000},
"Kcommaaccent": {GlyphName: "Kcommaaccent", Wx: 667.000000, Wy: 0.000000},
"L": {GlyphName: "L", Wx: 611.000000, Wy: 0.000000},
"Lacute": {GlyphName: "Lacute", Wx: 611.000000, Wy: 0.000000},
"Lcaron": {GlyphName: "Lcaron", Wx: 611.000000, Wy: 0.000000},
"Lcommaaccent": {GlyphName: "Lcommaaccent", Wx: 611.000000, Wy: 0.000000},
"Lslash": {GlyphName: "Lslash", Wx: 611.000000, Wy: 0.000000},
"M": {GlyphName: "M", Wx: 889.000000, Wy: 0.000000},
"N": {GlyphName: "N", Wx: 722.000000, Wy: 0.000000},
"Nacute": {GlyphName: "Nacute", Wx: 722.000000, Wy: 0.000000},
"Ncaron": {GlyphName: "Ncaron", Wx: 722.000000, Wy: 0.000000},
"Ncommaaccent": {GlyphName: "Ncommaaccent", Wx: 722.000000, Wy: 0.000000},
"Ntilde": {GlyphName: "Ntilde", Wx: 722.000000, Wy: 0.000000},
"O": {GlyphName: "O", Wx: 722.000000, Wy: 0.000000},
"OE": {GlyphName: "OE", Wx: 944.000000, Wy: 0.000000},
"Oacute": {GlyphName: "Oacute", Wx: 722.000000, Wy: 0.000000},
"Ocircumflex": {GlyphName: "Ocircumflex", Wx: 722.000000, Wy: 0.000000},
"Odieresis": {GlyphName: "Odieresis", Wx: 722.000000, Wy: 0.000000},
"Ograve": {GlyphName: "Ograve", Wx: 722.000000, Wy: 0.000000},
"Ohungarumlaut": {GlyphName: "Ohungarumlaut", Wx: 722.000000, Wy: 0.000000},
"Omacron": {GlyphName: "Omacron", Wx: 722.000000, Wy: 0.000000},
"Oslash": {GlyphName: "Oslash", Wx: 722.000000, Wy: 0.000000},
"Otilde": {GlyphName: "Otilde", Wx: 722.000000, Wy: 0.000000},
"P": {GlyphName: "P", Wx: 611.000000, Wy: 0.000000},
"Q": {GlyphName: "Q", Wx: 722.000000, Wy: 0.000000},
"R": {GlyphName: "R", Wx: 667.000000, Wy: 0.000000},
"Racute": {GlyphName: "Racute", Wx: 667.000000, Wy: 0.000000},
"Rcaron": {GlyphName: "Rcaron", Wx: 667.000000, Wy: 0.000000},
"Rcommaaccent": {GlyphName: "Rcommaaccent", Wx: 667.000000, Wy: 0.000000},
"S": {GlyphName: "S", Wx: 556.000000, Wy: 0.000000},
"Sacute": {GlyphName: "Sacute", Wx: 556.000000, Wy: 0.000000},
"Scaron": {GlyphName: "Scaron", Wx: 556.000000, Wy: 0.000000},
"Scedilla": {GlyphName: "Scedilla", Wx: 556.000000, Wy: 0.000000},
"Scommaaccent": {GlyphName: "Scommaaccent", Wx: 556.000000, Wy: 0.000000},
"T": {GlyphName: "T", Wx: 611.000000, Wy: 0.000000},
"Tcaron": {GlyphName: "Tcaron", Wx: 611.000000, Wy: 0.000000},
"Tcommaaccent": {GlyphName: "Tcommaaccent", Wx: 611.000000, Wy: 0.000000},
"Thorn": {GlyphName: "Thorn", Wx: 611.000000, Wy: 0.000000},
"U": {GlyphName: "U", Wx: 722.000000, Wy: 0.000000},
"Uacute": {GlyphName: "Uacute", Wx: 722.000000, Wy: 0.000000},
"Ucircumflex": {GlyphName: "Ucircumflex", Wx: 722.000000, Wy: 0.000000},
"Udieresis": {GlyphName: "Udieresis", Wx: 722.000000, Wy: 0.000000},
"Ugrave": {GlyphName: "Ugrave", Wx: 722.000000, Wy: 0.000000},
"Uhungarumlaut": {GlyphName: "Uhungarumlaut", Wx: 722.000000, Wy: 0.000000},
"Umacron": {GlyphName: "Umacron", Wx: 722.000000, Wy: 0.000000},
"Uogonek": {GlyphName: "Uogonek", Wx: 722.000000, Wy: 0.000000},
"Uring": {GlyphName: "Uring", Wx: 722.000000, Wy: 0.000000},
"V": {GlyphName: "V", Wx: 667.000000, Wy: 0.000000},
"W": {GlyphName: "W", Wx: 889.000000, Wy: 0.000000},
"X": {GlyphName: "X", Wx: 667.000000, Wy: 0.000000},
"Y": {GlyphName: "Y", Wx: 611.000000, Wy: 0.000000},
"Yacute": {GlyphName: "Yacute", Wx: 611.000000, Wy: 0.000000},
"Ydieresis": {GlyphName: "Ydieresis", Wx: 611.000000, Wy: 0.000000},
"Z": {GlyphName: "Z", Wx: 611.000000, Wy: 0.000000},
"Zacute": {GlyphName: "Zacute", Wx: 611.000000, Wy: 0.000000},
"Zcaron": {GlyphName: "Zcaron", Wx: 611.000000, Wy: 0.000000},
"Zdotaccent": {GlyphName: "Zdotaccent", Wx: 611.000000, Wy: 0.000000},
"a": {GlyphName: "a", Wx: 500.000000, Wy: 0.000000},
"aacute": {GlyphName: "aacute", Wx: 500.000000, Wy: 0.000000},
"abreve": {GlyphName: "abreve", Wx: 500.000000, Wy: 0.000000},
"acircumflex": {GlyphName: "acircumflex", Wx: 500.000000, Wy: 0.000000},
"acute": {GlyphName: "acute", Wx: 333.000000, Wy: 0.000000},
"adieresis": {GlyphName: "adieresis", Wx: 500.000000, Wy: 0.000000},
"ae": {GlyphName: "ae", Wx: 722.000000, Wy: 0.000000},
"agrave": {GlyphName: "agrave", Wx: 500.000000, Wy: 0.000000},
"amacron": {GlyphName: "amacron", Wx: 500.000000, Wy: 0.000000},
"ampersand": {GlyphName: "ampersand", Wx: 778.000000, Wy: 0.000000},
"aogonek": {GlyphName: "aogonek", Wx: 500.000000, Wy: 0.000000},
"aring": {GlyphName: "aring", Wx: 500.000000, Wy: 0.000000},
"asciicircum": {GlyphName: "asciicircum", Wx: 570.000000, Wy: 0.000000},
"asciitilde": {GlyphName: "asciitilde", Wx: 570.000000, Wy: 0.000000},
"asterisk": {GlyphName: "asterisk", Wx: 500.000000, Wy: 0.000000},
"at": {GlyphName: "at", Wx: 832.000000, Wy: 0.000000},
"atilde": {GlyphName: "atilde", Wx: 500.000000, Wy: 0.000000},
"b": {GlyphName: "b", Wx: 500.000000, Wy: 0.000000},
"backslash": {GlyphName: "backslash", Wx: 278.000000, Wy: 0.000000},
"bar": {GlyphName: "bar", Wx: 220.000000, Wy: 0.000000},
"braceleft": {GlyphName: "braceleft", Wx: 348.000000, Wy: 0.000000},
"braceright": {GlyphName: "braceright", Wx: 348.000000, Wy: 0.000000},
"bracketleft": {GlyphName: "bracketleft", Wx: 333.000000, Wy: 0.000000},
"bracketright": {GlyphName: "bracketright", Wx: 333.000000, Wy: 0.000000},
"breve": {GlyphName: "breve", Wx: 333.000000, Wy: 0.000000},
"brokenbar": {GlyphName: "brokenbar", Wx: 220.000000, Wy: 0.000000},
"bullet": {GlyphName: "bullet", Wx: 350.000000, Wy: 0.000000},
"c": {GlyphName: "c", Wx: 444.000000, Wy: 0.000000},
"cacute": {GlyphName: "cacute", Wx: 444.000000, Wy: 0.000000},
"caron": {GlyphName: "caron", Wx: 333.000000, Wy: 0.000000},
"ccaron": {GlyphName: "ccaron", Wx: 444.000000, Wy: 0.000000},
"ccedilla": {GlyphName: "ccedilla", Wx: 444.000000, Wy: 0.000000},
"cedilla": {GlyphName: "cedilla", Wx: 333.000000, Wy: 0.000000},
"cent": {GlyphName: "cent", Wx: 500.000000, Wy: 0.000000},
"circumflex": {GlyphName: "circumflex", Wx: 333.000000, Wy: 0.000000},
"colon": {GlyphName: "colon", Wx: 333.000000, Wy: 0.000000},
"comma": {GlyphName: "comma", Wx: 250.000000, Wy: 0.000000},
"commaaccent": {GlyphName: "commaaccent", Wx: 250.000000, Wy: 0.000000},
"copyright": {GlyphName: "copyright", Wx: 747.000000, Wy: 0.000000},
"currency": {GlyphName: "currency", Wx: 500.000000, Wy: 0.000000},
"d": {GlyphName: "d", Wx: 500.000000, Wy: 0.000000},
"dagger": {GlyphName: "dagger", Wx: 500.000000, Wy: 0.000000},
"daggerdbl": {GlyphName: "daggerdbl", Wx: 500.000000, Wy: 0.000000},
"dcaron": {GlyphName: "dcaron", Wx: 608.000000, Wy: 0.000000},
"dcroat": {GlyphName: "dcroat", Wx: 500.000000, Wy: 0.000000},
"degree": {GlyphName: "degree", Wx: 400.000000, Wy: 0.000000},
"dieresis": {GlyphName: "dieresis", Wx: 333.000000, Wy: 0.000000},
"divide": {GlyphName: "divide", Wx: 570.000000, Wy: 0.000000},
"dollar": {GlyphName: "dollar", Wx: 500.000000, Wy: 0.000000},
"dotaccent": {GlyphName: "dotaccent", Wx: 333.000000, Wy: 0.000000},
"dotlessi": {GlyphName: "dotlessi", Wx: 278.000000, Wy: 0.000000},
"e": {GlyphName: "e", Wx: 444.000000, Wy: 0.000000},
"eacute": {GlyphName: "eacute", Wx: 444.000000, Wy: 0.000000},
"ecaron": {GlyphName: "ecaron", Wx: 444.000000, Wy: 0.000000},
"ecircumflex": {GlyphName: "ecircumflex", Wx: 444.000000, Wy: 0.000000},
"edieresis": {GlyphName: "edieresis", Wx: 444.000000, Wy: 0.000000},
"edotaccent": {GlyphName: "edotaccent", Wx: 444.000000, Wy: 0.000000},
"egrave": {GlyphName: "egrave", Wx: 444.000000, Wy: 0.000000},
"eight": {GlyphName: "eight", Wx: 500.000000, Wy: 0.000000},
"ellipsis": {GlyphName: "ellipsis", Wx: 1000.000000, Wy: 0.000000},
"emacron": {GlyphName: "emacron", Wx: 444.000000, Wy: 0.000000},
"emdash": {GlyphName: "emdash", Wx: 1000.000000, Wy: 0.000000},
"endash": {GlyphName: "endash", Wx: 500.000000, Wy: 0.000000},
"eogonek": {GlyphName: "eogonek", Wx: 444.000000, Wy: 0.000000},
"equal": {GlyphName: "equal", Wx: 570.000000, Wy: 0.000000},
"eth": {GlyphName: "eth", Wx: 500.000000, Wy: 0.000000},
"exclam": {GlyphName: "exclam", Wx: 389.000000, Wy: 0.000000},
"exclamdown": {GlyphName: "exclamdown", Wx: 389.000000, Wy: 0.000000},
"f": {GlyphName: "f", Wx: 333.000000, Wy: 0.000000},
"fi": {GlyphName: "fi", Wx: 556.000000, Wy: 0.000000},
"five": {GlyphName: "five", Wx: 500.000000, Wy: 0.000000},
"fl": {GlyphName: "fl", Wx: 556.000000, Wy: 0.000000},
"florin": {GlyphName: "florin", Wx: 500.000000, Wy: 0.000000},
"four": {GlyphName: "four", Wx: 500.000000, Wy: 0.000000},
"fraction": {GlyphName: "fraction", Wx: 167.000000, Wy: 0.000000},
"g": {GlyphName: "g", Wx: 500.000000, Wy: 0.000000},
"gbreve": {GlyphName: "gbreve", Wx: 500.000000, Wy: 0.000000},
"gcommaaccent": {GlyphName: "gcommaaccent", Wx: 500.000000, Wy: 0.000000},
"germandbls": {GlyphName: "germandbls", Wx: 500.000000, Wy: 0.000000},
"grave": {GlyphName: "grave", Wx: 333.000000, Wy: 0.000000},
"greater": {GlyphName: "greater", Wx: 570.000000, Wy: 0.000000},
"greaterequal": {GlyphName: "greaterequal", Wx: 549.000000, Wy: 0.000000},
"guillemotleft": {GlyphName: "guillemotleft", Wx: 500.000000, Wy: 0.000000},
"guillemotright": {GlyphName: "guillemotright", Wx: 500.000000, Wy: 0.000000},
"guilsinglleft": {GlyphName: "guilsinglleft", Wx: 333.000000, Wy: 0.000000},
"guilsinglright": {GlyphName: "guilsinglright", Wx: 333.000000, Wy: 0.000000},
"h": {GlyphName: "h", Wx: 556.000000, Wy: 0.000000},
"hungarumlaut": {GlyphName: "hungarumlaut", Wx: 333.000000, Wy: 0.000000},
"hyphen": {GlyphName: "hyphen", Wx: 333.000000, Wy: 0.000000},
"i": {GlyphName: "i", Wx: 278.000000, Wy: 0.000000},
"iacute": {GlyphName: "iacute", Wx: 278.000000, Wy: 0.000000},
"icircumflex": {GlyphName: "icircumflex", Wx: 278.000000, Wy: 0.000000},
"idieresis": {GlyphName: "idieresis", Wx: 278.000000, Wy: 0.000000},
"igrave": {GlyphName: "igrave", Wx: 278.000000, Wy: 0.000000},
"imacron": {GlyphName: "imacron", Wx: 278.000000, Wy: 0.000000},
"iogonek": {GlyphName: "iogonek", Wx: 278.000000, Wy: 0.000000},
"j": {GlyphName: "j", Wx: 278.000000, Wy: 0.000000},
"k": {GlyphName: "k", Wx: 500.000000, Wy: 0.000000},
"kcommaaccent": {GlyphName: "kcommaaccent", Wx: 500.000000, Wy: 0.000000},
"l": {GlyphName: "l", Wx: 278.000000, Wy: 0.000000},
"lacute": {GlyphName: "lacute", Wx: 278.000000, Wy: 0.000000},
"lcaron": {GlyphName: "lcaron", Wx: 382.000000, Wy: 0.000000},
"lcommaaccent": {GlyphName: "lcommaaccent", Wx: 278.000000, Wy: 0.000000},
"less": {GlyphName: "less", Wx: 570.000000, Wy: 0.000000},
"lessequal": {GlyphName: "lessequal", Wx: 549.000000, Wy: 0.000000},
"logicalnot": {GlyphName: "logicalnot", Wx: 606.000000, Wy: 0.000000},
"lozenge": {GlyphName: "lozenge", Wx: 494.000000, Wy: 0.000000},
"lslash": {GlyphName: "lslash", Wx: 278.000000, Wy: 0.000000},
"m": {GlyphName: "m", Wx: 778.000000, Wy: 0.000000},
"macron": {GlyphName: "macron", Wx: 333.000000, Wy: 0.000000},
"minus": {GlyphName: "minus", Wx: 606.000000, Wy: 0.000000},
"mu": {GlyphName: "mu", Wx: 576.000000, Wy: 0.000000},
"multiply": {GlyphName: "multiply", Wx: 570.000000, Wy: 0.000000},
"n": {GlyphName: "n", Wx: 556.000000, Wy: 0.000000},
"nacute": {GlyphName: "nacute", Wx: 556.000000, Wy: 0.000000},
"ncaron": {GlyphName: "ncaron", Wx: 556.000000, Wy: 0.000000},
"ncommaaccent": {GlyphName: "ncommaaccent", Wx: 556.000000, Wy: 0.000000},
"nine": {GlyphName: "nine", Wx: 500.000000, Wy: 0.000000},
"notequal": {GlyphName: "notequal", Wx: 549.000000, Wy: 0.000000},
"ntilde": {GlyphName: "ntilde", Wx: 556.000000, Wy: 0.000000},
"numbersign": {GlyphName: "numbersign", Wx: 500.000000, Wy: 0.000000},
"o": {GlyphName: "o", Wx: 500.000000, Wy: 0.000000},
"oacute": {GlyphName: "oacute", Wx: 500.000000, Wy: 0.000000},
"ocircumflex": {GlyphName: "ocircumflex", Wx: 500.000000, Wy: 0.000000},
"odieresis": {GlyphName: "odieresis", Wx: 500.000000, Wy: 0.000000},
"oe": {GlyphName: "oe", Wx: 722.000000, Wy: 0.000000},
"ogonek": {GlyphName: "ogonek", Wx: 333.000000, Wy: 0.000000},
"ograve": {GlyphName: "ograve", Wx: 500.000000, Wy: 0.000000},
"ohungarumlaut": {GlyphName: "ohungarumlaut", Wx: 500.000000, Wy: 0.000000},
"omacron": {GlyphName: "omacron", Wx: 500.000000, Wy: 0.000000},
"one": {GlyphName: "one", Wx: 500.000000, Wy: 0.000000},
"onehalf": {GlyphName: "onehalf", Wx: 750.000000, Wy: 0.000000},
"onequarter": {GlyphName: "onequarter", Wx: 750.000000, Wy: 0.000000},
"onesuperior": {GlyphName: "onesuperior", Wx: 300.000000, Wy: 0.000000},
"ordfeminine": {GlyphName: "ordfeminine", Wx: 266.000000, Wy: 0.000000},
"ordmasculine": {GlyphName: "ordmasculine", Wx: 300.000000, Wy: 0.000000},
"oslash": {GlyphName: "oslash", Wx: 500.000000, Wy: 0.000000},
"otilde": {GlyphName: "otilde", Wx: 500.000000, Wy: 0.000000},
"p": {GlyphName: "p", Wx: 500.000000, Wy: 0.000000},
"paragraph": {GlyphName: "paragraph", Wx: 500.000000, Wy: 0.000000},
"parenleft": {GlyphName: "parenleft", Wx: 333.000000, Wy: 0.000000},
"parenright": {GlyphName: "parenright", Wx: 333.000000, Wy: 0.000000},
"partialdiff": {GlyphName: "partialdiff", Wx: 494.000000, Wy: 0.000000},
"percent": {GlyphName: "percent", Wx: 833.000000, Wy: 0.000000},
"period": {GlyphName: "period", Wx: 250.000000, Wy: 0.000000},
"periodcentered": {GlyphName: "periodcentered", Wx: 250.000000, Wy: 0.000000},
"perthousand": {GlyphName: "perthousand", Wx: 1000.000000, Wy: 0.000000},
"plus": {GlyphName: "plus", Wx: 570.000000, Wy: 0.000000},
"plusminus": {GlyphName: "plusminus", Wx: 570.000000, Wy: 0.000000},
"q": {GlyphName: "q", Wx: 500.000000, Wy: 0.000000},
"question": {GlyphName: "question", Wx: 500.000000, Wy: 0.000000},
"questiondown": {GlyphName: "questiondown", Wx: 500.000000, Wy: 0.000000},
"quotedbl": {GlyphName: "quotedbl", Wx: 555.000000, Wy: 0.000000},
"quotedblbase": {GlyphName: "quotedblbase", Wx: 500.000000, Wy: 0.000000},
"quotedblleft": {GlyphName: "quotedblleft", Wx: 500.000000, Wy: 0.000000},
"quotedblright": {GlyphName: "quotedblright", Wx: 500.000000, Wy: 0.000000},
"quoteleft": {GlyphName: "quoteleft", Wx: 333.000000, Wy: 0.000000},
"quoteright": {GlyphName: "quoteright", Wx: 333.000000, Wy: 0.000000},
"quotesinglbase": {GlyphName: "quotesinglbase", Wx: 333.000000, Wy: 0.000000},
"quotesingle": {GlyphName: "quotesingle", Wx: 278.000000, Wy: 0.000000},
"r": {GlyphName: "r", Wx: 389.000000, Wy: 0.000000},
"racute": {GlyphName: "racute", Wx: 389.000000, Wy: 0.000000},
"radical": {GlyphName: "radical", Wx: 549.000000, Wy: 0.000000},
"rcaron": {GlyphName: "rcaron", Wx: 389.000000, Wy: 0.000000},
"rcommaaccent": {GlyphName: "rcommaaccent", Wx: 389.000000, Wy: 0.000000},
"registered": {GlyphName: "registered", Wx: 747.000000, Wy: 0.000000},
"ring": {GlyphName: "ring", Wx: 333.000000, Wy: 0.000000},
"s": {GlyphName: "s", Wx: 389.000000, Wy: 0.000000},
"sacute": {GlyphName: "sacute", Wx: 389.000000, Wy: 0.000000},
"scaron": {GlyphName: "scaron", Wx: 389.000000, Wy: 0.000000},
"scedilla": {GlyphName: "scedilla", Wx: 389.000000, Wy: 0.000000},
"scommaaccent": {GlyphName: "scommaaccent", Wx: 389.000000, Wy: 0.000000},
"section": {GlyphName: "section", Wx: 500.000000, Wy: 0.000000},
"semicolon": {GlyphName: "semicolon", Wx: 333.000000, Wy: 0.000000},
"seven": {GlyphName: "seven", Wx: 500.000000, Wy: 0.000000},
"six": {GlyphName: "six", Wx: 500.000000, Wy: 0.000000},
"slash": {GlyphName: "slash", Wx: 278.000000, Wy: 0.000000},
"space": {GlyphName: "space", Wx: 250.000000, Wy: 0.000000},
"sterling": {GlyphName: "sterling", Wx: 500.000000, Wy: 0.000000},
"summation": {GlyphName: "summation", Wx: 600.000000, Wy: 0.000000},
"t": {GlyphName: "t", Wx: 278.000000, Wy: 0.000000},
"tcaron": {GlyphName: "tcaron", Wx: 366.000000, Wy: 0.000000},
"tcommaaccent": {GlyphName: "tcommaaccent", Wx: 278.000000, Wy: 0.000000},
"thorn": {GlyphName: "thorn", Wx: 500.000000, Wy: 0.000000},
"three": {GlyphName: "three", Wx: 500.000000, Wy: 0.000000},
"threequarters": {GlyphName: "threequarters", Wx: 750.000000, Wy: 0.000000},
"threesuperior": {GlyphName: "threesuperior", Wx: 300.000000, Wy: 0.000000},
"tilde": {GlyphName: "tilde", Wx: 333.000000, Wy: 0.000000},
"trademark": {GlyphName: "trademark", Wx: 1000.000000, Wy: 0.000000},
"two": {GlyphName: "two", Wx: 500.000000, Wy: 0.000000},
"twosuperior": {GlyphName: "twosuperior", Wx: 300.000000, Wy: 0.000000},
"u": {GlyphName: "u", Wx: 556.000000, Wy: 0.000000},
"uacute": {GlyphName: "uacute", Wx: 556.000000, Wy: 0.000000},
"ucircumflex": {GlyphName: "ucircumflex", Wx: 556.000000, Wy: 0.000000},
"udieresis": {GlyphName: "udieresis", Wx: 556.000000, Wy: 0.000000},
"ugrave": {GlyphName: "ugrave", Wx: 556.000000, Wy: 0.000000},
"uhungarumlaut": {GlyphName: "uhungarumlaut", Wx: 556.000000, Wy: 0.000000},
"umacron": {GlyphName: "umacron", Wx: 556.000000, Wy: 0.000000},
"underscore": {GlyphName: "underscore", Wx: 500.000000, Wy: 0.000000},
"uogonek": {GlyphName: "uogonek", Wx: 556.000000, Wy: 0.000000},
"uring": {GlyphName: "uring", Wx: 556.000000, Wy: 0.000000},
"v": {GlyphName: "v", Wx: 444.000000, Wy: 0.000000},
"w": {GlyphName: "w", Wx: 667.000000, Wy: 0.000000},
"x": {GlyphName: "x", Wx: 500.000000, Wy: 0.000000},
"y": {GlyphName: "y", Wx: 444.000000, Wy: 0.000000},
"yacute": {GlyphName: "yacute", Wx: 444.000000, Wy: 0.000000},
"ydieresis": {GlyphName: "ydieresis", Wx: 444.000000, Wy: 0.000000},
"yen": {GlyphName: "yen", Wx: 500.000000, Wy: 0.000000},
"z": {GlyphName: "z", Wx: 389.000000, Wy: 0.000000},
"zacute": {GlyphName: "zacute", Wx: 389.000000, Wy: 0.000000},
"zcaron": {GlyphName: "zcaron", Wx: 389.000000, Wy: 0.000000},
"zdotaccent": {GlyphName: "zdotaccent", Wx: 389.000000, Wy: 0.000000},
"zero": {GlyphName: "zero", Wx: 500.000000, Wy: 0.000000},
}

View File

@@ -0,0 +1,362 @@
package fonts
import (
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model/textencoding"
)
// Font Times-Italic. Implements Font interface.
// This is a built-in font and it is assumed that every reader has access to it.
type fontTimesItalic struct {
encoder textencoding.TextEncoder
}
func NewFontTimesItalic() fontTimesItalic {
font := fontTimesItalic{}
font.encoder = textencoding.NewWinAnsiTextEncoder() // Default
return font
}
func (font fontTimesItalic) SetEncoder(encoder textencoding.TextEncoder) {
font.encoder = encoder
}
func (font fontTimesItalic) GetGlyphCharMetrics(glyph string) (CharMetrics, bool) {
metrics, has := timesItalicCharMetrics[glyph]
if !has {
return metrics, false
}
return metrics, true
}
func (font fontTimesItalic) ToPdfObject() core.PdfObject {
obj := &core.PdfIndirectObject{}
fontDict := core.MakeDict()
fontDict.Set("Type", core.MakeName("Font"))
fontDict.Set("Subtype", core.MakeName("Type1"))
fontDict.Set("BaseFont", core.MakeName("Times-Italic"))
fontDict.Set("Encoding", font.encoder.ToPdfObject())
obj.PdfObject = fontDict
return obj
}
var timesItalicCharMetrics map[string]CharMetrics = map[string]CharMetrics{
"A": {GlyphName: "A", Wx: 611.000000, Wy: 0.000000},
"AE": {GlyphName: "AE", Wx: 889.000000, Wy: 0.000000},
"Aacute": {GlyphName: "Aacute", Wx: 611.000000, Wy: 0.000000},
"Abreve": {GlyphName: "Abreve", Wx: 611.000000, Wy: 0.000000},
"Acircumflex": {GlyphName: "Acircumflex", Wx: 611.000000, Wy: 0.000000},
"Adieresis": {GlyphName: "Adieresis", Wx: 611.000000, Wy: 0.000000},
"Agrave": {GlyphName: "Agrave", Wx: 611.000000, Wy: 0.000000},
"Amacron": {GlyphName: "Amacron", Wx: 611.000000, Wy: 0.000000},
"Aogonek": {GlyphName: "Aogonek", Wx: 611.000000, Wy: 0.000000},
"Aring": {GlyphName: "Aring", Wx: 611.000000, Wy: 0.000000},
"Atilde": {GlyphName: "Atilde", Wx: 611.000000, Wy: 0.000000},
"B": {GlyphName: "B", Wx: 611.000000, Wy: 0.000000},
"C": {GlyphName: "C", Wx: 667.000000, Wy: 0.000000},
"Cacute": {GlyphName: "Cacute", Wx: 667.000000, Wy: 0.000000},
"Ccaron": {GlyphName: "Ccaron", Wx: 667.000000, Wy: 0.000000},
"Ccedilla": {GlyphName: "Ccedilla", Wx: 667.000000, Wy: 0.000000},
"D": {GlyphName: "D", Wx: 722.000000, Wy: 0.000000},
"Dcaron": {GlyphName: "Dcaron", Wx: 722.000000, Wy: 0.000000},
"Dcroat": {GlyphName: "Dcroat", Wx: 722.000000, Wy: 0.000000},
"Delta": {GlyphName: "Delta", Wx: 612.000000, Wy: 0.000000},
"E": {GlyphName: "E", Wx: 611.000000, Wy: 0.000000},
"Eacute": {GlyphName: "Eacute", Wx: 611.000000, Wy: 0.000000},
"Ecaron": {GlyphName: "Ecaron", Wx: 611.000000, Wy: 0.000000},
"Ecircumflex": {GlyphName: "Ecircumflex", Wx: 611.000000, Wy: 0.000000},
"Edieresis": {GlyphName: "Edieresis", Wx: 611.000000, Wy: 0.000000},
"Edotaccent": {GlyphName: "Edotaccent", Wx: 611.000000, Wy: 0.000000},
"Egrave": {GlyphName: "Egrave", Wx: 611.000000, Wy: 0.000000},
"Emacron": {GlyphName: "Emacron", Wx: 611.000000, Wy: 0.000000},
"Eogonek": {GlyphName: "Eogonek", Wx: 611.000000, Wy: 0.000000},
"Eth": {GlyphName: "Eth", Wx: 722.000000, Wy: 0.000000},
"Euro": {GlyphName: "Euro", Wx: 500.000000, Wy: 0.000000},
"F": {GlyphName: "F", Wx: 611.000000, Wy: 0.000000},
"G": {GlyphName: "G", Wx: 722.000000, Wy: 0.000000},
"Gbreve": {GlyphName: "Gbreve", Wx: 722.000000, Wy: 0.000000},
"Gcommaaccent": {GlyphName: "Gcommaaccent", Wx: 722.000000, Wy: 0.000000},
"H": {GlyphName: "H", Wx: 722.000000, Wy: 0.000000},
"I": {GlyphName: "I", Wx: 333.000000, Wy: 0.000000},
"Iacute": {GlyphName: "Iacute", Wx: 333.000000, Wy: 0.000000},
"Icircumflex": {GlyphName: "Icircumflex", Wx: 333.000000, Wy: 0.000000},
"Idieresis": {GlyphName: "Idieresis", Wx: 333.000000, Wy: 0.000000},
"Idotaccent": {GlyphName: "Idotaccent", Wx: 333.000000, Wy: 0.000000},
"Igrave": {GlyphName: "Igrave", Wx: 333.000000, Wy: 0.000000},
"Imacron": {GlyphName: "Imacron", Wx: 333.000000, Wy: 0.000000},
"Iogonek": {GlyphName: "Iogonek", Wx: 333.000000, Wy: 0.000000},
"J": {GlyphName: "J", Wx: 444.000000, Wy: 0.000000},
"K": {GlyphName: "K", Wx: 667.000000, Wy: 0.000000},
"Kcommaaccent": {GlyphName: "Kcommaaccent", Wx: 667.000000, Wy: 0.000000},
"L": {GlyphName: "L", Wx: 556.000000, Wy: 0.000000},
"Lacute": {GlyphName: "Lacute", Wx: 556.000000, Wy: 0.000000},
"Lcaron": {GlyphName: "Lcaron", Wx: 611.000000, Wy: 0.000000},
"Lcommaaccent": {GlyphName: "Lcommaaccent", Wx: 556.000000, Wy: 0.000000},
"Lslash": {GlyphName: "Lslash", Wx: 556.000000, Wy: 0.000000},
"M": {GlyphName: "M", Wx: 833.000000, Wy: 0.000000},
"N": {GlyphName: "N", Wx: 667.000000, Wy: 0.000000},
"Nacute": {GlyphName: "Nacute", Wx: 667.000000, Wy: 0.000000},
"Ncaron": {GlyphName: "Ncaron", Wx: 667.000000, Wy: 0.000000},
"Ncommaaccent": {GlyphName: "Ncommaaccent", Wx: 667.000000, Wy: 0.000000},
"Ntilde": {GlyphName: "Ntilde", Wx: 667.000000, Wy: 0.000000},
"O": {GlyphName: "O", Wx: 722.000000, Wy: 0.000000},
"OE": {GlyphName: "OE", Wx: 944.000000, Wy: 0.000000},
"Oacute": {GlyphName: "Oacute", Wx: 722.000000, Wy: 0.000000},
"Ocircumflex": {GlyphName: "Ocircumflex", Wx: 722.000000, Wy: 0.000000},
"Odieresis": {GlyphName: "Odieresis", Wx: 722.000000, Wy: 0.000000},
"Ograve": {GlyphName: "Ograve", Wx: 722.000000, Wy: 0.000000},
"Ohungarumlaut": {GlyphName: "Ohungarumlaut", Wx: 722.000000, Wy: 0.000000},
"Omacron": {GlyphName: "Omacron", Wx: 722.000000, Wy: 0.000000},
"Oslash": {GlyphName: "Oslash", Wx: 722.000000, Wy: 0.000000},
"Otilde": {GlyphName: "Otilde", Wx: 722.000000, Wy: 0.000000},
"P": {GlyphName: "P", Wx: 611.000000, Wy: 0.000000},
"Q": {GlyphName: "Q", Wx: 722.000000, Wy: 0.000000},
"R": {GlyphName: "R", Wx: 611.000000, Wy: 0.000000},
"Racute": {GlyphName: "Racute", Wx: 611.000000, Wy: 0.000000},
"Rcaron": {GlyphName: "Rcaron", Wx: 611.000000, Wy: 0.000000},
"Rcommaaccent": {GlyphName: "Rcommaaccent", Wx: 611.000000, Wy: 0.000000},
"S": {GlyphName: "S", Wx: 500.000000, Wy: 0.000000},
"Sacute": {GlyphName: "Sacute", Wx: 500.000000, Wy: 0.000000},
"Scaron": {GlyphName: "Scaron", Wx: 500.000000, Wy: 0.000000},
"Scedilla": {GlyphName: "Scedilla", Wx: 500.000000, Wy: 0.000000},
"Scommaaccent": {GlyphName: "Scommaaccent", Wx: 500.000000, Wy: 0.000000},
"T": {GlyphName: "T", Wx: 556.000000, Wy: 0.000000},
"Tcaron": {GlyphName: "Tcaron", Wx: 556.000000, Wy: 0.000000},
"Tcommaaccent": {GlyphName: "Tcommaaccent", Wx: 556.000000, Wy: 0.000000},
"Thorn": {GlyphName: "Thorn", Wx: 611.000000, Wy: 0.000000},
"U": {GlyphName: "U", Wx: 722.000000, Wy: 0.000000},
"Uacute": {GlyphName: "Uacute", Wx: 722.000000, Wy: 0.000000},
"Ucircumflex": {GlyphName: "Ucircumflex", Wx: 722.000000, Wy: 0.000000},
"Udieresis": {GlyphName: "Udieresis", Wx: 722.000000, Wy: 0.000000},
"Ugrave": {GlyphName: "Ugrave", Wx: 722.000000, Wy: 0.000000},
"Uhungarumlaut": {GlyphName: "Uhungarumlaut", Wx: 722.000000, Wy: 0.000000},
"Umacron": {GlyphName: "Umacron", Wx: 722.000000, Wy: 0.000000},
"Uogonek": {GlyphName: "Uogonek", Wx: 722.000000, Wy: 0.000000},
"Uring": {GlyphName: "Uring", Wx: 722.000000, Wy: 0.000000},
"V": {GlyphName: "V", Wx: 611.000000, Wy: 0.000000},
"W": {GlyphName: "W", Wx: 833.000000, Wy: 0.000000},
"X": {GlyphName: "X", Wx: 611.000000, Wy: 0.000000},
"Y": {GlyphName: "Y", Wx: 556.000000, Wy: 0.000000},
"Yacute": {GlyphName: "Yacute", Wx: 556.000000, Wy: 0.000000},
"Ydieresis": {GlyphName: "Ydieresis", Wx: 556.000000, Wy: 0.000000},
"Z": {GlyphName: "Z", Wx: 556.000000, Wy: 0.000000},
"Zacute": {GlyphName: "Zacute", Wx: 556.000000, Wy: 0.000000},
"Zcaron": {GlyphName: "Zcaron", Wx: 556.000000, Wy: 0.000000},
"Zdotaccent": {GlyphName: "Zdotaccent", Wx: 556.000000, Wy: 0.000000},
"a": {GlyphName: "a", Wx: 500.000000, Wy: 0.000000},
"aacute": {GlyphName: "aacute", Wx: 500.000000, Wy: 0.000000},
"abreve": {GlyphName: "abreve", Wx: 500.000000, Wy: 0.000000},
"acircumflex": {GlyphName: "acircumflex", Wx: 500.000000, Wy: 0.000000},
"acute": {GlyphName: "acute", Wx: 333.000000, Wy: 0.000000},
"adieresis": {GlyphName: "adieresis", Wx: 500.000000, Wy: 0.000000},
"ae": {GlyphName: "ae", Wx: 667.000000, Wy: 0.000000},
"agrave": {GlyphName: "agrave", Wx: 500.000000, Wy: 0.000000},
"amacron": {GlyphName: "amacron", Wx: 500.000000, Wy: 0.000000},
"ampersand": {GlyphName: "ampersand", Wx: 778.000000, Wy: 0.000000},
"aogonek": {GlyphName: "aogonek", Wx: 500.000000, Wy: 0.000000},
"aring": {GlyphName: "aring", Wx: 500.000000, Wy: 0.000000},
"asciicircum": {GlyphName: "asciicircum", Wx: 422.000000, Wy: 0.000000},
"asciitilde": {GlyphName: "asciitilde", Wx: 541.000000, Wy: 0.000000},
"asterisk": {GlyphName: "asterisk", Wx: 500.000000, Wy: 0.000000},
"at": {GlyphName: "at", Wx: 920.000000, Wy: 0.000000},
"atilde": {GlyphName: "atilde", Wx: 500.000000, Wy: 0.000000},
"b": {GlyphName: "b", Wx: 500.000000, Wy: 0.000000},
"backslash": {GlyphName: "backslash", Wx: 278.000000, Wy: 0.000000},
"bar": {GlyphName: "bar", Wx: 275.000000, Wy: 0.000000},
"braceleft": {GlyphName: "braceleft", Wx: 400.000000, Wy: 0.000000},
"braceright": {GlyphName: "braceright", Wx: 400.000000, Wy: 0.000000},
"bracketleft": {GlyphName: "bracketleft", Wx: 389.000000, Wy: 0.000000},
"bracketright": {GlyphName: "bracketright", Wx: 389.000000, Wy: 0.000000},
"breve": {GlyphName: "breve", Wx: 333.000000, Wy: 0.000000},
"brokenbar": {GlyphName: "brokenbar", Wx: 275.000000, Wy: 0.000000},
"bullet": {GlyphName: "bullet", Wx: 350.000000, Wy: 0.000000},
"c": {GlyphName: "c", Wx: 444.000000, Wy: 0.000000},
"cacute": {GlyphName: "cacute", Wx: 444.000000, Wy: 0.000000},
"caron": {GlyphName: "caron", Wx: 333.000000, Wy: 0.000000},
"ccaron": {GlyphName: "ccaron", Wx: 444.000000, Wy: 0.000000},
"ccedilla": {GlyphName: "ccedilla", Wx: 444.000000, Wy: 0.000000},
"cedilla": {GlyphName: "cedilla", Wx: 333.000000, Wy: 0.000000},
"cent": {GlyphName: "cent", Wx: 500.000000, Wy: 0.000000},
"circumflex": {GlyphName: "circumflex", Wx: 333.000000, Wy: 0.000000},
"colon": {GlyphName: "colon", Wx: 333.000000, Wy: 0.000000},
"comma": {GlyphName: "comma", Wx: 250.000000, Wy: 0.000000},
"commaaccent": {GlyphName: "commaaccent", Wx: 250.000000, Wy: 0.000000},
"copyright": {GlyphName: "copyright", Wx: 760.000000, Wy: 0.000000},
"currency": {GlyphName: "currency", Wx: 500.000000, Wy: 0.000000},
"d": {GlyphName: "d", Wx: 500.000000, Wy: 0.000000},
"dagger": {GlyphName: "dagger", Wx: 500.000000, Wy: 0.000000},
"daggerdbl": {GlyphName: "daggerdbl", Wx: 500.000000, Wy: 0.000000},
"dcaron": {GlyphName: "dcaron", Wx: 544.000000, Wy: 0.000000},
"dcroat": {GlyphName: "dcroat", Wx: 500.000000, Wy: 0.000000},
"degree": {GlyphName: "degree", Wx: 400.000000, Wy: 0.000000},
"dieresis": {GlyphName: "dieresis", Wx: 333.000000, Wy: 0.000000},
"divide": {GlyphName: "divide", Wx: 675.000000, Wy: 0.000000},
"dollar": {GlyphName: "dollar", Wx: 500.000000, Wy: 0.000000},
"dotaccent": {GlyphName: "dotaccent", Wx: 333.000000, Wy: 0.000000},
"dotlessi": {GlyphName: "dotlessi", Wx: 278.000000, Wy: 0.000000},
"e": {GlyphName: "e", Wx: 444.000000, Wy: 0.000000},
"eacute": {GlyphName: "eacute", Wx: 444.000000, Wy: 0.000000},
"ecaron": {GlyphName: "ecaron", Wx: 444.000000, Wy: 0.000000},
"ecircumflex": {GlyphName: "ecircumflex", Wx: 444.000000, Wy: 0.000000},
"edieresis": {GlyphName: "edieresis", Wx: 444.000000, Wy: 0.000000},
"edotaccent": {GlyphName: "edotaccent", Wx: 444.000000, Wy: 0.000000},
"egrave": {GlyphName: "egrave", Wx: 444.000000, Wy: 0.000000},
"eight": {GlyphName: "eight", Wx: 500.000000, Wy: 0.000000},
"ellipsis": {GlyphName: "ellipsis", Wx: 889.000000, Wy: 0.000000},
"emacron": {GlyphName: "emacron", Wx: 444.000000, Wy: 0.000000},
"emdash": {GlyphName: "emdash", Wx: 889.000000, Wy: 0.000000},
"endash": {GlyphName: "endash", Wx: 500.000000, Wy: 0.000000},
"eogonek": {GlyphName: "eogonek", Wx: 444.000000, Wy: 0.000000},
"equal": {GlyphName: "equal", Wx: 675.000000, Wy: 0.000000},
"eth": {GlyphName: "eth", Wx: 500.000000, Wy: 0.000000},
"exclam": {GlyphName: "exclam", Wx: 333.000000, Wy: 0.000000},
"exclamdown": {GlyphName: "exclamdown", Wx: 389.000000, Wy: 0.000000},
"f": {GlyphName: "f", Wx: 278.000000, Wy: 0.000000},
"fi": {GlyphName: "fi", Wx: 500.000000, Wy: 0.000000},
"five": {GlyphName: "five", Wx: 500.000000, Wy: 0.000000},
"fl": {GlyphName: "fl", Wx: 500.000000, Wy: 0.000000},
"florin": {GlyphName: "florin", Wx: 500.000000, Wy: 0.000000},
"four": {GlyphName: "four", Wx: 500.000000, Wy: 0.000000},
"fraction": {GlyphName: "fraction", Wx: 167.000000, Wy: 0.000000},
"g": {GlyphName: "g", Wx: 500.000000, Wy: 0.000000},
"gbreve": {GlyphName: "gbreve", Wx: 500.000000, Wy: 0.000000},
"gcommaaccent": {GlyphName: "gcommaaccent", Wx: 500.000000, Wy: 0.000000},
"germandbls": {GlyphName: "germandbls", Wx: 500.000000, Wy: 0.000000},
"grave": {GlyphName: "grave", Wx: 333.000000, Wy: 0.000000},
"greater": {GlyphName: "greater", Wx: 675.000000, Wy: 0.000000},
"greaterequal": {GlyphName: "greaterequal", Wx: 549.000000, Wy: 0.000000},
"guillemotleft": {GlyphName: "guillemotleft", Wx: 500.000000, Wy: 0.000000},
"guillemotright": {GlyphName: "guillemotright", Wx: 500.000000, Wy: 0.000000},
"guilsinglleft": {GlyphName: "guilsinglleft", Wx: 333.000000, Wy: 0.000000},
"guilsinglright": {GlyphName: "guilsinglright", Wx: 333.000000, Wy: 0.000000},
"h": {GlyphName: "h", Wx: 500.000000, Wy: 0.000000},
"hungarumlaut": {GlyphName: "hungarumlaut", Wx: 333.000000, Wy: 0.000000},
"hyphen": {GlyphName: "hyphen", Wx: 333.000000, Wy: 0.000000},
"i": {GlyphName: "i", Wx: 278.000000, Wy: 0.000000},
"iacute": {GlyphName: "iacute", Wx: 278.000000, Wy: 0.000000},
"icircumflex": {GlyphName: "icircumflex", Wx: 278.000000, Wy: 0.000000},
"idieresis": {GlyphName: "idieresis", Wx: 278.000000, Wy: 0.000000},
"igrave": {GlyphName: "igrave", Wx: 278.000000, Wy: 0.000000},
"imacron": {GlyphName: "imacron", Wx: 278.000000, Wy: 0.000000},
"iogonek": {GlyphName: "iogonek", Wx: 278.000000, Wy: 0.000000},
"j": {GlyphName: "j", Wx: 278.000000, Wy: 0.000000},
"k": {GlyphName: "k", Wx: 444.000000, Wy: 0.000000},
"kcommaaccent": {GlyphName: "kcommaaccent", Wx: 444.000000, Wy: 0.000000},
"l": {GlyphName: "l", Wx: 278.000000, Wy: 0.000000},
"lacute": {GlyphName: "lacute", Wx: 278.000000, Wy: 0.000000},
"lcaron": {GlyphName: "lcaron", Wx: 300.000000, Wy: 0.000000},
"lcommaaccent": {GlyphName: "lcommaaccent", Wx: 278.000000, Wy: 0.000000},
"less": {GlyphName: "less", Wx: 675.000000, Wy: 0.000000},
"lessequal": {GlyphName: "lessequal", Wx: 549.000000, Wy: 0.000000},
"logicalnot": {GlyphName: "logicalnot", Wx: 675.000000, Wy: 0.000000},
"lozenge": {GlyphName: "lozenge", Wx: 471.000000, Wy: 0.000000},
"lslash": {GlyphName: "lslash", Wx: 278.000000, Wy: 0.000000},
"m": {GlyphName: "m", Wx: 722.000000, Wy: 0.000000},
"macron": {GlyphName: "macron", Wx: 333.000000, Wy: 0.000000},
"minus": {GlyphName: "minus", Wx: 675.000000, Wy: 0.000000},
"mu": {GlyphName: "mu", Wx: 500.000000, Wy: 0.000000},
"multiply": {GlyphName: "multiply", Wx: 675.000000, Wy: 0.000000},
"n": {GlyphName: "n", Wx: 500.000000, Wy: 0.000000},
"nacute": {GlyphName: "nacute", Wx: 500.000000, Wy: 0.000000},
"ncaron": {GlyphName: "ncaron", Wx: 500.000000, Wy: 0.000000},
"ncommaaccent": {GlyphName: "ncommaaccent", Wx: 500.000000, Wy: 0.000000},
"nine": {GlyphName: "nine", Wx: 500.000000, Wy: 0.000000},
"notequal": {GlyphName: "notequal", Wx: 549.000000, Wy: 0.000000},
"ntilde": {GlyphName: "ntilde", Wx: 500.000000, Wy: 0.000000},
"numbersign": {GlyphName: "numbersign", Wx: 500.000000, Wy: 0.000000},
"o": {GlyphName: "o", Wx: 500.000000, Wy: 0.000000},
"oacute": {GlyphName: "oacute", Wx: 500.000000, Wy: 0.000000},
"ocircumflex": {GlyphName: "ocircumflex", Wx: 500.000000, Wy: 0.000000},
"odieresis": {GlyphName: "odieresis", Wx: 500.000000, Wy: 0.000000},
"oe": {GlyphName: "oe", Wx: 667.000000, Wy: 0.000000},
"ogonek": {GlyphName: "ogonek", Wx: 333.000000, Wy: 0.000000},
"ograve": {GlyphName: "ograve", Wx: 500.000000, Wy: 0.000000},
"ohungarumlaut": {GlyphName: "ohungarumlaut", Wx: 500.000000, Wy: 0.000000},
"omacron": {GlyphName: "omacron", Wx: 500.000000, Wy: 0.000000},
"one": {GlyphName: "one", Wx: 500.000000, Wy: 0.000000},
"onehalf": {GlyphName: "onehalf", Wx: 750.000000, Wy: 0.000000},
"onequarter": {GlyphName: "onequarter", Wx: 750.000000, Wy: 0.000000},
"onesuperior": {GlyphName: "onesuperior", Wx: 300.000000, Wy: 0.000000},
"ordfeminine": {GlyphName: "ordfeminine", Wx: 276.000000, Wy: 0.000000},
"ordmasculine": {GlyphName: "ordmasculine", Wx: 310.000000, Wy: 0.000000},
"oslash": {GlyphName: "oslash", Wx: 500.000000, Wy: 0.000000},
"otilde": {GlyphName: "otilde", Wx: 500.000000, Wy: 0.000000},
"p": {GlyphName: "p", Wx: 500.000000, Wy: 0.000000},
"paragraph": {GlyphName: "paragraph", Wx: 523.000000, Wy: 0.000000},
"parenleft": {GlyphName: "parenleft", Wx: 333.000000, Wy: 0.000000},
"parenright": {GlyphName: "parenright", Wx: 333.000000, Wy: 0.000000},
"partialdiff": {GlyphName: "partialdiff", Wx: 476.000000, Wy: 0.000000},
"percent": {GlyphName: "percent", Wx: 833.000000, Wy: 0.000000},
"period": {GlyphName: "period", Wx: 250.000000, Wy: 0.000000},
"periodcentered": {GlyphName: "periodcentered", Wx: 250.000000, Wy: 0.000000},
"perthousand": {GlyphName: "perthousand", Wx: 1000.000000, Wy: 0.000000},
"plus": {GlyphName: "plus", Wx: 675.000000, Wy: 0.000000},
"plusminus": {GlyphName: "plusminus", Wx: 675.000000, Wy: 0.000000},
"q": {GlyphName: "q", Wx: 500.000000, Wy: 0.000000},
"question": {GlyphName: "question", Wx: 500.000000, Wy: 0.000000},
"questiondown": {GlyphName: "questiondown", Wx: 500.000000, Wy: 0.000000},
"quotedbl": {GlyphName: "quotedbl", Wx: 420.000000, Wy: 0.000000},
"quotedblbase": {GlyphName: "quotedblbase", Wx: 556.000000, Wy: 0.000000},
"quotedblleft": {GlyphName: "quotedblleft", Wx: 556.000000, Wy: 0.000000},
"quotedblright": {GlyphName: "quotedblright", Wx: 556.000000, Wy: 0.000000},
"quoteleft": {GlyphName: "quoteleft", Wx: 333.000000, Wy: 0.000000},
"quoteright": {GlyphName: "quoteright", Wx: 333.000000, Wy: 0.000000},
"quotesinglbase": {GlyphName: "quotesinglbase", Wx: 333.000000, Wy: 0.000000},
"quotesingle": {GlyphName: "quotesingle", Wx: 214.000000, Wy: 0.000000},
"r": {GlyphName: "r", Wx: 389.000000, Wy: 0.000000},
"racute": {GlyphName: "racute", Wx: 389.000000, Wy: 0.000000},
"radical": {GlyphName: "radical", Wx: 453.000000, Wy: 0.000000},
"rcaron": {GlyphName: "rcaron", Wx: 389.000000, Wy: 0.000000},
"rcommaaccent": {GlyphName: "rcommaaccent", Wx: 389.000000, Wy: 0.000000},
"registered": {GlyphName: "registered", Wx: 760.000000, Wy: 0.000000},
"ring": {GlyphName: "ring", Wx: 333.000000, Wy: 0.000000},
"s": {GlyphName: "s", Wx: 389.000000, Wy: 0.000000},
"sacute": {GlyphName: "sacute", Wx: 389.000000, Wy: 0.000000},
"scaron": {GlyphName: "scaron", Wx: 389.000000, Wy: 0.000000},
"scedilla": {GlyphName: "scedilla", Wx: 389.000000, Wy: 0.000000},
"scommaaccent": {GlyphName: "scommaaccent", Wx: 389.000000, Wy: 0.000000},
"section": {GlyphName: "section", Wx: 500.000000, Wy: 0.000000},
"semicolon": {GlyphName: "semicolon", Wx: 333.000000, Wy: 0.000000},
"seven": {GlyphName: "seven", Wx: 500.000000, Wy: 0.000000},
"six": {GlyphName: "six", Wx: 500.000000, Wy: 0.000000},
"slash": {GlyphName: "slash", Wx: 278.000000, Wy: 0.000000},
"space": {GlyphName: "space", Wx: 250.000000, Wy: 0.000000},
"sterling": {GlyphName: "sterling", Wx: 500.000000, Wy: 0.000000},
"summation": {GlyphName: "summation", Wx: 600.000000, Wy: 0.000000},
"t": {GlyphName: "t", Wx: 278.000000, Wy: 0.000000},
"tcaron": {GlyphName: "tcaron", Wx: 300.000000, Wy: 0.000000},
"tcommaaccent": {GlyphName: "tcommaaccent", Wx: 278.000000, Wy: 0.000000},
"thorn": {GlyphName: "thorn", Wx: 500.000000, Wy: 0.000000},
"three": {GlyphName: "three", Wx: 500.000000, Wy: 0.000000},
"threequarters": {GlyphName: "threequarters", Wx: 750.000000, Wy: 0.000000},
"threesuperior": {GlyphName: "threesuperior", Wx: 300.000000, Wy: 0.000000},
"tilde": {GlyphName: "tilde", Wx: 333.000000, Wy: 0.000000},
"trademark": {GlyphName: "trademark", Wx: 980.000000, Wy: 0.000000},
"two": {GlyphName: "two", Wx: 500.000000, Wy: 0.000000},
"twosuperior": {GlyphName: "twosuperior", Wx: 300.000000, Wy: 0.000000},
"u": {GlyphName: "u", Wx: 500.000000, Wy: 0.000000},
"uacute": {GlyphName: "uacute", Wx: 500.000000, Wy: 0.000000},
"ucircumflex": {GlyphName: "ucircumflex", Wx: 500.000000, Wy: 0.000000},
"udieresis": {GlyphName: "udieresis", Wx: 500.000000, Wy: 0.000000},
"ugrave": {GlyphName: "ugrave", Wx: 500.000000, Wy: 0.000000},
"uhungarumlaut": {GlyphName: "uhungarumlaut", Wx: 500.000000, Wy: 0.000000},
"umacron": {GlyphName: "umacron", Wx: 500.000000, Wy: 0.000000},
"underscore": {GlyphName: "underscore", Wx: 500.000000, Wy: 0.000000},
"uogonek": {GlyphName: "uogonek", Wx: 500.000000, Wy: 0.000000},
"uring": {GlyphName: "uring", Wx: 500.000000, Wy: 0.000000},
"v": {GlyphName: "v", Wx: 444.000000, Wy: 0.000000},
"w": {GlyphName: "w", Wx: 667.000000, Wy: 0.000000},
"x": {GlyphName: "x", Wx: 444.000000, Wy: 0.000000},
"y": {GlyphName: "y", Wx: 444.000000, Wy: 0.000000},
"yacute": {GlyphName: "yacute", Wx: 444.000000, Wy: 0.000000},
"ydieresis": {GlyphName: "ydieresis", Wx: 444.000000, Wy: 0.000000},
"yen": {GlyphName: "yen", Wx: 500.000000, Wy: 0.000000},
"z": {GlyphName: "z", Wx: 389.000000, Wy: 0.000000},
"zacute": {GlyphName: "zacute", Wx: 389.000000, Wy: 0.000000},
"zcaron": {GlyphName: "zcaron", Wx: 389.000000, Wy: 0.000000},
"zdotaccent": {GlyphName: "zdotaccent", Wx: 389.000000, Wy: 0.000000},
"zero": {GlyphName: "zero", Wx: 500.000000, Wy: 0.000000},
}

View File

@@ -0,0 +1,362 @@
package fonts
import (
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model/textencoding"
)
// Font Times-Roman. Implements Font interface.
// This is a built-in font and it is assumed that every reader has access to it.
type fontTimesRoman struct {
encoder textencoding.TextEncoder
}
func NewFontTimesRoman() fontTimesRoman {
font := fontTimesRoman{}
font.encoder = textencoding.NewWinAnsiTextEncoder() // Default
return font
}
func (font fontTimesRoman) SetEncoder(encoder textencoding.TextEncoder) {
font.encoder = encoder
}
func (font fontTimesRoman) GetGlyphCharMetrics(glyph string) (CharMetrics, bool) {
metrics, has := timesRomanCharMetrics[glyph]
if !has {
return metrics, false
}
return metrics, true
}
func (font fontTimesRoman) ToPdfObject() core.PdfObject {
obj := &core.PdfIndirectObject{}
fontDict := core.MakeDict()
fontDict.Set("Type", core.MakeName("Font"))
fontDict.Set("Subtype", core.MakeName("Type1"))
fontDict.Set("BaseFont", core.MakeName("Times-Roman"))
fontDict.Set("Encoding", font.encoder.ToPdfObject())
obj.PdfObject = fontDict
return obj
}
var timesRomanCharMetrics map[string]CharMetrics = map[string]CharMetrics{
"A": {GlyphName: "A", Wx: 722.000000, Wy: 0.000000},
"AE": {GlyphName: "AE", Wx: 889.000000, Wy: 0.000000},
"Aacute": {GlyphName: "Aacute", Wx: 722.000000, Wy: 0.000000},
"Abreve": {GlyphName: "Abreve", Wx: 722.000000, Wy: 0.000000},
"Acircumflex": {GlyphName: "Acircumflex", Wx: 722.000000, Wy: 0.000000},
"Adieresis": {GlyphName: "Adieresis", Wx: 722.000000, Wy: 0.000000},
"Agrave": {GlyphName: "Agrave", Wx: 722.000000, Wy: 0.000000},
"Amacron": {GlyphName: "Amacron", Wx: 722.000000, Wy: 0.000000},
"Aogonek": {GlyphName: "Aogonek", Wx: 722.000000, Wy: 0.000000},
"Aring": {GlyphName: "Aring", Wx: 722.000000, Wy: 0.000000},
"Atilde": {GlyphName: "Atilde", Wx: 722.000000, Wy: 0.000000},
"B": {GlyphName: "B", Wx: 667.000000, Wy: 0.000000},
"C": {GlyphName: "C", Wx: 667.000000, Wy: 0.000000},
"Cacute": {GlyphName: "Cacute", Wx: 667.000000, Wy: 0.000000},
"Ccaron": {GlyphName: "Ccaron", Wx: 667.000000, Wy: 0.000000},
"Ccedilla": {GlyphName: "Ccedilla", Wx: 667.000000, Wy: 0.000000},
"D": {GlyphName: "D", Wx: 722.000000, Wy: 0.000000},
"Dcaron": {GlyphName: "Dcaron", Wx: 722.000000, Wy: 0.000000},
"Dcroat": {GlyphName: "Dcroat", Wx: 722.000000, Wy: 0.000000},
"Delta": {GlyphName: "Delta", Wx: 612.000000, Wy: 0.000000},
"E": {GlyphName: "E", Wx: 611.000000, Wy: 0.000000},
"Eacute": {GlyphName: "Eacute", Wx: 611.000000, Wy: 0.000000},
"Ecaron": {GlyphName: "Ecaron", Wx: 611.000000, Wy: 0.000000},
"Ecircumflex": {GlyphName: "Ecircumflex", Wx: 611.000000, Wy: 0.000000},
"Edieresis": {GlyphName: "Edieresis", Wx: 611.000000, Wy: 0.000000},
"Edotaccent": {GlyphName: "Edotaccent", Wx: 611.000000, Wy: 0.000000},
"Egrave": {GlyphName: "Egrave", Wx: 611.000000, Wy: 0.000000},
"Emacron": {GlyphName: "Emacron", Wx: 611.000000, Wy: 0.000000},
"Eogonek": {GlyphName: "Eogonek", Wx: 611.000000, Wy: 0.000000},
"Eth": {GlyphName: "Eth", Wx: 722.000000, Wy: 0.000000},
"Euro": {GlyphName: "Euro", Wx: 500.000000, Wy: 0.000000},
"F": {GlyphName: "F", Wx: 556.000000, Wy: 0.000000},
"G": {GlyphName: "G", Wx: 722.000000, Wy: 0.000000},
"Gbreve": {GlyphName: "Gbreve", Wx: 722.000000, Wy: 0.000000},
"Gcommaaccent": {GlyphName: "Gcommaaccent", Wx: 722.000000, Wy: 0.000000},
"H": {GlyphName: "H", Wx: 722.000000, Wy: 0.000000},
"I": {GlyphName: "I", Wx: 333.000000, Wy: 0.000000},
"Iacute": {GlyphName: "Iacute", Wx: 333.000000, Wy: 0.000000},
"Icircumflex": {GlyphName: "Icircumflex", Wx: 333.000000, Wy: 0.000000},
"Idieresis": {GlyphName: "Idieresis", Wx: 333.000000, Wy: 0.000000},
"Idotaccent": {GlyphName: "Idotaccent", Wx: 333.000000, Wy: 0.000000},
"Igrave": {GlyphName: "Igrave", Wx: 333.000000, Wy: 0.000000},
"Imacron": {GlyphName: "Imacron", Wx: 333.000000, Wy: 0.000000},
"Iogonek": {GlyphName: "Iogonek", Wx: 333.000000, Wy: 0.000000},
"J": {GlyphName: "J", Wx: 389.000000, Wy: 0.000000},
"K": {GlyphName: "K", Wx: 722.000000, Wy: 0.000000},
"Kcommaaccent": {GlyphName: "Kcommaaccent", Wx: 722.000000, Wy: 0.000000},
"L": {GlyphName: "L", Wx: 611.000000, Wy: 0.000000},
"Lacute": {GlyphName: "Lacute", Wx: 611.000000, Wy: 0.000000},
"Lcaron": {GlyphName: "Lcaron", Wx: 611.000000, Wy: 0.000000},
"Lcommaaccent": {GlyphName: "Lcommaaccent", Wx: 611.000000, Wy: 0.000000},
"Lslash": {GlyphName: "Lslash", Wx: 611.000000, Wy: 0.000000},
"M": {GlyphName: "M", Wx: 889.000000, Wy: 0.000000},
"N": {GlyphName: "N", Wx: 722.000000, Wy: 0.000000},
"Nacute": {GlyphName: "Nacute", Wx: 722.000000, Wy: 0.000000},
"Ncaron": {GlyphName: "Ncaron", Wx: 722.000000, Wy: 0.000000},
"Ncommaaccent": {GlyphName: "Ncommaaccent", Wx: 722.000000, Wy: 0.000000},
"Ntilde": {GlyphName: "Ntilde", Wx: 722.000000, Wy: 0.000000},
"O": {GlyphName: "O", Wx: 722.000000, Wy: 0.000000},
"OE": {GlyphName: "OE", Wx: 889.000000, Wy: 0.000000},
"Oacute": {GlyphName: "Oacute", Wx: 722.000000, Wy: 0.000000},
"Ocircumflex": {GlyphName: "Ocircumflex", Wx: 722.000000, Wy: 0.000000},
"Odieresis": {GlyphName: "Odieresis", Wx: 722.000000, Wy: 0.000000},
"Ograve": {GlyphName: "Ograve", Wx: 722.000000, Wy: 0.000000},
"Ohungarumlaut": {GlyphName: "Ohungarumlaut", Wx: 722.000000, Wy: 0.000000},
"Omacron": {GlyphName: "Omacron", Wx: 722.000000, Wy: 0.000000},
"Oslash": {GlyphName: "Oslash", Wx: 722.000000, Wy: 0.000000},
"Otilde": {GlyphName: "Otilde", Wx: 722.000000, Wy: 0.000000},
"P": {GlyphName: "P", Wx: 556.000000, Wy: 0.000000},
"Q": {GlyphName: "Q", Wx: 722.000000, Wy: 0.000000},
"R": {GlyphName: "R", Wx: 667.000000, Wy: 0.000000},
"Racute": {GlyphName: "Racute", Wx: 667.000000, Wy: 0.000000},
"Rcaron": {GlyphName: "Rcaron", Wx: 667.000000, Wy: 0.000000},
"Rcommaaccent": {GlyphName: "Rcommaaccent", Wx: 667.000000, Wy: 0.000000},
"S": {GlyphName: "S", Wx: 556.000000, Wy: 0.000000},
"Sacute": {GlyphName: "Sacute", Wx: 556.000000, Wy: 0.000000},
"Scaron": {GlyphName: "Scaron", Wx: 556.000000, Wy: 0.000000},
"Scedilla": {GlyphName: "Scedilla", Wx: 556.000000, Wy: 0.000000},
"Scommaaccent": {GlyphName: "Scommaaccent", Wx: 556.000000, Wy: 0.000000},
"T": {GlyphName: "T", Wx: 611.000000, Wy: 0.000000},
"Tcaron": {GlyphName: "Tcaron", Wx: 611.000000, Wy: 0.000000},
"Tcommaaccent": {GlyphName: "Tcommaaccent", Wx: 611.000000, Wy: 0.000000},
"Thorn": {GlyphName: "Thorn", Wx: 556.000000, Wy: 0.000000},
"U": {GlyphName: "U", Wx: 722.000000, Wy: 0.000000},
"Uacute": {GlyphName: "Uacute", Wx: 722.000000, Wy: 0.000000},
"Ucircumflex": {GlyphName: "Ucircumflex", Wx: 722.000000, Wy: 0.000000},
"Udieresis": {GlyphName: "Udieresis", Wx: 722.000000, Wy: 0.000000},
"Ugrave": {GlyphName: "Ugrave", Wx: 722.000000, Wy: 0.000000},
"Uhungarumlaut": {GlyphName: "Uhungarumlaut", Wx: 722.000000, Wy: 0.000000},
"Umacron": {GlyphName: "Umacron", Wx: 722.000000, Wy: 0.000000},
"Uogonek": {GlyphName: "Uogonek", Wx: 722.000000, Wy: 0.000000},
"Uring": {GlyphName: "Uring", Wx: 722.000000, Wy: 0.000000},
"V": {GlyphName: "V", Wx: 722.000000, Wy: 0.000000},
"W": {GlyphName: "W", Wx: 944.000000, Wy: 0.000000},
"X": {GlyphName: "X", Wx: 722.000000, Wy: 0.000000},
"Y": {GlyphName: "Y", Wx: 722.000000, Wy: 0.000000},
"Yacute": {GlyphName: "Yacute", Wx: 722.000000, Wy: 0.000000},
"Ydieresis": {GlyphName: "Ydieresis", Wx: 722.000000, Wy: 0.000000},
"Z": {GlyphName: "Z", Wx: 611.000000, Wy: 0.000000},
"Zacute": {GlyphName: "Zacute", Wx: 611.000000, Wy: 0.000000},
"Zcaron": {GlyphName: "Zcaron", Wx: 611.000000, Wy: 0.000000},
"Zdotaccent": {GlyphName: "Zdotaccent", Wx: 611.000000, Wy: 0.000000},
"a": {GlyphName: "a", Wx: 444.000000, Wy: 0.000000},
"aacute": {GlyphName: "aacute", Wx: 444.000000, Wy: 0.000000},
"abreve": {GlyphName: "abreve", Wx: 444.000000, Wy: 0.000000},
"acircumflex": {GlyphName: "acircumflex", Wx: 444.000000, Wy: 0.000000},
"acute": {GlyphName: "acute", Wx: 333.000000, Wy: 0.000000},
"adieresis": {GlyphName: "adieresis", Wx: 444.000000, Wy: 0.000000},
"ae": {GlyphName: "ae", Wx: 667.000000, Wy: 0.000000},
"agrave": {GlyphName: "agrave", Wx: 444.000000, Wy: 0.000000},
"amacron": {GlyphName: "amacron", Wx: 444.000000, Wy: 0.000000},
"ampersand": {GlyphName: "ampersand", Wx: 778.000000, Wy: 0.000000},
"aogonek": {GlyphName: "aogonek", Wx: 444.000000, Wy: 0.000000},
"aring": {GlyphName: "aring", Wx: 444.000000, Wy: 0.000000},
"asciicircum": {GlyphName: "asciicircum", Wx: 469.000000, Wy: 0.000000},
"asciitilde": {GlyphName: "asciitilde", Wx: 541.000000, Wy: 0.000000},
"asterisk": {GlyphName: "asterisk", Wx: 500.000000, Wy: 0.000000},
"at": {GlyphName: "at", Wx: 921.000000, Wy: 0.000000},
"atilde": {GlyphName: "atilde", Wx: 444.000000, Wy: 0.000000},
"b": {GlyphName: "b", Wx: 500.000000, Wy: 0.000000},
"backslash": {GlyphName: "backslash", Wx: 278.000000, Wy: 0.000000},
"bar": {GlyphName: "bar", Wx: 200.000000, Wy: 0.000000},
"braceleft": {GlyphName: "braceleft", Wx: 480.000000, Wy: 0.000000},
"braceright": {GlyphName: "braceright", Wx: 480.000000, Wy: 0.000000},
"bracketleft": {GlyphName: "bracketleft", Wx: 333.000000, Wy: 0.000000},
"bracketright": {GlyphName: "bracketright", Wx: 333.000000, Wy: 0.000000},
"breve": {GlyphName: "breve", Wx: 333.000000, Wy: 0.000000},
"brokenbar": {GlyphName: "brokenbar", Wx: 200.000000, Wy: 0.000000},
"bullet": {GlyphName: "bullet", Wx: 350.000000, Wy: 0.000000},
"c": {GlyphName: "c", Wx: 444.000000, Wy: 0.000000},
"cacute": {GlyphName: "cacute", Wx: 444.000000, Wy: 0.000000},
"caron": {GlyphName: "caron", Wx: 333.000000, Wy: 0.000000},
"ccaron": {GlyphName: "ccaron", Wx: 444.000000, Wy: 0.000000},
"ccedilla": {GlyphName: "ccedilla", Wx: 444.000000, Wy: 0.000000},
"cedilla": {GlyphName: "cedilla", Wx: 333.000000, Wy: 0.000000},
"cent": {GlyphName: "cent", Wx: 500.000000, Wy: 0.000000},
"circumflex": {GlyphName: "circumflex", Wx: 333.000000, Wy: 0.000000},
"colon": {GlyphName: "colon", Wx: 278.000000, Wy: 0.000000},
"comma": {GlyphName: "comma", Wx: 250.000000, Wy: 0.000000},
"commaaccent": {GlyphName: "commaaccent", Wx: 250.000000, Wy: 0.000000},
"copyright": {GlyphName: "copyright", Wx: 760.000000, Wy: 0.000000},
"currency": {GlyphName: "currency", Wx: 500.000000, Wy: 0.000000},
"d": {GlyphName: "d", Wx: 500.000000, Wy: 0.000000},
"dagger": {GlyphName: "dagger", Wx: 500.000000, Wy: 0.000000},
"daggerdbl": {GlyphName: "daggerdbl", Wx: 500.000000, Wy: 0.000000},
"dcaron": {GlyphName: "dcaron", Wx: 588.000000, Wy: 0.000000},
"dcroat": {GlyphName: "dcroat", Wx: 500.000000, Wy: 0.000000},
"degree": {GlyphName: "degree", Wx: 400.000000, Wy: 0.000000},
"dieresis": {GlyphName: "dieresis", Wx: 333.000000, Wy: 0.000000},
"divide": {GlyphName: "divide", Wx: 564.000000, Wy: 0.000000},
"dollar": {GlyphName: "dollar", Wx: 500.000000, Wy: 0.000000},
"dotaccent": {GlyphName: "dotaccent", Wx: 333.000000, Wy: 0.000000},
"dotlessi": {GlyphName: "dotlessi", Wx: 278.000000, Wy: 0.000000},
"e": {GlyphName: "e", Wx: 444.000000, Wy: 0.000000},
"eacute": {GlyphName: "eacute", Wx: 444.000000, Wy: 0.000000},
"ecaron": {GlyphName: "ecaron", Wx: 444.000000, Wy: 0.000000},
"ecircumflex": {GlyphName: "ecircumflex", Wx: 444.000000, Wy: 0.000000},
"edieresis": {GlyphName: "edieresis", Wx: 444.000000, Wy: 0.000000},
"edotaccent": {GlyphName: "edotaccent", Wx: 444.000000, Wy: 0.000000},
"egrave": {GlyphName: "egrave", Wx: 444.000000, Wy: 0.000000},
"eight": {GlyphName: "eight", Wx: 500.000000, Wy: 0.000000},
"ellipsis": {GlyphName: "ellipsis", Wx: 1000.000000, Wy: 0.000000},
"emacron": {GlyphName: "emacron", Wx: 444.000000, Wy: 0.000000},
"emdash": {GlyphName: "emdash", Wx: 1000.000000, Wy: 0.000000},
"endash": {GlyphName: "endash", Wx: 500.000000, Wy: 0.000000},
"eogonek": {GlyphName: "eogonek", Wx: 444.000000, Wy: 0.000000},
"equal": {GlyphName: "equal", Wx: 564.000000, Wy: 0.000000},
"eth": {GlyphName: "eth", Wx: 500.000000, Wy: 0.000000},
"exclam": {GlyphName: "exclam", Wx: 333.000000, Wy: 0.000000},
"exclamdown": {GlyphName: "exclamdown", Wx: 333.000000, Wy: 0.000000},
"f": {GlyphName: "f", Wx: 333.000000, Wy: 0.000000},
"fi": {GlyphName: "fi", Wx: 556.000000, Wy: 0.000000},
"five": {GlyphName: "five", Wx: 500.000000, Wy: 0.000000},
"fl": {GlyphName: "fl", Wx: 556.000000, Wy: 0.000000},
"florin": {GlyphName: "florin", Wx: 500.000000, Wy: 0.000000},
"four": {GlyphName: "four", Wx: 500.000000, Wy: 0.000000},
"fraction": {GlyphName: "fraction", Wx: 167.000000, Wy: 0.000000},
"g": {GlyphName: "g", Wx: 500.000000, Wy: 0.000000},
"gbreve": {GlyphName: "gbreve", Wx: 500.000000, Wy: 0.000000},
"gcommaaccent": {GlyphName: "gcommaaccent", Wx: 500.000000, Wy: 0.000000},
"germandbls": {GlyphName: "germandbls", Wx: 500.000000, Wy: 0.000000},
"grave": {GlyphName: "grave", Wx: 333.000000, Wy: 0.000000},
"greater": {GlyphName: "greater", Wx: 564.000000, Wy: 0.000000},
"greaterequal": {GlyphName: "greaterequal", Wx: 549.000000, Wy: 0.000000},
"guillemotleft": {GlyphName: "guillemotleft", Wx: 500.000000, Wy: 0.000000},
"guillemotright": {GlyphName: "guillemotright", Wx: 500.000000, Wy: 0.000000},
"guilsinglleft": {GlyphName: "guilsinglleft", Wx: 333.000000, Wy: 0.000000},
"guilsinglright": {GlyphName: "guilsinglright", Wx: 333.000000, Wy: 0.000000},
"h": {GlyphName: "h", Wx: 500.000000, Wy: 0.000000},
"hungarumlaut": {GlyphName: "hungarumlaut", Wx: 333.000000, Wy: 0.000000},
"hyphen": {GlyphName: "hyphen", Wx: 333.000000, Wy: 0.000000},
"i": {GlyphName: "i", Wx: 278.000000, Wy: 0.000000},
"iacute": {GlyphName: "iacute", Wx: 278.000000, Wy: 0.000000},
"icircumflex": {GlyphName: "icircumflex", Wx: 278.000000, Wy: 0.000000},
"idieresis": {GlyphName: "idieresis", Wx: 278.000000, Wy: 0.000000},
"igrave": {GlyphName: "igrave", Wx: 278.000000, Wy: 0.000000},
"imacron": {GlyphName: "imacron", Wx: 278.000000, Wy: 0.000000},
"iogonek": {GlyphName: "iogonek", Wx: 278.000000, Wy: 0.000000},
"j": {GlyphName: "j", Wx: 278.000000, Wy: 0.000000},
"k": {GlyphName: "k", Wx: 500.000000, Wy: 0.000000},
"kcommaaccent": {GlyphName: "kcommaaccent", Wx: 500.000000, Wy: 0.000000},
"l": {GlyphName: "l", Wx: 278.000000, Wy: 0.000000},
"lacute": {GlyphName: "lacute", Wx: 278.000000, Wy: 0.000000},
"lcaron": {GlyphName: "lcaron", Wx: 344.000000, Wy: 0.000000},
"lcommaaccent": {GlyphName: "lcommaaccent", Wx: 278.000000, Wy: 0.000000},
"less": {GlyphName: "less", Wx: 564.000000, Wy: 0.000000},
"lessequal": {GlyphName: "lessequal", Wx: 549.000000, Wy: 0.000000},
"logicalnot": {GlyphName: "logicalnot", Wx: 564.000000, Wy: 0.000000},
"lozenge": {GlyphName: "lozenge", Wx: 471.000000, Wy: 0.000000},
"lslash": {GlyphName: "lslash", Wx: 278.000000, Wy: 0.000000},
"m": {GlyphName: "m", Wx: 778.000000, Wy: 0.000000},
"macron": {GlyphName: "macron", Wx: 333.000000, Wy: 0.000000},
"minus": {GlyphName: "minus", Wx: 564.000000, Wy: 0.000000},
"mu": {GlyphName: "mu", Wx: 500.000000, Wy: 0.000000},
"multiply": {GlyphName: "multiply", Wx: 564.000000, Wy: 0.000000},
"n": {GlyphName: "n", Wx: 500.000000, Wy: 0.000000},
"nacute": {GlyphName: "nacute", Wx: 500.000000, Wy: 0.000000},
"ncaron": {GlyphName: "ncaron", Wx: 500.000000, Wy: 0.000000},
"ncommaaccent": {GlyphName: "ncommaaccent", Wx: 500.000000, Wy: 0.000000},
"nine": {GlyphName: "nine", Wx: 500.000000, Wy: 0.000000},
"notequal": {GlyphName: "notequal", Wx: 549.000000, Wy: 0.000000},
"ntilde": {GlyphName: "ntilde", Wx: 500.000000, Wy: 0.000000},
"numbersign": {GlyphName: "numbersign", Wx: 500.000000, Wy: 0.000000},
"o": {GlyphName: "o", Wx: 500.000000, Wy: 0.000000},
"oacute": {GlyphName: "oacute", Wx: 500.000000, Wy: 0.000000},
"ocircumflex": {GlyphName: "ocircumflex", Wx: 500.000000, Wy: 0.000000},
"odieresis": {GlyphName: "odieresis", Wx: 500.000000, Wy: 0.000000},
"oe": {GlyphName: "oe", Wx: 722.000000, Wy: 0.000000},
"ogonek": {GlyphName: "ogonek", Wx: 333.000000, Wy: 0.000000},
"ograve": {GlyphName: "ograve", Wx: 500.000000, Wy: 0.000000},
"ohungarumlaut": {GlyphName: "ohungarumlaut", Wx: 500.000000, Wy: 0.000000},
"omacron": {GlyphName: "omacron", Wx: 500.000000, Wy: 0.000000},
"one": {GlyphName: "one", Wx: 500.000000, Wy: 0.000000},
"onehalf": {GlyphName: "onehalf", Wx: 750.000000, Wy: 0.000000},
"onequarter": {GlyphName: "onequarter", Wx: 750.000000, Wy: 0.000000},
"onesuperior": {GlyphName: "onesuperior", Wx: 300.000000, Wy: 0.000000},
"ordfeminine": {GlyphName: "ordfeminine", Wx: 276.000000, Wy: 0.000000},
"ordmasculine": {GlyphName: "ordmasculine", Wx: 310.000000, Wy: 0.000000},
"oslash": {GlyphName: "oslash", Wx: 500.000000, Wy: 0.000000},
"otilde": {GlyphName: "otilde", Wx: 500.000000, Wy: 0.000000},
"p": {GlyphName: "p", Wx: 500.000000, Wy: 0.000000},
"paragraph": {GlyphName: "paragraph", Wx: 453.000000, Wy: 0.000000},
"parenleft": {GlyphName: "parenleft", Wx: 333.000000, Wy: 0.000000},
"parenright": {GlyphName: "parenright", Wx: 333.000000, Wy: 0.000000},
"partialdiff": {GlyphName: "partialdiff", Wx: 476.000000, Wy: 0.000000},
"percent": {GlyphName: "percent", Wx: 833.000000, Wy: 0.000000},
"period": {GlyphName: "period", Wx: 250.000000, Wy: 0.000000},
"periodcentered": {GlyphName: "periodcentered", Wx: 250.000000, Wy: 0.000000},
"perthousand": {GlyphName: "perthousand", Wx: 1000.000000, Wy: 0.000000},
"plus": {GlyphName: "plus", Wx: 564.000000, Wy: 0.000000},
"plusminus": {GlyphName: "plusminus", Wx: 564.000000, Wy: 0.000000},
"q": {GlyphName: "q", Wx: 500.000000, Wy: 0.000000},
"question": {GlyphName: "question", Wx: 444.000000, Wy: 0.000000},
"questiondown": {GlyphName: "questiondown", Wx: 444.000000, Wy: 0.000000},
"quotedbl": {GlyphName: "quotedbl", Wx: 408.000000, Wy: 0.000000},
"quotedblbase": {GlyphName: "quotedblbase", Wx: 444.000000, Wy: 0.000000},
"quotedblleft": {GlyphName: "quotedblleft", Wx: 444.000000, Wy: 0.000000},
"quotedblright": {GlyphName: "quotedblright", Wx: 444.000000, Wy: 0.000000},
"quoteleft": {GlyphName: "quoteleft", Wx: 333.000000, Wy: 0.000000},
"quoteright": {GlyphName: "quoteright", Wx: 333.000000, Wy: 0.000000},
"quotesinglbase": {GlyphName: "quotesinglbase", Wx: 333.000000, Wy: 0.000000},
"quotesingle": {GlyphName: "quotesingle", Wx: 180.000000, Wy: 0.000000},
"r": {GlyphName: "r", Wx: 333.000000, Wy: 0.000000},
"racute": {GlyphName: "racute", Wx: 333.000000, Wy: 0.000000},
"radical": {GlyphName: "radical", Wx: 453.000000, Wy: 0.000000},
"rcaron": {GlyphName: "rcaron", Wx: 333.000000, Wy: 0.000000},
"rcommaaccent": {GlyphName: "rcommaaccent", Wx: 333.000000, Wy: 0.000000},
"registered": {GlyphName: "registered", Wx: 760.000000, Wy: 0.000000},
"ring": {GlyphName: "ring", Wx: 333.000000, Wy: 0.000000},
"s": {GlyphName: "s", Wx: 389.000000, Wy: 0.000000},
"sacute": {GlyphName: "sacute", Wx: 389.000000, Wy: 0.000000},
"scaron": {GlyphName: "scaron", Wx: 389.000000, Wy: 0.000000},
"scedilla": {GlyphName: "scedilla", Wx: 389.000000, Wy: 0.000000},
"scommaaccent": {GlyphName: "scommaaccent", Wx: 389.000000, Wy: 0.000000},
"section": {GlyphName: "section", Wx: 500.000000, Wy: 0.000000},
"semicolon": {GlyphName: "semicolon", Wx: 278.000000, Wy: 0.000000},
"seven": {GlyphName: "seven", Wx: 500.000000, Wy: 0.000000},
"six": {GlyphName: "six", Wx: 500.000000, Wy: 0.000000},
"slash": {GlyphName: "slash", Wx: 278.000000, Wy: 0.000000},
"space": {GlyphName: "space", Wx: 250.000000, Wy: 0.000000},
"sterling": {GlyphName: "sterling", Wx: 500.000000, Wy: 0.000000},
"summation": {GlyphName: "summation", Wx: 600.000000, Wy: 0.000000},
"t": {GlyphName: "t", Wx: 278.000000, Wy: 0.000000},
"tcaron": {GlyphName: "tcaron", Wx: 326.000000, Wy: 0.000000},
"tcommaaccent": {GlyphName: "tcommaaccent", Wx: 278.000000, Wy: 0.000000},
"thorn": {GlyphName: "thorn", Wx: 500.000000, Wy: 0.000000},
"three": {GlyphName: "three", Wx: 500.000000, Wy: 0.000000},
"threequarters": {GlyphName: "threequarters", Wx: 750.000000, Wy: 0.000000},
"threesuperior": {GlyphName: "threesuperior", Wx: 300.000000, Wy: 0.000000},
"tilde": {GlyphName: "tilde", Wx: 333.000000, Wy: 0.000000},
"trademark": {GlyphName: "trademark", Wx: 980.000000, Wy: 0.000000},
"two": {GlyphName: "two", Wx: 500.000000, Wy: 0.000000},
"twosuperior": {GlyphName: "twosuperior", Wx: 300.000000, Wy: 0.000000},
"u": {GlyphName: "u", Wx: 500.000000, Wy: 0.000000},
"uacute": {GlyphName: "uacute", Wx: 500.000000, Wy: 0.000000},
"ucircumflex": {GlyphName: "ucircumflex", Wx: 500.000000, Wy: 0.000000},
"udieresis": {GlyphName: "udieresis", Wx: 500.000000, Wy: 0.000000},
"ugrave": {GlyphName: "ugrave", Wx: 500.000000, Wy: 0.000000},
"uhungarumlaut": {GlyphName: "uhungarumlaut", Wx: 500.000000, Wy: 0.000000},
"umacron": {GlyphName: "umacron", Wx: 500.000000, Wy: 0.000000},
"underscore": {GlyphName: "underscore", Wx: 500.000000, Wy: 0.000000},
"uogonek": {GlyphName: "uogonek", Wx: 500.000000, Wy: 0.000000},
"uring": {GlyphName: "uring", Wx: 500.000000, Wy: 0.000000},
"v": {GlyphName: "v", Wx: 500.000000, Wy: 0.000000},
"w": {GlyphName: "w", Wx: 722.000000, Wy: 0.000000},
"x": {GlyphName: "x", Wx: 500.000000, Wy: 0.000000},
"y": {GlyphName: "y", Wx: 500.000000, Wy: 0.000000},
"yacute": {GlyphName: "yacute", Wx: 500.000000, Wy: 0.000000},
"ydieresis": {GlyphName: "ydieresis", Wx: 500.000000, Wy: 0.000000},
"yen": {GlyphName: "yen", Wx: 500.000000, Wy: 0.000000},
"z": {GlyphName: "z", Wx: 444.000000, Wy: 0.000000},
"zacute": {GlyphName: "zacute", Wx: 444.000000, Wy: 0.000000},
"zcaron": {GlyphName: "zcaron", Wx: 444.000000, Wy: 0.000000},
"zdotaccent": {GlyphName: "zdotaccent", Wx: 444.000000, Wy: 0.000000},
"zero": {GlyphName: "zero", Wx: 500.000000, Wy: 0.000000},
}

View File

@@ -0,0 +1,374 @@
/*
* Copyright (c) 2013 Kurt Jung (Gmail: kurt.w.jung)
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package fonts
// Utility to parse TTF font files
// Version: 1.0
// Date: 2011-06-18
// Author: Olivier PLATHEY
// Port to Go: Kurt Jung, 2013-07-15
import (
"encoding/binary"
"fmt"
"os"
"regexp"
"strings"
)
// TtfType contains metrics of a TrueType font.
type TtfType struct {
Embeddable bool
UnitsPerEm uint16
PostScriptName string
Bold bool
ItalicAngle int16
IsFixedPitch bool
TypoAscender int16
TypoDescender int16
UnderlinePosition int16
UnderlineThickness int16
Xmin, Ymin, Xmax, Ymax int16
CapHeight int16
Widths []uint16
Chars map[uint16]uint16
}
type ttfParser struct {
rec TtfType
f *os.File
tables map[string]uint32
numberOfHMetrics uint16
numGlyphs uint16
}
// TtfParse extracts various metrics from a TrueType font file.
func TtfParse(fileStr string) (TtfRec TtfType, err error) {
var t ttfParser
t.f, err = os.Open(fileStr)
if err != nil {
return
}
version, err := t.ReadStr(4)
if err != nil {
return
}
if version == "OTTO" {
err = fmt.Errorf("fonts based on PostScript outlines are not supported")
return
}
if version != "\x00\x01\x00\x00" {
err = fmt.Errorf("unrecognized file format")
return
}
numTables := int(t.ReadUShort())
t.Skip(3 * 2) // searchRange, entrySelector, rangeShift
t.tables = make(map[string]uint32)
var tag string
for j := 0; j < numTables; j++ {
tag, err = t.ReadStr(4)
if err != nil {
return
}
t.Skip(4) // checkSum
offset := t.ReadULong()
t.Skip(4) // length
t.tables[tag] = offset
}
err = t.ParseComponents()
if err != nil {
return
}
t.f.Close()
TtfRec = t.rec
return
}
func (t *ttfParser) ParseComponents() (err error) {
err = t.ParseHead()
if err == nil {
err = t.ParseHhea()
if err == nil {
err = t.ParseMaxp()
if err == nil {
err = t.ParseHmtx()
if err == nil {
err = t.ParseCmap()
if err == nil {
err = t.ParseName()
if err == nil {
err = t.ParseOS2()
if err == nil {
err = t.ParsePost()
}
}
}
}
}
}
}
return
}
func (t *ttfParser) ParseHead() (err error) {
err = t.Seek("head")
t.Skip(3 * 4) // version, fontRevision, checkSumAdjustment
magicNumber := t.ReadULong()
if magicNumber != 0x5F0F3CF5 {
err = fmt.Errorf("incorrect magic number")
return
}
t.Skip(2) // flags
t.rec.UnitsPerEm = t.ReadUShort()
t.Skip(2 * 8) // created, modified
t.rec.Xmin = t.ReadShort()
t.rec.Ymin = t.ReadShort()
t.rec.Xmax = t.ReadShort()
t.rec.Ymax = t.ReadShort()
return
}
func (t *ttfParser) ParseHhea() (err error) {
err = t.Seek("hhea")
if err == nil {
t.Skip(4 + 15*2)
t.numberOfHMetrics = t.ReadUShort()
}
return
}
func (t *ttfParser) ParseMaxp() (err error) {
err = t.Seek("maxp")
if err == nil {
t.Skip(4)
t.numGlyphs = t.ReadUShort()
}
return
}
func (t *ttfParser) ParseHmtx() (err error) {
err = t.Seek("hmtx")
if err == nil {
t.rec.Widths = make([]uint16, 0, 8)
for j := uint16(0); j < t.numberOfHMetrics; j++ {
t.rec.Widths = append(t.rec.Widths, t.ReadUShort())
t.Skip(2) // lsb
}
if t.numberOfHMetrics < t.numGlyphs {
lastWidth := t.rec.Widths[t.numberOfHMetrics-1]
for j := t.numberOfHMetrics; j < t.numGlyphs; j++ {
t.rec.Widths = append(t.rec.Widths, lastWidth)
}
}
}
return
}
func (t *ttfParser) ParseCmap() (err error) {
var offset int64
if err = t.Seek("cmap"); err != nil {
return
}
t.Skip(2) // version
numTables := int(t.ReadUShort())
offset31 := int64(0)
for j := 0; j < numTables; j++ {
platformID := t.ReadUShort()
encodingID := t.ReadUShort()
offset = int64(t.ReadULong())
if platformID == 3 && encodingID == 1 {
offset31 = offset
}
}
if offset31 == 0 {
err = fmt.Errorf("no Unicode encoding found")
return
}
startCount := make([]uint16, 0, 8)
endCount := make([]uint16, 0, 8)
idDelta := make([]int16, 0, 8)
idRangeOffset := make([]uint16, 0, 8)
t.rec.Chars = make(map[uint16]uint16)
t.f.Seek(int64(t.tables["cmap"])+offset31, os.SEEK_SET)
format := t.ReadUShort()
if format != 4 {
err = fmt.Errorf("unexpected subtable format: %d", format)
return
}
t.Skip(2 * 2) // length, language
segCount := int(t.ReadUShort() / 2)
t.Skip(3 * 2) // searchRange, entrySelector, rangeShift
for j := 0; j < segCount; j++ {
endCount = append(endCount, t.ReadUShort())
}
t.Skip(2) // reservedPad
for j := 0; j < segCount; j++ {
startCount = append(startCount, t.ReadUShort())
}
for j := 0; j < segCount; j++ {
idDelta = append(idDelta, t.ReadShort())
}
offset, _ = t.f.Seek(int64(0), os.SEEK_CUR)
for j := 0; j < segCount; j++ {
idRangeOffset = append(idRangeOffset, t.ReadUShort())
}
for j := 0; j < segCount; j++ {
c1 := startCount[j]
c2 := endCount[j]
d := idDelta[j]
ro := idRangeOffset[j]
if ro > 0 {
t.f.Seek(offset+2*int64(j)+int64(ro), os.SEEK_SET)
}
for c := c1; c <= c2; c++ {
if c == 0xFFFF {
break
}
var gid int32
if ro > 0 {
gid = int32(t.ReadUShort())
if gid > 0 {
gid += int32(d)
}
} else {
gid = int32(c) + int32(d)
}
if gid >= 65536 {
gid -= 65536
}
if gid > 0 {
t.rec.Chars[c] = uint16(gid)
}
}
}
return
}
func (t *ttfParser) ParseName() (err error) {
err = t.Seek("name")
if err == nil {
tableOffset, _ := t.f.Seek(0, os.SEEK_CUR)
t.rec.PostScriptName = ""
t.Skip(2) // format
count := t.ReadUShort()
stringOffset := t.ReadUShort()
for j := uint16(0); j < count && t.rec.PostScriptName == ""; j++ {
t.Skip(3 * 2) // platformID, encodingID, languageID
nameID := t.ReadUShort()
length := t.ReadUShort()
offset := t.ReadUShort()
if nameID == 6 {
// PostScript name
t.f.Seek(int64(tableOffset)+int64(stringOffset)+int64(offset), os.SEEK_SET)
var s string
s, err = t.ReadStr(int(length))
if err != nil {
return
}
s = strings.Replace(s, "\x00", "", -1)
var re *regexp.Regexp
if re, err = regexp.Compile("[(){}<> /%[\\]]"); err != nil {
return
}
t.rec.PostScriptName = re.ReplaceAllString(s, "")
}
}
if t.rec.PostScriptName == "" {
err = fmt.Errorf("the name PostScript was not found")
}
}
return
}
func (t *ttfParser) ParseOS2() (err error) {
err = t.Seek("OS/2")
if err == nil {
version := t.ReadUShort()
t.Skip(3 * 2) // xAvgCharWidth, usWeightClass, usWidthClass
fsType := t.ReadUShort()
t.rec.Embeddable = (fsType != 2) && (fsType&0x200) == 0
t.Skip(11*2 + 10 + 4*4 + 4)
fsSelection := t.ReadUShort()
t.rec.Bold = (fsSelection & 32) != 0
t.Skip(2 * 2) // usFirstCharIndex, usLastCharIndex
t.rec.TypoAscender = t.ReadShort()
t.rec.TypoDescender = t.ReadShort()
if version >= 2 {
t.Skip(3*2 + 2*4 + 2)
t.rec.CapHeight = t.ReadShort()
} else {
t.rec.CapHeight = 0
}
}
return
}
func (t *ttfParser) ParsePost() (err error) {
err = t.Seek("post")
if err == nil {
t.Skip(4) // version
t.rec.ItalicAngle = t.ReadShort()
t.Skip(2) // Skip decimal part
t.rec.UnderlinePosition = t.ReadShort()
t.rec.UnderlineThickness = t.ReadShort()
t.rec.IsFixedPitch = t.ReadULong() != 0
}
return
}
func (t *ttfParser) Seek(tag string) (err error) {
ofs, ok := t.tables[tag]
if ok {
t.f.Seek(int64(ofs), os.SEEK_SET)
} else {
err = fmt.Errorf("table not found: %s", tag)
}
return
}
func (t *ttfParser) Skip(n int) {
t.f.Seek(int64(n), os.SEEK_CUR)
}
func (t *ttfParser) ReadStr(length int) (str string, err error) {
var n int
buf := make([]byte, length)
n, err = t.f.Read(buf)
if err == nil {
if n == length {
str = string(buf)
} else {
err = fmt.Errorf("unable to read %d bytes", length)
}
}
return
}
func (t *ttfParser) ReadUShort() (val uint16) {
binary.Read(t.f, binary.BigEndian, &val)
return
}
func (t *ttfParser) ReadShort() (val int16) {
binary.Read(t.f, binary.BigEndian, &val)
return
}
func (t *ttfParser) ReadULong() (val uint32) {
binary.Read(t.f, binary.BigEndian, &val)
return
}

View File

@@ -0,0 +1,251 @@
package fonts
import (
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model/textencoding"
)
// Font ZapfDingbats. Implements Font interface.
// This is a built-in font and it is assumed that every reader has access to it.
type fontZapfDingbats struct {
// By default encoder is not set, which means that we use the font's built in encoding.
encoder textencoding.TextEncoder
}
func NewFontZapfDingbats() fontZapfDingbats {
font := fontZapfDingbats{}
return font
}
func (font fontZapfDingbats) SetEncoder(encoder textencoding.TextEncoder) {
font.encoder = encoder
}
func (font fontZapfDingbats) GetGlyphCharMetrics(glyph string) (CharMetrics, bool) {
metrics, has := zapfDingbatsCharMetrics[glyph]
if !has {
return metrics, false
}
return metrics, true
}
func (font fontZapfDingbats) ToPdfObject() core.PdfObject {
obj := &core.PdfIndirectObject{}
fontDict := core.MakeDict()
fontDict.Set("Type", core.MakeName("Font"))
fontDict.Set("Subtype", core.MakeName("Type1"))
fontDict.Set("BaseFont", core.MakeName("ZapfDingbats"))
if font.encoder != nil {
fontDict.Set("Encoding", font.encoder.ToPdfObject())
}
obj.PdfObject = fontDict
return obj
}
var zapfDingbatsCharMetrics map[string]CharMetrics = map[string]CharMetrics{
"a1": {GlyphName: "a1", Wx: 974.000000, Wy: 0.000000},
"a10": {GlyphName: "a10", Wx: 692.000000, Wy: 0.000000},
"a100": {GlyphName: "a100", Wx: 668.000000, Wy: 0.000000},
"a101": {GlyphName: "a101", Wx: 732.000000, Wy: 0.000000},
"a102": {GlyphName: "a102", Wx: 544.000000, Wy: 0.000000},
"a103": {GlyphName: "a103", Wx: 544.000000, Wy: 0.000000},
"a104": {GlyphName: "a104", Wx: 910.000000, Wy: 0.000000},
"a105": {GlyphName: "a105", Wx: 911.000000, Wy: 0.000000},
"a106": {GlyphName: "a106", Wx: 667.000000, Wy: 0.000000},
"a107": {GlyphName: "a107", Wx: 760.000000, Wy: 0.000000},
"a108": {GlyphName: "a108", Wx: 760.000000, Wy: 0.000000},
"a109": {GlyphName: "a109", Wx: 626.000000, Wy: 0.000000},
"a11": {GlyphName: "a11", Wx: 960.000000, Wy: 0.000000},
"a110": {GlyphName: "a110", Wx: 694.000000, Wy: 0.000000},
"a111": {GlyphName: "a111", Wx: 595.000000, Wy: 0.000000},
"a112": {GlyphName: "a112", Wx: 776.000000, Wy: 0.000000},
"a117": {GlyphName: "a117", Wx: 690.000000, Wy: 0.000000},
"a118": {GlyphName: "a118", Wx: 791.000000, Wy: 0.000000},
"a119": {GlyphName: "a119", Wx: 790.000000, Wy: 0.000000},
"a12": {GlyphName: "a12", Wx: 939.000000, Wy: 0.000000},
"a120": {GlyphName: "a120", Wx: 788.000000, Wy: 0.000000},
"a121": {GlyphName: "a121", Wx: 788.000000, Wy: 0.000000},
"a122": {GlyphName: "a122", Wx: 788.000000, Wy: 0.000000},
"a123": {GlyphName: "a123", Wx: 788.000000, Wy: 0.000000},
"a124": {GlyphName: "a124", Wx: 788.000000, Wy: 0.000000},
"a125": {GlyphName: "a125", Wx: 788.000000, Wy: 0.000000},
"a126": {GlyphName: "a126", Wx: 788.000000, Wy: 0.000000},
"a127": {GlyphName: "a127", Wx: 788.000000, Wy: 0.000000},
"a128": {GlyphName: "a128", Wx: 788.000000, Wy: 0.000000},
"a129": {GlyphName: "a129", Wx: 788.000000, Wy: 0.000000},
"a13": {GlyphName: "a13", Wx: 549.000000, Wy: 0.000000},
"a130": {GlyphName: "a130", Wx: 788.000000, Wy: 0.000000},
"a131": {GlyphName: "a131", Wx: 788.000000, Wy: 0.000000},
"a132": {GlyphName: "a132", Wx: 788.000000, Wy: 0.000000},
"a133": {GlyphName: "a133", Wx: 788.000000, Wy: 0.000000},
"a134": {GlyphName: "a134", Wx: 788.000000, Wy: 0.000000},
"a135": {GlyphName: "a135", Wx: 788.000000, Wy: 0.000000},
"a136": {GlyphName: "a136", Wx: 788.000000, Wy: 0.000000},
"a137": {GlyphName: "a137", Wx: 788.000000, Wy: 0.000000},
"a138": {GlyphName: "a138", Wx: 788.000000, Wy: 0.000000},
"a139": {GlyphName: "a139", Wx: 788.000000, Wy: 0.000000},
"a14": {GlyphName: "a14", Wx: 855.000000, Wy: 0.000000},
"a140": {GlyphName: "a140", Wx: 788.000000, Wy: 0.000000},
"a141": {GlyphName: "a141", Wx: 788.000000, Wy: 0.000000},
"a142": {GlyphName: "a142", Wx: 788.000000, Wy: 0.000000},
"a143": {GlyphName: "a143", Wx: 788.000000, Wy: 0.000000},
"a144": {GlyphName: "a144", Wx: 788.000000, Wy: 0.000000},
"a145": {GlyphName: "a145", Wx: 788.000000, Wy: 0.000000},
"a146": {GlyphName: "a146", Wx: 788.000000, Wy: 0.000000},
"a147": {GlyphName: "a147", Wx: 788.000000, Wy: 0.000000},
"a148": {GlyphName: "a148", Wx: 788.000000, Wy: 0.000000},
"a149": {GlyphName: "a149", Wx: 788.000000, Wy: 0.000000},
"a15": {GlyphName: "a15", Wx: 911.000000, Wy: 0.000000},
"a150": {GlyphName: "a150", Wx: 788.000000, Wy: 0.000000},
"a151": {GlyphName: "a151", Wx: 788.000000, Wy: 0.000000},
"a152": {GlyphName: "a152", Wx: 788.000000, Wy: 0.000000},
"a153": {GlyphName: "a153", Wx: 788.000000, Wy: 0.000000},
"a154": {GlyphName: "a154", Wx: 788.000000, Wy: 0.000000},
"a155": {GlyphName: "a155", Wx: 788.000000, Wy: 0.000000},
"a156": {GlyphName: "a156", Wx: 788.000000, Wy: 0.000000},
"a157": {GlyphName: "a157", Wx: 788.000000, Wy: 0.000000},
"a158": {GlyphName: "a158", Wx: 788.000000, Wy: 0.000000},
"a159": {GlyphName: "a159", Wx: 788.000000, Wy: 0.000000},
"a16": {GlyphName: "a16", Wx: 933.000000, Wy: 0.000000},
"a160": {GlyphName: "a160", Wx: 894.000000, Wy: 0.000000},
"a161": {GlyphName: "a161", Wx: 838.000000, Wy: 0.000000},
"a162": {GlyphName: "a162", Wx: 924.000000, Wy: 0.000000},
"a163": {GlyphName: "a163", Wx: 1016.000000, Wy: 0.000000},
"a164": {GlyphName: "a164", Wx: 458.000000, Wy: 0.000000},
"a165": {GlyphName: "a165", Wx: 924.000000, Wy: 0.000000},
"a166": {GlyphName: "a166", Wx: 918.000000, Wy: 0.000000},
"a167": {GlyphName: "a167", Wx: 927.000000, Wy: 0.000000},
"a168": {GlyphName: "a168", Wx: 928.000000, Wy: 0.000000},
"a169": {GlyphName: "a169", Wx: 928.000000, Wy: 0.000000},
"a17": {GlyphName: "a17", Wx: 945.000000, Wy: 0.000000},
"a170": {GlyphName: "a170", Wx: 834.000000, Wy: 0.000000},
"a171": {GlyphName: "a171", Wx: 873.000000, Wy: 0.000000},
"a172": {GlyphName: "a172", Wx: 828.000000, Wy: 0.000000},
"a173": {GlyphName: "a173", Wx: 924.000000, Wy: 0.000000},
"a174": {GlyphName: "a174", Wx: 917.000000, Wy: 0.000000},
"a175": {GlyphName: "a175", Wx: 930.000000, Wy: 0.000000},
"a176": {GlyphName: "a176", Wx: 931.000000, Wy: 0.000000},
"a177": {GlyphName: "a177", Wx: 463.000000, Wy: 0.000000},
"a178": {GlyphName: "a178", Wx: 883.000000, Wy: 0.000000},
"a179": {GlyphName: "a179", Wx: 836.000000, Wy: 0.000000},
"a18": {GlyphName: "a18", Wx: 974.000000, Wy: 0.000000},
"a180": {GlyphName: "a180", Wx: 867.000000, Wy: 0.000000},
"a181": {GlyphName: "a181", Wx: 696.000000, Wy: 0.000000},
"a182": {GlyphName: "a182", Wx: 874.000000, Wy: 0.000000},
"a183": {GlyphName: "a183", Wx: 760.000000, Wy: 0.000000},
"a184": {GlyphName: "a184", Wx: 946.000000, Wy: 0.000000},
"a185": {GlyphName: "a185", Wx: 865.000000, Wy: 0.000000},
"a186": {GlyphName: "a186", Wx: 967.000000, Wy: 0.000000},
"a187": {GlyphName: "a187", Wx: 831.000000, Wy: 0.000000},
"a188": {GlyphName: "a188", Wx: 873.000000, Wy: 0.000000},
"a189": {GlyphName: "a189", Wx: 927.000000, Wy: 0.000000},
"a19": {GlyphName: "a19", Wx: 755.000000, Wy: 0.000000},
"a190": {GlyphName: "a190", Wx: 970.000000, Wy: 0.000000},
"a191": {GlyphName: "a191", Wx: 918.000000, Wy: 0.000000},
"a192": {GlyphName: "a192", Wx: 748.000000, Wy: 0.000000},
"a193": {GlyphName: "a193", Wx: 836.000000, Wy: 0.000000},
"a194": {GlyphName: "a194", Wx: 771.000000, Wy: 0.000000},
"a195": {GlyphName: "a195", Wx: 888.000000, Wy: 0.000000},
"a196": {GlyphName: "a196", Wx: 748.000000, Wy: 0.000000},
"a197": {GlyphName: "a197", Wx: 771.000000, Wy: 0.000000},
"a198": {GlyphName: "a198", Wx: 888.000000, Wy: 0.000000},
"a199": {GlyphName: "a199", Wx: 867.000000, Wy: 0.000000},
"a2": {GlyphName: "a2", Wx: 961.000000, Wy: 0.000000},
"a20": {GlyphName: "a20", Wx: 846.000000, Wy: 0.000000},
"a200": {GlyphName: "a200", Wx: 696.000000, Wy: 0.000000},
"a201": {GlyphName: "a201", Wx: 874.000000, Wy: 0.000000},
"a202": {GlyphName: "a202", Wx: 974.000000, Wy: 0.000000},
"a203": {GlyphName: "a203", Wx: 762.000000, Wy: 0.000000},
"a204": {GlyphName: "a204", Wx: 759.000000, Wy: 0.000000},
"a205": {GlyphName: "a205", Wx: 509.000000, Wy: 0.000000},
"a206": {GlyphName: "a206", Wx: 410.000000, Wy: 0.000000},
"a21": {GlyphName: "a21", Wx: 762.000000, Wy: 0.000000},
"a22": {GlyphName: "a22", Wx: 761.000000, Wy: 0.000000},
"a23": {GlyphName: "a23", Wx: 571.000000, Wy: 0.000000},
"a24": {GlyphName: "a24", Wx: 677.000000, Wy: 0.000000},
"a25": {GlyphName: "a25", Wx: 763.000000, Wy: 0.000000},
"a26": {GlyphName: "a26", Wx: 760.000000, Wy: 0.000000},
"a27": {GlyphName: "a27", Wx: 759.000000, Wy: 0.000000},
"a28": {GlyphName: "a28", Wx: 754.000000, Wy: 0.000000},
"a29": {GlyphName: "a29", Wx: 786.000000, Wy: 0.000000},
"a3": {GlyphName: "a3", Wx: 980.000000, Wy: 0.000000},
"a30": {GlyphName: "a30", Wx: 788.000000, Wy: 0.000000},
"a31": {GlyphName: "a31", Wx: 788.000000, Wy: 0.000000},
"a32": {GlyphName: "a32", Wx: 790.000000, Wy: 0.000000},
"a33": {GlyphName: "a33", Wx: 793.000000, Wy: 0.000000},
"a34": {GlyphName: "a34", Wx: 794.000000, Wy: 0.000000},
"a35": {GlyphName: "a35", Wx: 816.000000, Wy: 0.000000},
"a36": {GlyphName: "a36", Wx: 823.000000, Wy: 0.000000},
"a37": {GlyphName: "a37", Wx: 789.000000, Wy: 0.000000},
"a38": {GlyphName: "a38", Wx: 841.000000, Wy: 0.000000},
"a39": {GlyphName: "a39", Wx: 823.000000, Wy: 0.000000},
"a4": {GlyphName: "a4", Wx: 719.000000, Wy: 0.000000},
"a40": {GlyphName: "a40", Wx: 833.000000, Wy: 0.000000},
"a41": {GlyphName: "a41", Wx: 816.000000, Wy: 0.000000},
"a42": {GlyphName: "a42", Wx: 831.000000, Wy: 0.000000},
"a43": {GlyphName: "a43", Wx: 923.000000, Wy: 0.000000},
"a44": {GlyphName: "a44", Wx: 744.000000, Wy: 0.000000},
"a45": {GlyphName: "a45", Wx: 723.000000, Wy: 0.000000},
"a46": {GlyphName: "a46", Wx: 749.000000, Wy: 0.000000},
"a47": {GlyphName: "a47", Wx: 790.000000, Wy: 0.000000},
"a48": {GlyphName: "a48", Wx: 792.000000, Wy: 0.000000},
"a49": {GlyphName: "a49", Wx: 695.000000, Wy: 0.000000},
"a5": {GlyphName: "a5", Wx: 789.000000, Wy: 0.000000},
"a50": {GlyphName: "a50", Wx: 776.000000, Wy: 0.000000},
"a51": {GlyphName: "a51", Wx: 768.000000, Wy: 0.000000},
"a52": {GlyphName: "a52", Wx: 792.000000, Wy: 0.000000},
"a53": {GlyphName: "a53", Wx: 759.000000, Wy: 0.000000},
"a54": {GlyphName: "a54", Wx: 707.000000, Wy: 0.000000},
"a55": {GlyphName: "a55", Wx: 708.000000, Wy: 0.000000},
"a56": {GlyphName: "a56", Wx: 682.000000, Wy: 0.000000},
"a57": {GlyphName: "a57", Wx: 701.000000, Wy: 0.000000},
"a58": {GlyphName: "a58", Wx: 826.000000, Wy: 0.000000},
"a59": {GlyphName: "a59", Wx: 815.000000, Wy: 0.000000},
"a6": {GlyphName: "a6", Wx: 494.000000, Wy: 0.000000},
"a60": {GlyphName: "a60", Wx: 789.000000, Wy: 0.000000},
"a61": {GlyphName: "a61", Wx: 789.000000, Wy: 0.000000},
"a62": {GlyphName: "a62", Wx: 707.000000, Wy: 0.000000},
"a63": {GlyphName: "a63", Wx: 687.000000, Wy: 0.000000},
"a64": {GlyphName: "a64", Wx: 696.000000, Wy: 0.000000},
"a65": {GlyphName: "a65", Wx: 689.000000, Wy: 0.000000},
"a66": {GlyphName: "a66", Wx: 786.000000, Wy: 0.000000},
"a67": {GlyphName: "a67", Wx: 787.000000, Wy: 0.000000},
"a68": {GlyphName: "a68", Wx: 713.000000, Wy: 0.000000},
"a69": {GlyphName: "a69", Wx: 791.000000, Wy: 0.000000},
"a7": {GlyphName: "a7", Wx: 552.000000, Wy: 0.000000},
"a70": {GlyphName: "a70", Wx: 785.000000, Wy: 0.000000},
"a71": {GlyphName: "a71", Wx: 791.000000, Wy: 0.000000},
"a72": {GlyphName: "a72", Wx: 873.000000, Wy: 0.000000},
"a73": {GlyphName: "a73", Wx: 761.000000, Wy: 0.000000},
"a74": {GlyphName: "a74", Wx: 762.000000, Wy: 0.000000},
"a75": {GlyphName: "a75", Wx: 759.000000, Wy: 0.000000},
"a76": {GlyphName: "a76", Wx: 892.000000, Wy: 0.000000},
"a77": {GlyphName: "a77", Wx: 892.000000, Wy: 0.000000},
"a78": {GlyphName: "a78", Wx: 788.000000, Wy: 0.000000},
"a79": {GlyphName: "a79", Wx: 784.000000, Wy: 0.000000},
"a8": {GlyphName: "a8", Wx: 537.000000, Wy: 0.000000},
"a81": {GlyphName: "a81", Wx: 438.000000, Wy: 0.000000},
"a82": {GlyphName: "a82", Wx: 138.000000, Wy: 0.000000},
"a83": {GlyphName: "a83", Wx: 277.000000, Wy: 0.000000},
"a84": {GlyphName: "a84", Wx: 415.000000, Wy: 0.000000},
"a85": {GlyphName: "a85", Wx: 509.000000, Wy: 0.000000},
"a86": {GlyphName: "a86", Wx: 410.000000, Wy: 0.000000},
"a87": {GlyphName: "a87", Wx: 234.000000, Wy: 0.000000},
"a88": {GlyphName: "a88", Wx: 234.000000, Wy: 0.000000},
"a89": {GlyphName: "a89", Wx: 390.000000, Wy: 0.000000},
"a9": {GlyphName: "a9", Wx: 577.000000, Wy: 0.000000},
"a90": {GlyphName: "a90", Wx: 390.000000, Wy: 0.000000},
"a91": {GlyphName: "a91", Wx: 276.000000, Wy: 0.000000},
"a92": {GlyphName: "a92", Wx: 276.000000, Wy: 0.000000},
"a93": {GlyphName: "a93", Wx: 317.000000, Wy: 0.000000},
"a94": {GlyphName: "a94", Wx: 317.000000, Wy: 0.000000},
"a95": {GlyphName: "a95", Wx: 334.000000, Wy: 0.000000},
"a96": {GlyphName: "a96", Wx: 334.000000, Wy: 0.000000},
"a97": {GlyphName: "a97", Wx: 392.000000, Wy: 0.000000},
"a98": {GlyphName: "a98", Wx: 392.000000, Wy: 0.000000},
"a99": {GlyphName: "a99", Wx: 668.000000, Wy: 0.000000},
"space": {GlyphName: "space", Wx: 278.000000, Wy: 0.000000},
}

412
internal/pdf/model/forms.go Normal file
View File

@@ -0,0 +1,412 @@
package model
import (
"fmt"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
)
// High level manipulation of forms (AcroForm).
type PdfAcroForm struct {
Fields *[]*PdfField
NeedAppearances *core.PdfObjectBool
SigFlags *core.PdfObjectInteger
CO *core.PdfObjectArray
DR *PdfPageResources
DA *core.PdfObjectString
Q *core.PdfObjectInteger
XFA core.PdfObject
primitive *core.PdfIndirectObject
}
func NewPdfAcroForm() *PdfAcroForm {
acroForm := &PdfAcroForm{}
container := &core.PdfIndirectObject{}
container.PdfObject = core.MakeDict()
acroForm.primitive = container
return acroForm
}
// Used when loading forms from PDF files.
func (r *PdfReader) newPdfAcroFormFromDict(d *core.PdfObjectDictionary) (*PdfAcroForm, error) {
acroForm := NewPdfAcroForm()
if obj := d.Get("Fields"); obj != nil {
obj, err := r.traceToObject(obj)
if err != nil {
return nil, err
}
fieldArray, ok := core.TraceToDirectObject(obj).(*core.PdfObjectArray)
if !ok {
return nil, fmt.Errorf("fields not an array (%T)", obj)
}
fields := []*PdfField{}
for _, obj := range *fieldArray {
obj, err := r.traceToObject(obj)
if err != nil {
return nil, err
}
container, isIndirect := obj.(*core.PdfIndirectObject)
if !isIndirect {
if _, isNull := obj.(*core.PdfObjectNull); isNull {
common.Log.Trace("Skipping over null field")
continue
}
common.Log.Debug("Field not contained in indirect object %T", obj)
return nil, fmt.Errorf("field not in an indirect object")
}
field, err := r.newPdfFieldFromIndirectObject(container, nil)
if err != nil {
return nil, err
}
common.Log.Trace("AcroForm Field: %+v", *field)
fields = append(fields, field)
}
acroForm.Fields = &fields
}
if obj := d.Get("NeedAppearances"); obj != nil {
val, ok := obj.(*core.PdfObjectBool)
if ok {
acroForm.NeedAppearances = val
} else {
common.Log.Debug("error: NeedAppearances invalid (got %T)", obj)
}
}
if obj := d.Get("SigFlags"); obj != nil {
val, ok := obj.(*core.PdfObjectInteger)
if ok {
acroForm.SigFlags = val
} else {
common.Log.Debug("error: SigFlags invalid (got %T)", obj)
}
}
if obj := d.Get("CO"); obj != nil {
obj = core.TraceToDirectObject(obj)
arr, ok := obj.(*core.PdfObjectArray)
if ok {
acroForm.CO = arr
} else {
common.Log.Debug("error: CO invalid (got %T)", obj)
}
}
if obj := d.Get("DR"); obj != nil {
obj = core.TraceToDirectObject(obj)
if d, ok := obj.(*core.PdfObjectDictionary); ok {
resources, err := NewPdfPageResourcesFromDict(d)
if err != nil {
common.Log.Error("invalid DR: %v", err)
return nil, err
}
acroForm.DR = resources
} else {
common.Log.Debug("error: DR invalid (got %T)", obj)
}
}
if obj := d.Get("DA"); obj != nil {
str, ok := obj.(*core.PdfObjectString)
if ok {
acroForm.DA = str
} else {
common.Log.Debug("error: DA invalid (got %T)", obj)
}
}
if obj := d.Get("Q"); obj != nil {
val, ok := obj.(*core.PdfObjectInteger)
if ok {
acroForm.Q = val
} else {
common.Log.Debug("error: Q invalid (got %T)", obj)
}
}
if obj := d.Get("XFA"); obj != nil {
acroForm.XFA = obj
}
return acroForm, nil
}
func (af *PdfAcroForm) GetContainingPdfObject() core.PdfObject {
return af.primitive
}
func (af *PdfAcroForm) ToPdfObject() core.PdfObject {
container := af.primitive
dict := container.PdfObject.(*core.PdfObjectDictionary)
if af.Fields != nil {
arr := core.PdfObjectArray{}
for _, field := range *af.Fields {
arr = append(arr, field.ToPdfObject())
}
dict.Set("Fields", &arr)
}
if af.NeedAppearances != nil {
dict.Set("NeedAppearances", af.NeedAppearances)
}
if af.SigFlags != nil {
dict.Set("SigFlags", af.SigFlags)
}
if af.CO != nil {
dict.Set("CO", af.CO)
}
if af.DR != nil {
dict.Set("DR", af.DR.ToPdfObject())
}
if af.DA != nil {
dict.Set("DA", af.DA)
}
if af.Q != nil {
dict.Set("Q", af.Q)
}
if af.XFA != nil {
dict.Set("XFA", af.XFA)
}
return container
}
// PdfField represents a field of an interactive form.
// Implements PdfModel interface.
type PdfField struct {
FT *core.PdfObjectName // field type
Parent *PdfField
// In a non-terminal field, the Kids array shall refer to field dictionaries that are immediate descendants of this field.
// In a terminal field, the Kids array ordinarily shall refer to one or more separate widget annotations that are associated
// with this field. However, if there is only one associated widget annotation, and its contents have been merged into the field
// dictionary, Kids shall be omitted.
KidsF []PdfModel // Kids can be array of other fields or widgets (PdfModel).
KidsA []*PdfAnnotation
T core.PdfObject
TU core.PdfObject
TM core.PdfObject
Ff core.PdfObject // field flag
V core.PdfObject //value
DV core.PdfObject
AA core.PdfObject
// Variable Text:
DA core.PdfObject
Q core.PdfObject
DS core.PdfObject
RV core.PdfObject
primitive *core.PdfIndirectObject
}
func NewPdfField() *PdfField {
field := &PdfField{}
container := &core.PdfIndirectObject{}
container.PdfObject = core.MakeDict()
field.primitive = container
return field
}
// Used when loading fields from PDF files.
func (r *PdfReader) newPdfFieldFromIndirectObject(container *core.PdfIndirectObject, parent *PdfField) (*PdfField, error) {
d, isDict := container.PdfObject.(*core.PdfObjectDictionary)
if !isDict {
return nil, fmt.Errorf("pdf Field indirect object not containing a dictionary")
}
field := NewPdfField()
// Field type (required in terminal fields).
// Can be /Btn /Tx /Ch /Sig
// Required for a terminal field (inheritable).
var err error
if obj := d.Get("FT"); obj != nil {
obj, err = r.traceToObject(obj)
if err != nil {
return nil, err
}
name, ok := obj.(*core.PdfObjectName)
if !ok {
return nil, fmt.Errorf("invalid type of FT field (%T)", obj)
}
field.FT = name
}
// Partial field name (Optional)
field.T = d.Get("T")
// Alternate description (Optional)
field.TU = d.Get("TU")
// Mapping name (Optional)
field.TM = d.Get("TM")
// Field flag. (Optional; inheritable)
field.Ff = d.Get("Ff")
// Value (Optional; inheritable) - Various types depending on the field type.
field.V = d.Get("V")
// Default value for reset (Optional; inheritable)
field.DV = d.Get("DV")
// Additional actions dictionary (Optional)
field.AA = d.Get("AA")
// Variable text:
field.DA = d.Get("DA")
field.Q = d.Get("Q")
field.DS = d.Get("DS")
field.RV = d.Get("RV")
// In a non-terminal field, the Kids array shall refer to field dictionaries that are immediate descendants of this field.
// In a terminal field, the Kids array ordinarily shall refer to one or more separate widget annotations that are associated
// with this field. However, if there is only one associated widget annotation, and its contents have been merged into the field
// dictionary, Kids shall be omitted.
// Set ourself?
if parent != nil {
field.Parent = parent
}
// Has a merged-in widget annotation?
if obj := d.Get("Subtype"); obj != nil {
obj, err = r.traceToObject(obj)
if err != nil {
return nil, err
}
common.Log.Trace("Merged in annotation (%T)", obj)
name, ok := obj.(*core.PdfObjectName)
if !ok {
return nil, fmt.Errorf("invalid type of Subtype (%T)", obj)
}
if *name == "Widget" {
// Is a merged field / widget dict.
// Check if the annotation has already been loaded?
// Most likely referenced to by a page... Could be in either direction.
// r.newPdfAnnotationFromIndirectObject acts as a caching mechanism.
annot, err := r.newPdfAnnotationFromIndirectObject(container)
if err != nil {
return nil, err
}
widget, ok := annot.GetContext().(*PdfAnnotationWidget)
if !ok {
return nil, fmt.Errorf("invalid widget")
}
widget.Parent = field.GetContainingPdfObject()
field.KidsA = append(field.KidsA, annot)
return field, nil
}
}
if obj := d.Get("Kids"); obj != nil {
obj, err := r.traceToObject(obj)
if err != nil {
return nil, err
}
fieldArray, ok := core.TraceToDirectObject(obj).(*core.PdfObjectArray)
if !ok {
return nil, fmt.Errorf("kids not an array (%T)", obj)
}
field.KidsF = []PdfModel{}
for _, obj := range *fieldArray {
obj, err := r.traceToObject(obj)
if err != nil {
return nil, err
}
container, isIndirect := obj.(*core.PdfIndirectObject)
if !isIndirect {
return nil, fmt.Errorf("not an indirect object (form field)")
}
childField, err := r.newPdfFieldFromIndirectObject(container, field)
if err != nil {
return nil, err
}
field.KidsF = append(field.KidsF, childField)
}
}
return field, nil
}
func (pf *PdfField) GetContainingPdfObject() core.PdfObject {
return pf.primitive
}
// If Kids refer only to a single pdf widget annotation widget, then can merge it in.
// Currently not merging it in.
func (pf *PdfField) ToPdfObject() core.PdfObject {
container := pf.primitive
dict := container.PdfObject.(*core.PdfObjectDictionary)
if pf.Parent != nil {
dict.Set("Parent", pf.Parent.GetContainingPdfObject())
}
if pf.KidsF != nil {
// Create an array of the kids (fields or widgets).
common.Log.Trace("KidsF: %+v", pf.KidsF)
arr := core.PdfObjectArray{}
for _, child := range pf.KidsF {
arr = append(arr, child.ToPdfObject())
}
dict.Set("Kids", &arr)
}
if pf.KidsA != nil {
common.Log.Trace("KidsA: %+v", pf.KidsA)
_, hasKids := dict.Get("Kids").(*core.PdfObjectArray)
if !hasKids {
dict.Set("Kids", &core.PdfObjectArray{})
}
arr := dict.Get("Kids").(*core.PdfObjectArray)
for _, child := range pf.KidsA {
*arr = append(*arr, child.GetContext().ToPdfObject())
}
}
if pf.FT != nil {
dict.Set("FT", pf.FT)
}
if pf.T != nil {
dict.Set("T", pf.T)
}
if pf.TU != nil {
dict.Set("TU", pf.TU)
}
if pf.TM != nil {
dict.Set("TM", pf.TM)
}
if pf.Ff != nil {
dict.Set("Ff", pf.Ff)
}
if pf.V != nil {
dict.Set("V", pf.V)
}
if pf.DV != nil {
dict.Set("DV", pf.DV)
}
if pf.AA != nil {
dict.Set("AA", pf.AA)
}
// Variable text:
dict.SetIfNotNil("DA", pf.DA)
dict.SetIfNotNil("Q", pf.Q)
dict.SetIfNotNil("DS", pf.DS)
dict.SetIfNotNil("RV", pf.RV)
return container
}

View File

@@ -0,0 +1,853 @@
package model
import (
"errors"
"math"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model/sampling"
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/ps"
)
type PdfValue any
type PdfFunction interface {
Evaluate([]float64) ([]float64, error)
ToPdfObject() core.PdfObject
}
// In PDF: A function object may be a dictionary or a stream, depending on the type of function.
// - Stream: Type 0, Type 4
// - Dictionary: Type 2, Type 3.
// Loads a PDF Function from a PdfObject (can be either stream or dictionary).
func newPdfFunctionFromPdfObject(obj core.PdfObject) (PdfFunction, error) {
if stream, is := obj.(*core.PdfObjectStream); is {
dict := stream.PdfObjectDictionary
ftype, ok := dict.Get("FunctionType").(*core.PdfObjectInteger)
if !ok {
common.Log.Error("FunctionType number missing")
return nil, errors.New("invalid parameter or missing")
}
switch *ftype {
case 0:
return newPdfFunctionType0FromStream(stream)
case 4:
return newPdfFunctionType4FromStream(stream)
default:
return nil, errors.New("invalid function type")
}
} else if indObj, is := obj.(*core.PdfIndirectObject); is {
// Indirect object containing a dictionary.
// The indirect object is the container (which is tracked).
dict, ok := indObj.PdfObject.(*core.PdfObjectDictionary)
if !ok {
common.Log.Error("Function Indirect object not containing dictionary")
return nil, errors.New("invalid parameter or missing")
}
ftype, ok := dict.Get("FunctionType").(*core.PdfObjectInteger)
if !ok {
common.Log.Error("FunctionType number missing")
return nil, errors.New("invalid parameter or missing")
}
switch *ftype {
case 2:
return newPdfFunctionType2FromPdfObject(indObj)
case 3:
return newPdfFunctionType3FromPdfObject(indObj)
default:
return nil, errors.New("invalid function type")
}
} else if dict, is := obj.(*core.PdfObjectDictionary); is {
ftype, ok := dict.Get("FunctionType").(*core.PdfObjectInteger)
if !ok {
common.Log.Error("FunctionType number missing")
return nil, errors.New("invalid parameter or missing")
}
switch *ftype {
case 2:
return newPdfFunctionType2FromPdfObject(dict)
case 3:
return newPdfFunctionType3FromPdfObject(dict)
default:
return nil, errors.New("invalid function type")
}
} else {
common.Log.Debug("Function Type error: %#v", obj)
return nil, errors.New("type error")
}
}
// Simple linear interpolation from the PDF manual.
func interpolate(x, xmin, xmax, ymin, ymax float64) float64 {
if math.Abs(xmax-xmin) < 0.000001 {
return ymin
}
y := ymin + (x-xmin)*(ymax-ymin)/(xmax-xmin)
return y
}
// Type 0 functions use a sequence of sample values (contained in a stream) to provide an approximation
// for functions whose domains and ranges are bounded. The samples are organized as an m-dimensional
// table in which each entry has n components
type PdfFunctionType0 struct {
Domain []float64 // required; 2*m length; where m is the number of input values
Range []float64 // required (type 0); 2*n length; where n is the number of output values
NumInputs int
NumOutputs int
Size []int
BitsPerSample int
Order int // Values 1 or 3 (linear or cubic spline interpolation)
Encode []float64
Decode []float64
rawData []byte
data []uint32
container *core.PdfObjectStream
}
// Construct the PDF function object from a stream object (typically loaded from a PDF file).
func newPdfFunctionType0FromStream(stream *core.PdfObjectStream) (*PdfFunctionType0, error) {
fun := &PdfFunctionType0{}
fun.container = stream
dict := stream.PdfObjectDictionary
// Domain
array, has := core.TraceToDirectObject(dict.Get("Domain")).(*core.PdfObjectArray)
if !has {
common.Log.Error("Domain not specified")
return nil, errors.New("required attribute missing or invalid")
}
if len(*array)%2 != 0 {
common.Log.Error("Domain invalid")
return nil, errors.New("invalid domain range")
}
fun.NumInputs = len(*array) / 2
domain, err := array.ToFloat64Array()
if err != nil {
return nil, err
}
fun.Domain = domain
// Range
array, has = core.TraceToDirectObject(dict.Get("Range")).(*core.PdfObjectArray)
if !has {
common.Log.Error("Range not specified")
return nil, errors.New("required attribute missing or invalid")
}
if len(*array)%2 != 0 {
return nil, errors.New("invalid range")
}
fun.NumOutputs = len(*array) / 2
rang, err := array.ToFloat64Array()
if err != nil {
return nil, err
}
fun.Range = rang
// Number of samples in each input dimension
array, has = core.TraceToDirectObject(dict.Get("Size")).(*core.PdfObjectArray)
if !has {
common.Log.Error("Size not specified")
return nil, errors.New("required attribute missing or invalid")
}
tablesize, err := array.ToIntegerArray()
if err != nil {
return nil, err
}
if len(tablesize) != fun.NumInputs {
common.Log.Error("Table size not matching number of inputs")
return nil, errors.New("range check")
}
fun.Size = tablesize
// BitsPerSample
bps, has := core.TraceToDirectObject(dict.Get("BitsPerSample")).(*core.PdfObjectInteger)
if !has {
common.Log.Error("BitsPerSample not specified")
return nil, errors.New("required attribute missing or invalid")
}
if *bps != 1 && *bps != 2 && *bps != 4 && *bps != 8 && *bps != 12 && *bps != 16 && *bps != 24 && *bps != 32 {
common.Log.Error("Bits per sample outside range (%d)", *bps)
return nil, errors.New("range check")
}
fun.BitsPerSample = int(*bps)
fun.Order = 1
order, has := core.TraceToDirectObject(dict.Get("Order")).(*core.PdfObjectInteger)
if has {
if *order != 1 && *order != 3 {
common.Log.Error("invalid order (%d)", *order)
return nil, errors.New("range check")
}
fun.Order = int(*order)
}
// Encode: is a 2*m array specifying the linear mapping of input values into the domain of the function's
// sample table.
array, has = core.TraceToDirectObject(dict.Get("Encode")).(*core.PdfObjectArray)
if has {
encode, err := array.ToFloat64Array()
if err != nil {
return nil, err
}
fun.Encode = encode
}
// Decode
array, has = core.TraceToDirectObject(dict.Get("Decode")).(*core.PdfObjectArray)
if has {
decode, err := array.ToFloat64Array()
if err != nil {
return nil, err
}
fun.Decode = decode
}
data, err := core.DecodeStream(stream)
if err != nil {
return nil, err
}
fun.rawData = data
return fun, nil
}
func (pft *PdfFunctionType0) ToPdfObject() core.PdfObject {
container := pft.container
if container != nil {
pft.container = &core.PdfObjectStream{}
}
dict := core.MakeDict()
dict.Set("FunctionType", core.MakeInteger(0))
// Domain (required).
domainArray := &core.PdfObjectArray{}
for _, val := range pft.Domain {
domainArray.Append(core.MakeFloat(val))
}
dict.Set("Domain", domainArray)
// Range (required).
rangeArray := &core.PdfObjectArray{}
for _, val := range pft.Range {
rangeArray.Append(core.MakeFloat(val))
}
dict.Set("Range", rangeArray)
// Size (required).
sizeArray := &core.PdfObjectArray{}
for _, val := range pft.Size {
sizeArray.Append(core.MakeInteger(int64(val)))
}
dict.Set("Size", sizeArray)
dict.Set("BitsPerSample", core.MakeInteger(int64(pft.BitsPerSample)))
if pft.Order != 1 {
dict.Set("Order", core.MakeInteger(int64(pft.Order)))
}
// TODO: Encode.
// Either here, or automatically later on when writing out.
dict.Set("Length", core.MakeInteger(int64(len(pft.rawData))))
container.Stream = pft.rawData
container.PdfObjectDictionary = dict
return container
}
func (pft *PdfFunctionType0) Evaluate(x []float64) ([]float64, error) {
if len(x) != pft.NumInputs {
common.Log.Error("Number of inputs not matching what is needed")
return nil, errors.New("range check error")
}
if pft.data == nil {
// Process the samples if not already done.
err := pft.processSamples()
if err != nil {
return nil, err
}
}
// Fall back to default Encode/Decode params if not set.
encode := pft.Encode
if encode == nil {
encode = []float64{}
for i := 0; i < len(pft.Size); i++ {
encode = append(encode, 0)
encode = append(encode, float64(pft.Size[i]-1))
}
}
decode := pft.Decode
if decode == nil {
decode = pft.Range
}
indices := []int{}
// Start with nearest neighbour interpolation.
for i := 0; i < len(x); i++ {
xi := x[i]
xip := math.Min(math.Max(xi, pft.Domain[2*i]), pft.Domain[2*i+1])
ei := interpolate(xip, pft.Domain[2*i], pft.Domain[2*i+1], encode[2*i], encode[2*i+1])
eip := math.Min(math.Max(ei, 0), float64(pft.Size[i]))
// eip represents coordinate into the data table.
// At this point it is real values.
// Interpolation shall be used to to determine output values
// from the nearest surrounding values in the sample table.
// Initial implementation is simply nearest neighbour.
// Then will add the linear and possibly bicubic/spline.
index := int(math.Floor(eip + 0.5))
if index < 0 {
index = 0
} else if index > pft.Size[i] {
index = pft.Size[i] - 1
}
indices = append(indices, index)
}
// Calculate the index
m := indices[0]
for i := 1; i < pft.NumInputs; i++ {
add := indices[i]
for j := 0; j < i; j++ {
add *= pft.Size[j]
}
m += add
}
m *= pft.NumOutputs
// Output values.
outputs := []float64{}
for j := 0; j < pft.NumOutputs; j++ {
rj := pft.data[m+j]
rjp := interpolate(float64(rj), 0, math.Pow(2, float64(pft.BitsPerSample)), decode[2*j], decode[2*j+1])
yj := math.Min(math.Max(rjp, pft.Range[2*j]), pft.Range[2*j+1])
outputs = append(outputs, yj)
}
return outputs, nil
}
// Convert raw data to data table. The maximum supported BitsPerSample is 32, so we store the resulting data
// in a uint32 array. This is somewhat wasteful in the case of a small BitsPerSample, but these tables are
// presumably not huge at any rate.
func (pft *PdfFunctionType0) processSamples() error {
data := sampling.ResampleBytes(pft.rawData, pft.BitsPerSample)
pft.data = data
return nil
}
// Type 2 functions define an exponential interpolation of one input value and n
// output values:
//
// f(x) = y_0, ..., y_(n-1)
//
// y_j = C0_j + x^N * (C1_j - C0_j); for 0 <= j < n
// When N=1 ; linear interpolation between C0 and C1.
type PdfFunctionType2 struct {
Domain []float64
Range []float64
C0 []float64
C1 []float64
N float64
container *core.PdfIndirectObject
}
// Can be either indirect object or dictionary. If indirect, then must be holding a dictionary,
// i.e. acting as a container. When converting back to pdf object, will use the container provided.
func newPdfFunctionType2FromPdfObject(obj core.PdfObject) (*PdfFunctionType2, error) {
fun := &PdfFunctionType2{}
var dict *core.PdfObjectDictionary
if indObj, is := obj.(*core.PdfIndirectObject); is {
d, ok := indObj.PdfObject.(*core.PdfObjectDictionary)
if !ok {
return nil, errors.New("type check error")
}
fun.container = indObj
dict = d
} else if d, is := obj.(*core.PdfObjectDictionary); is {
dict = d
} else {
return nil, errors.New("type check error")
}
common.Log.Trace("FUNC2: %s", dict.String())
// Domain
array, has := core.TraceToDirectObject(dict.Get("Domain")).(*core.PdfObjectArray)
if !has {
common.Log.Error("Domain not specified")
return nil, errors.New("required attribute missing or invalid")
}
if len(*array)%2 != 0 {
common.Log.Error("Domain range invalid")
return nil, errors.New("invalid domain range")
}
domain, err := array.ToFloat64Array()
if err != nil {
return nil, err
}
fun.Domain = domain
// Range
array, has = core.TraceToDirectObject(dict.Get("Range")).(*core.PdfObjectArray)
if has {
if len(*array)%2 != 0 {
return nil, errors.New("invalid range")
}
rang, err := array.ToFloat64Array()
if err != nil {
return nil, err
}
fun.Range = rang
}
// C0.
array, has = core.TraceToDirectObject(dict.Get("C0")).(*core.PdfObjectArray)
if has {
c0, err := array.ToFloat64Array()
if err != nil {
return nil, err
}
fun.C0 = c0
}
// C1.
array, has = core.TraceToDirectObject(dict.Get("C1")).(*core.PdfObjectArray)
if has {
c1, err := array.ToFloat64Array()
if err != nil {
return nil, err
}
fun.C1 = c1
}
if len(fun.C0) != len(fun.C1) {
common.Log.Error("C0 and C1 not matching")
return nil, errors.New("range check")
}
// Exponent.
N, err := getNumberAsFloat(core.TraceToDirectObject(dict.Get("N")))
if err != nil {
common.Log.Error("N missing or invalid, dict: %s", dict.String())
return nil, err
}
fun.N = N
return fun, nil
}
func (ftt *PdfFunctionType2) ToPdfObject() core.PdfObject {
dict := core.MakeDict()
dict.Set("FunctionType", core.MakeInteger(2))
// Domain (required).
domainArray := &core.PdfObjectArray{}
for _, val := range ftt.Domain {
domainArray.Append(core.MakeFloat(val))
}
dict.Set("Domain", domainArray)
// Range (required).
if ftt.Range != nil {
rangeArray := &core.PdfObjectArray{}
for _, val := range ftt.Range {
rangeArray.Append(core.MakeFloat(val))
}
dict.Set("Range", rangeArray)
}
// C0.
if ftt.C0 != nil {
c0Array := &core.PdfObjectArray{}
for _, val := range ftt.C0 {
c0Array.Append(core.MakeFloat(val))
}
dict.Set("C0", c0Array)
}
// C1.
if ftt.C1 != nil {
c1Array := &core.PdfObjectArray{}
for _, val := range ftt.C1 {
c1Array.Append(core.MakeFloat(val))
}
dict.Set("C1", c1Array)
}
// exponent
dict.Set("N", core.MakeFloat(ftt.N))
// Wrap in a container if we have one already specified.
if ftt.container != nil {
ftt.container.PdfObject = dict
return ftt.container
} else {
return dict
}
}
func (ftt *PdfFunctionType2) Evaluate(x []float64) ([]float64, error) {
if len(x) != 1 {
common.Log.Error("Only one input allowed")
return nil, errors.New("range check")
}
// Prepare.
c0 := []float64{0.0}
if ftt.C0 != nil {
c0 = ftt.C0
}
c1 := []float64{1.0}
if ftt.C1 != nil {
c1 = ftt.C1
}
y := []float64{}
for i := 0; i < len(c0); i++ {
yi := c0[i] + math.Pow(x[0], ftt.N)*(c1[i]-c0[i])
y = append(y, yi)
}
return y, nil
}
// Type 3 functions define stitching of the subdomains of serveral 1-input functions to produce
// a single new 1-input function.
type PdfFunctionType3 struct {
Domain []float64
Range []float64
Functions []PdfFunction // k-1 input functions
Bounds []float64 // k-1 numbers; defines the intervals where each function applies
Encode []float64 // Array of 2k numbers..
container *core.PdfIndirectObject
}
func (*PdfFunctionType3) Evaluate(x []float64) ([]float64, error) {
if len(x) != 1 {
common.Log.Error("Only one input allowed")
return nil, errors.New("range check")
}
// Determine which function to use
// Encode
return nil, errors.New("not implemented yet")
}
func newPdfFunctionType3FromPdfObject(obj core.PdfObject) (*PdfFunctionType3, error) {
fun := &PdfFunctionType3{}
var dict *core.PdfObjectDictionary
if indObj, is := obj.(*core.PdfIndirectObject); is {
d, ok := indObj.PdfObject.(*core.PdfObjectDictionary)
if !ok {
return nil, errors.New("type check error")
}
fun.container = indObj
dict = d
} else if d, is := obj.(*core.PdfObjectDictionary); is {
dict = d
} else {
return nil, errors.New("type check error")
}
// Domain
array, has := core.TraceToDirectObject(dict.Get("Domain")).(*core.PdfObjectArray)
if !has {
common.Log.Error("Domain not specified")
return nil, errors.New("required attribute missing or invalid")
}
if len(*array) != 2 {
common.Log.Error("Domain invalid")
return nil, errors.New("invalid domain range")
}
domain, err := array.ToFloat64Array()
if err != nil {
return nil, err
}
fun.Domain = domain
// Range
array, has = core.TraceToDirectObject(dict.Get("Range")).(*core.PdfObjectArray)
if has {
if len(*array)%2 != 0 {
return nil, errors.New("invalid range")
}
rang, err := array.ToFloat64Array()
if err != nil {
return nil, err
}
fun.Range = rang
}
// Functions.
array, has = core.TraceToDirectObject(dict.Get("Functions")).(*core.PdfObjectArray)
if !has {
common.Log.Error("Functions not specified")
return nil, errors.New("required attribute missing or invalid")
}
fun.Functions = []PdfFunction{}
for _, obj := range *array {
subf, err := newPdfFunctionFromPdfObject(obj)
if err != nil {
return nil, err
}
fun.Functions = append(fun.Functions, subf)
}
// Bounds
array, has = core.TraceToDirectObject(dict.Get("Bounds")).(*core.PdfObjectArray)
if !has {
common.Log.Error("Bounds not specified")
return nil, errors.New("required attribute missing or invalid")
}
bounds, err := array.ToFloat64Array()
if err != nil {
return nil, err
}
fun.Bounds = bounds
if len(fun.Bounds) != len(fun.Functions)-1 {
common.Log.Error("Bounds (%d) and num functions (%d) not matching", len(fun.Bounds), len(fun.Functions))
return nil, errors.New("range check")
}
// Encode.
array, has = core.TraceToDirectObject(dict.Get("Encode")).(*core.PdfObjectArray)
if !has {
common.Log.Error("Encode not specified")
return nil, errors.New("required attribute missing or invalid")
}
encode, err := array.ToFloat64Array()
if err != nil {
return nil, err
}
fun.Encode = encode
if len(fun.Encode) != 2*len(fun.Functions) {
common.Log.Error("Len encode (%d) and num functions (%d) not matching up", len(fun.Encode), len(fun.Functions))
return nil, errors.New("range check")
}
return fun, nil
}
func (ftt *PdfFunctionType3) ToPdfObject() core.PdfObject {
dict := core.MakeDict()
dict.Set("FunctionType", core.MakeInteger(3))
// Domain (required).
domainArray := &core.PdfObjectArray{}
for _, val := range ftt.Domain {
domainArray.Append(core.MakeFloat(val))
}
dict.Set("Domain", domainArray)
// Range (required).
if ftt.Range != nil {
rangeArray := &core.PdfObjectArray{}
for _, val := range ftt.Range {
rangeArray.Append(core.MakeFloat(val))
}
dict.Set("Range", rangeArray)
}
// Functions
if ftt.Functions != nil {
fArray := &core.PdfObjectArray{}
for _, fun := range ftt.Functions {
fArray.Append(fun.ToPdfObject())
}
dict.Set("Functions", fArray)
}
// Bounds.
if ftt.Bounds != nil {
bArray := &core.PdfObjectArray{}
for _, val := range ftt.Bounds {
bArray.Append(core.MakeFloat(val))
}
dict.Set("Bounds", bArray)
}
// Encode.
if ftt.Encode != nil {
eArray := &core.PdfObjectArray{}
for _, val := range ftt.Encode {
eArray.Append(core.MakeFloat(val))
}
dict.Set("Encode", eArray)
}
// Wrap in a container if we have one already specified.
if ftt.container != nil {
ftt.container.PdfObject = dict
return ftt.container
} else {
return dict
}
}
// Type 4. Postscript calculator functions.
type PdfFunctionType4 struct {
Domain []float64
Range []float64
Program *ps.PSProgram
executor *ps.PSExecutor
decodedData []byte
container *core.PdfObjectStream
}
// Input [x1 x2 x3]
func (ftt *PdfFunctionType4) Evaluate(xVec []float64) ([]float64, error) {
if ftt.executor == nil {
ftt.executor = ps.NewPSExecutor(ftt.Program)
}
inputs := []ps.PSObject{}
for _, val := range xVec {
inputs = append(inputs, ps.MakeReal(val))
}
outputs, err := ftt.executor.Execute(inputs)
if err != nil {
return nil, err
}
// After execution the outputs are on the stack [y1 ... yM]
// Convert to floats.
yVec, err := ps.PSObjectArrayToFloat64Array(outputs)
if err != nil {
return nil, err
}
return yVec, nil
}
// Load a type 4 function from a PDF stream object.
func newPdfFunctionType4FromStream(stream *core.PdfObjectStream) (*PdfFunctionType4, error) {
fun := &PdfFunctionType4{}
fun.container = stream
dict := stream.PdfObjectDictionary
// Domain
array, has := core.TraceToDirectObject(dict.Get("Domain")).(*core.PdfObjectArray)
if !has {
common.Log.Error("Domain not specified")
return nil, errors.New("required attribute missing or invalid")
}
if len(*array)%2 != 0 {
common.Log.Error("Domain invalid")
return nil, errors.New("invalid domain range")
}
domain, err := array.ToFloat64Array()
if err != nil {
return nil, err
}
fun.Domain = domain
// Range
array, has = core.TraceToDirectObject(dict.Get("Range")).(*core.PdfObjectArray)
if has {
if len(*array)%2 != 0 {
return nil, errors.New("invalid range")
}
rang, err := array.ToFloat64Array()
if err != nil {
return nil, err
}
fun.Range = rang
}
// Program. Decode the program and parse the PS code.
decoded, err := core.DecodeStream(stream)
if err != nil {
return nil, err
}
fun.decodedData = decoded
psParser := ps.NewPSParser([]byte(decoded))
prog, err := psParser.Parse()
if err != nil {
return nil, err
}
fun.Program = prog
return fun, nil
}
func (ftt *PdfFunctionType4) ToPdfObject() core.PdfObject {
container := ftt.container
if container == nil {
ftt.container = &core.PdfObjectStream{}
container = ftt.container
}
dict := core.MakeDict()
dict.Set("FunctionType", core.MakeInteger(4))
// Domain (required).
domainArray := &core.PdfObjectArray{}
for _, val := range ftt.Domain {
domainArray.Append(core.MakeFloat(val))
}
dict.Set("Domain", domainArray)
// Range (required).
rangeArray := &core.PdfObjectArray{}
for _, val := range ftt.Range {
rangeArray.Append(core.MakeFloat(val))
}
dict.Set("Range", rangeArray)
if ftt.decodedData == nil && ftt.Program != nil {
// Update data. This is used for created functions (not parsed ones).
ftt.decodedData = []byte(ftt.Program.String())
}
// TODO: Encode.
// Either here, or automatically later on when writing out.
dict.Set("Length", core.MakeInteger(int64(len(ftt.decodedData))))
container.Stream = ftt.decodedData
container.PdfObjectDictionary = dict
return container
}

Some files were not shown because too many files have changed in this diff Show More