16
16
import {
17
17
AnnotationActionEventType ,
18
18
AnnotationBorderStyleType ,
19
+ AnnotationEditorType ,
19
20
AnnotationFieldFlag ,
20
21
AnnotationFlag ,
21
22
AnnotationReplyType ,
@@ -24,12 +25,14 @@ import {
24
25
escapeString ,
25
26
getModificationDate ,
26
27
isAscii ,
28
+ LINE_DESCENT_FACTOR ,
27
29
LINE_FACTOR ,
28
30
OPS ,
29
31
RenderingIntentFlag ,
30
32
shadow ,
31
33
stringToPDFString ,
32
34
stringToUTF16BEString ,
35
+ stringToUTF8String ,
33
36
unreachable ,
34
37
Util ,
35
38
warn ,
@@ -45,6 +48,7 @@ import {
45
48
parseDefaultAppearance ,
46
49
} from "./default_appearance.js" ;
47
50
import { Dict , isName , Name , Ref , RefSet } from "./primitives.js" ;
51
+ import { writeDict , writeObject } from "./writer.js" ;
48
52
import { BaseStream } from "./base_stream.js" ;
49
53
import { bidi } from "./bidi.js" ;
50
54
import { Catalog } from "./catalog.js" ;
@@ -53,7 +57,6 @@ import { FileSpec } from "./file_spec.js";
53
57
import { ObjectLoader } from "./object_loader.js" ;
54
58
import { OperatorList } from "./operator_list.js" ;
55
59
import { StringStream } from "./stream.js" ;
56
- import { writeDict } from "./writer.js" ;
57
60
import { XFAFactory } from "./xfa/factory.js" ;
58
61
59
62
class AnnotationFactory {
@@ -237,6 +240,49 @@ class AnnotationFactory {
237
240
return - 1 ;
238
241
}
239
242
}
243
+
244
+ static async saveNewAnnotations ( evaluator , task , annotations ) {
245
+ const xref = evaluator . xref ;
246
+ let baseFontRef ;
247
+ const results = [ ] ;
248
+ const dependencies = [ ] ;
249
+ const promises = [ ] ;
250
+ for ( const annotation of annotations ) {
251
+ switch ( annotation . annotationType ) {
252
+ case AnnotationEditorType . FREETEXT :
253
+ if ( ! baseFontRef ) {
254
+ const baseFont = new Dict ( xref ) ;
255
+ baseFont . set ( "BaseFont" , Name . get ( "Helvetica" ) ) ;
256
+ baseFont . set ( "Type" , Name . get ( "Font" ) ) ;
257
+ baseFont . set ( "Subtype" , Name . get ( "Type1" ) ) ;
258
+ baseFont . set ( "Encoding" , Name . get ( "WinAnsiEncoding" ) ) ;
259
+ const buffer = [ ] ;
260
+ baseFontRef = xref . getNewRef ( ) ;
261
+ writeObject ( baseFontRef , baseFont , buffer , null ) ;
262
+ dependencies . push ( { ref : baseFontRef , data : buffer . join ( "" ) } ) ;
263
+ }
264
+ promises . push (
265
+ FreeTextAnnotation . createNewAnnotation (
266
+ xref ,
267
+ evaluator ,
268
+ task ,
269
+ annotation ,
270
+ baseFontRef ,
271
+ results ,
272
+ dependencies
273
+ )
274
+ ) ;
275
+ break ;
276
+ }
277
+ }
278
+
279
+ await Promise . all ( promises ) ;
280
+
281
+ return {
282
+ annotations : results ,
283
+ dependencies,
284
+ } ;
285
+ }
240
286
}
241
287
242
288
function getRgbColor ( color , defaultColor = new Uint8ClampedArray ( 3 ) ) {
@@ -1618,7 +1664,12 @@ class WidgetAnnotation extends Annotation {
1618
1664
) ;
1619
1665
}
1620
1666
1621
- const font = await this . _getFontData ( evaluator , task ) ;
1667
+ const font = await WidgetAnnotation . _getFontData (
1668
+ evaluator ,
1669
+ task ,
1670
+ this . data . defaultAppearanceData ,
1671
+ this . _fieldResources . mergedResources
1672
+ ) ;
1622
1673
const [ defaultAppearance , fontSize ] = this . _computeFontSize (
1623
1674
totalHeight - defaultPadding ,
1624
1675
totalWidth - 2 * hPadding ,
@@ -1701,7 +1752,7 @@ class WidgetAnnotation extends Annotation {
1701
1752
) ;
1702
1753
}
1703
1754
1704
- async _getFontData ( evaluator , task ) {
1755
+ static async _getFontData ( evaluator , task , appearanceData , resources ) {
1705
1756
const operatorList = new OperatorList ( ) ;
1706
1757
const initialState = {
1707
1758
font : null ,
@@ -1710,9 +1761,9 @@ class WidgetAnnotation extends Annotation {
1710
1761
} ,
1711
1762
} ;
1712
1763
1713
- const { fontName, fontSize } = this . data . defaultAppearanceData ;
1764
+ const { fontName, fontSize } = appearanceData ;
1714
1765
await evaluator . handleSetFont (
1715
- this . _fieldResources . mergedResources ,
1766
+ resources ,
1716
1767
[ fontName && Name . get ( fontName ) , fontSize ] ,
1717
1768
/* fontRef = */ null ,
1718
1769
operatorList ,
@@ -2641,7 +2692,12 @@ class ChoiceWidgetAnnotation extends WidgetAnnotation {
2641
2692
) ;
2642
2693
}
2643
2694
2644
- const font = await this . _getFontData ( evaluator , task ) ;
2695
+ const font = await WidgetAnnotation . _getFontData (
2696
+ evaluator ,
2697
+ task ,
2698
+ this . data . defaultAppearanceData ,
2699
+ this . _fieldResources . mergedResources
2700
+ ) ;
2645
2701
2646
2702
let defaultAppearance ;
2647
2703
let { fontSize } = this . data . defaultAppearanceData ;
@@ -2872,6 +2928,129 @@ class FreeTextAnnotation extends MarkupAnnotation {
2872
2928
2873
2929
this . data . annotationType = AnnotationType . FREETEXT ;
2874
2930
}
2931
+
2932
+ static async createNewAnnotation (
2933
+ xref ,
2934
+ evaluator ,
2935
+ task ,
2936
+ annotation ,
2937
+ baseFontRef ,
2938
+ results ,
2939
+ dependencies
2940
+ ) {
2941
+ const { color, fontSize, rect, user, value } = annotation ;
2942
+ const freetextRef = xref . getNewRef ( ) ;
2943
+ const freetext = new Dict ( xref ) ;
2944
+ freetext . set ( "Type" , Name . get ( "Annot" ) ) ;
2945
+ freetext . set ( "Subtype" , Name . get ( "FreeText" ) ) ;
2946
+ freetext . set ( "CreationDate" , `D:${ getModificationDate ( ) } ` ) ;
2947
+ freetext . set ( "Rect" , rect ) ;
2948
+ const da = `/Helv ${ fontSize } Tf ${ getPdfColor ( color ) } ` ;
2949
+ freetext . set ( "DA" , da ) ;
2950
+ freetext . set ( "Contents" , value ) ;
2951
+ freetext . set ( "F" , 4 ) ;
2952
+ freetext . set ( "Border" , [ 0 , 0 , 0 ] ) ;
2953
+ freetext . set ( "Rotate" , 0 ) ;
2954
+
2955
+ if ( user ) {
2956
+ freetext . set ( "T" , stringToUTF8String ( user ) ) ;
2957
+ }
2958
+
2959
+ const resources = new Dict ( xref ) ;
2960
+ const font = new Dict ( xref ) ;
2961
+ font . set ( "Helv" , baseFontRef ) ;
2962
+ resources . set ( "Font" , font ) ;
2963
+
2964
+ const helv = await WidgetAnnotation . _getFontData (
2965
+ evaluator ,
2966
+ task ,
2967
+ {
2968
+ fontName : "Helvetica" ,
2969
+ fontSize,
2970
+ } ,
2971
+ resources
2972
+ ) ;
2973
+
2974
+ const [ x1 , y1 , x2 , y2 ] = rect ;
2975
+ const w = x2 - x1 ;
2976
+ const h = y2 - y1 ;
2977
+
2978
+ const lines = value . split ( "\n" ) ;
2979
+ const scale = fontSize / 1000 ;
2980
+ let totalWidth = - Infinity ;
2981
+ const encodedLines = [ ] ;
2982
+ for ( let line of lines ) {
2983
+ line = helv . encodeString ( line ) . join ( "" ) ;
2984
+ encodedLines . push ( line ) ;
2985
+ let lineWidth = 0 ;
2986
+ const glyphs = helv . charsToGlyphs ( line ) ;
2987
+ for ( const glyph of glyphs ) {
2988
+ lineWidth += glyph . width * scale ;
2989
+ }
2990
+ totalWidth = Math . max ( totalWidth , lineWidth ) ;
2991
+ }
2992
+
2993
+ let hscale = 1 ;
2994
+ if ( totalWidth > w ) {
2995
+ hscale = w / totalWidth ;
2996
+ }
2997
+ let vscale = 1 ;
2998
+ const lineHeight = LINE_FACTOR * fontSize ;
2999
+ const lineDescent = LINE_DESCENT_FACTOR * fontSize ;
3000
+ const totalHeight = lineHeight * lines . length ;
3001
+ if ( totalHeight > h ) {
3002
+ vscale = h / totalHeight ;
3003
+ }
3004
+ const fscale = Math . min ( hscale , vscale ) ;
3005
+ const newFontSize = fontSize * fscale ;
3006
+ const buffer = [
3007
+ "q" ,
3008
+ `0 0 ${ numberToString ( w ) } ${ numberToString ( h ) } re W n` ,
3009
+ `BT` ,
3010
+ `1 0 0 1 0 ${ numberToString ( h + lineDescent ) } Tm 0 Tc ${ getPdfColor (
3011
+ color
3012
+ ) } `,
3013
+ `/Helv ${ numberToString ( newFontSize ) } Tf` ,
3014
+ ] ;
3015
+
3016
+ const vShift = numberToString ( lineHeight ) ;
3017
+ for ( const line of encodedLines ) {
3018
+ buffer . push ( `0 -${ vShift } Td (${ escapeString ( line ) } ) Tj` ) ;
3019
+ }
3020
+ buffer . push ( "ET" , "Q" ) ;
3021
+ const appearance = buffer . join ( "\n" ) ;
3022
+
3023
+ const appearanceStreamDict = new Dict ( xref ) ;
3024
+ appearanceStreamDict . set ( "FormType" , 1 ) ;
3025
+ appearanceStreamDict . set ( "Subtype" , Name . get ( "Form" ) ) ;
3026
+ appearanceStreamDict . set ( "Type" , Name . get ( "XObject" ) ) ;
3027
+ appearanceStreamDict . set ( "BBox" , [ 0 , 0 , w , h ] ) ;
3028
+ appearanceStreamDict . set ( "Length" , appearance . length ) ;
3029
+ appearanceStreamDict . set ( "Resources" , resources ) ;
3030
+
3031
+ const ap = new StringStream ( appearance ) ;
3032
+ ap . dict = appearanceStreamDict ;
3033
+
3034
+ buffer . length = 0 ;
3035
+ const apRef = xref . getNewRef ( ) ;
3036
+ let transform = xref . encrypt
3037
+ ? xref . encrypt . createCipherTransform ( apRef . num , apRef . gen )
3038
+ : null ;
3039
+ writeObject ( apRef , ap , buffer , transform ) ;
3040
+ dependencies . push ( { ref : apRef , data : buffer . join ( "" ) } ) ;
3041
+
3042
+ const n = new Dict ( xref ) ;
3043
+ n . set ( "N" , apRef ) ;
3044
+ freetext . set ( "AP" , n ) ;
3045
+
3046
+ buffer . length = 0 ;
3047
+ transform = xref . encrypt
3048
+ ? xref . encrypt . createCipherTransform ( freetextRef . num , freetextRef . gen )
3049
+ : null ;
3050
+ writeObject ( freetextRef , freetext , buffer , transform ) ;
3051
+
3052
+ results . push ( { ref : freetextRef , data : buffer . join ( "" ) } ) ;
3053
+ }
2875
3054
}
2876
3055
2877
3056
class LineAnnotation extends MarkupAnnotation {
0 commit comments