attempting to simplify render branch

This commit is contained in:
Peter Williams 2018-07-16 17:42:08 +10:00
parent 79aa75acf8
commit 7f5475badb
3 changed files with 375 additions and 184 deletions

View File

@ -22,27 +22,23 @@ import (
// - Type1
// - TrueType
// etc.
// It also holds the elements common to all fonts in fontSkeleton.
// XXX: The idea behind fontSkeleton is to avoid replicating the commmon font field parsing code
// in all fonts. Is there a better way of doing this?
type PdfFont struct {
fontSkeleton // The fields common to all fonts
context fonts.Font // The underlying font: Type0, Type1, Truetype, etc..
context fonts.Font // The underlying font: Type0, Type1, Truetype, etc..
}
// String returns a string that describes `font`.
func (font PdfFont) String() string {
return fmt.Sprintf("%T %s", font.context, font.fontSkeleton.String())
return fmt.Sprintf("FONT{%T %s}", font.context, font.baseFields().String())
}
// BaseFont returns the font's "BaseFont" field.
func (font PdfFont) BaseFont() string {
return font.basefont
return font.baseFields().basefont
}
// Subtype returns the font's "Subtype" field.
func (font PdfFont) Subtype() string {
subtype := font.subtype
subtype := font.baseFields().subtype
if t, ok := font.context.(*pdfFontType0); ok {
subtype = fmt.Sprintf("%s:%s", subtype, t.DescendantFont.Subtype())
}
@ -51,26 +47,20 @@ func (font PdfFont) Subtype() string {
// ToUnicode returns the name of the font's "ToUnicode" field if there is one, or "" if there isn't.
func (font PdfFont) ToUnicode() string {
if font.toUnicodeCmap == nil {
if font.baseFields().toUnicodeCmap == nil {
return ""
}
return font.toUnicodeCmap.Name()
return font.baseFields().toUnicodeCmap.Name()
}
// NewStandard14Font returns the standard 14 font named `basefont` as a *PdfFont, or an error if it
// `basefont` is not one the standard 14 font names.
func NewStandard14Font(basefont string) (*PdfFont, error) {
std, ok := fonts.Standard14Fonts[basefont]
std, ok := standard14Fonts[basefont]
if !ok {
return nil, ErrFontNotSupported
}
return &PdfFont{
fontSkeleton: fontSkeleton{
subtype: "Type1",
basefont: basefont,
},
context: std,
}, nil
return &PdfFont{context: &std}, nil
}
// NewPdfFontFromPdfObject loads a PdfFont from the dictionary `fontObj`. If there is a problem an
@ -84,43 +74,43 @@ func NewPdfFontFromPdfObject(fontObj core.PdfObject) (*PdfFont, error) {
// The allowType0 flag indicates whether loading Type0 font should be supported. This is used to
// avoid cyclical loading.
func newPdfFontFromPdfObject(fontObj core.PdfObject, allowType0 bool) (*PdfFont, error) {
skeleton, err := newFontSkeletonFromPdfObject(fontObj)
d, base, err := newFontBaseFieldsFromPdfObject(fontObj)
if err != nil {
return nil, err
}
font := &PdfFont{fontSkeleton: *skeleton}
switch skeleton.subtype {
font := &PdfFont{}
switch base.subtype {
case "Type0":
if !allowType0 {
common.Log.Debug("ERROR: Loading type0 not allowed. font=%s", font)
return nil, errors.New("Cyclical type0 loading")
}
type0font, err := newPdfFontType0FromPdfObject(skeleton)
type0font, err := newPdfFontType0FromPdfObject(d, base)
if err != nil {
common.Log.Debug("ERROR: While loading Type0 font. font=%s err=%v", font, err)
common.Log.Debug("ERROR: While loading Type0 font. font=%s err=%v", base, err)
return nil, err
}
font.context = type0font
case "Type1", "Type3", "MMType1", "TrueType": // !@#$
var simplefont *pdfFontSimple
if std, ok := fonts.Standard14Fonts[font.basefont]; ok && font.subtype == "Type1" {
font.context = std
if std, ok := standard14Fonts[base.basefont]; ok && base.subtype == "Type1" {
font.context = &std
stdObj := core.TraceToDirectObject(std.ToPdfObject())
stdSkeleton, err := newFontSkeletonFromPdfObject(stdObj)
d, stdBase, err := newFontBaseFieldsFromPdfObject(stdObj)
if err != nil {
common.Log.Debug("ERROR: Bad Standard14\n\tfont=%s\n\tstd=%+v", font, std)
common.Log.Debug("ERROR: Bad Standard14\n\tfont=%s\n\tstd=%+v", base, std)
return nil, err
}
simplefont, err = newSimpleFontFromPdfObject(stdSkeleton, true)
simplefont, err = newSimpleFontFromPdfObject(d, stdBase, true)
if err != nil {
common.Log.Debug("ERROR: Bad Standard14\n\tfont=%s\n\tstd=%+v", font, std)
common.Log.Debug("ERROR: Bad Standard14\n\tfont=%s\n\tstd=%+v", base, std)
return nil, err
}
} else {
simplefont, err = newSimpleFontFromPdfObject(skeleton, false)
simplefont, err = newSimpleFontFromPdfObject(d, base, false)
if err != nil {
common.Log.Debug("ERROR: While loading simple font: font=%s err=%v", font, err)
common.Log.Debug("ERROR: While loading simple font: font=%s err=%v", base, err)
return nil, err
}
}
@ -130,22 +120,22 @@ func newPdfFontFromPdfObject(fontObj core.PdfObject, allowType0 bool) (*PdfFont,
}
font.context = simplefont
case "CIDFontType0":
cidfont, err := newPdfCIDFontType0FromPdfObject(skeleton)
cidfont, err := newPdfCIDFontType0FromPdfObject(d, base)
if err != nil {
common.Log.Debug("ERROR: While loading cid font type0 font: %v", err)
return nil, err
}
font.context = cidfont
case "CIDFontType2":
cidfont, err := newPdfCIDFontType2FromPdfObject(skeleton)
cidfont, err := newPdfCIDFontType2FromPdfObject(d, base)
if err != nil {
common.Log.Debug("ERROR: While loading cid font type2 font. font=%s err=%v", font, err)
common.Log.Debug("ERROR: While loading cid font type2 font. font=%s err=%v", base, err)
return nil, err
}
font.context = cidfont
default:
common.Log.Debug("ERROR: Unsupported font type: font=%s", font)
return nil, fmt.Errorf("Unsupported font type: font=%s", font)
common.Log.Debug("ERROR: Unsupported font type: font=%s", base)
return nil, fmt.Errorf("Unsupported font type: font=%s", base)
}
return font, nil
@ -165,7 +155,7 @@ func (font PdfFont) CharcodeBytesToUnicode(data []byte) (string, int, int) {
common.Log.Debug("showText: data=[% 02x]=%#q", data, data)
charcodes := make([]uint16, 0, len(data)+len(data)%2)
if font.isCIDFont() {
if font.baseFields().isCIDFont() {
if len(data) == 1 {
data = []byte{0, data[0]}
}
@ -186,8 +176,8 @@ func (font PdfFont) CharcodeBytesToUnicode(data []byte) (string, int, int) {
charstrings := make([]string, 0, len(charcodes))
numMisses := 0
for _, code := range charcodes {
if font.toUnicodeCmap != nil {
r, ok := font.toUnicodeCmap.CharcodeToUnicode2(cmap.CharCode(code))
if font.baseFields().toUnicodeCmap != nil {
r, ok := font.baseFields().toUnicodeCmap.CharcodeToUnicode2(cmap.CharCode(code))
if ok {
charstrings = append(charstrings, r)
continue
@ -203,7 +193,7 @@ func (font PdfFont) CharcodeBytesToUnicode(data []byte) (string, int, int) {
common.Log.Debug("ERROR: No rune. code=0x%04x data=[% 02x]=%#q charcodes=[% 04x] CID=%t\n"+
"\tfont=%s\n\tencoding=%s",
code, data, data, charcodes, font.isCIDFont(), font, encoder)
code, data, data, charcodes, font.baseFields().isCIDFont(), font, encoder)
numMisses++
charstrings = append(charstrings, cmap.MissingCodeString)
}
@ -277,40 +267,36 @@ func (font PdfFont) actualFont() fonts.Font {
return t
case fonts.FontCourier:
return t
case fonts.FontCourierBold:
return t
case fonts.FontCourierBoldOblique:
return t
case fonts.FontCourierOblique:
return t
case fonts.FontHelvetica:
return t
case fonts.FontHelveticaBold:
return t
case fonts.FontHelveticaBoldOblique:
return t
case fonts.FontHelveticaOblique:
return t
case fonts.FontTimesRoman:
return t
case fonts.FontTimesBold:
return t
case fonts.FontTimesBoldItalic:
return t
case fonts.FontTimesItalic:
return t
case fonts.FontSymbol:
return t
case fonts.FontZapfDingbats:
return t
default:
common.Log.Debug("ERROR: actualFont. Unknown font type %t. font=%s", t, font)
return nil
}
}
// fontSkeleton represents the fields that are common to all PDF fonts.
type fontSkeleton struct {
// baseFields returns the fields of `font`.context that are common to all PDF fonts.
func (font PdfFont) baseFields() *fontCommon {
if font.context == nil {
common.Log.Debug("ERROR: baseFields. context is nil.")
panic("RRRR")
}
switch t := font.context.(type) {
case *pdfFontSimple:
return t.baseFields()
case *pdfFontType0:
return t.baseFields()
case *pdfCIDFontType0:
return t.baseFields()
case *pdfCIDFontType2:
return t.baseFields()
default:
//common.Log.Error("ERROR: base. Unknown font type %t. font=%s", t, font.String())
panic(fmt.Errorf("ERROR: base. Unknown font type %t. ", t))
return nil
}
}
// fontCommon represents the fields that are common to all PDF fonts.
type fontCommon struct {
// All fonts have these fields
basefont string // The font's "BaseFont" field.
subtype string // The font's "Subtype" field.
@ -322,68 +308,65 @@ type fontSkeleton struct {
toUnicodeCmap *cmap.CMap // Computed from "ToUnicode"
fontDescriptor *PdfFontDescriptor // Computed from "FontDescriptor"
// This is an internal implementation detail. It is passed to specific font types so they can parse it.
dict *core.PdfObjectDictionary
// objectNumber helps us find the font in the PDF being processed. This helps with debugging
objectNumber int64
}
// toFont returns a core.PdfObjectDictionary for `skel`.
// asPdfObjectDictionary returns `base` as a core.PdfObjectDictionary.
// It is for use in font ToPdfObject functions.
// NOTE: The returned dict's "Subtype" field is set to `subtype` if `skel` doesn't have a subtype.
func (skel fontSkeleton) toDict(subtype string) *core.PdfObjectDictionary {
// NOTE: The returned dict's "Subtype" field is set to `subtype` if `base` doesn't have a subtype.
func (base fontCommon) asPdfObjectDictionary(subtype string) *core.PdfObjectDictionary {
if subtype != "" && skel.subtype != "" && subtype != skel.subtype {
common.Log.Debug("ERROR: toDict. Overriding subtype to %#q %s", subtype, skel)
} else if subtype == "" && skel.subtype == "" {
common.Log.Debug("ERROR: toDict no subtype. font=%s", skel)
} else if skel.subtype == "" {
skel.subtype = subtype
if subtype != "" && base.subtype != "" && subtype != base.subtype {
common.Log.Debug("ERROR: asPdfObjectDictionary. Overriding subtype to %#q %s", subtype, base)
} else if subtype == "" && base.subtype == "" {
common.Log.Debug("ERROR: asPdfObjectDictionary no subtype. font=%s", base)
} else if base.subtype == "" {
base.subtype = subtype
}
d := core.MakeDict()
d.Set("Type", core.MakeName("Font"))
d.Set("BaseFont", core.MakeName(skel.basefont))
d.Set("Subtype", core.MakeName(skel.subtype))
d.Set("BaseFont", core.MakeName(base.basefont))
d.Set("Subtype", core.MakeName(base.subtype))
if skel.fontDescriptor != nil {
d.Set("FontDescriptor", skel.fontDescriptor.ToPdfObject())
if base.fontDescriptor != nil {
d.Set("FontDescriptor", base.fontDescriptor.ToPdfObject())
}
if skel.toUnicode != nil {
d.Set("ToUnicode", skel.toUnicode)
if base.toUnicode != nil {
d.Set("ToUnicode", base.toUnicode)
}
return d
}
// String returns a string that describes `skel`.
func (skel fontSkeleton) String() string {
// String returns a string that describes `base`.
func (base fontCommon) String() string {
descriptor := ""
if skel.fontDescriptor != nil {
descriptor = skel.fontDescriptor.String()
if base.fontDescriptor != nil {
descriptor = base.fontDescriptor.String()
}
return fmt.Sprintf("FONT{%#q %#q obj=%d %s}", skel.subtype, skel.basefont, skel.objectNumber, descriptor)
return fmt.Sprintf("FONT{%#q %#q obj=%d %s}", base.subtype, base.basefont, base.objectNumber, descriptor)
}
// isCIDFont returns true if `skel` is a CID font.
func (skel fontSkeleton) isCIDFont() bool {
if skel.subtype == "" {
common.Log.Debug("ERROR: isCIDFont. context is nil. font=%s", skel)
// isCIDFont returns true if `base` is a CID font.
func (base fontCommon) isCIDFont() bool {
if base.subtype == "" {
common.Log.Debug("ERROR: isCIDFont. context is nil. font=%s", base)
}
isCID := false
switch skel.subtype {
switch base.subtype {
case "Type0", "CIDFontType0", "CIDFontType2":
isCID = true
}
common.Log.Trace("isCIDFont: isCID=%t font=%s", isCID, skel)
common.Log.Trace("isCIDFont: isCID=%t font=%s", isCID, base)
return isCID
}
// newFontSkeletonFromPdfObject loads a fontSkeleton from a dictionary. If there is a problem an
// error is returned.
// The fontSkeleton is the group of fields common to all PDF fonts.
func newFontSkeletonFromPdfObject(fontObj core.PdfObject) (*fontSkeleton, error) {
font := &fontSkeleton{}
// newFontBaseFieldsFromPdfObject returns `fontObj` as a dictionary the common fields from that
// dictionary in the fontCommon return. If there is a problem an error is returned.
// The fontCommon is the group of fields common to all PDF fonts.
func newFontBaseFieldsFromPdfObject(fontObj core.PdfObject) (*core.PdfObjectDictionary, *fontCommon, error) {
font := &fontCommon{}
if obj, ok := fontObj.(*core.PdfIndirectObject); ok {
font.objectNumber = obj.ObjectNumber
@ -394,36 +377,35 @@ func newFontSkeletonFromPdfObject(fontObj core.PdfObject) (*fontSkeleton, error)
d, ok := dictObj.(*core.PdfObjectDictionary)
if !ok {
common.Log.Debug("ERROR: Font not given by a dictionary (%T)", fontObj)
return nil, ErrFontNotSupported
return nil, nil, ErrFontNotSupported
}
font.dict = d
objtype, err := core.GetName(core.TraceToDirectObject(d.Get("Type")))
if err != nil {
common.Log.Debug("ERROR: Font Incompatibility. Type (Required) missing")
return nil, ErrRequiredAttributeMissing
return nil, nil, ErrRequiredAttributeMissing
}
if objtype != "Font" {
common.Log.Debug("ERROR: Font Incompatibility. Type=%q. Should be %q.", objtype, "Font")
return nil, core.ErrTypeError
return nil, nil, core.ErrTypeError
}
subtype, err := core.GetName(core.TraceToDirectObject(d.Get("Subtype")))
if err != nil {
common.Log.Debug("ERROR: Font Incompatibility. Subtype (Required) missing")
return nil, ErrRequiredAttributeMissing
return nil, nil, ErrRequiredAttributeMissing
}
font.subtype = subtype
if subtype == "Type3" {
common.Log.Debug("ERROR: Type 3 font not supprted. d=%s", d)
return nil, ErrFontNotSupported
return nil, nil, ErrFontNotSupported
}
basefont, err := core.GetName(core.TraceToDirectObject(d.Get("BaseFont")))
if err != nil {
common.Log.Debug("ERROR: Font Incompatibility. BaseFont (Required) missing")
return nil, ErrRequiredAttributeMissing
return nil, nil, ErrRequiredAttributeMissing
}
font.basefont = basefont
@ -432,7 +414,7 @@ func newFontSkeletonFromPdfObject(fontObj core.PdfObject) (*fontSkeleton, error)
fontDescriptor, err := newPdfFontDescriptorFromPdfObject(obj)
if err != nil {
common.Log.Debug("ERROR: Bad font descriptor. err=%v", err)
return nil, err
return nil, nil, err
}
font.fontDescriptor = fontDescriptor
}
@ -441,12 +423,12 @@ func newFontSkeletonFromPdfObject(fontObj core.PdfObject) (*fontSkeleton, error)
if font.toUnicode != nil {
codemap, err := toUnicodeToCmap(font.toUnicode, font.isCIDFont())
if err != nil {
return nil, err
return nil, nil, err
}
font.toUnicodeCmap = codemap
}
return font, nil
return d, font, nil
}
// toUnicodeToCmap returns a CMap of `toUnicode` if it exists

View File

@ -7,6 +7,7 @@ import (
"github.com/unidoc/unidoc/common"
"github.com/unidoc/unidoc/pdf/core"
"github.com/unidoc/unidoc/pdf/internal/cmap"
"github.com/unidoc/unidoc/pdf/model/fonts"
"github.com/unidoc/unidoc/pdf/model/textencoding"
)
@ -87,13 +88,51 @@ import (
// associated CIDFont is called its descendant.
type pdfFontType0 struct {
container *core.PdfIndirectObject
*fontSkeleton
// These fields are common to all PDF fonts.
basefont string // The font's "BaseFont" field.
subtype string // The font's "Subtype" field.
// These are optional fields in the PDF font
toUnicode core.PdfObject // The stream containing toUnicodeCmap. We keep it around for ToPdfObject.
// These objects are computed from optional fields in the PDF font
toUnicodeCmap *cmap.CMap // Computed from "ToUnicode"
fontDescriptor *PdfFontDescriptor // Computed from "FontDescriptor"
// objectNumber helps us find the font in the PDF being processed. This helps with debugging
objectNumber int64
// These fields are specific to Type 0 fonts.
encoder textencoding.TextEncoder
Encoding core.PdfObject
DescendantFont *PdfFont // Can be either CIDFontType0 or CIDFontType2 font.
}
// pdfFontType0FromSkeleton returns a pdfFontType0 with its common fields initalized.
func pdfFontType0FromSkeleton(base *fontCommon) *pdfFontType0 {
return &pdfFontType0{
basefont: base.basefont,
subtype: base.subtype,
toUnicode: base.toUnicode,
fontDescriptor: base.fontDescriptor,
objectNumber: base.objectNumber,
}
}
// baseFields returns the fields of `font` that are common to all PDF fonts.
func (font *pdfFontType0) baseFields() *fontCommon {
return &fontCommon{
basefont: font.basefont,
subtype: font.subtype,
toUnicode: font.toUnicode,
fontDescriptor: font.fontDescriptor,
objectNumber: font.objectNumber,
}
}
// GetGlyphCharMetrics returns the character metrics for the specified glyph. A bool flag is
// returned to indicate whether or not the entry was found in the glyph to charcode mapping.
func (font pdfFontType0) GetGlyphCharMetrics(glyph string) (fonts.CharMetrics, bool) {
@ -119,7 +158,8 @@ func (font *pdfFontType0) ToPdfObject() core.PdfObject {
if font.container == nil {
font.container = &core.PdfIndirectObject{}
}
d := font.toDict("Type0")
d := font.baseFields().asPdfObjectDictionary("Type0")
font.container.PdfObject = d
if font.encoder != nil {
@ -133,16 +173,14 @@ func (font *pdfFontType0) ToPdfObject() core.PdfObject {
return font.container
}
// newPdfFontType0FromPdfObject makes a pdfFontType0 based on the input `d` in skeleton.
// newPdfFontType0FromPdfObject makes a pdfFontType0 based on the input `d` in base.
// If a problem is encountered, an error is returned.
func newPdfFontType0FromPdfObject(skeleton *fontSkeleton) (*pdfFontType0, error) {
d := skeleton.dict
func newPdfFontType0FromPdfObject(d *core.PdfObjectDictionary, base *fontCommon) (*pdfFontType0, error) {
// DescendantFonts.
arr, err := core.GetArray(core.TraceToDirectObject(d.Get("DescendantFonts")))
if err != nil {
common.Log.Debug("ERROR: Invalid DescendantFonts - not an array %s", skeleton)
common.Log.Debug("ERROR: Invalid DescendantFonts - not an array %s", base)
return nil, core.ErrRangeError
}
if len(arr) != 1 {
@ -151,14 +189,12 @@ func newPdfFontType0FromPdfObject(skeleton *fontSkeleton) (*pdfFontType0, error)
}
df, err := newPdfFontFromPdfObject(arr[0], false)
if err != nil {
common.Log.Debug("ERROR: Failed loading descendant font: err=%v %s", err, skeleton)
common.Log.Debug("ERROR: Failed loading descendant font: err=%v %s", err, base)
return nil, err
}
font := &pdfFontType0{
fontSkeleton: skeleton,
DescendantFont: df,
}
font := pdfFontType0FromSkeleton(base)
font.DescendantFont = df
encoderName, err := core.GetName(core.TraceToDirectObject(d.Get("Encoding")))
// XXX: FIXME This is not valid if encoder is not Identity-H !@#$
@ -172,14 +208,51 @@ func newPdfFontType0FromPdfObject(skeleton *fontSkeleton) (*pdfFontType0, error)
// XXX: This is a stub.
type pdfCIDFontType0 struct {
container *core.PdfIndirectObject
skeleton *fontSkeleton // Elements common to all font types.
// These fields are common to all PDF fonts.
basefont string // The font's "BaseFont" field.
subtype string // The font's "Subtype" field.
// These are optional fields in the PDF font
toUnicode core.PdfObject // The stream containing toUnicodeCmap. We keep it around for ToPdfObject.
// These objects are computed from optional fields in the PDF font
toUnicodeCmap *cmap.CMap // Computed from "ToUnicode"
fontDescriptor *PdfFontDescriptor // Computed from "FontDescriptor"
// objectNumber helps us find the font in the PDF being processed. This helps with debugging
objectNumber int64
// These fields are specific to Type 0 fonts.
encoder textencoding.TextEncoder
// Table 117 Entries in a CIDFont dictionary (page 269)
CIDSystemInfo core.PdfObject // (Required) Dictionary that defines the character collection of the CIDFont. See Table 116.
FontDescriptor core.PdfObject // (Required) Describes the CIDFonts default metrics other than its glyph widths
}
// pdfCIDFontType0FromSkeleton returns a pdfCIDFontType0 with its common fields initalized.
func pdfCIDFontType0FromSkeleton(base *fontCommon) *pdfCIDFontType0 {
return &pdfCIDFontType0{
basefont: base.basefont,
subtype: base.subtype,
toUnicode: base.toUnicode,
fontDescriptor: base.fontDescriptor,
objectNumber: base.objectNumber,
}
}
// baseFields returns the fields of `font` that are common to all PDF fonts.
func (font *pdfCIDFontType0) baseFields() *fontCommon {
return &fontCommon{
basefont: font.basefont,
subtype: font.subtype,
toUnicode: font.toUnicode,
fontDescriptor: font.fontDescriptor,
objectNumber: font.objectNumber,
}
}
// Encoder returns the font's text encoder.
@ -208,19 +281,18 @@ func (font *pdfCIDFontType0) ToPdfObject() core.PdfObject {
// newPdfCIDFontType0FromPdfObject creates a pdfCIDFontType0 object from a dictionary (either direct
// or via indirect object). If a problem occurs with loading an error is returned.
// XXX: This is a stub.
func newPdfCIDFontType0FromPdfObject(skeleton *fontSkeleton) (*pdfCIDFontType0, error) {
if skeleton.subtype != "CIDFontType0" {
common.Log.Debug("ERROR: Font SubType != CIDFontType0. font=%s", skeleton)
func newPdfCIDFontType0FromPdfObject(d *core.PdfObjectDictionary, base *fontCommon) (*pdfCIDFontType0, error) {
if base.subtype != "CIDFontType0" {
common.Log.Debug("ERROR: Font SubType != CIDFontType0. font=%s", base)
return nil, core.ErrRangeError
}
font := &pdfCIDFontType0{skeleton: skeleton}
d := skeleton.dict
font := pdfCIDFontType0FromSkeleton(base)
// CIDSystemInfo.
obj := core.TraceToDirectObject(d.Get("CIDSystemInfo"))
if obj == nil {
common.Log.Debug("ERROR: CIDSystemInfo (Required) missing. font=%s", skeleton)
common.Log.Debug("ERROR: CIDSystemInfo (Required) missing. font=%s", base)
return nil, ErrRequiredAttributeMissing
}
font.CIDSystemInfo = obj
@ -230,10 +302,26 @@ func newPdfCIDFontType0FromPdfObject(skeleton *fontSkeleton) (*pdfCIDFontType0,
// pdfCIDFontType2 represents a CIDFont Type2 font dictionary.
type pdfCIDFontType2 struct {
container *core.PdfIndirectObject
*fontSkeleton // Elements common to all font types
container *core.PdfIndirectObject
encoder textencoding.TextEncoder // !@#$ In skeleton?
// These fields are common to all PDF fonts.
basefont string // The font's "BaseFont" field.
subtype string // The font's "Subtype" field.
// These are optional fields in the PDF font
toUnicode core.PdfObject // The stream containing toUnicodeCmap. We keep it around for ToPdfObject.
// These objects are computed from optional fields in the PDF font
toUnicodeCmap *cmap.CMap // Computed from "ToUnicode"
fontDescriptor *PdfFontDescriptor // Computed from "FontDescriptor"
// objectNumber helps us find the font in the PDF being processed. This helps with debugging
objectNumber int64
// These fields are specific to Type 0 fonts.
encoder textencoding.TextEncoder // !@#$ In base?
ttfParser *fonts.TtfType
CIDSystemInfo core.PdfObject
@ -250,6 +338,28 @@ type pdfCIDFontType2 struct {
gidToWidthMap map[uint16]int
}
// pdfCIDFontType2FromSkeleton returns a pdfCIDFontType2 with its common fields initalized.
func pdfCIDFontType2FromSkeleton(base *fontCommon) *pdfCIDFontType2 {
return &pdfCIDFontType2{
basefont: base.basefont,
subtype: base.subtype,
toUnicode: base.toUnicode,
fontDescriptor: base.fontDescriptor,
objectNumber: base.objectNumber,
}
}
// baseFields returns the fields of `font` that are common to all PDF fonts.
func (font *pdfCIDFontType2) baseFields() *fontCommon {
return &fontCommon{
basefont: font.basefont,
subtype: font.subtype,
toUnicode: font.toUnicode,
fontDescriptor: font.fontDescriptor,
objectNumber: font.objectNumber,
}
}
// Encoder returns the font's text encoder.
func (font pdfCIDFontType2) Encoder() textencoding.TextEncoder {
return font.encoder
@ -289,7 +399,7 @@ func (font *pdfCIDFontType2) ToPdfObject() core.PdfObject {
if font.container == nil {
font.container = &core.PdfIndirectObject{}
}
d := font.toDict("CIDFontType2")
d := font.baseFields().asPdfObjectDictionary("CIDFontType2")
font.container.PdfObject = d
if font.CIDSystemInfo != nil {
@ -316,19 +426,18 @@ func (font *pdfCIDFontType2) ToPdfObject() core.PdfObject {
// newPdfCIDFontType2FromPdfObject creates a pdfCIDFontType2 object from a dictionary (either direct
// or via indirect object). If a problem occurs with loading, an error is returned.
func newPdfCIDFontType2FromPdfObject(skeleton *fontSkeleton) (*pdfCIDFontType2, error) {
if skeleton.subtype != "CIDFontType2" {
common.Log.Debug("ERROR: Font SubType != CIDFontType2. font=%s", skeleton)
func newPdfCIDFontType2FromPdfObject(d *core.PdfObjectDictionary, base *fontCommon) (*pdfCIDFontType2, error) {
if base.subtype != "CIDFontType2" {
common.Log.Debug("ERROR: Font SubType != CIDFontType2. font=%s", base)
return nil, core.ErrRangeError
}
font := &pdfCIDFontType2{fontSkeleton: skeleton}
d := skeleton.dict
font := pdfCIDFontType2FromSkeleton(base)
// CIDSystemInfo.
obj := d.Get("CIDSystemInfo")
if obj == nil {
common.Log.Debug("ERROR: CIDSystemInfo (Required) missing. font=%s", skeleton)
common.Log.Debug("ERROR: CIDSystemInfo (Required) missing. font=%s", base)
return nil, ErrRequiredAttributeMissing
}
font.CIDSystemInfo = obj
@ -357,8 +466,7 @@ func NewCompositePdfFontFromTTFFile(filePath string) (*PdfFont, error) {
}
// Prepare the inner descendant font (CIDFontType2).
skeletonCID := fontSkeleton{subtype: "CIDFontType2"}
cidfont := &pdfCIDFontType2{fontSkeleton: &skeletonCID}
cidfont := &pdfCIDFontType2{subtype: "CIDFontType2"}
cidfont.ttfParser = &ttf
// 2-byte character codes ➞ runes
@ -370,7 +478,7 @@ func NewCompositePdfFontFromTTFFile(filePath string) (*PdfFont, error) {
return runes[i] < runes[j]
})
skeleton := fontSkeleton{
base := fontCommon{
subtype: "Type0",
basefont: ttf.PostScriptName,
}
@ -476,15 +584,14 @@ func NewCompositePdfFontFromTTFFile(filePath string) (*PdfFont, error) {
flags |= 1 << 2 // Symbolic.
descriptor.Flags = core.MakeInteger(int64(flags))
skeleton.fontDescriptor = descriptor
base.fontDescriptor = descriptor
descendantFont := PdfFont{
context: cidfont,
fontSkeleton: skeletonCID,
context: cidfont,
}
// Make root Type0 font.
type0 := pdfFontType0{
fontSkeleton: &skeleton,
fontDescriptor: descriptor,
DescendantFont: &descendantFont,
Encoding: core.MakeName("Identity-H"),
encoder: textencoding.NewTrueTypeFontEncoder(ttf.Chars),
@ -492,8 +599,7 @@ func NewCompositePdfFontFromTTFFile(filePath string) (*PdfFont, error) {
// Build Font.
font := PdfFont{
fontSkeleton: skeleton,
context: &type0,
context: &type0,
}
return &font, nil

View File

@ -6,6 +6,7 @@ import (
"github.com/unidoc/unidoc/common"
"github.com/unidoc/unidoc/pdf/core"
"github.com/unidoc/unidoc/pdf/internal/cmap"
"github.com/unidoc/unidoc/pdf/model/fonts"
"github.com/unidoc/unidoc/pdf/model/textencoding"
)
@ -27,9 +28,23 @@ import (
// containing font-wide metrics and other attributes of the font.
// Among those attributes is an optional font filestream containing the font program.
type pdfFontSimple struct {
container *core.PdfIndirectObject
*fontSkeleton // Elements common to all font types
container *core.PdfIndirectObject
// These fields are common to all PDF fonts.
basefont string // The font's "BaseFont" field.
subtype string // The font's "Subtype" field.
// These are optional fields in the PDF font
toUnicode core.PdfObject // The stream containing toUnicodeCmap. We keep it around for ToPdfObject.
// These objects are computed from optional fields in the PDF font
toUnicodeCmap *cmap.CMap // Computed from "ToUnicode"
fontDescriptor *PdfFontDescriptor // Computed from "FontDescriptor"
// objectNumber helps us find the font in the PDF being processed. This helps with debugging
objectNumber int64
// These fields are specific to simple PDF fonts.
firstChar int
lastChar int
charWidths []float64
@ -41,6 +56,31 @@ type pdfFontSimple struct {
LastChar core.PdfObject
Widths core.PdfObject
Encoding core.PdfObject
// Standard 14 fonts metrics
fontMetrics map[string]fonts.CharMetrics
}
// pdfCIDFontType0FromSkeleton returns a pdfFontSimple with its common fields initalized.
func pdfFontSimpleFromSkeleton(base *fontCommon) *pdfFontSimple {
return &pdfFontSimple{
basefont: base.basefont,
subtype: base.subtype,
toUnicode: base.toUnicode,
fontDescriptor: base.fontDescriptor,
objectNumber: base.objectNumber,
}
}
// baseFields returns the fields of `font` that are common to all PDF fonts.
func (font *pdfFontSimple) baseFields() *fontCommon {
return &fontCommon{
basefont: font.basefont,
subtype: font.subtype,
toUnicode: font.toUnicode,
fontDescriptor: font.fontDescriptor,
objectNumber: font.objectNumber,
}
}
// Encoder returns the font's text encoder.
@ -56,6 +96,11 @@ func (font *pdfFontSimple) SetEncoder(encoder textencoding.TextEncoder) {
// GetGlyphCharMetrics returns the character metrics for the specified glyph. A bool flag is
// returned to indicate whether or not the entry was found in the glyph to charcode mapping.
func (font pdfFontSimple) GetGlyphCharMetrics(glyph string) (fonts.CharMetrics, bool) {
if font.fontMetrics != nil {
metrics, ok := font.fontMetrics[glyph]
return metrics, ok
}
metrics := fonts.CharMetrics{}
code, found := font.encoder.GlyphToCharcode(glyph)
@ -87,7 +132,7 @@ func (font pdfFontSimple) GetGlyphCharMetrics(glyph string) (fonts.CharMetrics,
}
// newSimpleFontFromPdfObject creates a pdfFontSimple from dictionary `d`. Elements of `d` that
// are already parsed are contained in `skeleton`.
// are already parsed are contained in `base`.
// An error is returned if there is a problem with loading.
// !@#$ Just return a base 14 font, if obj is a base 14 font
//
@ -96,20 +141,14 @@ func (font pdfFontSimple) GetGlyphCharMetrics(glyph string) (fonts.CharMetrics,
//
// !@#$ 9.6.6.4 Encodings for TrueType Fonts (page 265)
// Need to get TrueType font's cmap
func newSimpleFontFromPdfObject(skeleton *fontSkeleton, std14 bool) (*pdfFontSimple, error) {
font := &pdfFontSimple{fontSkeleton: skeleton}
d := skeleton.dict
func newSimpleFontFromPdfObject(d *core.PdfObjectDictionary, base *fontCommon, std14 bool) (*pdfFontSimple, error) {
font := pdfFontSimpleFromSkeleton(base)
// !@#$ Failing on ~/testdata/The-Byzantine-Generals-Problem.pdf
// FirstChar is not defined in ~/testdata/shamirturing.pdf
if !std14 {
obj := d.Get("FirstChar")
if obj == nil {
// See ~/testdata/shamirturing.pdf
// if skeleton.subtype == "TrueType" {
// common.Log.Debug("ERROR: FirstChar attribute missing. font=%s d=%s", skeleton, d)
// return nil, ErrRequiredAttributeMissing
// }
obj = core.PdfObject(core.MakeInteger(0))
}
font.FirstChar = obj
@ -123,11 +162,7 @@ func newSimpleFontFromPdfObject(skeleton *fontSkeleton, std14 bool) (*pdfFontSim
obj = d.Get("LastChar")
if obj == nil {
// if skeleton.subtype == "TrueType" {
// common.Log.Debug("ERROR: LastChar attribute missing")
// return nil, ErrRequiredAttributeMissing
// }
obj = core.PdfObject(core.MakeInteger(0))
obj = core.PdfObject(core.MakeInteger(255))
}
font.LastChar = obj
intVal, ok = core.TraceToDirectObject(obj).(*core.PdfObjectInteger)
@ -140,9 +175,6 @@ func newSimpleFontFromPdfObject(skeleton *fontSkeleton, std14 bool) (*pdfFontSim
font.charWidths = []float64{}
obj = d.Get("Widths")
if obj != nil {
// common.Log.Debug("ERROR: Widths missing from font")
// return nil, ErrRequiredAttributeMissing
// }
font.Widths = obj
arr, ok := core.TraceToDirectObject(obj).(*core.PdfObjectArray)
@ -173,7 +205,6 @@ func newSimpleFontFromPdfObject(skeleton *fontSkeleton, std14 bool) (*pdfFontSim
// addEncoding adds the encoding to the font.
// The order of precedence is important
func (font *pdfFontSimple) addEncoding() error {
skeleton := font.fontSkeleton
var baseEncoder string
var differences map[byte]string
var err error
@ -181,12 +212,12 @@ func (font *pdfFontSimple) addEncoding() error {
// !@#$ Stop setting default encoding in getFontEncoding XXX
baseEncoder, differences, err = getFontEncoding(font.Encoding)
if err != nil {
common.Log.Debug("ERROR: BaseFont=%q Subtype=%q Encoding=%s (%T) err=%v", skeleton.basefont,
skeleton.subtype, font.Encoding, font.Encoding, err)
common.Log.Debug("ERROR: BaseFont=%q Subtype=%q Encoding=%s (%T) err=%v", font.basefont,
font.subtype, font.Encoding, font.Encoding, err)
return err
}
common.Log.Debug("addEncoding: BaseFont=%q Subtype=%q Encoding=%s (%T)", skeleton.basefont,
skeleton.subtype, font.Encoding, font.Encoding)
common.Log.Debug("addEncoding: BaseFont=%q Subtype=%q Encoding=%s (%T)", font.basefont,
font.subtype, font.Encoding, font.Encoding)
encoder, err := textencoding.NewSimpleTextEncoder(baseEncoder, differences)
if err != nil {
@ -196,9 +227,9 @@ func (font *pdfFontSimple) addEncoding() error {
}
if font.Encoder() == nil {
descriptor := skeleton.fontDescriptor
descriptor := font.fontDescriptor
if descriptor != nil {
switch skeleton.subtype {
switch font.subtype {
case "Type1":
// XXX: !@#$ Is this the right order? Do the /Differences need to be reapplied?
if descriptor.fontFile != nil && descriptor.fontFile.encoder != nil {
@ -212,7 +243,6 @@ func (font *pdfFontSimple) addEncoding() error {
if err == nil {
font.SetEncoder(encoder)
}
}
}
}
@ -277,7 +307,7 @@ func (font *pdfFontSimple) ToPdfObject() core.PdfObject {
if font.container == nil {
font.container = &core.PdfIndirectObject{}
}
d := font.toDict("")
d := font.baseFields().asPdfObjectDictionary("")
font.container.PdfObject = d
if font.FirstChar != nil {
@ -291,6 +321,8 @@ func (font *pdfFontSimple) ToPdfObject() core.PdfObject {
}
if font.Encoding != nil {
d.Set("Encoding", font.Encoding)
} else if font.encoder != nil {
d.Set("Encoding", font.encoder.ToPdfObject())
}
return font.container
@ -309,8 +341,7 @@ func NewPdfFontFromTTFFile(filePath string) (*PdfFont, error) {
return nil, err
}
skeleton := fontSkeleton{subtype: "TrueType"}
truefont := &pdfFontSimple{fontSkeleton: &skeleton}
truefont := &pdfFontSimple{subtype: "TrueType"}
// TODO: Make more generic to allow customization... Need to know which glyphs are to be used,
// then can derive
@ -403,12 +434,84 @@ func NewPdfFontFromTTFFile(filePath string) (*PdfFont, error) {
descriptor.Flags = core.MakeInteger(int64(flags))
// Build Font.
skeleton.fontDescriptor = descriptor
truefont.fontDescriptor = descriptor
font := &PdfFont{
fontSkeleton: skeleton,
context: truefont,
context: truefont,
}
return font, nil
}
var standard14Fonts = map[string]pdfFontSimple{
"Courier": pdfFontSimple{subtype: "Type1",
basefont: "Courier",
encoder: textencoding.NewWinAnsiTextEncoder(),
fontMetrics: fonts.CourierCharMetrics,
},
"Courier-Bold": pdfFontSimple{subtype: "Type1",
basefont: "Courier-Bold",
encoder: textencoding.NewWinAnsiTextEncoder(),
fontMetrics: fonts.CourierBoldCharMetrics,
},
"Courier-BoldOblique": pdfFontSimple{subtype: "Type1",
basefont: "Courier-BoldOblique",
encoder: textencoding.NewWinAnsiTextEncoder(),
fontMetrics: fonts.CourierBoldObliqueCharMetrics,
},
"Courier-Oblique": pdfFontSimple{subtype: "Type1",
basefont: "Courier-Oblique",
encoder: textencoding.NewWinAnsiTextEncoder(),
fontMetrics: fonts.CourierObliqueCharMetrics,
},
"Helvetica": pdfFontSimple{subtype: "Type1",
basefont: "Helvetica",
encoder: textencoding.NewWinAnsiTextEncoder(),
fontMetrics: fonts.HelveticaCharMetrics,
},
"Helvetica-Bold": pdfFontSimple{subtype: "Type1",
basefont: "Helvetica-Bold",
encoder: textencoding.NewWinAnsiTextEncoder(),
fontMetrics: fonts.HelveticaBoldCharMetrics,
},
"Helvetica-BoldOblique": pdfFontSimple{subtype: "Type1",
basefont: "Helvetica-BoldOblique",
encoder: textencoding.NewWinAnsiTextEncoder(),
fontMetrics: fonts.HelveticaBoldObliqueCharMetrics,
},
"Helvetica-Oblique": pdfFontSimple{subtype: "Type1",
basefont: "Helvetica-Oblique",
encoder: textencoding.NewWinAnsiTextEncoder(),
fontMetrics: fonts.HelveticaObliqueCharMetrics,
},
"Times-Roman": pdfFontSimple{subtype: "Type1",
basefont: "Times-Roman",
encoder: textencoding.NewWinAnsiTextEncoder(),
fontMetrics: fonts.TimesRomanCharMetrics,
},
"Times-Bold": pdfFontSimple{subtype: "Type1",
basefont: "Times-Bold",
encoder: textencoding.NewWinAnsiTextEncoder(),
fontMetrics: fonts.TimesBoldCharMetrics,
},
"Times-BoldItalic": pdfFontSimple{subtype: "Type1",
basefont: "Times-BoldItalic",
encoder: textencoding.NewWinAnsiTextEncoder(),
fontMetrics: fonts.TimesBoldItalicCharMetrics,
},
"Times-Italic": pdfFontSimple{subtype: "Type1",
basefont: "Times-Italic",
encoder: textencoding.NewWinAnsiTextEncoder(),
fontMetrics: fonts.TimesItalicCharMetrics,
},
"Symbol": pdfFontSimple{subtype: "Type1",
basefont: "Symbol",
encoder: textencoding.NewSymbolEncoder(),
fontMetrics: fonts.SymbolCharMetrics,
},
"ZapfDingbats": pdfFontSimple{subtype: "Type1",
basefont: "ZapfDingbats",
encoder: textencoding.NewZapfDingbatsEncoder(),
fontMetrics: fonts.ZapfDingbatsCharMetrics,
},
}