Skip to content

Commit

Permalink
[Editor] Add support for saving/printing a newly added Highlight anno…
Browse files Browse the repository at this point in the history
…tation (bug 1865708)
  • Loading branch information
calixteman committed Nov 22, 2023
1 parent 83f0029 commit f8f4432
Show file tree
Hide file tree
Showing 4 changed files with 335 additions and 1 deletion.
109 changes: 109 additions & 0 deletions src/core/annotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,15 @@ class AnnotationFactory {
)
);
break;
case AnnotationEditorType.HIGHLIGHT:
promises.push(
HighlightAnnotation.createNewAnnotation(
xref,
annotation,
dependencies
)
);
break;
case AnnotationEditorType.INK:
promises.push(
InkAnnotation.createNewAnnotation(xref, annotation, dependencies)
Expand Down Expand Up @@ -429,6 +438,18 @@ class AnnotationFactory {
)
);
break;
case AnnotationEditorType.HIGHLIGHT:
promises.push(
HighlightAnnotation.createNewPrintAnnotation(
annotationGlobals,
xref,
annotation,
{
evaluatorOptions: options,
}
)
);
break;
case AnnotationEditorType.INK:
promises.push(
InkAnnotation.createNewPrintAnnotation(
Expand Down Expand Up @@ -4432,6 +4453,94 @@ class HighlightAnnotation extends MarkupAnnotation {
this.data.popupRef = null;
}
}

static createNewDict(annotation, xref, { apRef, ap }) {
const { color, opacity, rect, rotation, user, quadPoints } = annotation;
const highlight = new Dict(xref);
highlight.set("Type", Name.get("Annot"));
highlight.set("Subtype", Name.get("Highlight"));
highlight.set("CreationDate", `D:${getModificationDate()}`);
highlight.set("Rect", rect);
highlight.set("F", 4);
highlight.set("Border", [0, 0, 0]);
highlight.set("Rotate", rotation);
highlight.set("QuadPoints", quadPoints);

// Color.
highlight.set(
"C",
Array.from(color, c => c / 255)
);

// Opacity.
highlight.set("CA", opacity);

if (user) {
highlight.set(
"T",
isAscii(user) ? user : stringToUTF16String(user, /* bigEndian = */ true)
);
}

if (apRef || ap) {
const n = new Dict(xref);
highlight.set("AP", n);
n.set("N", apRef || ap);
}

return highlight;
}

static async createNewAppearanceStream(annotation, xref, params) {
const { color, rect, outlines, opacity } = annotation;

const appearanceBuffer = [
`${getPdfColor(color, /* isFill */ true)}`,
"/R0 gs",
];

const buffer = [];
for (const outline of outlines) {
buffer.length = 0;
buffer.push(
`${numberToString(outline[0])} ${numberToString(outline[1])} m`
);
for (let i = 2, ii = outline.length; i < ii; i += 2) {
buffer.push(
`${numberToString(outline[i])} ${numberToString(outline[i + 1])} l`
);
}
buffer.push("h");
appearanceBuffer.push(buffer.join("\n"));
}
appearanceBuffer.push("f*");
const appearance = appearanceBuffer.join("\n");

const appearanceStreamDict = new Dict(xref);
appearanceStreamDict.set("FormType", 1);
appearanceStreamDict.set("Subtype", Name.get("Form"));
appearanceStreamDict.set("Type", Name.get("XObject"));
appearanceStreamDict.set("BBox", rect);
appearanceStreamDict.set("Length", appearance.length);

const resources = new Dict(xref);
const extGState = new Dict(xref);
resources.set("ExtGState", extGState);
appearanceStreamDict.set("Resources", resources);
const r0 = new Dict(xref);
extGState.set("R0", r0);
r0.set("BM", Name.get("Multiply"));

if (opacity !== 1) {
r0.set("ca", opacity);
r0.set("Type", Name.get("ExtGState"));
}

const ap = new StringStream(appearance);
ap.dict = appearanceStreamDict;

return ap;
}
}

class UnderlineAnnotation extends MarkupAnnotation {
Expand Down
1 change: 1 addition & 0 deletions src/shared/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ const AnnotationEditorType = {
DISABLE: -1,
NONE: 0,
FREETEXT: 3,
HIGHLIGHT: 9,
STAMP: 13,
INK: 15,
};
Expand Down
133 changes: 133 additions & 0 deletions test/test_manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -8228,5 +8228,138 @@
"md5": "71591f11ee717e12887f529c84d5ae89",
"rounds": 1,
"type": "highlight"
},
{
"id": "tracemonkey-highlight-editor-print",
"file": "pdfs/tracemonkey.pdf",
"md5": "9a192d8b1a7dc652a19835f6f08098bd",
"rounds": 1,
"lastPage": 1,
"type": "eq",
"print": true,
"annotationStorage": {
"pdfjs_internal_editor_0": {
"annotationType": 9,
"color": [
255,
240,
102
],
"opacity": 0.4,
"quadPoints": [
80.53230785208449,
696.385203481336,
528.9234102584737,
696.385203481336,
80.53230785208449,
716.3783153330397,
528.9234102584737,
716.3783153330397,
263.70067924380567,
676.5056819423341,
346.3002317296901,
676.5056819423341,
263.70067924380567,
696.4987937940379,
346.3002317296901,
696.4987937940379
],
"outlines": [
[
79.866,
695.5343999999999,
79.866,
717.2352,
529.5636000000001,
717.2352,
529.5636000000001,
695.5343999999999,
346.94280000000003,
695.5343999999999,
346.94280000000003,
675.6551999999999,
263.0376,
675.6551999999999,
263.0376,
695.5343999999999
]
],
"pageIndex": 0,
"rect": [
79.866,
675.6551999999999,
529.5636000000001,
717.2352
],
"rotation": 0
}
}
},
{
"id": "tracemonkey-highlight-editor-save-print",
"file": "pdfs/tracemonkey.pdf",
"md5": "9a192d8b1a7dc652a19835f6f08098bd",
"rounds": 1,
"lastPage": 1,
"type": "eq",
"save": true,
"print": true,
"annotationStorage": {
"pdfjs_internal_editor_0": {
"annotationType": 9,
"color": [
249,
108,
147
],
"opacity": 0.4,
"quadPoints": [
80.53230785208449,
696.385203481336,
528.9234102584737,
696.385203481336,
80.53230785208449,
716.3783153330397,
528.9234102584737,
716.3783153330397,
263.70067924380567,
676.5056819423341,
346.3002317296901,
676.5056819423341,
263.70067924380567,
696.4987937940379,
346.3002317296901,
696.4987937940379
],
"outlines": [
[
79.866,
695.5343999999999,
79.866,
717.2352,
529.5636000000001,
717.2352,
529.5636000000001,
695.5343999999999,
346.94280000000003,
695.5343999999999,
346.94280000000003,
675.6551999999999,
263.0376,
675.6551999999999,
263.0376,
695.5343999999999
]
],
"pageIndex": 0,
"rect": [
79.866,
675.6551999999999,
529.5636000000001,
717.2352
],
"rotation": 0
}
}
}
]
93 changes: 92 additions & 1 deletion test/unit/annotation_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4554,7 +4554,7 @@ describe("annotation", function () {
});
});

describe("HightlightAnnotation", function () {
describe("HighlightAnnotation", function () {
it("should set quadpoints to null if not defined", async function () {
const highlightDict = new Dict();
highlightDict.set("Type", Name.get("Annot"));
Expand Down Expand Up @@ -4619,6 +4619,97 @@ describe("annotation", function () {
expect(data.annotationType).toEqual(AnnotationType.HIGHLIGHT);
expect(data.quadPoints).toEqual(null);
});

it("should create a new Highlight annotation", async function () {
partialEvaluator.xref = new XRefMock();
const task = new WorkerTask("test Highlight creation");
const data = await AnnotationFactory.saveNewAnnotations(
partialEvaluator,
task,
[
{
annotationType: AnnotationEditorType.HIGHLIGHT,
rect: [12, 34, 56, 78],
rotation: 0,
opacity: 1,
color: [0, 0, 0],
quadPoints: [1, 2, 3, 4, 5, 6, 7],
outlines: [
[8, 9, 10, 11],
[12, 13, 14, 15],
],
},
]
);

const base = data.annotations[0].data.replace(/\(D:\d+\)/, "(date)");
expect(base).toEqual(
"1 0 obj\n" +
"<< /Type /Annot /Subtype /Highlight /CreationDate (date) /Rect [12 34 56 78] " +
"/F 4 /Border [0 0 0] /Rotate 0 /QuadPoints [1 2 3 4 5 6 7] /C [0 0 0] " +
"/CA 1 /AP << /N 2 0 R>>>>\n" +
"endobj\n"
);

const appearance = data.dependencies[0].data;
expect(appearance).toEqual(
"2 0 obj\n" +
"<< /FormType 1 /Subtype /Form /Type /XObject /BBox [12 34 56 78] " +
"/Length 47 /Resources << /ExtGState << /R0 << /BM /Multiply>>>>>>>> stream\n" +
"0 g\n" +
"/R0 gs\n" +
"8 9 m\n" +
"10 11 l\n" +
"h\n" +
"12 13 m\n" +
"14 15 l\n" +
"h\n" +
"f*\n" +
"endstream\n" +
"endobj\n"
);
});

it("should render a new Highlight annotation for printing", async function () {
partialEvaluator.xref = new XRefMock();
const task = new WorkerTask("test Highlight printing");
const highlightAnnotation = (
await AnnotationFactory.printNewAnnotations(
annotationGlobalsMock,
partialEvaluator,
task,
[
{
annotationType: AnnotationEditorType.HIGHLIGHT,
rect: [12, 34, 56, 78],
rotation: 0,
opacity: 0.5,
color: [0, 255, 0],
quadPoints: [1, 2, 3, 4, 5, 6, 7],
outlines: [[8, 9, 10, 11]],
},
]
)
)[0];

const { opList } = await highlightAnnotation.getOperatorList(
partialEvaluator,
task,
RenderingIntentFlag.PRINT,
false,
null
);

expect(opList.argsArray.length).toEqual(6);
expect(opList.fnArray).toEqual([
OPS.beginAnnotation,
OPS.setFillRGBColor,
OPS.setGState,
OPS.constructPath,
OPS.eoFill,
OPS.endAnnotation,
]);
});
});

describe("UnderlineAnnotation", function () {
Expand Down

0 comments on commit f8f4432

Please sign in to comment.