Implement rendering square/circle annotations without appearance stream
This commit is contained in:
parent
8e74278b65
commit
fa6cebf045
@ -192,6 +192,33 @@ class AnnotationFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getRgbColor(color) {
|
||||||
|
const rgbColor = new Uint8ClampedArray(3);
|
||||||
|
if (!Array.isArray(color)) {
|
||||||
|
return rgbColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (color.length) {
|
||||||
|
case 0: // Transparent, which we indicate with a null value
|
||||||
|
return null;
|
||||||
|
|
||||||
|
case 1: // Convert grayscale to RGB
|
||||||
|
ColorSpace.singletons.gray.getRgbItem(color, 0, rgbColor, 0);
|
||||||
|
return rgbColor;
|
||||||
|
|
||||||
|
case 3: // Convert RGB percentages to RGB
|
||||||
|
ColorSpace.singletons.rgb.getRgbItem(color, 0, rgbColor, 0);
|
||||||
|
return rgbColor;
|
||||||
|
|
||||||
|
case 4: // Convert CMYK to RGB
|
||||||
|
ColorSpace.singletons.cmyk.getRgbItem(color, 0, rgbColor, 0);
|
||||||
|
return rgbColor;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return rgbColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function getQuadPoints(dict, rect) {
|
function getQuadPoints(dict, rect) {
|
||||||
if (!dict.has("QuadPoints")) {
|
if (!dict.has("QuadPoints")) {
|
||||||
return null;
|
return null;
|
||||||
@ -462,36 +489,7 @@ class Annotation {
|
|||||||
* 4 (CMYK) elements
|
* 4 (CMYK) elements
|
||||||
*/
|
*/
|
||||||
setColor(color) {
|
setColor(color) {
|
||||||
const rgbColor = new Uint8ClampedArray(3);
|
this.color = getRgbColor(color);
|
||||||
if (!Array.isArray(color)) {
|
|
||||||
this.color = rgbColor;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (color.length) {
|
|
||||||
case 0: // Transparent, which we indicate with a null value
|
|
||||||
this.color = null;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1: // Convert grayscale to RGB
|
|
||||||
ColorSpace.singletons.gray.getRgbItem(color, 0, rgbColor, 0);
|
|
||||||
this.color = rgbColor;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3: // Convert RGB percentages to RGB
|
|
||||||
ColorSpace.singletons.rgb.getRgbItem(color, 0, rgbColor, 0);
|
|
||||||
this.color = rgbColor;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 4: // Convert CMYK to RGB
|
|
||||||
ColorSpace.singletons.cmyk.getRgbItem(color, 0, rgbColor, 0);
|
|
||||||
this.color = rgbColor;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
this.color = rgbColor;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -929,7 +927,22 @@ class MarkupAnnotation extends Annotation {
|
|||||||
buffer.push(`${fillColor[0]} ${fillColor[1]} ${fillColor[2]} rg`);
|
buffer.push(`${fillColor[0]} ${fillColor[1]} ${fillColor[2]} rg`);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const points of this.data.quadPoints) {
|
let pointsArray = this.data.quadPoints;
|
||||||
|
if (!pointsArray) {
|
||||||
|
// If there are no quadpoints, the rectangle should be used instead.
|
||||||
|
// Convert the rectangle definition to a points array similar to how the
|
||||||
|
// quadpoints are defined.
|
||||||
|
pointsArray = [
|
||||||
|
[
|
||||||
|
{ x: this.rectangle[0], y: this.rectangle[3] },
|
||||||
|
{ x: this.rectangle[2], y: this.rectangle[3] },
|
||||||
|
{ x: this.rectangle[0], y: this.rectangle[1] },
|
||||||
|
{ x: this.rectangle[2], y: this.rectangle[1] },
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const points of pointsArray) {
|
||||||
const [mX, MX, mY, MY] = pointsCallback(buffer, points);
|
const [mX, MX, mY, MY] = pointsCallback(buffer, points);
|
||||||
minX = Math.min(minX, mX);
|
minX = Math.min(minX, mX);
|
||||||
maxX = Math.max(maxX, MX);
|
maxX = Math.max(maxX, MX);
|
||||||
@ -2278,6 +2291,43 @@ class SquareAnnotation extends MarkupAnnotation {
|
|||||||
super(parameters);
|
super(parameters);
|
||||||
|
|
||||||
this.data.annotationType = AnnotationType.SQUARE;
|
this.data.annotationType = AnnotationType.SQUARE;
|
||||||
|
|
||||||
|
if (!this.appearance) {
|
||||||
|
// The default stroke color is black.
|
||||||
|
const strokeColor = this.color
|
||||||
|
? Array.from(this.color).map(c => c / 255)
|
||||||
|
: [0, 0, 0];
|
||||||
|
|
||||||
|
// The default fill color is transparent.
|
||||||
|
let fillColor = null;
|
||||||
|
let interiorColor = parameters.dict.getArray("IC");
|
||||||
|
if (interiorColor) {
|
||||||
|
interiorColor = getRgbColor(interiorColor);
|
||||||
|
fillColor = interiorColor
|
||||||
|
? Array.from(interiorColor).map(c => c / 255)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._setDefaultAppearance({
|
||||||
|
xref: parameters.xref,
|
||||||
|
extra: `${this.borderStyle.width} w`,
|
||||||
|
strokeColor,
|
||||||
|
fillColor,
|
||||||
|
pointsCallback: (buffer, points) => {
|
||||||
|
const x = points[2].x + this.borderStyle.width / 2;
|
||||||
|
const y = points[2].y + this.borderStyle.width / 2;
|
||||||
|
const width = points[3].x - points[2].x - this.borderStyle.width;
|
||||||
|
const height = points[1].y - points[3].y - this.borderStyle.width;
|
||||||
|
buffer.push(`${x} ${y} ${width} ${height} re`);
|
||||||
|
if (fillColor) {
|
||||||
|
buffer.push("B");
|
||||||
|
} else {
|
||||||
|
buffer.push("S");
|
||||||
|
}
|
||||||
|
return [points[0].x, points[1].x, points[3].y, points[1].y];
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2286,6 +2336,67 @@ class CircleAnnotation extends MarkupAnnotation {
|
|||||||
super(parameters);
|
super(parameters);
|
||||||
|
|
||||||
this.data.annotationType = AnnotationType.CIRCLE;
|
this.data.annotationType = AnnotationType.CIRCLE;
|
||||||
|
|
||||||
|
if (!this.appearance) {
|
||||||
|
// The default stroke color is black.
|
||||||
|
const strokeColor = this.color
|
||||||
|
? Array.from(this.color).map(c => c / 255)
|
||||||
|
: [0, 0, 0];
|
||||||
|
|
||||||
|
// The default fill color is transparent.
|
||||||
|
let fillColor = null;
|
||||||
|
let interiorColor = parameters.dict.getArray("IC");
|
||||||
|
if (interiorColor) {
|
||||||
|
interiorColor = getRgbColor(interiorColor);
|
||||||
|
fillColor = interiorColor
|
||||||
|
? Array.from(interiorColor).map(c => c / 255)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Circles are approximated by Bézier curves with four segments since
|
||||||
|
// there is no circle primitive in the PDF specification. For the control
|
||||||
|
// points distance, see https://stackoverflow.com/a/27863181.
|
||||||
|
const controlPointsDistance = (4 / 3) * Math.tan(Math.PI / (2 * 4));
|
||||||
|
|
||||||
|
this._setDefaultAppearance({
|
||||||
|
xref: parameters.xref,
|
||||||
|
extra: `${this.borderStyle.width} w`,
|
||||||
|
strokeColor,
|
||||||
|
fillColor,
|
||||||
|
pointsCallback: (buffer, points) => {
|
||||||
|
const x0 = points[0].x + this.borderStyle.width / 2;
|
||||||
|
const y0 = points[0].y - this.borderStyle.width / 2;
|
||||||
|
const x1 = points[3].x - this.borderStyle.width / 2;
|
||||||
|
const y1 = points[3].y + this.borderStyle.width / 2;
|
||||||
|
const xMid = x0 + (x1 - x0) / 2;
|
||||||
|
const yMid = y0 + (y1 - y0) / 2;
|
||||||
|
const xOffset = ((x1 - x0) / 2) * controlPointsDistance;
|
||||||
|
const yOffset = ((y1 - y0) / 2) * controlPointsDistance;
|
||||||
|
|
||||||
|
buffer.push(`${xMid} ${y1} m`);
|
||||||
|
buffer.push(
|
||||||
|
`${xMid + xOffset} ${y1} ${x1} ${yMid + yOffset} ${x1} ${yMid} c`
|
||||||
|
);
|
||||||
|
buffer.push(
|
||||||
|
`${x1} ${yMid - yOffset} ${xMid + xOffset} ${y0} ${xMid} ${y0} c`
|
||||||
|
);
|
||||||
|
buffer.push(
|
||||||
|
`${xMid - xOffset} ${y0} ${x0} ${yMid - yOffset} ${x0} ${yMid} c`
|
||||||
|
);
|
||||||
|
buffer.push(
|
||||||
|
`${x0} ${yMid + yOffset} ${xMid - xOffset} ${y1} ${xMid} ${y1} c`
|
||||||
|
);
|
||||||
|
|
||||||
|
buffer.push("h");
|
||||||
|
if (fillColor) {
|
||||||
|
buffer.push("B");
|
||||||
|
} else {
|
||||||
|
buffer.push("S");
|
||||||
|
}
|
||||||
|
return [points[0].x, points[1].x, points[3].y, points[1].y];
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1
test/pdfs/.gitignore
vendored
1
test/pdfs/.gitignore
vendored
@ -395,6 +395,7 @@
|
|||||||
!annotation-line.pdf
|
!annotation-line.pdf
|
||||||
!bug1669099.pdf
|
!bug1669099.pdf
|
||||||
!annotation-square-circle.pdf
|
!annotation-square-circle.pdf
|
||||||
|
!annotation-square-circle-without-appearance.pdf
|
||||||
!annotation-stamp.pdf
|
!annotation-stamp.pdf
|
||||||
!annotation-fileattachment.pdf
|
!annotation-fileattachment.pdf
|
||||||
!annotation-text-widget.pdf
|
!annotation-text-widget.pdf
|
||||||
|
BIN
test/pdfs/annotation-square-circle-without-appearance.pdf
Normal file
BIN
test/pdfs/annotation-square-circle-without-appearance.pdf
Normal file
Binary file not shown.
@ -4724,6 +4724,13 @@
|
|||||||
"type": "eq",
|
"type": "eq",
|
||||||
"annotations": true
|
"annotations": true
|
||||||
},
|
},
|
||||||
|
{ "id": "annotation-square-circle-without-appearance",
|
||||||
|
"file": "pdfs/annotation-square-circle-without-appearance.pdf",
|
||||||
|
"md5": "5b2a5e1e918137993d26a0cd8b0947f6",
|
||||||
|
"rounds": 1,
|
||||||
|
"annotations": true,
|
||||||
|
"type": "eq"
|
||||||
|
},
|
||||||
{ "id": "annotation-stamp",
|
{ "id": "annotation-stamp",
|
||||||
"file": "pdfs/annotation-stamp.pdf",
|
"file": "pdfs/annotation-stamp.pdf",
|
||||||
"md5": "0a04d7ce1ad103cb3c033d26855d6ec7",
|
"md5": "0a04d7ce1ad103cb3c033d26855d6ec7",
|
||||||
|
Loading…
Reference in New Issue
Block a user