mirror of
https://github.com/unidoc/unipdf.git
synced 2025-04-29 13:48:54 +08:00
Merge pull request #218 from gunnsth/release/v3.3.1
Prepare unipdf release v3.3.1
This commit is contained in:
commit
d0f9c139ad
@ -10,13 +10,13 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const releaseYear = 2019
|
||||
const releaseMonth = 9
|
||||
const releaseDay = 7
|
||||
const releaseHour = 13
|
||||
const releaseMin = 10
|
||||
const releaseYear = 2020
|
||||
const releaseMonth = 1
|
||||
const releaseDay = 4
|
||||
const releaseHour = 19
|
||||
const releaseMin = 5
|
||||
|
||||
// Version holds version information, when bumping this make sure to bump the released at stamp also.
|
||||
const Version = "3.2.0"
|
||||
const Version = "3.3.1"
|
||||
|
||||
var ReleasedAt = time.Date(releaseYear, releaseMonth, releaseDay, releaseHour, releaseMin, 0, 0, time.UTC)
|
||||
|
@ -88,7 +88,7 @@ func newFlateEncoderFromInlineImage(inlineImage *ContentStreamInlineImage, decod
|
||||
if decodeParams == nil {
|
||||
obj := inlineImage.DecodeParms
|
||||
if obj != nil {
|
||||
dp, isDict := obj.(*core.PdfObjectDictionary)
|
||||
dp, isDict := core.GetDict(obj)
|
||||
if !isDict {
|
||||
common.Log.Debug("Error: DecodeParms not a dictionary (%T)", obj)
|
||||
return nil, fmt.Errorf("invalid DecodeParms")
|
||||
@ -163,7 +163,7 @@ func newLZWEncoderFromInlineImage(inlineImage *ContentStreamInlineImage, decodeP
|
||||
// If decodeParams not provided, see if we can get from the inline image directly.
|
||||
if decodeParams == nil {
|
||||
if inlineImage.DecodeParms != nil {
|
||||
dp, isDict := inlineImage.DecodeParms.(*core.PdfObjectDictionary)
|
||||
dp, isDict := core.GetDict(inlineImage.DecodeParms)
|
||||
if !isDict {
|
||||
common.Log.Debug("Error: DecodeParms not a dictionary (%T)", inlineImage.DecodeParms)
|
||||
return nil, fmt.Errorf("invalid DecodeParms")
|
||||
|
@ -316,7 +316,7 @@ func (csp *ContentStreamParser) ParseInlineImage() (*ContentStreamInlineImage, e
|
||||
|
||||
if !isOperand {
|
||||
// Not an operand.. Read key value properties..
|
||||
param, ok := obj.(*core.PdfObjectName)
|
||||
param, ok := core.GetName(obj)
|
||||
if !ok {
|
||||
common.Log.Debug("Invalid inline image property (expecting name) - %T", obj)
|
||||
return nil, fmt.Errorf("invalid inline image property (expecting name) - %T", obj)
|
||||
|
@ -7,6 +7,7 @@ package contentstream
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/unidoc/unipdf/v3/common"
|
||||
"github.com/unidoc/unipdf/v3/core"
|
||||
@ -141,7 +142,7 @@ func (proc *ContentStreamProcessor) getColorspace(name string, resources *model.
|
||||
|
||||
// Otherwise unsupported.
|
||||
common.Log.Debug("Unknown colorspace requested: %s", name)
|
||||
return nil, errors.New("unsupported colorspace")
|
||||
return nil, fmt.Errorf("unsupported colorspace: %s", name)
|
||||
}
|
||||
|
||||
// Get initial color for a given colorspace.
|
||||
|
@ -443,7 +443,7 @@ func (enc *FlateEncoder) EncodeBytes(data []byte) ([]byte, error) {
|
||||
rowLength := int(enc.Columns)
|
||||
rows := len(data) / rowLength
|
||||
if len(data)%rowLength != 0 {
|
||||
common.Log.Error("Invalid column length")
|
||||
common.Log.Error("Invalid row length")
|
||||
return nil, errors.New("invalid row length")
|
||||
}
|
||||
|
||||
@ -590,7 +590,7 @@ func newLZWEncoderFromStream(streamObj *PdfObjectStream, decodeParams *PdfObject
|
||||
|
||||
// If decodeParams not provided, see if we can get from the stream.
|
||||
if decodeParams == nil {
|
||||
obj := encDict.Get("DecodeParms")
|
||||
obj := TraceToDirectObject(encDict.Get("DecodeParms"))
|
||||
if obj != nil {
|
||||
if dp, isDict := obj.(*PdfObjectDictionary); isDict {
|
||||
decodeParams = dp
|
||||
@ -1751,7 +1751,7 @@ func newCCITTFaxEncoderFromStream(streamObj *PdfObjectStream, decodeParams *PdfO
|
||||
|
||||
// If decodeParams not provided, see if we can get from the stream.
|
||||
if decodeParams == nil {
|
||||
obj := encDict.Get("DecodeParms")
|
||||
obj := TraceToDirectObject(encDict.Get("DecodeParms"))
|
||||
if obj != nil {
|
||||
switch t := obj.(type) {
|
||||
case *PdfObjectDictionary:
|
||||
@ -2061,7 +2061,7 @@ func newJBIG2EncoderFromStream(streamObj *PdfObjectStream, decodeParams *PdfObje
|
||||
|
||||
if decodeParams != nil {
|
||||
if globals := decodeParams.Get("JBIG2Globals"); globals != nil {
|
||||
globalsStream, ok := globals.(*PdfObjectStream)
|
||||
globalsStream, ok := GetStream(globals)
|
||||
if !ok {
|
||||
err := errors.New("the Globals stream should be an Object Stream")
|
||||
common.Log.Debug("ERROR: %s", err.Error())
|
||||
|
@ -22,52 +22,26 @@ func IsFloatDigit(c byte) bool {
|
||||
|
||||
// IsDecimalDigit checks if the character is a part of a decimal number string.
|
||||
func IsDecimalDigit(c byte) bool {
|
||||
if c >= '0' && c <= '9' {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
return '0' <= c && c <= '9'
|
||||
}
|
||||
|
||||
// IsOctalDigit checks if a character can be part of an octal digit string.
|
||||
func IsOctalDigit(c byte) bool {
|
||||
if c >= '0' && c <= '7' {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
return '0' <= c && c <= '7'
|
||||
}
|
||||
|
||||
// IsPrintable checks if a character is printable.
|
||||
// Regular characters that are outside the range EXCLAMATION MARK(21h)
|
||||
// (!) to TILDE (7Eh) (~) should be written using the hexadecimal notation.
|
||||
func IsPrintable(char byte) bool {
|
||||
if char < 0x21 || char > 0x7E {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
func IsPrintable(c byte) bool {
|
||||
return 0x21 <= c && c <= 0x7E
|
||||
}
|
||||
|
||||
// IsDelimiter checks if a character represents a delimiter.
|
||||
func IsDelimiter(char byte) bool {
|
||||
if char == '(' || char == ')' {
|
||||
return true
|
||||
}
|
||||
if char == '<' || char == '>' {
|
||||
return true
|
||||
}
|
||||
if char == '[' || char == ']' {
|
||||
return true
|
||||
}
|
||||
if char == '{' || char == '}' {
|
||||
return true
|
||||
}
|
||||
if char == '/' {
|
||||
return true
|
||||
}
|
||||
if char == '%' {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
func IsDelimiter(c byte) bool {
|
||||
return c == '(' || c == ')' ||
|
||||
c == '<' || c == '>' ||
|
||||
c == '[' || c == ']' ||
|
||||
c == '{' || c == '}' ||
|
||||
c == '/' || c == '%'
|
||||
}
|
||||
|
@ -346,9 +346,25 @@ func (c *Creator) Context() DrawContext {
|
||||
return c.context
|
||||
}
|
||||
|
||||
// Call before writing out. Takes care of adding headers and footers, as well
|
||||
// as generating front Page and table of contents.
|
||||
func (c *Creator) finalize() error {
|
||||
// Finalize renders all blocks to the creator pages. In addition, it takes care
|
||||
// of adding headers and footers, as well as generating the front page,
|
||||
// table of contents and outlines.
|
||||
// Finalize is automatically called before writing the document out. Calling the
|
||||
// method manually can be useful when adding external pages to the creator,
|
||||
// using the AddPage method, as it renders all creator blocks to the added
|
||||
// pages, without having to write the document out.
|
||||
// NOTE: TOC and outlines are generated only if the AddTOC and AddOutlines
|
||||
// fields of the creator are set to true (enabled by default). Furthermore, TOCs
|
||||
// and outlines without content are skipped. TOC and outline content is
|
||||
// added automatically when using the chapter component. TOCs and outlines can
|
||||
// also be set externally, using the SetTOC and SetOutlineTree methods.
|
||||
// Finalize should only be called once, after all draw calls have taken place,
|
||||
// as it will return immediately if the creator instance has been finalized.
|
||||
func (c *Creator) Finalize() error {
|
||||
if c.finalized {
|
||||
return nil
|
||||
}
|
||||
|
||||
totPages := len(c.pages)
|
||||
|
||||
// Estimate number of additional generated pages and update TOC.
|
||||
@ -563,8 +579,12 @@ func (c *Creator) MoveDown(dy float64) {
|
||||
c.context.Y += dy
|
||||
}
|
||||
|
||||
// Draw draws the Drawable widget to the document. This can span over 1 or more pages. Additional
|
||||
// pages are added if the contents go over the current Page.
|
||||
// Draw processes the specified Drawable widget and generates blocks that can
|
||||
// be rendered to the output document. The generated blocks can span over one
|
||||
// or more pages. Additional pages are added if the contents go over the current
|
||||
// page. Each generated block is assigned to the creator page it will be
|
||||
// rendered to. In order to render the generated blocks to the creator pages,
|
||||
// call Finalize, Write or WriteToFile.
|
||||
func (c *Creator) Draw(d Drawable) error {
|
||||
if c.getActivePage() == nil {
|
||||
// Add a new Page if none added already.
|
||||
@ -604,10 +624,8 @@ func (c *Creator) Draw(d Drawable) error {
|
||||
|
||||
// Write output of creator to io.Writer interface.
|
||||
func (c *Creator) Write(ws io.Writer) error {
|
||||
if !c.finalized {
|
||||
if err := c.finalize(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Finalize(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pdfWriter := model.NewPdfWriter()
|
||||
|
@ -822,7 +822,7 @@ func TestSubchaptersSimple(t *testing.T) {
|
||||
c.AddTOC = true
|
||||
|
||||
lineStyle := c.NewTextStyle()
|
||||
lineStyle.Font = model.NewStandard14FontMustCompile(model.HelveticaBoldName)
|
||||
lineStyle.Font = model.NewStandard14FontMustCompile(model.HelveticaName)
|
||||
|
||||
toc := c.TOC()
|
||||
toc.SetLineStyle(lineStyle)
|
||||
@ -898,9 +898,6 @@ func TestSubchaptersSimple(t *testing.T) {
|
||||
toc.SetHeading("Table of Contents", style)
|
||||
|
||||
// Set style of TOC lines just before render.
|
||||
lineStyle := c.NewTextStyle()
|
||||
lineStyle.FontSize = 14
|
||||
|
||||
helveticaBold := model.NewStandard14FontMustCompile(model.HelveticaBoldName)
|
||||
|
||||
lines := toc.Lines()
|
||||
@ -923,7 +920,6 @@ func TestSubchapters(t *testing.T) {
|
||||
|
||||
lineStyle := c.NewTextStyle()
|
||||
lineStyle.Font = model.NewStandard14FontMustCompile(model.HelveticaName)
|
||||
lineStyle.FontSize = 14
|
||||
lineStyle.Color = ColorRGBFromArithmetic(0.5, 0.5, 0.5)
|
||||
|
||||
toc := c.TOC()
|
||||
|
@ -155,7 +155,12 @@ func (tl *TOCLine) SetLink(page int64, x, y float64) {
|
||||
tl.linkY = y
|
||||
tl.linkPage = page
|
||||
|
||||
tl.SetStyle(tl.sp.defaultLinkStyle)
|
||||
// Set the color of the line components to the default link color.
|
||||
linkColor := tl.sp.defaultLinkStyle.Color
|
||||
tl.Number.Style.Color = linkColor
|
||||
tl.Title.Style.Color = linkColor
|
||||
tl.Separator.Style.Color = linkColor
|
||||
tl.Page.Style.Color = linkColor
|
||||
}
|
||||
|
||||
// getLineLink returns a new annotation if the line has a link set.
|
||||
|
@ -61,7 +61,8 @@ func (e *Extractor) extractPageText(contents string, resources *model.PdfPageRes
|
||||
pageText := &PageText{}
|
||||
state := newTextState()
|
||||
fontStack := fontStacker{}
|
||||
var to *textObject
|
||||
to := newTextObject(e, resources, contentstream.GraphicsState{}, &state, &fontStack)
|
||||
var inTextObj bool
|
||||
|
||||
cstreamParser := contentstream.NewContentStreamParser(contents)
|
||||
operations, err := cstreamParser.Parse()
|
||||
@ -102,16 +103,31 @@ func (e *Extractor) extractPageText(contents string, resources *model.PdfPageRes
|
||||
state.tfont = fontStack.pop()
|
||||
}
|
||||
case "BT": // Begin text
|
||||
// Begin a text object, initializing the text matrix, Tm, and the text line matrix,
|
||||
// Tlm, to the identity matrix. Text objects shall not be nested; a second BT shall
|
||||
// not appear before an ET.
|
||||
if to != nil {
|
||||
// Begin a text object, initializing the text matrix, Tm, and
|
||||
// the text line matrix, Tlm, to the identity matrix. Text
|
||||
// objects shall not be nested. A second BT shall not appear
|
||||
// before an ET. However, if that happens, all existing marks
|
||||
// are added to the page marks, in order to avoid losing content.
|
||||
if inTextObj {
|
||||
common.Log.Debug("BT called while in a text object")
|
||||
pageText.marks = append(pageText.marks, to.marks...)
|
||||
}
|
||||
inTextObj = true
|
||||
to = newTextObject(e, resources, gs, &state, &fontStack)
|
||||
case "ET": // End Text
|
||||
// End text object, discarding text matrix. If the current
|
||||
// text object contains text marks, they are added to the
|
||||
// page text marks collection.
|
||||
// The ET operator should always have a matching BT operator.
|
||||
// However, if ET appears outside of a text object, the behavior
|
||||
// does not change: the text matrices are discarded and all
|
||||
// existing marks in the text object are added to the page marks.
|
||||
if !inTextObj {
|
||||
common.Log.Debug("ET called outside of a text object")
|
||||
}
|
||||
inTextObj = false
|
||||
pageText.marks = append(pageText.marks, to.marks...)
|
||||
to = nil
|
||||
to.reset()
|
||||
case "T*": // Move to start of next text line
|
||||
to.nextLine()
|
||||
case "Td": // Move text location
|
||||
@ -202,10 +218,6 @@ func (e *Extractor) extractPageText(contents string, resources *model.PdfPageRes
|
||||
}
|
||||
to.setCharSpacing(y)
|
||||
case "Tf": // Set font.
|
||||
if to == nil {
|
||||
// This is needed for 26-Hazard-Thermal-environment.pdf
|
||||
to = newTextObject(e, resources, gs, &state, &fontStack)
|
||||
}
|
||||
if ok, err := to.checkOp(op, 2, true); !ok {
|
||||
common.Log.Debug("ERROR: Tf err=%v", err)
|
||||
return err
|
||||
@ -659,6 +671,14 @@ func newTextObject(e *Extractor, resources *model.PdfPageResources, gs contentst
|
||||
}
|
||||
}
|
||||
|
||||
// reset sets the text matrix `Tm` and the text line matrix `Tlm` of the text
|
||||
// object to the identity matrix. In addition, the marks collection is cleared.
|
||||
func (to *textObject) reset() {
|
||||
to.tm = transform.IdentityMatrix()
|
||||
to.tlm = transform.IdentityMatrix()
|
||||
to.marks = nil
|
||||
}
|
||||
|
||||
// renderText processes and renders byte array `data` for extraction purposes.
|
||||
func (to *textObject) renderText(data []byte) error {
|
||||
font := to.getCurrentFont()
|
||||
@ -1205,7 +1225,7 @@ func (pt *PageText) sortPosition(tol float64) {
|
||||
if pt.marks[i-1].orient != pt.marks[i].orient {
|
||||
cluster++
|
||||
} else {
|
||||
if pt.marks[i-1].orientedStart.Y - pt.marks[i].orientedStart.Y > tol {
|
||||
if pt.marks[i-1].orientedStart.Y-pt.marks[i].orientedStart.Y > tol {
|
||||
cluster++
|
||||
}
|
||||
}
|
||||
|
57
internal/textencoding/utf16.go
Normal file
57
internal/textencoding/utf16.go
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* This file is subject to the terms and conditions defined in
|
||||
* file 'LICENSE.md', which is part of this source code package.
|
||||
*/
|
||||
|
||||
package textencoding
|
||||
|
||||
import (
|
||||
"github.com/unidoc/unipdf/v3/core"
|
||||
"github.com/unidoc/unipdf/v3/internal/strutils"
|
||||
)
|
||||
|
||||
// UTF16Encoder represents UTF-16 encoding.
|
||||
type UTF16Encoder struct {
|
||||
baseName string
|
||||
}
|
||||
|
||||
// NewUTF16TextEncoder returns a new UTF16Encoder based on the predefined
|
||||
// encoding `baseName`.
|
||||
func NewUTF16TextEncoder(baseName string) UTF16Encoder {
|
||||
return UTF16Encoder{baseName}
|
||||
}
|
||||
|
||||
// String returns a string that describes `enc`.
|
||||
func (enc UTF16Encoder) String() string {
|
||||
return enc.baseName
|
||||
}
|
||||
|
||||
// Encode converts the Go unicode string to a PDF encoded string.
|
||||
func (enc UTF16Encoder) Encode(str string) []byte {
|
||||
return []byte(strutils.StringToUTF16(str))
|
||||
}
|
||||
|
||||
// Decode converts PDF encoded string to a Go unicode string.
|
||||
func (enc UTF16Encoder) Decode(raw []byte) string {
|
||||
return strutils.UTF16ToString(raw)
|
||||
}
|
||||
|
||||
// RuneToCharcode converts rune `r` to a PDF character code.
|
||||
// The bool return flag is true if there was a match, and false otherwise.
|
||||
func (enc UTF16Encoder) RuneToCharcode(r rune) (CharCode, bool) {
|
||||
return CharCode(r), true
|
||||
}
|
||||
|
||||
// CharcodeToRune converts PDF character code `code` to a rune.
|
||||
// The bool return flag is true if there was a match, and false otherwise.
|
||||
func (enc UTF16Encoder) CharcodeToRune(code CharCode) (rune, bool) {
|
||||
return rune(code), true
|
||||
}
|
||||
|
||||
// ToPdfObject returns a PDF Object that represents the encoding.
|
||||
func (enc UTF16Encoder) ToPdfObject() core.PdfObject {
|
||||
if enc.baseName != "" {
|
||||
return core.MakeName(enc.baseName)
|
||||
}
|
||||
return core.MakeNull()
|
||||
}
|
@ -194,9 +194,23 @@ func newPdfFontType0FromPdfObject(d *core.PdfObjectDictionary, base *fontCommon)
|
||||
|
||||
encoderName, ok := core.GetNameVal(d.Get("Encoding"))
|
||||
if ok {
|
||||
if encoderName == "Identity-H" || encoderName == "Identity-V" {
|
||||
switch encoderName {
|
||||
case "Identity-H", "Identity-V":
|
||||
font.encoder = textencoding.NewIdentityTextEncoder(encoderName)
|
||||
} else {
|
||||
case
|
||||
// Reference: https://www.adobe.com/content/dam/acom/en/devnet/font/pdfs/5094.CJK_CID.pdf
|
||||
// Adobe-GB1-4, Adobe-GB1-5
|
||||
"UniGB-UTF16-H", "UniGB-UTF16-V",
|
||||
// Adobe-CNS1-4, Adobe-CNS1-5
|
||||
"UniCNS-UTF16-H", "UniCNS-UTF16-V",
|
||||
// Adobe-Japan1-4, Adobe-Japan1-5, Adobe-Japan1-6
|
||||
"UniJIS-UTF16-H", "UniJIS-UTF16-V", "UniJIS2004-UTF16-H",
|
||||
// Adobe-Japan2-0
|
||||
"UniHojo-UTF16-H", "UniHojo-UTF16-V",
|
||||
// Adobe-Korea1-2
|
||||
"UniKS-UTF16-H", "UniKS-UTF16-V":
|
||||
font.encoder = textencoding.NewUTF16TextEncoder(encoderName)
|
||||
default:
|
||||
common.Log.Debug("Unhandled cmap %q", encoderName)
|
||||
}
|
||||
}
|
||||
|
@ -6,8 +6,11 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/unidoc/unipdf/v3/common"
|
||||
@ -379,16 +382,35 @@ func (font *pdfFontSimple) ToPdfObject() core.PdfObject {
|
||||
return font.container
|
||||
}
|
||||
|
||||
// NewPdfFontFromTTFFile loads a TTF font and returns a PdfFont type that can be used in text
|
||||
// styling functions.
|
||||
// NewPdfFontFromTTFFile loads a TTF font file and returns a PdfFont type
|
||||
// that can be used in text styling functions.
|
||||
// Uses a WinAnsiTextEncoder and loads only character codes 32-255.
|
||||
func NewPdfFontFromTTFFile(filePath string) (*PdfFont, error) {
|
||||
f, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
common.Log.Debug("ERROR: reading TTF font file: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return NewPdfFontFromTTF(f)
|
||||
}
|
||||
|
||||
// NewPdfFontFromTTF loads a TTF font and returns a PdfFont type that can be
|
||||
// used in text styling functions.
|
||||
// Uses a WinAnsiTextEncoder and loads only character codes 32-255.
|
||||
func NewPdfFontFromTTF(r io.ReadSeeker) (*PdfFont, error) {
|
||||
const minCode = textencoding.CharCode(32)
|
||||
const maxCode = textencoding.CharCode(255)
|
||||
|
||||
ttf, err := fonts.TtfParseFile(filePath)
|
||||
ttfBytes, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
common.Log.Debug("ERROR: loading ttf font: %v", err)
|
||||
common.Log.Debug("ERROR: Unable to read font contents: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
ttf, err := fonts.TtfParse(bytes.NewReader(ttfBytes))
|
||||
if err != nil {
|
||||
common.Log.Debug("ERROR: loading TTF font: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -457,12 +479,6 @@ func NewPdfFontFromTTFFile(filePath string) (*PdfFont, error) {
|
||||
descriptor.ItalicAngle = core.MakeFloat(float64(ttf.ItalicAngle))
|
||||
descriptor.MissingWidth = core.MakeFloat(k * float64(ttf.Widths[0]))
|
||||
|
||||
ttfBytes, err := ioutil.ReadFile(filePath)
|
||||
if err != nil {
|
||||
common.Log.Debug("ERROR: Unable to read file contents: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stream, err := core.MakeStream(ttfBytes, core.NewFlateEncoder())
|
||||
if err != nil {
|
||||
common.Log.Debug("ERROR: Unable to make stream: %v", err)
|
||||
|
@ -864,3 +864,10 @@ func newStandandTextEncoder(t *testing.T) textencoding.SimpleEncoder {
|
||||
}
|
||||
return enc
|
||||
}
|
||||
|
||||
func TestNewFontFromFile(t *testing.T) {
|
||||
_, err := model.NewPdfFontFromTTFFile("testdata/font/OpenSans-Regular.ttf")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load font from file. err=%v", err)
|
||||
}
|
||||
}
|
||||
|
@ -905,7 +905,7 @@ func newPdfPageResourcesColorspacesFromPdfObject(obj core.PdfObject) (*PdfPageRe
|
||||
obj = indObj.PdfObject
|
||||
}
|
||||
|
||||
dict, ok := obj.(*core.PdfObjectDictionary)
|
||||
dict, ok := core.GetDict(obj)
|
||||
if !ok {
|
||||
return nil, errors.New("CS attribute type error")
|
||||
}
|
||||
|
@ -162,7 +162,7 @@ func newPdfPatternFromPdfObject(container core.PdfObject) (*PdfPattern, error) {
|
||||
pattern := &PdfPattern{}
|
||||
|
||||
var dict *core.PdfObjectDictionary
|
||||
if indObj, is := container.(*core.PdfIndirectObject); is {
|
||||
if indObj, is := core.GetIndirect(container); is {
|
||||
pattern.container = indObj
|
||||
d, ok := indObj.PdfObject.(*core.PdfObjectDictionary)
|
||||
if !ok {
|
||||
@ -170,11 +170,11 @@ func newPdfPatternFromPdfObject(container core.PdfObject) (*PdfPattern, error) {
|
||||
return nil, core.ErrTypeError
|
||||
}
|
||||
dict = d
|
||||
} else if streamObj, is := container.(*core.PdfObjectStream); is {
|
||||
} else if streamObj, is := core.GetStream(container); is {
|
||||
pattern.container = streamObj
|
||||
dict = streamObj.PdfObjectDictionary
|
||||
} else {
|
||||
common.Log.Debug("Pattern not an indirect object or stream")
|
||||
common.Log.Debug("Pattern not an indirect object or stream. %T", container)
|
||||
return nil, core.ErrTypeError
|
||||
}
|
||||
|
||||
|
@ -298,7 +298,7 @@ func (r *PdfReader) loadOutlines() (*PdfOutlineTreeNode, error) {
|
||||
|
||||
common.Log.Trace("Outline root dict: %v", dict)
|
||||
|
||||
outlineTree, _, err := r.buildOutlineTree(outlineRoot, nil, nil)
|
||||
outlineTree, _, err := r.buildOutlineTree(outlineRoot, nil, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -313,7 +313,12 @@ func (r *PdfReader) loadOutlines() (*PdfOutlineTreeNode, error) {
|
||||
// Parent, Prev are the parent or previous node in the hierarchy.
|
||||
// The function returns the corresponding tree node and the last node which is used
|
||||
// for setting the Last pointer of the tree node structures.
|
||||
func (r *PdfReader) buildOutlineTree(obj core.PdfObject, parent *PdfOutlineTreeNode, prev *PdfOutlineTreeNode) (*PdfOutlineTreeNode, *PdfOutlineTreeNode, error) {
|
||||
func (r *PdfReader) buildOutlineTree(obj core.PdfObject, parent *PdfOutlineTreeNode, prev *PdfOutlineTreeNode, visited map[core.PdfObject]struct{}) (*PdfOutlineTreeNode, *PdfOutlineTreeNode, error) {
|
||||
if visited == nil {
|
||||
visited = map[core.PdfObject]struct{}{}
|
||||
}
|
||||
visited[obj] = struct{}{}
|
||||
|
||||
container, isInd := obj.(*core.PdfIndirectObject)
|
||||
if !isInd {
|
||||
return nil, nil, fmt.Errorf("outline container not an indirect object %T", obj)
|
||||
@ -336,7 +341,7 @@ func (r *PdfReader) buildOutlineTree(obj core.PdfObject, parent *PdfOutlineTreeN
|
||||
if firstObj := dict.Get("First"); firstObj != nil {
|
||||
firstObj = core.ResolveReference(firstObj)
|
||||
if !core.IsNullObject(firstObj) {
|
||||
first, last, err := r.buildOutlineTree(firstObj, &outlineItem.PdfOutlineTreeNode, nil)
|
||||
first, last, err := r.buildOutlineTree(firstObj, &outlineItem.PdfOutlineTreeNode, nil, visited)
|
||||
if err != nil {
|
||||
common.Log.Debug("DEBUG: could not build outline item tree: %v. Skipping node children.", err)
|
||||
} else {
|
||||
@ -348,9 +353,9 @@ func (r *PdfReader) buildOutlineTree(obj core.PdfObject, parent *PdfOutlineTreeN
|
||||
|
||||
// Resolve the reference to next
|
||||
nextObj := core.ResolveReference(dict.Get("Next"))
|
||||
if nextObj != nil && nextObj != container {
|
||||
if _, processed := visited[nextObj]; nextObj != nil && nextObj != container && !processed {
|
||||
if _, isNull := nextObj.(*core.PdfObjectNull); !isNull {
|
||||
next, last, err := r.buildOutlineTree(nextObj, parent, &outlineItem.PdfOutlineTreeNode)
|
||||
next, last, err := r.buildOutlineTree(nextObj, parent, &outlineItem.PdfOutlineTreeNode, visited)
|
||||
if err != nil {
|
||||
common.Log.Debug("DEBUG: could not build outline tree for Next node: %v. Skipping node.", err)
|
||||
} else {
|
||||
@ -375,7 +380,7 @@ func (r *PdfReader) buildOutlineTree(obj core.PdfObject, parent *PdfOutlineTreeN
|
||||
firstObj = core.ResolveReference(firstObj)
|
||||
firstObjDirect := core.TraceToDirectObject(firstObj)
|
||||
if _, isNull := firstObjDirect.(*core.PdfObjectNull); !isNull && firstObjDirect != nil {
|
||||
first, last, err := r.buildOutlineTree(firstObj, &outline.PdfOutlineTreeNode, nil)
|
||||
first, last, err := r.buildOutlineTree(firstObj, &outline.PdfOutlineTreeNode, nil, visited)
|
||||
if err != nil {
|
||||
common.Log.Debug("DEBUG: could not build outline tree: %v. Skipping node children.", err)
|
||||
} else {
|
||||
|
@ -335,11 +335,7 @@ func (r *PdfPageResources) SetColorspaceByName(keyName core.PdfObjectName, cs Pd
|
||||
// HasXObjectByName checks if an XObject with a specified keyName is defined.
|
||||
func (r *PdfPageResources) HasXObjectByName(keyName core.PdfObjectName) bool {
|
||||
obj, _ := r.GetXObjectByName(keyName)
|
||||
if obj != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
return obj != nil
|
||||
}
|
||||
|
||||
// GenerateXObjectName generates an unused XObject name that can be used for
|
||||
@ -362,10 +358,10 @@ type XObjectType int
|
||||
// XObject types.
|
||||
const (
|
||||
XObjectTypeUndefined XObjectType = iota
|
||||
XObjectTypeImage XObjectType = iota
|
||||
XObjectTypeForm XObjectType = iota
|
||||
XObjectTypePS XObjectType = iota
|
||||
XObjectTypeUnknown XObjectType = iota
|
||||
XObjectTypeImage
|
||||
XObjectTypeForm
|
||||
XObjectTypePS
|
||||
XObjectTypeUnknown
|
||||
)
|
||||
|
||||
// GetXObjectByName returns the XObject with the specified keyName and the object type.
|
||||
|
@ -141,7 +141,7 @@ func newPdfShadingFromPdfObject(obj core.PdfObject) (*PdfShading, error) {
|
||||
shading := &PdfShading{}
|
||||
|
||||
var dict *core.PdfObjectDictionary
|
||||
if indObj, isInd := obj.(*core.PdfIndirectObject); isInd {
|
||||
if indObj, isInd := core.GetIndirect(obj); isInd {
|
||||
shading.container = indObj
|
||||
|
||||
d, ok := indObj.PdfObject.(*core.PdfObjectDictionary)
|
||||
@ -151,10 +151,10 @@ func newPdfShadingFromPdfObject(obj core.PdfObject) (*PdfShading, error) {
|
||||
}
|
||||
|
||||
dict = d
|
||||
} else if streamObj, isStream := obj.(*core.PdfObjectStream); isStream {
|
||||
} else if streamObj, isStream := core.GetStream(obj); isStream {
|
||||
shading.container = streamObj
|
||||
dict = streamObj.PdfObjectDictionary
|
||||
} else if d, isDict := obj.(*core.PdfObjectDictionary); isDict {
|
||||
} else if d, isDict := core.GetDict(obj); isDict {
|
||||
shading.container = d
|
||||
dict = d
|
||||
} else {
|
||||
|
BIN
model/testdata/font/OpenSans-Regular.ttf
vendored
Normal file
BIN
model/testdata/font/OpenSans-Regular.ttf
vendored
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user