Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/core/evaluator.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
lookupMatrix,
lookupNormalRect,
} from "./core_utils.js";
import { FontInfo, PatternInfo } from "../shared/obj-bin-transform.js";
import {
getEncoding,
MacRomanEncoding,
Expand Down Expand Up @@ -72,7 +73,6 @@ import { BaseStream } from "./base_stream.js";
import { bidi } from "./bidi.js";
import { ColorSpace } from "./colorspace.js";
import { ColorSpaceUtils } from "./colorspace_utils.js";
import { FontInfo } from "../shared/obj-bin-transform.js";
import { getFontSubstitution } from "./font_substitutions.js";
import { getGlyphsUnicode } from "./glyphlist.js";
import { getMetrics } from "./metrics.js";
Expand Down Expand Up @@ -1517,7 +1517,10 @@ class PartialEvaluator {
localShadingPatternCache.set(shading, id);

if (this.parsingType3Font) {
this.handler.send("commonobj", [id, "Pattern", patternIR]);
const transfers = [];
const patternBuffer = PatternInfo.write(patternIR);
transfers.push(patternBuffer);
this.handler.send("commonobj", [id, "Pattern", patternBuffer], transfers);
} else {
this.handler.send("obj", [id, this.pageIndex, "Pattern", patternIR]);
}
Expand Down
7 changes: 5 additions & 2 deletions src/display/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import {
StatTimer,
} from "./display_utils.js";
import { FontFaceObject, FontLoader } from "./font_loader.js";
import { FontInfo, PatternInfo } from "../shared/obj-bin-transform.js";
import {
getDataProp,
getFactoryUrlProp,
Expand All @@ -67,7 +68,6 @@ import { DOMCMapReaderFactory } from "display-cmap_reader_factory";
import { DOMFilterFactory } from "./filter_factory.js";
import { DOMStandardFontDataFactory } from "display-standard_fontdata_factory";
import { DOMWasmFactory } from "display-wasm_factory";
import { FontInfo } from "../shared/obj-bin-transform.js";
import { GlobalWorkerOptions } from "./worker_options.js";
import { Metadata } from "./metadata.js";
import { OptionalContentConfig } from "./optional_content_config.js";
Expand Down Expand Up @@ -2804,9 +2804,12 @@ class WorkerTransport {
break;
case "FontPath":
case "Image":
case "Pattern":
this.commonObjs.resolve(id, exportedData);
break;
case "Pattern":
const pattern = new PatternInfo(exportedData);
this.commonObjs.resolve(id, pattern.getIR());
break;
default:
throw new Error(`Got unknown common object type ${type}`);
}
Expand Down
279 changes: 277 additions & 2 deletions src/shared/obj-bin-transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* limitations under the License.
*/

import { assert } from "./util.js";
import { assert, MeshFigureType } from "./util.js";

class CssFontInfo {
#buffer;
Expand Down Expand Up @@ -606,4 +606,279 @@ class FontInfo {
}
}

export { CssFontInfo, FontInfo, SystemFontInfo };
class PatternInfo {
static #KIND = 0; // 1=axial, 2=radial, 3=mesh

static #HAS_BBOX = 1; // 0/1

static #HAS_BACKGROUND = 2; // 0/1 (background for mesh patterns)

static #SHADING_TYPE = 3; // shadingType (only for mesh patterns)

static #N_COORD = 4; // number of coordinate pairs

static #N_COLOR = 8; // number of rgb triplets

static #N_STOP = 12; // number of gradient stops

static #N_FIGURES = 16; // number of figures

constructor(buffer) {
this.buffer = buffer;
this.view = new DataView(buffer);
this.data = new Uint8Array(buffer);
}

static write(ir) {
let kind,
bbox = null,
coords = [],
colors = [],
colorStops = [],
figures = [],
shadingType = null, // only needed for mesh patterns
background = null; // background for mesh patterns

switch (ir[0]) {
case "RadialAxial":
kind = ir[1] === "axial" ? 1 : 2;
bbox = ir[2];
colorStops = ir[3];
if (kind === 1) {
coords.push(...ir[4], ...ir[5]);
} else {
coords.push(ir[4][0], ir[4][1], ir[6], ir[5][0], ir[5][1], ir[7]);
}
break;
case "Mesh":
kind = 3;
shadingType = ir[1];
coords = ir[2];
colors = ir[3];
figures = ir[4] || [];
bbox = ir[6];
background = ir[7];
break;
default:
throw new Error(`Unsupported pattern type: ${ir[0]}`);
}

const nCoord = Math.floor(coords.length / 2);
const nColor = Math.floor(colors.length / 3);
const nStop = colorStops.length;
const nFigures = figures.length;

let figuresSize = 0;
for (const figure of figures) {
figuresSize += 1;
figuresSize = Math.ceil(figuresSize / 4) * 4; // Ensure 4-byte alignment
figuresSize += 4 + figure.coords.length * 4;
figuresSize += 4 + figure.colors.length * 4;
if (figure.verticesPerRow !== undefined) {
figuresSize += 4;
}
}

const byteLen =
20 +
nCoord * 8 +
nColor * 3 +
nStop * 8 +
(bbox ? 16 : 0) +
(background ? 3 : 0) +
figuresSize;
const buffer = new ArrayBuffer(byteLen);
const dataView = new DataView(buffer);
const u8data = new Uint8Array(buffer);

dataView.setUint8(PatternInfo.#KIND, kind);
dataView.setUint8(PatternInfo.#HAS_BBOX, bbox ? 1 : 0);
dataView.setUint8(PatternInfo.#HAS_BACKGROUND, background ? 1 : 0);
dataView.setUint8(PatternInfo.#SHADING_TYPE, shadingType); // Only for mesh pattern, null otherwise
dataView.setUint32(PatternInfo.#N_COORD, nCoord, true);
dataView.setUint32(PatternInfo.#N_COLOR, nColor, true);
dataView.setUint32(PatternInfo.#N_STOP, nStop, true);
dataView.setUint32(PatternInfo.#N_FIGURES, nFigures, true);

let offset = 20;
const coordsView = new Float32Array(buffer, offset, nCoord * 2);
coordsView.set(coords);
offset += nCoord * 8;

u8data.set(colors, offset);
offset += nColor * 3;

for (const [pos, hex] of colorStops) {
dataView.setFloat32(offset, pos, true);
offset += 4;
dataView.setUint32(offset, parseInt(hex.slice(1), 16), true);
offset += 4;
}
if (bbox) {
for (const v of bbox) {
dataView.setFloat32(offset, v, true);
offset += 4;
}
}

if (background) {
u8data.set(background, offset);
offset += 3;
}

for (let i = 0; i < figures.length; i++) {
const figure = figures[i];
dataView.setUint8(offset, figure.type);
offset += 1;
// Ensure 4-byte alignment
offset = Math.ceil(offset / 4) * 4;
dataView.setUint32(offset, figure.coords.length, true);
offset += 4;
const figureCoordsView = new Int32Array(
buffer,
offset,
figure.coords.length
);
figureCoordsView.set(figure.coords);
offset += figure.coords.length * 4;
dataView.setUint32(offset, figure.colors.length, true);
offset += 4;
const colorsView = new Int32Array(buffer, offset, figure.colors.length);
colorsView.set(figure.colors);
offset += figure.colors.length * 4;

if (figure.verticesPerRow !== undefined) {
dataView.setUint32(offset, figure.verticesPerRow, true);
offset += 4;
}
}
return buffer;
}

getIR() {
const dataView = this.view;
const kind = this.data[PatternInfo.#KIND];
const hasBBox = !!this.data[PatternInfo.#HAS_BBOX];
const hasBackground = !!this.data[PatternInfo.#HAS_BACKGROUND];
const nCoord = dataView.getUint32(PatternInfo.#N_COORD, true);
const nColor = dataView.getUint32(PatternInfo.#N_COLOR, true);
const nStop = dataView.getUint32(PatternInfo.#N_STOP, true);
const nFigures = dataView.getUint32(PatternInfo.#N_FIGURES, true);

let offset = 20;
const coords = new Float32Array(this.buffer, offset, nCoord * 2);
offset += nCoord * 8;
const colors = new Uint8Array(this.buffer, offset, nColor * 3);
offset += nColor * 3;
const stops = [];
for (let i = 0; i < nStop; ++i) {
const p = dataView.getFloat32(offset, true);
offset += 4;
const rgb = dataView.getUint32(offset, true);
offset += 4;
stops.push([p, `#${rgb.toString(16).padStart(6, "0")}`]);
}
let bbox = null;
if (hasBBox) {
bbox = [];
for (let i = 0; i < 4; ++i) {
bbox.push(dataView.getFloat32(offset, true));
offset += 4;
}
}

let background = null;
if (hasBackground) {
background = new Uint8Array(this.buffer, offset, 3);
offset += 3;
}

const figures = [];
for (let i = 0; i < nFigures; ++i) {
const type = dataView.getUint8(offset);
offset += 1;
// Ensure 4-byte alignment
offset = Math.ceil(offset / 4) * 4;

const coordsLength = dataView.getUint32(offset, true);
offset += 4;
const figureCoords = new Int32Array(this.buffer, offset, coordsLength);
offset += coordsLength * 4;

const colorsLength = dataView.getUint32(offset, true);
offset += 4;
const figureColors = new Int32Array(this.buffer, offset, colorsLength);
offset += colorsLength * 4;

const figure = {
type,
coords: figureCoords,
colors: figureColors,
};

if (type === MeshFigureType.LATTICE) {
figure.verticesPerRow = dataView.getUint32(offset, true);
offset += 4;
}

figures.push(figure);
}

if (kind === 1) {
// axial
return [
"RadialAxial",
"axial",
bbox,
stops,
Array.from(coords.slice(0, 2)),
Array.from(coords.slice(2, 4)),
null,
null,
];
}
if (kind === 2) {
return [
"RadialAxial",
"radial",
bbox,
stops,
[coords[0], coords[1]],
[coords[3], coords[4]],
coords[2],
coords[5],
];
}
if (kind === 3) {
const shadingType = this.data[PatternInfo.#SHADING_TYPE];
let bounds = null;
if (coords.length > 0) {
let minX = coords[0],
maxX = coords[0];
let minY = coords[1],
maxY = coords[1];
for (let i = 0; i < coords.length; i += 2) {
const x = coords[i],
y = coords[i + 1];
minX = minX > x ? x : minX;
minY = minY > y ? y : minY;
maxX = maxX < x ? x : maxX;
maxY = maxY < y ? y : maxY;
}
bounds = [minX, minY, maxX, maxY];
}
return [
"Mesh",
shadingType,
coords,
colors,
figures,
bounds,
bbox,
background,
];
}
throw new Error(`Unsupported pattern kind: ${kind}`);
}
}
export { CssFontInfo, FontInfo, PatternInfo, SystemFontInfo };
Loading