github.com/pdfcpu/pdfcpu@v0.11.1/pkg/pdfcpu/write.go (about)

     1  /*
     2  Copyright 2018 The pdfcpu Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  	http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package pdfcpu
    18  
    19  import (
    20  	"bufio"
    21  	"bytes"
    22  	"encoding/hex"
    23  	"fmt"
    24  	"os"
    25  	"path/filepath"
    26  	"sort"
    27  	"strings"
    28  
    29  	"github.com/pdfcpu/pdfcpu/pkg/filter"
    30  	"github.com/pdfcpu/pdfcpu/pkg/log"
    31  	"github.com/pdfcpu/pdfcpu/pkg/pdfcpu/model"
    32  	"github.com/pdfcpu/pdfcpu/pkg/pdfcpu/types"
    33  	"github.com/pkg/errors"
    34  )
    35  
    36  func writeObjects(ctx *model.Context) error {
    37  	// Write root object(aka the document catalog) and page tree.
    38  	if err := writeRootObject(ctx); err != nil {
    39  		return err
    40  	}
    41  
    42  	if log.WriteEnabled() {
    43  		log.Write.Printf("offset after writeRootObject: %d\n", ctx.Write.Offset)
    44  	}
    45  
    46  	// Write document information dictionary.
    47  	if err := writeDocumentInfoDict(ctx); err != nil {
    48  		return err
    49  	}
    50  
    51  	if log.WriteEnabled() {
    52  		log.Write.Printf("offset after writeInfoObject: %d\n", ctx.Write.Offset)
    53  	}
    54  
    55  	// Write offspec additional streams as declared in pdf trailer.
    56  	if err := writeAdditionalStreams(ctx); err != nil {
    57  		return err
    58  	}
    59  
    60  	return writeEncryptDict(ctx)
    61  }
    62  
    63  // WriteContext generates a PDF file for the cross reference table contained in Context.
    64  func WriteContext(ctx *model.Context) (err error) {
    65  	// Create a writer for dirname and filename if not already supplied.
    66  	if ctx.Write.Writer == nil {
    67  
    68  		fileName := filepath.Join(ctx.Write.DirName, ctx.Write.FileName)
    69  		if log.CLIEnabled() {
    70  			log.CLI.Printf("writing to %s\n", fileName)
    71  		}
    72  
    73  		file, err := os.Create(fileName)
    74  		if err != nil {
    75  			return errors.Wrapf(err, "can't create %s\n%s", fileName, err)
    76  		}
    77  
    78  		ctx.Write.Writer = bufio.NewWriter(file)
    79  
    80  		defer func() {
    81  
    82  			// The underlying bufio.Writer has already been flushed.
    83  
    84  			// Processing error takes precedence.
    85  			if err != nil {
    86  				file.Close()
    87  				return
    88  			}
    89  
    90  			// Do not miss out on closing errors.
    91  			err = file.Close()
    92  
    93  		}()
    94  
    95  	}
    96  
    97  	if err = prepareContextForWriting(ctx); err != nil {
    98  		return err
    99  	}
   100  
   101  	// if exists metadata, update from info dict
   102  	// else if v2 create from scratch
   103  	// else nothing just write info dict
   104  
   105  	// We support PDF Collections (since V1.7) for file attachments
   106  	v := model.V17
   107  
   108  	if ctx.XRefTable.Version() == model.V20 {
   109  		v = model.V20
   110  	}
   111  
   112  	if err = writeHeader(ctx.Write, v); err != nil {
   113  		return err
   114  	}
   115  
   116  	// Ensure there is no root version.
   117  	if ctx.RootVersion != nil {
   118  		ctx.RootDict.Delete("Version")
   119  	}
   120  
   121  	if log.WriteEnabled() {
   122  		log.Write.Printf("offset after writeHeader: %d\n", ctx.Write.Offset)
   123  	}
   124  
   125  	if err := writeObjects(ctx); err != nil {
   126  		return err
   127  	}
   128  
   129  	// Mark redundant objects as free.
   130  	// eg. duplicate resources, compressed objects, linearization dicts..
   131  	deleteRedundantObjects(ctx)
   132  
   133  	if err = writeXRef(ctx); err != nil {
   134  		return err
   135  	}
   136  
   137  	// Write pdf trailer.
   138  	if err = writeTrailer(ctx.Write); err != nil {
   139  		return err
   140  	}
   141  
   142  	if err = setFileSizeOfWrittenFile(ctx.Write); err != nil {
   143  		return err
   144  	}
   145  
   146  	if ctx.Read != nil {
   147  		ctx.Write.BinaryImageSize = ctx.Read.BinaryImageSize
   148  		ctx.Write.BinaryFontSize = ctx.Read.BinaryFontSize
   149  		logWriteStats(ctx)
   150  	}
   151  
   152  	return nil
   153  }
   154  
   155  // WriteIncrement writes a PDF increment..
   156  func WriteIncrement(ctx *model.Context) error {
   157  	// Write all modified objects that are part of this increment.
   158  	for _, i := range ctx.Write.ObjNrs {
   159  		if err := writeFlatObject(ctx, i); err != nil {
   160  			return err
   161  		}
   162  	}
   163  
   164  	if err := writeXRef(ctx); err != nil {
   165  		return err
   166  	}
   167  
   168  	return writeTrailer(ctx.Write)
   169  }
   170  
   171  func prepareContextForWriting(ctx *model.Context) error {
   172  	if err := ensureInfoDictAndFileID(ctx); err != nil {
   173  		return err
   174  	}
   175  
   176  	return handleEncryption(ctx)
   177  }
   178  
   179  func writeAdditionalStreams(ctx *model.Context) error {
   180  	if ctx.AdditionalStreams == nil {
   181  		return nil
   182  	}
   183  
   184  	if _, _, err := writeDeepObject(ctx, ctx.AdditionalStreams); err != nil {
   185  		return err
   186  	}
   187  
   188  	return nil
   189  }
   190  
   191  func ensureFileID(ctx *model.Context) error {
   192  	fid, err := fileID(ctx)
   193  	if err != nil {
   194  		return err
   195  	}
   196  
   197  	if ctx.ID == nil {
   198  		// Ensure ctx.ID
   199  		ctx.ID = types.Array{fid, fid}
   200  		return nil
   201  	}
   202  
   203  	// Update ctx.ID
   204  	a := ctx.ID
   205  	if len(a) != 2 {
   206  		return errors.New("pdfcpu: ID must be an array with 2 elements")
   207  	}
   208  
   209  	a[1] = fid
   210  
   211  	return nil
   212  }
   213  
   214  func ensureInfoDictAndFileID(ctx *model.Context) error {
   215  	if ctx.XRefTable.Version() < model.V20 {
   216  		if err := ensureInfoDict(ctx); err != nil {
   217  			return err
   218  		}
   219  	}
   220  
   221  	return ensureFileID(ctx)
   222  }
   223  
   224  // Write root entry to disk.
   225  func writeRootEntry(ctx *model.Context, d types.Dict, dictName, entryName string, statsAttr int) error {
   226  	o, err := writeEntry(ctx, d, dictName, entryName)
   227  	if err != nil {
   228  		return err
   229  	}
   230  
   231  	if o != nil {
   232  		ctx.Stats.AddRootAttr(statsAttr)
   233  	}
   234  
   235  	return nil
   236  }
   237  
   238  // Write root entry to object stream.
   239  func writeRootEntryToObjStream(ctx *model.Context, d types.Dict, dictName, entryName string, statsAttr int) error {
   240  	ctx.Write.WriteToObjectStream = true
   241  
   242  	if err := writeRootEntry(ctx, d, dictName, entryName, statsAttr); err != nil {
   243  		return err
   244  	}
   245  
   246  	return stopObjectStream(ctx)
   247  }
   248  
   249  // Write page tree.
   250  func writePages(ctx *model.Context, rootDict types.Dict) error {
   251  	// Page tree root (the top "Pages" dict) must be indirect reference.
   252  	indRef := rootDict.IndirectRefEntry("Pages")
   253  	if indRef == nil {
   254  		return errors.New("pdfcpu: writePages: missing indirect obj for pages dict")
   255  	}
   256  
   257  	// Embed all page tree objects into objects stream.
   258  	ctx.Write.WriteToObjectStream = true
   259  
   260  	// Write page tree.
   261  	p := 0
   262  	if _, _, err := writePagesDict(ctx, indRef, &p); err != nil {
   263  		return err
   264  	}
   265  
   266  	return stopObjectStream(ctx)
   267  }
   268  
   269  func writeRootAttrsBatch1(ctx *model.Context, d types.Dict, dictName string) error {
   270  
   271  	if err := writeAcroFormRootEntry(ctx, d, dictName); err != nil {
   272  		return err
   273  	}
   274  
   275  	for _, e := range []struct {
   276  		entryName string
   277  		statsAttr int
   278  	}{
   279  		{"Extensions", model.RootExtensions},
   280  		{"PageLabels", model.RootPageLabels},
   281  		{"Names", model.RootNames},
   282  		{"Dests", model.RootDests},
   283  		{"ViewerPreferences", model.RootViewerPrefs},
   284  		{"PageLayout", model.RootPageLayout},
   285  		{"PageMode", model.RootPageMode},
   286  		{"Outlines", model.RootOutlines},
   287  		{"Threads", model.RootThreads},
   288  		{"OpenAction", model.RootOpenAction},
   289  		{"AA", model.RootAA},
   290  		{"URI", model.RootURI},
   291  		//{"AcroForm", model.RootAcroForm},
   292  		{"Metadata", model.RootMetadata},
   293  	} {
   294  		if err := writeRootEntry(ctx, d, dictName, e.entryName, e.statsAttr); err != nil {
   295  			return err
   296  		}
   297  	}
   298  
   299  	return nil
   300  }
   301  
   302  func writeRootAttrsBatch2(ctx *model.Context, d types.Dict, dictName string) error {
   303  	for _, e := range []struct {
   304  		entryName string
   305  		statsAttr int
   306  	}{
   307  		{"MarkInfo", model.RootMarkInfo},
   308  		{"Lang", model.RootLang},
   309  		{"SpiderInfo", model.RootSpiderInfo},
   310  		{"OutputIntents", model.RootOutputIntents},
   311  		{"PieceInfo", model.RootPieceInfo},
   312  		{"OCProperties", model.RootOCProperties},
   313  		{"Perms", model.RootPerms},
   314  		{"Legal", model.RootLegal},
   315  		{"Requirements", model.RootRequirements},
   316  		{"Collection", model.RootCollection},
   317  		{"NeedsRendering", model.RootNeedsRendering},
   318  	} {
   319  		if err := writeRootEntry(ctx, d, dictName, e.entryName, e.statsAttr); err != nil {
   320  			return err
   321  		}
   322  	}
   323  
   324  	return nil
   325  }
   326  
   327  func writeRootObject(ctx *model.Context) error {
   328  	// => 7.7.2 Document Catalog
   329  
   330  	xRefTable := ctx.XRefTable
   331  	catalog := *xRefTable.Root
   332  	objNumber := int(catalog.ObjectNumber)
   333  	genNumber := int(catalog.GenerationNumber)
   334  
   335  	if log.WriteEnabled() {
   336  		log.Write.Printf("*** writeRootObject: begin offset=%d *** %s\n", ctx.Write.Offset, catalog)
   337  	}
   338  
   339  	// Ensure corresponding and accurate name tree object graphs.
   340  	if !ctx.ApplyReducedFeatureSet() {
   341  		if err := ctx.BindNameTrees(); err != nil {
   342  			return err
   343  		}
   344  	}
   345  
   346  	d, err := xRefTable.DereferenceDict(catalog)
   347  	if err != nil {
   348  		return err
   349  	}
   350  
   351  	if d == nil {
   352  		return errors.Errorf("pdfcpu: writeRootObject: unable to dereference root dict")
   353  	}
   354  
   355  	dictName := "rootDict"
   356  
   357  	if ctx.ApplyReducedFeatureSet() {
   358  		log.Write.Println("writeRootObject - reducedFeatureSet:exclude complex entries.")
   359  		d.Delete("Names")
   360  		d.Delete("Dests")
   361  		d.Delete("Outlines")
   362  		d.Delete("OpenAction")
   363  		d.Delete("StructTreeRoot")
   364  		d.Delete("OCProperties")
   365  	}
   366  
   367  	if err = writeDictObject(ctx, objNumber, genNumber, d); err != nil {
   368  		return err
   369  	}
   370  
   371  	if log.WriteEnabled() {
   372  		log.Write.Printf("writeRootObject: %s\n", d)
   373  		log.Write.Printf("writeRootObject: new offset after rootDict = %d\n", ctx.Write.Offset)
   374  	}
   375  
   376  	if err = writeRootEntry(ctx, d, dictName, "Version", model.RootVersion); err != nil {
   377  		return err
   378  	}
   379  
   380  	if err = writePages(ctx, d); err != nil {
   381  		return err
   382  	}
   383  
   384  	if err := writeRootAttrsBatch1(ctx, d, dictName); err != nil {
   385  		return err
   386  	}
   387  
   388  	if err = writeRootEntryToObjStream(ctx, d, dictName, "StructTreeRoot", model.RootStructTreeRoot); err != nil {
   389  		return err
   390  	}
   391  
   392  	if err := writeRootAttrsBatch2(ctx, d, dictName); err != nil {
   393  		return err
   394  	}
   395  
   396  	if log.WriteEnabled() {
   397  		log.Write.Printf("*** writeRootObject: end offset=%d ***\n", ctx.Write.Offset)
   398  	}
   399  
   400  	return nil
   401  }
   402  
   403  func writeTrailerDict(ctx *model.Context) error {
   404  	if log.WriteEnabled() {
   405  		log.Write.Printf("writeTrailerDict begin\n")
   406  	}
   407  
   408  	w := ctx.Write
   409  	xRefTable := ctx.XRefTable
   410  
   411  	if _, err := w.WriteString("trailer"); err != nil {
   412  		return err
   413  	}
   414  
   415  	if err := w.WriteEol(); err != nil {
   416  		return err
   417  	}
   418  
   419  	d := types.NewDict()
   420  	d.Insert("Size", types.Integer(*xRefTable.Size))
   421  	d.Insert("Root", *xRefTable.Root)
   422  
   423  	if xRefTable.Info != nil {
   424  		d.Insert("Info", *xRefTable.Info)
   425  	}
   426  
   427  	if ctx.Encrypt != nil && ctx.EncKey != nil {
   428  		d.Insert("Encrypt", *ctx.Encrypt)
   429  	}
   430  
   431  	if xRefTable.ID != nil {
   432  		d.Insert("ID", xRefTable.ID)
   433  	}
   434  
   435  	if ctx.Write.Increment {
   436  		d.Insert("Prev", types.Integer(*ctx.Write.OffsetPrevXRef))
   437  	}
   438  
   439  	if _, err := w.WriteString(d.PDFString()); err != nil {
   440  		return err
   441  	}
   442  
   443  	if log.WriteEnabled() {
   444  		log.Write.Printf("writeTrailerDict end\n")
   445  	}
   446  
   447  	return nil
   448  }
   449  
   450  func writeXRefSubsection(ctx *model.Context, start int, size int) error {
   451  	if log.WriteEnabled() {
   452  		log.Write.Printf("writeXRefSubsection: start=%d size=%d\n", start, size)
   453  	}
   454  
   455  	w := ctx.Write
   456  
   457  	if _, err := w.WriteString(fmt.Sprintf("%d %d%s", start, size, w.Eol)); err != nil {
   458  		return err
   459  	}
   460  
   461  	var lines []string
   462  
   463  	for i := start; i < start+size; i++ {
   464  
   465  		entry := ctx.XRefTable.Table[i]
   466  
   467  		if entry.Compressed {
   468  			return errors.New("pdfcpu: writeXRefSubsection: compressed entries present")
   469  		}
   470  
   471  		var s string
   472  
   473  		if entry.Free {
   474  			s = fmt.Sprintf("%010d %05d f%2s", *entry.Offset, *entry.Generation, w.Eol)
   475  		} else {
   476  			var off int64
   477  			writeOffset, found := ctx.Write.Table[i]
   478  			if found {
   479  				off = writeOffset
   480  			}
   481  			s = fmt.Sprintf("%010d %05d n%2s", off, *entry.Generation, w.Eol)
   482  		}
   483  
   484  		lines = append(lines, fmt.Sprintf("%d: %s", i, s))
   485  
   486  		if _, err := w.WriteString(s); err != nil {
   487  			return err
   488  		}
   489  	}
   490  
   491  	if log.WriteEnabled() {
   492  		log.Write.Printf("\n%s\n", strings.Join(lines, ""))
   493  		log.Write.Printf("writeXRefSubsection: end\n")
   494  	}
   495  
   496  	return nil
   497  }
   498  
   499  func deleteRedundantObject(ctx *model.Context, objNr int) {
   500  	if len(ctx.Write.SelectedPages) == 0 &&
   501  		(ctx.Optimize.IsDuplicateFontObject(objNr) || ctx.Optimize.IsDuplicateImageObject(objNr)) {
   502  		ctx.FreeObject(objNr)
   503  	}
   504  
   505  	if ctx.IsLinearizationObject(objNr) || ctx.Optimize.IsDuplicateInfoObject(objNr) ||
   506  		ctx.Read.IsObjectStreamObject(objNr) {
   507  		ctx.FreeObject(objNr)
   508  	}
   509  
   510  }
   511  
   512  func detectLinearizationObjs(xRefTable *model.XRefTable, entry *model.XRefTableEntry, i int) {
   513  	if _, ok := entry.Object.(types.StreamDict); ok {
   514  
   515  		if *entry.Offset == *xRefTable.OffsetPrimaryHintTable {
   516  			xRefTable.LinearizationObjs[i] = true
   517  			if log.WriteEnabled() {
   518  				log.Write.Printf("detectLinearizationObjs: primaryHintTable at obj #%d\n", i)
   519  			}
   520  		}
   521  
   522  		if xRefTable.OffsetOverflowHintTable != nil &&
   523  			*entry.Offset == *xRefTable.OffsetOverflowHintTable {
   524  			xRefTable.LinearizationObjs[i] = true
   525  			if log.WriteEnabled() {
   526  				log.Write.Printf("detectLinearizationObjs: overflowHintTable at obj #%d\n", i)
   527  			}
   528  		}
   529  
   530  	}
   531  }
   532  
   533  func deleteRedundantObjects(ctx *model.Context) {
   534  	if ctx.Optimize == nil {
   535  		return
   536  	}
   537  
   538  	xRefTable := ctx.XRefTable
   539  
   540  	if log.WriteEnabled() {
   541  		log.Write.Printf("deleteRedundantObjects begin: Size=%d\n", *xRefTable.Size)
   542  	}
   543  
   544  	for i := 0; i < *xRefTable.Size; i++ {
   545  
   546  		// Missing object remains missing.
   547  		entry, found := xRefTable.Find(i)
   548  		if !found {
   549  			continue
   550  		}
   551  
   552  		// Free object
   553  		if entry.Free {
   554  			continue
   555  		}
   556  
   557  		// Object written
   558  		if ctx.Write.HasWriteOffset(i) {
   559  			// Resources may be cross referenced from different objects
   560  			// eg. font descriptors may be shared by different font dicts.
   561  			// Try to remove this object from the list of the potential duplicate objects.
   562  			if log.WriteEnabled() {
   563  				log.Write.Printf("deleteRedundantObjects: remove duplicate obj #%d\n", i)
   564  			}
   565  			delete(ctx.Optimize.DuplicateFontObjs, i)
   566  			delete(ctx.Optimize.DuplicateImageObjs, i)
   567  			delete(ctx.Optimize.DuplicateInfoObjects, i)
   568  			continue
   569  		}
   570  
   571  		// Object not written
   572  
   573  		if ctx.Read.Linearized && entry.Offset != nil {
   574  			// This block applies to pre existing objects only.
   575  			// Since there is no type entry for stream dicts associated with linearization dicts
   576  			// we have to check every StreamDict that has not been written.
   577  			detectLinearizationObjs(xRefTable, entry, i)
   578  		}
   579  
   580  		deleteRedundantObject(ctx, i)
   581  	}
   582  
   583  	if log.WriteEnabled() {
   584  		log.Write.Println("deleteRedundantObjects end")
   585  	}
   586  }
   587  
   588  func sortedWritableKeys(ctx *model.Context) []int {
   589  	var keys []int
   590  
   591  	for i, e := range ctx.Table {
   592  		if !ctx.Write.Increment && e.Free || ctx.Write.HasWriteOffset(i) {
   593  			keys = append(keys, i)
   594  		}
   595  	}
   596  
   597  	sort.Ints(keys)
   598  
   599  	return keys
   600  }
   601  
   602  // After inserting the last object write the cross reference table to disk.
   603  func writeXRefTable(ctx *model.Context) error {
   604  	keys := sortedWritableKeys(ctx)
   605  
   606  	objCount := len(keys)
   607  	if log.WriteEnabled() {
   608  		log.Write.Printf("xref has %d entries\n", objCount)
   609  	}
   610  
   611  	if _, err := ctx.Write.WriteString("xref"); err != nil {
   612  		return err
   613  	}
   614  
   615  	if err := ctx.Write.WriteEol(); err != nil {
   616  		return err
   617  	}
   618  
   619  	start := keys[0]
   620  	size := 1
   621  
   622  	for i := 1; i < len(keys); i++ {
   623  
   624  		if keys[i]-keys[i-1] > 1 {
   625  
   626  			if err := writeXRefSubsection(ctx, start, size); err != nil {
   627  				return err
   628  			}
   629  
   630  			start = keys[i]
   631  			size = 1
   632  			continue
   633  		}
   634  
   635  		size++
   636  	}
   637  
   638  	if err := writeXRefSubsection(ctx, start, size); err != nil {
   639  		return err
   640  	}
   641  
   642  	if err := writeTrailerDict(ctx); err != nil {
   643  		return err
   644  	}
   645  
   646  	if err := ctx.Write.WriteEol(); err != nil {
   647  		return err
   648  	}
   649  
   650  	if _, err := ctx.Write.WriteString("startxref"); err != nil {
   651  		return err
   652  	}
   653  
   654  	if err := ctx.Write.WriteEol(); err != nil {
   655  		return err
   656  	}
   657  
   658  	if _, err := ctx.Write.WriteString(fmt.Sprintf("%d", ctx.Write.Offset)); err != nil {
   659  		return err
   660  	}
   661  
   662  	return ctx.Write.WriteEol()
   663  }
   664  
   665  // int64ToBuf returns a byte slice with length byteCount representing integer i.
   666  func int64ToBuf(i int64, byteCount int) (buf []byte) {
   667  	j := 0
   668  	var b []byte
   669  
   670  	for k := i; k > 0; {
   671  		b = append(b, byte(k&0xff))
   672  		k >>= 8
   673  		j++
   674  	}
   675  
   676  	// Swap byte order
   677  	for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
   678  		b[i], b[j] = b[j], b[i]
   679  	}
   680  
   681  	if j < byteCount {
   682  		buf = append(bytes.Repeat([]byte{0}, byteCount-j), b...)
   683  	} else {
   684  		buf = b
   685  	}
   686  
   687  	return
   688  }
   689  
   690  func createXRefStream(ctx *model.Context, i1, i2, i3 int, objNrs []int) ([]byte, *types.Array, error) {
   691  	if log.WriteEnabled() {
   692  		log.Write.Println("createXRefStream begin")
   693  	}
   694  
   695  	xRefTable := ctx.XRefTable
   696  
   697  	var (
   698  		buf []byte
   699  		a   types.Array
   700  	)
   701  
   702  	objCount := len(objNrs)
   703  	if log.WriteEnabled() {
   704  		log.Write.Printf("createXRefStream: xref has %d entries\n", objCount)
   705  	}
   706  
   707  	start := objNrs[0]
   708  	size := 0
   709  
   710  	for i := 0; i < len(objNrs); i++ {
   711  
   712  		j := objNrs[i]
   713  		entry := xRefTable.Table[j]
   714  		var s1, s2, s3 []byte
   715  
   716  		if entry.Free {
   717  
   718  			// unused
   719  			if log.WriteEnabled() {
   720  				log.Write.Printf("createXRefStream: unused i=%d nextFreeAt:%d gen:%d\n", j, int(*entry.Offset), int(*entry.Generation))
   721  			}
   722  
   723  			s1 = int64ToBuf(0, i1)
   724  			s2 = int64ToBuf(*entry.Offset, i2)
   725  			s3 = int64ToBuf(int64(*entry.Generation), i3)
   726  
   727  		} else if entry.Compressed {
   728  
   729  			// in use, compressed into object stream
   730  			if log.WriteEnabled() {
   731  				log.Write.Printf("createXRefStream: compressed i=%d at objstr %d[%d]\n", j, int(*entry.ObjectStream), int(*entry.ObjectStreamInd))
   732  			}
   733  
   734  			s1 = int64ToBuf(2, i1)
   735  			s2 = int64ToBuf(int64(*entry.ObjectStream), i2)
   736  			s3 = int64ToBuf(int64(*entry.ObjectStreamInd), i3)
   737  
   738  		} else {
   739  
   740  			off, found := ctx.Write.Table[j]
   741  			if !found {
   742  				return nil, nil, errors.Errorf("pdfcpu: createXRefStream: missing write offset for obj #%d\n", i)
   743  			}
   744  
   745  			// in use, uncompressed
   746  			if log.WriteEnabled() {
   747  				log.Write.Printf("createXRefStream: used i=%d offset:%d gen:%d\n", j, int(off), int(*entry.Generation))
   748  			}
   749  
   750  			s1 = int64ToBuf(1, i1)
   751  			s2 = int64ToBuf(off, i2)
   752  			s3 = int64ToBuf(int64(*entry.Generation), i3)
   753  
   754  		}
   755  
   756  		if log.WriteEnabled() {
   757  			log.Write.Printf("createXRefStream: written: %x %x %x \n", s1, s2, s3)
   758  		}
   759  
   760  		buf = append(buf, s1...)
   761  		buf = append(buf, s2...)
   762  		buf = append(buf, s3...)
   763  
   764  		if i > 0 && (objNrs[i]-objNrs[i-1] > 1) {
   765  
   766  			a = append(a, types.Integer(start))
   767  			a = append(a, types.Integer(size))
   768  
   769  			start = objNrs[i]
   770  			size = 1
   771  			continue
   772  		}
   773  
   774  		size++
   775  	}
   776  
   777  	a = append(a, types.Integer(start))
   778  	a = append(a, types.Integer(size))
   779  
   780  	if log.WriteEnabled() {
   781  		log.Write.Println("createXRefStream end")
   782  	}
   783  
   784  	return buf, &a, nil
   785  }
   786  
   787  // NewXRefStreamDict creates a new PDFXRefStreamDict object.
   788  func newXRefStreamDict(ctx *model.Context) *types.XRefStreamDict {
   789  	sd := types.StreamDict{Dict: types.NewDict()}
   790  	sd.Insert("Type", types.Name("XRef"))
   791  	sd.Insert("Filter", types.Name(filter.Flate))
   792  	sd.FilterPipeline = []types.PDFFilter{{Name: filter.Flate, DecodeParms: nil}}
   793  	sd.Insert("Root", *ctx.Root)
   794  	if ctx.Info != nil {
   795  		sd.Insert("Info", *ctx.Info)
   796  	}
   797  	if ctx.ID != nil {
   798  		sd.Insert("ID", ctx.ID)
   799  	}
   800  	if ctx.Encrypt != nil && ctx.EncKey != nil {
   801  		sd.Insert("Encrypt", *ctx.Encrypt)
   802  	}
   803  	if ctx.Write.Increment {
   804  		sd.Insert("Prev", types.Integer(*ctx.Write.OffsetPrevXRef))
   805  	}
   806  	return &types.XRefStreamDict{StreamDict: sd}
   807  }
   808  
   809  func writeXRefStream(ctx *model.Context) error {
   810  	if log.WriteEnabled() {
   811  		log.Write.Println("writeXRefStream begin")
   812  	}
   813  
   814  	xRefTable := ctx.XRefTable
   815  	xRefStreamDict := newXRefStreamDict(ctx)
   816  	xRefTableEntry := model.NewXRefTableEntryGen0(*xRefStreamDict)
   817  
   818  	// Reuse free objects (including recycled objects from this run).
   819  	objNumber, err := xRefTable.InsertAndUseRecycled(*xRefTableEntry)
   820  	if err != nil {
   821  		return err
   822  	}
   823  
   824  	xRefStreamDict.Insert("Size", types.Integer(*xRefTable.Size))
   825  
   826  	// Include xref stream dict obj within xref stream dict.
   827  	offset := ctx.Write.Offset
   828  	ctx.Write.SetWriteOffset(objNumber)
   829  
   830  	i2Base := int64(*ctx.Size)
   831  	if offset > i2Base {
   832  		i2Base = offset
   833  	}
   834  
   835  	i1 := 1 // 0, 1 or 2 always fit into 1 byte.
   836  
   837  	i2 := func(i int64) (byteCount int) {
   838  		for i > 0 {
   839  			i >>= 8
   840  			byteCount++
   841  		}
   842  		return byteCount
   843  	}(i2Base)
   844  
   845  	i3 := 2 // scale for max objectstream index <= 0x ff ff
   846  
   847  	wArr := types.Array{types.Integer(i1), types.Integer(i2), types.Integer(i3)}
   848  	xRefStreamDict.Insert("W", wArr)
   849  
   850  	// Generate xRefStreamDict data = xref entries -> xRefStreamDict.Content
   851  	objNrs := sortedWritableKeys(ctx)
   852  	content, indArr, err := createXRefStream(ctx, i1, i2, i3, objNrs)
   853  	if err != nil {
   854  		return err
   855  	}
   856  
   857  	xRefStreamDict.Content = content
   858  	xRefStreamDict.Insert("Index", *indArr)
   859  
   860  	// Encode xRefStreamDict.Content -> xRefStreamDict.Raw
   861  	if err = xRefStreamDict.StreamDict.Encode(); err != nil {
   862  		return err
   863  	}
   864  
   865  	if log.WriteEnabled() {
   866  		log.Write.Printf("writeXRefStream: xRefStreamDict: %s\n", xRefStreamDict)
   867  	}
   868  
   869  	if err = writeStreamDictObject(ctx, objNumber, 0, xRefStreamDict.StreamDict); err != nil {
   870  		return err
   871  	}
   872  
   873  	w := ctx.Write
   874  
   875  	if _, err = w.WriteString("startxref"); err != nil {
   876  		return err
   877  	}
   878  
   879  	if err = w.WriteEol(); err != nil {
   880  		return err
   881  	}
   882  
   883  	if _, err = w.WriteString(fmt.Sprintf("%d", offset)); err != nil {
   884  		return err
   885  	}
   886  
   887  	if err = w.WriteEol(); err != nil {
   888  		return err
   889  	}
   890  
   891  	if log.WriteEnabled() {
   892  		log.Write.Println("writeXRefStream end")
   893  	}
   894  
   895  	return nil
   896  }
   897  
   898  func writeEncryptDict(ctx *model.Context) error {
   899  	// Bail out unless we really have to write encrypted.
   900  	if ctx.Encrypt == nil || ctx.EncKey == nil {
   901  		return nil
   902  	}
   903  
   904  	indRef := *ctx.Encrypt
   905  	objNumber := int(indRef.ObjectNumber)
   906  	genNumber := int(indRef.GenerationNumber)
   907  
   908  	d, err := ctx.DereferenceDict(indRef)
   909  	if err != nil {
   910  		return err
   911  	}
   912  
   913  	return writeObject(ctx, objNumber, genNumber, d.PDFString())
   914  }
   915  
   916  func setupEncryption(ctx *model.Context) error {
   917  	var err error
   918  
   919  	if ok := validateAlgorithm(ctx); !ok {
   920  		return errors.New("pdfcpu: unsupported encryption algorithm (PDF 2.0 assumes AES/256)")
   921  	}
   922  
   923  	d := newEncryptDict(
   924  		ctx.XRefTable.Version(),
   925  		ctx.EncryptUsingAES,
   926  		ctx.EncryptKeyLength,
   927  		int16(ctx.Permissions),
   928  	)
   929  
   930  	if ctx.E, err = supportedEncryption(ctx, d); err != nil {
   931  		return err
   932  	}
   933  
   934  	if ctx.ID == nil {
   935  		return errors.New("pdfcpu: encrypt: missing ID")
   936  	}
   937  
   938  	if ctx.E.ID, err = ctx.IDFirstElement(); err != nil {
   939  		return err
   940  	}
   941  
   942  	if err = calcOAndU(ctx, d); err != nil {
   943  		return err
   944  	}
   945  
   946  	if err = writePermissions(ctx, d); err != nil {
   947  		return err
   948  	}
   949  
   950  	xRefTableEntry := model.NewXRefTableEntryGen0(d)
   951  
   952  	// Reuse free objects (including recycled objects from this run).
   953  	objNumber, err := ctx.InsertAndUseRecycled(*xRefTableEntry)
   954  	if err != nil {
   955  		return err
   956  	}
   957  
   958  	ctx.Encrypt = types.NewIndirectRef(objNumber, 0)
   959  
   960  	return nil
   961  }
   962  
   963  func updateEncryption(ctx *model.Context) error {
   964  	if ctx.Encrypt == nil {
   965  		return errors.New("pdfcpu: This file is not encrypted - nothing written.")
   966  	}
   967  
   968  	d, err := ctx.EncryptDict()
   969  	if err != nil {
   970  		return err
   971  	}
   972  
   973  	if ctx.Cmd == model.SETPERMISSIONS {
   974  		//fmt.Printf("updating permissions to: %v\n", ctx.UserAccessPermissions)
   975  		ctx.E.P = int(ctx.Permissions)
   976  		d.Update("P", types.Integer(ctx.E.P))
   977  		// and moving on, U is dependent on P
   978  	}
   979  
   980  	// ctx.Cmd == CHANGEUPW or CHANGE OPW
   981  
   982  	if ctx.UserPWNew != nil {
   983  		//fmt.Printf("change upw from <%s> to <%s>\n", ctx.UserPW, *ctx.UserPWNew)
   984  		ctx.UserPW = *ctx.UserPWNew
   985  	}
   986  
   987  	if ctx.OwnerPWNew != nil {
   988  		//fmt.Printf("change opw from <%s> to <%s>\n", ctx.OwnerPW, *ctx.OwnerPWNew)
   989  		ctx.OwnerPW = *ctx.OwnerPWNew
   990  	}
   991  
   992  	if ctx.E.R == 5 || ctx.E.R == 6 {
   993  
   994  		if err = calcOAndU(ctx, d); err != nil {
   995  			return err
   996  		}
   997  
   998  		// Calc Perms for rev 5, 6.
   999  		return writePermissions(ctx, d)
  1000  	}
  1001  
  1002  	//fmt.Printf("opw before: length:%d <%s>\n", len(ctx.E.O), ctx.E.O)
  1003  	if ctx.E.O, err = o(ctx); err != nil {
  1004  		return err
  1005  	}
  1006  	//fmt.Printf("opw after: length:%d <%s> %0X\n", len(ctx.E.O), ctx.E.O, ctx.E.O)
  1007  	d.Update("O", types.HexLiteral(hex.EncodeToString(ctx.E.O)))
  1008  
  1009  	//fmt.Printf("upw before: length:%d <%s>\n", len(ctx.E.U), ctx.E.U)
  1010  	if ctx.E.U, ctx.EncKey, err = u(ctx); err != nil {
  1011  		return err
  1012  	}
  1013  	//fmt.Printf("upw after: length:%d <%s> %0X\n", len(ctx.E.U), ctx.E.U, ctx.E.U)
  1014  	//fmt.Printf("encKey = %0X\n", ctx.EncKey)
  1015  	d.Update("U", types.HexLiteral(hex.EncodeToString(ctx.E.U)))
  1016  
  1017  	return nil
  1018  }
  1019  
  1020  func handleEncryption(ctx *model.Context) error {
  1021  
  1022  	if ctx.Cmd == model.ENCRYPT || ctx.Cmd == model.DECRYPT {
  1023  
  1024  		if ctx.Cmd == model.DECRYPT {
  1025  
  1026  			// Remove encryption.
  1027  			ctx.EncKey = nil
  1028  
  1029  		} else {
  1030  
  1031  			if err := setupEncryption(ctx); err != nil {
  1032  				return err
  1033  			}
  1034  
  1035  			alg := "RC4"
  1036  			if ctx.EncryptUsingAES {
  1037  				alg = "AES"
  1038  			}
  1039  			if log.CLIEnabled() {
  1040  				log.CLI.Printf("using %s-%d\n", alg, ctx.EncryptKeyLength)
  1041  			}
  1042  		}
  1043  
  1044  	} else if ctx.UserPWNew != nil || ctx.OwnerPWNew != nil || ctx.Cmd == model.SETPERMISSIONS {
  1045  
  1046  		if err := updateEncryption(ctx); err != nil {
  1047  			return err
  1048  		}
  1049  
  1050  	}
  1051  
  1052  	// write xrefstream if using xrefstream only.
  1053  	if ctx.Encrypt != nil && ctx.EncKey != nil && !ctx.Read.UsingXRefStreams {
  1054  		ctx.WriteObjectStream = false
  1055  		ctx.WriteXRefStream = false
  1056  	}
  1057  
  1058  	return nil
  1059  }
  1060  
  1061  func writeXRef(ctx *model.Context) error {
  1062  	if ctx.WriteXRefStream {
  1063  		// Write cross reference stream and generate objectstreams.
  1064  		return writeXRefStream(ctx)
  1065  	}
  1066  
  1067  	// Write cross reference table section.
  1068  	return writeXRefTable(ctx)
  1069  }
  1070  
  1071  func setFileSizeOfWrittenFile(w *model.WriteContext) error {
  1072  	if err := w.Flush(); err != nil {
  1073  		return err
  1074  	}
  1075  
  1076  	// If writing is Writer based then f is nil.
  1077  	if w.Fp == nil {
  1078  		return nil
  1079  	}
  1080  
  1081  	fileInfo, err := w.Fp.Stat()
  1082  	if err != nil {
  1083  		return err
  1084  	}
  1085  
  1086  	w.FileSize = fileInfo.Size()
  1087  
  1088  	return nil
  1089  }