Skip to content

Commit

Permalink
Merge pull request #15006 from calixteman/ink2
Browse files Browse the repository at this point in the history
[editor] Add support for saving newly added Ink
  • Loading branch information
calixteman authored Jun 9, 2022
2 parents 61a6534 + 36aae43 commit 5d88233
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 9 deletions.
105 changes: 101 additions & 4 deletions src/core/annotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,17 @@ class AnnotationFactory {
)
);
break;
case AnnotationEditorType.INK:
promises.push(
InkAnnotation.createNewAnnotation(
xref,
evaluator,
task,
annotation,
results,
dependencies
)
);
}
}

Expand Down Expand Up @@ -720,12 +731,18 @@ class Annotation {

let str = "";
if (this.backgroundColor) {
str = `${getPdfColor(this.backgroundColor)} ${rect} f `;
str = `${getPdfColor(
this.backgroundColor,
/* isFill */ true
)} ${rect} f `;
}

if (this.borderColor) {
const borderWidth = this.borderStyle.width || 1;
str += `${borderWidth} w ${getPdfColor(this.borderColor)} ${rect} S `;
str += `${borderWidth} w ${getPdfColor(
this.borderColor,
/* isFill */ false
)} ${rect} S `;
}

return str;
Expand Down Expand Up @@ -2945,7 +2962,7 @@ class FreeTextAnnotation extends MarkupAnnotation {
freetext.set("Subtype", Name.get("FreeText"));
freetext.set("CreationDate", `D:${getModificationDate()}`);
freetext.set("Rect", rect);
const da = `/Helv ${fontSize} Tf ${getPdfColor(color)}`;
const da = `/Helv ${fontSize} Tf ${getPdfColor(color, /* isFill */ true)}`;
freetext.set("DA", da);
freetext.set("Contents", value);
freetext.set("F", 4);
Expand Down Expand Up @@ -3008,7 +3025,8 @@ class FreeTextAnnotation extends MarkupAnnotation {
`0 0 ${numberToString(w)} ${numberToString(h)} re W n`,
`BT`,
`1 0 0 1 0 ${numberToString(h + lineDescent)} Tm 0 Tc ${getPdfColor(
color
color,
/* isFill */ true
)}`,
`/Helv ${numberToString(newFontSize)} Tf`,
];
Expand Down Expand Up @@ -3412,6 +3430,85 @@ class InkAnnotation extends MarkupAnnotation {
});
}
}

static async createNewAnnotation(
xref,
evaluator,
task,
annotation,
results,
others
) {
const inkRef = xref.getNewRef();
const ink = new Dict(xref);
ink.set("Type", Name.get("Annot"));
ink.set("Subtype", Name.get("Ink"));
ink.set("CreationDate", `D:${getModificationDate()}`);
ink.set("Rect", annotation.rect);
ink.set(
"InkList",
annotation.paths.map(p => p.points)
);
ink.set("F", 4);
ink.set("Border", [0, 0, 0]);
ink.set("Rotate", 0);

const [x1, y1, x2, y2] = annotation.rect;
const w = x2 - x1;
const h = y2 - y1;

const appearanceBuffer = [
`${annotation.thickness} w`,
`${getPdfColor(annotation.color, /* isFill */ false)}`,
];
const buffer = [];
for (const { bezier } of annotation.paths) {
buffer.length = 0;
buffer.push(
`${numberToString(bezier[0])} ${numberToString(bezier[1])} m`
);
for (let i = 2, ii = bezier.length; i < ii; i += 6) {
const curve = bezier
.slice(i, i + 6)
.map(numberToString)
.join(" ");
buffer.push(`${curve} c`);
}
buffer.push("S");
appearanceBuffer.push(buffer.join("\n"));
}
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", [0, 0, w, h]);
appearanceStreamDict.set("Length", appearance.length);

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

buffer.length = 0;
const apRef = xref.getNewRef();
let transform = xref.encrypt
? xref.encrypt.createCipherTransform(apRef.num, apRef.gen)
: null;
writeObject(apRef, ap, buffer, transform);
others.push({ ref: apRef, data: buffer.join("") });

const n = new Dict(xref);
n.set("N", apRef);
ink.set("AP", n);

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("") });
}
}

class HighlightAnnotation extends MarkupAnnotation {
Expand Down
11 changes: 7 additions & 4 deletions src/core/default_appearance.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,21 +82,24 @@ function parseDefaultAppearance(str) {
return new DefaultAppearanceEvaluator(str).parse();
}

function getPdfColor(color) {
function getPdfColor(color, isFill) {
if (color[0] === color[1] && color[1] === color[2]) {
const gray = color[0] / 255;
return `${numberToString(gray)} g`;
return `${numberToString(gray)} ${isFill ? "g" : "G"}`;
}
return (
Array.from(color)
.map(c => numberToString(c / 255))
.join(" ") + " rg"
.join(" ") + ` ${isFill ? "rg" : "RG"}`
);
}

// Create default appearance string from some information.
function createDefaultAppearance({ fontSize, fontName, fontColor }) {
return `/${escapePDFName(fontName)} ${fontSize} Tf ${getPdfColor(fontColor)}`;
return `/${escapePDFName(fontName)} ${fontSize} Tf ${getPdfColor(
fontColor,
/* isFill */ true
)}`;
}

export { createDefaultAppearance, getPdfColor, parseDefaultAppearance };
62 changes: 61 additions & 1 deletion test/unit/annotation_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3859,7 +3859,7 @@ describe("annotation", function () {
});

describe("FreeTextAnnotation", () => {
it("should create an new FreeText annotation", async () => {
it("should create a new FreeText annotation", async () => {
partialEvaluator.xref = new XRefMock();
const task = new WorkerTask("test FreeText creation");
const data = await AnnotationFactory.saveNewAnnotations(
Expand Down Expand Up @@ -3968,6 +3968,66 @@ describe("annotation", function () {
{ x: 4, y: 5 },
]);
});

it("should create a new Ink annotation", async function () {
partialEvaluator.xref = new XRefMock();
const task = new WorkerTask("test Ink creation");
const data = await AnnotationFactory.saveNewAnnotations(
partialEvaluator,
task,
[
{
annotationType: AnnotationEditorType.INK,
rect: [12, 34, 56, 78],
thickness: 1,
color: [0, 0, 0],
paths: [
{
bezier: [
10, 11, 12, 13, 14, 15, 16, 17, 22, 23, 24, 25, 26, 27,
],
points: [1, 2, 3, 4, 5, 6, 7, 8],
},
{
bezier: [
910, 911, 912, 913, 914, 915, 916, 917, 922, 923, 924, 925,
926, 927,
],
points: [91, 92, 93, 94, 95, 96, 97, 98],
},
],
},
]
);

const base = data.annotations[0].data.replace(/\(D:\d+\)/, "(date)");
expect(base).toEqual(
"1 0 obj\n" +
"<< /Type /Annot /Subtype /Ink /CreationDate (date) /Rect [12 34 56 78] " +
"/InkList [[1 2 3 4 5 6 7 8] [91 92 93 94 95 96 97 98]] /F 4 /Border [0 0 0] " +
"/Rotate 0 /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 [0 0 44 44] /Length 121>> stream\n" +
"1 w\n" +
"0 G\n" +
"10 11 m\n" +
"12 13 14 15 16 17 c\n" +
"22 23 24 25 26 27 c\n" +
"S\n" +
"910 911 m\n" +
"912 913 914 915 916 917 c\n" +
"922 923 924 925 926 927 c\n" +
"S\n" +
"endstream\n" +
"\n" +
"endobj\n"
);
});
});

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

0 comments on commit 5d88233

Please sign in to comment.