2018-07-24 21:32:02 +10:00
|
|
|
|
/*
|
|
|
|
|
* This file is subject to the terms and conditions defined in
|
|
|
|
|
* file 'LICENSE.md', which is part of this source code package.
|
|
|
|
|
*/
|
|
|
|
|
|
2017-09-01 13:20:51 +00:00
|
|
|
|
package model
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"errors"
|
|
|
|
|
"io/ioutil"
|
|
|
|
|
"sort"
|
|
|
|
|
|
|
|
|
|
"github.com/unidoc/unidoc/common"
|
2018-07-15 16:28:56 +10:00
|
|
|
|
"github.com/unidoc/unidoc/pdf/core"
|
2017-09-01 13:20:51 +00:00
|
|
|
|
"github.com/unidoc/unidoc/pdf/model/fonts"
|
|
|
|
|
"github.com/unidoc/unidoc/pdf/model/textencoding"
|
|
|
|
|
)
|
|
|
|
|
|
2018-06-27 12:25:59 +10:00
|
|
|
|
/*
|
|
|
|
|
9.7.2 CID-Keyed Fonts Overview (page 267)
|
|
|
|
|
The CID-keyed font architecture specifies the external representation of certain font programs,
|
|
|
|
|
called *CMap* and *CIDFont* files, along with some conventions for combining and using those files.
|
|
|
|
|
|
|
|
|
|
A *CMap* (character map) file shall specify the correspondence between character codes and the CID
|
|
|
|
|
numbers used to identify glyphs. It is equivalent to the concept of an encoding in simple fonts.
|
|
|
|
|
Whereas a simple font allows a maximum of 256 glyphs to be encoded and accessible at one time, a
|
|
|
|
|
CMap can describe a mapping from multiple-byte codes to thousands of glyphs in a large CID-keyed
|
|
|
|
|
font.
|
|
|
|
|
|
|
|
|
|
9.7.4 CIDFonts (page 269)
|
|
|
|
|
|
|
|
|
|
A CIDFont program contains glyph descriptions that are accessed using a CID as the character
|
|
|
|
|
selector. There are two types of CIDFonts:
|
|
|
|
|
• A Type 0 CIDFont contains glyph descriptions based on CFF
|
|
|
|
|
• A Type 2 CIDFont contains glyph descriptions based on the TrueType font format
|
|
|
|
|
|
|
|
|
|
A CIDFont dictionary is a PDF object that contains information about a CIDFont program. Although
|
|
|
|
|
its Type value is Font, a CIDFont is not actually a font.
|
|
|
|
|
It does not have an Encoding entry,
|
|
|
|
|
it may not be listed in the Font subdictionary of a resource dictionary, and
|
|
|
|
|
it may not be used as the operand of the Tf operator.
|
|
|
|
|
It shall be used only as a descendant of a Type 0 font.
|
|
|
|
|
The CMap in the Type 0 font shall be what defines the encoding that maps character codes to CIDs
|
|
|
|
|
in the CIDFont.
|
|
|
|
|
|
|
|
|
|
9.7.6 Type 0 Font Dictionaries (page 279)
|
|
|
|
|
|
|
|
|
|
Type Font
|
|
|
|
|
Subtype Type0
|
|
|
|
|
BaseFont (Required) The name of the font. If the descendant is a Type 0 CIDFont, this name
|
|
|
|
|
should be the concatenation of the CIDFont’s BaseFont name, a hyphen, and the CMap
|
|
|
|
|
name given in the Encoding entry (or the CMapName entry in the CMap). If the
|
|
|
|
|
descendant is a Type 2 CIDFont, this name should be the same as the CIDFont’s BaseFont
|
|
|
|
|
name.
|
|
|
|
|
NOTE In principle, this is an arbitrary name, since there is no font program
|
|
|
|
|
associated directly with a Type 0 font dictionary. The conventions described here
|
|
|
|
|
ensure maximum compatibility with existing readers.
|
|
|
|
|
Encoding name or stream (Required)
|
|
|
|
|
The name of a predefined CMap, or a stream containing a CMap that maps character codes
|
|
|
|
|
to font numbers and CIDs. If the descendant is a Type 2 CIDFont whose associated
|
|
|
|
|
TrueType font program is not embedded in the PDF file, the Encoding entry shall be a
|
|
|
|
|
predefined CMap name (see 9.7.4.2, "Glyph Selection in CIDFonts").
|
|
|
|
|
|
|
|
|
|
Type 0 font from 000046.pdf
|
|
|
|
|
|
|
|
|
|
103 0 obj
|
|
|
|
|
<< /Type /Font /Subtype /Type0 /Encoding /Identity-H /DescendantFonts [179 0 R]
|
|
|
|
|
/BaseFont /FLDOLC+PingFangSC-Regular >>
|
|
|
|
|
endobj
|
|
|
|
|
179 0 obj
|
|
|
|
|
<< /Type /Font /Subtype /CIDFontType0 /BaseFont /FLDOLC+PingFangSC-Regular
|
|
|
|
|
/CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >>
|
|
|
|
|
/W 180 0 R /DW 1000 /FontDescriptor 181 0 R >>
|
|
|
|
|
endobj
|
|
|
|
|
180 0 obj
|
|
|
|
|
[ ]
|
|
|
|
|
endobj
|
|
|
|
|
181 0 obj
|
|
|
|
|
<< /Type /FontDescriptor /FontName /FLDOLC+PingFangSC-Regular /Flags 4 /FontBBox
|
|
|
|
|
[-123 -263 1177 1003] /ItalicAngle 0 /Ascent 972 /Descent -232 /CapHeight
|
|
|
|
|
864 /StemV 70 /XHeight 648 /StemH 64 /AvgWidth 1000 /MaxWidth 1300 /FontFile3
|
|
|
|
|
182 0 R >>
|
|
|
|
|
endobj
|
|
|
|
|
182 0 obj
|
|
|
|
|
<< /Length 183 0 R /Subtype /CIDFontType0C /Filter /FlateDecode >>
|
|
|
|
|
stream
|
|
|
|
|
....
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
// pdfFontType0 represents a Type0 font in PDF. Used for composite fonts which can encode multiple
|
|
|
|
|
// bytes for complex symbols (e.g. used in Asian languages). Represents the root font whereas the
|
|
|
|
|
// associated CIDFont is called its descendant.
|
2017-09-01 13:20:51 +00:00
|
|
|
|
type pdfFontType0 struct {
|
2018-07-15 16:28:56 +10:00
|
|
|
|
container *core.PdfIndirectObject
|
2018-07-23 14:55:23 +10:00
|
|
|
|
fontCommon
|
2018-07-16 17:42:08 +10:00
|
|
|
|
|
|
|
|
|
// These fields are specific to Type 0 fonts.
|
2018-06-27 12:25:59 +10:00
|
|
|
|
encoder textencoding.TextEncoder
|
2018-07-15 16:28:56 +10:00
|
|
|
|
Encoding core.PdfObject
|
2017-09-01 13:20:51 +00:00
|
|
|
|
DescendantFont *PdfFont // Can be either CIDFontType0 or CIDFontType2 font.
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-16 17:42:08 +10:00
|
|
|
|
// pdfFontType0FromSkeleton returns a pdfFontType0 with its common fields initalized.
|
|
|
|
|
func pdfFontType0FromSkeleton(base *fontCommon) *pdfFontType0 {
|
|
|
|
|
return &pdfFontType0{
|
2018-07-23 14:55:23 +10:00
|
|
|
|
fontCommon: *base,
|
2018-07-16 17:42:08 +10:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// baseFields returns the fields of `font` that are common to all PDF fonts.
|
|
|
|
|
func (font *pdfFontType0) baseFields() *fontCommon {
|
2018-07-23 14:55:23 +10:00
|
|
|
|
return &font.fontCommon
|
2018-07-16 17:42:08 +10:00
|
|
|
|
}
|
|
|
|
|
|
2018-06-27 12:25:59 +10:00
|
|
|
|
// 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.
|
2017-09-01 13:20:51 +00:00
|
|
|
|
func (font pdfFontType0) GetGlyphCharMetrics(glyph string) (fonts.CharMetrics, bool) {
|
|
|
|
|
if font.DescendantFont == nil {
|
2018-06-27 12:25:59 +10:00
|
|
|
|
common.Log.Debug("ERROR: No descendant. font=%s", font)
|
|
|
|
|
return fonts.CharMetrics{}, false
|
2017-09-01 13:20:51 +00:00
|
|
|
|
}
|
|
|
|
|
return font.DescendantFont.GetGlyphCharMetrics(glyph)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Encoder returns the font's text encoder.
|
|
|
|
|
func (font pdfFontType0) Encoder() textencoding.TextEncoder {
|
|
|
|
|
return font.encoder
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetEncoder sets the encoder for the truetype font.
|
|
|
|
|
func (font pdfFontType0) SetEncoder(encoder textencoding.TextEncoder) {
|
|
|
|
|
font.encoder = encoder
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ToPdfObject converts the pdfFontType0 to a PDF representation.
|
2018-07-15 16:28:56 +10:00
|
|
|
|
func (font *pdfFontType0) ToPdfObject() core.PdfObject {
|
2017-09-01 13:20:51 +00:00
|
|
|
|
if font.container == nil {
|
2018-07-15 16:28:56 +10:00
|
|
|
|
font.container = &core.PdfIndirectObject{}
|
2017-09-01 13:20:51 +00:00
|
|
|
|
}
|
2018-07-16 17:42:08 +10:00
|
|
|
|
d := font.baseFields().asPdfObjectDictionary("Type0")
|
|
|
|
|
|
2017-09-01 13:20:51 +00:00
|
|
|
|
font.container.PdfObject = d
|
|
|
|
|
|
2018-06-29 18:09:44 +10:00
|
|
|
|
if font.encoder != nil {
|
|
|
|
|
d.Set("Encoding", font.encoder.ToPdfObject())
|
2017-09-01 13:20:51 +00:00
|
|
|
|
}
|
|
|
|
|
if font.DescendantFont != nil {
|
|
|
|
|
// Shall be 1 element array.
|
2018-07-15 16:28:56 +10:00
|
|
|
|
d.Set("DescendantFonts", core.MakeArray(font.DescendantFont.ToPdfObject()))
|
2017-09-01 13:20:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return font.container
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-16 17:42:08 +10:00
|
|
|
|
// newPdfFontType0FromPdfObject makes a pdfFontType0 based on the input `d` in base.
|
2018-07-11 09:04:17 +10:00
|
|
|
|
// If a problem is encountered, an error is returned.
|
2018-07-16 17:42:08 +10:00
|
|
|
|
func newPdfFontType0FromPdfObject(d *core.PdfObjectDictionary, base *fontCommon) (*pdfFontType0, error) {
|
2017-09-01 13:20:51 +00:00
|
|
|
|
|
2018-06-27 12:25:59 +10:00
|
|
|
|
// DescendantFonts.
|
2018-07-21 21:20:39 +10:00
|
|
|
|
arr, ok := core.GetArrayVal(core.TraceToDirectObject(d.Get("DescendantFonts")))
|
|
|
|
|
if !ok {
|
2018-07-16 17:42:08 +10:00
|
|
|
|
common.Log.Debug("ERROR: Invalid DescendantFonts - not an array %s", base)
|
2018-07-15 16:28:56 +10:00
|
|
|
|
return nil, core.ErrRangeError
|
2018-06-27 12:25:59 +10:00
|
|
|
|
}
|
2018-06-28 11:06:23 +10:00
|
|
|
|
if len(arr) != 1 {
|
|
|
|
|
common.Log.Debug("ERROR: Array length != 1 (%d)", len(arr))
|
2018-07-15 16:28:56 +10:00
|
|
|
|
return nil, core.ErrRangeError
|
2018-06-27 12:25:59 +10:00
|
|
|
|
}
|
2018-06-28 11:06:23 +10:00
|
|
|
|
df, err := newPdfFontFromPdfObject(arr[0], false)
|
2018-06-27 12:25:59 +10:00
|
|
|
|
if err != nil {
|
2018-07-16 17:42:08 +10:00
|
|
|
|
common.Log.Debug("ERROR: Failed loading descendant font: err=%v %s", err, base)
|
2018-06-27 12:25:59 +10:00
|
|
|
|
return nil, err
|
2017-09-01 13:20:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-07-16 17:42:08 +10:00
|
|
|
|
font := pdfFontType0FromSkeleton(base)
|
|
|
|
|
font.DescendantFont = df
|
2018-06-27 22:01:17 +10:00
|
|
|
|
|
2018-07-24 21:32:02 +10:00
|
|
|
|
encoderName, ok := core.GetNameVal(d.Get("Encoding"))
|
|
|
|
|
if ok {
|
|
|
|
|
if encoderName == "Identity-H" || encoderName == "Identity-V" {
|
|
|
|
|
font.encoder = textencoding.NewIdentityTextEncoder(encoderName)
|
|
|
|
|
} else {
|
|
|
|
|
common.Log.Debug("Unhandled cmap %q", encoderName)
|
|
|
|
|
}
|
2018-06-27 22:01:17 +10:00
|
|
|
|
}
|
2018-06-27 12:25:59 +10:00
|
|
|
|
return font, nil
|
|
|
|
|
}
|
2017-09-01 13:20:51 +00:00
|
|
|
|
|
2018-06-27 12:25:59 +10:00
|
|
|
|
// pdfCIDFontType0 represents a CIDFont Type0 font dictionary.
|
|
|
|
|
type pdfCIDFontType0 struct {
|
2018-07-15 16:28:56 +10:00
|
|
|
|
container *core.PdfIndirectObject
|
2018-07-23 14:55:23 +10:00
|
|
|
|
fontCommon
|
2018-07-16 17:42:08 +10:00
|
|
|
|
|
|
|
|
|
// These fields are specific to Type 0 fonts.
|
2018-06-27 12:25:59 +10:00
|
|
|
|
encoder textencoding.TextEncoder
|
|
|
|
|
|
|
|
|
|
// Table 117 – Entries in a CIDFont dictionary (page 269)
|
2018-07-15 16:28:56 +10:00
|
|
|
|
CIDSystemInfo core.PdfObject // (Required) Dictionary that defines the character collection of the CIDFont. See Table 116.
|
|
|
|
|
FontDescriptor core.PdfObject // (Required) Describes the CIDFont’s default metrics other than its glyph widths
|
2018-07-16 17:42:08 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// pdfCIDFontType0FromSkeleton returns a pdfCIDFontType0 with its common fields initalized.
|
|
|
|
|
func pdfCIDFontType0FromSkeleton(base *fontCommon) *pdfCIDFontType0 {
|
|
|
|
|
return &pdfCIDFontType0{
|
2018-07-23 14:55:23 +10:00
|
|
|
|
fontCommon: *base,
|
2018-07-16 17:42:08 +10:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-06-29 16:36:06 +10:00
|
|
|
|
|
2018-07-16 17:42:08 +10:00
|
|
|
|
// baseFields returns the fields of `font` that are common to all PDF fonts.
|
|
|
|
|
func (font *pdfCIDFontType0) baseFields() *fontCommon {
|
2018-07-23 14:55:23 +10:00
|
|
|
|
return &font.fontCommon
|
2018-06-27 12:25:59 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Encoder returns the font's text encoder.
|
|
|
|
|
func (font pdfCIDFontType0) Encoder() textencoding.TextEncoder {
|
|
|
|
|
return font.encoder
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetEncoder sets the encoder for the truetype font.
|
|
|
|
|
func (font pdfCIDFontType0) SetEncoder(encoder textencoding.TextEncoder) {
|
|
|
|
|
font.encoder = encoder
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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 pdfCIDFontType0) GetGlyphCharMetrics(glyph string) (fonts.CharMetrics, bool) {
|
2018-06-29 16:36:06 +10:00
|
|
|
|
return fonts.CharMetrics{}, true
|
2018-06-27 12:25:59 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ToPdfObject converts the pdfCIDFontType0 to a PDF representation.
|
2018-07-15 16:28:56 +10:00
|
|
|
|
func (font *pdfCIDFontType0) ToPdfObject() core.PdfObject {
|
|
|
|
|
return core.MakeNull()
|
2018-06-27 12:25:59 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// newPdfCIDFontType0FromPdfObject creates a pdfCIDFontType0 object from a dictionary (either direct
|
|
|
|
|
// or via indirect object). If a problem occurs with loading an error is returned.
|
2018-07-16 17:42:08 +10:00
|
|
|
|
func newPdfCIDFontType0FromPdfObject(d *core.PdfObjectDictionary, base *fontCommon) (*pdfCIDFontType0, error) {
|
|
|
|
|
if base.subtype != "CIDFontType0" {
|
|
|
|
|
common.Log.Debug("ERROR: Font SubType != CIDFontType0. font=%s", base)
|
2018-07-15 16:28:56 +10:00
|
|
|
|
return nil, core.ErrRangeError
|
2017-09-01 13:20:51 +00:00
|
|
|
|
}
|
2018-06-27 12:25:59 +10:00
|
|
|
|
|
2018-07-16 17:42:08 +10:00
|
|
|
|
font := pdfCIDFontType0FromSkeleton(base)
|
2018-06-27 12:25:59 +10:00
|
|
|
|
|
|
|
|
|
// CIDSystemInfo.
|
2018-07-15 16:28:56 +10:00
|
|
|
|
obj := core.TraceToDirectObject(d.Get("CIDSystemInfo"))
|
2018-06-27 12:25:59 +10:00
|
|
|
|
if obj == nil {
|
2018-07-16 17:42:08 +10:00
|
|
|
|
common.Log.Debug("ERROR: CIDSystemInfo (Required) missing. font=%s", base)
|
2018-06-27 12:25:59 +10:00
|
|
|
|
return nil, ErrRequiredAttributeMissing
|
2017-09-01 13:20:51 +00:00
|
|
|
|
}
|
2018-06-27 12:25:59 +10:00
|
|
|
|
font.CIDSystemInfo = obj
|
2017-09-01 13:20:51 +00:00
|
|
|
|
|
|
|
|
|
return font, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// pdfCIDFontType2 represents a CIDFont Type2 font dictionary.
|
|
|
|
|
type pdfCIDFontType2 struct {
|
2018-07-16 17:42:08 +10:00
|
|
|
|
container *core.PdfIndirectObject
|
2018-07-23 14:55:23 +10:00
|
|
|
|
fontCommon
|
2018-07-16 17:42:08 +10:00
|
|
|
|
|
|
|
|
|
// These fields are specific to Type 0 fonts.
|
2018-07-24 21:32:02 +10:00
|
|
|
|
encoder textencoding.TextEncoder
|
2017-09-01 13:20:51 +00:00
|
|
|
|
ttfParser *fonts.TtfType
|
|
|
|
|
|
2018-07-15 16:28:56 +10:00
|
|
|
|
CIDSystemInfo core.PdfObject
|
|
|
|
|
DW core.PdfObject
|
|
|
|
|
W core.PdfObject
|
|
|
|
|
DW2 core.PdfObject
|
|
|
|
|
W2 core.PdfObject
|
|
|
|
|
CIDToGIDMap core.PdfObject
|
2017-09-01 13:20:51 +00:00
|
|
|
|
|
|
|
|
|
// Mapping between unicode runes to widths.
|
|
|
|
|
runeToWidthMap map[uint16]int
|
|
|
|
|
|
|
|
|
|
// Also mapping between GIDs (glyph index) and width.
|
|
|
|
|
gidToWidthMap map[uint16]int
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-16 17:42:08 +10:00
|
|
|
|
// pdfCIDFontType2FromSkeleton returns a pdfCIDFontType2 with its common fields initalized.
|
|
|
|
|
func pdfCIDFontType2FromSkeleton(base *fontCommon) *pdfCIDFontType2 {
|
|
|
|
|
return &pdfCIDFontType2{
|
2018-07-23 14:55:23 +10:00
|
|
|
|
fontCommon: *base,
|
2018-07-16 17:42:08 +10:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// baseFields returns the fields of `font` that are common to all PDF fonts.
|
|
|
|
|
func (font *pdfCIDFontType2) baseFields() *fontCommon {
|
2018-07-23 14:55:23 +10:00
|
|
|
|
return &font.fontCommon
|
2018-07-16 17:42:08 +10:00
|
|
|
|
}
|
|
|
|
|
|
2017-09-01 13:20:51 +00:00
|
|
|
|
// Encoder returns the font's text encoder.
|
|
|
|
|
func (font pdfCIDFontType2) Encoder() textencoding.TextEncoder {
|
|
|
|
|
return font.encoder
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetEncoder sets the encoder for the truetype font.
|
|
|
|
|
func (font pdfCIDFontType2) SetEncoder(encoder textencoding.TextEncoder) {
|
|
|
|
|
font.encoder = encoder
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-27 12:25:59 +10:00
|
|
|
|
// 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.
|
2017-09-01 13:20:51 +00:00
|
|
|
|
func (font pdfCIDFontType2) GetGlyphCharMetrics(glyph string) (fonts.CharMetrics, bool) {
|
|
|
|
|
metrics := fonts.CharMetrics{}
|
|
|
|
|
|
|
|
|
|
enc := textencoding.NewTrueTypeFontEncoder(font.ttfParser.Chars)
|
|
|
|
|
|
|
|
|
|
// Convert the glyph to character code.
|
|
|
|
|
rune, found := enc.GlyphToRune(glyph)
|
|
|
|
|
if !found {
|
2018-06-28 11:06:23 +10:00
|
|
|
|
common.Log.Debug("Unable to convert glyph %q to charcode (identity)", glyph)
|
2017-09-01 13:20:51 +00:00
|
|
|
|
return metrics, false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w, found := font.runeToWidthMap[uint16(rune)]
|
|
|
|
|
if !found {
|
|
|
|
|
return metrics, false
|
|
|
|
|
}
|
|
|
|
|
metrics.GlyphName = glyph
|
|
|
|
|
metrics.Wx = float64(w)
|
|
|
|
|
|
|
|
|
|
return metrics, true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ToPdfObject converts the pdfCIDFontType2 to a PDF representation.
|
2018-07-15 16:28:56 +10:00
|
|
|
|
func (font *pdfCIDFontType2) ToPdfObject() core.PdfObject {
|
2017-09-01 13:20:51 +00:00
|
|
|
|
if font.container == nil {
|
2018-07-15 16:28:56 +10:00
|
|
|
|
font.container = &core.PdfIndirectObject{}
|
2017-09-01 13:20:51 +00:00
|
|
|
|
}
|
2018-07-16 17:42:08 +10:00
|
|
|
|
d := font.baseFields().asPdfObjectDictionary("CIDFontType2")
|
2017-09-01 13:20:51 +00:00
|
|
|
|
font.container.PdfObject = d
|
|
|
|
|
|
|
|
|
|
if font.CIDSystemInfo != nil {
|
|
|
|
|
d.Set("CIDSystemInfo", font.CIDSystemInfo)
|
|
|
|
|
}
|
|
|
|
|
if font.DW != nil {
|
|
|
|
|
d.Set("DW", font.DW)
|
|
|
|
|
}
|
|
|
|
|
if font.DW2 != nil {
|
|
|
|
|
d.Set("DW2", font.DW2)
|
|
|
|
|
}
|
|
|
|
|
if font.W != nil {
|
|
|
|
|
d.Set("W", font.W)
|
|
|
|
|
}
|
|
|
|
|
if font.W2 != nil {
|
|
|
|
|
d.Set("W2", font.W2)
|
|
|
|
|
}
|
|
|
|
|
if font.CIDToGIDMap != nil {
|
|
|
|
|
d.Set("CIDToGIDMap", font.CIDToGIDMap)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return font.container
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-27 12:25:59 +10:00
|
|
|
|
// newPdfCIDFontType2FromPdfObject creates a pdfCIDFontType2 object from a dictionary (either direct
|
2018-07-15 16:28:56 +10:00
|
|
|
|
// or via indirect object). If a problem occurs with loading, an error is returned.
|
2018-07-16 17:42:08 +10:00
|
|
|
|
func newPdfCIDFontType2FromPdfObject(d *core.PdfObjectDictionary, base *fontCommon) (*pdfCIDFontType2, error) {
|
|
|
|
|
if base.subtype != "CIDFontType2" {
|
|
|
|
|
common.Log.Debug("ERROR: Font SubType != CIDFontType2. font=%s", base)
|
2018-07-15 16:28:56 +10:00
|
|
|
|
return nil, core.ErrRangeError
|
2017-09-01 13:20:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-07-16 17:42:08 +10:00
|
|
|
|
font := pdfCIDFontType2FromSkeleton(base)
|
2017-09-01 13:20:51 +00:00
|
|
|
|
|
|
|
|
|
// CIDSystemInfo.
|
2018-07-11 09:04:17 +10:00
|
|
|
|
obj := d.Get("CIDSystemInfo")
|
2017-09-01 13:20:51 +00:00
|
|
|
|
if obj == nil {
|
2018-07-16 17:42:08 +10:00
|
|
|
|
common.Log.Debug("ERROR: CIDSystemInfo (Required) missing. font=%s", base)
|
2017-09-01 13:20:51 +00:00
|
|
|
|
return nil, ErrRequiredAttributeMissing
|
|
|
|
|
}
|
|
|
|
|
font.CIDSystemInfo = obj
|
|
|
|
|
|
|
|
|
|
// Optional attributes.
|
|
|
|
|
font.DW = d.Get("DW")
|
|
|
|
|
font.W = d.Get("W")
|
|
|
|
|
font.DW2 = d.Get("DW2")
|
|
|
|
|
font.W2 = d.Get("W2")
|
|
|
|
|
font.CIDToGIDMap = d.Get("CIDToGIDMap")
|
|
|
|
|
|
|
|
|
|
return font, nil
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-27 12:25:59 +10:00
|
|
|
|
// NewCompositePdfFontFromTTFFile loads a composite font from a TTF font file. Composite fonts can
|
|
|
|
|
// be used to represent unicode fonts which can have multi-byte character codes, representing a wide
|
|
|
|
|
// range of values.
|
2017-09-01 13:20:51 +00:00
|
|
|
|
// It is represented by a Type0 Font with an underlying CIDFontType2 and an Identity-H encoding map.
|
|
|
|
|
// TODO: May be extended in the future to support a larger variety of CMaps and vertical fonts.
|
|
|
|
|
func NewCompositePdfFontFromTTFFile(filePath string) (*PdfFont, error) {
|
|
|
|
|
// Load the truetype font data.
|
|
|
|
|
ttf, err := fonts.TtfParse(filePath)
|
|
|
|
|
if err != nil {
|
2018-06-27 12:25:59 +10:00
|
|
|
|
common.Log.Debug("ERROR: while loading ttf font: %v", err)
|
2017-09-01 13:20:51 +00:00
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Prepare the inner descendant font (CIDFontType2).
|
2018-07-23 14:55:23 +10:00
|
|
|
|
cidfont := &pdfCIDFontType2{
|
|
|
|
|
fontCommon: fontCommon{
|
|
|
|
|
subtype: "CIDFontType2",
|
|
|
|
|
},
|
|
|
|
|
}
|
2017-09-01 13:20:51 +00:00
|
|
|
|
cidfont.ttfParser = &ttf
|
|
|
|
|
|
2018-07-15 16:28:56 +10:00
|
|
|
|
// 2-byte character codes ➞ runes
|
2018-07-02 13:49:06 +10:00
|
|
|
|
runes := make([]uint16, 0, len(ttf.Chars))
|
2018-06-27 12:25:59 +10:00
|
|
|
|
for r := range ttf.Chars {
|
|
|
|
|
runes = append(runes, r)
|
2017-09-01 13:20:51 +00:00
|
|
|
|
}
|
|
|
|
|
sort.Slice(runes, func(i, j int) bool {
|
|
|
|
|
return runes[i] < runes[j]
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
k := 1000.0 / float64(ttf.UnitsPerEm)
|
|
|
|
|
|
|
|
|
|
if len(ttf.Widths) <= 0 {
|
2018-06-27 12:25:59 +10:00
|
|
|
|
return nil, errors.New("ERROR: Missing required attribute (Widths)")
|
2017-09-01 13:20:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
missingWidth := k * float64(ttf.Widths[0])
|
|
|
|
|
|
2018-07-15 16:28:56 +10:00
|
|
|
|
// Construct a rune ➞ width map.
|
2017-09-01 13:20:51 +00:00
|
|
|
|
runeToWidthMap := map[uint16]int{}
|
|
|
|
|
gidToWidthMap := map[uint16]int{}
|
|
|
|
|
for _, r := range runes {
|
|
|
|
|
glyphIndex := ttf.Chars[r]
|
|
|
|
|
|
|
|
|
|
w := k * float64(ttf.Widths[glyphIndex])
|
|
|
|
|
runeToWidthMap[r] = int(w)
|
|
|
|
|
gidToWidthMap[glyphIndex] = int(w)
|
|
|
|
|
}
|
|
|
|
|
cidfont.runeToWidthMap = runeToWidthMap
|
|
|
|
|
cidfont.gidToWidthMap = gidToWidthMap
|
|
|
|
|
|
|
|
|
|
// Default width.
|
2018-07-15 16:28:56 +10:00
|
|
|
|
cidfont.DW = core.MakeInteger(int64(missingWidth))
|
2017-09-01 13:20:51 +00:00
|
|
|
|
|
|
|
|
|
// Construct W array. Stores character code to width mappings.
|
2018-07-15 16:28:56 +10:00
|
|
|
|
wArr := &core.PdfObjectArray{}
|
2017-09-01 13:20:51 +00:00
|
|
|
|
i := uint16(0)
|
|
|
|
|
for int(i) < len(runes) {
|
|
|
|
|
|
|
|
|
|
j := i + 1
|
|
|
|
|
for int(j) < len(runes) {
|
|
|
|
|
if runeToWidthMap[runes[i]] != runeToWidthMap[runes[j]] {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
j++
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The W maps from CID to width, here CID = GID.
|
|
|
|
|
gid1 := ttf.Chars[runes[i]]
|
|
|
|
|
gid2 := ttf.Chars[runes[j-1]]
|
|
|
|
|
|
2018-07-15 16:28:56 +10:00
|
|
|
|
wArr.Append(core.MakeInteger(int64(gid1)))
|
|
|
|
|
wArr.Append(core.MakeInteger(int64(gid2)))
|
|
|
|
|
wArr.Append(core.MakeInteger(int64(runeToWidthMap[runes[i]])))
|
2017-09-01 13:20:51 +00:00
|
|
|
|
|
|
|
|
|
i = j
|
|
|
|
|
}
|
2018-07-15 16:28:56 +10:00
|
|
|
|
cidfont.W = core.MakeIndirectObject(wArr)
|
2017-09-01 13:20:51 +00:00
|
|
|
|
|
|
|
|
|
// Use identity character id (CID) to glyph id (GID) mapping.
|
2018-07-15 16:28:56 +10:00
|
|
|
|
cidfont.CIDToGIDMap = core.MakeName("Identity")
|
2017-09-01 13:20:51 +00:00
|
|
|
|
|
2018-07-15 16:28:56 +10:00
|
|
|
|
d := core.MakeDict()
|
|
|
|
|
d.Set("Ordering", core.MakeString("Identity"))
|
|
|
|
|
d.Set("Registry", core.MakeString("Adobe"))
|
|
|
|
|
d.Set("Supplement", core.MakeInteger(0))
|
2017-09-01 13:20:51 +00:00
|
|
|
|
cidfont.CIDSystemInfo = d
|
|
|
|
|
|
|
|
|
|
// Make the font descriptor.
|
|
|
|
|
descriptor := &PdfFontDescriptor{}
|
2018-07-15 16:28:56 +10:00
|
|
|
|
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]))
|
2018-06-27 12:25:59 +10:00
|
|
|
|
|
|
|
|
|
// Embed the TrueType font program.
|
2017-09-01 13:20:51 +00:00
|
|
|
|
ttfBytes, err := ioutil.ReadFile(filePath)
|
|
|
|
|
if err != nil {
|
2018-06-27 12:25:59 +10:00
|
|
|
|
common.Log.Debug("ERROR: :Unable to read file contents: %v", err)
|
2017-09-01 13:20:51 +00:00
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-15 16:28:56 +10:00
|
|
|
|
stream, err := core.MakeStream(ttfBytes, core.NewFlateEncoder())
|
2017-09-01 13:20:51 +00:00
|
|
|
|
if err != nil {
|
2018-06-27 12:25:59 +10:00
|
|
|
|
common.Log.Debug("ERROR: Unable to make stream: %v", err)
|
2017-09-01 13:20:51 +00:00
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2018-07-15 16:28:56 +10:00
|
|
|
|
stream.PdfObjectDictionary.Set("Length1", core.MakeInteger(int64(len(ttfBytes))))
|
2017-09-01 13:20:51 +00:00
|
|
|
|
descriptor.FontFile2 = stream
|
|
|
|
|
|
|
|
|
|
if ttf.Bold {
|
2018-07-15 16:28:56 +10:00
|
|
|
|
descriptor.StemV = core.MakeInteger(120)
|
2017-09-01 13:20:51 +00:00
|
|
|
|
} else {
|
2018-07-15 16:28:56 +10:00
|
|
|
|
descriptor.StemV = core.MakeInteger(70)
|
2017-09-01 13:20:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-07-23 13:21:13 +10:00
|
|
|
|
// Flags
|
|
|
|
|
flags := fontFlagSymbolic // Symbolic.
|
2017-09-01 13:20:51 +00:00
|
|
|
|
if ttf.IsFixedPitch {
|
2018-07-23 13:21:13 +10:00
|
|
|
|
flags |= fontFlagFixedPitch
|
2017-09-01 13:20:51 +00:00
|
|
|
|
}
|
|
|
|
|
if ttf.ItalicAngle != 0 {
|
2018-07-23 13:21:13 +10:00
|
|
|
|
flags |= fontFlagItalic
|
2017-09-01 13:20:51 +00:00
|
|
|
|
}
|
2018-07-15 16:28:56 +10:00
|
|
|
|
descriptor.Flags = core.MakeInteger(int64(flags))
|
2017-09-01 13:20:51 +00:00
|
|
|
|
|
|
|
|
|
// Make root Type0 font.
|
2018-06-27 12:25:59 +10:00
|
|
|
|
type0 := pdfFontType0{
|
2018-07-23 14:55:23 +10:00
|
|
|
|
fontCommon: fontCommon{
|
|
|
|
|
subtype: "Type0",
|
|
|
|
|
basefont: ttf.PostScriptName,
|
|
|
|
|
fontDescriptor: descriptor,
|
|
|
|
|
},
|
|
|
|
|
DescendantFont: &PdfFont{
|
|
|
|
|
context: cidfont,
|
|
|
|
|
},
|
|
|
|
|
Encoding: core.MakeName("Identity-H"),
|
|
|
|
|
encoder: textencoding.NewTrueTypeFontEncoder(ttf.Chars),
|
2018-06-27 12:25:59 +10:00
|
|
|
|
}
|
2017-09-01 13:20:51 +00:00
|
|
|
|
|
|
|
|
|
// Build Font.
|
2018-06-27 12:25:59 +10:00
|
|
|
|
font := PdfFont{
|
2018-07-16 17:42:08 +10:00
|
|
|
|
context: &type0,
|
2018-06-27 12:25:59 +10:00
|
|
|
|
}
|
2017-09-01 13:20:51 +00:00
|
|
|
|
|
2018-06-27 12:25:59 +10:00
|
|
|
|
return &font, nil
|
2017-09-01 13:20:51 +00:00
|
|
|
|
}
|