Skip to content

Commit c5dc082

Browse files
authored
Merge pull request #15082 from calixteman/print_freetext
[Editor] Add support for printing newly added FreeText annotations
2 parents d72a85f + 30c63eb commit c5dc082

File tree

3 files changed

+125
-79
lines changed

3 files changed

+125
-79
lines changed

src/core/annotation.js

+79-78
Original file line numberDiff line numberDiff line change
@@ -265,21 +265,17 @@ class AnnotationFactory {
265265
promises.push(
266266
FreeTextAnnotation.createNewAnnotation(
267267
xref,
268-
evaluator,
269-
task,
270268
annotation,
271-
baseFontRef,
272269
results,
273-
dependencies
270+
dependencies,
271+
{ evaluator, task, baseFontRef }
274272
)
275273
);
276274
break;
277275
case AnnotationEditorType.INK:
278276
promises.push(
279277
InkAnnotation.createNewAnnotation(
280278
xref,
281-
evaluator,
282-
task,
283279
annotation,
284280
results,
285281
dependencies
@@ -306,11 +302,18 @@ class AnnotationFactory {
306302
for (const annotation of annotations) {
307303
switch (annotation.annotationType) {
308304
case AnnotationEditorType.FREETEXT:
305+
promises.push(
306+
FreeTextAnnotation.createNewPrintAnnotation(xref, annotation, {
307+
evaluator,
308+
task,
309+
})
310+
);
309311
break;
310312
case AnnotationEditorType.INK:
311313
promises.push(
312-
InkAnnotation.createNewPrintAnnotation(annotation, xref)
314+
InkAnnotation.createNewPrintAnnotation(xref, annotation)
313315
);
316+
break;
314317
}
315318
}
316319

@@ -1356,6 +1359,44 @@ class MarkupAnnotation extends Annotation {
13561359
// so `this.appearance` is not pushed yet in the `Annotation` constructor.
13571360
this._streams.push(this.appearance, appearanceStream);
13581361
}
1362+
1363+
static async createNewAnnotation(
1364+
xref,
1365+
annotation,
1366+
results,
1367+
dependencies,
1368+
params
1369+
) {
1370+
const annotationRef = xref.getNewRef();
1371+
const apRef = xref.getNewRef();
1372+
const annotationDict = this.createNewDict(annotation, xref, { apRef });
1373+
const ap = await this.createNewAppearanceStream(annotation, xref, params);
1374+
1375+
const buffer = [];
1376+
let transform = xref.encrypt
1377+
? xref.encrypt.createCipherTransform(apRef.num, apRef.gen)
1378+
: null;
1379+
writeObject(apRef, ap, buffer, transform);
1380+
dependencies.push({ ref: apRef, data: buffer.join("") });
1381+
1382+
buffer.length = 0;
1383+
transform = xref.encrypt
1384+
? xref.encrypt.createCipherTransform(annotationRef.num, annotationRef.gen)
1385+
: null;
1386+
writeObject(annotationRef, annotationDict, buffer, transform);
1387+
1388+
results.push({ ref: annotationRef, data: buffer.join("") });
1389+
}
1390+
1391+
static async createNewPrintAnnotation(xref, annotation, params) {
1392+
const ap = await this.createNewAppearanceStream(annotation, xref, params);
1393+
const annotationDict = this.createNewDict(annotation, xref, { ap });
1394+
1395+
return new this.prototype.constructor({
1396+
dict: annotationDict,
1397+
xref,
1398+
});
1399+
}
13591400
}
13601401

13611402
class WidgetAnnotation extends Annotation {
@@ -3157,17 +3198,8 @@ class FreeTextAnnotation extends MarkupAnnotation {
31573198
this.data.annotationType = AnnotationType.FREETEXT;
31583199
}
31593200

3160-
static async createNewAnnotation(
3161-
xref,
3162-
evaluator,
3163-
task,
3164-
annotation,
3165-
baseFontRef,
3166-
results,
3167-
dependencies
3168-
) {
3201+
static createNewDict(annotation, xref, { apRef, ap }) {
31693202
const { color, fontSize, rect, user, value } = annotation;
3170-
const freetextRef = xref.getNewRef();
31713203
const freetext = new Dict(xref);
31723204
freetext.set("Type", Name.get("Annot"));
31733205
freetext.set("Subtype", Name.get("FreeText"));
@@ -3184,9 +3216,35 @@ class FreeTextAnnotation extends MarkupAnnotation {
31843216
freetext.set("T", stringToUTF8String(user));
31853217
}
31863218

3219+
const n = new Dict(xref);
3220+
freetext.set("AP", n);
3221+
3222+
if (apRef) {
3223+
n.set("N", apRef);
3224+
} else {
3225+
n.set("N", ap);
3226+
}
3227+
3228+
return freetext;
3229+
}
3230+
3231+
static async createNewAppearanceStream(annotation, xref, params) {
3232+
const { baseFontRef, evaluator, task } = params;
3233+
const { color, fontSize, rect, value } = annotation;
3234+
31873235
const resources = new Dict(xref);
31883236
const font = new Dict(xref);
3189-
font.set("Helv", baseFontRef);
3237+
3238+
if (baseFontRef) {
3239+
font.set("Helv", baseFontRef);
3240+
} else {
3241+
const baseFont = new Dict(xref);
3242+
baseFont.set("BaseFont", Name.get("Helvetica"));
3243+
baseFont.set("Type", Name.get("Font"));
3244+
baseFont.set("Subtype", Name.get("Type1"));
3245+
baseFont.set("Encoding", Name.get("WinAnsiEncoding"));
3246+
font.set("Helv", baseFont);
3247+
}
31903248
resources.set("Font", font);
31913249

31923250
const helv = await WidgetAnnotation._getFontData(
@@ -3260,25 +3318,7 @@ class FreeTextAnnotation extends MarkupAnnotation {
32603318
const ap = new StringStream(appearance);
32613319
ap.dict = appearanceStreamDict;
32623320

3263-
buffer.length = 0;
3264-
const apRef = xref.getNewRef();
3265-
let transform = xref.encrypt
3266-
? xref.encrypt.createCipherTransform(apRef.num, apRef.gen)
3267-
: null;
3268-
writeObject(apRef, ap, buffer, transform);
3269-
dependencies.push({ ref: apRef, data: buffer.join("") });
3270-
3271-
const n = new Dict(xref);
3272-
n.set("N", apRef);
3273-
freetext.set("AP", n);
3274-
3275-
buffer.length = 0;
3276-
transform = xref.encrypt
3277-
? xref.encrypt.createCipherTransform(freetextRef.num, freetextRef.gen)
3278-
: null;
3279-
writeObject(freetextRef, freetext, buffer, transform);
3280-
3281-
results.push({ ref: freetextRef, data: buffer.join("") });
3321+
return ap;
32823322
}
32833323
}
32843324

@@ -3642,7 +3682,7 @@ class InkAnnotation extends MarkupAnnotation {
36423682
}
36433683
}
36443684

3645-
static createInkDict(annotation, xref, { apRef, ap }) {
3685+
static createNewDict(annotation, xref, { apRef, ap }) {
36463686
const ink = new Dict(xref);
36473687
ink.set("Type", Name.get("Annot"));
36483688
ink.set("Subtype", Name.get("Ink"));
@@ -3668,7 +3708,7 @@ class InkAnnotation extends MarkupAnnotation {
36683708
return ink;
36693709
}
36703710

3671-
static createNewAppearanceStream(annotation, xref) {
3711+
static async createNewAppearanceStream(annotation, xref, params) {
36723712
const [x1, y1, x2, y2] = annotation.rect;
36733713
const w = x2 - x1;
36743714
const h = y2 - y1;
@@ -3707,45 +3747,6 @@ class InkAnnotation extends MarkupAnnotation {
37073747

37083748
return ap;
37093749
}
3710-
3711-
static async createNewAnnotation(
3712-
xref,
3713-
evaluator,
3714-
task,
3715-
annotation,
3716-
results,
3717-
others
3718-
) {
3719-
const inkRef = xref.getNewRef();
3720-
const apRef = xref.getNewRef();
3721-
const ink = this.createInkDict(annotation, xref, { apRef });
3722-
const ap = this.createNewAppearanceStream(annotation, xref);
3723-
3724-
const buffer = [];
3725-
let transform = xref.encrypt
3726-
? xref.encrypt.createCipherTransform(apRef.num, apRef.gen)
3727-
: null;
3728-
writeObject(apRef, ap, buffer, transform);
3729-
others.push({ ref: apRef, data: buffer.join("") });
3730-
3731-
buffer.length = 0;
3732-
transform = xref.encrypt
3733-
? xref.encrypt.createCipherTransform(inkRef.num, inkRef.gen)
3734-
: null;
3735-
writeObject(inkRef, ink, buffer, transform);
3736-
3737-
results.push({ ref: inkRef, data: buffer.join("") });
3738-
}
3739-
3740-
static async createNewPrintAnnotation(annotation, xref) {
3741-
const ap = this.createNewAppearanceStream(annotation, xref);
3742-
const ink = this.createInkDict(annotation, xref, { ap });
3743-
3744-
return new InkAnnotation({
3745-
dict: ink,
3746-
xref,
3747-
});
3748-
}
37493750
}
37503751

37513752
class HighlightAnnotation extends MarkupAnnotation {

test/unit/annotation_spec.js

+44
Original file line numberDiff line numberDiff line change
@@ -4069,6 +4069,50 @@ describe("annotation", function () {
40694069
"endobj\n"
40704070
);
40714071
});
4072+
4073+
it("should render an added FreeText annotation for printing", async function () {
4074+
partialEvaluator.xref = new XRefMock();
4075+
const task = new WorkerTask("test FreeText printing");
4076+
const freetextAnnotation = (
4077+
await AnnotationFactory.printNewAnnotations(partialEvaluator, task, [
4078+
{
4079+
annotationType: AnnotationEditorType.FREETEXT,
4080+
rect: [12, 34, 56, 78],
4081+
fontSize: 10,
4082+
color: [0, 0, 0],
4083+
value: "A",
4084+
},
4085+
])
4086+
)[0];
4087+
4088+
const operatorList = await freetextAnnotation.getOperatorList(
4089+
partialEvaluator,
4090+
task,
4091+
RenderingIntentFlag.PRINT,
4092+
false,
4093+
null
4094+
);
4095+
4096+
expect(operatorList.fnArray.length).toEqual(16);
4097+
expect(operatorList.fnArray).toEqual([
4098+
OPS.beginAnnotation,
4099+
OPS.save,
4100+
OPS.constructPath,
4101+
OPS.clip,
4102+
OPS.endPath,
4103+
OPS.beginText,
4104+
OPS.setTextMatrix,
4105+
OPS.setCharSpacing,
4106+
OPS.setFillRGBColor,
4107+
OPS.dependency,
4108+
OPS.setFont,
4109+
OPS.moveText,
4110+
OPS.showText,
4111+
OPS.endText,
4112+
OPS.restore,
4113+
OPS.endAnnotation,
4114+
]);
4115+
});
40724116
});
40734117

40744118
describe("InkAnnotation", function () {

test/unit/test_utils.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@
1313
* limitations under the License.
1414
*/
1515

16+
import { NullStream, StringStream } from "../../src/core/stream.js";
1617
import { Page, PDFDocument } from "../../src/core/document.js";
1718
import { assert } from "../../src/shared/util.js";
1819
import { DocStats } from "../../src/core/core_utils.js";
1920
import { isNodeJS } from "../../src/shared/is_node.js";
2021
import { Ref } from "../../src/core/primitives.js";
21-
import { StringStream } from "../../src/core/stream.js";
2222

2323
const TEST_PDFS_PATH = isNodeJS ? "./test/pdfs/" : "../pdfs/";
2424

@@ -79,6 +79,7 @@ class XRefMock {
7979
this._map = Object.create(null);
8080
this.stats = new DocStats({ send: () => {} });
8181
this._newRefNum = null;
82+
this.stream = new NullStream();
8283

8384
for (const key in array) {
8485
const obj = array[key];

0 commit comments

Comments
 (0)