Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aafa89a703 | ||
|
|
ef0778c8b3 | ||
|
|
8f313c00f0 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,2 +1,2 @@
|
||||
dst
|
||||
*pdf
|
||||
*.pdf
|
||||
126
internal/pdf/annotator/circle.go
Normal file
126
internal/pdf/annotator/circle.go
Normal 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
|
||||
}
|
||||
133
internal/pdf/annotator/line.go
Normal file
133
internal/pdf/annotator/line.go
Normal 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
|
||||
}
|
||||
129
internal/pdf/annotator/rectangle.go
Normal file
129
internal/pdf/annotator/rectangle.go
Normal 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
401
internal/pdf/cmap/cmap.go
Normal 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
|
||||
}
|
||||
13
internal/pdf/cmap/const.go
Normal file
13
internal/pdf/cmap/const.go
Normal 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
467
internal/pdf/cmap/parser.go
Normal 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
|
||||
}
|
||||
43
internal/pdf/cmap/primitives.go
Normal file
43
internal/pdf/cmap/primitives.go
Normal 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
|
||||
}
|
||||
29
internal/pdf/cmap/utils.go
Normal file
29
internal/pdf/cmap/utils.go
Normal 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()
|
||||
}
|
||||
116
internal/pdf/common/logging.go
Normal file
116
internal/pdf/common/logging.go
Normal file
@@ -0,0 +1,116 @@
|
||||
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 (DummyLogger) Error(format string, args ...any) {
|
||||
}
|
||||
|
||||
func (DummyLogger) Warning(format string, args ...any) {
|
||||
}
|
||||
|
||||
func (DummyLogger) Notice(format string, args ...any) {
|
||||
}
|
||||
|
||||
func (DummyLogger) Info(format string, args ...any) {
|
||||
}
|
||||
|
||||
func (DummyLogger) Debug(format string, args ...any) {
|
||||
}
|
||||
|
||||
func (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 (cL ConsoleLogger) Error(format string, args ...any) {
|
||||
if cL.LogLevel >= LogLevelError {
|
||||
cL.output(os.Stdout, "[ERROR] ", format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (cL ConsoleLogger) Warning(format string, args ...any) {
|
||||
if cL.LogLevel >= LogLevelWarning {
|
||||
cL.output(os.Stdout, "[WARNING] ", format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (cL ConsoleLogger) Notice(format string, args ...any) {
|
||||
if cL.LogLevel >= LogLevelNotice {
|
||||
cL.output(os.Stdout, "[NOTICE] ", format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (cL ConsoleLogger) Info(format string, args ...any) {
|
||||
if cL.LogLevel >= LogLevelInfo {
|
||||
cL.output(os.Stdout, "[INFO] ", format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (cL ConsoleLogger) Debug(format string, args ...any) {
|
||||
if cL.LogLevel >= LogLevelDebug {
|
||||
cL.output(os.Stdout, "[DEBUG] ", format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (cL ConsoleLogger) Trace(format string, args ...any) {
|
||||
if cL.LogLevel >= LogLevelTrace {
|
||||
cL.output(os.Stdout, "[TRACE] ", 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 (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...)
|
||||
}
|
||||
7
internal/pdf/contentstream/const.go
Normal file
7
internal/pdf/contentstream/const.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package contentstream
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrInvalidOperand = errors.New("invalid operand")
|
||||
)
|
||||
198
internal/pdf/contentstream/contentstream.go
Normal file
198
internal/pdf/contentstream/contentstream.go
Normal file
@@ -0,0 +1,198 @@
|
||||
package contentstream
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"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)
|
||||
var txt strings.Builder
|
||||
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.WriteString("\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.WriteString("\n")
|
||||
xPos = float64(*xfloat)
|
||||
yPos = float64(*yfloat)
|
||||
continue
|
||||
}
|
||||
if xPos == -1 {
|
||||
xPos = float64(*xfloat)
|
||||
} else if xPos < float64(*xfloat) {
|
||||
txt.WriteString("\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.WriteString(string(*v))
|
||||
case *core.PdfObjectFloat:
|
||||
if *v < -100 {
|
||||
txt.WriteString(" ")
|
||||
}
|
||||
case *core.PdfObjectInteger:
|
||||
if *v < -100 {
|
||||
txt.WriteString(" ")
|
||||
}
|
||||
}
|
||||
}
|
||||
} 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.WriteString(string(*param))
|
||||
}
|
||||
}
|
||||
|
||||
return txt.String(), nil
|
||||
}
|
||||
613
internal/pdf/contentstream/creator.go
Normal file
613
internal/pdf/contentstream/creator.go
Normal 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
|
||||
}
|
||||
149
internal/pdf/contentstream/draw/bezier_curve.go
Normal file
149
internal/pdf/contentstream/draw/bezier_curve.go
Normal 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 (c CubicBezierPath) AppendCurve(curve CubicBezierCurve) CubicBezierPath {
|
||||
c.Curves = append(c.Curves, curve)
|
||||
return c
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
98
internal/pdf/contentstream/draw/path.go
Normal file
98
internal/pdf/contentstream/draw/path.go
Normal 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 (p Path) AppendPoint(point Point) Path {
|
||||
p.Points = append(p.Points, point)
|
||||
return p
|
||||
}
|
||||
|
||||
func (p Path) RemovePoint(number int) Path {
|
||||
if number < 1 || number > len(p.Points) {
|
||||
return p
|
||||
}
|
||||
|
||||
idx := number - 1
|
||||
p.Points = append(p.Points[:idx], p.Points[idx+1:]...)
|
||||
return p
|
||||
}
|
||||
|
||||
func (p Path) Length() int {
|
||||
return len(p.Points)
|
||||
}
|
||||
|
||||
func (p Path) GetPointNumber(number int) Point {
|
||||
if number < 1 || number > len(p.Points) {
|
||||
return Point{}
|
||||
}
|
||||
return p.Points[number-1]
|
||||
}
|
||||
|
||||
func (p Path) Copy() Path {
|
||||
pathcopy := Path{}
|
||||
pathcopy.Points = []Point{}
|
||||
for _, p := range p.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
|
||||
}
|
||||
32
internal/pdf/contentstream/draw/point.go
Normal file
32
internal/pdf/contentstream/draw/point.go
Normal 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 (p Point) AddVector(v Vector) Point {
|
||||
p.X += v.Dx
|
||||
p.Y += v.Dy
|
||||
return p
|
||||
}
|
||||
|
||||
func (p Point) String() string {
|
||||
return fmt.Sprintf("(%.1f,%.1f)", p.X, p.Y)
|
||||
}
|
||||
353
internal/pdf/contentstream/draw/shapes.go
Normal file
353
internal/pdf/contentstream/draw/shapes.go
Normal 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
|
||||
}
|
||||
28
internal/pdf/contentstream/draw/utils.go
Normal file
28
internal/pdf/contentstream/draw/utils.go
Normal 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)
|
||||
}
|
||||
}
|
||||
80
internal/pdf/contentstream/draw/vector.go
Normal file
80
internal/pdf/contentstream/draw/vector.go
Normal 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 (v Vector) Flip() Vector {
|
||||
mag := v.Magnitude()
|
||||
theta := v.GetPolarAngle()
|
||||
|
||||
v.Dx = mag * math.Cos(theta+math.Pi)
|
||||
v.Dy = mag * math.Sin(theta+math.Pi)
|
||||
return v
|
||||
}
|
||||
|
||||
func (v Vector) FlipY() Vector {
|
||||
v.Dy = -v.Dy
|
||||
return v
|
||||
}
|
||||
|
||||
func (v Vector) FlipX() Vector {
|
||||
v.Dx = -v.Dx
|
||||
return v
|
||||
}
|
||||
|
||||
func (v Vector) Scale(factor float64) Vector {
|
||||
mag := v.Magnitude()
|
||||
theta := v.GetPolarAngle()
|
||||
|
||||
v.Dx = factor * mag * math.Cos(theta)
|
||||
v.Dy = factor * mag * math.Sin(theta)
|
||||
return v
|
||||
}
|
||||
|
||||
func (v Vector) Magnitude() float64 {
|
||||
return math.Sqrt(math.Pow(v.Dx, 2.0) + math.Pow(v.Dy, 2.0))
|
||||
}
|
||||
|
||||
func (v Vector) GetPolarAngle() float64 {
|
||||
return math.Atan2(v.Dy, v.Dx)
|
||||
}
|
||||
390
internal/pdf/contentstream/encoding.go
Normal file
390
internal/pdf/contentstream/encoding.go
Normal 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
|
||||
}
|
||||
456
internal/pdf/contentstream/inline-image.go
Normal file
456
internal/pdf/contentstream/inline-image.go
Normal 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).
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
586
internal/pdf/contentstream/parser.go
Normal file
586
internal/pdf/contentstream/parser.go
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
541
internal/pdf/contentstream/processor.go
Normal file
541
internal/pdf/contentstream/processor.go
Normal 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
|
||||
}
|
||||
90
internal/pdf/contentstream/utils.go
Normal file
90
internal/pdf/contentstream/utils.go
Normal 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)
|
||||
}
|
||||
13
internal/pdf/core/const.go
Normal file
13
internal/pdf/core/const.go
Normal 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")
|
||||
)
|
||||
365
internal/pdf/core/crossrefs.go
Normal file
365
internal/pdf/core/crossrefs.go
Normal file
@@ -0,0 +1,365 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
|
||||
)
|
||||
|
||||
const (
|
||||
// xRefTableEntry indicates a normal xref table entry.
|
||||
xRefTableEntry = iota
|
||||
|
||||
// xRefObjectStream indicates an xref entry in an xref object stream.
|
||||
xRefObjectStream = 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).
|
||||
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.
|
||||
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.
|
||||
type objectStream struct {
|
||||
n int
|
||||
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.
|
||||
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 xRefTableEntry:
|
||||
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 xRefObjectStream:
|
||||
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
1732
internal/pdf/core/crypt.go
Normal file
File diff suppressed because it is too large
Load Diff
265
internal/pdf/core/crypt_filters.go
Normal file
265
internal/pdf/core/crypt_filters.go
Normal 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
61
internal/pdf/core/ecb.go
Normal 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:]
|
||||
}
|
||||
}
|
||||
1846
internal/pdf/core/encoding.go
Normal file
1846
internal/pdf/core/encoding.go
Normal file
File diff suppressed because it is too large
Load Diff
44
internal/pdf/core/io.go
Normal file
44
internal/pdf/core/io.go
Normal 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)
|
||||
}
|
||||
1627
internal/pdf/core/parser.go
Normal file
1627
internal/pdf/core/parser.go
Normal file
File diff suppressed because it is too large
Load Diff
572
internal/pdf/core/primitives.go
Normal file
572
internal/pdf/core/primitives.go
Normal file
@@ -0,0 +1,572 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"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 {
|
||||
var outStr strings.Builder
|
||||
outStr.WriteString("[")
|
||||
for ind, o := range *array {
|
||||
outStr.WriteString(o.DefaultWriteString())
|
||||
if ind < (len(*array) - 1) {
|
||||
outStr.WriteString(" ")
|
||||
}
|
||||
}
|
||||
outStr.WriteString("]")
|
||||
return outStr.String()
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
281
internal/pdf/core/repairs.go
Normal file
281
internal/pdf/core/repairs.go
Normal 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 = xRefTableEntry
|
||||
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
129
internal/pdf/core/stream.go
Normal 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
|
||||
}
|
||||
73
internal/pdf/core/symbols.go
Normal file
73
internal/pdf/core/symbols.go
Normal file
@@ -0,0 +1,73 @@
|
||||
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.
|
||||
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
172
internal/pdf/core/utils.go
Normal 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
|
||||
}
|
||||
}
|
||||
543
internal/pdf/creator/block.go
Normal file
543
internal/pdf/creator/block.go
Normal 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
|
||||
}
|
||||
174
internal/pdf/creator/chapters.go
Normal file
174
internal/pdf/creator/chapters.go
Normal 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
|
||||
}
|
||||
115
internal/pdf/creator/color.go
Normal file
115
internal/pdf/creator/color.go
Normal 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
|
||||
}
|
||||
49
internal/pdf/creator/const.go
Normal file
49
internal/pdf/creator/const.go
Normal 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
|
||||
}
|
||||
524
internal/pdf/creator/creator.go
Normal file
524
internal/pdf/creator/creator.go
Normal 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
|
||||
}
|
||||
66
internal/pdf/creator/curve.go
Normal file
66
internal/pdf/creator/curve.go
Normal 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
|
||||
}
|
||||
189
internal/pdf/creator/division.go
Normal file
189
internal/pdf/creator/division.go
Normal 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
|
||||
}
|
||||
44
internal/pdf/creator/drawable.go
Normal file
44
internal/pdf/creator/drawable.go
Normal 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
|
||||
}
|
||||
89
internal/pdf/creator/ellipse.go
Normal file
89
internal/pdf/creator/ellipse.go
Normal 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
|
||||
}
|
||||
107
internal/pdf/creator/filled_curve.go
Normal file
107
internal/pdf/creator/filled_curve.go
Normal 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
|
||||
}
|
||||
329
internal/pdf/creator/image.go
Normal file
329
internal/pdf/creator/image.go
Normal 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
|
||||
}
|
||||
85
internal/pdf/creator/line.go
Normal file
85
internal/pdf/creator/line.go
Normal 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
|
||||
}
|
||||
31
internal/pdf/creator/pagebreak.go
Normal file
31
internal/pdf/creator/pagebreak.go
Normal 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
|
||||
}
|
||||
525
internal/pdf/creator/paragraph.go
Normal file
525
internal/pdf/creator/paragraph.go
Normal 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
|
||||
}
|
||||
88
internal/pdf/creator/rectangle.go
Normal file
88
internal/pdf/creator/rectangle.go
Normal 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
|
||||
}
|
||||
638
internal/pdf/creator/styled_paragraph.go
Normal file
638
internal/pdf/creator/styled_paragraph.go
Normal 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
|
||||
}
|
||||
179
internal/pdf/creator/subchapter.go
Normal file
179
internal/pdf/creator/subchapter.go
Normal 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
|
||||
}
|
||||
628
internal/pdf/creator/table.go
Normal file
628
internal/pdf/creator/table.go
Normal 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
|
||||
}
|
||||
38
internal/pdf/creator/text_style.go
Normal file
38
internal/pdf/creator/text_style.go
Normal 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
|
||||
}
|
||||
38
internal/pdf/creator/toc.go
Normal file
38
internal/pdf/creator/toc.go
Normal 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
|
||||
}
|
||||
23
internal/pdf/extractor/extractor.go
Normal file
23
internal/pdf/extractor/extractor.go
Normal 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
|
||||
}
|
||||
225
internal/pdf/extractor/text.go
Normal file
225
internal/pdf/extractor/text.go
Normal 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
|
||||
}
|
||||
20
internal/pdf/extractor/utils.go
Normal file
20
internal/pdf/extractor/utils.go
Normal 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")
|
||||
}
|
||||
1899
internal/pdf/model/annotations.go
Normal file
1899
internal/pdf/model/annotations.go
Normal file
File diff suppressed because it is too large
Load Diff
2778
internal/pdf/model/colorspace.go
Normal file
2778
internal/pdf/model/colorspace.go
Normal file
File diff suppressed because it is too large
Load Diff
14
internal/pdf/model/const.go
Normal file
14
internal/pdf/model/const.go
Normal 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
393
internal/pdf/model/font.go
Normal 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
|
||||
}
|
||||
342
internal/pdf/model/fonts/afms/Courier-Bold.afm
Normal file
342
internal/pdf/model/fonts/afms/Courier-Bold.afm
Normal 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
|
||||
342
internal/pdf/model/fonts/afms/Courier-BoldOblique.afm
Normal file
342
internal/pdf/model/fonts/afms/Courier-BoldOblique.afm
Normal 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
|
||||
342
internal/pdf/model/fonts/afms/Courier-Oblique.afm
Normal file
342
internal/pdf/model/fonts/afms/Courier-Oblique.afm
Normal 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
|
||||
342
internal/pdf/model/fonts/afms/Courier.afm
Normal file
342
internal/pdf/model/fonts/afms/Courier.afm
Normal 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
|
||||
2827
internal/pdf/model/fonts/afms/Helvetica-Bold.afm
Normal file
2827
internal/pdf/model/fonts/afms/Helvetica-Bold.afm
Normal file
File diff suppressed because it is too large
Load Diff
2827
internal/pdf/model/fonts/afms/Helvetica-BoldOblique.afm
Normal file
2827
internal/pdf/model/fonts/afms/Helvetica-BoldOblique.afm
Normal file
File diff suppressed because it is too large
Load Diff
3051
internal/pdf/model/fonts/afms/Helvetica-Oblique.afm
Normal file
3051
internal/pdf/model/fonts/afms/Helvetica-Oblique.afm
Normal file
File diff suppressed because it is too large
Load Diff
3051
internal/pdf/model/fonts/afms/Helvetica.afm
Normal file
3051
internal/pdf/model/fonts/afms/Helvetica.afm
Normal file
File diff suppressed because it is too large
Load Diff
1
internal/pdf/model/fonts/afms/MustRead.html
Normal file
1
internal/pdf/model/fonts/afms/MustRead.html
Normal file
@@ -0,0 +1 @@
|
||||
<html>
|
||||
213
internal/pdf/model/fonts/afms/Symbol.afm
Normal file
213
internal/pdf/model/fonts/afms/Symbol.afm
Normal 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
|
||||
2588
internal/pdf/model/fonts/afms/Times-Bold.afm
Normal file
2588
internal/pdf/model/fonts/afms/Times-Bold.afm
Normal file
File diff suppressed because it is too large
Load Diff
2384
internal/pdf/model/fonts/afms/Times-BoldItalic.afm
Normal file
2384
internal/pdf/model/fonts/afms/Times-BoldItalic.afm
Normal file
File diff suppressed because it is too large
Load Diff
2667
internal/pdf/model/fonts/afms/Times-Italic.afm
Normal file
2667
internal/pdf/model/fonts/afms/Times-Italic.afm
Normal file
File diff suppressed because it is too large
Load Diff
2419
internal/pdf/model/fonts/afms/Times-Roman.afm
Normal file
2419
internal/pdf/model/fonts/afms/Times-Roman.afm
Normal file
File diff suppressed because it is too large
Load Diff
225
internal/pdf/model/fonts/afms/ZapfDingbats.afm
Normal file
225
internal/pdf/model/fonts/afms/ZapfDingbats.afm
Normal 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
|
||||
362
internal/pdf/model/fonts/courier.go
Normal file
362
internal/pdf/model/fonts/courier.go
Normal 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},
|
||||
}
|
||||
362
internal/pdf/model/fonts/courier_bold.go
Normal file
362
internal/pdf/model/fonts/courier_bold.go
Normal 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},
|
||||
}
|
||||
362
internal/pdf/model/fonts/courier_bold_oblique.go
Normal file
362
internal/pdf/model/fonts/courier_bold_oblique.go
Normal 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},
|
||||
}
|
||||
362
internal/pdf/model/fonts/courier_oblique.go
Normal file
362
internal/pdf/model/fonts/courier_oblique.go
Normal 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},
|
||||
}
|
||||
18
internal/pdf/model/fonts/font.go
Normal file
18
internal/pdf/model/fonts/font.go
Normal 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
|
||||
}
|
||||
362
internal/pdf/model/fonts/helvetica.go
Normal file
362
internal/pdf/model/fonts/helvetica.go
Normal 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},
|
||||
}
|
||||
362
internal/pdf/model/fonts/helvetica_bold.go
Normal file
362
internal/pdf/model/fonts/helvetica_bold.go
Normal 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},
|
||||
}
|
||||
362
internal/pdf/model/fonts/helvetica_bold_oblique.go
Normal file
362
internal/pdf/model/fonts/helvetica_bold_oblique.go
Normal 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},
|
||||
}
|
||||
362
internal/pdf/model/fonts/helvetica_oblique.go
Normal file
362
internal/pdf/model/fonts/helvetica_oblique.go
Normal 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},
|
||||
}
|
||||
239
internal/pdf/model/fonts/symbol.go
Normal file
239
internal/pdf/model/fonts/symbol.go
Normal 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},
|
||||
}
|
||||
362
internal/pdf/model/fonts/times_bold.go
Normal file
362
internal/pdf/model/fonts/times_bold.go
Normal 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},
|
||||
}
|
||||
362
internal/pdf/model/fonts/times_bold_italic.go
Normal file
362
internal/pdf/model/fonts/times_bold_italic.go
Normal 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},
|
||||
}
|
||||
362
internal/pdf/model/fonts/times_italic.go
Normal file
362
internal/pdf/model/fonts/times_italic.go
Normal 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},
|
||||
}
|
||||
362
internal/pdf/model/fonts/times_roman.go
Normal file
362
internal/pdf/model/fonts/times_roman.go
Normal 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},
|
||||
}
|
||||
374
internal/pdf/model/fonts/ttfparser.go
Normal file
374
internal/pdf/model/fonts/ttfparser.go
Normal 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
|
||||
}
|
||||
251
internal/pdf/model/fonts/zapfdingbats.go
Normal file
251
internal/pdf/model/fonts/zapfdingbats.go
Normal 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
412
internal/pdf/model/forms.go
Normal 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
|
||||
}
|
||||
853
internal/pdf/model/functions.go
Normal file
853
internal/pdf/model/functions.go
Normal 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
|
||||
}
|
||||
293
internal/pdf/model/image.go
Normal file
293
internal/pdf/model/image.go
Normal file
@@ -0,0 +1,293 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"errors"
|
||||
goimage "image"
|
||||
gocolor "image/color"
|
||||
"image/draw"
|
||||
_ "image/gif"
|
||||
_ "image/png"
|
||||
"io"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// Basic representation of an image.
|
||||
// The colorspace is not specified, but must be known when handling the image.
|
||||
type Image struct {
|
||||
Width int64 // The width of the image in samples
|
||||
Height int64 // The height of the image in samples
|
||||
BitsPerComponent int64 // The number of bits per color component
|
||||
ColorComponents int // Color components per pixel
|
||||
Data []byte // Image data stored as bytes.
|
||||
|
||||
// Transparency data: alpha channel.
|
||||
// Stored in same bits per component as original data with 1 color component.
|
||||
alphaData []byte // Alpha channel data.
|
||||
hasAlpha bool // Indicates whether the alpha channel data is available.
|
||||
|
||||
decode []float64 // [Dmin Dmax ... values for each color component]
|
||||
}
|
||||
|
||||
// Threshold alpha channel. Set all alpha values below threshold to transparent.
|
||||
type AlphaMapFunc func(alpha byte) byte
|
||||
|
||||
// Allow mapping of alpha data for transformations. Can allow custom filtering of alpha data etc.
|
||||
func (img Image) AlphaMap(mapFunc AlphaMapFunc) {
|
||||
for idx, alpha := range img.alphaData {
|
||||
img.alphaData[idx] = mapFunc(alpha)
|
||||
}
|
||||
}
|
||||
|
||||
// Convert the raw byte slice into samples which are stored in a uint32 bit array.
|
||||
// Each sample is represented by BitsPerComponent consecutive bits in the raw data.
|
||||
func (img *Image) GetSamples() []uint32 {
|
||||
samples := sampling.ResampleBytes(img.Data, int(img.BitsPerComponent))
|
||||
|
||||
expectedLen := int(img.Width) * int(img.Height) * img.ColorComponents
|
||||
if len(samples) < expectedLen {
|
||||
// Return error, or fill with 0s?
|
||||
common.Log.Debug("error: Too few samples (got %d, expecting %d)", len(samples), expectedLen)
|
||||
return samples
|
||||
} else if len(samples) > expectedLen {
|
||||
samples = samples[:expectedLen]
|
||||
}
|
||||
return samples
|
||||
}
|
||||
|
||||
// Convert samples to byte-data.
|
||||
func (img *Image) SetSamples(samples []uint32) {
|
||||
resampled := sampling.ResampleUint32(samples, int(img.BitsPerComponent), 8)
|
||||
data := []byte{}
|
||||
for _, val := range resampled {
|
||||
data = append(data, byte(val))
|
||||
}
|
||||
|
||||
img.Data = data
|
||||
}
|
||||
|
||||
// Resample resamples the image data converting from current BitsPerComponent to a target BitsPerComponent
|
||||
// value. Sets the image's BitsPerComponent to the target value following resampling.
|
||||
//
|
||||
// For example, converting an 8-bit RGB image to 1-bit grayscale (common for scanned images):
|
||||
//
|
||||
// // Convert RGB image to grayscale.
|
||||
// rgbColorSpace := pdf.NewPdfColorspaceDeviceRGB()
|
||||
// grayImage, err := rgbColorSpace.ImageToGray(rgbImage)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// // Resample as 1 bit.
|
||||
// grayImage.Resample(1)
|
||||
func (img *Image) Resample(targetBitsPerComponent int64) {
|
||||
samples := img.GetSamples()
|
||||
|
||||
// Image data are stored row by row. If the number of bits per row is not a multiple of 8, the end of the
|
||||
// row needs to be padded with extra bits to fill out the last byte.
|
||||
// Thus the processing is done on a row by row basis below.
|
||||
|
||||
// This one simply resamples the data so that each component has target bits per component...
|
||||
// So if the original data was 10011010, then will have 1 0 0 1 1 0 1 0... much longer
|
||||
// The key to resampling is that we need to upsample/downsample,
|
||||
// i.e. 10011010 >> targetBitsPerComponent
|
||||
// Current bits: 8, target bits: 1... need to downsample by 8-1 = 7
|
||||
|
||||
if targetBitsPerComponent < img.BitsPerComponent {
|
||||
downsampling := img.BitsPerComponent - targetBitsPerComponent
|
||||
for i := range samples {
|
||||
samples[i] >>= uint(downsampling)
|
||||
}
|
||||
} else if targetBitsPerComponent > img.BitsPerComponent {
|
||||
upsampling := targetBitsPerComponent - img.BitsPerComponent
|
||||
for i := range samples {
|
||||
samples[i] <<= uint(upsampling)
|
||||
}
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
// Write out row by row...
|
||||
data := []byte{}
|
||||
for i := int64(0); i < img.Height; i++ {
|
||||
ind1 := i * img.Width * int64(img.ColorComponents)
|
||||
ind2 := (i+1)*img.Width*int64(img.ColorComponents) - 1
|
||||
|
||||
resampled := sampling.ResampleUint32(samples[ind1:ind2], int(targetBitsPerComponent), 8)
|
||||
for _, val := range resampled {
|
||||
data = append(data, byte(val))
|
||||
}
|
||||
}
|
||||
|
||||
img.Data = data
|
||||
img.BitsPerComponent = int64(targetBitsPerComponent)
|
||||
}
|
||||
|
||||
// Converts the Image to a golang Image structure.
|
||||
func (im *Image) ToGoImage() (goimage.Image, error) {
|
||||
common.Log.Trace("Converting to go image")
|
||||
bounds := goimage.Rect(0, 0, int(im.Width), int(im.Height))
|
||||
var img core.DrawableImage
|
||||
|
||||
switch im.ColorComponents {
|
||||
case 1:
|
||||
if im.BitsPerComponent == 16 {
|
||||
img = goimage.NewGray16(bounds)
|
||||
} else {
|
||||
img = goimage.NewGray(bounds)
|
||||
}
|
||||
case 3:
|
||||
if im.BitsPerComponent == 16 {
|
||||
img = goimage.NewRGBA64(bounds)
|
||||
} else {
|
||||
img = goimage.NewRGBA(bounds)
|
||||
}
|
||||
case 4:
|
||||
img = goimage.NewCMYK(bounds)
|
||||
default:
|
||||
// XXX? Force RGB convert?
|
||||
common.Log.Debug("Unsupported number of colors components per sample: %d", im.ColorComponents)
|
||||
return nil, errors.New("unsupported colors")
|
||||
}
|
||||
|
||||
// Draw the data on the image..
|
||||
x := 0
|
||||
y := 0
|
||||
aidx := 0
|
||||
|
||||
samples := im.GetSamples()
|
||||
bytesPerColor := im.ColorComponents
|
||||
for i := 0; i+bytesPerColor-1 < len(samples); i += bytesPerColor {
|
||||
var c gocolor.Color
|
||||
switch im.ColorComponents {
|
||||
case 1:
|
||||
if im.BitsPerComponent == 16 {
|
||||
val := uint16(samples[i])<<8 | uint16(samples[i+1])
|
||||
c = gocolor.Gray16{val}
|
||||
} else {
|
||||
val := uint8(samples[i] & 0xff)
|
||||
c = gocolor.Gray{val}
|
||||
}
|
||||
case 3:
|
||||
if im.BitsPerComponent == 16 {
|
||||
r := uint16(samples[i])<<8 | uint16(samples[i+1])
|
||||
g := uint16(samples[i+2])<<8 | uint16(samples[i+3])
|
||||
b := uint16(samples[i+4])<<8 | uint16(samples[i+5])
|
||||
a := uint16(0xffff) // Default: solid (0xffff) whereas transparent=0.
|
||||
if im.alphaData != nil && len(im.alphaData) > aidx+1 {
|
||||
a = (uint16(im.alphaData[aidx]) << 8) | uint16(im.alphaData[aidx+1])
|
||||
aidx += 2
|
||||
}
|
||||
c = gocolor.RGBA64{R: r, G: g, B: b, A: a}
|
||||
} else {
|
||||
r := uint8(samples[i] & 0xff)
|
||||
g := uint8(samples[i+1] & 0xff)
|
||||
b := uint8(samples[i+2] & 0xff)
|
||||
a := uint8(0xff) // Default: solid (0xff) whereas transparent=0.
|
||||
if im.alphaData != nil && len(im.alphaData) > aidx {
|
||||
a = uint8(im.alphaData[aidx])
|
||||
aidx++
|
||||
}
|
||||
c = gocolor.RGBA{R: r, G: g, B: b, A: a}
|
||||
}
|
||||
case 4:
|
||||
c1 := uint8(samples[i] & 0xff)
|
||||
m1 := uint8(samples[i+1] & 0xff)
|
||||
y1 := uint8(samples[i+2] & 0xff)
|
||||
k1 := uint8(samples[i+3] & 0xff)
|
||||
c = gocolor.CMYK{C: c1, M: m1, Y: y1, K: k1}
|
||||
}
|
||||
|
||||
img.Set(x, y, c)
|
||||
x++
|
||||
if x == int(im.Width) {
|
||||
x = 0
|
||||
y++
|
||||
}
|
||||
}
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
// The ImageHandler interface implements common image loading and processing tasks.
|
||||
// Implementing as an interface allows for the possibility to use non-standard libraries for faster
|
||||
// loading and processing of images.
|
||||
type ImageHandler interface {
|
||||
// Read any image type and load into a new Image object.
|
||||
Read(r io.Reader) (*Image, error)
|
||||
|
||||
// Load a Image from a standard Go image structure.
|
||||
NewImageFromGoImage(goimg goimage.Image) (*Image, error)
|
||||
|
||||
// Compress an image.
|
||||
Compress(input *Image, quality int64) (*Image, error)
|
||||
}
|
||||
|
||||
// Default implementation.
|
||||
|
||||
type DefaultImageHandler struct{}
|
||||
|
||||
// Create a Image from a golang Image.
|
||||
func (dih DefaultImageHandler) NewImageFromGoImage(goimg goimage.Image) (*Image, error) {
|
||||
// Speed up jpeg encoding by converting to RGBA first.
|
||||
// Will not be required once the golang image/jpeg package is optimized.
|
||||
b := goimg.Bounds()
|
||||
m := goimage.NewRGBA(goimage.Rect(0, 0, b.Dx(), b.Dy()))
|
||||
draw.Draw(m, m.Bounds(), goimg, b.Min, draw.Src)
|
||||
|
||||
alphaData := []byte{}
|
||||
hasAlpha := false
|
||||
|
||||
data := []byte{}
|
||||
for i := 0; i < len(m.Pix); i += 4 {
|
||||
data = append(data, m.Pix[i], m.Pix[i+1], m.Pix[i+2])
|
||||
|
||||
alpha := m.Pix[i+3]
|
||||
if alpha != 255 {
|
||||
// If all alpha values are 255 (opaque), means that the alpha transparency channel is unnecessary.
|
||||
hasAlpha = true
|
||||
}
|
||||
alphaData = append(alphaData, alpha)
|
||||
}
|
||||
|
||||
imag := Image{}
|
||||
imag.Width = int64(b.Dx())
|
||||
imag.Height = int64(b.Dy())
|
||||
imag.BitsPerComponent = 8 // RGBA colormap
|
||||
imag.ColorComponents = 3
|
||||
imag.Data = data // buf.Bytes()
|
||||
|
||||
imag.hasAlpha = hasAlpha
|
||||
if hasAlpha {
|
||||
imag.alphaData = alphaData
|
||||
}
|
||||
|
||||
return &imag, nil
|
||||
}
|
||||
|
||||
// Reads an image and loads into a new Image object with an RGB
|
||||
// colormap and 8 bits per component.
|
||||
func (dih DefaultImageHandler) Read(reader io.Reader) (*Image, error) {
|
||||
// Load the image with the native implementation.
|
||||
goimg, _, err := goimage.Decode(reader)
|
||||
if err != nil {
|
||||
common.Log.Debug("error decoding file: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dih.NewImageFromGoImage(goimg)
|
||||
}
|
||||
|
||||
// To be implemented.
|
||||
// Should be able to compress in terms of JPEG quality parameter,
|
||||
// and DPI threshold (need to know bounding area dimensions).
|
||||
func (DefaultImageHandler) Compress(input *Image, quality int64) (*Image, error) {
|
||||
return input, nil
|
||||
}
|
||||
|
||||
var ImageHandling ImageHandler = DefaultImageHandler{}
|
||||
|
||||
func SetImageHandler(imgHandling ImageHandler) {
|
||||
ImageHandling = imgHandling
|
||||
}
|
||||
56
internal/pdf/model/model.go
Normal file
56
internal/pdf/model/model.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
|
||||
)
|
||||
|
||||
// A PDFModel is a higher level PDF construct which can be collapsed into a PDF primitive.
|
||||
// Each PDFModel has an underlying Primitive and vice versa.
|
||||
// Copies can be made, but care must be taken to do it properly.
|
||||
type PdfModel interface {
|
||||
ToPdfObject() core.PdfObject
|
||||
GetContainingPdfObject() core.PdfObject
|
||||
}
|
||||
|
||||
// The model manager is used to cache Primitive <-> Model mappings where needed.
|
||||
// In many cases only Model -> Primitive mapping is needed and only a reference to the Primitive
|
||||
// is stored in the Model. In some cases, the Model needs to be found from the Primitive,
|
||||
// and that is where the ModelManager can be used (in both directions).
|
||||
//
|
||||
// Note that it is not always used, the Primitive <-> Model mapping needs to be registered
|
||||
// for each time it is used. Thus, it is only used for special cases, commonly where the same
|
||||
// object is used by two higher level objects. (Example PDF Widgets owned by both Page Annotations,
|
||||
// and the interactive form - AcroForm).
|
||||
type ModelManager struct {
|
||||
primitiveCache map[PdfModel]core.PdfObject
|
||||
modelCache map[core.PdfObject]PdfModel
|
||||
}
|
||||
|
||||
func NewModelManager() *ModelManager {
|
||||
mm := ModelManager{}
|
||||
mm.primitiveCache = map[PdfModel]core.PdfObject{}
|
||||
mm.modelCache = map[core.PdfObject]PdfModel{}
|
||||
return &mm
|
||||
}
|
||||
|
||||
// Register (cache) a model to primitive relationship.
|
||||
func (mm *ModelManager) Register(primitive core.PdfObject, model PdfModel) {
|
||||
mm.primitiveCache[model] = primitive
|
||||
mm.modelCache[primitive] = model
|
||||
}
|
||||
|
||||
func (mm *ModelManager) GetPrimitiveFromModel(model PdfModel) core.PdfObject {
|
||||
primitive, has := mm.primitiveCache[model]
|
||||
if !has {
|
||||
return nil
|
||||
}
|
||||
return primitive
|
||||
}
|
||||
|
||||
func (mm *ModelManager) GetModelFromPrimitive(primitive core.PdfObject) PdfModel {
|
||||
model, has := mm.modelCache[primitive]
|
||||
if !has {
|
||||
return nil
|
||||
}
|
||||
return model
|
||||
}
|
||||
294
internal/pdf/model/outlines.go
Normal file
294
internal/pdf/model/outlines.go
Normal file
@@ -0,0 +1,294 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
|
||||
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
|
||||
)
|
||||
|
||||
type PdfOutlineTreeNode struct {
|
||||
context any // Allow accessing outer structure.
|
||||
First *PdfOutlineTreeNode
|
||||
Last *PdfOutlineTreeNode
|
||||
}
|
||||
|
||||
// PDF outline dictionary (Table 152 - p. 376).
|
||||
type PdfOutline struct {
|
||||
PdfOutlineTreeNode
|
||||
Parent *PdfOutlineTreeNode
|
||||
Count *int64
|
||||
|
||||
primitive *core.PdfIndirectObject
|
||||
}
|
||||
|
||||
// Pdf outline item dictionary (Table 153 - pp. 376 - 377).
|
||||
type PdfOutlineItem struct {
|
||||
PdfOutlineTreeNode
|
||||
Title *core.PdfObjectString
|
||||
Parent *PdfOutlineTreeNode
|
||||
Prev *PdfOutlineTreeNode
|
||||
Next *PdfOutlineTreeNode
|
||||
Count *int64
|
||||
Dest core.PdfObject
|
||||
A core.PdfObject
|
||||
SE core.PdfObject
|
||||
C core.PdfObject
|
||||
F core.PdfObject
|
||||
|
||||
primitive *core.PdfIndirectObject
|
||||
}
|
||||
|
||||
func NewPdfOutline() *PdfOutline {
|
||||
outline := &PdfOutline{}
|
||||
|
||||
container := &core.PdfIndirectObject{}
|
||||
container.PdfObject = core.MakeDict()
|
||||
|
||||
outline.primitive = container
|
||||
|
||||
return outline
|
||||
}
|
||||
|
||||
func NewPdfOutlineTree() *PdfOutline {
|
||||
outlineTree := NewPdfOutline()
|
||||
outlineTree.context = &outlineTree
|
||||
return outlineTree
|
||||
}
|
||||
|
||||
func NewPdfOutlineItem() *PdfOutlineItem {
|
||||
outlineItem := &PdfOutlineItem{}
|
||||
|
||||
container := &core.PdfIndirectObject{}
|
||||
container.PdfObject = core.MakeDict()
|
||||
|
||||
outlineItem.primitive = container
|
||||
return outlineItem
|
||||
}
|
||||
|
||||
func NewOutlineBookmark(title string, page *core.PdfIndirectObject) *PdfOutlineItem {
|
||||
bookmark := PdfOutlineItem{}
|
||||
bookmark.context = &bookmark
|
||||
|
||||
bookmark.Title = core.MakeString(title)
|
||||
|
||||
destArray := core.PdfObjectArray{}
|
||||
destArray = append(destArray, page)
|
||||
destArray = append(destArray, core.MakeName("Fit"))
|
||||
bookmark.Dest = &destArray
|
||||
|
||||
return &bookmark
|
||||
}
|
||||
|
||||
// Does not traverse the tree.
|
||||
func newPdfOutlineFromIndirectObject(container *core.PdfIndirectObject) (*PdfOutline, error) {
|
||||
dict, isDict := container.PdfObject.(*core.PdfObjectDictionary)
|
||||
if !isDict {
|
||||
return nil, fmt.Errorf("outline object not a dictionary")
|
||||
}
|
||||
|
||||
outline := PdfOutline{}
|
||||
outline.primitive = container
|
||||
outline.context = &outline
|
||||
|
||||
if obj := dict.Get("Type"); obj != nil {
|
||||
typeVal, ok := obj.(*core.PdfObjectName)
|
||||
if ok {
|
||||
if *typeVal != "Outlines" {
|
||||
common.Log.Debug("error Type != Outlines (%s)", *typeVal)
|
||||
// Should be "Outlines" if there, but some files have other types
|
||||
// Log as an error but do not quit.
|
||||
// Might be a good idea to log this kind of deviation from the standard separately.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if obj := dict.Get("Count"); obj != nil {
|
||||
// This should always be an integer, but in a few cases has been a float.
|
||||
count, err := getNumberAsInt64(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outline.Count = &count
|
||||
}
|
||||
|
||||
return &outline, nil
|
||||
}
|
||||
|
||||
// Does not traverse the tree.
|
||||
func (pr *PdfReader) newPdfOutlineItemFromIndirectObject(container *core.PdfIndirectObject) (*PdfOutlineItem, error) {
|
||||
dict, isDict := container.PdfObject.(*core.PdfObjectDictionary)
|
||||
if !isDict {
|
||||
return nil, fmt.Errorf("outline object not a dictionary")
|
||||
}
|
||||
|
||||
item := PdfOutlineItem{}
|
||||
item.primitive = container
|
||||
item.context = &item
|
||||
|
||||
// Title (required).
|
||||
obj := dict.Get("Title")
|
||||
if obj == nil {
|
||||
return nil, fmt.Errorf("missing Title from Outline Item (required)")
|
||||
}
|
||||
obj, err := pr.traceToObject(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
title, ok := core.TraceToDirectObject(obj).(*core.PdfObjectString)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("title not a string (%T)", obj)
|
||||
}
|
||||
item.Title = title
|
||||
|
||||
// Count (optional).
|
||||
if obj := dict.Get("Count"); obj != nil {
|
||||
countVal, ok := obj.(*core.PdfObjectInteger)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("count not an integer (%T)", obj)
|
||||
}
|
||||
count := int64(*countVal)
|
||||
item.Count = &count
|
||||
}
|
||||
|
||||
// Other keys.
|
||||
if obj := dict.Get("Dest"); obj != nil {
|
||||
item.Dest, err = pr.traceToObject(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err := pr.traverseObjectData(item.Dest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if obj := dict.Get("A"); obj != nil {
|
||||
item.A, err = pr.traceToObject(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err := pr.traverseObjectData(item.A)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if obj := dict.Get("SE"); obj != nil {
|
||||
// XXX: To add structure element support.
|
||||
// Currently not supporting structure elements.
|
||||
item.SE = nil
|
||||
}
|
||||
if obj := dict.Get("C"); obj != nil {
|
||||
item.C, err = pr.traceToObject(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if obj := dict.Get("F"); obj != nil {
|
||||
item.F, err = pr.traceToObject(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &item, nil
|
||||
}
|
||||
|
||||
// Get the outer object of the tree node (Outline or OutlineItem).
|
||||
func (n *PdfOutlineTreeNode) getOuter() PdfModel {
|
||||
if outline, isOutline := n.context.(*PdfOutline); isOutline {
|
||||
return outline
|
||||
}
|
||||
if outlineItem, isOutlineItem := n.context.(*PdfOutlineItem); isOutlineItem {
|
||||
return outlineItem
|
||||
}
|
||||
|
||||
common.Log.Debug("error Invalid outline tree node item") // Should never happen.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pot *PdfOutlineTreeNode) GetContainingPdfObject() core.PdfObject {
|
||||
return pot.getOuter().GetContainingPdfObject()
|
||||
}
|
||||
|
||||
func (pot *PdfOutlineTreeNode) ToPdfObject() core.PdfObject {
|
||||
return pot.getOuter().ToPdfObject()
|
||||
}
|
||||
|
||||
func (po *PdfOutline) GetContainingPdfObject() core.PdfObject {
|
||||
return po.primitive
|
||||
}
|
||||
|
||||
// Recursively build the Outline tree PDF object.
|
||||
func (po *PdfOutline) ToPdfObject() core.PdfObject {
|
||||
container := po.primitive
|
||||
dict := container.PdfObject.(*core.PdfObjectDictionary)
|
||||
|
||||
dict.Set("Type", core.MakeName("Outlines"))
|
||||
|
||||
if po.First != nil {
|
||||
dict.Set("First", po.First.ToPdfObject())
|
||||
}
|
||||
|
||||
if po.Last != nil {
|
||||
dict.Set("Last", po.Last.getOuter().GetContainingPdfObject())
|
||||
//PdfObjectConverterCache[this.Last.getOuter()]
|
||||
}
|
||||
|
||||
if po.Parent != nil {
|
||||
dict.Set("Parent", po.Parent.getOuter().GetContainingPdfObject())
|
||||
}
|
||||
|
||||
return container
|
||||
}
|
||||
|
||||
func (poi *PdfOutlineItem) GetContainingPdfObject() core.PdfObject {
|
||||
return poi.primitive
|
||||
}
|
||||
|
||||
// Outline item.
|
||||
// Recursively build the Outline tree PDF object.
|
||||
func (poi *PdfOutlineItem) ToPdfObject() core.PdfObject {
|
||||
container := poi.primitive
|
||||
dict := container.PdfObject.(*core.PdfObjectDictionary)
|
||||
|
||||
dict.Set("Title", poi.Title)
|
||||
if poi.A != nil {
|
||||
dict.Set("A", poi.A)
|
||||
}
|
||||
if obj := dict.Get("SE"); obj != nil {
|
||||
// XXX: Currently not supporting structure element hierarchy.
|
||||
// Remove it.
|
||||
dict.Remove("SE")
|
||||
// delete(*dict, "SE")
|
||||
}
|
||||
|
||||
if poi.C != nil {
|
||||
dict.Set("C", poi.C)
|
||||
}
|
||||
if poi.Dest != nil {
|
||||
dict.Set("Dest", poi.Dest)
|
||||
}
|
||||
if poi.F != nil {
|
||||
dict.Set("F", poi.F)
|
||||
}
|
||||
if poi.Count != nil {
|
||||
dict.Set("Count", core.MakeInteger(*poi.Count))
|
||||
}
|
||||
if poi.Next != nil {
|
||||
dict.Set("Next", poi.Next.ToPdfObject())
|
||||
}
|
||||
if poi.First != nil {
|
||||
dict.Set("First", poi.First.ToPdfObject())
|
||||
}
|
||||
if poi.Prev != nil {
|
||||
dict.Set("Prev", poi.Prev.getOuter().GetContainingPdfObject())
|
||||
}
|
||||
if poi.Last != nil {
|
||||
dict.Set("Last", poi.Last.getOuter().GetContainingPdfObject())
|
||||
}
|
||||
if poi.Parent != nil {
|
||||
dict.Set("Parent", poi.Parent.getOuter().GetContainingPdfObject())
|
||||
}
|
||||
|
||||
return container
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user