import PDFCrossRefSection from 'src/core/document/PDFCrossRefSection' ; import PDFHeader from 'src/core/document/PDFHeader' ; import PDFTrailer from 'src/core/document/PDFTrailer' ; import PDFTrailerDict from 'src/core/document/PDFTrailerDict' ; import PDFDict from 'src/core/objects/PDFDict' ; import PDFObject from 'src/core/objects/PDFObject' ; import PDFRef from 'src/core/objects/PDFRef' ; import PDFContext from 'src/core/PDFContext' ; import PDFObjectStream from 'src/core/structures/PDFObjectStream' ; import CharCodes from 'src/core/syntax/CharCodes' ; import { copyStringIntoBuffer , waitForTick } from 'src/utils' ; export interface SerializationInfo { size : number ; header : PDFHeader ; indirectObjects : [ PDFRef , PDFObject ] [ ] ; xref ? : PDFCrossRefSection ; trailerDict ? : PDFTrailerDict ; trailer : PDFTrailer ; } class PDFWriter { static forContext = ( context , objectsPerTick ) => new PDFWriter ( context , objectsPerTick ) ; protected readonly context : PDFContext ; protected readonly objectsPerTick : number ; private parsedObjects = 0 ; protected constructor ( context , objectsPerTick ) { this . context = context ; this . objectsPerTick = objectsPerTick ; } async serializeToBuffer ( ) { const { size , header , indirectObjects , xref , trailerDict , trailer , } = await this . computeBufferSize ( ) ; let offset = 0 ; const buffer = new Uint8Array ( size ) ; offset += header . copyBytesInto ( buffer , offset ) ; buffer [ offset ++ ] = CharCodes . Newline ; buffer [ offset ++ ] = CharCodes . Newline ; for ( let idx = 0 , len = indirectObjects . length ; idx < len ; idx ++ ) { const [ ref , object ] = indirectObjects [ idx ] ; const objectNumber = String ( ref . objectNumber ) ; offset += copyStringIntoBuffer ( objectNumber , buffer , offset ) ; buffer [ offset ++ ] = CharCodes . Space ; const generationNumber = String ( ref . generationNumber ) ; offset += copyStringIntoBuffer ( generationNumber , buffer , offset ) ; buffer [ offset ++ ] = CharCodes . Space ; buffer [ offset ++ ] = CharCodes . o ; buffer [ offset ++ ] = CharCodes . b ; buffer [ offset ++ ] = CharCodes . j ; buffer [ offset ++ ] = CharCodes . Newline ; offset += object . copyBytesInto ( buffer , offset ) ; buffer [ offset ++ ] = CharCodes . Newline ; buffer [ offset ++ ] = CharCodes . e ; buffer [ offset ++ ] = CharCodes . n ; buffer [ offset ++ ] = CharCodes . d ; buffer [ offset ++ ] = CharCodes . o ; buffer [ offset ++ ] = CharCodes . b ; buffer [ offset ++ ] = CharCodes . j ; buffer [ offset ++ ] = CharCodes . Newline ; buffer [ offset ++ ] = CharCodes . Newline ; const n = object instanceof PDFObjectStream ? object . getObjectsCount ( ) : 1 ; if ( this . shouldWaitForTick ( n ) ) await waitForTick ( ) ; } if ( xref ) { offset += xref . copyBytesInto ( buffer , offset ) ; buffer [ offset ++ ] = CharCodes . Newline ; } if ( trailerDict ) { offset += trailerDict . copyBytesInto ( buffer , offset ) ; buffer [ offset ++ ] = CharCodes . Newline ; buffer [ offset ++ ] = CharCodes . Newline ; } offset += trailer . copyBytesInto ( buffer , offset ) ; return buffer ; } protected computeIndirectObjectSize ( [ ref , object ] ) { const refSize = ref . sizeInBytes ( ) + 3 ; const objectSize = object . sizeInBytes ( ) + 9 ; return refSize + objectSize ; } protected createTrailerDict ( ) { return this . context . obj ( { Size : this . context . largestObjectNumber + 1 , Root : this . context . trailerInfo . Root , Encrypt : this . context . trailerInfo . Encrypt , Info : this . context . trailerInfo . Info , ID : this . context . trailerInfo . ID , } ) ; } protected async computeBufferSize ( ) { const header = PDFHeader . forVersion ( 1 , 7 ) ; let size = header . sizeInBytes ( ) + 2 ; const xref = PDFCrossRefSection . create ( ) ; const indirectObjects = this . context . enumerateIndirectObjects ( ) ; for ( let idx = 0 , len = indirectObjects . length ; idx < len ; idx ++ ) { const indirectObject = indirectObjects [ idx ] ; const [ ref ] = indirectObject ; xref . addEntry ( ref , size ) ; size += this . computeIndirectObjectSize ( indirectObject ) ; if ( this . shouldWaitForTick ( 1 ) ) await waitForTick ( ) ; } const xrefOffset = size ; size += xref . sizeInBytes ( ) + 1 ; const trailerDict = PDFTrailerDict . of ( this . createTrailerDict ( ) ) ; size += trailerDict . sizeInBytes ( ) + 2 ; const trailer = PDFTrailer . forLastCrossRefSectionOffset ( xrefOffset ) ; size += trailer . sizeInBytes ( ) ; return { size , header , indirectObjects , xref , trailerDict , trailer } ; } protected shouldWaitForTick = ( n ) => { this . parsedObjects += n ; return this . parsedObjects % this . objectsPerTick === 0 ; } ; } export default PDFWriter ;