import { Color , rgb } from 'src/api/colors' ; import { drawImage , drawLine , drawLinesOfText , drawPage , drawRectangle , drawSvgPath , drawEllipse , } from 'src/api/operations' ; import { popGraphicsState , pushGraphicsState , translate , LineCapStyle , scale , } from 'src/api/operators' ; import PDFDocument from 'src/api/PDFDocument' ; import PDFEmbeddedPage from 'src/api/PDFEmbeddedPage' ; import PDFFont from 'src/api/PDFFont' ; import PDFImage from 'src/api/PDFImage' ; import { PDFPageDrawCircleOptions , PDFPageDrawEllipseOptions , PDFPageDrawImageOptions , PDFPageDrawLineOptions , PDFPageDrawPageOptions , PDFPageDrawRectangleOptions , PDFPageDrawSquareOptions , PDFPageDrawSVGOptions , PDFPageDrawTextOptions , BlendMode , } from 'src/api/PDFPageOptions' ; import { degrees , Rotation , toDegrees } from 'src/api/rotations' ; import { StandardFonts } from 'src/api/StandardFonts' ; import { PDFContentStream , PDFHexString , PDFName , PDFOperator , PDFPageLeaf , PDFRef , PDFDict , PDFArray , } from 'src/core' ; import { assertEachIs , assertIs , assertMultiple , assertOrUndefined , breakTextIntoLines , cleanText , rectanglesAreEqual , lineSplit , assertRangeOrUndefined , assertIsOneOfOrUndefined , } from 'src/utils' ; /**
 * Represents a single page of a [[PDFDocument]].
 */ export default class PDFPage { param leafNode param ref param doc static of = ( leafNode , ref , doc ) => new PDFPage ( leafNode , ref , doc ) ; param doc static create = ( doc ) => { assertIs ( doc , 'doc' , [ [ PDFDocument , 'PDFDocument' ] ] ) ; const dummyRef = PDFRef . of ( - 1 ) ; const pageLeaf = PDFPageLeaf . withContextAndParent ( doc . context , dummyRef ) ; const pageRef = doc . context . register ( pageLeaf ) ; return new PDFPage ( pageLeaf , pageRef , doc ) ; } ; /** The low-level PDFDictionary wrapped by this page. */ readonly node : PDFPageLeaf ; /** The unique reference assigned to this page within the document. */ readonly ref : PDFRef ; /** The document to which this page belongs. */ readonly doc : PDFDocument ; private fontKey ? : PDFName ; private font ? : PDFFont ; private fontSize = 24 ; private fontColor = rgb ( 0 , 0 , 0 ) as Color ; private lineHeight = 24 ; private x = 0 ; private y = 0 ; private contentStream ? : PDFContentStream ; private contentStreamRef ? : PDFRef ; private constructor ( leafNode , ref , doc ) { assertIs ( leafNode , 'leafNode' , [ [ PDFPageLeaf , 'PDFPageLeaf' ] ] ) ; assertIs ( ref , 'ref' , [ [ PDFRef , 'PDFRef' ] ] ) ; assertIs ( doc , 'doc' , [ [ PDFDocument , 'PDFDocument' ] ] ) ; this . node = leafNode ; this . ref = ref ; this . doc = doc ; } param angle setRotation ( angle ) { const degreesAngle = toDegrees ( angle ) ; assertMultiple ( degreesAngle , 'degreesAngle' , 90 ) ; this . node . set ( PDFName . of ( 'Rotate' ) , this . doc . context . obj ( degreesAngle ) ) ; } returns getRotation ( ) { const Rotate = this . node . Rotate ( ) ; return degrees ( Rotate ? Rotate . asNumber ( ) : 0 ) ; } param width param height setSize ( width , height ) { assertIs ( width , 'width' , [ 'number' ] ) ; assertIs ( height , 'height' , [ 'number' ] ) ; const mediaBox = this . getMediaBox ( ) ; this . setMediaBox ( mediaBox . x , mediaBox . y , width , height ) ; const cropBox = this . getCropBox ( ) ; const bleedBox = this . getBleedBox ( ) ; const trimBox = this . getTrimBox ( ) ; const artBox = this . getArtBox ( ) ; const hasCropBox = this . node . CropBox ( ) ! ! ; const hasBleedBox = this . node . BleedBox ( ) ! ! ; const hasTrimBox = this . node . TrimBox ( ) ! ! ; const hasArtBox = this . node . ArtBox ( ) ! ! ; if ( hasCropBox && rectanglesAreEqual ( cropBox , mediaBox ) ) { this . setCropBox ( mediaBox . x , mediaBox . y , width , height ) ; } if ( hasBleedBox && rectanglesAreEqual ( bleedBox , mediaBox ) ) { this . setBleedBox ( mediaBox . x , mediaBox . y , width , height ) ; } if ( hasTrimBox && rectanglesAreEqual ( trimBox , mediaBox ) ) { this . setTrimBox ( mediaBox . x , mediaBox . y , width , height ) ; } if ( hasArtBox && rectanglesAreEqual ( artBox , mediaBox ) ) { this . setArtBox ( mediaBox . x , mediaBox . y , width , height ) ; } } param width setWidth ( width ) { assertIs ( width , 'width' , [ 'number' ] ) ; this . setSize ( width , this . getSize ( ) . height ) ; } param height setHeight ( height ) { assertIs ( height , 'height' , [ 'number' ] ) ; this . setSize ( this . getSize ( ) . width , height ) ; } param x param y param width param height setMediaBox ( x , y , width , height ) { assertIs ( x , 'x' , [ 'number' ] ) ; assertIs ( y , 'y' , [ 'number' ] ) ; assertIs ( width , 'width' , [ 'number' ] ) ; assertIs ( height , 'height' , [ 'number' ] ) ; const mediaBox = this . doc . context . obj ( [ x , y , x + width , y + height ] ) ; this . node . set ( PDFName . MediaBox , mediaBox ) ; } param x param y param width param height setCropBox ( x , y , width , height ) { assertIs ( x , 'x' , [ 'number' ] ) ; assertIs ( y , 'y' , [ 'number' ] ) ; assertIs ( width , 'width' , [ 'number' ] ) ; assertIs ( height , 'height' , [ 'number' ] ) ; const cropBox = this . doc . context . obj ( [ x , y , x + width , y + height ] ) ; this . node . set ( PDFName . CropBox , cropBox ) ; } param x param y param width param height setBleedBox ( x , y , width , height ) { assertIs ( x , 'x' , [ 'number' ] ) ; assertIs ( y , 'y' , [ 'number' ] ) ; assertIs ( width , 'width' , [ 'number' ] ) ; assertIs ( height , 'height' , [ 'number' ] ) ; const bleedBox = this . doc . context . obj ( [ x , y , x + width , y + height ] ) ; this . node . set ( PDFName . BleedBox , bleedBox ) ; } param x param y param width param height setTrimBox ( x , y , width , height ) { assertIs ( x , 'x' , [ 'number' ] ) ; assertIs ( y , 'y' , [ 'number' ] ) ; assertIs ( width , 'width' , [ 'number' ] ) ; assertIs ( height , 'height' , [ 'number' ] ) ; const trimBox = this . doc . context . obj ( [ x , y , x + width , y + height ] ) ; this . node . set ( PDFName . TrimBox , trimBox ) ; } param x param y param width param height setArtBox ( x , y , width , height ) { assertIs ( x , 'x' , [ 'number' ] ) ; assertIs ( y , 'y' , [ 'number' ] ) ; assertIs ( width , 'width' , [ 'number' ] ) ; assertIs ( height , 'height' , [ 'number' ] ) ; const artBox = this . doc . context . obj ( [ x , y , x + width , y + height ] ) ; this . node . set ( PDFName . ArtBox , artBox ) ; } returns getSize ( ) { const { width , height } = this . getMediaBox ( ) ; return { width , height } ; } returns getWidth ( ) { return this . getSize ( ) . width ; } returns getHeight ( ) { return this . getSize ( ) . height ; } returns getMediaBox ( ) { const mediaBox = this . node . MediaBox ( ) ; return mediaBox . asRectangle ( ) ; } returns getCropBox ( ) { const cropBox = this . node . CropBox ( ) ; return cropBox ?. asRectangle ( ) ?? this . getMediaBox ( ) ; } returns getBleedBox ( ) { const bleedBox = this . node . BleedBox ( ) ; return bleedBox ?. asRectangle ( ) ?? this . getCropBox ( ) ; } returns getTrimBox ( ) { const trimBox = this . node . TrimBox ( ) ; return trimBox ?. asRectangle ( ) ?? this . getCropBox ( ) ; } returns getArtBox ( ) { const artBox = this . node . ArtBox ( ) ; return artBox ?. asRectangle ( ) ?? this . getCropBox ( ) ; } param x param y translateContent ( x , y ) { assertIs ( x , 'x' , [ 'number' ] ) ; assertIs ( y , 'y' , [ 'number' ] ) ; this . node . normalize ( ) ; this . getContentStream ( ) ; const start = this . createContentStream ( pushGraphicsState ( ) , translate ( x , y ) ) ; const startRef = this . doc . context . register ( start ) ; const end = this . createContentStream ( popGraphicsState ( ) ) ; const endRef = this . doc . context . register ( end ) ; this . node . wrapContentStreams ( startRef , endRef ) ; } param x param y scale ( x , y ) { assertIs ( x , 'x' , [ 'number' ] ) ; assertIs ( y , 'y' , [ 'number' ] ) ; this . setSize ( this . getWidth ( ) * x , this . getHeight ( ) * y ) ; this . scaleContent ( x , y ) ; this . scaleAnnotations ( x , y ) ; } param x param y scaleContent ( x , y ) { assertIs ( x , 'x' , [ 'number' ] ) ; assertIs ( y , 'y' , [ 'number' ] ) ; this . node . normalize ( ) ; this . getContentStream ( ) ; const start = this . createContentStream ( pushGraphicsState ( ) , scale ( x , y ) ) ; const startRef = this . doc . context . register ( start ) ; const end = this . createContentStream ( popGraphicsState ( ) ) ; const endRef = this . doc . context . register ( end ) ; this . node . wrapContentStreams ( startRef , endRef ) ; } param x param y scaleAnnotations ( x , y ) { assertIs ( x , 'x' , [ 'number' ] ) ; assertIs ( y , 'y' , [ 'number' ] ) ; const annots = this . node . Annots ( ) ; if ( ! annots ) return ; for ( let idx = 0 ; idx < annots . size ( ) ; idx ++ ) { const annot = annots . lookup ( idx ) ; if ( annot instanceof PDFDict ) this . scaleAnnot ( annot , x , y ) ; } } /**
     * Reset the x and y coordinates of this page to `(0, 0)`. This operation is
     * often useful after calling [[translateContent]]. For example:
     * ```js
     * // Shift the page's contents up and to the right by 50 units
     * page.translateContent(50, 50)
     *
     * // This text will shifted - it will be drawn at (50, 50)
     * page.drawText('I am shifted')
     *
     * // Move back to (0, 0)
     * page.resetPosition()
     *
     * // This text will not be shifted - it will be drawn at (0, 0)
     * page.drawText('I am not shifted')
     * ```
     */ resetPosition ( ) { this . getContentStream ( false ) ; this . x = 0 ; this . y = 0 ; } param font setFont ( font ) { assertIs ( font , 'font' , [ [ PDFFont , 'PDFFont' ] ] ) ; this . font = font ; this . fontKey = this . node . newFontDictionary ( this . font . name , this . font . ref ) ; } param fontSize setFontSize ( fontSize ) { assertIs ( fontSize , 'fontSize' , [ 'number' ] ) ; this . fontSize = fontSize ; } param fontColor setFontColor ( fontColor ) { assertIs ( fontColor , 'fontColor' , [ [ Object , 'Color' ] ] ) ; this . fontColor = fontColor ; } param lineHeight setLineHeight ( lineHeight ) { assertIs ( lineHeight , 'lineHeight' , [ 'number' ] ) ; this . lineHeight = lineHeight ; } returns getPosition ( ) { return { x : this . x , y : this . y } ; } returns getX ( ) { return this . x ; } returns getY ( ) { return this . y ; } param x param y moveTo ( x , y ) { assertIs ( x , 'x' , [ 'number' ] ) ; assertIs ( y , 'y' , [ 'number' ] ) ; this . x = x ; this . y = y ; } param yDecrease moveDown ( yDecrease ) { assertIs ( yDecrease , 'yDecrease' , [ 'number' ] ) ; this . y -= yDecrease ; } param yIncrease moveUp ( yIncrease ) { assertIs ( yIncrease , 'yIncrease' , [ 'number' ] ) ; this . y += yIncrease ; } param xDecrease moveLeft ( xDecrease ) { assertIs ( xDecrease , 'xDecrease' , [ 'number' ] ) ; this . x -= xDecrease ; } param xIncrease moveRight ( xIncrease ) { assertIs ( xIncrease , 'xIncrease' , [ 'number' ] ) ; this . x += xIncrease ; } param operator pushOperators ( ... operator ) { assertEachIs ( operator , 'operator' , [ [ PDFOperator , 'PDFOperator' ] ] ) ; const contentStream = this . getContentStream ( ) ; contentStream . push ( ... operator ) ; } param text param options drawText ( text , options = { } ) { assertIs ( text , 'text' , [ 'string' ] ) ; assertOrUndefined ( options . color , 'options.color' , [ [ Object , 'Color' ] ] ) ; assertRangeOrUndefined ( options . opacity , 'opacity.opacity' , 0 , 1 ) ; assertOrUndefined ( options . font , 'options.font' , [ [ PDFFont , 'PDFFont' ] ] ) ; assertOrUndefined ( options . size , 'options.size' , [ 'number' ] ) ; assertOrUndefined ( options . rotate , 'options.rotate' , [ [ Object , 'Rotation' ] ] ) ; assertOrUndefined ( options . xSkew , 'options.xSkew' , [ [ Object , 'Rotation' ] ] ) ; assertOrUndefined ( options . ySkew , 'options.ySkew' , [ [ Object , 'Rotation' ] ] ) ; assertOrUndefined ( options . x , 'options.x' , [ 'number' ] ) ; assertOrUndefined ( options . y , 'options.y' , [ 'number' ] ) ; assertOrUndefined ( options . lineHeight , 'options.lineHeight' , [ 'number' ] ) ; assertOrUndefined ( options . maxWidth , 'options.maxWidth' , [ 'number' ] ) ; assertOrUndefined ( options . wordBreaks , 'options.wordBreaks' , [ Array ] ) ; assertIsOneOfOrUndefined ( options . blendMode , 'options.blendMode' , BlendMode ) ; const { oldFont , newFont , newFontKey } = this . setOrEmbedFont ( options . font ) ; const fontSize = options . size || this . fontSize ; const wordBreaks = options . wordBreaks || this . doc . defaultWordBreaks ; const textWidth = ( t ) => newFont . widthOfTextAtSize ( t , fontSize ) ; const lines = options . maxWidth === undefined ? lineSplit ( cleanText ( text ) ) : breakTextIntoLines ( text , wordBreaks , options . maxWidth , textWidth ) ; const encodedLines = new Array ( lines . length ) as PDFHexString [ ] ; for ( let idx = 0 , len = lines . length ; idx < len ; idx ++ ) { encodedLines [ idx ] = newFont . encodeText ( lines [ idx ] ) ; } const graphicsStateKey = this . maybeEmbedGraphicsState ( { opacity : options . opacity , blendMode : options . blendMode , } ) ; const contentStream = this . getContentStream ( ) ; contentStream . push ( ... drawLinesOfText ( encodedLines , { color : options . color ?? this . fontColor , font : newFontKey , size : fontSize , rotate : options . rotate ?? degrees ( 0 ) , xSkew : options . xSkew ?? degrees ( 0 ) , ySkew : options . ySkew ?? degrees ( 0 ) , x : options . x ?? this . x , y : options . y ?? this . y , lineHeight : options . lineHeight ?? this . lineHeight , graphicsState : graphicsStateKey , } ) ) ; if ( options . font ) { if ( oldFont ) this . setFont ( oldFont ) ; else this . resetFont ( ) ; } } param image param options drawImage ( image , options = { } ) { assertIs ( image , 'image' , [ [ PDFImage , 'PDFImage' ] ] ) ; assertOrUndefined ( options . x , 'options.x' , [ 'number' ] ) ; assertOrUndefined ( options . y , 'options.y' , [ 'number' ] ) ; assertOrUndefined ( options . width , 'options.width' , [ 'number' ] ) ; assertOrUndefined ( options . height , 'options.height' , [ 'number' ] ) ; assertOrUndefined ( options . rotate , 'options.rotate' , [ [ Object , 'Rotation' ] ] ) ; assertOrUndefined ( options . xSkew , 'options.xSkew' , [ [ Object , 'Rotation' ] ] ) ; assertOrUndefined ( options . ySkew , 'options.ySkew' , [ [ Object , 'Rotation' ] ] ) ; assertRangeOrUndefined ( options . opacity , 'opacity.opacity' , 0 , 1 ) ; assertIsOneOfOrUndefined ( options . blendMode , 'options.blendMode' , BlendMode ) ; const xObjectKey = this . node . newXObject ( 'Image' , image . ref ) ; const graphicsStateKey = this . maybeEmbedGraphicsState ( { opacity : options . opacity , blendMode : options . blendMode , } ) ; const contentStream = this . getContentStream ( ) ; contentStream . push ( ... drawImage ( xObjectKey , { x : options . x ?? this . x , y : options . y ?? this . y , width : options . width ?? image . size ( ) . width , height : options . height ?? image . size ( ) . height , rotate : options . rotate ?? degrees ( 0 ) , xSkew : options . xSkew ?? degrees ( 0 ) , ySkew : options . ySkew ?? degrees ( 0 ) , graphicsState : graphicsStateKey , } ) ) ; } param embeddedPage param options drawPage ( embeddedPage , options = { } ) { assertIs ( embeddedPage , 'embeddedPage' , [ [ PDFEmbeddedPage , 'PDFEmbeddedPage' ] , ] ) ; assertOrUndefined ( options . x , 'options.x' , [ 'number' ] ) ; assertOrUndefined ( options . y , 'options.y' , [ 'number' ] ) ; assertOrUndefined ( options . xScale , 'options.xScale' , [ 'number' ] ) ; assertOrUndefined ( options . yScale , 'options.yScale' , [ 'number' ] ) ; assertOrUndefined ( options . width , 'options.width' , [ 'number' ] ) ; assertOrUndefined ( options . height , 'options.height' , [ 'number' ] ) ; assertOrUndefined ( options . rotate , 'options.rotate' , [ [ Object , 'Rotation' ] ] ) ; assertOrUndefined ( options . xSkew , 'options.xSkew' , [ [ Object , 'Rotation' ] ] ) ; assertOrUndefined ( options . ySkew , 'options.ySkew' , [ [ Object , 'Rotation' ] ] ) ; assertRangeOrUndefined ( options . opacity , 'opacity.opacity' , 0 , 1 ) ; assertIsOneOfOrUndefined ( options . blendMode , 'options.blendMode' , BlendMode ) ; const xObjectKey = this . node . newXObject ( 'EmbeddedPdfPage' , embeddedPage . ref ) ; const graphicsStateKey = this . maybeEmbedGraphicsState ( { opacity : options . opacity , blendMode : options . blendMode , } ) ; const xScale = ( options . width !== undefined ? options . width / embeddedPage . width : options . xScale !== undefined ? options . xScale : 1 ) ; const yScale = ( options . height !== undefined ? options . height / embeddedPage . height : options . yScale !== undefined ? options . yScale : 1 ) ; const contentStream = this . getContentStream ( ) ; contentStream . push ( ... drawPage ( xObjectKey , { x : options . x ?? this . x , y : options . y ?? this . y , xScale , yScale , rotate : options . rotate ?? degrees ( 0 ) , xSkew : options . xSkew ?? degrees ( 0 ) , ySkew : options . ySkew ?? degrees ( 0 ) , graphicsState : graphicsStateKey , } ) ) ; } param path param options drawSvgPath ( path , options = { } ) { assertIs ( path , 'path' , [ 'string' ] ) ; assertOrUndefined ( options . x , 'options.x' , [ 'number' ] ) ; assertOrUndefined ( options . y , 'options.y' , [ 'number' ] ) ; assertOrUndefined ( options . scale , 'options.scale' , [ 'number' ] ) ; assertOrUndefined ( options . rotate , 'options.rotate' , [ [ Object , 'Rotation' ] ] ) ; assertOrUndefined ( options . borderWidth , 'options.borderWidth' , [ 'number' ] ) ; assertOrUndefined ( options . color , 'options.color' , [ [ Object , 'Color' ] ] ) ; assertRangeOrUndefined ( options . opacity , 'opacity.opacity' , 0 , 1 ) ; assertOrUndefined ( options . borderColor , 'options.borderColor' , [ [ Object , 'Color' ] , ] ) ; assertOrUndefined ( options . borderDashArray , 'options.borderDashArray' , [ Array , ] ) ; assertOrUndefined ( options . borderDashPhase , 'options.borderDashPhase' , [ 'number' , ] ) ; assertIsOneOfOrUndefined ( options . borderLineCap , 'options.borderLineCap' , LineCapStyle ) ; assertRangeOrUndefined ( options . borderOpacity , 'options.borderOpacity' , 0 , 1 ) ; assertIsOneOfOrUndefined ( options . blendMode , 'options.blendMode' , BlendMode ) ; const graphicsStateKey = this . maybeEmbedGraphicsState ( { opacity : options . opacity , borderOpacity : options . borderOpacity , blendMode : options . blendMode , } ) ; if ( ! ( 'color' in options ) && ! ( 'borderColor' in options ) ) { options . borderColor = rgb ( 0 , 0 , 0 ) ; } const contentStream = this . getContentStream ( ) ; contentStream . push ( ... drawSvgPath ( path , { x : options . x ?? this . x , y : options . y ?? this . y , scale : options . scale , rotate : options . rotate ?? degrees ( 0 ) , color : options . color ?? undefined , borderColor : options . borderColor ?? undefined , borderWidth : options . borderWidth ?? 0 , borderDashArray : options . borderDashArray ?? undefined , borderDashPhase : options . borderDashPhase ?? undefined , borderLineCap : options . borderLineCap ?? undefined , graphicsState : graphicsStateKey , } ) ) ; } param options drawLine ( options ) { assertIs ( options . start , 'options.start' , [ [ Object , '{ x: number, y: number }' ] , ] ) ; assertIs ( options . end , 'options.end' , [ [ Object , '{ x: number, y: number }' ] , ] ) ; assertIs ( options . start . x , 'options.start.x' , [ 'number' ] ) ; assertIs ( options . start . y , 'options.start.y' , [ 'number' ] ) ; assertIs ( options . end . x , 'options.end.x' , [ 'number' ] ) ; assertIs ( options . end . y , 'options.end.y' , [ 'number' ] ) ; assertOrUndefined ( options . thickness , 'options.thickness' , [ 'number' ] ) ; assertOrUndefined ( options . color , 'options.color' , [ [ Object , 'Color' ] ] ) ; assertOrUndefined ( options . dashArray , 'options.dashArray' , [ Array ] ) ; assertOrUndefined ( options . dashPhase , 'options.dashPhase' , [ 'number' ] ) ; assertIsOneOfOrUndefined ( options . lineCap , 'options.lineCap' , LineCapStyle ) ; assertRangeOrUndefined ( options . opacity , 'opacity.opacity' , 0 , 1 ) ; assertIsOneOfOrUndefined ( options . blendMode , 'options.blendMode' , BlendMode ) ; const graphicsStateKey = this . maybeEmbedGraphicsState ( { borderOpacity : options . opacity , blendMode : options . blendMode , } ) ; if ( ! ( 'color' in options ) ) { options . color = rgb ( 0 , 0 , 0 ) ; } const contentStream = this . getContentStream ( ) ; contentStream . push ( ... drawLine ( { start : options . start , end : options . end , thickness : options . thickness ?? 1 , color : options . color ?? undefined , dashArray : options . dashArray ?? undefined , dashPhase : options . dashPhase ?? undefined , lineCap : options . lineCap ?? undefined , graphicsState : graphicsStateKey , } ) ) ; } param options drawRectangle ( options = { } ) { assertOrUndefined ( options . x , 'options.x' , [ 'number' ] ) ; assertOrUndefined ( options . y , 'options.y' , [ 'number' ] ) ; assertOrUndefined ( options . width , 'options.width' , [ 'number' ] ) ; assertOrUndefined ( options . height , 'options.height' , [ 'number' ] ) ; assertOrUndefined ( options . rotate , 'options.rotate' , [ [ Object , 'Rotation' ] ] ) ; assertOrUndefined ( options . xSkew , 'options.xSkew' , [ [ Object , 'Rotation' ] ] ) ; assertOrUndefined ( options . ySkew , 'options.ySkew' , [ [ Object , 'Rotation' ] ] ) ; assertOrUndefined ( options . borderWidth , 'options.borderWidth' , [ 'number' ] ) ; assertOrUndefined ( options . color , 'options.color' , [ [ Object , 'Color' ] ] ) ; assertRangeOrUndefined ( options . opacity , 'opacity.opacity' , 0 , 1 ) ; assertOrUndefined ( options . borderColor , 'options.borderColor' , [ [ Object , 'Color' ] , ] ) ; assertOrUndefined ( options . borderDashArray , 'options.borderDashArray' , [ Array , ] ) ; assertOrUndefined ( options . borderDashPhase , 'options.borderDashPhase' , [ 'number' , ] ) ; assertIsOneOfOrUndefined ( options . borderLineCap , 'options.borderLineCap' , LineCapStyle ) ; assertRangeOrUndefined ( options . borderOpacity , 'options.borderOpacity' , 0 , 1 ) ; assertIsOneOfOrUndefined ( options . blendMode , 'options.blendMode' , BlendMode ) ; const graphicsStateKey = this . maybeEmbedGraphicsState ( { opacity : options . opacity , borderOpacity : options . borderOpacity , blendMode : options . blendMode , } ) ; if ( ! ( 'color' in options ) && ! ( 'borderColor' in options ) ) { options . color = rgb ( 0 , 0 , 0 ) ; } const contentStream = this . getContentStream ( ) ; contentStream . push ( ... drawRectangle ( { x : options . x ?? this . x , y : options . y ?? this . y , width : options . width ?? 150 , height : options . height ?? 100 , rotate : options . rotate ?? degrees ( 0 ) , xSkew : options . xSkew ?? degrees ( 0 ) , ySkew : options . ySkew ?? degrees ( 0 ) , borderWidth : options . borderWidth ?? 0 , color : options . color ?? undefined , borderColor : options . borderColor ?? undefined , borderDashArray : options . borderDashArray ?? undefined , borderDashPhase : options . borderDashPhase ?? undefined , graphicsState : graphicsStateKey , borderLineCap : options . borderLineCap ?? undefined , } ) ) ; } param options drawSquare ( options = { } ) { const { size } = options ; assertOrUndefined ( size , 'size' , [ 'number' ] ) ; this . drawRectangle ( { ... options , width : size , height : size } ) ; } param options drawEllipse ( options = { } ) { assertOrUndefined ( options . x , 'options.x' , [ 'number' ] ) ; assertOrUndefined ( options . y , 'options.y' , [ 'number' ] ) ; assertOrUndefined ( options . xScale , 'options.xScale' , [ 'number' ] ) ; assertOrUndefined ( options . yScale , 'options.yScale' , [ 'number' ] ) ; assertOrUndefined ( options . rotate , 'options.rotate' , [ [ Object , 'Rotation' ] ] ) ; assertOrUndefined ( options . color , 'options.color' , [ [ Object , 'Color' ] ] ) ; assertRangeOrUndefined ( options . opacity , 'opacity.opacity' , 0 , 1 ) ; assertOrUndefined ( options . borderColor , 'options.borderColor' , [ [ Object , 'Color' ] , ] ) ; assertRangeOrUndefined ( options . borderOpacity , 'options.borderOpacity' , 0 , 1 ) ; assertOrUndefined ( options . borderWidth , 'options.borderWidth' , [ 'number' ] ) ; assertOrUndefined ( options . borderDashArray , 'options.borderDashArray' , [ Array , ] ) ; assertOrUndefined ( options . borderDashPhase , 'options.borderDashPhase' , [ 'number' , ] ) ; assertIsOneOfOrUndefined ( options . borderLineCap , 'options.borderLineCap' , LineCapStyle ) ; assertIsOneOfOrUndefined ( options . blendMode , 'options.blendMode' , BlendMode ) ; const graphicsStateKey = this . maybeEmbedGraphicsState ( { opacity : options . opacity , borderOpacity : options . borderOpacity , blendMode : options . blendMode , } ) ; if ( ! ( 'color' in options ) && ! ( 'borderColor' in options ) ) { options . color = rgb ( 0 , 0 , 0 ) ; } const contentStream = this . getContentStream ( ) ; contentStream . push ( ... drawEllipse ( { x : options . x ?? this . x , y : options . y ?? this . y , xScale : options . xScale ?? 100 , yScale : options . yScale ?? 100 , rotate : options . rotate ?? undefined , color : options . color ?? undefined , borderColor : options . borderColor ?? undefined , borderWidth : options . borderWidth ?? 0 , borderDashArray : options . borderDashArray ?? undefined , borderDashPhase : options . borderDashPhase ?? undefined , borderLineCap : options . borderLineCap ?? undefined , graphicsState : graphicsStateKey , } ) ) ; } param options drawCircle ( options = { } ) { const { size = 100 } = options ; assertOrUndefined ( size , 'size' , [ 'number' ] ) ; this . drawEllipse ( { ... options , xScale : size , yScale : size } ) ; } private setOrEmbedFont ( font ? ) { const oldFont = this . font ; const oldFontKey = this . fontKey ; if ( font ) this . setFont ( font ) ; else this . getFont ( ) ; const newFont = this . font ! ; const newFontKey = this . fontKey ! ; return { oldFont , oldFontKey , newFont , newFontKey } ; } private getFont ( ) { if ( ! this . font || ! this . fontKey ) { const font = this . doc . embedStandardFont ( StandardFonts . Helvetica ) ; this . setFont ( font ) ; } return [ this . font ! , this . fontKey ! ] ; } private resetFont ( ) { this . font = undefined ; this . fontKey = undefined ; } private getContentStream ( useExisting = true ) { if ( useExisting && this . contentStream ) return this . contentStream ; this . contentStream = this . createContentStream ( ) ; this . contentStreamRef = this . doc . context . register ( this . contentStream ) ; this . node . addContentStream ( this . contentStreamRef ) ; return this . contentStream ; } private createContentStream ( ... operators ) { const dict = this . doc . context . obj ( { } ) ; const contentStream = PDFContentStream . of ( dict , operators ) ; return contentStream ; } private maybeEmbedGraphicsState ( options ) { const { opacity , borderOpacity , blendMode } = options ; if ( opacity === undefined && borderOpacity === undefined && blendMode === undefined ) { return undefined ; } const graphicsState = this . doc . context . obj ( { Type : 'ExtGState' , ca : opacity , CA : borderOpacity , BM : blendMode , } ) ; const key = this . node . newExtGState ( 'GS' , graphicsState ) ; return key ; } private scaleAnnot ( annot , x , y ) { const selectors = [ 'RD' , 'CL' , 'Vertices' , 'QuadPoints' , 'L' , 'Rect' ] ; for ( let idx = 0 , len = selectors . length ; idx < len ; idx ++ ) { const list = annot . lookup ( PDFName . of ( selectors [ idx ] ) ) ; if ( list instanceof PDFArray ) list . scalePDFNumbers ( x , y ) ; } const inkLists = annot . lookup ( PDFName . of ( 'InkList' ) ) ; if ( inkLists instanceof PDFArray ) { for ( let idx = 0 , len = inkLists . size ( ) ; idx < len ; idx ++ ) { const arr = inkLists . lookup ( idx ) ; if ( arr instanceof PDFArray ) arr . scalePDFNumbers ( x , y ) ; } } } }