2017-07-05 23:10:57 +00:00
|
|
|
/*
|
|
|
|
* This file is subject to the terms and conditions defined in
|
|
|
|
* file 'LICENSE.md', which is part of this source code package.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package model
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2018-06-27 12:25:59 +10:00
|
|
|
"fmt"
|
2018-07-04 18:00:37 +10:00
|
|
|
"strings"
|
2017-07-05 23:10:57 +00:00
|
|
|
|
|
|
|
"github.com/unidoc/unidoc/common"
|
2018-07-15 16:28:56 +10:00
|
|
|
"github.com/unidoc/unidoc/pdf/core"
|
2018-06-27 12:25:59 +10:00
|
|
|
"github.com/unidoc/unidoc/pdf/internal/cmap"
|
2017-07-05 23:10:57 +00:00
|
|
|
"github.com/unidoc/unidoc/pdf/model/fonts"
|
|
|
|
"github.com/unidoc/unidoc/pdf/model/textencoding"
|
|
|
|
)
|
|
|
|
|
2017-09-01 13:20:51 +00:00
|
|
|
// PdfFont represents an underlying font structure which can be of type:
|
2017-07-05 23:10:57 +00:00
|
|
|
// - Type0
|
|
|
|
// - Type1
|
|
|
|
// - TrueType
|
|
|
|
// etc.
|
2018-06-28 11:06:23 +10:00
|
|
|
// 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?
|
2017-07-05 23:10:57 +00:00
|
|
|
type PdfFont struct {
|
2018-06-28 11:06:23 +10:00
|
|
|
fontSkeleton // The fields common to all fonts
|
|
|
|
context fonts.Font // The underlying font: Type0, Type1, Truetype, etc..
|
2017-07-05 23:10:57 +00:00
|
|
|
}
|
|
|
|
|
2018-06-28 17:39:20 +10:00
|
|
|
// String returns a string that describes `font`.
|
|
|
|
func (font PdfFont) String() string {
|
2018-07-21 08:43:03 +10:00
|
|
|
enc := ""
|
|
|
|
if font.context.Encoder() != nil {
|
|
|
|
enc = font.context.Encoder().String()
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("FONT{%T %s %s}", font.context, font.fontSkeleton.coreString(), enc)
|
2018-06-28 17:39:20 +10:00
|
|
|
}
|
2018-06-28 11:06:23 +10:00
|
|
|
|
2018-06-28 17:39:20 +10:00
|
|
|
// BaseFont returns the font's "BaseFont" field.
|
|
|
|
func (font PdfFont) BaseFont() string {
|
2018-07-09 18:00:10 +10:00
|
|
|
return font.basefont
|
2018-06-28 17:39:20 +10:00
|
|
|
}
|
2018-06-27 12:25:59 +10:00
|
|
|
|
2018-06-28 17:39:20 +10:00
|
|
|
// Subtype returns the font's "Subtype" field.
|
|
|
|
func (font PdfFont) Subtype() string {
|
2018-07-09 18:00:10 +10:00
|
|
|
subtype := font.subtype
|
2018-06-29 14:21:59 +10:00
|
|
|
if t, ok := font.context.(*pdfFontType0); ok {
|
|
|
|
subtype = fmt.Sprintf("%s:%s", subtype, t.DescendantFont.Subtype())
|
|
|
|
}
|
|
|
|
return subtype
|
|
|
|
}
|
|
|
|
|
|
|
|
// ToUnicode returns the name of the font's "ToUnicode" field if there is one, or "" if there isn't.
|
|
|
|
func (font PdfFont) ToUnicode() string {
|
2018-06-29 16:36:06 +10:00
|
|
|
if font.toUnicodeCmap == nil {
|
2018-06-29 14:21:59 +10:00
|
|
|
return ""
|
|
|
|
}
|
2018-06-29 16:36:06 +10:00
|
|
|
return font.toUnicodeCmap.Name()
|
2018-06-27 12:25:59 +10:00
|
|
|
}
|
|
|
|
|
2018-07-13 17:40:27 +10:00
|
|
|
// 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.
|
2018-07-07 09:45:55 +10:00
|
|
|
func NewStandard14Font(basefont string) (*PdfFont, error) {
|
|
|
|
std, ok := fonts.Standard14Fonts[basefont]
|
|
|
|
if !ok {
|
|
|
|
return nil, ErrFontNotSupported
|
|
|
|
}
|
|
|
|
return &PdfFont{
|
|
|
|
fontSkeleton: fontSkeleton{
|
|
|
|
subtype: "Type1",
|
|
|
|
basefont: basefont,
|
|
|
|
},
|
|
|
|
context: std,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2018-06-28 17:39:20 +10:00
|
|
|
// NewPdfFontFromPdfObject loads a PdfFont from the dictionary `fontObj`. If there is a problem an
|
|
|
|
// error is returned.
|
2018-07-15 16:28:56 +10:00
|
|
|
func NewPdfFontFromPdfObject(fontObj core.PdfObject) (*PdfFont, error) {
|
2018-06-28 17:39:20 +10:00
|
|
|
return newPdfFontFromPdfObject(fontObj, true)
|
|
|
|
}
|
2018-06-27 12:25:59 +10:00
|
|
|
|
2018-06-28 17:39:20 +10:00
|
|
|
// newPdfFontFromPdfObject loads a PdfFont from the dictionary `fontObj`. If there is a problem an
|
|
|
|
// error is returned.
|
|
|
|
// The allowType0 flag indicates whether loading Type0 font should be supported. This is used to
|
|
|
|
// avoid cyclical loading.
|
2018-07-15 16:28:56 +10:00
|
|
|
func newPdfFontFromPdfObject(fontObj core.PdfObject, allowType0 bool) (*PdfFont, error) {
|
2018-06-28 17:39:20 +10:00
|
|
|
skeleton, err := newFontSkeletonFromPdfObject(fontObj)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2018-06-27 12:25:59 +10:00
|
|
|
}
|
2018-06-28 17:39:20 +10:00
|
|
|
font := &PdfFont{fontSkeleton: *skeleton}
|
2018-06-27 12:25:59 +10:00
|
|
|
|
2018-06-28 17:39:20 +10:00
|
|
|
switch skeleton.subtype {
|
|
|
|
case "Type0":
|
|
|
|
if !allowType0 {
|
2018-07-21 08:43:03 +10:00
|
|
|
common.Log.Debug("ERROR: Loading type0 not allowed. font=%s", skeleton)
|
2018-06-28 17:39:20 +10:00
|
|
|
return nil, errors.New("Cyclical type0 loading")
|
|
|
|
}
|
2018-07-11 09:04:17 +10:00
|
|
|
type0font, err := newPdfFontType0FromPdfObject(skeleton)
|
2018-06-28 17:39:20 +10:00
|
|
|
if err != nil {
|
2018-07-21 08:43:03 +10:00
|
|
|
common.Log.Debug("ERROR: While loading Type0 font. font=%s err=%v", skeleton, err)
|
2018-06-28 17:39:20 +10:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
font.context = type0font
|
|
|
|
case "Type1", "Type3", "MMType1", "TrueType": // !@#$
|
2018-07-06 16:41:13 +10:00
|
|
|
var simplefont *pdfFontSimple
|
2018-06-28 17:39:20 +10:00
|
|
|
if std, ok := fonts.Standard14Fonts[font.basefont]; ok && font.subtype == "Type1" {
|
|
|
|
font.context = std
|
2018-07-15 16:28:56 +10:00
|
|
|
stdObj := core.TraceToDirectObject(std.ToPdfObject())
|
2018-07-06 16:41:13 +10:00
|
|
|
stdSkeleton, err := newFontSkeletonFromPdfObject(stdObj)
|
|
|
|
if err != nil {
|
|
|
|
common.Log.Debug("ERROR: Bad Standard14\n\tfont=%s\n\tstd=%+v", font, std)
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-07-11 09:04:17 +10:00
|
|
|
simplefont, err = newSimpleFontFromPdfObject(stdSkeleton, true)
|
2018-07-06 16:41:13 +10:00
|
|
|
if err != nil {
|
|
|
|
common.Log.Debug("ERROR: Bad Standard14\n\tfont=%s\n\tstd=%+v", font, std)
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-06-28 17:39:20 +10:00
|
|
|
} else {
|
2018-07-11 09:04:17 +10:00
|
|
|
simplefont, err = newSimpleFontFromPdfObject(skeleton, false)
|
2018-06-28 17:39:20 +10:00
|
|
|
if err != nil {
|
|
|
|
common.Log.Debug("ERROR: While loading simple font: font=%s err=%v", font, err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
2018-07-06 16:41:13 +10:00
|
|
|
err = simplefont.addEncoding()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
font.context = simplefont
|
2018-06-28 17:39:20 +10:00
|
|
|
case "CIDFontType0":
|
2018-07-11 09:04:17 +10:00
|
|
|
cidfont, err := newPdfCIDFontType0FromPdfObject(skeleton)
|
2018-06-28 17:39:20 +10:00
|
|
|
if err != nil {
|
|
|
|
common.Log.Debug("ERROR: While loading cid font type0 font: %v", err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
font.context = cidfont
|
|
|
|
case "CIDFontType2":
|
2018-07-11 09:04:17 +10:00
|
|
|
cidfont, err := newPdfCIDFontType2FromPdfObject(skeleton)
|
2018-06-28 17:39:20 +10:00
|
|
|
if err != nil {
|
|
|
|
common.Log.Debug("ERROR: While loading cid font type2 font. font=%s err=%v", font, err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
font.context = cidfont
|
|
|
|
default:
|
|
|
|
common.Log.Debug("ERROR: Unsupported font type: font=%s", font)
|
2018-07-04 18:00:37 +10:00
|
|
|
return nil, fmt.Errorf("Unsupported font type: font=%s", font)
|
2018-06-27 12:25:59 +10:00
|
|
|
}
|
|
|
|
|
2018-06-28 17:39:20 +10:00
|
|
|
return font, nil
|
2018-06-27 12:25:59 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
// CharcodeBytesToUnicode converts PDF character codes `data` to a Go unicode string.
|
2018-06-28 11:06:23 +10:00
|
|
|
//
|
|
|
|
// 9.10 Extraction of Text Content (page 292)
|
2018-06-27 22:01:17 +10:00
|
|
|
// The process of finding glyph descriptions in OpenType fonts by a conforming reader shall be the following:
|
|
|
|
// • For Type 1 fonts using “CFF” tables, the process shall be as described in 9.6.6.2, "Encodings
|
|
|
|
// for Type 1 Fonts".
|
|
|
|
// • For TrueType fonts using “glyf” tables, the process shall be as described in 9.6.6.4,
|
|
|
|
// "Encodings for TrueType Fonts". Since this process sometimes produces ambiguous results,
|
|
|
|
// conforming writers, instead of using a simple font, shall use a Type 0 font with an Identity-H
|
|
|
|
// encoding and use the glyph indices as character codes, as described following Table 118.
|
2018-07-13 17:40:27 +10:00
|
|
|
func (font PdfFont) CharcodeBytesToUnicode(data []byte) (string, int, int) {
|
|
|
|
common.Log.Debug("showText: data=[% 02x]=%#q", data, data)
|
|
|
|
|
2018-07-02 13:49:06 +10:00
|
|
|
charcodes := make([]uint16, 0, len(data)+len(data)%2)
|
2018-06-27 22:01:17 +10:00
|
|
|
if font.isCIDFont() {
|
|
|
|
if len(data) == 1 {
|
|
|
|
data = []byte{0, data[0]}
|
|
|
|
}
|
|
|
|
if len(data)%2 != 0 {
|
|
|
|
common.Log.Debug("ERROR: Padding data=%+v to even length", data)
|
|
|
|
data = append(data, 0)
|
|
|
|
}
|
|
|
|
for i := 0; i < len(data); i += 2 {
|
|
|
|
b := uint16(data[i])<<8 | uint16(data[i+1])
|
|
|
|
charcodes = append(charcodes, b)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for _, b := range data {
|
|
|
|
charcodes = append(charcodes, uint16(b))
|
|
|
|
}
|
2018-06-27 12:25:59 +10:00
|
|
|
}
|
2018-07-13 17:40:27 +10:00
|
|
|
|
|
|
|
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 ok {
|
|
|
|
charstrings = append(charstrings, r)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Fall back to encoding
|
|
|
|
if encoder := font.Encoder(); encoder != nil {
|
2018-06-27 22:01:17 +10:00
|
|
|
r, ok := encoder.CharcodeToRune(code)
|
2018-07-13 17:40:27 +10:00
|
|
|
if ok {
|
2018-07-16 08:09:23 +10:00
|
|
|
charstrings = append(charstrings, textencoding.RuneToString(r))
|
2018-07-13 17:40:27 +10:00
|
|
|
continue
|
2018-06-27 12:25:59 +10:00
|
|
|
}
|
2018-07-13 17:40:27 +10:00
|
|
|
|
|
|
|
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)
|
|
|
|
numMisses++
|
|
|
|
charstrings = append(charstrings, cmap.MissingCodeString)
|
2018-06-27 12:25:59 +10:00
|
|
|
}
|
|
|
|
}
|
2018-07-13 17:40:27 +10:00
|
|
|
|
|
|
|
if numMisses != 0 {
|
|
|
|
common.Log.Debug("ERROR: Couldn't convert to unicode. Using input. data=%#q=[% 02x]\n"+
|
|
|
|
"\tnumChars=%d numMisses=%d\n"+
|
|
|
|
"\tfont=%s",
|
|
|
|
string(data), data, len(charcodes), numMisses, font)
|
|
|
|
}
|
|
|
|
|
2018-07-21 08:43:03 +10:00
|
|
|
out := strings.Join(charstrings, "")
|
|
|
|
return out, len([]rune(out)), numMisses
|
2018-06-27 12:25:59 +10:00
|
|
|
}
|
|
|
|
|
2018-06-28 17:39:20 +10:00
|
|
|
// ToPdfObject converts the PdfFont object to its PDF representation.
|
2018-07-15 16:28:56 +10:00
|
|
|
func (font PdfFont) ToPdfObject() core.PdfObject {
|
2018-06-28 17:39:20 +10:00
|
|
|
if t := font.actualFont(); t != nil {
|
|
|
|
return t.ToPdfObject()
|
2018-06-27 12:25:59 +10:00
|
|
|
}
|
2018-06-28 17:39:20 +10:00
|
|
|
common.Log.Debug("ERROR: ToPdfObject Not implemented for font type=%#T. Returning null object",
|
|
|
|
font.context)
|
2018-07-15 16:28:56 +10:00
|
|
|
return core.MakeNull()
|
2018-06-28 17:39:20 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
// Encoder returns the font's text encoder.
|
|
|
|
func (font PdfFont) Encoder() textencoding.TextEncoder {
|
|
|
|
t := font.actualFont()
|
|
|
|
if t == nil {
|
|
|
|
common.Log.Debug("ERROR: Encoder not implemented for font type=%#T", font.context)
|
|
|
|
// XXX: Should we return a default encoding?
|
|
|
|
return nil
|
2018-06-27 12:25:59 +10:00
|
|
|
}
|
2018-06-28 17:39:20 +10:00
|
|
|
return t.Encoder()
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetEncoder sets the encoding for the underlying font.
|
|
|
|
// !@#$ Is this only possible for simple fonts?
|
|
|
|
func (font PdfFont) SetEncoder(encoder textencoding.TextEncoder) {
|
|
|
|
t := font.actualFont()
|
|
|
|
if t == nil {
|
|
|
|
common.Log.Debug("ERROR: SetEncoder. Not implemented for font type=%#T", font.context)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
t.SetEncoder(encoder)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetGlyphCharMetrics returns the specified char metrics for a specified glyph name.
|
|
|
|
func (font PdfFont) GetGlyphCharMetrics(glyph string) (fonts.CharMetrics, bool) {
|
|
|
|
t := font.actualFont()
|
|
|
|
if t == nil {
|
|
|
|
common.Log.Debug("ERROR: GetGlyphCharMetrics Not implemented for font type=%#T", font.context)
|
|
|
|
return fonts.CharMetrics{}, false
|
|
|
|
}
|
|
|
|
return t.GetGlyphCharMetrics(glyph)
|
2018-06-27 12:25:59 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
// actualFont returns the Font in font.context
|
|
|
|
func (font PdfFont) actualFont() fonts.Font {
|
|
|
|
if font.context == nil {
|
|
|
|
common.Log.Debug("ERROR: actualFont. context is nil. font=%s", font)
|
|
|
|
}
|
2017-09-01 13:20:51 +00:00
|
|
|
switch t := font.context.(type) {
|
2018-06-27 12:25:59 +10:00
|
|
|
case *pdfFontSimple:
|
|
|
|
return t
|
2017-09-01 13:20:51 +00:00
|
|
|
case *pdfFontType0:
|
2018-06-27 12:25:59 +10:00
|
|
|
return t
|
|
|
|
case *pdfCIDFontType0:
|
|
|
|
return t
|
2017-09-01 13:20:51 +00:00
|
|
|
case *pdfCIDFontType2:
|
2018-06-27 12:25:59 +10:00
|
|
|
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
|
2017-09-01 13:20:51 +00:00
|
|
|
}
|
2018-06-27 12:25:59 +10:00
|
|
|
}
|
2017-09-01 13:20:51 +00:00
|
|
|
|
2018-06-28 17:39:20 +10:00
|
|
|
// fontSkeleton represents the fields that are common to all PDF fonts.
|
|
|
|
type fontSkeleton struct {
|
|
|
|
// All fonts have these fields
|
|
|
|
basefont string // The font's "BaseFont" field.
|
|
|
|
subtype string // The font's "Subtype" field.
|
|
|
|
|
|
|
|
// These are optional fields in the PDF font
|
2018-07-15 16:28:56 +10:00
|
|
|
toUnicode core.PdfObject // The stream containing toUnicodeCmap. We keep it around for ToPdfObject.
|
2018-06-28 17:39:20 +10:00
|
|
|
|
|
|
|
// These objects are computed from optional fields in the PDF font
|
2018-06-29 16:36:06 +10:00
|
|
|
toUnicodeCmap *cmap.CMap // Computed from "ToUnicode"
|
2018-06-28 17:39:20 +10:00
|
|
|
fontDescriptor *PdfFontDescriptor // Computed from "FontDescriptor"
|
|
|
|
|
|
|
|
// This is an internal implementation detail. It is passed to specific font types so they can parse it.
|
2018-07-15 16:28:56 +10:00
|
|
|
dict *core.PdfObjectDictionary
|
2018-07-05 09:58:25 +10:00
|
|
|
|
|
|
|
// objectNumber helps us find the font in the PDF being processed. This helps with debugging
|
|
|
|
objectNumber int64
|
2017-09-01 13:20:51 +00:00
|
|
|
}
|
|
|
|
|
2018-07-15 16:28:56 +10:00
|
|
|
// toFont returns a core.PdfObjectDictionary for `skel`.
|
2018-06-28 17:39:20 +10:00
|
|
|
// 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.
|
2018-07-15 16:28:56 +10:00
|
|
|
func (skel fontSkeleton) toDict(subtype string) *core.PdfObjectDictionary {
|
2018-06-28 17:39:20 +10:00
|
|
|
|
2018-06-29 18:09:44 +10:00
|
|
|
if subtype != "" && skel.subtype != "" && subtype != skel.subtype {
|
2018-06-28 17:39:20 +10:00
|
|
|
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
|
2017-07-05 23:10:57 +00:00
|
|
|
}
|
|
|
|
|
2018-07-15 16:28:56 +10:00
|
|
|
d := core.MakeDict()
|
|
|
|
d.Set("Type", core.MakeName("Font"))
|
|
|
|
d.Set("BaseFont", core.MakeName(skel.basefont))
|
|
|
|
d.Set("Subtype", core.MakeName(skel.subtype))
|
2018-06-28 17:39:20 +10:00
|
|
|
|
|
|
|
if skel.fontDescriptor != nil {
|
|
|
|
d.Set("FontDescriptor", skel.fontDescriptor.ToPdfObject())
|
2018-06-27 12:25:59 +10:00
|
|
|
}
|
2018-06-28 17:39:20 +10:00
|
|
|
if skel.toUnicode != nil {
|
|
|
|
d.Set("ToUnicode", skel.toUnicode)
|
|
|
|
}
|
|
|
|
return d
|
2018-06-27 12:25:59 +10:00
|
|
|
}
|
|
|
|
|
2018-06-28 17:39:20 +10:00
|
|
|
// String returns a string that describes `skel`.
|
|
|
|
func (skel fontSkeleton) String() string {
|
2018-07-21 08:43:03 +10:00
|
|
|
return fmt.Sprintf("FONT{%s}", skel.coreString())
|
|
|
|
}
|
|
|
|
|
|
|
|
func (skel fontSkeleton) coreString() string {
|
2018-06-28 17:39:20 +10:00
|
|
|
descriptor := ""
|
|
|
|
if skel.fontDescriptor != nil {
|
2018-07-04 18:00:37 +10:00
|
|
|
descriptor = skel.fontDescriptor.String()
|
2018-06-28 17:39:20 +10:00
|
|
|
}
|
2018-07-21 08:43:03 +10:00
|
|
|
return fmt.Sprintf("%#q %#q obj=%d ToUnicode=%t %s",
|
|
|
|
skel.subtype, skel.basefont, skel.objectNumber, skel.toUnicode != nil, descriptor)
|
2018-06-27 12:25:59 +10:00
|
|
|
}
|
|
|
|
|
2018-06-28 17:39:20 +10:00
|
|
|
// 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)
|
2018-06-27 12:25:59 +10:00
|
|
|
}
|
2018-06-28 17:39:20 +10:00
|
|
|
isCID := false
|
|
|
|
switch skel.subtype {
|
|
|
|
case "Type0", "CIDFontType0", "CIDFontType2":
|
|
|
|
isCID = true
|
2017-07-05 23:10:57 +00:00
|
|
|
}
|
2018-06-28 17:39:20 +10:00
|
|
|
common.Log.Trace("isCIDFont: isCID=%t font=%s", isCID, skel)
|
|
|
|
return isCID
|
2017-07-05 23:10:57 +00:00
|
|
|
}
|
|
|
|
|
2018-06-28 11:06:23 +10:00
|
|
|
// 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.
|
2018-07-15 16:28:56 +10:00
|
|
|
func newFontSkeletonFromPdfObject(fontObj core.PdfObject) (*fontSkeleton, error) {
|
2018-06-27 12:25:59 +10:00
|
|
|
font := &fontSkeleton{}
|
2017-07-05 23:10:57 +00:00
|
|
|
|
2018-07-15 16:28:56 +10:00
|
|
|
if obj, ok := fontObj.(*core.PdfIndirectObject); ok {
|
2018-07-05 09:58:25 +10:00
|
|
|
font.objectNumber = obj.ObjectNumber
|
|
|
|
}
|
|
|
|
|
2018-07-15 16:28:56 +10:00
|
|
|
dictObj := core.TraceToDirectObject(fontObj)
|
2017-07-05 23:10:57 +00:00
|
|
|
|
2018-07-15 16:28:56 +10:00
|
|
|
d, ok := dictObj.(*core.PdfObjectDictionary)
|
2017-07-05 23:10:57 +00:00
|
|
|
if !ok {
|
2018-07-21 08:43:03 +10:00
|
|
|
if ref, ok := dictObj.(*core.PdfObjectReference); ok {
|
|
|
|
common.Log.Debug("ERROR: Font is reference %s", ref)
|
|
|
|
} else {
|
|
|
|
common.Log.Debug("ERROR: Font not given by a dictionary (%T)", fontObj)
|
|
|
|
}
|
2018-07-13 17:40:27 +10:00
|
|
|
return nil, ErrFontNotSupported
|
2018-06-27 12:25:59 +10:00
|
|
|
}
|
|
|
|
font.dict = d
|
|
|
|
|
2018-07-15 16:28:56 +10:00
|
|
|
objtype, err := core.GetName(core.TraceToDirectObject(d.Get("Type")))
|
2018-06-28 11:06:23 +10:00
|
|
|
if err != nil {
|
2018-06-27 12:25:59 +10:00
|
|
|
common.Log.Debug("ERROR: Font Incompatibility. Type (Required) missing")
|
|
|
|
return nil, ErrRequiredAttributeMissing
|
2017-07-05 23:10:57 +00:00
|
|
|
}
|
2018-06-28 11:06:23 +10:00
|
|
|
if objtype != "Font" {
|
|
|
|
common.Log.Debug("ERROR: Font Incompatibility. Type=%q. Should be %q.", objtype, "Font")
|
2018-07-15 16:28:56 +10:00
|
|
|
return nil, core.ErrTypeError
|
2018-06-28 11:06:23 +10:00
|
|
|
}
|
2017-07-05 23:10:57 +00:00
|
|
|
|
2018-07-15 16:28:56 +10:00
|
|
|
subtype, err := core.GetName(core.TraceToDirectObject(d.Get("Subtype")))
|
2018-06-28 11:06:23 +10:00
|
|
|
if err != nil {
|
2018-07-03 14:26:42 +10:00
|
|
|
common.Log.Debug("ERROR: Font Incompatibility. Subtype (Required) missing")
|
2018-06-27 12:25:59 +10:00
|
|
|
return nil, ErrRequiredAttributeMissing
|
2017-07-05 23:10:57 +00:00
|
|
|
}
|
2018-07-03 14:26:42 +10:00
|
|
|
font.subtype = subtype
|
2018-06-28 11:06:23 +10:00
|
|
|
|
2018-07-03 14:26:42 +10:00
|
|
|
if subtype == "Type3" {
|
2018-07-15 16:28:56 +10:00
|
|
|
common.Log.Debug("ERROR: Type 3 font not supprted. d=%s", d)
|
2018-07-03 14:26:42 +10:00
|
|
|
return nil, ErrFontNotSupported
|
|
|
|
}
|
|
|
|
|
2018-07-15 16:28:56 +10:00
|
|
|
basefont, err := core.GetName(core.TraceToDirectObject(d.Get("BaseFont")))
|
2018-06-27 12:25:59 +10:00
|
|
|
if err != nil {
|
2018-07-03 14:26:42 +10:00
|
|
|
common.Log.Debug("ERROR: Font Incompatibility. BaseFont (Required) missing")
|
2018-06-28 11:06:23 +10:00
|
|
|
return nil, ErrRequiredAttributeMissing
|
2017-07-05 23:10:57 +00:00
|
|
|
}
|
2018-07-03 14:26:42 +10:00
|
|
|
font.basefont = basefont
|
2017-07-05 23:10:57 +00:00
|
|
|
|
2018-06-28 11:06:23 +10:00
|
|
|
obj := d.Get("FontDescriptor")
|
2018-06-27 12:25:59 +10:00
|
|
|
if obj != nil {
|
|
|
|
fontDescriptor, err := newPdfFontDescriptorFromPdfObject(obj)
|
2017-09-01 13:20:51 +00:00
|
|
|
if err != nil {
|
2018-07-02 16:46:43 +10:00
|
|
|
common.Log.Debug("ERROR: Bad font descriptor. err=%v", err)
|
2018-07-03 14:26:42 +10:00
|
|
|
return nil, err
|
2017-09-01 13:20:51 +00:00
|
|
|
}
|
2018-06-28 11:06:23 +10:00
|
|
|
font.fontDescriptor = fontDescriptor
|
2018-06-27 12:25:59 +10:00
|
|
|
}
|
2017-07-05 23:10:57 +00:00
|
|
|
|
2018-07-21 08:43:03 +10:00
|
|
|
toUnicode := d.Get("ToUnicode")
|
|
|
|
if toUnicode != nil {
|
|
|
|
font.toUnicode = core.TraceToDirectObject(toUnicode)
|
|
|
|
codemap, err := toUnicodeToCmap(font.toUnicode, font)
|
2017-09-01 13:20:51 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-06-29 16:36:06 +10:00
|
|
|
font.toUnicodeCmap = codemap
|
2017-07-05 23:10:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return font, nil
|
2018-06-27 12:25:59 +10:00
|
|
|
}
|
2017-07-05 23:10:57 +00:00
|
|
|
|
2018-06-27 12:25:59 +10:00
|
|
|
// toUnicodeToCmap returns a CMap of `toUnicode` if it exists
|
2018-07-21 08:43:03 +10:00
|
|
|
func toUnicodeToCmap(toUnicode core.PdfObject, font *fontSkeleton) (*cmap.CMap, error) {
|
2018-07-15 16:28:56 +10:00
|
|
|
toUnicodeStream, ok := toUnicode.(*core.PdfObjectStream)
|
2018-06-27 12:25:59 +10:00
|
|
|
if !ok {
|
|
|
|
common.Log.Debug("ERROR: toUnicodeToCmap: Not a stream (%T)", toUnicode)
|
2018-07-15 16:28:56 +10:00
|
|
|
return nil, core.ErrTypeError
|
2018-06-27 12:25:59 +10:00
|
|
|
}
|
2018-07-15 16:28:56 +10:00
|
|
|
data, err := core.DecodeStream(toUnicodeStream)
|
2018-06-27 12:25:59 +10:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-07-21 08:43:03 +10:00
|
|
|
|
|
|
|
cm, err := cmap.LoadCmapFromData(data, !font.isCIDFont())
|
|
|
|
if err != nil {
|
|
|
|
// Show the object number of the bad cmap to help with debugging.
|
|
|
|
common.Log.Debug("ERROR: ObjectNumber=%d err=%v", toUnicodeStream.ObjectNumber, err)
|
|
|
|
}
|
|
|
|
return cm, err
|
2017-07-05 23:10:57 +00:00
|
|
|
}
|
|
|
|
|
2018-06-27 12:25:59 +10:00
|
|
|
// PdfFontDescriptor specifies metrics and other attributes of a font and can refer to a FontFile
|
|
|
|
// for embedded fonts.
|
|
|
|
// 9.8 Font Descriptors (page 281)
|
2017-07-05 23:10:57 +00:00
|
|
|
type PdfFontDescriptor struct {
|
2018-07-15 16:28:56 +10:00
|
|
|
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 // PFB
|
|
|
|
FontFile2 core.PdfObject // TTF
|
|
|
|
FontFile3 core.PdfObject // OTF / CFF
|
|
|
|
CharSet core.PdfObject
|
2017-07-05 23:10:57 +00:00
|
|
|
|
2018-07-02 16:46:43 +10:00
|
|
|
*fontFile
|
2018-07-13 17:40:27 +10:00
|
|
|
fontFile2 *fonts.TtfType
|
2018-07-02 16:46:43 +10:00
|
|
|
|
2017-07-05 23:10:57 +00:00
|
|
|
// Additional entries for CIDFonts
|
2018-07-15 16:28:56 +10:00
|
|
|
Style core.PdfObject
|
|
|
|
Lang core.PdfObject
|
|
|
|
FD core.PdfObject
|
|
|
|
CIDSet core.PdfObject
|
2017-07-05 23:10:57 +00:00
|
|
|
|
|
|
|
// Container.
|
2018-07-15 16:28:56 +10:00
|
|
|
container *core.PdfIndirectObject
|
2017-07-05 23:10:57 +00:00
|
|
|
}
|
|
|
|
|
2018-07-13 17:40:27 +10:00
|
|
|
// String returns a string describing the font descriptor.
|
2018-07-04 18:00:37 +10:00
|
|
|
func (descriptor *PdfFontDescriptor) String() string {
|
|
|
|
parts := []string{}
|
|
|
|
if descriptor.FontName != nil {
|
|
|
|
parts = append(parts, descriptor.FontName.String())
|
|
|
|
}
|
|
|
|
if descriptor.FontFamily != nil {
|
|
|
|
parts = append(parts, descriptor.FontFamily.String())
|
|
|
|
}
|
2018-07-13 17:40:27 +10:00
|
|
|
if descriptor.fontFile != nil {
|
|
|
|
parts = append(parts, descriptor.fontFile.String())
|
|
|
|
}
|
|
|
|
if descriptor.fontFile2 != nil {
|
|
|
|
parts = append(parts, descriptor.fontFile2.String())
|
|
|
|
}
|
2018-07-04 18:00:37 +10:00
|
|
|
parts = append(parts, fmt.Sprintf("FontFile3=%t", descriptor.FontFile3 != nil))
|
2018-07-13 17:40:27 +10:00
|
|
|
|
2018-07-04 18:00:37 +10:00
|
|
|
return fmt.Sprintf("FONT_DESCRIPTON{%s}", strings.Join(parts, ", "))
|
|
|
|
}
|
|
|
|
|
2018-07-15 16:28:56 +10:00
|
|
|
// newPdfFontDescriptorFromPdfObject loads the font descriptor from a core.PdfObject. Can either be a
|
|
|
|
// *PdfIndirectObject or a *core.PdfObjectDictionary.
|
|
|
|
func newPdfFontDescriptorFromPdfObject(obj core.PdfObject) (*PdfFontDescriptor, error) {
|
2017-07-05 23:10:57 +00:00
|
|
|
descriptor := &PdfFontDescriptor{}
|
|
|
|
|
2018-07-15 16:28:56 +10:00
|
|
|
if ind, is := obj.(*core.PdfIndirectObject); is {
|
2017-07-05 23:10:57 +00:00
|
|
|
descriptor.container = ind
|
|
|
|
obj = ind.PdfObject
|
|
|
|
}
|
|
|
|
|
2018-07-15 16:28:56 +10:00
|
|
|
d, ok := obj.(*core.PdfObjectDictionary)
|
2017-07-05 23:10:57 +00:00
|
|
|
if !ok {
|
2018-06-27 12:25:59 +10:00
|
|
|
common.Log.Debug("ERROR: FontDescriptor not given by a dictionary (%T)", obj)
|
2018-07-15 16:28:56 +10:00
|
|
|
return nil, core.ErrTypeError
|
2017-07-05 23:10:57 +00:00
|
|
|
}
|
|
|
|
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := d.Get("FontName"); obj != nil {
|
2017-07-05 23:10:57 +00:00
|
|
|
descriptor.FontName = obj
|
|
|
|
} else {
|
|
|
|
common.Log.Debug("Incompatibility: FontName (Required) missing")
|
|
|
|
}
|
2018-07-15 16:28:56 +10:00
|
|
|
fontname, _ := core.GetName(descriptor.FontName)
|
2018-06-27 12:25:59 +10:00
|
|
|
|
|
|
|
if obj := d.Get("Type"); obj != nil {
|
2018-07-15 16:28:56 +10:00
|
|
|
oname, is := obj.(*core.PdfObjectName)
|
2018-06-27 12:25:59 +10:00
|
|
|
if !is || string(*oname) != "FontDescriptor" {
|
|
|
|
common.Log.Debug("Incompatibility: Font descriptor Type invalid (%T) font=%q %T",
|
|
|
|
obj, fontname, descriptor.FontName)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
common.Log.Trace("Incompatibility: Type (Required) missing. font=%q %T",
|
|
|
|
fontname, descriptor.FontName)
|
|
|
|
}
|
2017-07-05 23:10:57 +00:00
|
|
|
|
2017-07-08 21:04:13 +00:00
|
|
|
descriptor.FontFamily = d.Get("FontFamily")
|
|
|
|
descriptor.FontStretch = d.Get("FontStretch")
|
|
|
|
descriptor.FontWeight = d.Get("FontWeight")
|
|
|
|
descriptor.Flags = d.Get("Flags")
|
|
|
|
descriptor.FontBBox = d.Get("FontBBox")
|
|
|
|
descriptor.ItalicAngle = d.Get("ItalicAngle")
|
|
|
|
descriptor.Ascent = d.Get("Ascent")
|
|
|
|
descriptor.Descent = d.Get("Descent")
|
|
|
|
descriptor.Leading = d.Get("Leading")
|
|
|
|
descriptor.CapHeight = d.Get("CapHeight")
|
|
|
|
descriptor.XHeight = d.Get("XHeight")
|
|
|
|
descriptor.StemV = d.Get("StemV")
|
|
|
|
descriptor.StemH = d.Get("StemH")
|
|
|
|
descriptor.AvgWidth = d.Get("AvgWidth")
|
|
|
|
descriptor.MaxWidth = d.Get("MaxWidth")
|
|
|
|
descriptor.MissingWidth = d.Get("MissingWidth")
|
|
|
|
descriptor.FontFile = d.Get("FontFile")
|
|
|
|
descriptor.FontFile2 = d.Get("FontFile2")
|
|
|
|
descriptor.FontFile3 = d.Get("FontFile3")
|
|
|
|
descriptor.CharSet = d.Get("CharSet")
|
|
|
|
descriptor.Style = d.Get("Style")
|
|
|
|
descriptor.Lang = d.Get("Lang")
|
|
|
|
descriptor.FD = d.Get("FD")
|
|
|
|
descriptor.CIDSet = d.Get("CIDSet")
|
2017-07-05 23:10:57 +00:00
|
|
|
|
2018-07-02 16:46:43 +10:00
|
|
|
if descriptor.FontFile != nil {
|
2018-07-15 16:28:56 +10:00
|
|
|
fontFile, err := newFontFileFromPdfObject(descriptor.FontFile)
|
2018-07-02 16:46:43 +10:00
|
|
|
if err != nil {
|
|
|
|
return descriptor, err
|
|
|
|
}
|
2018-07-15 16:28:56 +10:00
|
|
|
common.Log.Debug("fontFile=%s", fontFile)
|
|
|
|
descriptor.fontFile = fontFile
|
2018-07-02 16:46:43 +10:00
|
|
|
}
|
2018-07-13 17:40:27 +10:00
|
|
|
if descriptor.FontFile2 != nil {
|
2018-07-15 16:28:56 +10:00
|
|
|
fontFile2, err := fonts.NewFontFile2FromPdfObject(descriptor.FontFile2)
|
2018-07-13 17:40:27 +10:00
|
|
|
if err != nil {
|
|
|
|
return descriptor, err
|
|
|
|
}
|
2018-07-15 16:28:56 +10:00
|
|
|
common.Log.Debug("fontFile2=%s", fontFile2.String())
|
|
|
|
descriptor.fontFile2 = &fontFile2
|
2018-07-13 17:40:27 +10:00
|
|
|
}
|
2017-07-05 23:10:57 +00:00
|
|
|
return descriptor, nil
|
|
|
|
}
|
|
|
|
|
2017-09-01 13:20:51 +00:00
|
|
|
// ToPdfObject returns the PdfFontDescriptor as a PDF dictionary inside an indirect object.
|
2018-07-15 16:28:56 +10:00
|
|
|
func (this *PdfFontDescriptor) ToPdfObject() core.PdfObject {
|
|
|
|
d := core.MakeDict()
|
2017-07-05 23:10:57 +00:00
|
|
|
if this.container == nil {
|
2018-07-15 16:28:56 +10:00
|
|
|
this.container = &core.PdfIndirectObject{}
|
2017-07-05 23:10:57 +00:00
|
|
|
}
|
|
|
|
this.container.PdfObject = d
|
|
|
|
|
2018-07-15 16:28:56 +10:00
|
|
|
d.Set("Type", core.MakeName("FontDescriptor"))
|
2017-07-05 23:10:57 +00:00
|
|
|
|
|
|
|
if this.FontName != nil {
|
|
|
|
d.Set("FontName", this.FontName)
|
|
|
|
}
|
|
|
|
|
|
|
|
if this.FontFamily != nil {
|
|
|
|
d.Set("FontFamily", this.FontFamily)
|
|
|
|
}
|
|
|
|
|
|
|
|
if this.FontStretch != nil {
|
|
|
|
d.Set("FontStretch", this.FontStretch)
|
|
|
|
}
|
|
|
|
|
|
|
|
if this.FontWeight != nil {
|
|
|
|
d.Set("FontWeight", this.FontWeight)
|
|
|
|
}
|
|
|
|
|
|
|
|
if this.Flags != nil {
|
|
|
|
d.Set("Flags", this.Flags)
|
|
|
|
}
|
|
|
|
|
|
|
|
if this.FontBBox != nil {
|
|
|
|
d.Set("FontBBox", this.FontBBox)
|
|
|
|
}
|
|
|
|
|
|
|
|
if this.ItalicAngle != nil {
|
|
|
|
d.Set("ItalicAngle", this.ItalicAngle)
|
|
|
|
}
|
|
|
|
|
|
|
|
if this.Ascent != nil {
|
|
|
|
d.Set("Ascent", this.Ascent)
|
|
|
|
}
|
|
|
|
|
|
|
|
if this.Descent != nil {
|
|
|
|
d.Set("Descent", this.Descent)
|
|
|
|
}
|
|
|
|
|
|
|
|
if this.Leading != nil {
|
|
|
|
d.Set("Leading", this.Leading)
|
|
|
|
}
|
|
|
|
|
|
|
|
if this.CapHeight != nil {
|
|
|
|
d.Set("CapHeight", this.CapHeight)
|
|
|
|
}
|
|
|
|
|
|
|
|
if this.XHeight != nil {
|
|
|
|
d.Set("XHeight", this.XHeight)
|
|
|
|
}
|
|
|
|
|
|
|
|
if this.StemV != nil {
|
|
|
|
d.Set("StemV", this.StemV)
|
|
|
|
}
|
|
|
|
|
|
|
|
if this.StemH != nil {
|
|
|
|
d.Set("StemH", this.StemH)
|
|
|
|
}
|
|
|
|
|
|
|
|
if this.AvgWidth != nil {
|
|
|
|
d.Set("AvgWidth", this.AvgWidth)
|
|
|
|
}
|
|
|
|
|
|
|
|
if this.MaxWidth != nil {
|
|
|
|
d.Set("MaxWidth", this.MaxWidth)
|
|
|
|
}
|
|
|
|
|
|
|
|
if this.MissingWidth != nil {
|
|
|
|
d.Set("MissingWidth", this.MissingWidth)
|
|
|
|
}
|
|
|
|
|
|
|
|
if this.FontFile != nil {
|
|
|
|
d.Set("FontFile", this.FontFile)
|
|
|
|
}
|
|
|
|
|
|
|
|
if this.FontFile2 != nil {
|
|
|
|
d.Set("FontFile2", this.FontFile2)
|
|
|
|
}
|
|
|
|
|
|
|
|
if this.FontFile3 != nil {
|
|
|
|
d.Set("FontFile3", this.FontFile3)
|
|
|
|
}
|
|
|
|
|
|
|
|
if this.CharSet != nil {
|
|
|
|
d.Set("CharSet", this.CharSet)
|
|
|
|
}
|
|
|
|
|
|
|
|
if this.Style != nil {
|
|
|
|
d.Set("FontName", this.FontName)
|
|
|
|
}
|
|
|
|
|
|
|
|
if this.Lang != nil {
|
|
|
|
d.Set("Lang", this.Lang)
|
|
|
|
}
|
|
|
|
|
|
|
|
if this.FD != nil {
|
|
|
|
d.Set("FD", this.FD)
|
|
|
|
}
|
|
|
|
|
|
|
|
if this.CIDSet != nil {
|
|
|
|
d.Set("CIDSet", this.CIDSet)
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.container
|
|
|
|
}
|