mirror of
https://github.com/mozilla/pdf.js.git
synced 2026-07-10 17:15:51 +02:00
Compare commits
7 Commits
a4950c0b71
...
e5fbf52405
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e5fbf52405 | ||
|
|
b33522a208 | ||
|
|
25693dc0ee | ||
|
|
9cd5a9658a | ||
|
|
213830f44f | ||
|
|
e0e59eaf01 | ||
|
|
6146e5fee7 |
@ -32,6 +32,7 @@ import {
|
||||
} from "../shared/util.js";
|
||||
import { CMapFactory, IdentityCMap } from "./cmap.js";
|
||||
import { Cmd, Dict, EOF, isName, Name, Ref, RefSet } from "./primitives.js";
|
||||
import { compileType3Glyph, FontFlags } from "./fonts_utils.js";
|
||||
import { ErrorFont, Font } from "./fonts.js";
|
||||
import {
|
||||
fetchBinaryData,
|
||||
@ -72,7 +73,6 @@ import { bidi } from "./bidi.js";
|
||||
import { ColorSpace } from "./colorspace.js";
|
||||
import { ColorSpaceUtils } from "./colorspace_utils.js";
|
||||
import { DecodeStream } from "./decode_stream.js";
|
||||
import { FontFlags } from "./fonts_utils.js";
|
||||
import { getFontSubstitution } from "./font_substitutions.js";
|
||||
import { getGlyphsUnicode } from "./glyphlist.js";
|
||||
import { getMetrics } from "./metrics.js";
|
||||
@ -180,14 +180,17 @@ function normalizeBlendMode(value, parsingArray = false) {
|
||||
return "source-over";
|
||||
}
|
||||
|
||||
function addLocallyCachedImageOps(opList, data) {
|
||||
if (data.objId) {
|
||||
opList.addDependency(data.objId);
|
||||
function addCachedImageOps(
|
||||
opList,
|
||||
{ objId, fn, args, optionalContent, hasMask }
|
||||
) {
|
||||
if (objId) {
|
||||
opList.addDependency(objId);
|
||||
}
|
||||
opList.addImageOps(data.fn, data.args, data.optionalContent, data.hasMask);
|
||||
opList.addImageOps(fn, args, optionalContent, hasMask);
|
||||
|
||||
if (data.fn === OPS.paintImageMaskXObject && data.args[0]?.count > 0) {
|
||||
data.args[0].count++;
|
||||
if (fn === OPS.paintImageMaskXObject && args[0]?.count > 0) {
|
||||
args[0].count++;
|
||||
}
|
||||
}
|
||||
|
||||
@ -608,6 +611,12 @@ class PartialEvaluator {
|
||||
const decode = dict.getArray("D", "Decode");
|
||||
|
||||
if (this.parsingType3Font) {
|
||||
// NOTE: Compared to other image resources we don't bother caching
|
||||
// Type3-glyph image masks, since we've not come across any cases
|
||||
// where that actually helps.
|
||||
// In Type3-glyphs image masks are "always" inline resources,
|
||||
// they're usually fairly small and aren't being re-used either.
|
||||
|
||||
imgData = PDFImage.createRawMask({
|
||||
imgArray,
|
||||
width: w,
|
||||
@ -616,25 +625,21 @@ class PartialEvaluator {
|
||||
inverseDecode: decode?.[0] > 0,
|
||||
interpolate,
|
||||
});
|
||||
args = compileType3Glyph(imgData);
|
||||
|
||||
imgData.cached = !!cacheKey;
|
||||
|
||||
fn = OPS.paintImageMaskXObject;
|
||||
args = [imgData];
|
||||
operatorList.addImageOps(fn, args, optionalContent);
|
||||
|
||||
if (cacheKey) {
|
||||
const cacheData = { fn, args, optionalContent };
|
||||
localImageCache.set(cacheKey, imageRef, cacheData);
|
||||
|
||||
if (imageRef) {
|
||||
this._regionalImageCache.set(
|
||||
/* name = */ null,
|
||||
imageRef,
|
||||
cacheData
|
||||
);
|
||||
}
|
||||
if (args) {
|
||||
operatorList.addImageOps(OPS.constructPath, args, optionalContent);
|
||||
return;
|
||||
}
|
||||
warn("Cannot compile Type3 glyph.");
|
||||
|
||||
// If compilation failed, or was disabled, fallback to using an inline
|
||||
// image mask; this case should be extremely rare.
|
||||
operatorList.addImageOps(
|
||||
OPS.paintImageMaskXObject,
|
||||
[imgData],
|
||||
optionalContent
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -742,7 +747,8 @@ class PartialEvaluator {
|
||||
// If there is no imageMask, create the PDFImage and a lot
|
||||
// of image processing can be done here.
|
||||
let objId = `img_${this.idFactory.createObjId()}`,
|
||||
cacheGlobally = false;
|
||||
cacheGlobally = false,
|
||||
globalCacheData = null;
|
||||
|
||||
if (this.parsingType3Font) {
|
||||
objId = `${this.idFactory.getDocId()}_type3_${objId}`;
|
||||
@ -767,15 +773,17 @@ class PartialEvaluator {
|
||||
operatorList.addImageOps(fn, args, optionalContent, hasMask);
|
||||
|
||||
if (cacheGlobally) {
|
||||
globalCacheData = {
|
||||
objId,
|
||||
fn,
|
||||
args,
|
||||
optionalContent,
|
||||
hasMask,
|
||||
byteSize: 0, // Temporary entry, to avoid `setData` returning early.
|
||||
};
|
||||
|
||||
if (this.globalImageCache.hasDecodeFailed(imageRef)) {
|
||||
this.globalImageCache.setData(imageRef, {
|
||||
objId,
|
||||
fn,
|
||||
args,
|
||||
optionalContent,
|
||||
hasMask,
|
||||
byteSize: 0, // Data is `null`, since decoding failed previously.
|
||||
});
|
||||
this.globalImageCache.setData(imageRef, globalCacheData);
|
||||
|
||||
this._sendImgData(objId, /* imgData = */ null, cacheGlobally);
|
||||
return;
|
||||
@ -792,14 +800,7 @@ class PartialEvaluator {
|
||||
]);
|
||||
|
||||
if (localLength) {
|
||||
this.globalImageCache.setData(imageRef, {
|
||||
objId,
|
||||
fn,
|
||||
args,
|
||||
optionalContent,
|
||||
hasMask,
|
||||
byteSize: 0, // Temporary entry, to avoid `setData` returning early.
|
||||
});
|
||||
this.globalImageCache.setData(imageRef, globalCacheData);
|
||||
this.globalImageCache.addByteSize(imageRef, localLength);
|
||||
return;
|
||||
}
|
||||
@ -848,14 +849,8 @@ class PartialEvaluator {
|
||||
this._regionalImageCache.set(/* name = */ null, imageRef, cacheData);
|
||||
|
||||
if (cacheGlobally) {
|
||||
this.globalImageCache.setData(imageRef, {
|
||||
objId,
|
||||
fn,
|
||||
args,
|
||||
optionalContent,
|
||||
hasMask,
|
||||
byteSize: 0, // Temporary entry, note `addByteSize` above.
|
||||
});
|
||||
assert(globalCacheData, "The global cache-data must be available.");
|
||||
this.globalImageCache.setData(imageRef, globalCacheData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1779,7 +1774,7 @@ class PartialEvaluator {
|
||||
if (isValidName) {
|
||||
const localImage = localImageCache.getByName(name);
|
||||
if (localImage) {
|
||||
addLocallyCachedImageOps(operatorList, localImage);
|
||||
addCachedImageOps(operatorList, localImage);
|
||||
args = null;
|
||||
continue;
|
||||
}
|
||||
@ -1793,28 +1788,12 @@ class PartialEvaluator {
|
||||
|
||||
let xobj = xobjs.getRaw(name);
|
||||
if (xobj instanceof Ref) {
|
||||
const localImage =
|
||||
const cachedImage =
|
||||
localImageCache.getByRef(xobj) ||
|
||||
self._regionalImageCache.getByRef(xobj);
|
||||
if (localImage) {
|
||||
addLocallyCachedImageOps(operatorList, localImage);
|
||||
resolveXObject();
|
||||
return;
|
||||
}
|
||||
|
||||
const globalImage = self.globalImageCache.getData(
|
||||
xobj,
|
||||
self.pageIndex
|
||||
);
|
||||
if (globalImage) {
|
||||
operatorList.addDependency(globalImage.objId);
|
||||
operatorList.addImageOps(
|
||||
globalImage.fn,
|
||||
globalImage.args,
|
||||
globalImage.optionalContent,
|
||||
globalImage.hasMask
|
||||
);
|
||||
|
||||
self._regionalImageCache.getByRef(xobj) ||
|
||||
self.globalImageCache.getData(xobj, self.pageIndex);
|
||||
if (cachedImage) {
|
||||
addCachedImageOps(operatorList, cachedImage);
|
||||
resolveXObject();
|
||||
return;
|
||||
}
|
||||
@ -1907,7 +1886,7 @@ class PartialEvaluator {
|
||||
if (cacheKey) {
|
||||
const localImage = localImageCache.getByName(cacheKey);
|
||||
if (localImage) {
|
||||
addLocallyCachedImageOps(operatorList, localImage);
|
||||
addCachedImageOps(operatorList, localImage);
|
||||
args = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -13,11 +13,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { DrawOPS, info, OPS } from "../shared/util.js";
|
||||
import { getEncoding, StandardEncoding } from "./encodings.js";
|
||||
import { getGlyphsUnicode } from "./glyphlist.js";
|
||||
import { getLookupTableFactory } from "./core_utils.js";
|
||||
import { getUnicodeForGlyph } from "./unicode.js";
|
||||
import { info } from "../shared/util.js";
|
||||
|
||||
// Accented characters have issues on Windows and Linux. When this flag is
|
||||
// enabled glyphs that use seac and seac style endchar operators are truncated
|
||||
@ -207,7 +207,177 @@ const getVerticalPresentationForm = getLookupTableFactory(t => {
|
||||
t[0xff5d] = 0xfe38; // FULLWIDTH RIGHT CURLY BRACKET
|
||||
});
|
||||
|
||||
// To disable Type3 compilation, set the value to `-1`.
|
||||
const MAX_SIZE_TO_COMPILE = 1000;
|
||||
|
||||
function compileType3Glyph({ data: img, width, height }) {
|
||||
if (width > MAX_SIZE_TO_COMPILE || height > MAX_SIZE_TO_COMPILE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const POINT_TO_PROCESS_LIMIT = 1000;
|
||||
const POINT_TYPES = new Uint8Array([
|
||||
0, 2, 4, 0, 1, 0, 5, 4, 8, 10, 0, 8, 0, 2, 1, 0,
|
||||
]);
|
||||
|
||||
const width1 = width + 1;
|
||||
const points = new Uint8Array(width1 * (height + 1));
|
||||
let i, j, j0;
|
||||
|
||||
// decodes bit-packed mask data
|
||||
const lineSize = (width + 7) & ~7;
|
||||
const data = new Uint8Array(lineSize * height);
|
||||
let pos = 0;
|
||||
for (const elem of img) {
|
||||
let mask = 128;
|
||||
while (mask > 0) {
|
||||
data[pos++] = elem & mask ? 0 : 255;
|
||||
mask >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// finding interesting points: every point is located between mask pixels,
|
||||
// so there will be points of the (width + 1)x(height + 1) grid. Every point
|
||||
// will have flags assigned based on neighboring mask pixels:
|
||||
// 4 | 8
|
||||
// --P--
|
||||
// 2 | 1
|
||||
// We are interested only in points with the flags:
|
||||
// - outside corners: 1, 2, 4, 8;
|
||||
// - inside corners: 7, 11, 13, 14;
|
||||
// - and, intersections: 5, 10.
|
||||
let count = 0;
|
||||
pos = 0;
|
||||
if (data[pos] !== 0) {
|
||||
points[0] = 1;
|
||||
++count;
|
||||
}
|
||||
for (j = 1; j < width; j++) {
|
||||
if (data[pos] !== data[pos + 1]) {
|
||||
points[j] = data[pos] ? 2 : 1;
|
||||
++count;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
if (data[pos] !== 0) {
|
||||
points[j] = 2;
|
||||
++count;
|
||||
}
|
||||
for (i = 1; i < height; i++) {
|
||||
pos = i * lineSize;
|
||||
j0 = i * width1;
|
||||
if (data[pos - lineSize] !== data[pos]) {
|
||||
points[j0] = data[pos] ? 1 : 8;
|
||||
++count;
|
||||
}
|
||||
// 'sum' is the position of the current pixel configuration in the 'TYPES'
|
||||
// array (in order 8-1-2-4, so we can use '>>2' to shift the column).
|
||||
let sum = (data[pos] ? 4 : 0) + (data[pos - lineSize] ? 8 : 0);
|
||||
for (j = 1; j < width; j++) {
|
||||
sum =
|
||||
(sum >> 2) +
|
||||
(data[pos + 1] ? 4 : 0) +
|
||||
(data[pos - lineSize + 1] ? 8 : 0);
|
||||
if (POINT_TYPES[sum]) {
|
||||
points[j0 + j] = POINT_TYPES[sum];
|
||||
++count;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
if (data[pos - lineSize] !== data[pos]) {
|
||||
points[j0 + j] = data[pos] ? 2 : 4;
|
||||
++count;
|
||||
}
|
||||
|
||||
if (count > POINT_TO_PROCESS_LIMIT) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
pos = lineSize * (height - 1);
|
||||
j0 = i * width1;
|
||||
if (data[pos] !== 0) {
|
||||
points[j0] = 8;
|
||||
++count;
|
||||
}
|
||||
for (j = 1; j < width; j++) {
|
||||
if (data[pos] !== data[pos + 1]) {
|
||||
points[j0 + j] = data[pos] ? 4 : 8;
|
||||
++count;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
if (data[pos] !== 0) {
|
||||
points[j0 + j] = 4;
|
||||
++count;
|
||||
}
|
||||
if (count > POINT_TO_PROCESS_LIMIT) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// building outlines
|
||||
const steps = new Int32Array([0, width1, -1, 0, -width1, 0, 0, 0, 1]);
|
||||
const pathBuf = [];
|
||||
|
||||
// the path shall be painted in [0..1]x[0..1] space
|
||||
const { a, b, c, d, e, f } = new DOMMatrix()
|
||||
.scaleSelf(1 / width, -1 / height)
|
||||
.translateSelf(0, -height);
|
||||
|
||||
for (i = 0; count && i <= height; i++) {
|
||||
let p = i * width1;
|
||||
const end = p + width;
|
||||
while (p < end && !points[p]) {
|
||||
p++;
|
||||
}
|
||||
if (p === end) {
|
||||
continue;
|
||||
}
|
||||
let x = p % width1;
|
||||
let y = i;
|
||||
pathBuf.push(DrawOPS.moveTo, a * x + c * y + e, b * x + d * y + f);
|
||||
|
||||
const p0 = p;
|
||||
let type = points[p];
|
||||
do {
|
||||
const step = steps[type];
|
||||
do {
|
||||
p += step;
|
||||
} while (!points[p]);
|
||||
|
||||
const pp = points[p];
|
||||
if (pp !== 5 && pp !== 10) {
|
||||
// set new direction
|
||||
type = pp;
|
||||
// delete mark
|
||||
points[p] = 0;
|
||||
} else {
|
||||
// type is 5 or 10, ie, a crossing
|
||||
// set new direction
|
||||
type = pp & ((0x33 * type) >> 4);
|
||||
// set new type for "future hit"
|
||||
points[p] &= (type >> 2) | (type << 2);
|
||||
}
|
||||
x = p % width1;
|
||||
y = (p / width1) | 0;
|
||||
pathBuf.push(DrawOPS.lineTo, a * x + c * y + e, b * x + d * y + f);
|
||||
|
||||
if (!points[p]) {
|
||||
--count;
|
||||
}
|
||||
} while (p0 !== p);
|
||||
--i;
|
||||
}
|
||||
|
||||
return [
|
||||
OPS.rawFillPath,
|
||||
[new Float32Array(pathBuf)],
|
||||
new Float32Array([0, 0, width, height]),
|
||||
];
|
||||
}
|
||||
|
||||
export {
|
||||
compileType3Glyph,
|
||||
FontFlags,
|
||||
getVerticalPresentationForm,
|
||||
MacStandardGlyphOrdering,
|
||||
|
||||
@ -13,7 +13,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ImageKind, OPS, RenderingIntentFlag, warn } from "../shared/util.js";
|
||||
import {
|
||||
DrawOPS,
|
||||
ImageKind,
|
||||
OPS,
|
||||
RenderingIntentFlag,
|
||||
Util,
|
||||
warn,
|
||||
} from "../shared/util.js";
|
||||
|
||||
function addState(parentState, pattern, checkFn, iterateFn, processFn) {
|
||||
let state = parentState;
|
||||
@ -470,6 +477,70 @@ addState(
|
||||
}
|
||||
);
|
||||
|
||||
// This replaces (save, transform, constructPath, restore)
|
||||
// sequences with |constructPath| operation.
|
||||
addState(
|
||||
InitialState,
|
||||
[OPS.save, OPS.transform, OPS.constructPath, OPS.restore],
|
||||
context => {
|
||||
const argsArray = context.argsArray;
|
||||
const iFirstConstructPath = context.iCurr - 1;
|
||||
const op = argsArray[iFirstConstructPath][0];
|
||||
|
||||
// When stroking the transform has to be applied to the line width too.
|
||||
// So we can only optimize if the transform is an identity.
|
||||
if (
|
||||
op !== OPS.stroke &&
|
||||
op !== OPS.closeStroke &&
|
||||
op !== OPS.fillStroke &&
|
||||
op !== OPS.eoFillStroke &&
|
||||
op !== OPS.closeFillStroke &&
|
||||
op !== OPS.closeEOFillStroke
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
const iFirstTransform = context.iCurr - 2;
|
||||
const transform = argsArray[iFirstTransform];
|
||||
return (
|
||||
transform[0] === 1 &&
|
||||
transform[1] === 0 &&
|
||||
transform[2] === 0 &&
|
||||
transform[3] === 1
|
||||
);
|
||||
},
|
||||
() => false,
|
||||
(context, i) => {
|
||||
const { fnArray, argsArray } = context;
|
||||
const curr = context.iCurr;
|
||||
const iFirstSave = curr - 3;
|
||||
const iFirstTransform = curr - 2;
|
||||
const iFirstConstructPath = curr - 1;
|
||||
const args = argsArray[iFirstConstructPath];
|
||||
const transform = argsArray[iFirstTransform];
|
||||
const [, [buffer], minMax] = args;
|
||||
|
||||
Util.scaleMinMax(transform, minMax);
|
||||
for (let k = 0, kk = buffer.length; k < kk; ) {
|
||||
switch (buffer[k++]) {
|
||||
case DrawOPS.moveTo:
|
||||
case DrawOPS.lineTo:
|
||||
Util.applyTransformInPlace(buffer.subarray(k), transform);
|
||||
k += 2;
|
||||
break;
|
||||
case DrawOPS.curveTo:
|
||||
Util.applyTransformToBezierInPlace(buffer.subarray(k), transform);
|
||||
k += 6;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Replace queue items.
|
||||
fnArray.splice(iFirstSave, 4, OPS.constructPath);
|
||||
argsArray.splice(iFirstSave, 4, args);
|
||||
|
||||
return iFirstSave + 1;
|
||||
}
|
||||
);
|
||||
|
||||
class NullOptimizer {
|
||||
constructor(queue) {
|
||||
this.queue = queue;
|
||||
@ -699,7 +770,7 @@ class OperatorList {
|
||||
case OPS.paintInlineImageXObjectGroup:
|
||||
case OPS.paintImageMaskXObject:
|
||||
const arg = argsArray[i][0]; // First parameter in imgData.
|
||||
if (!arg.cached && arg.data?.buffer instanceof ArrayBuffer) {
|
||||
if (arg.data?.buffer instanceof ArrayBuffer) {
|
||||
transfers.push(arg.data.buffer);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -54,9 +54,6 @@ const EXECUTION_TIME = 15; // ms
|
||||
// Defines the number of steps before checking the execution time.
|
||||
const EXECUTION_STEPS = 10;
|
||||
|
||||
// To disable Type3 compilation, set the value to `-1`.
|
||||
const MAX_SIZE_TO_COMPILE = 1000;
|
||||
|
||||
const FULL_CHUNK_HEIGHT = 16;
|
||||
|
||||
// Only used in rescaleAndStroke. The goal is to avoid
|
||||
@ -299,169 +296,6 @@ function drawImageAtIntegerCoords(
|
||||
return [scaleX * destW, scaleY * destH];
|
||||
}
|
||||
|
||||
function compileType3Glyph(imgData) {
|
||||
const { width, height } = imgData;
|
||||
if (width > MAX_SIZE_TO_COMPILE || height > MAX_SIZE_TO_COMPILE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const POINT_TO_PROCESS_LIMIT = 1000;
|
||||
const POINT_TYPES = new Uint8Array([
|
||||
0, 2, 4, 0, 1, 0, 5, 4, 8, 10, 0, 8, 0, 2, 1, 0,
|
||||
]);
|
||||
|
||||
const width1 = width + 1;
|
||||
const points = new Uint8Array(width1 * (height + 1));
|
||||
let i, j, j0;
|
||||
|
||||
// decodes bit-packed mask data
|
||||
const lineSize = (width + 7) & ~7;
|
||||
const data = new Uint8Array(lineSize * height);
|
||||
let pos = 0;
|
||||
for (const elem of imgData.data) {
|
||||
let mask = 128;
|
||||
while (mask > 0) {
|
||||
data[pos++] = elem & mask ? 0 : 255;
|
||||
mask >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// finding interesting points: every point is located between mask pixels,
|
||||
// so there will be points of the (width + 1)x(height + 1) grid. Every point
|
||||
// will have flags assigned based on neighboring mask pixels:
|
||||
// 4 | 8
|
||||
// --P--
|
||||
// 2 | 1
|
||||
// We are interested only in points with the flags:
|
||||
// - outside corners: 1, 2, 4, 8;
|
||||
// - inside corners: 7, 11, 13, 14;
|
||||
// - and, intersections: 5, 10.
|
||||
let count = 0;
|
||||
pos = 0;
|
||||
if (data[pos] !== 0) {
|
||||
points[0] = 1;
|
||||
++count;
|
||||
}
|
||||
for (j = 1; j < width; j++) {
|
||||
if (data[pos] !== data[pos + 1]) {
|
||||
points[j] = data[pos] ? 2 : 1;
|
||||
++count;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
if (data[pos] !== 0) {
|
||||
points[j] = 2;
|
||||
++count;
|
||||
}
|
||||
for (i = 1; i < height; i++) {
|
||||
pos = i * lineSize;
|
||||
j0 = i * width1;
|
||||
if (data[pos - lineSize] !== data[pos]) {
|
||||
points[j0] = data[pos] ? 1 : 8;
|
||||
++count;
|
||||
}
|
||||
// 'sum' is the position of the current pixel configuration in the 'TYPES'
|
||||
// array (in order 8-1-2-4, so we can use '>>2' to shift the column).
|
||||
let sum = (data[pos] ? 4 : 0) + (data[pos - lineSize] ? 8 : 0);
|
||||
for (j = 1; j < width; j++) {
|
||||
sum =
|
||||
(sum >> 2) +
|
||||
(data[pos + 1] ? 4 : 0) +
|
||||
(data[pos - lineSize + 1] ? 8 : 0);
|
||||
if (POINT_TYPES[sum]) {
|
||||
points[j0 + j] = POINT_TYPES[sum];
|
||||
++count;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
if (data[pos - lineSize] !== data[pos]) {
|
||||
points[j0 + j] = data[pos] ? 2 : 4;
|
||||
++count;
|
||||
}
|
||||
|
||||
if (count > POINT_TO_PROCESS_LIMIT) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
pos = lineSize * (height - 1);
|
||||
j0 = i * width1;
|
||||
if (data[pos] !== 0) {
|
||||
points[j0] = 8;
|
||||
++count;
|
||||
}
|
||||
for (j = 1; j < width; j++) {
|
||||
if (data[pos] !== data[pos + 1]) {
|
||||
points[j0 + j] = data[pos] ? 4 : 8;
|
||||
++count;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
if (data[pos] !== 0) {
|
||||
points[j0 + j] = 4;
|
||||
++count;
|
||||
}
|
||||
if (count > POINT_TO_PROCESS_LIMIT) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// building outlines
|
||||
const steps = new Int32Array([0, width1, -1, 0, -width1, 0, 0, 0, 1]);
|
||||
const path = new Path2D();
|
||||
|
||||
// the path shall be painted in [0..1]x[0..1] space
|
||||
const { a, b, c, d, e, f } = new DOMMatrix()
|
||||
.scaleSelf(1 / width, -1 / height)
|
||||
.translateSelf(0, -height);
|
||||
|
||||
for (i = 0; count && i <= height; i++) {
|
||||
let p = i * width1;
|
||||
const end = p + width;
|
||||
while (p < end && !points[p]) {
|
||||
p++;
|
||||
}
|
||||
if (p === end) {
|
||||
continue;
|
||||
}
|
||||
let x = p % width1;
|
||||
let y = i;
|
||||
path.moveTo(a * x + c * y + e, b * x + d * y + f);
|
||||
|
||||
const p0 = p;
|
||||
let type = points[p];
|
||||
do {
|
||||
const step = steps[type];
|
||||
do {
|
||||
p += step;
|
||||
} while (!points[p]);
|
||||
|
||||
const pp = points[p];
|
||||
if (pp !== 5 && pp !== 10) {
|
||||
// set new direction
|
||||
type = pp;
|
||||
// delete mark
|
||||
points[p] = 0;
|
||||
} else {
|
||||
// type is 5 or 10, ie, a crossing
|
||||
// set new direction
|
||||
type = pp & ((0x33 * type) >> 4);
|
||||
// set new type for "future hit"
|
||||
points[p] &= (type >> 2) | (type << 2);
|
||||
}
|
||||
x = p % width1;
|
||||
y = (p / width1) | 0;
|
||||
path.lineTo(a * x + c * y + e, b * x + d * y + f);
|
||||
|
||||
if (!points[p]) {
|
||||
--count;
|
||||
}
|
||||
} while (p0 !== p);
|
||||
--i;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
class CanvasExtraState {
|
||||
constructor(width, height) {
|
||||
// Are soft masks and alpha values shapes or opacities?
|
||||
@ -821,7 +655,6 @@ class CanvasGraphics {
|
||||
this.canvasFactory = canvasFactory;
|
||||
this.filterFactory = filterFactory;
|
||||
this.groupStack = [];
|
||||
this.processingType3 = null;
|
||||
// Patterns are painted relative to the initial page/form transform, see
|
||||
// PDF spec 8.7.2 NOTE 1.
|
||||
this.baseTransform = null;
|
||||
@ -1747,6 +1580,10 @@ class CanvasGraphics {
|
||||
this.consumePath(path);
|
||||
}
|
||||
|
||||
rawFillPath(path) {
|
||||
this.ctx.fill(path);
|
||||
}
|
||||
|
||||
// Clipping
|
||||
clip() {
|
||||
this.pendingClip = NORMAL_CLIP;
|
||||
@ -2267,7 +2104,6 @@ class CanvasGraphics {
|
||||
if (!operatorList) {
|
||||
warn(`Type3 character "${glyph.operatorListId}" is not available.`);
|
||||
} else if (this.contentVisible) {
|
||||
this.processingType3 = glyph;
|
||||
this.save();
|
||||
ctx.scale(fontSize, fontSize);
|
||||
ctx.transform(...fontMatrix);
|
||||
@ -2282,7 +2118,6 @@ class CanvasGraphics {
|
||||
current.x += width * textHScale;
|
||||
}
|
||||
ctx.restore();
|
||||
this.processingType3 = null;
|
||||
}
|
||||
|
||||
// Type3 fonts
|
||||
@ -2703,18 +2538,6 @@ class CanvasGraphics {
|
||||
img.count = count;
|
||||
|
||||
const ctx = this.ctx;
|
||||
const glyph = this.processingType3;
|
||||
|
||||
if (glyph) {
|
||||
if (glyph.compiled === undefined) {
|
||||
glyph.compiled = compileType3Glyph(img);
|
||||
}
|
||||
|
||||
if (glyph.compiled) {
|
||||
ctx.fill(glyph.compiled);
|
||||
return;
|
||||
}
|
||||
}
|
||||
const mask = this._createMaskCanvas(img);
|
||||
const maskCanvas = mask.canvas;
|
||||
|
||||
|
||||
@ -339,6 +339,7 @@ const OPS = {
|
||||
constructPath: 91,
|
||||
setStrokeTransparent: 92,
|
||||
setFillTransparent: 93,
|
||||
rawFillPath: 94,
|
||||
};
|
||||
|
||||
// In order to have a switch statement that is fast (i.e. which use a jump
|
||||
@ -676,6 +677,57 @@ class Util {
|
||||
return `#${hexNumbers[r]}${hexNumbers[g]}${hexNumbers[b]}`;
|
||||
}
|
||||
|
||||
// Apply a scaling matrix to some min/max values.
|
||||
// If a scaling factor is negative then min and max must be
|
||||
// swapped.
|
||||
static scaleMinMax(transform, minMax) {
|
||||
let temp;
|
||||
if (transform[0]) {
|
||||
if (transform[0] < 0) {
|
||||
temp = minMax[0];
|
||||
minMax[0] = minMax[2];
|
||||
minMax[2] = temp;
|
||||
}
|
||||
minMax[0] *= transform[0];
|
||||
minMax[2] *= transform[0];
|
||||
|
||||
if (transform[3] < 0) {
|
||||
temp = minMax[1];
|
||||
minMax[1] = minMax[3];
|
||||
minMax[3] = temp;
|
||||
}
|
||||
minMax[1] *= transform[3];
|
||||
minMax[3] *= transform[3];
|
||||
} else {
|
||||
temp = minMax[0];
|
||||
minMax[0] = minMax[1];
|
||||
minMax[1] = temp;
|
||||
temp = minMax[2];
|
||||
minMax[2] = minMax[3];
|
||||
minMax[3] = temp;
|
||||
|
||||
if (transform[1] < 0) {
|
||||
temp = minMax[1];
|
||||
minMax[1] = minMax[3];
|
||||
minMax[3] = temp;
|
||||
}
|
||||
minMax[1] *= transform[1];
|
||||
minMax[3] *= transform[1];
|
||||
|
||||
if (transform[2] < 0) {
|
||||
temp = minMax[0];
|
||||
minMax[0] = minMax[2];
|
||||
minMax[2] = temp;
|
||||
}
|
||||
minMax[0] *= transform[2];
|
||||
minMax[2] *= transform[2];
|
||||
}
|
||||
minMax[0] += transform[4];
|
||||
minMax[1] += transform[5];
|
||||
minMax[2] += transform[4];
|
||||
minMax[3] += transform[5];
|
||||
}
|
||||
|
||||
// Concatenates two transformation matrices together and returns the result.
|
||||
static transform(m1, m2) {
|
||||
return [
|
||||
@ -695,6 +747,22 @@ class Util {
|
||||
return [xt, yt];
|
||||
}
|
||||
|
||||
static applyTransformInPlace(p, m) {
|
||||
const [p0, p1] = p;
|
||||
p[0] = p0 * m[0] + p1 * m[2] + m[4];
|
||||
p[1] = p0 * m[1] + p1 * m[3] + m[5];
|
||||
}
|
||||
|
||||
// For 2d affine transforms
|
||||
static applyTransformToBezierInPlace(p, [m0, m1, m2, m3, m4, m5]) {
|
||||
for (let i = 0; i < 6; i += 2) {
|
||||
const pI = p[i];
|
||||
const pI1 = p[i + 1];
|
||||
p[i] = pI * m0 + pI1 * m2 + m4;
|
||||
p[i + 1] = pI * m1 + pI1 * m3 + m5;
|
||||
}
|
||||
}
|
||||
|
||||
static applyInverseTransform(p, m) {
|
||||
const d = m[0] * m[3] - m[1] * m[2];
|
||||
const xt = (p[0] * m[3] - p[1] * m[2] + m[2] * m[5] - m[4] * m[3]) / d;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user