import pako from 'pako' ; import PDFHeader from 'src/core/document/PDFHeader' ; import { UnexpectedObjectTypeError } from 'src/core/errors' ; import PDFArray from 'src/core/objects/PDFArray' ; import PDFBool from 'src/core/objects/PDFBool' ; import PDFDict from 'src/core/objects/PDFDict' ; import PDFHexString from 'src/core/objects/PDFHexString' ; import PDFName from 'src/core/objects/PDFName' ; import PDFNull from 'src/core/objects/PDFNull' ; import PDFNumber from 'src/core/objects/PDFNumber' ; import PDFObject from 'src/core/objects/PDFObject' ; import PDFRawStream from 'src/core/objects/PDFRawStream' ; import PDFRef from 'src/core/objects/PDFRef' ; import PDFStream from 'src/core/objects/PDFStream' ; import PDFString from 'src/core/objects/PDFString' ; import PDFOperator from 'src/core/operators/PDFOperator' ; import Ops from 'src/core/operators/PDFOperatorNames' ; import PDFContentStream from 'src/core/structures/PDFContentStream' ; import { typedArrayFor } from 'src/utils' ; import { SimpleRNG } from 'src/utils/rng' ; type LookupKey = PDFRef | PDFObject | undefined ; interface LiteralObject { [ name ] : Literal | PDFObject ; } interface LiteralArray { [ index ] : Literal | PDFObject ; } type Literal = LiteralObject | LiteralArray | string | number | boolean | null | undefined ; const byAscendingObjectNumber = ( [ a ] , [ b ] ) => a . objectNumber - b . objectNumber ; class PDFContext { static create = ( ) => new PDFContext ( ) ; largestObjectNumber : number ; header : PDFHeader ; trailerInfo : { Root ? : PDFObject ; Encrypt ? : PDFObject ; Info ? : PDFObject ; ID ? : PDFObject ; } ; rng : SimpleRNG ; private readonly indirectObjects : Map < PDFRef , PDFObject > ; private pushGraphicsStateContentStreamRef ? : PDFRef ; private popGraphicsStateContentStreamRef ? : PDFRef ; private constructor ( ) { this . largestObjectNumber = 0 ; this . header = PDFHeader . forVersion ( 1 , 7 ) ; this . trailerInfo = { } ; this . indirectObjects = new Map ( ) ; this . rng = SimpleRNG . withSeed ( 1 ) ; } assign ( ref , object ) { this . indirectObjects . set ( ref , object ) ; if ( ref . objectNumber > this . largestObjectNumber ) { this . largestObjectNumber = ref . objectNumber ; } } nextRef ( ) { this . largestObjectNumber += 1 ; return PDFRef . of ( this . largestObjectNumber ) ; } register ( object ) { const ref = this . nextRef ( ) ; this . assign ( ref , object ) ; return ref ; } delete ( ref ) { return this . indirectObjects . delete ( ref ) ; } lookupMaybe ( ref , type ) ; lookupMaybe ( ref , type ) ; lookupMaybe ( ref , type ) ; lookupMaybe ( ref , type ) ; lookupMaybe ( ref , type ) ; lookupMaybe ( ref , type ) ; lookupMaybe ( ref , type ) ; lookupMaybe ( ref , type ) ; lookupMaybe ( ref , type ) ; lookupMaybe ( ref , type ) ; lookupMaybe ( ref , type1 , type2 ) ; lookupMaybe ( ref , ... types ) { const preservePDFNull = types . includes ( PDFNull ) ; const result = ref instanceof PDFRef ? this . indirectObjects . get ( ref ) : ref ; if ( ! result || ( result === PDFNull && ! preservePDFNull ) ) return undefined ; for ( let idx = 0 , len = types . length ; idx < len ; idx ++ ) { const type = types [ idx ] ; if ( type === PDFNull ) { if ( result === PDFNull ) return result ; } else { if ( result instanceof type ) return result ; } } throw new UnexpectedObjectTypeError ( types , result ) ; } lookup ( ref ) ; lookup ( ref , type ) ; lookup ( ref , type ) ; lookup ( ref , type ) ; lookup ( ref , type ) ; lookup ( ref , type ) ; lookup ( ref , type ) ; lookup ( ref , type ) ; lookup ( ref , type ) ; lookup ( ref , type ) ; lookup ( ref , type ) ; lookup ( ref , type1 , type2 ) ; lookup ( ref , ... types ) { const result = ref instanceof PDFRef ? this . indirectObjects . get ( ref ) : ref ; if ( types . length === 0 ) return result ; for ( let idx = 0 , len = types . length ; idx < len ; idx ++ ) { const type = types [ idx ] ; if ( type === PDFNull ) { if ( result === PDFNull ) return result ; } else { if ( result instanceof type ) return result ; } } throw new UnexpectedObjectTypeError ( types , result ) ; } getObjectRef ( pdfObject ) { const entries = Array . from ( this . indirectObjects . entries ( ) ) ; for ( let idx = 0 , len = entries . length ; idx < len ; idx ++ ) { const [ ref , object ] = entries [ idx ] ; if ( object === pdfObject ) { return ref ; } } return undefined ; } enumerateIndirectObjects ( ) { return Array . from ( this . indirectObjects . entries ( ) ) . sort ( byAscendingObjectNumber ) ; } obj ( literal ) ; obj ( literal ) ; obj ( literal ) ; obj ( literal ) ; obj ( literal ) ; obj ( literal ) ; obj ( literal ) { if ( literal instanceof PDFObject ) { return literal ; } else if ( literal === null || literal === undefined ) { return PDFNull ; } else if ( typeof literal === 'string' ) { return PDFName . of ( literal ) ; } else if ( typeof literal === 'number' ) { return PDFNumber . of ( literal ) ; } else if ( typeof literal === 'boolean' ) { return literal ? PDFBool . True : PDFBool . False ; } else if ( Array . isArray ( literal ) ) { const array = PDFArray . withContext ( this ) ; for ( let idx = 0 , len = literal . length ; idx < len ; idx ++ ) { array . push ( this . obj ( literal [ idx ] ) ) ; } return array ; } else { const dict = PDFDict . withContext ( this ) ; const keys = Object . keys ( literal ) ; for ( let idx = 0 , len = keys . length ; idx < len ; idx ++ ) { const key = keys [ idx ] ; const value = ( literal as LiteralObject ) [ key ] as any ; if ( value !== undefined ) dict . set ( PDFName . of ( key ) , this . obj ( value ) ) ; } return dict ; } } stream ( contents , dict = { } ) { return PDFRawStream . of ( this . obj ( dict ) , typedArrayFor ( contents ) ) ; } flateStream ( contents , dict = { } ) { return this . stream ( pako . deflate ( typedArrayFor ( contents ) ) , { ... dict , Filter : 'FlateDecode' , } ) ; } contentStream ( operators , dict = { } ) { return PDFContentStream . of ( this . obj ( dict ) , operators ) ; } formXObject ( operators , dict = { } ) { return this . contentStream ( operators , { BBox : this . obj ( [ 0 , 0 , 0 , 0 ] ) , Matrix : this . obj ( [ 1 , 0 , 0 , 1 , 0 , 0 ] ) , ... dict , Type : 'XObject' , Subtype : 'Form' , } ) ; } getPushGraphicsStateContentStream ( ) { if ( this . pushGraphicsStateContentStreamRef ) { return this . pushGraphicsStateContentStreamRef ; } const dict = this . obj ( { } ) ; const op = PDFOperator . of ( Ops . PushGraphicsState ) ; const stream = PDFContentStream . of ( dict , [ op ] ) ; this . pushGraphicsStateContentStreamRef = this . register ( stream ) ; return this . pushGraphicsStateContentStreamRef ; } getPopGraphicsStateContentStream ( ) { if ( this . popGraphicsStateContentStreamRef ) { return this . popGraphicsStateContentStreamRef ; } const dict = this . obj ( { } ) ; const op = PDFOperator . of ( Ops . PopGraphicsState ) ; const stream = PDFContentStream . of ( dict , [ op ] ) ; this . popGraphicsStateContentStreamRef = this . register ( stream ) ; return this . popGraphicsStateContentStreamRef ; } addRandomSuffix ( prefix , suffixLength = 4 ) { return `${ prefix }-${ Math . floor ( this . rng . nextInt ( ) * 10 ** suffixLength ) }` ; } } export default PDFContext ;