@@ -192,6 +192,33 @@ class AnnotationFactory {
192
192
}
193
193
}
194
194
195
+ function getRgbColor ( color ) {
196
+ const rgbColor = new Uint8ClampedArray ( 3 ) ;
197
+ if ( ! Array . isArray ( color ) ) {
198
+ return rgbColor ;
199
+ }
200
+
201
+ switch ( color . length ) {
202
+ case 0 : // Transparent, which we indicate with a null value
203
+ return null ;
204
+
205
+ case 1 : // Convert grayscale to RGB
206
+ ColorSpace . singletons . gray . getRgbItem ( color , 0 , rgbColor , 0 ) ;
207
+ return rgbColor ;
208
+
209
+ case 3 : // Convert RGB percentages to RGB
210
+ ColorSpace . singletons . rgb . getRgbItem ( color , 0 , rgbColor , 0 ) ;
211
+ return rgbColor ;
212
+
213
+ case 4 : // Convert CMYK to RGB
214
+ ColorSpace . singletons . cmyk . getRgbItem ( color , 0 , rgbColor , 0 ) ;
215
+ return rgbColor ;
216
+
217
+ default :
218
+ return rgbColor ;
219
+ }
220
+ }
221
+
195
222
function getQuadPoints ( dict , rect ) {
196
223
if ( ! dict . has ( "QuadPoints" ) ) {
197
224
return null ;
@@ -462,36 +489,7 @@ class Annotation {
462
489
* 4 (CMYK) elements
463
490
*/
464
491
setColor ( color ) {
465
- const rgbColor = new Uint8ClampedArray ( 3 ) ;
466
- if ( ! Array . isArray ( color ) ) {
467
- this . color = rgbColor ;
468
- return ;
469
- }
470
-
471
- switch ( color . length ) {
472
- case 0 : // Transparent, which we indicate with a null value
473
- this . color = null ;
474
- break ;
475
-
476
- case 1 : // Convert grayscale to RGB
477
- ColorSpace . singletons . gray . getRgbItem ( color , 0 , rgbColor , 0 ) ;
478
- this . color = rgbColor ;
479
- break ;
480
-
481
- case 3 : // Convert RGB percentages to RGB
482
- ColorSpace . singletons . rgb . getRgbItem ( color , 0 , rgbColor , 0 ) ;
483
- this . color = rgbColor ;
484
- break ;
485
-
486
- case 4 : // Convert CMYK to RGB
487
- ColorSpace . singletons . cmyk . getRgbItem ( color , 0 , rgbColor , 0 ) ;
488
- this . color = rgbColor ;
489
- break ;
490
-
491
- default :
492
- this . color = rgbColor ;
493
- break ;
494
- }
492
+ this . color = getRgbColor ( color ) ;
495
493
}
496
494
497
495
/**
@@ -929,7 +927,22 @@ class MarkupAnnotation extends Annotation {
929
927
buffer . push ( `${ fillColor [ 0 ] } ${ fillColor [ 1 ] } ${ fillColor [ 2 ] } rg` ) ;
930
928
}
931
929
932
- for ( const points of this . data . quadPoints ) {
930
+ let pointsArray = this . data . quadPoints ;
931
+ if ( ! pointsArray ) {
932
+ // If there are no quadpoints, the rectangle should be used instead.
933
+ // Convert the rectangle definition to a points array similar to how the
934
+ // quadpoints are defined.
935
+ pointsArray = [
936
+ [
937
+ { x : this . rectangle [ 0 ] , y : this . rectangle [ 3 ] } ,
938
+ { x : this . rectangle [ 2 ] , y : this . rectangle [ 3 ] } ,
939
+ { x : this . rectangle [ 0 ] , y : this . rectangle [ 1 ] } ,
940
+ { x : this . rectangle [ 2 ] , y : this . rectangle [ 1 ] } ,
941
+ ] ,
942
+ ] ;
943
+ }
944
+
945
+ for ( const points of pointsArray ) {
933
946
const [ mX , MX , mY , MY ] = pointsCallback ( buffer , points ) ;
934
947
minX = Math . min ( minX , mX ) ;
935
948
maxX = Math . max ( maxX , MX ) ;
@@ -2278,6 +2291,43 @@ class SquareAnnotation extends MarkupAnnotation {
2278
2291
super ( parameters ) ;
2279
2292
2280
2293
this . data . annotationType = AnnotationType . SQUARE ;
2294
+
2295
+ if ( ! this . appearance ) {
2296
+ // The default stroke color is black.
2297
+ const strokeColor = this . color
2298
+ ? Array . from ( this . color ) . map ( c => c / 255 )
2299
+ : [ 0 , 0 , 0 ] ;
2300
+
2301
+ // The default fill color is transparent.
2302
+ let fillColor = null ;
2303
+ let interiorColor = parameters . dict . getArray ( "IC" ) ;
2304
+ if ( interiorColor ) {
2305
+ interiorColor = getRgbColor ( interiorColor ) ;
2306
+ fillColor = interiorColor
2307
+ ? Array . from ( interiorColor ) . map ( c => c / 255 )
2308
+ : null ;
2309
+ }
2310
+
2311
+ this . _setDefaultAppearance ( {
2312
+ xref : parameters . xref ,
2313
+ extra : `${ this . borderStyle . width } w` ,
2314
+ strokeColor,
2315
+ fillColor,
2316
+ pointsCallback : ( buffer , points ) => {
2317
+ const x = points [ 2 ] . x + this . borderStyle . width / 2 ;
2318
+ const y = points [ 2 ] . y + this . borderStyle . width / 2 ;
2319
+ const width = points [ 3 ] . x - points [ 2 ] . x - this . borderStyle . width ;
2320
+ const height = points [ 1 ] . y - points [ 3 ] . y - this . borderStyle . width ;
2321
+ buffer . push ( `${ x } ${ y } ${ width } ${ height } re` ) ;
2322
+ if ( fillColor ) {
2323
+ buffer . push ( "B" ) ;
2324
+ } else {
2325
+ buffer . push ( "S" ) ;
2326
+ }
2327
+ return [ points [ 0 ] . x , points [ 1 ] . x , points [ 3 ] . y , points [ 1 ] . y ] ;
2328
+ } ,
2329
+ } ) ;
2330
+ }
2281
2331
}
2282
2332
}
2283
2333
@@ -2286,6 +2336,67 @@ class CircleAnnotation extends MarkupAnnotation {
2286
2336
super ( parameters ) ;
2287
2337
2288
2338
this . data . annotationType = AnnotationType . CIRCLE ;
2339
+
2340
+ if ( ! this . appearance ) {
2341
+ // The default stroke color is black.
2342
+ const strokeColor = this . color
2343
+ ? Array . from ( this . color ) . map ( c => c / 255 )
2344
+ : [ 0 , 0 , 0 ] ;
2345
+
2346
+ // The default fill color is transparent.
2347
+ let fillColor = null ;
2348
+ let interiorColor = parameters . dict . getArray ( "IC" ) ;
2349
+ if ( interiorColor ) {
2350
+ interiorColor = getRgbColor ( interiorColor ) ;
2351
+ fillColor = interiorColor
2352
+ ? Array . from ( interiorColor ) . map ( c => c / 255 )
2353
+ : null ;
2354
+ }
2355
+
2356
+ this . _setDefaultAppearance ( {
2357
+ xref : parameters . xref ,
2358
+ extra : `${ this . borderStyle . width } w` ,
2359
+ strokeColor,
2360
+ fillColor,
2361
+ pointsCallback : ( buffer , points ) => {
2362
+ // Circles are approximated by Bézier curves with four segments since
2363
+ // there is no circle primitive in the PDF specification. For the
2364
+ // control points distance, see https://stackoverflow.com/a/27863181.
2365
+ const controlPointsDistance = ( 4 / 3 ) * Math . tan ( Math . PI / ( 2 * 4 ) ) ;
2366
+
2367
+ const x0 = points [ 0 ] . x + this . borderStyle . width ;
2368
+ const y0 = points [ 0 ] . y - this . borderStyle . width ;
2369
+ const x1 = points [ 3 ] . x - this . borderStyle . width ;
2370
+ const y1 = points [ 3 ] . y + this . borderStyle . width ;
2371
+ const xMid = x0 + ( x1 - x0 ) / 2 ;
2372
+ const yMid = y0 + ( y1 - y0 ) / 2 ;
2373
+ const xOffset = ( ( x1 - x0 ) / 2 ) * controlPointsDistance ;
2374
+ const yOffset = ( ( y1 - y0 ) / 2 ) * controlPointsDistance ;
2375
+
2376
+ buffer . push ( `${ xMid } ${ y1 } m` ) ;
2377
+ buffer . push (
2378
+ `${ xMid + xOffset } ${ y1 } ${ x1 } ${ yMid + yOffset } ${ x1 } ${ yMid } c`
2379
+ ) ;
2380
+ buffer . push (
2381
+ `${ x1 } ${ yMid - yOffset } ${ xMid + xOffset } ${ y0 } ${ xMid } ${ y0 } c`
2382
+ ) ;
2383
+ buffer . push (
2384
+ `${ xMid - xOffset } ${ y0 } ${ x0 } ${ yMid - yOffset } ${ x0 } ${ yMid } c`
2385
+ ) ;
2386
+ buffer . push (
2387
+ `${ x0 } ${ yMid + yOffset } ${ xMid - xOffset } ${ y1 } ${ xMid } ${ y1 } c`
2388
+ ) ;
2389
+
2390
+ buffer . push ( "h" ) ;
2391
+ if ( fillColor ) {
2392
+ buffer . push ( "B" ) ;
2393
+ } else {
2394
+ buffer . push ( "S" ) ;
2395
+ }
2396
+ return [ points [ 0 ] . x , points [ 1 ] . x , points [ 3 ] . y , points [ 1 ] . y ] ;
2397
+ } ,
2398
+ } ) ;
2399
+ }
2289
2400
}
2290
2401
}
2291
2402
0 commit comments