@@ -22,18 +22,20 @@ import {
22
22
AnnotationType ,
23
23
assert ,
24
24
escapeString ,
25
+ getModificationDate ,
25
26
isString ,
26
27
OPS ,
27
28
stringToPDFString ,
28
29
Util ,
29
30
warn ,
30
31
} from "../shared/util.js" ;
31
32
import { Catalog , FileSpec , ObjectLoader } from "./obj.js" ;
32
- import { Dict , isDict , isName , isRef , isStream } from "./primitives.js" ;
33
+ import { Dict , isDict , isName , isRef , isStream , Name } from "./primitives.js" ;
33
34
import { ColorSpace } from "./colorspace.js" ;
34
35
import { getInheritableProperty } from "./core_utils.js" ;
35
36
import { OperatorList } from "./operator_list.js" ;
36
37
import { StringStream } from "./stream.js" ;
38
+ import { writeDict } from "./writer.js" ;
37
39
38
40
class AnnotationFactory {
39
41
/**
@@ -68,6 +70,7 @@ class AnnotationFactory {
68
70
if ( ! isDict ( dict ) ) {
69
71
return undefined ;
70
72
}
73
+
71
74
const id = isRef ( ref ) ? ref . toString ( ) : `annot_${ idFactory . createObjId ( ) } ` ;
72
75
73
76
// Determine the annotation's subtype.
@@ -77,6 +80,7 @@ class AnnotationFactory {
77
80
// Return the right annotation object based on the subtype and field type.
78
81
const parameters = {
79
82
xref,
83
+ ref,
80
84
dict,
81
85
subtype,
82
86
id,
@@ -550,6 +554,10 @@ class Annotation {
550
554
} ) ;
551
555
} ) ;
552
556
}
557
+
558
+ async save ( evaluator , task , annotationStorage ) {
559
+ return null ;
560
+ }
553
561
}
554
562
555
563
/**
@@ -791,6 +799,7 @@ class WidgetAnnotation extends Annotation {
791
799
792
800
const dict = params . dict ;
793
801
const data = this . data ;
802
+ this . ref = params . ref ;
794
803
795
804
data . annotationType = AnnotationType . WIDGET ;
796
805
data . fieldName = this . _constructFieldName ( dict ) ;
@@ -953,6 +962,78 @@ class WidgetAnnotation extends Annotation {
953
962
) ;
954
963
}
955
964
965
+ async save ( evaluator , task , annotationStorage ) {
966
+ if ( this . data . fieldValue === annotationStorage [ this . data . id ] ) {
967
+ return null ;
968
+ }
969
+
970
+ let appearance = await this . _getAppearance (
971
+ evaluator ,
972
+ task ,
973
+ annotationStorage
974
+ ) ;
975
+ if ( appearance === null ) {
976
+ return null ;
977
+ }
978
+
979
+ const dict = evaluator . xref . fetchIfRef ( this . ref ) ;
980
+ if ( ! isDict ( dict ) ) {
981
+ return null ;
982
+ }
983
+
984
+ const bbox = [
985
+ 0 ,
986
+ 0 ,
987
+ this . data . rect [ 2 ] - this . data . rect [ 0 ] ,
988
+ this . data . rect [ 3 ] - this . data . rect [ 1 ] ,
989
+ ] ;
990
+
991
+ const newRef = evaluator . xref . getNewRef ( ) ;
992
+ const AP = new Dict ( evaluator . xref ) ;
993
+ AP . set ( "N" , newRef ) ;
994
+
995
+ const value = annotationStorage [ this . data . id ] ;
996
+ const encrypt = evaluator . xref . encrypt ;
997
+ let originalTransform = null ;
998
+ let newTransform = null ;
999
+ if ( encrypt ) {
1000
+ originalTransform = encrypt . createCipherTransform (
1001
+ this . ref . num ,
1002
+ this . ref . gen
1003
+ ) ;
1004
+ newTransform = encrypt . createCipherTransform ( newRef . num , newRef . gen ) ;
1005
+ appearance = newTransform . encryptString ( appearance ) ;
1006
+ }
1007
+
1008
+ dict . set ( "V" , value ) ;
1009
+ dict . set ( "AP" , AP ) ;
1010
+ dict . set ( "M" , `D:${ getModificationDate ( ) } ` ) ;
1011
+
1012
+ const appearanceDict = new Dict ( evaluator . xref ) ;
1013
+ appearanceDict . set ( "Length" , appearance . length ) ;
1014
+ appearanceDict . set ( "Subtype" , Name . get ( "Form" ) ) ;
1015
+ appearanceDict . set ( "Resources" , this . fieldResources ) ;
1016
+ appearanceDict . set ( "BBox" , bbox ) ;
1017
+
1018
+ const bufferOriginal = [ `${ this . ref . num } ${ this . ref . gen } obj\n` ] ;
1019
+ writeDict ( dict , bufferOriginal , originalTransform ) ;
1020
+ bufferOriginal . push ( "\nendobj\n" ) ;
1021
+
1022
+ const bufferNew = [ `${ newRef . num } ${ newRef . gen } obj\n` ] ;
1023
+ writeDict ( appearanceDict , bufferNew , newTransform ) ;
1024
+ bufferNew . push ( " stream\n" ) ;
1025
+ bufferNew . push ( appearance ) ;
1026
+ bufferNew . push ( "\nendstream\nendobj\n" ) ;
1027
+
1028
+ return [
1029
+ // data for the original object
1030
+ // V field changed + reference for new AP
1031
+ { ref : this . ref , data : bufferOriginal . join ( "" ) } ,
1032
+ // data for the new AP
1033
+ { ref : newRef , data : bufferNew . join ( "" ) } ,
1034
+ ] ;
1035
+ }
1036
+
956
1037
async _getAppearance ( evaluator , task , annotationStorage ) {
957
1038
const isPassword = this . hasFieldFlag ( AnnotationFieldFlag . PASSWORD ) ;
958
1039
if ( ! annotationStorage || isPassword ) {
@@ -1312,6 +1393,111 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
1312
1393
) ;
1313
1394
}
1314
1395
1396
+ async save ( evaluator , task , annotationStorage ) {
1397
+ if ( this . data . checkBox ) {
1398
+ return this . _saveCheckbox ( evaluator , task , annotationStorage ) ;
1399
+ }
1400
+
1401
+ if ( this . data . radioButton ) {
1402
+ return this . _saveRadioButton ( evaluator , task , annotationStorage ) ;
1403
+ }
1404
+
1405
+ return super . save ( evaluator , task , annotationStorage ) ;
1406
+ }
1407
+
1408
+ async _saveCheckbox ( evaluator , task , annotationStorage ) {
1409
+ const defaultValue = this . data . fieldValue && this . data . fieldValue !== "Off" ;
1410
+ const value = annotationStorage [ this . data . id ] ;
1411
+
1412
+ if ( defaultValue === value ) {
1413
+ return null ;
1414
+ }
1415
+
1416
+ const dict = evaluator . xref . fetchIfRef ( this . ref ) ;
1417
+ if ( ! isDict ( dict ) ) {
1418
+ return null ;
1419
+ }
1420
+
1421
+ const name = Name . get ( value ? this . data . exportValue : "Off" ) ;
1422
+ dict . set ( "V" , name ) ;
1423
+ dict . set ( "AS" , name ) ;
1424
+ dict . set ( "M" , `D:${ getModificationDate ( ) } ` ) ;
1425
+
1426
+ const encrypt = evaluator . xref . encrypt ;
1427
+ let originalTransform = null ;
1428
+ if ( encrypt ) {
1429
+ originalTransform = encrypt . createCipherTransform (
1430
+ this . ref . num ,
1431
+ this . ref . gen
1432
+ ) ;
1433
+ }
1434
+
1435
+ const buffer = [ `${ this . ref . num } ${ this . ref . gen } obj\n` ] ;
1436
+ writeDict ( dict , buffer , originalTransform ) ;
1437
+ buffer . push ( "\nendobj\n" ) ;
1438
+
1439
+ return [ { ref : this . ref , data : buffer . join ( "" ) } ] ;
1440
+ }
1441
+
1442
+ async _saveRadioButton ( evaluator , task , annotationStorage ) {
1443
+ const defaultValue = this . data . fieldValue === this . data . buttonValue ;
1444
+ const value = annotationStorage [ this . data . id ] ;
1445
+
1446
+ if ( defaultValue === value ) {
1447
+ return null ;
1448
+ }
1449
+
1450
+ const dict = evaluator . xref . fetchIfRef ( this . ref ) ;
1451
+ if ( ! isDict ( dict ) ) {
1452
+ return null ;
1453
+ }
1454
+
1455
+ const name = Name . get ( value ? this . data . buttonValue : "Off" ) ;
1456
+ let parentBuffer = null ;
1457
+ const encrypt = evaluator . xref . encrypt ;
1458
+
1459
+ if ( value ) {
1460
+ if ( isRef ( this . parent ) ) {
1461
+ const parent = evaluator . xref . fetch ( this . parent ) ;
1462
+ let parentTransform = null ;
1463
+ if ( encrypt ) {
1464
+ parentTransform = encrypt . createCipherTransform (
1465
+ this . parent . num ,
1466
+ this . parent . gen
1467
+ ) ;
1468
+ }
1469
+ parent . set ( "V" , name ) ;
1470
+ parentBuffer = [ `${ this . parent . num } ${ this . parent . gen } obj\n` ] ;
1471
+ writeDict ( parent , parentBuffer , parentTransform ) ;
1472
+ parentBuffer . push ( "\nendobj\n" ) ;
1473
+ } else if ( isDict ( this . parent ) ) {
1474
+ this . parent . set ( "V" , name ) ;
1475
+ }
1476
+ }
1477
+
1478
+ dict . set ( "AS" , name ) ;
1479
+ dict . set ( "M" , `D:${ getModificationDate ( ) } ` ) ;
1480
+
1481
+ let originalTransform = null ;
1482
+ if ( encrypt ) {
1483
+ originalTransform = encrypt . createCipherTransform (
1484
+ this . ref . num ,
1485
+ this . ref . gen
1486
+ ) ;
1487
+ }
1488
+
1489
+ const buffer = [ `${ this . ref . num } ${ this . ref . gen } obj\n` ] ;
1490
+ writeDict ( dict , buffer , originalTransform ) ;
1491
+ buffer . push ( "\nendobj\n" ) ;
1492
+
1493
+ const newRefs = [ { ref : this . ref , data : buffer . join ( "" ) } ] ;
1494
+ if ( parentBuffer !== null ) {
1495
+ newRefs . push ( { ref : this . parent , data : parentBuffer . join ( "" ) } ) ;
1496
+ }
1497
+
1498
+ return newRefs ;
1499
+ }
1500
+
1315
1501
_processCheckBox ( params ) {
1316
1502
if ( isName ( this . data . fieldValue ) ) {
1317
1503
this . data . fieldValue = this . data . fieldValue . name ;
@@ -1354,6 +1540,7 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
1354
1540
if ( isDict ( fieldParent ) && fieldParent . has ( "V" ) ) {
1355
1541
const fieldParentValue = fieldParent . get ( "V" ) ;
1356
1542
if ( isName ( fieldParentValue ) ) {
1543
+ this . parent = params . dict . getRaw ( "Parent" ) ;
1357
1544
this . data . fieldValue = fieldParentValue . name ;
1358
1545
}
1359
1546
}
0 commit comments