This commit is contained in:
2026-06-08 13:26:57 +02:00
parent be60be5b03
commit 141762041b
2444 changed files with 1179392 additions and 15 deletions

118
test/unit/pattern_spec.js Normal file
View File

@@ -0,0 +1,118 @@
/* Copyright 2026 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.
*/
import { Dict, Name } from "../../src/core/primitives.js";
import {
GlobalColorSpaceCache,
LocalColorSpaceCache,
} from "../../src/core/image_utils.js";
import { compilePatternInfo } from "../../src/core/obj_bin_transform_core.js";
import { Pattern } from "../../src/core/pattern.js";
import { PatternInfo } from "../../src/display/obj_bin_transform_display.js";
describe("pattern", function () {
describe("FunctionBasedShading", function () {
function createFunctionBasedShading({
colorSpace = "DeviceRGB",
domain = [0, 1, 0, 1],
matrix = [2, 0, 0, 3, 10, 20],
background = null,
fn = (src, srcOffset, dest, destOffset) => {
dest[destOffset] = src[srcOffset];
dest[destOffset + 1] = src[srcOffset + 1];
dest[destOffset + 2] = 0;
},
} = {}) {
const dict = new Dict();
dict.set("ShadingType", 1);
dict.set("ColorSpace", Name.get(colorSpace));
dict.set("Domain", domain);
dict.set("Matrix", matrix);
if (background) {
dict.set("Background", background);
}
dict.set("Function", {
fn,
});
const pdfFunctionFactory = {
create(fnObj) {
return fnObj.fn;
},
};
const xref = {
fetchIfRef(obj) {
return obj;
},
};
return Pattern.parseShading(
dict,
xref,
/* res = */ null,
pdfFunctionFactory,
new GlobalColorSpaceCache(),
new LocalColorSpaceCache()
);
}
it("must convert Type 1 shading into packed mesh IR", function () {
const shading = createFunctionBasedShading();
const ir = shading.getIR();
expect(ir[0]).toEqual("Mesh");
expect(ir[1]).toEqual(1);
// Vertices are pre-expanded: 3×4 lattice →
// 6 quads → 12 triangles → 36 vertices
expect(ir[2]).toBeInstanceOf(Float32Array);
expect(ir[2].length).toEqual(72); // 36 vertices × 2 coords
expect(ir[3]).toBeInstanceOf(Uint8Array);
expect(ir[3].length).toEqual(144); // 36 vertices × 4 bytes
expect(ir[4]).toEqual(36); // vertexCount
expect(ir[5]).toEqual([10, 20, 12, 23]);
expect(ir[6]).toBeNull();
expect(ir[7]).toBeNull();
});
it("must keep mesh colors intact through binary serialization", function () {
const shading = createFunctionBasedShading({
background: [0.25, 0.5, 0.75],
});
const ir = shading.getIR();
const buffer = compilePatternInfo(ir);
const reconstructedIR = new PatternInfo(buffer).getIR();
expect(reconstructedIR[0]).toEqual("Mesh");
expect(reconstructedIR[1]).toEqual(1);
expect(Array.from(reconstructedIR[2])).toEqual(Array.from(ir[2]));
expect(Array.from(reconstructedIR[3])).toEqual(Array.from(ir[3]));
expect(Array.from(reconstructedIR[7])).toEqual([64, 128, 191]);
});
it("must sample the upper and right edges half a step inside", function () {
const shading = createFunctionBasedShading({
colorSpace: "DeviceGray",
fn(src, srcOffset, dest, destOffset) {
dest[destOffset] =
src[srcOffset] === 1 || src[srcOffset + 1] === 1 ? 1 : 0;
},
});
const [, , , colors] = shading.getIR();
expect(colors.length).toEqual(144);
expect(Array.from(colors)).toEqual(new Array(144).fill(0));
});
});
});