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 (
|
2019-01-02 17:17:58 +02:00
|
|
|
|
"bytes"
|
2017-09-01 13:20:51 +00:00
|
|
|
|
"errors"
|
2018-11-08 15:20:12 +11:00
|
|
|
|
"fmt"
|
2017-09-01 13:20:51 +00:00
|
|
|
|
"io/ioutil"
|
2018-11-29 02:56:26 +02:00
|
|
|
|
"sort"
|
2017-09-01 13:20:51 +00:00
|
|
|
|
|
2019-05-16 23:08:40 +03:00
|
|
|
|
"github.com/unidoc/unipdf/v3/common"
|
2019-05-16 23:44:51 +03:00
|
|
|
|
"github.com/unidoc/unipdf/v3/core"
|
2019-03-09 18:02:57 +00:00
|
|
|
|
|
2019-05-16 23:44:51 +03:00
|
|
|
|
"github.com/unidoc/unipdf/v3/internal/textencoding"
|
|
|
|
|
"github.com/unidoc/unipdf/v3/model/internal/fonts"
|
2017-09-01 13:20:51 +00:00
|
|
|
|
)
|
|
|
|
|
|
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
|
|
|
|
|
....
|
|
|
|
|
*/
|
|
|
|
|
|
2018-12-19 13:54:45 +05:00
|
|
|
|
// pdfFontType0 implements pdfFont
|
|
|
|
|
var _ pdfFont = (*pdfFontType0)(nil)
|
|
|
|
|
|
2018-06-27 12:25:59 +10:00
|
|
|
|
// 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-23 14:55:23 +10:00
|
|
|
|
fontCommon
|
2018-07-25 16:14:19 +10:00
|
|
|
|
container *core.PdfIndirectObject
|
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-12-19 13:54:45 +05:00
|
|
|
|
func (font *pdfFontType0) getFontDescriptor() *PdfFontDescriptor {
|
|
|
|
|
return font.fontDescriptor
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-28 01:38:48 +02:00
|
|
|
|
// GetRuneMetrics returns the character metrics for the specified rune.
|
|
|
|
|
// A bool flag is returned to indicate whether or not the entry was found.
|
|
|
|
|
func (font pdfFontType0) GetRuneMetrics(r rune) (fonts.CharMetrics, bool) {
|
2017-09-01 13:20:51 +00:00
|
|
|
|
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
|
|
|
|
}
|
2018-12-28 01:38:48 +02:00
|
|
|
|
return font.DescendantFont.GetRuneMetrics(r)
|
2017-09-01 13:20:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-08 15:20:12 +11:00
|
|
|
|
// GetCharMetrics returns the char metrics for character code `code`.
|
2018-12-07 18:43:24 +02:00
|
|
|
|
func (font pdfFontType0) GetCharMetrics(code textencoding.CharCode) (fonts.CharMetrics, bool) {
|
2018-10-30 21:55:30 +11:00
|
|
|
|
if font.DescendantFont == nil {
|
|
|
|
|
common.Log.Debug("ERROR: No descendant. font=%s", font)
|
|
|
|
|
return fonts.CharMetrics{}, false
|
|
|
|
|
}
|
|
|
|
|
return font.DescendantFont.GetCharMetrics(code)
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-01 13:20:51 +00:00
|
|
|
|
// Encoder returns the font's text encoder.
|
|
|
|
|
func (font pdfFontType0) Encoder() textencoding.TextEncoder {
|
|
|
|
|
return font.encoder
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-25 14:29:39 +10:00
|
|
|
|
// ToPdfObject converts the font 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-09-03 10:48:31 +10:00
|
|
|
|
if font.Encoding != nil {
|
|
|
|
|
d.Set("Encoding", font.Encoding)
|
|
|
|
|
} else if font.encoder != nil {
|
2018-06-29 18:09:44 +10:00
|
|
|
|
d.Set("Encoding", font.encoder.ToPdfObject())
|
2017-09-01 13:20:51 +00:00
|
|
|
|
}
|
2018-09-03 10:48:31 +10:00
|
|
|
|
|
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-25 13:19:09 +10:00
|
|
|
|
arr, ok := core.GetArray(d.Get("DescendantFonts"))
|
2018-07-21 21:20:39 +10:00
|
|
|
|
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-07-25 13:19:09 +10:00
|
|
|
|
if arr.Len() != 1 {
|
|
|
|
|
common.Log.Debug("ERROR: Array length != 1 (%d)", arr.Len())
|
2018-07-15 16:28:56 +10:00
|
|
|
|
return nil, core.ErrRangeError
|
2018-06-27 12:25:59 +10:00
|
|
|
|
}
|
2018-07-25 13:19:09 +10:00
|
|
|
|
df, err := newPdfFontFromPdfObject(arr.Get(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-12-19 13:54:45 +05:00
|
|
|
|
// pdfCIDFontType0 implements pdfFont
|
|
|
|
|
var _ pdfFont = (*pdfCIDFontType0)(nil)
|
|
|
|
|
|
2018-06-27 12:25:59 +10:00
|
|
|
|
// pdfCIDFontType0 represents a CIDFont Type0 font dictionary.
|
|
|
|
|
type pdfCIDFontType0 struct {
|
2018-07-23 14:55:23 +10:00
|
|
|
|
fontCommon
|
2018-07-25 16:14:19 +10:00
|
|
|
|
container *core.PdfIndirectObject
|
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-09-17 17:57:52 +10:00
|
|
|
|
CIDSystemInfo *core.PdfObjectDictionary // (Required) Dictionary that defines the character
|
|
|
|
|
// collection of the CIDFont. See Table 116.
|
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
|
|
|
|
}
|
|
|
|
|
|
2018-12-19 13:54:45 +05:00
|
|
|
|
func (font *pdfCIDFontType0) getFontDescriptor() *PdfFontDescriptor {
|
|
|
|
|
return font.fontDescriptor
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-27 12:25:59 +10:00
|
|
|
|
// Encoder returns the font's text encoder.
|
|
|
|
|
func (font pdfCIDFontType0) Encoder() textencoding.TextEncoder {
|
|
|
|
|
return font.encoder
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-28 01:38:48 +02:00
|
|
|
|
// GetRuneMetrics returns the character metrics for the specified rune.
|
|
|
|
|
// A bool flag is returned to indicate whether or not the entry was found.
|
|
|
|
|
func (font pdfCIDFontType0) GetRuneMetrics(r rune) (fonts.CharMetrics, bool) {
|
2018-06-29 16:36:06 +10:00
|
|
|
|
return fonts.CharMetrics{}, true
|
2018-06-27 12:25:59 +10:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-08 15:20:12 +11:00
|
|
|
|
// GetCharMetrics returns the char metrics for character code `code`.
|
2018-12-07 18:43:24 +02:00
|
|
|
|
func (font pdfCIDFontType0) GetCharMetrics(code textencoding.CharCode) (fonts.CharMetrics, bool) {
|
2018-10-30 21:55:30 +11: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-25 14:29:39 +10:00
|
|
|
|
obj, ok := core.GetDict(d.Get("CIDSystemInfo"))
|
|
|
|
|
if !ok {
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-19 13:54:45 +05:00
|
|
|
|
// pdfCIDFontType2 implements pdfFont
|
|
|
|
|
var _ pdfFont = (*pdfCIDFontType2)(nil)
|
|
|
|
|
|
2017-09-01 13:20:51 +00:00
|
|
|
|
// pdfCIDFontType2 represents a CIDFont Type2 font dictionary.
|
|
|
|
|
type pdfCIDFontType2 struct {
|
2018-07-23 14:55:23 +10:00
|
|
|
|
fontCommon
|
2018-07-25 16:14:19 +10:00
|
|
|
|
container *core.PdfIndirectObject
|
2018-07-16 17:42:08 +10:00
|
|
|
|
|
|
|
|
|
// These fields are specific to Type 0 fonts.
|
2018-11-29 06:01:04 +02:00
|
|
|
|
encoder textencoding.TextEncoder
|
2017-09-01 13:20:51 +00:00
|
|
|
|
|
2018-07-25 14:29:39 +10:00
|
|
|
|
CIDSystemInfo *core.PdfObjectDictionary
|
2018-07-15 16:28:56 +10:00
|
|
|
|
DW core.PdfObject
|
|
|
|
|
W core.PdfObject
|
|
|
|
|
DW2 core.PdfObject
|
|
|
|
|
W2 core.PdfObject
|
|
|
|
|
CIDToGIDMap core.PdfObject
|
2017-09-01 13:20:51 +00:00
|
|
|
|
|
2018-12-07 18:43:24 +02:00
|
|
|
|
widths map[textencoding.CharCode]float64
|
2018-11-08 15:20:12 +11:00
|
|
|
|
defaultWidth float64
|
|
|
|
|
|
2017-09-01 13:20:51 +00:00
|
|
|
|
// Mapping between unicode runes to widths.
|
2018-12-28 16:48:38 +02:00
|
|
|
|
// TODO(dennwc): it is used only in GetGlyphCharMetrics
|
|
|
|
|
// we can precompute metrics and drop it
|
2018-11-29 03:37:12 +02:00
|
|
|
|
runeToWidthMap map[rune]int
|
2017-09-01 13:20:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
2018-12-19 13:54:45 +05:00
|
|
|
|
func (font *pdfCIDFontType2) getFontDescriptor() *PdfFontDescriptor {
|
|
|
|
|
return font.fontDescriptor
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-01 13:20:51 +00:00
|
|
|
|
// Encoder returns the font's text encoder.
|
|
|
|
|
func (font pdfCIDFontType2) Encoder() textencoding.TextEncoder {
|
|
|
|
|
return font.encoder
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-28 01:38:48 +02:00
|
|
|
|
// GetRuneMetrics returns the character metrics for the specified rune.
|
|
|
|
|
// A bool flag is returned to indicate whether or not the entry was found.
|
|
|
|
|
func (font pdfCIDFontType2) GetRuneMetrics(r rune) (fonts.CharMetrics, bool) {
|
2018-11-29 03:37:12 +02:00
|
|
|
|
w, found := font.runeToWidthMap[r]
|
2017-09-01 13:20:51 +00:00
|
|
|
|
if !found {
|
2018-09-03 10:48:31 +10:00
|
|
|
|
dw, ok := core.GetInt(font.DW)
|
|
|
|
|
if !ok {
|
2018-12-28 01:38:48 +02:00
|
|
|
|
return fonts.CharMetrics{}, false
|
2018-09-03 10:48:31 +10:00
|
|
|
|
}
|
|
|
|
|
w = int(*dw)
|
2017-09-01 13:20:51 +00:00
|
|
|
|
}
|
2018-12-28 01:38:48 +02:00
|
|
|
|
return fonts.CharMetrics{Wx: float64(w)}, true
|
2017-09-01 13:20:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-08 15:20:12 +11:00
|
|
|
|
// GetCharMetrics returns the char metrics for character code `code`.
|
2018-12-07 18:43:24 +02:00
|
|
|
|
func (font pdfCIDFontType2) GetCharMetrics(code textencoding.CharCode) (fonts.CharMetrics, bool) {
|
|
|
|
|
if w, ok := font.widths[code]; ok {
|
2018-11-08 15:20:12 +11:00
|
|
|
|
return fonts.CharMetrics{Wx: float64(w)}, true
|
|
|
|
|
}
|
2018-12-07 10:32:49 +00:00
|
|
|
|
// TODO(peterwilliams97): The remainder of this function is pure guesswork. Explain it.
|
2018-12-07 18:43:24 +02:00
|
|
|
|
// FIXME(gunnsth): Appears that we are assuming a code <-> rune identity mapping.
|
|
|
|
|
r := rune(code)
|
|
|
|
|
w, ok := font.runeToWidthMap[r]
|
2018-11-20 15:49:28 +11:00
|
|
|
|
if !ok {
|
|
|
|
|
w = int(font.defaultWidth)
|
2018-11-08 15:20:12 +11:00
|
|
|
|
}
|
2018-11-20 15:49:28 +11:00
|
|
|
|
return fonts.CharMetrics{Wx: float64(w)}, true
|
2018-10-30 21:55:30 +11:00
|
|
|
|
}
|
|
|
|
|
|
2017-09-01 13:20:51 +00:00
|
|
|
|
// 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-25 14:29:39 +10:00
|
|
|
|
obj, ok := core.GetDict(d.Get("CIDSystemInfo"))
|
|
|
|
|
if !ok {
|
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")
|
|
|
|
|
|
2018-11-08 15:20:12 +11:00
|
|
|
|
if arr2, ok := core.GetArray(font.W); ok {
|
2018-12-07 18:43:24 +02:00
|
|
|
|
font.widths = make(map[textencoding.CharCode]float64)
|
2018-11-08 15:20:12 +11:00
|
|
|
|
for i := 0; i < arr2.Len()-1; i++ {
|
|
|
|
|
obj0 := (*arr2).Get(i)
|
|
|
|
|
n, ok0 := core.GetIntVal(obj0)
|
|
|
|
|
if !ok0 {
|
|
|
|
|
return nil, fmt.Errorf("Bad font W obj0: i=%d %#v", i, obj0)
|
|
|
|
|
}
|
|
|
|
|
i++
|
|
|
|
|
if i > arr2.Len()-1 {
|
|
|
|
|
return nil, fmt.Errorf("Bad font W array: arr2=%+v", arr2)
|
|
|
|
|
}
|
|
|
|
|
obj1 := (*arr2).Get(i)
|
|
|
|
|
switch obj1.(type) {
|
|
|
|
|
case *core.PdfObjectArray:
|
|
|
|
|
arr, _ := core.GetArray(obj1)
|
|
|
|
|
if widths, err := arr.ToFloat64Array(); err == nil {
|
|
|
|
|
for j := 0; j < len(widths); j++ {
|
2018-12-07 18:43:24 +02:00
|
|
|
|
font.widths[textencoding.CharCode(n+j)] = widths[j]
|
2018-11-08 15:20:12 +11:00
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return nil, fmt.Errorf("Bad font W array obj1: i=%d %#v", i, obj1)
|
|
|
|
|
}
|
|
|
|
|
case *core.PdfObjectInteger:
|
|
|
|
|
n1, ok1 := core.GetIntVal(obj1)
|
|
|
|
|
if !ok1 {
|
|
|
|
|
return nil, fmt.Errorf("Bad font W int obj1: i=%d %#v", i, obj1)
|
|
|
|
|
}
|
|
|
|
|
i++
|
|
|
|
|
if i > arr2.Len()-1 {
|
|
|
|
|
return nil, fmt.Errorf("Bad font W array: arr2=%+v", arr2)
|
|
|
|
|
}
|
|
|
|
|
obj2 := (*arr2).Get(i)
|
|
|
|
|
v, err := core.GetNumberAsFloat(obj2)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("Bad font W int obj2: i=%d %#v", i, obj2)
|
|
|
|
|
}
|
|
|
|
|
for j := n; j <= n1; j++ {
|
2018-12-07 18:43:24 +02:00
|
|
|
|
font.widths[textencoding.CharCode(j)] = v
|
2018-11-08 15:20:12 +11:00
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
return nil, fmt.Errorf("Bad font W obj1 type: i=%d %#v", i, obj1)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if defaultWidth, err := core.GetNumberAsFloat(font.DW); err == nil {
|
|
|
|
|
font.defaultWidth = defaultWidth
|
2018-11-20 15:49:28 +11:00
|
|
|
|
} else {
|
|
|
|
|
font.defaultWidth = 1000.0
|
2018-11-08 15:20:12 +11:00
|
|
|
|
}
|
|
|
|
|
|
2017-09-01 13:20:51 +00:00
|
|
|
|
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.
|
2019-01-02 17:17:58 +02:00
|
|
|
|
ttfBytes, err := ioutil.ReadFile(filePath)
|
|
|
|
|
if err != nil {
|
|
|
|
|
common.Log.Debug("ERROR: while reading ttf font: %v", err)
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
ttf, err := fonts.TtfParse(bytes.NewReader(ttfBytes))
|
2017-09-01 13:20:51 +00:00
|
|
|
|
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",
|
|
|
|
|
},
|
2018-11-29 05:49:51 +02:00
|
|
|
|
|
|
|
|
|
// Use identity character id (CID) to glyph id (GID) mapping.
|
|
|
|
|
// Code below relies on the fact that identity mapping is used.
|
|
|
|
|
CIDToGIDMap: core.MakeName("Identity"),
|
2018-07-23 14:55:23 +10:00
|
|
|
|
}
|
2017-09-01 13:20:51 +00:00
|
|
|
|
|
2018-07-15 16:28:56 +10:00
|
|
|
|
// 2-byte character codes ➞ runes
|
2018-11-29 03:37:12 +02:00
|
|
|
|
runes := make([]rune, 0, len(ttf.Chars))
|
2018-10-13 22:51:22 +03:00
|
|
|
|
for r := range ttf.Chars {
|
2018-11-29 03:37:12 +02:00
|
|
|
|
runes = append(runes, rune(r))
|
2017-09-01 13:20:51 +00:00
|
|
|
|
}
|
2018-11-29 02:56:26 +02:00
|
|
|
|
// make sure runes are sorted so PDF output is stable
|
|
|
|
|
sort.Slice(runes, func(i, j int) bool {
|
|
|
|
|
return runes[i] < runes[j]
|
|
|
|
|
})
|
2017-09-01 13:20:51 +00:00
|
|
|
|
|
|
|
|
|
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.
|
2018-11-29 03:37:12 +02:00
|
|
|
|
runeToWidthMap := make(map[rune]int)
|
2017-09-01 13:20:51 +00:00
|
|
|
|
for _, r := range runes {
|
2018-11-29 04:02:20 +02:00
|
|
|
|
gid := ttf.Chars[r]
|
2017-09-01 13:20:51 +00:00
|
|
|
|
|
2018-11-29 04:02:20 +02:00
|
|
|
|
w := k * float64(ttf.Widths[gid])
|
2017-09-01 13:20:51 +00:00
|
|
|
|
runeToWidthMap[r] = int(w)
|
|
|
|
|
}
|
|
|
|
|
cidfont.runeToWidthMap = runeToWidthMap
|
|
|
|
|
|
|
|
|
|
// 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-12-07 21:54:24 +02:00
|
|
|
|
wArr := makeCIDWidthArr(runes, runeToWidthMap, ttf.Chars)
|
2018-07-15 16:28:56 +10:00
|
|
|
|
cidfont.W = core.MakeIndirectObject(wArr)
|
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.
|
2018-10-13 22:08:31 +03:00
|
|
|
|
descriptor := &PdfFontDescriptor{
|
|
|
|
|
FontName: core.MakeName(ttf.PostScriptName),
|
|
|
|
|
Ascent: core.MakeFloat(k * float64(ttf.TypoAscender)),
|
|
|
|
|
Descent: core.MakeFloat(k * float64(ttf.TypoDescender)),
|
|
|
|
|
CapHeight: core.MakeFloat(k * float64(ttf.CapHeight)),
|
|
|
|
|
FontBBox: core.MakeArrayFromFloats([]float64{
|
|
|
|
|
k * float64(ttf.Xmin),
|
|
|
|
|
k * float64(ttf.Ymin),
|
|
|
|
|
k * float64(ttf.Xmax),
|
|
|
|
|
k * float64(ttf.Ymax),
|
|
|
|
|
}),
|
|
|
|
|
ItalicAngle: core.MakeFloat(float64(ttf.ItalicAngle)),
|
|
|
|
|
MissingWidth: core.MakeFloat(k * float64(ttf.Widths[0])),
|
|
|
|
|
}
|
2018-06-27 12:25:59 +10:00
|
|
|
|
|
|
|
|
|
// Embed the TrueType font program.
|
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
|
|
|
|
|
2018-08-29 21:30:05 +10:00
|
|
|
|
cidfont.basefont = ttf.PostScriptName
|
|
|
|
|
cidfont.fontDescriptor = descriptor
|
|
|
|
|
|
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{
|
2018-08-29 21:30:05 +10:00
|
|
|
|
subtype: "Type0",
|
|
|
|
|
basefont: ttf.PostScriptName,
|
2018-07-23 14:55:23 +10:00
|
|
|
|
},
|
|
|
|
|
DescendantFont: &PdfFont{
|
|
|
|
|
context: cidfont,
|
|
|
|
|
},
|
|
|
|
|
Encoding: core.MakeName("Identity-H"),
|
2018-11-29 05:18:39 +02:00
|
|
|
|
encoder: ttf.NewEncoder(),
|
2018-06-27 12:25:59 +10:00
|
|
|
|
}
|
2017-09-01 13:20:51 +00:00
|
|
|
|
|
2018-09-17 17:57:52 +10:00
|
|
|
|
type0.toUnicodeCmap = ttf.MakeToUnicode()
|
|
|
|
|
|
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
|
|
|
|
}
|
2018-12-07 21:54:24 +02:00
|
|
|
|
|
|
|
|
|
func makeCIDWidthArr(runes []rune, widths map[rune]int, gids map[rune]fonts.GID) *core.PdfObjectArray {
|
|
|
|
|
// Construct W array. Stores character code to width mappings.
|
|
|
|
|
arr := &core.PdfObjectArray{}
|
|
|
|
|
|
|
|
|
|
// 9.7.4.3 Glyph metrics in CIDFonts
|
|
|
|
|
|
|
|
|
|
// In the first format, c shall be an integer specifying a starting CID value; it shall be followed by an array of
|
|
|
|
|
// n numbers that shall specify the widths for n consecutive CIDs, starting with c.
|
|
|
|
|
// The second format shall define the same width, w, as a number, for all CIDs in the range c_first to c_last.
|
|
|
|
|
|
|
|
|
|
// We always use the second format.
|
|
|
|
|
|
|
|
|
|
// TODO(dennwc): this can be done on GIDs instead of runes
|
|
|
|
|
for i := 0; i < len(runes); {
|
|
|
|
|
w := widths[runes[i]]
|
|
|
|
|
|
|
|
|
|
li := i
|
|
|
|
|
for j := i + 1; j < len(runes); j++ {
|
|
|
|
|
w2 := widths[runes[j]]
|
|
|
|
|
if w == w2 {
|
|
|
|
|
li = j
|
|
|
|
|
} else {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The W maps from CID to width, here CID = GID.
|
|
|
|
|
gid1 := gids[runes[i]]
|
|
|
|
|
gid2 := gids[runes[li]]
|
|
|
|
|
|
|
|
|
|
arr.Append(core.MakeInteger(int64(gid1)))
|
|
|
|
|
arr.Append(core.MakeInteger(int64(gid2)))
|
|
|
|
|
arr.Append(core.MakeInteger(int64(w)))
|
|
|
|
|
|
|
|
|
|
i = li + 1
|
|
|
|
|
}
|
|
|
|
|
return arr
|
|
|
|
|
}
|