github.com/pdfcpu/pdfcpu@v0.11.1/pkg/pdfcpu/merge.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 "fmt" 21 22 "github.com/pdfcpu/pdfcpu/pkg/log" 23 "github.com/pdfcpu/pdfcpu/pkg/pdfcpu/model" 24 "github.com/pdfcpu/pdfcpu/pkg/pdfcpu/types" 25 "github.com/pkg/errors" 26 ) 27 28 func EnsureOutlines(ctx *model.Context, fName string, append bool) error { 29 30 rootDict, err := ctx.Catalog() 31 if err != nil { 32 return err 33 } 34 35 if err := ctx.LocateNameTree("Dests", true); err != nil { 36 return err 37 } 38 39 outlinesDict := types.Dict(map[string]types.Object{"Type": types.Name("Outlines")}) 40 indRef, err := ctx.IndRefForNewObject(outlinesDict) 41 if err != nil { 42 return err 43 } 44 45 first, last, total, visible, err := createOutlineItemDict(ctx, []Bookmark{{PageFrom: 1, Title: fName}}, indRef, nil) 46 if err != nil { 47 return err 48 } 49 50 outlinesDict["First"] = *first 51 outlinesDict["Last"] = *last 52 outlinesDict["Count"] = types.Integer(total + visible) 53 54 if obj, ok := rootDict.Find("Outlines"); ok { 55 if append { 56 return nil 57 } 58 d, err := ctx.DereferenceDict(obj) 59 if err != nil { 60 return err 61 } 62 count := d.IntEntry("Count") 63 c := 0 64 f, l := d.IndirectRefEntry("First"), d.IndirectRefEntry("Last") 65 for ir := f; ir != nil; ir = d.IndirectRefEntry("Next") { 66 d, err = ctx.DereferenceDict(*ir) 67 if err != nil { 68 return err 69 } 70 d["Parent"] = *first 71 c++ 72 } 73 d, err = ctx.DereferenceDict(*first) 74 if err != nil { 75 return err 76 } 77 78 d["First"] = *f 79 d["Last"] = *l 80 if count != nil && *count != 0 { 81 c = *count 82 } 83 d["Count"] = types.Integer(-c) 84 } 85 86 rootDict["Outlines"] = *indRef 87 88 return nil 89 } 90 91 func mergeOutlines(fName string, p int, ctxSrc, ctxDest *model.Context) error { 92 rootDictDest, _ := ctxDest.Catalog() 93 indRef := rootDictDest.IndirectRefEntry("Outlines") 94 outlinesDict, err := ctxDest.DereferenceDict(*indRef) 95 if err != nil { 96 return err 97 } 98 99 first, last, _, _, err := createOutlineItemDict(ctxDest, []Bookmark{{PageFrom: p, Title: fName}}, indRef, nil) 100 if err != nil { 101 return err 102 } 103 104 l := outlinesDict.IndirectRefEntry("Last") 105 outlinesDict["Last"] = *last 106 107 topCount := 0 108 109 count := outlinesDict.IntEntry("Count") 110 if count != nil { 111 topCount = *count 112 } 113 114 topCount++ 115 116 d1, err := ctxDest.DereferenceDict(*l) 117 if err != nil { 118 return err 119 } 120 d1["Next"] = *last 121 122 d2, err := ctxDest.DereferenceDict(*last) 123 if err != nil { 124 return err 125 } 126 d2["Previous"] = *l 127 128 rootDictSource, err := ctxSrc.Catalog() 129 if err != nil { 130 return err 131 } 132 133 if obj, ok := rootDictSource.Find("Outlines"); ok { 134 135 // Integrate existing outlines from ctxSource. 136 137 d, err := ctxDest.DereferenceDict(obj) 138 if err != nil { 139 return err 140 } 141 142 f, l := d.IndirectRefEntry("First"), d.IndirectRefEntry("Last") 143 if f == nil && l == nil { 144 outlinesDict["Count"] = types.Integer(topCount) 145 return nil 146 } 147 148 d2["First"] = *f 149 d2["Last"] = *l 150 151 c := 0 152 153 // Update parents. 154 // TODO Collapse outline dicts. 155 for ir := f; ir != nil; ir = d.IndirectRefEntry("Next") { 156 d, err = ctxDest.DereferenceDict(*ir) 157 if err != nil { 158 return err 159 } 160 d["Parent"] = *first 161 162 i := d.IntEntry("Count") 163 if i != nil && *i > 0 { 164 c += *i 165 } 166 167 c++ 168 } 169 170 d2["Count"] = types.Integer(c) 171 topCount += c 172 } 173 174 outlinesDict["Count"] = types.Integer(topCount) 175 return nil 176 } 177 178 func handleNeedAppearances(ctxSrc *model.Context, dSrc, dDest types.Dict) error { 179 o, found := dSrc.Find("NeedAppearances") 180 if !found || o == nil { 181 return nil 182 } 183 b, err := ctxSrc.DereferenceBoolean(o, model.V10) 184 if err != nil { 185 return err 186 } 187 if b != nil && *b { 188 dDest["NeedAppearances"] = types.Boolean(true) 189 } 190 return nil 191 } 192 193 func handleCO(ctxSrc, ctxDest *model.Context, dSrc, dDest types.Dict) error { 194 o, found := dSrc.Find("CO") 195 if !found { 196 return nil 197 } 198 arrSrc, err := ctxSrc.DereferenceArray(o) 199 if err != nil { 200 return err 201 } 202 o, found = dDest.Find("CO") 203 if !found { 204 dDest["CO"] = arrSrc 205 return nil 206 } 207 arrDest, err := ctxDest.DereferenceArray(o) 208 if err != nil { 209 return err 210 } 211 if len(arrDest) == 0 { 212 dDest["CO"] = arrSrc 213 } else { 214 arrDest = append(arrDest, arrSrc...) 215 dDest["CO"] = arrDest 216 } 217 return nil 218 } 219 220 func handleDR(ctxSrc *model.Context, dSrc, dDest types.Dict) error { 221 o, found := dSrc.Find("DR") 222 if !found { 223 return nil 224 } 225 dSrc, err := ctxSrc.DereferenceDict(o) 226 if err != nil { 227 return err 228 } 229 if len(dSrc) == 0 { 230 return nil 231 } 232 _, found = dDest.Find("DR") 233 if !found { 234 dDest["DR"] = dSrc 235 } 236 return nil 237 } 238 239 func handleDA(ctxSrc *model.Context, dSrc, dDest types.Dict, arrFieldsSrc types.Array) error { 240 // (for each with field type /FT /Tx w/o DA, set DA to default DA) 241 // TODO Walk field tree and inspect terminal fields. 242 243 sSrc := dSrc.StringEntry("DA") 244 if sSrc == nil || len(*sSrc) == 0 { 245 return nil 246 } 247 sDest := dDest.StringEntry("DA") 248 if sDest == nil { 249 dDest["DA"] = types.StringLiteral(*sSrc) 250 return nil 251 } 252 // Push sSrc down to all top level fields of dSource 253 for _, o := range arrFieldsSrc { 254 d, err := ctxSrc.DereferenceDict(o) 255 if err != nil { 256 return err 257 } 258 n := d.NameEntry("FT") 259 if n != nil && *n == "Tx" { 260 _, found := d.Find("DA") 261 if !found { 262 d["DA"] = types.StringLiteral(*sSrc) 263 } 264 } 265 } 266 return nil 267 } 268 269 func handleQ(ctxSrc *model.Context, dSrc, dDest types.Dict, arrFieldsSrc types.Array) error { 270 // (for each with field type /FT /Tx w/o Q, set Q to default Q) 271 // TODO Walk field tree and inspect terminal fields. 272 273 iSrc := dSrc.IntEntry("Q") 274 if iSrc == nil { 275 return nil 276 } 277 iDest := dDest.IntEntry("Q") 278 if iDest == nil { 279 dDest["Q"] = types.Integer(*iSrc) 280 return nil 281 } 282 // Push iSrc down to all top level fields of dSource 283 for _, o := range arrFieldsSrc { 284 d, err := ctxSrc.DereferenceDict(o) 285 if err != nil { 286 return err 287 } 288 n := d.NameEntry("FT") 289 if n != nil && *n == "Tx" { 290 _, found := d.Find("Q") 291 if !found { 292 d["Q"] = types.Integer(*iSrc) 293 } 294 } 295 } 296 return nil 297 } 298 299 func handleFormAttributes(ctxSrc, ctxDest *model.Context, dSrc, dDest types.Dict, arrFieldsSrc types.Array) error { 300 // NeedAppearances: try: set to true only 301 if err := handleNeedAppearances(ctxSrc, dSrc, dDest); err != nil { 302 return err 303 } 304 305 // SigFlags: set bit 1 to true only (SignaturesExist) 306 // set bit 2 to true only (AppendOnly) 307 dDest.Delete("SigFields") 308 309 // CO: add all indrefs 310 if err := handleCO(ctxSrc, ctxDest, dSrc, dDest); err != nil { 311 return err 312 } 313 314 // DR: default resource dict 315 if err := handleDR(ctxSrc, dSrc, dDest); err != nil { 316 return err 317 } 318 319 // DA: default appearance streams for variable text fields 320 if err := handleDA(ctxSrc, dSrc, dDest, arrFieldsSrc); err != nil { 321 return err 322 } 323 324 // Q: left, center, right for variable text fields 325 if err := handleQ(ctxSrc, dSrc, dDest, arrFieldsSrc); err != nil { 326 return err 327 } 328 329 // XFA: ignore 330 delete(dDest, "XFA") 331 332 return nil 333 } 334 335 func rootDicts(ctxSrc, ctxDest *model.Context) (types.Dict, types.Dict, error) { 336 rootDictSource, err := ctxSrc.Catalog() 337 if err != nil { 338 return nil, nil, err 339 } 340 341 rootDictDest, err := ctxDest.Catalog() 342 if err != nil { 343 return nil, nil, err 344 } 345 346 return rootDictSource, rootDictDest, nil 347 } 348 349 func mergeInFields(ctxDest *model.Context, arrFieldsSrc, arrFieldsDest types.Array, dDest types.Dict) error { 350 parentDict := 351 types.Dict(map[string]types.Object{ 352 "Kids": arrFieldsSrc, 353 "T": types.StringLiteral(fmt.Sprintf("%d", len(arrFieldsDest))), 354 }) 355 356 ir, err := ctxDest.IndRefForNewObject(parentDict) 357 if err != nil { 358 return err 359 } 360 361 for _, ir1 := range arrFieldsSrc { 362 d, err := ctxDest.DereferenceDict(ir1) 363 if err != nil { 364 return err 365 } 366 if len(d) == 0 { 367 continue 368 } 369 d["Parent"] = *ir 370 } 371 372 dDest["Fields"] = append(arrFieldsDest, *ir) 373 374 return nil 375 } 376 377 func mergeDests(ctxSource, ctxDest *model.Context) error { 378 rootDictSource, rootDictDest, err := rootDicts(ctxSource, ctxDest) 379 if err != nil { 380 return err 381 } 382 383 o1, found := rootDictSource.Find("Dests") 384 if !found { 385 return nil 386 } 387 388 o2, found := rootDictDest.Find("Dests") 389 if !found { 390 rootDictDest["Dests"] = o1 391 return nil 392 } 393 394 destsSrc, err := ctxSource.DereferenceDict(o1) 395 if err != nil { 396 return err 397 } 398 399 destsDest, err := ctxDest.DereferenceDict(o2) 400 if err != nil { 401 return err 402 } 403 404 // Note: We ignore duplicate keys 405 for k, v := range destsSrc { 406 destsDest[k] = v 407 } 408 409 return nil 410 } 411 412 func mergeNames(ctxSrc, ctxDest *model.Context) error { 413 414 rootDictSrc, rootDictDest, err := rootDicts(ctxSrc, ctxDest) 415 if err != nil { 416 return err 417 } 418 419 _, found := rootDictSrc.Find("Names") 420 if !found { 421 // Nothing to merge in. 422 return nil 423 } 424 425 if _, found := rootDictDest.Find("Names"); !found { 426 ctxDest.Names = ctxSrc.Names 427 return nil 428 } 429 430 // We need to merge src Names into dest Names. 431 432 for id, namesSrc := range ctxSrc.Names { 433 if namesDest, ok := ctxDest.Names[id]; ok { 434 // Merge src tree into dest tree including collision detection. 435 if err := namesDest.AddTree(ctxDest.XRefTable, namesSrc, ctxSrc.NameRefs[id], []string{"D", "Dest"}); err != nil { 436 return err 437 } 438 continue 439 } 440 441 // Name tree missing in dest ctx => copy over names from src ctx 442 ctxDest.Names[id] = namesSrc 443 } 444 445 return nil 446 } 447 448 func mergeForms(ctxSrc, ctxDest *model.Context) error { 449 450 rootDictSource, rootDictDest, err := rootDicts(ctxSrc, ctxDest) 451 if err != nil { 452 return err 453 } 454 455 o, found := rootDictSource.Find("AcroForm") 456 if !found { 457 return nil 458 } 459 460 dSrc, err := ctxSrc.DereferenceDict(o) 461 if err != nil || len(dSrc) == 0 { 462 return err 463 } 464 465 // Retrieve ctxSrc Form Fields 466 o, found = dSrc.Find("Fields") 467 if !found { 468 return nil 469 } 470 arrFieldsSrc, err := ctxSrc.DereferenceArray(o) 471 if err != nil { 472 return err 473 } 474 if len(arrFieldsSrc) == 0 { 475 return nil 476 } 477 478 // We have a ctxSrc.Form with fields. 479 480 o, found = rootDictDest.Find("AcroForm") 481 if !found { 482 rootDictDest["AcroForm"] = dSrc 483 return nil 484 } 485 486 dDest, err := ctxDest.DereferenceDict(o) 487 if err != nil { 488 return err 489 } 490 491 if len(dDest) == 0 { 492 rootDictDest["AcroForm"] = dSrc 493 return nil 494 } 495 496 // Retrieve ctxDest AcroForm Fields 497 o, found = dDest.Find("Fields") 498 if !found { 499 rootDictDest["AcroForm"] = dSrc 500 return nil 501 } 502 arrFieldsDest, err := ctxDest.DereferenceArray(o) 503 if err != nil { 504 return err 505 } 506 if len(arrFieldsDest) == 0 { 507 rootDictDest["AcroForm"] = dSrc 508 return nil 509 } 510 511 if err := mergeInFields(ctxDest, arrFieldsSrc, arrFieldsDest, dDest); err != nil { 512 return err 513 } 514 515 return handleFormAttributes(ctxSrc, ctxDest, dSrc, dDest, arrFieldsSrc) 516 } 517 518 func patchIndRef(ir *types.IndirectRef, lookup map[int]int) { 519 i := ir.ObjectNumber.Value() 520 ir.ObjectNumber = types.Integer(lookup[i]) 521 } 522 523 func patchObject(o types.Object, lookup map[int]int) types.Object { 524 if log.TraceEnabled() { 525 log.Trace.Printf("patchObject before: %v\n", o) 526 } 527 528 var ob types.Object 529 530 switch obj := o.(type) { 531 532 case types.IndirectRef: 533 patchIndRef(&obj, lookup) 534 ob = obj 535 536 case types.Dict: 537 patchDict(obj, lookup) 538 ob = obj 539 540 case types.StreamDict: 541 patchDict(obj.Dict, lookup) 542 ob = obj 543 544 case types.ObjectStreamDict: 545 patchDict(obj.Dict, lookup) 546 ob = obj 547 548 case types.XRefStreamDict: 549 patchDict(obj.Dict, lookup) 550 ob = obj 551 552 case types.Array: 553 patchArray(&obj, lookup) 554 ob = obj 555 556 } 557 558 if log.TraceEnabled() { 559 log.Trace.Printf("patchObject end: %v\n", ob) 560 } 561 562 return ob 563 } 564 565 func patchDict(d types.Dict, lookup map[int]int) { 566 if log.TraceEnabled() { 567 log.Trace.Printf("patchDict before: %v\n", d) 568 } 569 570 for k, obj := range d { 571 o := patchObject(obj, lookup) 572 if o != nil { 573 d[k] = o 574 } 575 } 576 577 if log.TraceEnabled() { 578 log.Trace.Printf("patchDict after: %v\n", d) 579 } 580 } 581 582 func patchArray(a *types.Array, lookup map[int]int) { 583 if log.TraceEnabled() { 584 log.Trace.Printf("patchArray begin: %v\n", *a) 585 } 586 587 for i, obj := range *a { 588 o := patchObject(obj, lookup) 589 if o != nil { 590 (*a)[i] = o 591 } 592 } 593 594 if log.TraceEnabled() { 595 log.Trace.Printf("patchArray end: %v\n", a) 596 } 597 } 598 599 func objNrsIntSet(ctx *model.Context) types.IntSet { 600 objNrs := types.IntSet{} 601 602 for k := range ctx.Table { 603 if k == 0 { 604 // obj#0 is always the head of the freelist. 605 continue 606 } 607 objNrs[k] = true 608 } 609 610 return objNrs 611 } 612 613 func lookupTable(keys types.IntSet, i int) map[int]int { 614 m := map[int]int{} 615 616 for k := range keys { 617 m[k] = i 618 i++ 619 } 620 621 return m 622 } 623 624 // Patch an IntSet of objNrs using lookup. 625 func patchObjects(s types.IntSet, lookup map[int]int) types.IntSet { 626 t := types.IntSet{} 627 628 for k, v := range s { 629 if v { 630 t[lookup[k]] = v 631 } 632 } 633 634 return t 635 } 636 637 func patchNameTree(n *model.Node, lookup map[int]int) error { 638 639 patchValues := func(xRefTable *model.XRefTable, k string, v *types.Object) error { 640 *v = patchObject(*v, lookup) 641 return nil 642 } 643 644 return n.Process(nil, patchValues) 645 } 646 647 func patchSourceObjectNumbers(ctxSrc, ctxDest *model.Context) { 648 if log.DebugEnabled() { 649 log.Debug.Printf("patchSourceObjectNumbers: ctxSrc: xRefTableSize:%d trailer.Size:%d - %s\n", len(ctxSrc.Table), *ctxSrc.Size, ctxSrc.Read.FileName) 650 log.Debug.Printf("patchSourceObjectNumbers: ctxDest: xRefTableSize:%d trailer.Size:%d - %s\n", len(ctxDest.Table), *ctxDest.Size, ctxDest.Read.FileName) 651 } 652 653 // Patch source xref tables obj numbers which are essentially the keys. 654 //logInfoMerge.Printf("Source XRefTable before:\n%s\n", ctxSource) 655 656 objNrs := objNrsIntSet(ctxSrc) 657 658 // Create lookup table for object numbers. 659 // The first number is the successor of the last number in ctxDest. 660 lookup := lookupTable(objNrs, *ctxDest.Size) 661 662 // Patch pointer to root object 663 patchIndRef(ctxSrc.Root, lookup) 664 665 // Patch pointer to info object 666 if ctxSrc.Info != nil { 667 patchIndRef(ctxSrc.Info, lookup) 668 } 669 670 // Patch free object zero 671 entry := ctxSrc.Table[0] 672 off := int(*entry.Offset) 673 if off != 0 { 674 i := int64(lookup[off]) 675 entry.Offset = &i 676 } 677 678 // Patch all indRefs for xref table entries. 679 for k := range objNrs { 680 681 //logDebugMerge.Printf("patching obj #%d\n", k) 682 683 entry := ctxSrc.Table[k] 684 685 if entry.Free { 686 if log.DebugEnabled() { 687 log.Debug.Printf("patch free entry: old offset:%d\n", *entry.Offset) 688 } 689 off := int(*entry.Offset) 690 if off == 0 { 691 continue 692 } 693 i := int64(lookup[off]) 694 entry.Offset = &i 695 if log.DebugEnabled() { 696 log.Debug.Printf("patch free entry: new offset:%d\n", *entry.Offset) 697 } 698 continue 699 } 700 701 patchObject(entry.Object, lookup) 702 } 703 704 // Patch xref entry object numbers. 705 m := make(map[int]*model.XRefTableEntry, *ctxSrc.Size) 706 for k, v := range lookup { 707 m[v] = ctxSrc.Table[k] 708 } 709 m[0] = ctxSrc.Table[0] 710 ctxSrc.Table = m 711 712 // Patch DuplicateInfo object numbers. 713 ctxSrc.Optimize.DuplicateInfoObjects = patchObjects(ctxSrc.Optimize.DuplicateInfoObjects, lookup) 714 715 // Patch Linearization object numbers. 716 ctxSrc.LinearizationObjs = patchObjects(ctxSrc.LinearizationObjs, lookup) 717 718 // Patch XRefStream objects numbers. 719 ctxSrc.Read.XRefStreams = patchObjects(ctxSrc.Read.XRefStreams, lookup) 720 721 // Patch object stream object numbers. 722 ctxSrc.Read.ObjectStreams = patchObjects(ctxSrc.Read.ObjectStreams, lookup) 723 724 // Patch cached name trees. 725 for _, v := range ctxSrc.Names { 726 patchNameTree(v, lookup) 727 } 728 729 if log.DebugEnabled() { 730 log.Debug.Printf("patchSourceObjectNumbers end") 731 } 732 } 733 734 func createDividerPagesDict(ctx *model.Context, parentIndRef types.IndirectRef) (*types.IndirectRef, error) { 735 d := types.Dict( 736 map[string]types.Object{ 737 "Type": types.Name("Pages"), 738 "Parent": parentIndRef, 739 "Count": types.Integer(1), 740 }, 741 ) 742 743 indRef, err := ctx.IndRefForNewObject(d) 744 if err != nil { 745 return nil, err 746 } 747 748 dims, err := ctx.XRefTable.PageDims() 749 if err != nil { 750 return nil, err 751 } 752 753 last := len(dims) - 1 754 mediaBox := types.NewRectangle(0, 0, dims[last].Width, dims[last].Height) 755 756 indRefPageDict, err := ctx.EmptyPage(indRef, mediaBox, 0) 757 if err != nil { 758 return nil, err 759 } 760 ctx.SetValid(*indRefPageDict) 761 762 d.Insert("Kids", types.Array{*indRefPageDict}) 763 764 return indRef, nil 765 } 766 767 func appendSourcePageTreeToDestPageTree(ctxSrc, ctxDest *model.Context, dividerPage bool) error { 768 if log.DebugEnabled() { 769 log.Debug.Println("appendSourcePageTreeToDestPageTree begin") 770 } 771 772 indRefPageTreeRootDictDest, err := ctxDest.Pages() 773 if err != nil { 774 return err 775 } 776 777 pageTreeRootDictDest, err := ctxDest.XRefTable.DereferenceDict(*indRefPageTreeRootDictDest) 778 if err != nil { 779 return err 780 } 781 782 pageCountDest := pageTreeRootDictDest.IntEntry("Count") 783 if pageCountDest == nil || *pageCountDest != ctxDest.PageCount { 784 return errors.Errorf("pdfcpu: corrupt page node at obj #%d\n", indRefPageTreeRootDictDest.ObjectNumber) 785 } 786 787 c := ctxDest.PageCount 788 789 d := types.NewDict() 790 d.InsertName("Type", "Pages") 791 kids := types.Array{*indRefPageTreeRootDictDest} 792 793 indRef, err := ctxDest.IndRefForNewObject(d) 794 if err != nil { 795 return err 796 } 797 798 if dividerPage { 799 dividerPagesNodeIndRef, err := createDividerPagesDict(ctxDest, *indRef) 800 if err != nil { 801 return err 802 } 803 kids = append(kids, *dividerPagesNodeIndRef) 804 c++ 805 } 806 807 pageTreeRootDictDest["Parent"] = *indRef 808 809 indRefPageTreeRootDictSource, err := ctxSrc.Pages() 810 if err != nil { 811 return err 812 } 813 814 d.Insert("Kids", append(kids, *indRefPageTreeRootDictSource)) 815 816 pageTreeRootDictSource, err := ctxSrc.XRefTable.DereferenceDict(*indRefPageTreeRootDictSource) 817 if err != nil { 818 return err 819 } 820 821 pageTreeRootDictSource["Parent"] = *indRef 822 823 pageCountSource := pageTreeRootDictSource.IntEntry("Count") 824 if pageCountSource == nil || *pageCountSource != ctxSrc.PageCount { 825 return errors.Errorf("pdfcpu: corrupt page node at obj #%d\n", indRefPageTreeRootDictSource.ObjectNumber) 826 } 827 828 c += ctxSrc.PageCount 829 d.InsertInt("Count", c) 830 ctxDest.PageCount = c 831 832 rootDict, err := ctxDest.Catalog() 833 if err != nil { 834 return err 835 } 836 837 rootDict["Pages"] = *indRef 838 839 if log.DebugEnabled() { 840 log.Debug.Println("appendSourcePageTreeToDestPageTree end") 841 } 842 843 return nil 844 } 845 846 func zipSourcePageTreeIntoDestPageTree(ctxSrc, ctxDest *model.Context) error { 847 if log.DebugEnabled() { 848 log.Debug.Println("zipSourcePageTreeIntoDestPageTree begin") 849 } 850 851 appendFromPageNr := 0 852 if ctxSrc.PageCount > ctxDest.PageCount { 853 appendFromPageNr = ctxDest.PageCount + 1 854 } 855 856 rootPageIndRef, err := ctxDest.Pages() 857 if err != nil { 858 return err 859 } 860 861 // Process dest page tree recursively and weave in src pages 862 p := 0 863 if ctxDest.PageCount, err = ctxDest.InsertPages(rootPageIndRef, &p, ctxSrc); err != nil { 864 return err 865 } 866 867 if appendFromPageNr > 0 { 868 // append remaining src pages 869 if ctxDest.PageCount, err = ctxDest.AppendPages(rootPageIndRef, appendFromPageNr, ctxSrc); err != nil { 870 return err 871 } 872 } 873 874 if log.DebugEnabled() { 875 log.Debug.Println("zipSourcePageTreeIntoDestPageTree end") 876 } 877 878 return nil 879 } 880 881 func appendSourceObjectsToDest(ctxSrc, ctxDest *model.Context) { 882 if log.DebugEnabled() { 883 log.Debug.Println("appendSourceObjectsToDest begin") 884 } 885 886 for objNr, entry := range ctxSrc.Table { 887 888 // Do not copy free list head. 889 if objNr == 0 { 890 continue 891 } 892 893 if log.DebugEnabled() { 894 log.Debug.Printf("adding obj %d from src to dest\n", objNr) 895 } 896 897 ctxDest.Table[objNr] = entry 898 899 *ctxDest.Size++ 900 901 } 902 903 if log.DebugEnabled() { 904 log.Debug.Println("appendSourceObjectsToDest end") 905 } 906 } 907 908 // merge two disjunct IntSets 909 func mergeIntSets(src, dest types.IntSet) { 910 for k := range src { 911 dest[k] = true 912 } 913 } 914 915 func mergeDuplicateObjNumberIntSets(ctxSrc, ctxDest *model.Context) { 916 if log.DebugEnabled() { 917 log.Debug.Println("mergeDuplicateObjNumberIntSets begin") 918 } 919 920 mergeIntSets(ctxSrc.Optimize.DuplicateInfoObjects, ctxDest.Optimize.DuplicateInfoObjects) 921 mergeIntSets(ctxSrc.LinearizationObjs, ctxDest.LinearizationObjs) 922 mergeIntSets(ctxSrc.Read.XRefStreams, ctxDest.Read.XRefStreams) 923 mergeIntSets(ctxSrc.Read.ObjectStreams, ctxDest.Read.ObjectStreams) 924 925 if log.DebugEnabled() { 926 log.Debug.Println("mergeDuplicateObjNumberIntSets end") 927 } 928 } 929 930 // MergeXRefTables merges Context ctxSrc into ctxDest by appending its page tree. 931 // zip ... zip 2 files together (eg. 1A,1B,2A,2B,3A,3B...) 932 // dividerPage ... insert blank page between merged files (not applicable for zipping) 933 func MergeXRefTables(fName string, ctxSrc, ctxDest *model.Context, zip, dividerPage bool) (err error) { 934 935 patchSourceObjectNumbers(ctxSrc, ctxDest) 936 937 appendSourceObjectsToDest(ctxSrc, ctxDest) 938 939 origDestPageCount := ctxDest.PageCount 940 if dividerPage { 941 origDestPageCount++ 942 } 943 944 if zip { 945 err = zipSourcePageTreeIntoDestPageTree(ctxSrc, ctxDest) 946 } else { 947 err = appendSourcePageTreeToDestPageTree(ctxSrc, ctxDest, dividerPage) 948 } 949 950 if err != nil { 951 return nil 952 } 953 954 if err = mergeForms(ctxSrc, ctxDest); err != nil { 955 return err 956 } 957 958 if err = mergeDests(ctxSrc, ctxDest); err != nil { 959 return err 960 } 961 962 if err = mergeNames(ctxSrc, ctxDest); err != nil { 963 return err 964 } 965 966 if !zip && ctxDest.Configuration.CreateBookmarks { 967 if err = mergeOutlines(fName, origDestPageCount+1, ctxSrc, ctxDest); err != nil { 968 return err 969 } 970 } 971 972 // Mark src's root object as free. 973 if err = ctxDest.FreeObject(int(ctxSrc.Root.ObjectNumber)); err != nil { 974 return 975 } 976 977 // Mark source's info object as free. 978 // Note: Any indRefs this info object depends on are missed. 979 if ctxSrc.Info != nil { 980 if err = ctxDest.FreeObject(int(ctxSrc.Info.ObjectNumber)); err != nil { 981 return 982 } 983 } 984 985 // Merge all IntSets containing redundant object numbers. 986 mergeDuplicateObjNumberIntSets(ctxSrc, ctxDest) 987 988 if log.InfoEnabled() { 989 log.Info.Printf("Dest XRefTable after merge:\n%s\n", ctxDest) 990 } 991 992 return nil 993 }