fix wrong git ignore
This commit is contained in:
420
internal/pdf/model/pattern.go
Normal file
420
internal/pdf/model/pattern.go
Normal file
@@ -0,0 +1,420 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"fmt"
|
||||
|
||||
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
|
||||
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
|
||||
)
|
||||
|
||||
// A PdfPattern can represent a Pattern, either a tiling pattern or a shading pattern.
|
||||
// Note that all patterns shall be treated as colours; a Pattern colour space shall be established with the CS or cs
|
||||
// operator just like other colour spaces, and a particular pattern shall be installed as the current colour with the
|
||||
// SCN or scn operator.
|
||||
type PdfPattern struct {
|
||||
// Type: Pattern
|
||||
PatternType int64
|
||||
context PdfModel // The sub pattern, either PdfTilingPattern (Type 1) or PdfShadingPattern (Type 2).
|
||||
|
||||
container core.PdfObject
|
||||
}
|
||||
|
||||
func (pp *PdfPattern) GetContainingPdfObject() core.PdfObject {
|
||||
return pp.container
|
||||
}
|
||||
|
||||
// Context in this case is a reference to the subpattern entry: either PdfTilingPattern or PdfShadingPattern.
|
||||
func (pp *PdfPattern) GetContext() PdfModel {
|
||||
return pp.context
|
||||
}
|
||||
|
||||
// Set the sub pattern (context). Either PdfTilingPattern or PdfShadingPattern.
|
||||
func (pp *PdfPattern) SetContext(ctx PdfModel) {
|
||||
pp.context = ctx
|
||||
}
|
||||
|
||||
func (pp *PdfPattern) IsTiling() bool {
|
||||
return pp.PatternType == 1
|
||||
}
|
||||
|
||||
func (pp *PdfPattern) IsShading() bool {
|
||||
return pp.PatternType == 2
|
||||
}
|
||||
|
||||
// Check with IsTiling() prior to using this to ensure is a tiling pattern.
|
||||
func (pp *PdfPattern) GetAsTilingPattern() *PdfTilingPattern {
|
||||
return pp.context.(*PdfTilingPattern)
|
||||
}
|
||||
|
||||
// Check with IsShading() prior to using this, to ensure is a shading pattern.
|
||||
func (pp *PdfPattern) GetAsShadingPattern() *PdfShadingPattern {
|
||||
return pp.context.(*PdfShadingPattern)
|
||||
}
|
||||
|
||||
// A Tiling pattern consists of repetitions of a pattern cell with defined intervals.
|
||||
// It is a type 1 pattern. (PatternType = 1).
|
||||
// A tiling pattern is represented by a stream object, where the stream content is
|
||||
// a content stream that describes the pattern cell.
|
||||
type PdfTilingPattern struct {
|
||||
*PdfPattern
|
||||
PaintType *core.PdfObjectInteger // Colored or uncolored tiling pattern.
|
||||
TilingType *core.PdfObjectInteger // Constant spacing, no distortion or constant spacing/faster tiling.
|
||||
BBox *PdfRectangle
|
||||
XStep *core.PdfObjectFloat
|
||||
YStep *core.PdfObjectFloat
|
||||
Resources *PdfPageResources
|
||||
Matrix *core.PdfObjectArray // Pattern matrix (6 numbers).
|
||||
}
|
||||
|
||||
func (ptp *PdfTilingPattern) IsColored() bool {
|
||||
if ptp.PaintType != nil && *ptp.PaintType == 1 {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// GetContentStream returns the pattern cell's content stream
|
||||
func (ptp *PdfTilingPattern) GetContentStream() ([]byte, error) {
|
||||
decoded, _, err := ptp.GetContentStreamWithEncoder()
|
||||
return decoded, err
|
||||
}
|
||||
|
||||
// GetContentStreamWithEncoder returns the pattern cell's content stream and its encoder
|
||||
// TODO (v3): Change GetContentStreamWithEncoder to GetContentStream
|
||||
func (ptp *PdfTilingPattern) GetContentStreamWithEncoder() ([]byte, core.StreamEncoder, error) {
|
||||
streamObj, ok := ptp.container.(*core.PdfObjectStream)
|
||||
if !ok {
|
||||
common.Log.Debug("Tiling pattern container not a stream (got %T)", ptp.container)
|
||||
return nil, nil, ErrTypeError
|
||||
}
|
||||
|
||||
decoded, err := core.DecodeStream(streamObj)
|
||||
if err != nil {
|
||||
common.Log.Debug("Failed decoding stream, err: %v", err)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
encoder, err := core.NewEncoderFromStream(streamObj)
|
||||
if err != nil {
|
||||
common.Log.Debug("Failed finding decoding encoder: %v", err)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return decoded, encoder, nil
|
||||
}
|
||||
|
||||
// Set the pattern cell's content stream.
|
||||
func (ptp *PdfTilingPattern) SetContentStream(content []byte, encoder core.StreamEncoder) error {
|
||||
streamObj, ok := ptp.container.(*core.PdfObjectStream)
|
||||
if !ok {
|
||||
common.Log.Debug("Tiling pattern container not a stream (got %T)", ptp.container)
|
||||
return ErrTypeError
|
||||
}
|
||||
|
||||
// If encoding is not set, use raw encoder.
|
||||
if encoder == nil {
|
||||
encoder = core.NewRawEncoder()
|
||||
}
|
||||
|
||||
streamDict := streamObj.PdfObjectDictionary
|
||||
|
||||
// Make a new stream dict based on the encoding parameters.
|
||||
encDict := encoder.MakeStreamDict()
|
||||
// Merge the encoding dict into the stream dict.
|
||||
streamDict.Merge(encDict)
|
||||
|
||||
encoded, err := encoder.EncodeBytes(content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update length.
|
||||
streamDict.Set("Length", core.MakeInteger(int64(len(encoded))))
|
||||
|
||||
streamObj.Stream = []byte(encoded)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Shading patterns provide a smooth transition between colors across an area to be painted, i.e.
|
||||
// color(x,y) = f(x,y) at each point.
|
||||
// It is a type 2 pattern (PatternType = 2).
|
||||
type PdfShadingPattern struct {
|
||||
*PdfPattern
|
||||
Shading *PdfShading
|
||||
Matrix *core.PdfObjectArray
|
||||
ExtGState core.PdfObject
|
||||
}
|
||||
|
||||
// Load a pdf pattern from an indirect object. Used in parsing/loading PDFs.
|
||||
func newPdfPatternFromPdfObject(container core.PdfObject) (*PdfPattern, error) {
|
||||
pattern := &PdfPattern{}
|
||||
|
||||
var dict *core.PdfObjectDictionary
|
||||
if indObj, is := container.(*core.PdfIndirectObject); is {
|
||||
pattern.container = indObj
|
||||
d, ok := indObj.PdfObject.(*core.PdfObjectDictionary)
|
||||
if !ok {
|
||||
common.Log.Debug("Pattern indirect object not containing dictionary (got %T)", indObj.PdfObject)
|
||||
return nil, ErrTypeError
|
||||
}
|
||||
dict = d
|
||||
} else if streamObj, is := container.(*core.PdfObjectStream); is {
|
||||
pattern.container = streamObj
|
||||
dict = streamObj.PdfObjectDictionary
|
||||
} else {
|
||||
common.Log.Debug("Pattern not an indirect object or stream")
|
||||
return nil, ErrTypeError
|
||||
}
|
||||
|
||||
// PatternType.
|
||||
obj := dict.Get("PatternType")
|
||||
if obj == nil {
|
||||
common.Log.Debug("Pdf Pattern not containing PatternType")
|
||||
return nil, ErrRequiredAttributeMissing
|
||||
}
|
||||
patternType, ok := obj.(*core.PdfObjectInteger)
|
||||
if !ok {
|
||||
common.Log.Debug("Pattern type not an integer (got %T)", obj)
|
||||
return nil, ErrTypeError
|
||||
}
|
||||
if *patternType != 1 && *patternType != 2 {
|
||||
common.Log.Debug("Pattern type != 1/2 (got %d)", *patternType)
|
||||
return nil, ErrRangeError
|
||||
}
|
||||
pattern.PatternType = int64(*patternType)
|
||||
|
||||
switch *patternType {
|
||||
case 1: // Tiling pattern.
|
||||
ctx, err := newPdfTilingPatternFromDictionary(dict)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx.PdfPattern = pattern
|
||||
pattern.context = ctx
|
||||
return pattern, nil
|
||||
case 2: // Shading pattern.
|
||||
ctx, err := newPdfShadingPatternFromDictionary(dict)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx.PdfPattern = pattern
|
||||
pattern.context = ctx
|
||||
return pattern, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("unknown pattern")
|
||||
}
|
||||
|
||||
// Load entries specific to a pdf tiling pattern from a dictionary. Used in parsing/loading PDFs.
|
||||
func newPdfTilingPatternFromDictionary(dict *core.PdfObjectDictionary) (*PdfTilingPattern, error) {
|
||||
pattern := &PdfTilingPattern{}
|
||||
|
||||
// PaintType (required).
|
||||
obj := dict.Get("PaintType")
|
||||
if obj == nil {
|
||||
common.Log.Debug("PaintType missing")
|
||||
return nil, ErrRequiredAttributeMissing
|
||||
}
|
||||
paintType, ok := obj.(*core.PdfObjectInteger)
|
||||
if !ok {
|
||||
common.Log.Debug("PaintType not an integer (got %T)", obj)
|
||||
return nil, ErrTypeError
|
||||
}
|
||||
pattern.PaintType = paintType
|
||||
|
||||
// TilingType (required).
|
||||
obj = dict.Get("TilingType")
|
||||
if obj == nil {
|
||||
common.Log.Debug("TilingType missing")
|
||||
return nil, ErrRequiredAttributeMissing
|
||||
}
|
||||
tilingType, ok := obj.(*core.PdfObjectInteger)
|
||||
if !ok {
|
||||
common.Log.Debug("TilingType not an integer (got %T)", obj)
|
||||
return nil, ErrTypeError
|
||||
}
|
||||
pattern.TilingType = tilingType
|
||||
|
||||
// BBox (required).
|
||||
obj = dict.Get("BBox")
|
||||
if obj == nil {
|
||||
common.Log.Debug("BBox missing")
|
||||
return nil, ErrRequiredAttributeMissing
|
||||
}
|
||||
obj = core.TraceToDirectObject(obj)
|
||||
arr, ok := obj.(*core.PdfObjectArray)
|
||||
if !ok {
|
||||
common.Log.Debug("BBox should be specified by an array (got %T)", obj)
|
||||
return nil, ErrTypeError
|
||||
}
|
||||
rect, err := NewPdfRectangle(*arr)
|
||||
if err != nil {
|
||||
common.Log.Debug("BBox error: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
pattern.BBox = rect
|
||||
|
||||
// XStep (required).
|
||||
obj = dict.Get("XStep")
|
||||
if obj == nil {
|
||||
common.Log.Debug("XStep missing")
|
||||
return nil, ErrRequiredAttributeMissing
|
||||
}
|
||||
xStep, err := getNumberAsFloat(obj)
|
||||
if err != nil {
|
||||
common.Log.Debug("error getting XStep as float: %v", xStep)
|
||||
return nil, err
|
||||
}
|
||||
pattern.XStep = core.MakeFloat(xStep)
|
||||
|
||||
// YStep (required).
|
||||
obj = dict.Get("YStep")
|
||||
if obj == nil {
|
||||
common.Log.Debug("YStep missing")
|
||||
return nil, ErrRequiredAttributeMissing
|
||||
}
|
||||
yStep, err := getNumberAsFloat(obj)
|
||||
if err != nil {
|
||||
common.Log.Debug("error getting YStep as float: %v", yStep)
|
||||
return nil, err
|
||||
}
|
||||
pattern.YStep = core.MakeFloat(yStep)
|
||||
|
||||
// Resources (required).
|
||||
obj = dict.Get("Resources")
|
||||
if obj == nil {
|
||||
common.Log.Debug("Resources missing")
|
||||
return nil, ErrRequiredAttributeMissing
|
||||
}
|
||||
dict, ok = core.TraceToDirectObject(obj).(*core.PdfObjectDictionary)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid resource dictionary (%T)", obj)
|
||||
}
|
||||
resources, err := NewPdfPageResourcesFromDict(dict)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pattern.Resources = resources
|
||||
|
||||
// Matrix (optional).
|
||||
if obj := dict.Get("Matrix"); obj != nil {
|
||||
arr, ok := obj.(*core.PdfObjectArray)
|
||||
if !ok {
|
||||
common.Log.Debug("Matrix not an array (got %T)", obj)
|
||||
return nil, ErrTypeError
|
||||
}
|
||||
pattern.Matrix = arr
|
||||
}
|
||||
|
||||
return pattern, nil
|
||||
}
|
||||
|
||||
// Load entries specific to a pdf shading pattern from a dictionary. Used in parsing/loading PDFs.
|
||||
func newPdfShadingPatternFromDictionary(dict *core.PdfObjectDictionary) (*PdfShadingPattern, error) {
|
||||
pattern := &PdfShadingPattern{}
|
||||
|
||||
// Shading (required).
|
||||
obj := dict.Get("Shading")
|
||||
if obj == nil {
|
||||
common.Log.Debug("Shading missing")
|
||||
return nil, ErrRequiredAttributeMissing
|
||||
}
|
||||
shading, err := newPdfShadingFromPdfObject(obj)
|
||||
if err != nil {
|
||||
common.Log.Debug("error loading shading: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
pattern.Shading = shading
|
||||
|
||||
// Matrix (optional).
|
||||
if obj := dict.Get("Matrix"); obj != nil {
|
||||
arr, ok := obj.(*core.PdfObjectArray)
|
||||
if !ok {
|
||||
common.Log.Debug("Matrix not an array (got %T)", obj)
|
||||
return nil, ErrTypeError
|
||||
}
|
||||
pattern.Matrix = arr
|
||||
}
|
||||
|
||||
// ExtGState (optional).
|
||||
if obj := dict.Get("ExtGState"); obj != nil {
|
||||
pattern.ExtGState = obj
|
||||
}
|
||||
|
||||
return pattern, nil
|
||||
}
|
||||
|
||||
/* Conversions to pdf objects. */
|
||||
|
||||
func (pp *PdfPattern) getDict() *core.PdfObjectDictionary {
|
||||
if indObj, is := pp.container.(*core.PdfIndirectObject); is {
|
||||
dict, ok := indObj.PdfObject.(*core.PdfObjectDictionary)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return dict
|
||||
} else if streamObj, is := pp.container.(*core.PdfObjectStream); is {
|
||||
return streamObj.PdfObjectDictionary
|
||||
} else {
|
||||
common.Log.Debug("Trying to access pattern dictionary of invalid object type (%T)", pp.container)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (pp *PdfPattern) ToPdfObject() core.PdfObject {
|
||||
d := pp.getDict()
|
||||
d.Set("Type", core.MakeName("Pattern"))
|
||||
d.Set("PatternType", core.MakeInteger(pp.PatternType))
|
||||
|
||||
return pp.container
|
||||
}
|
||||
|
||||
func (pt *PdfTilingPattern) ToPdfObject() core.PdfObject {
|
||||
pt.PdfPattern.ToPdfObject()
|
||||
|
||||
d := pt.getDict()
|
||||
if pt.PaintType != nil {
|
||||
d.Set("PaintType", pt.PaintType)
|
||||
}
|
||||
if pt.TilingType != nil {
|
||||
d.Set("TilingType", pt.TilingType)
|
||||
}
|
||||
if pt.BBox != nil {
|
||||
d.Set("BBox", pt.BBox.ToPdfObject())
|
||||
}
|
||||
if pt.XStep != nil {
|
||||
d.Set("XStep", pt.XStep)
|
||||
}
|
||||
if pt.YStep != nil {
|
||||
d.Set("YStep", pt.YStep)
|
||||
}
|
||||
if pt.Resources != nil {
|
||||
d.Set("Resources", pt.Resources.ToPdfObject())
|
||||
}
|
||||
if pt.Matrix != nil {
|
||||
d.Set("Matrix", pt.Matrix)
|
||||
}
|
||||
|
||||
return pt.container
|
||||
}
|
||||
|
||||
func (psp *PdfShadingPattern) ToPdfObject() core.PdfObject {
|
||||
psp.PdfPattern.ToPdfObject()
|
||||
d := psp.getDict()
|
||||
|
||||
if psp.Shading != nil {
|
||||
d.Set("Shading", psp.Shading.ToPdfObject())
|
||||
}
|
||||
if psp.Matrix != nil {
|
||||
d.Set("Matrix", psp.Matrix)
|
||||
}
|
||||
if psp.ExtGState != nil {
|
||||
d.Set("ExtGState", psp.ExtGState)
|
||||
}
|
||||
|
||||
return psp.container
|
||||
}
|
||||
Reference in New Issue
Block a user