mirror of
https://github.com/mozilla/pdf.js.git
synced 2025-04-22 16:18:08 +02:00
Improve parseAppearanceStream
to handle more "complex" ColorSpaces
The existing code is unable to *correctly* extract the color from the appearance-stream when the ColorSpace-data is "complex". To reproduce this: - Open `freetexts.pdf` in the viewer. - Note the purple color of the "Hello World from Preview" annotation. - Enable any of the Editors. - Note how the relevant annotation is now black.
This commit is contained in:
parent
8281bb8858
commit
6442a6cc4e
4 changed files with 201 additions and 42 deletions
|
@ -148,8 +148,7 @@ class AnnotationFactory {
|
|||
needAppearances:
|
||||
!collectFields && acroFormDict.get("NeedAppearances") === true,
|
||||
pageIndex,
|
||||
isOffscreenCanvasSupported:
|
||||
pdfManager.evaluatorOptions.isOffscreenCanvasSupported,
|
||||
evaluatorOptions: pdfManager.evaluatorOptions,
|
||||
};
|
||||
|
||||
switch (subtype) {
|
||||
|
@ -341,7 +340,7 @@ class AnnotationFactory {
|
|||
xref,
|
||||
annotation,
|
||||
dependencies,
|
||||
image
|
||||
{ image }
|
||||
)
|
||||
);
|
||||
break;
|
||||
|
@ -364,8 +363,7 @@ class AnnotationFactory {
|
|||
return null;
|
||||
}
|
||||
|
||||
const xref = evaluator.xref;
|
||||
const { isOffscreenCanvasSupported } = evaluator.options;
|
||||
const { options, xref } = evaluator;
|
||||
const promises = [];
|
||||
for (const annotation of annotations) {
|
||||
if (annotation.deleted) {
|
||||
|
@ -377,19 +375,19 @@ class AnnotationFactory {
|
|||
FreeTextAnnotation.createNewPrintAnnotation(xref, annotation, {
|
||||
evaluator,
|
||||
task,
|
||||
isOffscreenCanvasSupported,
|
||||
evaluatorOptions: options,
|
||||
})
|
||||
);
|
||||
break;
|
||||
case AnnotationEditorType.INK:
|
||||
promises.push(
|
||||
InkAnnotation.createNewPrintAnnotation(xref, annotation, {
|
||||
isOffscreenCanvasSupported,
|
||||
evaluatorOptions: options,
|
||||
})
|
||||
);
|
||||
break;
|
||||
case AnnotationEditorType.STAMP:
|
||||
if (!isOffscreenCanvasSupported) {
|
||||
if (!options.isOffscreenCanvasSupported) {
|
||||
break;
|
||||
}
|
||||
const image = await imagePromises.get(annotation.bitmapId);
|
||||
|
@ -402,7 +400,10 @@ class AnnotationFactory {
|
|||
image.imageStream = image.smaskStream = null;
|
||||
}
|
||||
promises.push(
|
||||
StampAnnotation.createNewPrintAnnotation(xref, annotation, image)
|
||||
StampAnnotation.createNewPrintAnnotation(xref, annotation, {
|
||||
image,
|
||||
evaluatorOptions: options,
|
||||
})
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
@ -600,7 +601,8 @@ class Annotation {
|
|||
this.data.pageIndex = params.pageIndex;
|
||||
}
|
||||
|
||||
this._isOffscreenCanvasSupported = params.isOffscreenCanvasSupported;
|
||||
this._isOffscreenCanvasSupported =
|
||||
params.evaluatorOptions.isOffscreenCanvasSupported;
|
||||
this._fallbackFontDict = null;
|
||||
this._needAppearances = false;
|
||||
}
|
||||
|
@ -1587,7 +1589,7 @@ class MarkupAnnotation extends Annotation {
|
|||
const newAnnotation = new this.prototype.constructor({
|
||||
dict: annotationDict,
|
||||
xref,
|
||||
isOffscreenCanvasSupported: params.isOffscreenCanvasSupported,
|
||||
evaluatorOptions: params.evaluatorOptions,
|
||||
});
|
||||
|
||||
if (annotation.ref) {
|
||||
|
@ -3648,11 +3650,15 @@ class FreeTextAnnotation extends MarkupAnnotation {
|
|||
|
||||
this.data.hasOwnCanvas = true;
|
||||
|
||||
const { xref } = params;
|
||||
const { evaluatorOptions, xref } = params;
|
||||
this.data.annotationType = AnnotationType.FREETEXT;
|
||||
this.setDefaultAppearance(params);
|
||||
if (this.appearance) {
|
||||
const { fontColor, fontSize } = parseAppearanceStream(this.appearance);
|
||||
const { fontColor, fontSize } = parseAppearanceStream(
|
||||
this.appearance,
|
||||
evaluatorOptions,
|
||||
xref
|
||||
);
|
||||
this.data.defaultAppearanceData.fontColor = fontColor;
|
||||
this.data.defaultAppearanceData.fontSize = fontSize || 10;
|
||||
} else if (this._isOffscreenCanvasSupported) {
|
||||
|
@ -4570,7 +4576,7 @@ class StampAnnotation extends MarkupAnnotation {
|
|||
|
||||
static async createNewAppearanceStream(annotation, xref, params) {
|
||||
const { rotation } = annotation;
|
||||
const { imageRef, width, height } = params;
|
||||
const { imageRef, width, height } = params.image;
|
||||
const resources = new Dict(xref);
|
||||
const xobject = new Dict(xref);
|
||||
resources.set("XObject", xobject);
|
||||
|
|
|
@ -20,9 +20,17 @@ import {
|
|||
numberToString,
|
||||
stringToUTF16HexString,
|
||||
} from "./core_utils.js";
|
||||
import { LINE_DESCENT_FACTOR, LINE_FACTOR, OPS, warn } from "../shared/util.js";
|
||||
import {
|
||||
LINE_DESCENT_FACTOR,
|
||||
LINE_FACTOR,
|
||||
OPS,
|
||||
shadow,
|
||||
warn,
|
||||
} from "../shared/util.js";
|
||||
import { ColorSpace } from "./colorspace.js";
|
||||
import { EvaluatorPreprocessor } from "./evaluator.js";
|
||||
import { LocalColorSpaceCache } from "./image_utils.js";
|
||||
import { PDFFunctionFactory } from "./function.js";
|
||||
import { StringStream } from "./stream.js";
|
||||
|
||||
class DefaultAppearanceEvaluator extends EvaluatorPreprocessor {
|
||||
|
@ -88,9 +96,13 @@ function parseDefaultAppearance(str) {
|
|||
}
|
||||
|
||||
class AppearanceStreamEvaluator extends EvaluatorPreprocessor {
|
||||
constructor(stream) {
|
||||
constructor(stream, evaluatorOptions, xref) {
|
||||
super(stream);
|
||||
this.stream = stream;
|
||||
this.evaluatorOptions = evaluatorOptions;
|
||||
this.xref = xref;
|
||||
|
||||
this.resources = stream.dict?.get("Resources");
|
||||
}
|
||||
|
||||
parse() {
|
||||
|
@ -103,6 +115,7 @@ class AppearanceStreamEvaluator extends EvaluatorPreprocessor {
|
|||
fontSize: 0,
|
||||
fontName: "",
|
||||
fontColor: /* black = */ new Uint8ClampedArray(3),
|
||||
fillColorSpace: ColorSpace.singletons.gray,
|
||||
};
|
||||
let breakLoop = false;
|
||||
const stack = [];
|
||||
|
@ -123,6 +136,7 @@ class AppearanceStreamEvaluator extends EvaluatorPreprocessor {
|
|||
fontSize: result.fontSize,
|
||||
fontName: result.fontName,
|
||||
fontColor: result.fontColor.slice(),
|
||||
fillColorSpace: result.fillColorSpace,
|
||||
});
|
||||
break;
|
||||
case OPS.restore:
|
||||
|
@ -140,6 +154,19 @@ class AppearanceStreamEvaluator extends EvaluatorPreprocessor {
|
|||
result.fontSize = fontSize * result.scaleFactor;
|
||||
}
|
||||
break;
|
||||
case OPS.setFillColorSpace:
|
||||
result.fillColorSpace = ColorSpace.parse({
|
||||
cs: args[0],
|
||||
xref: this.xref,
|
||||
resources: this.resources,
|
||||
pdfFunctionFactory: this._pdfFunctionFactory,
|
||||
localColorSpaceCache: this._localColorSpaceCache,
|
||||
});
|
||||
break;
|
||||
case OPS.setFillColor:
|
||||
const cs = result.fillColorSpace;
|
||||
cs.getRgbItem(args, 0, result.fontColor, 0);
|
||||
break;
|
||||
case OPS.setFillRGBColor:
|
||||
ColorSpace.singletons.rgb.getRgbItem(args, 0, result.fontColor, 0);
|
||||
break;
|
||||
|
@ -162,15 +189,28 @@ class AppearanceStreamEvaluator extends EvaluatorPreprocessor {
|
|||
}
|
||||
this.stream.reset();
|
||||
delete result.scaleFactor;
|
||||
delete result.fillColorSpace;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
get _localColorSpaceCache() {
|
||||
return shadow(this, "_localColorSpaceCache", new LocalColorSpaceCache());
|
||||
}
|
||||
|
||||
get _pdfFunctionFactory() {
|
||||
const pdfFunctionFactory = new PDFFunctionFactory({
|
||||
xref: this.xref,
|
||||
isEvalSupported: this.evaluatorOptions.isEvalSupported,
|
||||
});
|
||||
return shadow(this, "_pdfFunctionFactory", pdfFunctionFactory);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse appearance stream to extract font and color information.
|
||||
// It returns the font properties used to render the first text object.
|
||||
function parseAppearanceStream(stream) {
|
||||
return new AppearanceStreamEvaluator(stream).parse();
|
||||
function parseAppearanceStream(stream, evaluatorOptions, xref) {
|
||||
return new AppearanceStreamEvaluator(stream, evaluatorOptions, xref).parse();
|
||||
}
|
||||
|
||||
function getPdfColor(color, isFill) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue