Split annotation if position data exceeds the limit

This commit is contained in:
Martynas Bagdonas 2022-02-28 20:10:20 +02:00
parent b54466f089
commit b7f26c47c1
2 changed files with 171 additions and 0 deletions

View file

@ -26,6 +26,7 @@
"use strict";
Zotero.Annotations = new function () {
Zotero.defineProperty(this, 'ANNOTATION_POSITION_MAX_SIZE', { value: 65000 });
// Keep in sync with items.js::loadAnnotations()
Zotero.defineProperty(this, 'ANNOTATION_TYPE_HIGHLIGHT', { value: 1 });
Zotero.defineProperty(this, 'ANNOTATION_TYPE_NOTE', { value: 2 });
@ -234,4 +235,109 @@ Zotero.Annotations = new function () {
return item;
};
/**
* Split annotation if position exceed the limit
*
* @param {Object} annotation
* @returns {Array<Object>} annotations
*/
this.splitAnnotationJSON = function (annotation) {
let splitAnnotations = [];
let tmpAnnotation = null;
let totalLength = 0;
if (annotation.position.rects) {
for (let i = 0; i < annotation.position.rects.length; i++) {
let rect = annotation.position.rects[i];
if (!tmpAnnotation) {
tmpAnnotation = JSON.parse(JSON.stringify(annotation));
tmpAnnotation.key = Zotero.DataObjectUtilities.generateKey();
tmpAnnotation.position.rects = [];
totalLength = JSON.stringify(tmpAnnotation.position).length;
}
// [],
let length = rect.join(',').length + 3;
if (totalLength + length <= this.ANNOTATION_POSITION_MAX_SIZE) {
tmpAnnotation.position.rects.push(rect);
totalLength += length;
}
else if (!tmpAnnotation.position.rects.length) {
throw new Error(`Cannot fit single 'rect' into 'position'`);
}
else {
splitAnnotations.push(tmpAnnotation);
tmpAnnotation = null;
i--;
}
}
if (tmpAnnotation) {
splitAnnotations.push(tmpAnnotation);
}
}
else if (annotation.position.paths) {
for (let i = 0; i < annotation.position.paths.length; i++) {
let path = annotation.position.paths[i];
for (let j = 0; j < path.length; j += 2) {
if (!tmpAnnotation) {
tmpAnnotation = JSON.parse(JSON.stringify(annotation));
tmpAnnotation.key = Zotero.DataObjectUtilities.generateKey();
tmpAnnotation.position.paths = [[]];
totalLength = JSON.stringify(tmpAnnotation.position).length;
}
let point = [path[j], path[j + 1]];
// 1,2,
let length = point.join(',').length + 1;
if (totalLength + length <= this.ANNOTATION_POSITION_MAX_SIZE) {
tmpAnnotation.position.paths[tmpAnnotation.position.paths.length - 1].push(...point);
totalLength += length;
}
else if (tmpAnnotation.position.paths.length === 1
&& !tmpAnnotation.position.paths[tmpAnnotation.position.paths.length - 1].length) {
throw new Error(`Cannot fit single point into 'position'`);
}
else {
splitAnnotations.push(tmpAnnotation);
tmpAnnotation = null;
j -= 2;
}
}
// If not the last path
if (i !== annotation.position.paths.length - 1) {
// [],
totalLength += 3;
tmpAnnotation.position.paths.push([]);
}
}
if (tmpAnnotation) {
splitAnnotations.push(tmpAnnotation);
}
}
return splitAnnotations;
};
/**
* Split annotations
*
* @param {Zotero.Item[]} items
* @returns {Promise<void>}
*/
this.splitAnnotations = async function (items) {
if (!Array.isArray(items)) {
items = [items];
}
if (!items.every(item => item.isAnnotation())) {
throw new Error('All items must be annotations');
}
for (let item of items) {
if (item.annotationPosition.length <= this.ANNOTATION_POSITION_MAX_SIZE) {
continue;
}
let annotation = await this.toJSON(item);
let splitAnnotations = this.splitAnnotationJSON(annotation);
for (let splitAnnotation of splitAnnotations) {
await this.saveFromJSON(item.parentItem, splitAnnotation);
}
await item.eraseTx();
}
};
};

View file

@ -338,4 +338,69 @@ describe("Zotero.Annotations", function() {
assert.isNull(annotation.annotationPageLabel);
});
});
describe("#splitAnnotations()", function () {
it("should split a highlight annotation", async function () {
await Zotero.Items.erase(attachment.getAnnotations().map(x => x.id));
let annotation = await createAnnotation('highlight', attachment);
let position = {
pageIndex: 1,
rects: []
};
for (let i = 0; i < 10000; i++) {
position.rects.push([100, 200, 100, 200]);
}
annotation.annotationPosition = JSON.stringify(position);
annotation.annotationText = 'test';
await annotation.saveTx();
await Zotero.Annotations.splitAnnotations([annotation]);
let splitAnnotations = attachment.getAnnotations();
assert.equal(splitAnnotations.length, 3);
assert.equal(splitAnnotations[0].annotationPosition.length, 64987);
assert.equal(splitAnnotations[1].annotationPosition.length, 64987);
assert.equal(splitAnnotations[2].annotationPosition.length, 50101);
assert.equal(splitAnnotations[0].annotationText, 'test');
assert.equal(splitAnnotations[1].annotationText, 'test');
assert.equal(splitAnnotations[2].annotationText, 'test');
assert.equal(Zotero.Items.get(annotation.id), false);
await Zotero.Items.erase(splitAnnotations.map(x => x.id));
});
it("should split an ink annotation", async function () {
await Zotero.Items.erase(attachment.getAnnotations().map(x => x.id));
let annotation = await createAnnotation('ink', attachment);
let position = {
pageIndex: 1,
width: 2,
paths: []
};
for (let i = 0; i < 100; i++) {
let path = [];
for (let j = 0; j < 200; j++) {
path.push(100, 200);
}
position.paths.push(path);
}
annotation.annotationPosition = JSON.stringify(position);
annotation.annotationComment = 'test';
await annotation.saveTx();
await Zotero.Annotations.splitAnnotations([annotation]);
let splitAnnotations = attachment.getAnnotations();
assert.equal(splitAnnotations.length, 3);
assert.equal(splitAnnotations[0].annotationPosition.length, 64957);
assert.equal(splitAnnotations[1].annotationPosition.length, 64951);
assert.equal(splitAnnotations[2].annotationPosition.length, 30401);
assert.equal(splitAnnotations[0].annotationComment, 'test');
assert.equal(splitAnnotations[1].annotationComment, 'test');
assert.equal(splitAnnotations[2].annotationComment, 'test');
assert.equal(Zotero.Items.get(annotation.id), false);
await Zotero.Items.erase(splitAnnotations.map(x => x.id));
});
});
})