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 }