Merge pull request #15082 from calixteman/print_freetext

[Editor] Add support for printing newly added FreeText annotations
This commit is contained in:
calixteman 2022-06-22 16:19:42 +02:00 committed by GitHub
commit c5dc082da4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 125 additions and 79 deletions

View File

@ -265,12 +265,10 @@ class AnnotationFactory {
promises.push( promises.push(
FreeTextAnnotation.createNewAnnotation( FreeTextAnnotation.createNewAnnotation(
xref, xref,
evaluator,
task,
annotation, annotation,
baseFontRef,
results, results,
dependencies dependencies,
{ evaluator, task, baseFontRef }
) )
); );
break; break;
@ -278,8 +276,6 @@ class AnnotationFactory {
promises.push( promises.push(
InkAnnotation.createNewAnnotation( InkAnnotation.createNewAnnotation(
xref, xref,
evaluator,
task,
annotation, annotation,
results, results,
dependencies dependencies
@ -306,11 +302,18 @@ class AnnotationFactory {
for (const annotation of annotations) { for (const annotation of annotations) {
switch (annotation.annotationType) { switch (annotation.annotationType) {
case AnnotationEditorType.FREETEXT: case AnnotationEditorType.FREETEXT:
promises.push(
FreeTextAnnotation.createNewPrintAnnotation(xref, annotation, {
evaluator,
task,
})
);
break; break;
case AnnotationEditorType.INK: case AnnotationEditorType.INK:
promises.push( promises.push(
InkAnnotation.createNewPrintAnnotation(annotation, xref) InkAnnotation.createNewPrintAnnotation(xref, annotation)
); );
break;
} }
} }
@ -1356,6 +1359,44 @@ class MarkupAnnotation extends Annotation {
// so `this.appearance` is not pushed yet in the `Annotation` constructor. // so `this.appearance` is not pushed yet in the `Annotation` constructor.
this._streams.push(this.appearance, appearanceStream); this._streams.push(this.appearance, appearanceStream);
} }
static async createNewAnnotation(
xref,
annotation,
results,
dependencies,
params
) {
const annotationRef = xref.getNewRef();
const apRef = xref.getNewRef();
const annotationDict = this.createNewDict(annotation, xref, { apRef });
const ap = await this.createNewAppearanceStream(annotation, xref, params);
const buffer = [];
let transform = xref.encrypt
? xref.encrypt.createCipherTransform(apRef.num, apRef.gen)
: null;
writeObject(apRef, ap, buffer, transform);
dependencies.push({ ref: apRef, data: buffer.join("") });
buffer.length = 0;
transform = xref.encrypt
? xref.encrypt.createCipherTransform(annotationRef.num, annotationRef.gen)
: null;
writeObject(annotationRef, annotationDict, buffer, transform);
results.push({ ref: annotationRef, data: buffer.join("") });
}
static async createNewPrintAnnotation(xref, annotation, params) {
const ap = await this.createNewAppearanceStream(annotation, xref, params);
const annotationDict = this.createNewDict(annotation, xref, { ap });
return new this.prototype.constructor({
dict: annotationDict,
xref,
});
}
} }
class WidgetAnnotation extends Annotation { class WidgetAnnotation extends Annotation {
@ -3157,17 +3198,8 @@ class FreeTextAnnotation extends MarkupAnnotation {
this.data.annotationType = AnnotationType.FREETEXT; this.data.annotationType = AnnotationType.FREETEXT;
} }
static async createNewAnnotation( static createNewDict(annotation, xref, { apRef, ap }) {
xref,
evaluator,
task,
annotation,
baseFontRef,
results,
dependencies
) {
const { color, fontSize, rect, user, value } = annotation; const { color, fontSize, rect, user, value } = annotation;
const freetextRef = xref.getNewRef();
const freetext = new Dict(xref); const freetext = new Dict(xref);
freetext.set("Type", Name.get("Annot")); freetext.set("Type", Name.get("Annot"));
freetext.set("Subtype", Name.get("FreeText")); freetext.set("Subtype", Name.get("FreeText"));
@ -3184,9 +3216,35 @@ class FreeTextAnnotation extends MarkupAnnotation {
freetext.set("T", stringToUTF8String(user)); freetext.set("T", stringToUTF8String(user));
} }
const n = new Dict(xref);
freetext.set("AP", n);
if (apRef) {
n.set("N", apRef);
} else {
n.set("N", ap);
}
return freetext;
}
static async createNewAppearanceStream(annotation, xref, params) {
const { baseFontRef, evaluator, task } = params;
const { color, fontSize, rect, value } = annotation;
const resources = new Dict(xref); const resources = new Dict(xref);
const font = new Dict(xref); const font = new Dict(xref);
font.set("Helv", baseFontRef);
if (baseFontRef) {
font.set("Helv", baseFontRef);
} else {
const baseFont = new Dict(xref);
baseFont.set("BaseFont", Name.get("Helvetica"));
baseFont.set("Type", Name.get("Font"));
baseFont.set("Subtype", Name.get("Type1"));
baseFont.set("Encoding", Name.get("WinAnsiEncoding"));
font.set("Helv", baseFont);
}
resources.set("Font", font); resources.set("Font", font);
const helv = await WidgetAnnotation._getFontData( const helv = await WidgetAnnotation._getFontData(
@ -3260,25 +3318,7 @@ class FreeTextAnnotation extends MarkupAnnotation {
const ap = new StringStream(appearance); const ap = new StringStream(appearance);
ap.dict = appearanceStreamDict; ap.dict = appearanceStreamDict;
buffer.length = 0; return ap;
const apRef = xref.getNewRef();
let transform = xref.encrypt
? xref.encrypt.createCipherTransform(apRef.num, apRef.gen)
: null;
writeObject(apRef, ap, buffer, transform);
dependencies.push({ ref: apRef, data: buffer.join("") });
const n = new Dict(xref);
n.set("N", apRef);
freetext.set("AP", n);
buffer.length = 0;
transform = xref.encrypt
? xref.encrypt.createCipherTransform(freetextRef.num, freetextRef.gen)
: null;
writeObject(freetextRef, freetext, buffer, transform);
results.push({ ref: freetextRef, data: buffer.join("") });
} }
} }
@ -3642,7 +3682,7 @@ class InkAnnotation extends MarkupAnnotation {
} }
} }
static createInkDict(annotation, xref, { apRef, ap }) { static createNewDict(annotation, xref, { apRef, ap }) {
const ink = new Dict(xref); const ink = new Dict(xref);
ink.set("Type", Name.get("Annot")); ink.set("Type", Name.get("Annot"));
ink.set("Subtype", Name.get("Ink")); ink.set("Subtype", Name.get("Ink"));
@ -3668,7 +3708,7 @@ class InkAnnotation extends MarkupAnnotation {
return ink; return ink;
} }
static createNewAppearanceStream(annotation, xref) { static async createNewAppearanceStream(annotation, xref, params) {
const [x1, y1, x2, y2] = annotation.rect; const [x1, y1, x2, y2] = annotation.rect;
const w = x2 - x1; const w = x2 - x1;
const h = y2 - y1; const h = y2 - y1;
@ -3707,45 +3747,6 @@ class InkAnnotation extends MarkupAnnotation {
return ap; return ap;
} }
static async createNewAnnotation(
xref,
evaluator,
task,
annotation,
results,
others
) {
const inkRef = xref.getNewRef();
const apRef = xref.getNewRef();
const ink = this.createInkDict(annotation, xref, { apRef });
const ap = this.createNewAppearanceStream(annotation, xref);
const buffer = [];
let transform = xref.encrypt
? xref.encrypt.createCipherTransform(apRef.num, apRef.gen)
: null;
writeObject(apRef, ap, buffer, transform);
others.push({ ref: apRef, data: buffer.join("") });
buffer.length = 0;
transform = xref.encrypt
? xref.encrypt.createCipherTransform(inkRef.num, inkRef.gen)
: null;
writeObject(inkRef, ink, buffer, transform);
results.push({ ref: inkRef, data: buffer.join("") });
}
static async createNewPrintAnnotation(annotation, xref) {
const ap = this.createNewAppearanceStream(annotation, xref);
const ink = this.createInkDict(annotation, xref, { ap });
return new InkAnnotation({
dict: ink,
xref,
});
}
} }
class HighlightAnnotation extends MarkupAnnotation { class HighlightAnnotation extends MarkupAnnotation {

View File

@ -4069,6 +4069,50 @@ describe("annotation", function () {
"endobj\n" "endobj\n"
); );
}); });
it("should render an added FreeText annotation for printing", async function () {
partialEvaluator.xref = new XRefMock();
const task = new WorkerTask("test FreeText printing");
const freetextAnnotation = (
await AnnotationFactory.printNewAnnotations(partialEvaluator, task, [
{
annotationType: AnnotationEditorType.FREETEXT,
rect: [12, 34, 56, 78],
fontSize: 10,
color: [0, 0, 0],
value: "A",
},
])
)[0];
const operatorList = await freetextAnnotation.getOperatorList(
partialEvaluator,
task,
RenderingIntentFlag.PRINT,
false,
null
);
expect(operatorList.fnArray.length).toEqual(16);
expect(operatorList.fnArray).toEqual([
OPS.beginAnnotation,
OPS.save,
OPS.constructPath,
OPS.clip,
OPS.endPath,
OPS.beginText,
OPS.setTextMatrix,
OPS.setCharSpacing,
OPS.setFillRGBColor,
OPS.dependency,
OPS.setFont,
OPS.moveText,
OPS.showText,
OPS.endText,
OPS.restore,
OPS.endAnnotation,
]);
});
}); });
describe("InkAnnotation", function () { describe("InkAnnotation", function () {

View File

@ -13,12 +13,12 @@
* limitations under the License. * limitations under the License.
*/ */
import { NullStream, StringStream } from "../../src/core/stream.js";
import { Page, PDFDocument } from "../../src/core/document.js"; import { Page, PDFDocument } from "../../src/core/document.js";
import { assert } from "../../src/shared/util.js"; import { assert } from "../../src/shared/util.js";
import { DocStats } from "../../src/core/core_utils.js"; import { DocStats } from "../../src/core/core_utils.js";
import { isNodeJS } from "../../src/shared/is_node.js"; import { isNodeJS } from "../../src/shared/is_node.js";
import { Ref } from "../../src/core/primitives.js"; import { Ref } from "../../src/core/primitives.js";
import { StringStream } from "../../src/core/stream.js";
const TEST_PDFS_PATH = isNodeJS ? "./test/pdfs/" : "../pdfs/"; const TEST_PDFS_PATH = isNodeJS ? "./test/pdfs/" : "../pdfs/";
@ -79,6 +79,7 @@ class XRefMock {
this._map = Object.create(null); this._map = Object.create(null);
this.stats = new DocStats({ send: () => {} }); this.stats = new DocStats({ send: () => {} });
this._newRefNum = null; this._newRefNum = null;
this.stream = new NullStream();
for (const key in array) { for (const key in array) {
const obj = array[key]; const obj = array[key];