de36b2aaba
Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes). Prettier is being used for a couple of reasons: - To be consistent with `mozilla-central`, where Prettier is already in use across the tree. - To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters. Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some). Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long. *Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit. (On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
968 lines
30 KiB
JavaScript
968 lines
30 KiB
JavaScript
/* Copyright 2012 Mozilla Foundation
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/* eslint-disable no-multi-spaces */
|
|
|
|
import {
|
|
assert,
|
|
FormatError,
|
|
info,
|
|
unreachable,
|
|
UNSUPPORTED_FEATURES,
|
|
Util,
|
|
warn,
|
|
} from "../shared/util";
|
|
import { ColorSpace } from "./colorspace";
|
|
import { isStream } from "./primitives";
|
|
import { MissingDataException } from "./core_utils";
|
|
|
|
var ShadingType = {
|
|
FUNCTION_BASED: 1,
|
|
AXIAL: 2,
|
|
RADIAL: 3,
|
|
FREE_FORM_MESH: 4,
|
|
LATTICE_FORM_MESH: 5,
|
|
COONS_PATCH_MESH: 6,
|
|
TENSOR_PATCH_MESH: 7,
|
|
};
|
|
|
|
var Pattern = (function PatternClosure() {
|
|
// Constructor should define this.getPattern
|
|
function Pattern() {
|
|
unreachable("should not call Pattern constructor");
|
|
}
|
|
|
|
Pattern.prototype = {
|
|
// Input: current Canvas context
|
|
// Output: the appropriate fillStyle or strokeStyle
|
|
getPattern: function Pattern_getPattern(ctx) {
|
|
unreachable(`Should not call Pattern.getStyle: ${ctx}`);
|
|
},
|
|
};
|
|
|
|
Pattern.parseShading = function(
|
|
shading,
|
|
matrix,
|
|
xref,
|
|
res,
|
|
handler,
|
|
pdfFunctionFactory
|
|
) {
|
|
var dict = isStream(shading) ? shading.dict : shading;
|
|
var type = dict.get("ShadingType");
|
|
|
|
try {
|
|
switch (type) {
|
|
case ShadingType.AXIAL:
|
|
case ShadingType.RADIAL:
|
|
// Both radial and axial shadings are handled by RadialAxial shading.
|
|
return new Shadings.RadialAxial(
|
|
dict,
|
|
matrix,
|
|
xref,
|
|
res,
|
|
pdfFunctionFactory
|
|
);
|
|
case ShadingType.FREE_FORM_MESH:
|
|
case ShadingType.LATTICE_FORM_MESH:
|
|
case ShadingType.COONS_PATCH_MESH:
|
|
case ShadingType.TENSOR_PATCH_MESH:
|
|
return new Shadings.Mesh(
|
|
shading,
|
|
matrix,
|
|
xref,
|
|
res,
|
|
pdfFunctionFactory
|
|
);
|
|
default:
|
|
throw new FormatError("Unsupported ShadingType: " + type);
|
|
}
|
|
} catch (ex) {
|
|
if (ex instanceof MissingDataException) {
|
|
throw ex;
|
|
}
|
|
handler.send("UnsupportedFeature", {
|
|
featureId: UNSUPPORTED_FEATURES.shadingPattern,
|
|
});
|
|
warn(ex);
|
|
return new Shadings.Dummy();
|
|
}
|
|
};
|
|
return Pattern;
|
|
})();
|
|
|
|
var Shadings = {};
|
|
|
|
// A small number to offset the first/last color stops so we can insert ones to
|
|
// support extend. Number.MIN_VALUE is too small and breaks the extend.
|
|
Shadings.SMALL_NUMBER = 1e-6;
|
|
|
|
// Radial and axial shading have very similar implementations
|
|
// If needed, the implementations can be broken into two classes
|
|
Shadings.RadialAxial = (function RadialAxialClosure() {
|
|
function RadialAxial(dict, matrix, xref, res, pdfFunctionFactory) {
|
|
this.matrix = matrix;
|
|
this.coordsArr = dict.getArray("Coords");
|
|
this.shadingType = dict.get("ShadingType");
|
|
this.type = "Pattern";
|
|
var cs = dict.get("ColorSpace", "CS");
|
|
cs = ColorSpace.parse(cs, xref, res, pdfFunctionFactory);
|
|
this.cs = cs;
|
|
const bbox = dict.getArray("BBox");
|
|
if (Array.isArray(bbox) && bbox.length === 4) {
|
|
this.bbox = Util.normalizeRect(bbox);
|
|
} else {
|
|
this.bbox = null;
|
|
}
|
|
|
|
var t0 = 0.0,
|
|
t1 = 1.0;
|
|
if (dict.has("Domain")) {
|
|
var domainArr = dict.getArray("Domain");
|
|
t0 = domainArr[0];
|
|
t1 = domainArr[1];
|
|
}
|
|
|
|
var extendStart = false,
|
|
extendEnd = false;
|
|
if (dict.has("Extend")) {
|
|
var extendArr = dict.getArray("Extend");
|
|
extendStart = extendArr[0];
|
|
extendEnd = extendArr[1];
|
|
}
|
|
|
|
if (
|
|
this.shadingType === ShadingType.RADIAL &&
|
|
(!extendStart || !extendEnd)
|
|
) {
|
|
// Radial gradient only currently works if either circle is fully within
|
|
// the other circle.
|
|
var x1 = this.coordsArr[0];
|
|
var y1 = this.coordsArr[1];
|
|
var r1 = this.coordsArr[2];
|
|
var x2 = this.coordsArr[3];
|
|
var y2 = this.coordsArr[4];
|
|
var r2 = this.coordsArr[5];
|
|
var distance = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
|
|
if (r1 <= r2 + distance && r2 <= r1 + distance) {
|
|
warn("Unsupported radial gradient.");
|
|
}
|
|
}
|
|
|
|
this.extendStart = extendStart;
|
|
this.extendEnd = extendEnd;
|
|
|
|
var fnObj = dict.get("Function");
|
|
var fn = pdfFunctionFactory.createFromArray(fnObj);
|
|
|
|
// 10 samples seems good enough for now, but probably won't work
|
|
// if there are sharp color changes. Ideally, we would implement
|
|
// the spec faithfully and add lossless optimizations.
|
|
const NUMBER_OF_SAMPLES = 10;
|
|
const step = (t1 - t0) / NUMBER_OF_SAMPLES;
|
|
|
|
var colorStops = (this.colorStops = []);
|
|
|
|
// Protect against bad domains.
|
|
if (t0 >= t1 || step <= 0) {
|
|
// Acrobat doesn't seem to handle these cases so we'll ignore for
|
|
// now.
|
|
info("Bad shading domain.");
|
|
return;
|
|
}
|
|
|
|
var color = new Float32Array(cs.numComps),
|
|
ratio = new Float32Array(1);
|
|
var rgbColor;
|
|
for (let i = 0; i <= NUMBER_OF_SAMPLES; i++) {
|
|
ratio[0] = t0 + i * step;
|
|
fn(ratio, 0, color, 0);
|
|
rgbColor = cs.getRgb(color, 0);
|
|
var cssColor = Util.makeCssRgb(rgbColor[0], rgbColor[1], rgbColor[2]);
|
|
colorStops.push([i / NUMBER_OF_SAMPLES, cssColor]);
|
|
}
|
|
|
|
var background = "transparent";
|
|
if (dict.has("Background")) {
|
|
rgbColor = cs.getRgb(dict.get("Background"), 0);
|
|
background = Util.makeCssRgb(rgbColor[0], rgbColor[1], rgbColor[2]);
|
|
}
|
|
|
|
if (!extendStart) {
|
|
// Insert a color stop at the front and offset the first real color stop
|
|
// so it doesn't conflict with the one we insert.
|
|
colorStops.unshift([0, background]);
|
|
colorStops[1][0] += Shadings.SMALL_NUMBER;
|
|
}
|
|
if (!extendEnd) {
|
|
// Same idea as above in extendStart but for the end.
|
|
colorStops[colorStops.length - 1][0] -= Shadings.SMALL_NUMBER;
|
|
colorStops.push([1, background]);
|
|
}
|
|
|
|
this.colorStops = colorStops;
|
|
}
|
|
|
|
RadialAxial.prototype = {
|
|
getIR: function RadialAxial_getIR() {
|
|
var coordsArr = this.coordsArr;
|
|
var shadingType = this.shadingType;
|
|
var type, p0, p1, r0, r1;
|
|
if (shadingType === ShadingType.AXIAL) {
|
|
p0 = [coordsArr[0], coordsArr[1]];
|
|
p1 = [coordsArr[2], coordsArr[3]];
|
|
r0 = null;
|
|
r1 = null;
|
|
type = "axial";
|
|
} else if (shadingType === ShadingType.RADIAL) {
|
|
p0 = [coordsArr[0], coordsArr[1]];
|
|
p1 = [coordsArr[3], coordsArr[4]];
|
|
r0 = coordsArr[2];
|
|
r1 = coordsArr[5];
|
|
type = "radial";
|
|
} else {
|
|
unreachable(`getPattern type unknown: ${shadingType}`);
|
|
}
|
|
|
|
var matrix = this.matrix;
|
|
if (matrix) {
|
|
p0 = Util.applyTransform(p0, matrix);
|
|
p1 = Util.applyTransform(p1, matrix);
|
|
if (shadingType === ShadingType.RADIAL) {
|
|
var scale = Util.singularValueDecompose2dScale(matrix);
|
|
r0 *= scale[0];
|
|
r1 *= scale[1];
|
|
}
|
|
}
|
|
|
|
return ["RadialAxial", type, this.bbox, this.colorStops, p0, p1, r0, r1];
|
|
},
|
|
};
|
|
|
|
return RadialAxial;
|
|
})();
|
|
|
|
// All mesh shading. For now, they will be presented as set of the triangles
|
|
// to be drawn on the canvas and rgb color for each vertex.
|
|
Shadings.Mesh = (function MeshClosure() {
|
|
function MeshStreamReader(stream, context) {
|
|
this.stream = stream;
|
|
this.context = context;
|
|
this.buffer = 0;
|
|
this.bufferLength = 0;
|
|
|
|
var numComps = context.numComps;
|
|
this.tmpCompsBuf = new Float32Array(numComps);
|
|
var csNumComps = context.colorSpace.numComps;
|
|
this.tmpCsCompsBuf = context.colorFn
|
|
? new Float32Array(csNumComps)
|
|
: this.tmpCompsBuf;
|
|
}
|
|
MeshStreamReader.prototype = {
|
|
get hasData() {
|
|
if (this.stream.end) {
|
|
return this.stream.pos < this.stream.end;
|
|
}
|
|
if (this.bufferLength > 0) {
|
|
return true;
|
|
}
|
|
var nextByte = this.stream.getByte();
|
|
if (nextByte < 0) {
|
|
return false;
|
|
}
|
|
this.buffer = nextByte;
|
|
this.bufferLength = 8;
|
|
return true;
|
|
},
|
|
readBits: function MeshStreamReader_readBits(n) {
|
|
var buffer = this.buffer;
|
|
var bufferLength = this.bufferLength;
|
|
if (n === 32) {
|
|
if (bufferLength === 0) {
|
|
return (
|
|
((this.stream.getByte() << 24) |
|
|
(this.stream.getByte() << 16) |
|
|
(this.stream.getByte() << 8) |
|
|
this.stream.getByte()) >>>
|
|
0
|
|
);
|
|
}
|
|
buffer =
|
|
(buffer << 24) |
|
|
(this.stream.getByte() << 16) |
|
|
(this.stream.getByte() << 8) |
|
|
this.stream.getByte();
|
|
var nextByte = this.stream.getByte();
|
|
this.buffer = nextByte & ((1 << bufferLength) - 1);
|
|
return (
|
|
((buffer << (8 - bufferLength)) |
|
|
((nextByte & 0xff) >> bufferLength)) >>>
|
|
0
|
|
);
|
|
}
|
|
if (n === 8 && bufferLength === 0) {
|
|
return this.stream.getByte();
|
|
}
|
|
while (bufferLength < n) {
|
|
buffer = (buffer << 8) | this.stream.getByte();
|
|
bufferLength += 8;
|
|
}
|
|
bufferLength -= n;
|
|
this.bufferLength = bufferLength;
|
|
this.buffer = buffer & ((1 << bufferLength) - 1);
|
|
return buffer >> bufferLength;
|
|
},
|
|
align: function MeshStreamReader_align() {
|
|
this.buffer = 0;
|
|
this.bufferLength = 0;
|
|
},
|
|
readFlag: function MeshStreamReader_readFlag() {
|
|
return this.readBits(this.context.bitsPerFlag);
|
|
},
|
|
readCoordinate: function MeshStreamReader_readCoordinate() {
|
|
var bitsPerCoordinate = this.context.bitsPerCoordinate;
|
|
var xi = this.readBits(bitsPerCoordinate);
|
|
var yi = this.readBits(bitsPerCoordinate);
|
|
var decode = this.context.decode;
|
|
var scale =
|
|
bitsPerCoordinate < 32
|
|
? 1 / ((1 << bitsPerCoordinate) - 1)
|
|
: 2.3283064365386963e-10; // 2 ^ -32
|
|
return [
|
|
xi * scale * (decode[1] - decode[0]) + decode[0],
|
|
yi * scale * (decode[3] - decode[2]) + decode[2],
|
|
];
|
|
},
|
|
readComponents: function MeshStreamReader_readComponents() {
|
|
var numComps = this.context.numComps;
|
|
var bitsPerComponent = this.context.bitsPerComponent;
|
|
var scale =
|
|
bitsPerComponent < 32
|
|
? 1 / ((1 << bitsPerComponent) - 1)
|
|
: 2.3283064365386963e-10; // 2 ^ -32
|
|
var decode = this.context.decode;
|
|
var components = this.tmpCompsBuf;
|
|
for (var i = 0, j = 4; i < numComps; i++, j += 2) {
|
|
var ci = this.readBits(bitsPerComponent);
|
|
components[i] = ci * scale * (decode[j + 1] - decode[j]) + decode[j];
|
|
}
|
|
var color = this.tmpCsCompsBuf;
|
|
if (this.context.colorFn) {
|
|
this.context.colorFn(components, 0, color, 0);
|
|
}
|
|
return this.context.colorSpace.getRgb(color, 0);
|
|
},
|
|
};
|
|
|
|
function decodeType4Shading(mesh, reader) {
|
|
var coords = mesh.coords;
|
|
var colors = mesh.colors;
|
|
var operators = [];
|
|
var ps = []; // not maintaining cs since that will match ps
|
|
var verticesLeft = 0; // assuming we have all data to start a new triangle
|
|
while (reader.hasData) {
|
|
var f = reader.readFlag();
|
|
var coord = reader.readCoordinate();
|
|
var color = reader.readComponents();
|
|
if (verticesLeft === 0) {
|
|
// ignoring flags if we started a triangle
|
|
if (!(0 <= f && f <= 2)) {
|
|
throw new FormatError("Unknown type4 flag");
|
|
}
|
|
switch (f) {
|
|
case 0:
|
|
verticesLeft = 3;
|
|
break;
|
|
case 1:
|
|
ps.push(ps[ps.length - 2], ps[ps.length - 1]);
|
|
verticesLeft = 1;
|
|
break;
|
|
case 2:
|
|
ps.push(ps[ps.length - 3], ps[ps.length - 1]);
|
|
verticesLeft = 1;
|
|
break;
|
|
}
|
|
operators.push(f);
|
|
}
|
|
ps.push(coords.length);
|
|
coords.push(coord);
|
|
colors.push(color);
|
|
verticesLeft--;
|
|
|
|
reader.align();
|
|
}
|
|
mesh.figures.push({
|
|
type: "triangles",
|
|
coords: new Int32Array(ps),
|
|
colors: new Int32Array(ps),
|
|
});
|
|
}
|
|
|
|
function decodeType5Shading(mesh, reader, verticesPerRow) {
|
|
var coords = mesh.coords;
|
|
var colors = mesh.colors;
|
|
var ps = []; // not maintaining cs since that will match ps
|
|
while (reader.hasData) {
|
|
var coord = reader.readCoordinate();
|
|
var color = reader.readComponents();
|
|
ps.push(coords.length);
|
|
coords.push(coord);
|
|
colors.push(color);
|
|
}
|
|
mesh.figures.push({
|
|
type: "lattice",
|
|
coords: new Int32Array(ps),
|
|
colors: new Int32Array(ps),
|
|
verticesPerRow,
|
|
});
|
|
}
|
|
|
|
var MIN_SPLIT_PATCH_CHUNKS_AMOUNT = 3;
|
|
var MAX_SPLIT_PATCH_CHUNKS_AMOUNT = 20;
|
|
|
|
var TRIANGLE_DENSITY = 20; // count of triangles per entire mesh bounds
|
|
|
|
var getB = (function getBClosure() {
|
|
function buildB(count) {
|
|
var lut = [];
|
|
for (var i = 0; i <= count; i++) {
|
|
var t = i / count,
|
|
t_ = 1 - t;
|
|
lut.push(
|
|
new Float32Array([
|
|
t_ * t_ * t_,
|
|
3 * t * t_ * t_,
|
|
3 * t * t * t_,
|
|
t * t * t,
|
|
])
|
|
);
|
|
}
|
|
return lut;
|
|
}
|
|
var cache = [];
|
|
return function getB(count) {
|
|
if (!cache[count]) {
|
|
cache[count] = buildB(count);
|
|
}
|
|
return cache[count];
|
|
};
|
|
})();
|
|
|
|
function buildFigureFromPatch(mesh, index) {
|
|
var figure = mesh.figures[index];
|
|
assert(figure.type === "patch", "Unexpected patch mesh figure");
|
|
|
|
var coords = mesh.coords,
|
|
colors = mesh.colors;
|
|
var pi = figure.coords;
|
|
var ci = figure.colors;
|
|
|
|
var figureMinX = Math.min(
|
|
coords[pi[0]][0],
|
|
coords[pi[3]][0],
|
|
coords[pi[12]][0],
|
|
coords[pi[15]][0]
|
|
);
|
|
var figureMinY = Math.min(
|
|
coords[pi[0]][1],
|
|
coords[pi[3]][1],
|
|
coords[pi[12]][1],
|
|
coords[pi[15]][1]
|
|
);
|
|
var figureMaxX = Math.max(
|
|
coords[pi[0]][0],
|
|
coords[pi[3]][0],
|
|
coords[pi[12]][0],
|
|
coords[pi[15]][0]
|
|
);
|
|
var figureMaxY = Math.max(
|
|
coords[pi[0]][1],
|
|
coords[pi[3]][1],
|
|
coords[pi[12]][1],
|
|
coords[pi[15]][1]
|
|
);
|
|
var splitXBy = Math.ceil(
|
|
((figureMaxX - figureMinX) * TRIANGLE_DENSITY) /
|
|
(mesh.bounds[2] - mesh.bounds[0])
|
|
);
|
|
splitXBy = Math.max(
|
|
MIN_SPLIT_PATCH_CHUNKS_AMOUNT,
|
|
Math.min(MAX_SPLIT_PATCH_CHUNKS_AMOUNT, splitXBy)
|
|
);
|
|
var splitYBy = Math.ceil(
|
|
((figureMaxY - figureMinY) * TRIANGLE_DENSITY) /
|
|
(mesh.bounds[3] - mesh.bounds[1])
|
|
);
|
|
splitYBy = Math.max(
|
|
MIN_SPLIT_PATCH_CHUNKS_AMOUNT,
|
|
Math.min(MAX_SPLIT_PATCH_CHUNKS_AMOUNT, splitYBy)
|
|
);
|
|
|
|
var verticesPerRow = splitXBy + 1;
|
|
var figureCoords = new Int32Array((splitYBy + 1) * verticesPerRow);
|
|
var figureColors = new Int32Array((splitYBy + 1) * verticesPerRow);
|
|
var k = 0;
|
|
var cl = new Uint8Array(3),
|
|
cr = new Uint8Array(3);
|
|
var c0 = colors[ci[0]],
|
|
c1 = colors[ci[1]],
|
|
c2 = colors[ci[2]],
|
|
c3 = colors[ci[3]];
|
|
var bRow = getB(splitYBy),
|
|
bCol = getB(splitXBy);
|
|
for (var row = 0; row <= splitYBy; row++) {
|
|
cl[0] = ((c0[0] * (splitYBy - row) + c2[0] * row) / splitYBy) | 0;
|
|
cl[1] = ((c0[1] * (splitYBy - row) + c2[1] * row) / splitYBy) | 0;
|
|
cl[2] = ((c0[2] * (splitYBy - row) + c2[2] * row) / splitYBy) | 0;
|
|
|
|
cr[0] = ((c1[0] * (splitYBy - row) + c3[0] * row) / splitYBy) | 0;
|
|
cr[1] = ((c1[1] * (splitYBy - row) + c3[1] * row) / splitYBy) | 0;
|
|
cr[2] = ((c1[2] * (splitYBy - row) + c3[2] * row) / splitYBy) | 0;
|
|
|
|
for (var col = 0; col <= splitXBy; col++, k++) {
|
|
if (
|
|
(row === 0 || row === splitYBy) &&
|
|
(col === 0 || col === splitXBy)
|
|
) {
|
|
continue;
|
|
}
|
|
var x = 0,
|
|
y = 0;
|
|
var q = 0;
|
|
for (var i = 0; i <= 3; i++) {
|
|
for (var j = 0; j <= 3; j++, q++) {
|
|
var m = bRow[row][i] * bCol[col][j];
|
|
x += coords[pi[q]][0] * m;
|
|
y += coords[pi[q]][1] * m;
|
|
}
|
|
}
|
|
figureCoords[k] = coords.length;
|
|
coords.push([x, y]);
|
|
figureColors[k] = colors.length;
|
|
var newColor = new Uint8Array(3);
|
|
newColor[0] = ((cl[0] * (splitXBy - col) + cr[0] * col) / splitXBy) | 0;
|
|
newColor[1] = ((cl[1] * (splitXBy - col) + cr[1] * col) / splitXBy) | 0;
|
|
newColor[2] = ((cl[2] * (splitXBy - col) + cr[2] * col) / splitXBy) | 0;
|
|
colors.push(newColor);
|
|
}
|
|
}
|
|
figureCoords[0] = pi[0];
|
|
figureColors[0] = ci[0];
|
|
figureCoords[splitXBy] = pi[3];
|
|
figureColors[splitXBy] = ci[1];
|
|
figureCoords[verticesPerRow * splitYBy] = pi[12];
|
|
figureColors[verticesPerRow * splitYBy] = ci[2];
|
|
figureCoords[verticesPerRow * splitYBy + splitXBy] = pi[15];
|
|
figureColors[verticesPerRow * splitYBy + splitXBy] = ci[3];
|
|
|
|
mesh.figures[index] = {
|
|
type: "lattice",
|
|
coords: figureCoords,
|
|
colors: figureColors,
|
|
verticesPerRow,
|
|
};
|
|
}
|
|
|
|
function decodeType6Shading(mesh, reader) {
|
|
// A special case of Type 7. The p11, p12, p21, p22 automatically filled
|
|
var coords = mesh.coords;
|
|
var colors = mesh.colors;
|
|
var ps = new Int32Array(16); // p00, p10, ..., p30, p01, ..., p33
|
|
var cs = new Int32Array(4); // c00, c30, c03, c33
|
|
while (reader.hasData) {
|
|
var f = reader.readFlag();
|
|
if (!(0 <= f && f <= 3)) {
|
|
throw new FormatError("Unknown type6 flag");
|
|
}
|
|
var i, ii;
|
|
var pi = coords.length;
|
|
for (i = 0, ii = f !== 0 ? 8 : 12; i < ii; i++) {
|
|
coords.push(reader.readCoordinate());
|
|
}
|
|
var ci = colors.length;
|
|
for (i = 0, ii = f !== 0 ? 2 : 4; i < ii; i++) {
|
|
colors.push(reader.readComponents());
|
|
}
|
|
var tmp1, tmp2, tmp3, tmp4;
|
|
switch (f) {
|
|
// prettier-ignore
|
|
case 0:
|
|
ps[12] = pi + 3; ps[13] = pi + 4; ps[14] = pi + 5; ps[15] = pi + 6;
|
|
ps[ 8] = pi + 2; /* values for 5, 6, 9, 10 are */ ps[11] = pi + 7;
|
|
ps[ 4] = pi + 1; /* calculated below */ ps[ 7] = pi + 8;
|
|
ps[ 0] = pi; ps[ 1] = pi + 11; ps[ 2] = pi + 10; ps[ 3] = pi + 9;
|
|
cs[2] = ci + 1; cs[3] = ci + 2;
|
|
cs[0] = ci; cs[1] = ci + 3;
|
|
break;
|
|
// prettier-ignore
|
|
case 1:
|
|
tmp1 = ps[12]; tmp2 = ps[13]; tmp3 = ps[14]; tmp4 = ps[15];
|
|
ps[12] = tmp4; ps[13] = pi + 0; ps[14] = pi + 1; ps[15] = pi + 2;
|
|
ps[ 8] = tmp3; /* values for 5, 6, 9, 10 are */ ps[11] = pi + 3;
|
|
ps[ 4] = tmp2; /* calculated below */ ps[ 7] = pi + 4;
|
|
ps[ 0] = tmp1; ps[ 1] = pi + 7; ps[ 2] = pi + 6; ps[ 3] = pi + 5;
|
|
tmp1 = cs[2]; tmp2 = cs[3];
|
|
cs[2] = tmp2; cs[3] = ci;
|
|
cs[0] = tmp1; cs[1] = ci + 1;
|
|
break;
|
|
// prettier-ignore
|
|
case 2:
|
|
tmp1 = ps[15];
|
|
tmp2 = ps[11];
|
|
ps[12] = ps[3]; ps[13] = pi + 0; ps[14] = pi + 1; ps[15] = pi + 2;
|
|
ps[ 8] = ps[7]; /* values for 5, 6, 9, 10 are */ ps[11] = pi + 3;
|
|
ps[ 4] = tmp2; /* calculated below */ ps[ 7] = pi + 4;
|
|
ps[ 0] = tmp1; ps[ 1] = pi + 7; ps[ 2] = pi + 6; ps[ 3] = pi + 5;
|
|
tmp1 = cs[3];
|
|
cs[2] = cs[1]; cs[3] = ci;
|
|
cs[0] = tmp1; cs[1] = ci + 1;
|
|
break;
|
|
// prettier-ignore
|
|
case 3:
|
|
ps[12] = ps[0]; ps[13] = pi + 0; ps[14] = pi + 1; ps[15] = pi + 2;
|
|
ps[ 8] = ps[1]; /* values for 5, 6, 9, 10 are */ ps[11] = pi + 3;
|
|
ps[ 4] = ps[2]; /* calculated below */ ps[ 7] = pi + 4;
|
|
ps[ 0] = ps[3]; ps[ 1] = pi + 7; ps[ 2] = pi + 6; ps[ 3] = pi + 5;
|
|
cs[2] = cs[0]; cs[3] = ci;
|
|
cs[0] = cs[1]; cs[1] = ci + 1;
|
|
break;
|
|
}
|
|
// set p11, p12, p21, p22
|
|
ps[5] = coords.length;
|
|
coords.push([
|
|
(-4 * coords[ps[0]][0] -
|
|
coords[ps[15]][0] +
|
|
6 * (coords[ps[4]][0] + coords[ps[1]][0]) -
|
|
2 * (coords[ps[12]][0] + coords[ps[3]][0]) +
|
|
3 * (coords[ps[13]][0] + coords[ps[7]][0])) /
|
|
9,
|
|
(-4 * coords[ps[0]][1] -
|
|
coords[ps[15]][1] +
|
|
6 * (coords[ps[4]][1] + coords[ps[1]][1]) -
|
|
2 * (coords[ps[12]][1] + coords[ps[3]][1]) +
|
|
3 * (coords[ps[13]][1] + coords[ps[7]][1])) /
|
|
9,
|
|
]);
|
|
ps[6] = coords.length;
|
|
coords.push([
|
|
(-4 * coords[ps[3]][0] -
|
|
coords[ps[12]][0] +
|
|
6 * (coords[ps[2]][0] + coords[ps[7]][0]) -
|
|
2 * (coords[ps[0]][0] + coords[ps[15]][0]) +
|
|
3 * (coords[ps[4]][0] + coords[ps[14]][0])) /
|
|
9,
|
|
(-4 * coords[ps[3]][1] -
|
|
coords[ps[12]][1] +
|
|
6 * (coords[ps[2]][1] + coords[ps[7]][1]) -
|
|
2 * (coords[ps[0]][1] + coords[ps[15]][1]) +
|
|
3 * (coords[ps[4]][1] + coords[ps[14]][1])) /
|
|
9,
|
|
]);
|
|
ps[9] = coords.length;
|
|
coords.push([
|
|
(-4 * coords[ps[12]][0] -
|
|
coords[ps[3]][0] +
|
|
6 * (coords[ps[8]][0] + coords[ps[13]][0]) -
|
|
2 * (coords[ps[0]][0] + coords[ps[15]][0]) +
|
|
3 * (coords[ps[11]][0] + coords[ps[1]][0])) /
|
|
9,
|
|
(-4 * coords[ps[12]][1] -
|
|
coords[ps[3]][1] +
|
|
6 * (coords[ps[8]][1] + coords[ps[13]][1]) -
|
|
2 * (coords[ps[0]][1] + coords[ps[15]][1]) +
|
|
3 * (coords[ps[11]][1] + coords[ps[1]][1])) /
|
|
9,
|
|
]);
|
|
ps[10] = coords.length;
|
|
coords.push([
|
|
(-4 * coords[ps[15]][0] -
|
|
coords[ps[0]][0] +
|
|
6 * (coords[ps[11]][0] + coords[ps[14]][0]) -
|
|
2 * (coords[ps[12]][0] + coords[ps[3]][0]) +
|
|
3 * (coords[ps[2]][0] + coords[ps[8]][0])) /
|
|
9,
|
|
(-4 * coords[ps[15]][1] -
|
|
coords[ps[0]][1] +
|
|
6 * (coords[ps[11]][1] + coords[ps[14]][1]) -
|
|
2 * (coords[ps[12]][1] + coords[ps[3]][1]) +
|
|
3 * (coords[ps[2]][1] + coords[ps[8]][1])) /
|
|
9,
|
|
]);
|
|
mesh.figures.push({
|
|
type: "patch",
|
|
coords: new Int32Array(ps), // making copies of ps and cs
|
|
colors: new Int32Array(cs),
|
|
});
|
|
}
|
|
}
|
|
|
|
function decodeType7Shading(mesh, reader) {
|
|
var coords = mesh.coords;
|
|
var colors = mesh.colors;
|
|
var ps = new Int32Array(16); // p00, p10, ..., p30, p01, ..., p33
|
|
var cs = new Int32Array(4); // c00, c30, c03, c33
|
|
while (reader.hasData) {
|
|
var f = reader.readFlag();
|
|
if (!(0 <= f && f <= 3)) {
|
|
throw new FormatError("Unknown type7 flag");
|
|
}
|
|
var i, ii;
|
|
var pi = coords.length;
|
|
for (i = 0, ii = f !== 0 ? 12 : 16; i < ii; i++) {
|
|
coords.push(reader.readCoordinate());
|
|
}
|
|
var ci = colors.length;
|
|
for (i = 0, ii = f !== 0 ? 2 : 4; i < ii; i++) {
|
|
colors.push(reader.readComponents());
|
|
}
|
|
var tmp1, tmp2, tmp3, tmp4;
|
|
switch (f) {
|
|
// prettier-ignore
|
|
case 0:
|
|
ps[12] = pi + 3; ps[13] = pi + 4; ps[14] = pi + 5; ps[15] = pi + 6;
|
|
ps[ 8] = pi + 2; ps[ 9] = pi + 13; ps[10] = pi + 14; ps[11] = pi + 7;
|
|
ps[ 4] = pi + 1; ps[ 5] = pi + 12; ps[ 6] = pi + 15; ps[ 7] = pi + 8;
|
|
ps[ 0] = pi; ps[ 1] = pi + 11; ps[ 2] = pi + 10; ps[ 3] = pi + 9;
|
|
cs[2] = ci + 1; cs[3] = ci + 2;
|
|
cs[0] = ci; cs[1] = ci + 3;
|
|
break;
|
|
// prettier-ignore
|
|
case 1:
|
|
tmp1 = ps[12]; tmp2 = ps[13]; tmp3 = ps[14]; tmp4 = ps[15];
|
|
ps[12] = tmp4; ps[13] = pi + 0; ps[14] = pi + 1; ps[15] = pi + 2;
|
|
ps[ 8] = tmp3; ps[ 9] = pi + 9; ps[10] = pi + 10; ps[11] = pi + 3;
|
|
ps[ 4] = tmp2; ps[ 5] = pi + 8; ps[ 6] = pi + 11; ps[ 7] = pi + 4;
|
|
ps[ 0] = tmp1; ps[ 1] = pi + 7; ps[ 2] = pi + 6; ps[ 3] = pi + 5;
|
|
tmp1 = cs[2]; tmp2 = cs[3];
|
|
cs[2] = tmp2; cs[3] = ci;
|
|
cs[0] = tmp1; cs[1] = ci + 1;
|
|
break;
|
|
// prettier-ignore
|
|
case 2:
|
|
tmp1 = ps[15];
|
|
tmp2 = ps[11];
|
|
ps[12] = ps[3]; ps[13] = pi + 0; ps[14] = pi + 1; ps[15] = pi + 2;
|
|
ps[ 8] = ps[7]; ps[ 9] = pi + 9; ps[10] = pi + 10; ps[11] = pi + 3;
|
|
ps[ 4] = tmp2; ps[ 5] = pi + 8; ps[ 6] = pi + 11; ps[ 7] = pi + 4;
|
|
ps[ 0] = tmp1; ps[ 1] = pi + 7; ps[ 2] = pi + 6; ps[ 3] = pi + 5;
|
|
tmp1 = cs[3];
|
|
cs[2] = cs[1]; cs[3] = ci;
|
|
cs[0] = tmp1; cs[1] = ci + 1;
|
|
break;
|
|
// prettier-ignore
|
|
case 3:
|
|
ps[12] = ps[0]; ps[13] = pi + 0; ps[14] = pi + 1; ps[15] = pi + 2;
|
|
ps[ 8] = ps[1]; ps[ 9] = pi + 9; ps[10] = pi + 10; ps[11] = pi + 3;
|
|
ps[ 4] = ps[2]; ps[ 5] = pi + 8; ps[ 6] = pi + 11; ps[ 7] = pi + 4;
|
|
ps[ 0] = ps[3]; ps[ 1] = pi + 7; ps[ 2] = pi + 6; ps[ 3] = pi + 5;
|
|
cs[2] = cs[0]; cs[3] = ci;
|
|
cs[0] = cs[1]; cs[1] = ci + 1;
|
|
break;
|
|
}
|
|
mesh.figures.push({
|
|
type: "patch",
|
|
coords: new Int32Array(ps), // making copies of ps and cs
|
|
colors: new Int32Array(cs),
|
|
});
|
|
}
|
|
}
|
|
|
|
function updateBounds(mesh) {
|
|
var minX = mesh.coords[0][0],
|
|
minY = mesh.coords[0][1],
|
|
maxX = minX,
|
|
maxY = minY;
|
|
for (var i = 1, ii = mesh.coords.length; i < ii; i++) {
|
|
var x = mesh.coords[i][0],
|
|
y = mesh.coords[i][1];
|
|
minX = minX > x ? x : minX;
|
|
minY = minY > y ? y : minY;
|
|
maxX = maxX < x ? x : maxX;
|
|
maxY = maxY < y ? y : maxY;
|
|
}
|
|
mesh.bounds = [minX, minY, maxX, maxY];
|
|
}
|
|
|
|
function packData(mesh) {
|
|
var i, ii, j, jj;
|
|
|
|
var coords = mesh.coords;
|
|
var coordsPacked = new Float32Array(coords.length * 2);
|
|
for (i = 0, j = 0, ii = coords.length; i < ii; i++) {
|
|
var xy = coords[i];
|
|
coordsPacked[j++] = xy[0];
|
|
coordsPacked[j++] = xy[1];
|
|
}
|
|
mesh.coords = coordsPacked;
|
|
|
|
var colors = mesh.colors;
|
|
var colorsPacked = new Uint8Array(colors.length * 3);
|
|
for (i = 0, j = 0, ii = colors.length; i < ii; i++) {
|
|
var c = colors[i];
|
|
colorsPacked[j++] = c[0];
|
|
colorsPacked[j++] = c[1];
|
|
colorsPacked[j++] = c[2];
|
|
}
|
|
mesh.colors = colorsPacked;
|
|
|
|
var figures = mesh.figures;
|
|
for (i = 0, ii = figures.length; i < ii; i++) {
|
|
var figure = figures[i],
|
|
ps = figure.coords,
|
|
cs = figure.colors;
|
|
for (j = 0, jj = ps.length; j < jj; j++) {
|
|
ps[j] *= 2;
|
|
cs[j] *= 3;
|
|
}
|
|
}
|
|
}
|
|
|
|
function Mesh(stream, matrix, xref, res, pdfFunctionFactory) {
|
|
if (!isStream(stream)) {
|
|
throw new FormatError("Mesh data is not a stream");
|
|
}
|
|
var dict = stream.dict;
|
|
this.matrix = matrix;
|
|
this.shadingType = dict.get("ShadingType");
|
|
this.type = "Pattern";
|
|
const bbox = dict.getArray("BBox");
|
|
if (Array.isArray(bbox) && bbox.length === 4) {
|
|
this.bbox = Util.normalizeRect(bbox);
|
|
} else {
|
|
this.bbox = null;
|
|
}
|
|
var cs = dict.get("ColorSpace", "CS");
|
|
cs = ColorSpace.parse(cs, xref, res, pdfFunctionFactory);
|
|
this.cs = cs;
|
|
this.background = dict.has("Background")
|
|
? cs.getRgb(dict.get("Background"), 0)
|
|
: null;
|
|
|
|
var fnObj = dict.get("Function");
|
|
var fn = fnObj ? pdfFunctionFactory.createFromArray(fnObj) : null;
|
|
|
|
this.coords = [];
|
|
this.colors = [];
|
|
this.figures = [];
|
|
|
|
var decodeContext = {
|
|
bitsPerCoordinate: dict.get("BitsPerCoordinate"),
|
|
bitsPerComponent: dict.get("BitsPerComponent"),
|
|
bitsPerFlag: dict.get("BitsPerFlag"),
|
|
decode: dict.getArray("Decode"),
|
|
colorFn: fn,
|
|
colorSpace: cs,
|
|
numComps: fn ? 1 : cs.numComps,
|
|
};
|
|
var reader = new MeshStreamReader(stream, decodeContext);
|
|
|
|
var patchMesh = false;
|
|
switch (this.shadingType) {
|
|
case ShadingType.FREE_FORM_MESH:
|
|
decodeType4Shading(this, reader);
|
|
break;
|
|
case ShadingType.LATTICE_FORM_MESH:
|
|
var verticesPerRow = dict.get("VerticesPerRow") | 0;
|
|
if (verticesPerRow < 2) {
|
|
throw new FormatError("Invalid VerticesPerRow");
|
|
}
|
|
decodeType5Shading(this, reader, verticesPerRow);
|
|
break;
|
|
case ShadingType.COONS_PATCH_MESH:
|
|
decodeType6Shading(this, reader);
|
|
patchMesh = true;
|
|
break;
|
|
case ShadingType.TENSOR_PATCH_MESH:
|
|
decodeType7Shading(this, reader);
|
|
patchMesh = true;
|
|
break;
|
|
default:
|
|
unreachable("Unsupported mesh type.");
|
|
break;
|
|
}
|
|
|
|
if (patchMesh) {
|
|
// dirty bounds calculation for determining, how dense shall be triangles
|
|
updateBounds(this);
|
|
for (var i = 0, ii = this.figures.length; i < ii; i++) {
|
|
buildFigureFromPatch(this, i);
|
|
}
|
|
}
|
|
// calculate bounds
|
|
updateBounds(this);
|
|
|
|
packData(this);
|
|
}
|
|
|
|
Mesh.prototype = {
|
|
getIR: function Mesh_getIR() {
|
|
return [
|
|
"Mesh",
|
|
this.shadingType,
|
|
this.coords,
|
|
this.colors,
|
|
this.figures,
|
|
this.bounds,
|
|
this.matrix,
|
|
this.bbox,
|
|
this.background,
|
|
];
|
|
},
|
|
};
|
|
|
|
return Mesh;
|
|
})();
|
|
|
|
Shadings.Dummy = (function DummyClosure() {
|
|
function Dummy() {
|
|
this.type = "Pattern";
|
|
}
|
|
|
|
Dummy.prototype = {
|
|
getIR: function Dummy_getIR() {
|
|
return ["Dummy"];
|
|
},
|
|
};
|
|
return Dummy;
|
|
})();
|
|
|
|
function getTilingPatternIR(operatorList, dict, args) {
|
|
let matrix = dict.getArray("Matrix");
|
|
let bbox = Util.normalizeRect(dict.getArray("BBox"));
|
|
let xstep = dict.get("XStep");
|
|
let ystep = dict.get("YStep");
|
|
let paintType = dict.get("PaintType");
|
|
let tilingType = dict.get("TilingType");
|
|
|
|
// Ensure that the pattern has a non-zero width and height, to prevent errors
|
|
// in `pattern_helper.js` (fixes issue8330.pdf).
|
|
if (bbox[2] - bbox[0] === 0 || bbox[3] - bbox[1] === 0) {
|
|
throw new FormatError(`Invalid getTilingPatternIR /BBox array: [${bbox}].`);
|
|
}
|
|
|
|
return [
|
|
"TilingPattern",
|
|
args,
|
|
operatorList,
|
|
matrix,
|
|
bbox,
|
|
xstep,
|
|
ystep,
|
|
paintType,
|
|
tilingType,
|
|
];
|
|
}
|
|
|
|
export { Pattern, getTilingPatternIR };
|