Merge pull request #15082 from calixteman/print_freetext
[Editor] Add support for printing newly added FreeText annotations
This commit is contained in:
commit
c5dc082da4
@ -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 {
|
||||||
|
@ -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 () {
|
||||||
|
@ -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];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user