Skip to content

Commit

Permalink
Merge pull request #16648 from Snuffleupagus/AppearanceStreamEvaluato…
Browse files Browse the repository at this point in the history
…r-setFillColorSpace

Improve `parseAppearanceStream` to handle more "complex" ColorSpaces
  • Loading branch information
Snuffleupagus authored Jul 6, 2023
2 parents c5caa98 + 6442a6c commit 091266c
Show file tree
Hide file tree
Showing 4 changed files with 201 additions and 42 deletions.
34 changes: 20 additions & 14 deletions src/core/annotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,7 @@ class AnnotationFactory {
needAppearances:
!collectFields && acroFormDict.get("NeedAppearances") === true,
pageIndex,
isOffscreenCanvasSupported:
pdfManager.evaluatorOptions.isOffscreenCanvasSupported,
evaluatorOptions: pdfManager.evaluatorOptions,
};

switch (subtype) {
Expand Down Expand Up @@ -341,7 +340,7 @@ class AnnotationFactory {
xref,
annotation,
dependencies,
image
{ image }
)
);
break;
Expand All @@ -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) {
Expand All @@ -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);
Expand All @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down
48 changes: 44 additions & 4 deletions src/core/default_appearance.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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() {
Expand All @@ -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 = [];
Expand All @@ -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:
Expand All @@ -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;
Expand All @@ -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) {
Expand Down
Loading

0 comments on commit 091266c

Please sign in to comment.