2 Star 0 Fork 1

Deeao/golang-pdfcpu

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
write.go 24.04 KB
一键复制 编辑 原始数据 按行查看 历史
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085
/*
Copyright 2018 The pdfcpu Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package pdfcpu
import (
"bufio"
"bytes"
"encoding/hex"
"fmt"
"os"
"path/filepath"
"sort"
"strings"
"gitee.com/deeao/golang-pdfcpu/pkg/filter"
"gitee.com/deeao/golang-pdfcpu/pkg/log"
"gitee.com/deeao/golang-pdfcpu/pkg/pdfcpu/model"
"gitee.com/deeao/golang-pdfcpu/pkg/pdfcpu/types"
"github.com/pkg/errors"
)
func writeObjects(ctx *model.Context) error {
// Write root object(aka the document catalog) and page tree.
if err := writeRootObject(ctx); err != nil {
return err
}
if log.WriteEnabled() {
log.Write.Printf("offset after writeRootObject: %d\n", ctx.Write.Offset)
}
// Write document information dictionary.
if err := writeDocumentInfoDict(ctx); err != nil {
return err
}
if log.WriteEnabled() {
log.Write.Printf("offset after writeInfoObject: %d\n", ctx.Write.Offset)
}
// Write offspec additional streams as declared in pdf trailer.
if err := writeAdditionalStreams(ctx); err != nil {
return err
}
return writeEncryptDict(ctx)
}
// Write generates a PDF file for the cross reference table contained in Context.
func Write(ctx *model.Context) (err error) {
// Create a writer for dirname and filename if not already supplied.
if ctx.Write.Writer == nil {
fileName := filepath.Join(ctx.Write.DirName, ctx.Write.FileName)
if log.CLIEnabled() {
log.CLI.Printf("writing to %s\n", fileName)
}
file, err := os.Create(fileName)
if err != nil {
return errors.Wrapf(err, "can't create %s\n%s", fileName, err)
}
ctx.Write.Writer = bufio.NewWriter(file)
defer func() {
// The underlying bufio.Writer has already been flushed.
// Processing error takes precedence.
if err != nil {
file.Close()
return
}
// Do not miss out on closing errors.
err = file.Close()
}()
}
if err = prepareContextForWriting(ctx); err != nil {
return err
}
// if exists metadata, update from info dict
// else if v2 create from scratch
// else nothing just write info dict
// Since we support PDF Collections (since V1.7) for file attachments
// we need to generate V1.7 PDF files.
v := model.V17
if ctx.Version() == model.V20 {
v = model.V20
}
if err = writeHeader(ctx.Write, v); err != nil {
return err
}
// Ensure there is no root version.
if ctx.RootVersion != nil {
ctx.RootDict.Delete("Version")
}
if log.WriteEnabled() {
log.Write.Printf("offset after writeHeader: %d\n", ctx.Write.Offset)
}
if err := writeObjects(ctx); err != nil {
return err
}
// Mark redundant objects as free.
// eg. duplicate resources, compressed objects, linearization dicts..
deleteRedundantObjects(ctx)
if err = writeXRef(ctx); err != nil {
return err
}
// Write pdf trailer.
if err = writeTrailer(ctx.Write); err != nil {
return err
}
if err = setFileSizeOfWrittenFile(ctx.Write); err != nil {
return err
}
if ctx.Read != nil {
ctx.Write.BinaryImageSize = ctx.Read.BinaryImageSize
ctx.Write.BinaryFontSize = ctx.Read.BinaryFontSize
logWriteStats(ctx)
}
return nil
}
// WriteIncrement writes a PDF increment..
func WriteIncrement(ctx *model.Context) error {
// Write all modified objects that are part of this increment.
for _, i := range ctx.Write.ObjNrs {
if err := writeFlatObject(ctx, i); err != nil {
return err
}
}
if err := writeXRef(ctx); err != nil {
return err
}
return writeTrailer(ctx.Write)
}
func prepareContextForWriting(ctx *model.Context) error {
if err := ensureInfoDictAndFileID(ctx); err != nil {
return err
}
return handleEncryption(ctx)
}
func writeAdditionalStreams(ctx *model.Context) error {
if ctx.AdditionalStreams == nil {
return nil
}
if _, _, err := writeDeepObject(ctx, ctx.AdditionalStreams); err != nil {
return err
}
return nil
}
func ensureFileID(ctx *model.Context) error {
fid, err := fileID(ctx)
if err != nil {
return err
}
if ctx.ID == nil {
// Ensure ctx.ID
ctx.ID = types.Array{fid, fid}
return nil
}
// Update ctx.ID
a := ctx.ID
if len(a) != 2 {
return errors.New("pdfcpu: ID must be an array with 2 elements")
}
a[1] = fid
return nil
}
func ensureInfoDictAndFileID(ctx *model.Context) error {
if ctx.Version() < model.V20 {
if err := ensureInfoDict(ctx); err != nil {
return err
}
}
return ensureFileID(ctx)
}
// Write root entry to disk.
func writeRootEntry(ctx *model.Context, d types.Dict, dictName, entryName string, statsAttr int) error {
o, err := writeEntry(ctx, d, dictName, entryName)
if err != nil {
return err
}
if o != nil {
ctx.Stats.AddRootAttr(statsAttr)
}
return nil
}
// Write root entry to object stream.
func writeRootEntryToObjStream(ctx *model.Context, d types.Dict, dictName, entryName string, statsAttr int) error {
ctx.Write.WriteToObjectStream = true
if err := writeRootEntry(ctx, d, dictName, entryName, statsAttr); err != nil {
return err
}
return stopObjectStream(ctx)
}
// Write page tree.
func writePages(ctx *model.Context, rootDict types.Dict) error {
// Page tree root (the top "Pages" dict) must be indirect reference.
indRef := rootDict.IndirectRefEntry("Pages")
if indRef == nil {
return errors.New("pdfcpu: writePages: missing indirect obj for pages dict")
}
// Embed all page tree objects into objects stream.
ctx.Write.WriteToObjectStream = true
// Write page tree.
p := 0
if _, _, err := writePagesDict(ctx, indRef, &p); err != nil {
return err
}
return stopObjectStream(ctx)
}
func writeRootAttrsBatch1(ctx *model.Context, d types.Dict, dictName string) error {
for _, e := range []struct {
entryName string
statsAttr int
}{
{"Extensions", model.RootExtensions},
{"PageLabels", model.RootPageLabels},
{"Names", model.RootNames},
{"Dests", model.RootDests},
{"ViewerPreferences", model.RootViewerPrefs},
{"PageLayout", model.RootPageLayout},
{"PageMode", model.RootPageMode},
{"Outlines", model.RootOutlines},
{"Threads", model.RootThreads},
{"OpenAction", model.RootOpenAction},
{"AA", model.RootAA},
{"URI", model.RootURI},
{"AcroForm", model.RootAcroForm},
{"Metadata", model.RootMetadata},
} {
if err := writeRootEntry(ctx, d, dictName, e.entryName, e.statsAttr); err != nil {
return err
}
}
return nil
}
func writeRootAttrsBatch2(ctx *model.Context, d types.Dict, dictName string) error {
for _, e := range []struct {
entryName string
statsAttr int
}{
{"MarkInfo", model.RootMarkInfo},
{"Lang", model.RootLang},
{"SpiderInfo", model.RootSpiderInfo},
{"OutputIntents", model.RootOutputIntents},
{"PieceInfo", model.RootPieceInfo},
{"OCProperties", model.RootOCProperties},
{"Perms", model.RootPerms},
{"Legal", model.RootLegal},
{"Requirements", model.RootRequirements},
{"Collection", model.RootCollection},
{"NeedsRendering", model.RootNeedsRendering},
} {
if err := writeRootEntry(ctx, d, dictName, e.entryName, e.statsAttr); err != nil {
return err
}
}
return nil
}
func writeRootObject(ctx *model.Context) error {
// => 7.7.2 Document Catalog
xRefTable := ctx.XRefTable
catalog := *xRefTable.Root
objNumber := int(catalog.ObjectNumber)
genNumber := int(catalog.GenerationNumber)
if log.WriteEnabled() {
log.Write.Printf("*** writeRootObject: begin offset=%d *** %s\n", ctx.Write.Offset, catalog)
}
// Ensure corresponding and accurate name tree object graphs.
if !ctx.ApplyReducedFeatureSet() {
if err := ctx.BindNameTrees(); err != nil {
return err
}
}
d, err := xRefTable.DereferenceDict(catalog)
if err != nil {
return err
}
if d == nil {
return errors.Errorf("pdfcpu: writeRootObject: unable to dereference root dict")
}
dictName := "rootDict"
if ctx.ApplyReducedFeatureSet() {
log.Write.Println("writeRootObject - reducedFeatureSet:exclude complex entries.")
d.Delete("Names")
d.Delete("Dests")
d.Delete("Outlines")
d.Delete("OpenAction")
d.Delete("StructTreeRoot")
d.Delete("OCProperties")
}
if err = writeDictObject(ctx, objNumber, genNumber, d); err != nil {
return err
}
if log.WriteEnabled() {
log.Write.Printf("writeRootObject: %s\n", d)
log.Write.Printf("writeRootObject: new offset after rootDict = %d\n", ctx.Write.Offset)
}
if err = writeRootEntry(ctx, d, dictName, "Version", model.RootVersion); err != nil {
return err
}
if err = writePages(ctx, d); err != nil {
return err
}
if err := writeRootAttrsBatch1(ctx, d, dictName); err != nil {
return err
}
if err = writeRootEntryToObjStream(ctx, d, dictName, "StructTreeRoot", model.RootStructTreeRoot); err != nil {
return err
}
if err := writeRootAttrsBatch2(ctx, d, dictName); err != nil {
return err
}
if log.WriteEnabled() {
log.Write.Printf("*** writeRootObject: end offset=%d ***\n", ctx.Write.Offset)
}
return nil
}
func writeTrailerDict(ctx *model.Context) error {
if log.WriteEnabled() {
log.Write.Printf("writeTrailerDict begin\n")
}
w := ctx.Write
xRefTable := ctx.XRefTable
if _, err := w.WriteString("trailer"); err != nil {
return err
}
if err := w.WriteEol(); err != nil {
return err
}
d := types.NewDict()
d.Insert("Size", types.Integer(*xRefTable.Size))
d.Insert("Root", *xRefTable.Root)
if xRefTable.Info != nil {
d.Insert("Info", *xRefTable.Info)
}
if ctx.Encrypt != nil && ctx.EncKey != nil {
d.Insert("Encrypt", *ctx.Encrypt)
}
if xRefTable.ID != nil {
d.Insert("ID", xRefTable.ID)
}
if ctx.Write.Increment {
d.Insert("Prev", types.Integer(*ctx.Write.OffsetPrevXRef))
}
if _, err := w.WriteString(d.PDFString()); err != nil {
return err
}
if log.WriteEnabled() {
log.Write.Printf("writeTrailerDict end\n")
}
return nil
}
func writeXRefSubsection(ctx *model.Context, start int, size int) error {
if log.WriteEnabled() {
log.Write.Printf("writeXRefSubsection: start=%d size=%d\n", start, size)
}
w := ctx.Write
if _, err := w.WriteString(fmt.Sprintf("%d %d%s", start, size, w.Eol)); err != nil {
return err
}
var lines []string
for i := start; i < start+size; i++ {
entry := ctx.XRefTable.Table[i]
if entry.Compressed {
return errors.New("pdfcpu: writeXRefSubsection: compressed entries present")
}
var s string
if entry.Free {
s = fmt.Sprintf("%010d %05d f%2s", *entry.Offset, *entry.Generation, w.Eol)
} else {
var off int64
writeOffset, found := ctx.Write.Table[i]
if found {
off = writeOffset
}
s = fmt.Sprintf("%010d %05d n%2s", off, *entry.Generation, w.Eol)
}
lines = append(lines, fmt.Sprintf("%d: %s", i, s))
if _, err := w.WriteString(s); err != nil {
return err
}
}
if log.WriteEnabled() {
log.Write.Printf("\n%s\n", strings.Join(lines, ""))
log.Write.Printf("writeXRefSubsection: end\n")
}
return nil
}
func deleteRedundantObject(ctx *model.Context, objNr int) {
if len(ctx.Write.SelectedPages) == 0 &&
(ctx.Optimize.IsDuplicateFontObject(objNr) || ctx.Optimize.IsDuplicateImageObject(objNr)) {
ctx.FreeObject(objNr)
}
if ctx.IsLinearizationObject(objNr) || ctx.Optimize.IsDuplicateInfoObject(objNr) ||
ctx.Read.IsObjectStreamObject(objNr) {
ctx.FreeObject(objNr)
}
}
func detectLinearizationObjs(xRefTable *model.XRefTable, entry *model.XRefTableEntry, i int) {
if _, ok := entry.Object.(types.StreamDict); ok {
if *entry.Offset == *xRefTable.OffsetPrimaryHintTable {
xRefTable.LinearizationObjs[i] = true
if log.WriteEnabled() {
log.Write.Printf("detectLinearizationObjs: primaryHintTable at obj #%d\n", i)
}
}
if xRefTable.OffsetOverflowHintTable != nil &&
*entry.Offset == *xRefTable.OffsetOverflowHintTable {
xRefTable.LinearizationObjs[i] = true
if log.WriteEnabled() {
log.Write.Printf("detectLinearizationObjs: overflowHintTable at obj #%d\n", i)
}
}
}
}
func deleteRedundantObjects(ctx *model.Context) {
if ctx.Optimize == nil {
return
}
xRefTable := ctx.XRefTable
if log.WriteEnabled() {
log.Write.Printf("deleteRedundantObjects begin: Size=%d\n", *xRefTable.Size)
}
for i := 0; i < *xRefTable.Size; i++ {
// Missing object remains missing.
entry, found := xRefTable.Find(i)
if !found {
continue
}
// Free object
if entry.Free {
continue
}
// Object written
if ctx.Write.HasWriteOffset(i) {
// Resources may be cross referenced from different objects
// eg. font descriptors may be shared by different font dicts.
// Try to remove this object from the list of the potential duplicate objects.
if log.WriteEnabled() {
log.Write.Printf("deleteRedundantObjects: remove duplicate obj #%d\n", i)
}
delete(ctx.Optimize.DuplicateFontObjs, i)
delete(ctx.Optimize.DuplicateImageObjs, i)
delete(ctx.Optimize.DuplicateInfoObjects, i)
continue
}
// Object not written
if ctx.Read.Linearized && entry.Offset != nil {
// This block applies to pre existing objects only.
// Since there is no type entry for stream dicts associated with linearization dicts
// we have to check every StreamDict that has not been written.
detectLinearizationObjs(xRefTable, entry, i)
}
deleteRedundantObject(ctx, i)
}
if log.WriteEnabled() {
log.Write.Println("deleteRedundantObjects end")
}
}
func sortedWritableKeys(ctx *model.Context) []int {
var keys []int
for i, e := range ctx.Table {
if !ctx.Write.Increment && e.Free || ctx.Write.HasWriteOffset(i) {
keys = append(keys, i)
}
}
sort.Ints(keys)
return keys
}
// After inserting the last object write the cross reference table to disk.
func writeXRefTable(ctx *model.Context) error {
keys := sortedWritableKeys(ctx)
objCount := len(keys)
if log.WriteEnabled() {
log.Write.Printf("xref has %d entries\n", objCount)
}
if _, err := ctx.Write.WriteString("xref"); err != nil {
return err
}
if err := ctx.Write.WriteEol(); err != nil {
return err
}
start := keys[0]
size := 1
for i := 1; i < len(keys); i++ {
if keys[i]-keys[i-1] > 1 {
if err := writeXRefSubsection(ctx, start, size); err != nil {
return err
}
start = keys[i]
size = 1
continue
}
size++
}
if err := writeXRefSubsection(ctx, start, size); err != nil {
return err
}
if err := writeTrailerDict(ctx); err != nil {
return err
}
if err := ctx.Write.WriteEol(); err != nil {
return err
}
if _, err := ctx.Write.WriteString("startxref"); err != nil {
return err
}
if err := ctx.Write.WriteEol(); err != nil {
return err
}
if _, err := ctx.Write.WriteString(fmt.Sprintf("%d", ctx.Write.Offset)); err != nil {
return err
}
return ctx.Write.WriteEol()
}
// int64ToBuf returns a byte slice with length byteCount representing integer i.
func int64ToBuf(i int64, byteCount int) (buf []byte) {
j := 0
var b []byte
for k := i; k > 0; {
b = append(b, byte(k&0xff))
k >>= 8
j++
}
// Swap byte order
for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
b[i], b[j] = b[j], b[i]
}
if j < byteCount {
buf = append(bytes.Repeat([]byte{0}, byteCount-j), b...)
} else {
buf = b
}
return
}
func createXRefStream(ctx *model.Context, i1, i2, i3 int, objNrs []int) ([]byte, *types.Array, error) {
if log.WriteEnabled() {
log.Write.Println("createXRefStream begin")
}
xRefTable := ctx.XRefTable
var (
buf []byte
a types.Array
)
objCount := len(objNrs)
if log.WriteEnabled() {
log.Write.Printf("createXRefStream: xref has %d entries\n", objCount)
}
start := objNrs[0]
size := 0
for i := 0; i < len(objNrs); i++ {
j := objNrs[i]
entry := xRefTable.Table[j]
var s1, s2, s3 []byte
if entry.Free {
// unused
if log.WriteEnabled() {
log.Write.Printf("createXRefStream: unused i=%d nextFreeAt:%d gen:%d\n", j, int(*entry.Offset), int(*entry.Generation))
}
s1 = int64ToBuf(0, i1)
s2 = int64ToBuf(*entry.Offset, i2)
s3 = int64ToBuf(int64(*entry.Generation), i3)
} else if entry.Compressed {
// in use, compressed into object stream
if log.WriteEnabled() {
log.Write.Printf("createXRefStream: compressed i=%d at objstr %d[%d]\n", j, int(*entry.ObjectStream), int(*entry.ObjectStreamInd))
}
s1 = int64ToBuf(2, i1)
s2 = int64ToBuf(int64(*entry.ObjectStream), i2)
s3 = int64ToBuf(int64(*entry.ObjectStreamInd), i3)
} else {
off, found := ctx.Write.Table[j]
if !found {
return nil, nil, errors.Errorf("pdfcpu: createXRefStream: missing write offset for obj #%d\n", i)
}
// in use, uncompressed
if log.WriteEnabled() {
log.Write.Printf("createXRefStream: used i=%d offset:%d gen:%d\n", j, int(off), int(*entry.Generation))
}
s1 = int64ToBuf(1, i1)
s2 = int64ToBuf(off, i2)
s3 = int64ToBuf(int64(*entry.Generation), i3)
}
if log.WriteEnabled() {
log.Write.Printf("createXRefStream: written: %x %x %x \n", s1, s2, s3)
}
buf = append(buf, s1...)
buf = append(buf, s2...)
buf = append(buf, s3...)
if i > 0 && (objNrs[i]-objNrs[i-1] > 1) {
a = append(a, types.Integer(start))
a = append(a, types.Integer(size))
start = objNrs[i]
size = 1
continue
}
size++
}
a = append(a, types.Integer(start))
a = append(a, types.Integer(size))
if log.WriteEnabled() {
log.Write.Println("createXRefStream end")
}
return buf, &a, nil
}
// NewXRefStreamDict creates a new PDFXRefStreamDict object.
func newXRefStreamDict(ctx *model.Context) *types.XRefStreamDict {
sd := types.StreamDict{Dict: types.NewDict()}
sd.Insert("Type", types.Name("XRef"))
sd.Insert("Filter", types.Name(filter.Flate))
sd.FilterPipeline = []types.PDFFilter{{Name: filter.Flate, DecodeParms: nil}}
sd.Insert("Root", *ctx.Root)
if ctx.Info != nil {
sd.Insert("Info", *ctx.Info)
}
if ctx.ID != nil {
sd.Insert("ID", ctx.ID)
}
if ctx.Encrypt != nil && ctx.EncKey != nil {
sd.Insert("Encrypt", *ctx.Encrypt)
}
if ctx.Write.Increment {
sd.Insert("Prev", types.Integer(*ctx.Write.OffsetPrevXRef))
}
return &types.XRefStreamDict{StreamDict: sd}
}
func writeXRefStream(ctx *model.Context) error {
if log.WriteEnabled() {
log.Write.Println("writeXRefStream begin")
}
xRefTable := ctx.XRefTable
xRefStreamDict := newXRefStreamDict(ctx)
xRefTableEntry := model.NewXRefTableEntryGen0(*xRefStreamDict)
// Reuse free objects (including recycled objects from this run).
objNumber, err := xRefTable.InsertAndUseRecycled(*xRefTableEntry)
if err != nil {
return err
}
xRefStreamDict.Insert("Size", types.Integer(*xRefTable.Size))
// Include xref stream dict obj within xref stream dict.
offset := ctx.Write.Offset
ctx.Write.SetWriteOffset(objNumber)
i2Base := int64(*ctx.Size)
if offset > i2Base {
i2Base = offset
}
i1 := 1 // 0, 1 or 2 always fit into 1 byte.
i2 := func(i int64) (byteCount int) {
for i > 0 {
i >>= 8
byteCount++
}
return byteCount
}(i2Base)
i3 := 2 // scale for max objectstream index <= 0x ff ff
wArr := types.Array{types.Integer(i1), types.Integer(i2), types.Integer(i3)}
xRefStreamDict.Insert("W", wArr)
// Generate xRefStreamDict data = xref entries -> xRefStreamDict.Content
objNrs := sortedWritableKeys(ctx)
content, indArr, err := createXRefStream(ctx, i1, i2, i3, objNrs)
if err != nil {
return err
}
xRefStreamDict.Content = content
xRefStreamDict.Insert("Index", *indArr)
// Encode xRefStreamDict.Content -> xRefStreamDict.Raw
if err = xRefStreamDict.StreamDict.Encode(); err != nil {
return err
}
if log.WriteEnabled() {
log.Write.Printf("writeXRefStream: xRefStreamDict: %s\n", xRefStreamDict)
}
if err = writeStreamDictObject(ctx, objNumber, 0, xRefStreamDict.StreamDict); err != nil {
return err
}
w := ctx.Write
if _, err = w.WriteString("startxref"); err != nil {
return err
}
if err = w.WriteEol(); err != nil {
return err
}
if _, err = w.WriteString(fmt.Sprintf("%d", offset)); err != nil {
return err
}
if err = w.WriteEol(); err != nil {
return err
}
if log.WriteEnabled() {
log.Write.Println("writeXRefStream end")
}
return nil
}
func writeEncryptDict(ctx *model.Context) error {
// Bail out unless we really have to write encrypted.
if ctx.Encrypt == nil || ctx.EncKey == nil {
return nil
}
indRef := *ctx.Encrypt
objNumber := int(indRef.ObjectNumber)
genNumber := int(indRef.GenerationNumber)
d, err := ctx.DereferenceDict(indRef)
if err != nil {
return err
}
return writeObject(ctx, objNumber, genNumber, d.PDFString())
}
func setupEncryption(ctx *model.Context) error {
var err error
if ok := validateAlgorithm(ctx); !ok {
return errors.New("pdfcpu: unsupported encryption algorithm (PDF 2.0 assumes AES/256)")
}
d := newEncryptDict(
ctx.Version(),
ctx.EncryptUsingAES,
ctx.EncryptKeyLength,
int16(ctx.Permissions),
)
if ctx.E, err = supportedEncryption(ctx, d); err != nil {
return err
}
if ctx.ID == nil {
return errors.New("pdfcpu: encrypt: missing ID")
}
if ctx.E.ID, err = ctx.IDFirstElement(); err != nil {
return err
}
if err = calcOAndU(ctx, d); err != nil {
return err
}
if err = writePermissions(ctx, d); err != nil {
return err
}
xRefTableEntry := model.NewXRefTableEntryGen0(d)
// Reuse free objects (including recycled objects from this run).
objNumber, err := ctx.InsertAndUseRecycled(*xRefTableEntry)
if err != nil {
return err
}
ctx.Encrypt = types.NewIndirectRef(objNumber, 0)
return nil
}
func updateEncryption(ctx *model.Context) error {
if ctx.Encrypt == nil {
return errors.New("pdfcpu: This file is not encrypted - nothing written.")
}
d, err := ctx.EncryptDict()
if err != nil {
return err
}
if ctx.Cmd == model.SETPERMISSIONS {
//fmt.Printf("updating permissions to: %v\n", ctx.UserAccessPermissions)
ctx.E.P = int(ctx.Permissions)
d.Update("P", types.Integer(ctx.E.P))
// and moving on, U is dependent on P
}
// ctx.Cmd == CHANGEUPW or CHANGE OPW
if ctx.UserPWNew != nil {
//fmt.Printf("change upw from <%s> to <%s>\n", ctx.UserPW, *ctx.UserPWNew)
ctx.UserPW = *ctx.UserPWNew
}
if ctx.OwnerPWNew != nil {
//fmt.Printf("change opw from <%s> to <%s>\n", ctx.OwnerPW, *ctx.OwnerPWNew)
ctx.OwnerPW = *ctx.OwnerPWNew
}
if ctx.E.R == 5 || ctx.E.R == 6 {
if err = calcOAndU(ctx, d); err != nil {
return err
}
// Calc Perms for rev 5, 6.
return writePermissions(ctx, d)
}
//fmt.Printf("opw before: length:%d <%s>\n", len(ctx.E.O), ctx.E.O)
if ctx.E.O, err = o(ctx); err != nil {
return err
}
//fmt.Printf("opw after: length:%d <%s> %0X\n", len(ctx.E.O), ctx.E.O, ctx.E.O)
d.Update("O", types.HexLiteral(hex.EncodeToString(ctx.E.O)))
//fmt.Printf("upw before: length:%d <%s>\n", len(ctx.E.U), ctx.E.U)
if ctx.E.U, ctx.EncKey, err = u(ctx); err != nil {
return err
}
//fmt.Printf("upw after: length:%d <%s> %0X\n", len(ctx.E.U), ctx.E.U, ctx.E.U)
//fmt.Printf("encKey = %0X\n", ctx.EncKey)
d.Update("U", types.HexLiteral(hex.EncodeToString(ctx.E.U)))
return nil
}
func handleEncryption(ctx *model.Context) error {
if ctx.Cmd == model.ENCRYPT || ctx.Cmd == model.DECRYPT {
if ctx.Cmd == model.DECRYPT {
// Remove encryption.
ctx.EncKey = nil
} else {
if err := setupEncryption(ctx); err != nil {
return err
}
alg := "RC4"
if ctx.EncryptUsingAES {
alg = "AES"
}
if log.CLIEnabled() {
log.CLI.Printf("using %s-%d\n", alg, ctx.EncryptKeyLength)
}
}
} else if ctx.UserPWNew != nil || ctx.OwnerPWNew != nil || ctx.Cmd == model.SETPERMISSIONS {
if err := updateEncryption(ctx); err != nil {
return err
}
}
// write xrefstream if using xrefstream only.
if ctx.Encrypt != nil && ctx.EncKey != nil && !ctx.Read.UsingXRefStreams {
ctx.WriteObjectStream = false
ctx.WriteXRefStream = false
}
return nil
}
func writeXRef(ctx *model.Context) error {
if ctx.WriteXRefStream {
// Write cross reference stream and generate objectstreams.
return writeXRefStream(ctx)
}
// Write cross reference table section.
return writeXRefTable(ctx)
}
func setFileSizeOfWrittenFile(w *model.WriteContext) error {
if err := w.Flush(); err != nil {
return err
}
// If writing is Writer based then f is nil.
if w.Fp == nil {
return nil
}
fileInfo, err := w.Fp.Stat()
if err != nil {
return err
}
w.FileSize = fileInfo.Size()
return nil
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/deeao/golang-pdfcpu.git
git@gitee.com:deeao/golang-pdfcpu.git
deeao
golang-pdfcpu
golang-pdfcpu
v1.0.1

搜索帮助

0d507c66 1850385 C8b1a773 1850385