import PDFArray from 'src/core/objects/PDFArray' ; import PDFDict from 'src/core/objects/PDFDict' ; import PDFName from 'src/core/objects/PDFName' ; import PDFObject from 'src/core/objects/PDFObject' ; import PDFRef from 'src/core/objects/PDFRef' ; import PDFStream from 'src/core/objects/PDFStream' ; import PDFContext from 'src/core/PDFContext' ; import PDFPageLeaf from 'src/core/structures/PDFPageLeaf' ; /**
 * PDFObjectCopier copies PDFObjects from a src context to a dest context.
 * The primary use case for this is to copy pages between PDFs.
 *
 * _Copying_ an object with a PDFObjectCopier is different from _cloning_ an
 * object with its [[PDFObject.clone]] method:
 *
 * ```
 *   const src: PDFContext = ...
 *   const dest: PDFContext = ...
 *   const originalObject: PDFObject = ...
 *   const copiedObject = PDFObjectCopier.for(src, dest).copy(originalObject);
 *   const clonedObject = originalObject.clone();
 * ```
 *
 * Copying an object is equivalent to cloning it and then copying over any other
 * objects that it references. Note that only dictionaries, arrays, and streams
 * (or structures build from them) can contain indirect references to other
 * objects. Copying a PDFObject that is not a dictionary, array, or stream is
 * supported, but is equivalent to cloning it.
 */ class PDFObjectCopier { static for = ( src , dest ) => new PDFObjectCopier ( src , dest ) ; private readonly src : PDFContext ; private readonly dest : PDFContext ; private readonly traversedObjects = new Map < PDFObject , PDFObject > ( ) ; private constructor ( src , dest ) { this . src = src ; this . dest = dest ; } copy = < T extends PDFObject > ( object ) : T => ( object instanceof PDFPageLeaf ? this . copyPDFPage ( object ) : object instanceof PDFDict ? this . copyPDFDict ( object ) : object instanceof PDFArray ? this . copyPDFArray ( object ) : object instanceof PDFStream ? this . copyPDFStream ( object ) : object instanceof PDFRef ? this . copyPDFIndirectObject ( object ) : object . clone ( ) ) as T ; private copyPDFPage = ( originalPage ) : PDFPageLeaf => { const clonedPage = originalPage . clone ( ) ; const { InheritableEntries } = PDFPageLeaf ; for ( let idx = 0 , len = InheritableEntries . length ; idx < len ; idx ++ ) { const key = PDFName . of ( InheritableEntries [ idx ] ) ; const value = clonedPage . getInheritableAttribute ( key ) ! ; if ( ! clonedPage . get ( key ) && value ) clonedPage . set ( key , value ) ; } clonedPage . delete ( PDFName . of ( 'Parent' ) ) ; return this . copyPDFDict ( clonedPage ) as PDFPageLeaf ; } ; private copyPDFDict = ( originalDict ) : PDFDict => { if ( this . traversedObjects . has ( originalDict ) ) { return this . traversedObjects . get ( originalDict ) as PDFDict ; } const clonedDict = originalDict . clone ( this . dest ) ; this . traversedObjects . set ( originalDict , clonedDict ) ; const entries = originalDict . entries ( ) ; for ( let idx = 0 , len = entries . length ; idx < len ; idx ++ ) { const [ key , value ] = entries [ idx ] ; clonedDict . set ( key , this . copy ( value ) ) ; } return clonedDict ; } ; private copyPDFArray = ( originalArray ) : PDFArray => { if ( this . traversedObjects . has ( originalArray ) ) { return this . traversedObjects . get ( originalArray ) as PDFArray ; } const clonedArray = originalArray . clone ( this . dest ) ; this . traversedObjects . set ( originalArray , clonedArray ) ; for ( let idx = 0 , len = originalArray . size ( ) ; idx < len ; idx ++ ) { const value = originalArray . get ( idx ) ; clonedArray . set ( idx , this . copy ( value ) ) ; } return clonedArray ; } ; private copyPDFStream = ( originalStream ) : PDFStream => { if ( this . traversedObjects . has ( originalStream ) ) { return this . traversedObjects . get ( originalStream ) as PDFStream ; } const clonedStream = originalStream . clone ( this . dest ) ; this . traversedObjects . set ( originalStream , clonedStream ) ; const entries = originalStream . dict . entries ( ) ; for ( let idx = 0 , len = entries . length ; idx < len ; idx ++ ) { const [ key , value ] = entries [ idx ] ; clonedStream . dict . set ( key , this . copy ( value ) ) ; } return clonedStream ; } ; private copyPDFIndirectObject = ( ref ) : PDFRef => { const alreadyMapped = this . traversedObjects . has ( ref ) ; if ( ! alreadyMapped ) { const newRef = this . dest . nextRef ( ) ; this . traversedObjects . set ( ref , newRef ) ; const dereferencedValue = this . src . lookup ( ref ) ; if ( dereferencedValue ) { const cloned = this . copy ( dereferencedValue ) ; this . dest . assign ( newRef , cloned ) ; } } return this . traversedObjects . get ( ref ) as PDFRef ; } ; } export default PDFObjectCopier ;