pagyra-js 0.0.21 → 0.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +283 -264
- package/dist/browser/pagyra.min.js +18 -18
- package/dist/browser/pagyra.min.js.map +3 -3
- package/dist/src/layout/inline/run-placer.js +1 -1
- package/dist/src/layout/strategies/grid.js +0 -3
- package/dist/src/pdf/font/embedder.js +3 -3
- package/dist/src/pdf/font/font-subset.js +1 -3
- package/dist/src/pdf/font/to-unicode.js +16 -16
- package/dist/src/pdf/utils/node-text-run-factory.js +1 -27
- package/dist/tests/environment/path-resolution.spec.js +2 -1
- package/dist/tests/helpers/ai-layout-diagnostics.js +6 -6
- package/dist/tests/layout/container-query-units.spec.js +0 -7
- package/dist/tests/layout/inline-background-alignment.spec.js +6 -6
- package/dist/tests/pdf/alignments.spec.js +12 -12
- package/dist/tests/pdf/svg-stroke-dash.spec.js +8 -8
- package/dist/tests/pdf/text-transform-matrix.spec.js +1 -1
- package/dist/tests/pdf/xref-integrity.spec.js +1 -1
- package/dist/tests/verify-subset-multi.spec.js +14 -14
- package/dist/tests/verify-subset.spec.js +12 -12
- package/package.json +89 -71
- package/dist/src/image/js-png-backend.d.ts +0 -7
- package/dist/src/image/js-png-backend.js +0 -9
- package/dist/src/image/png-backend.d.ts +0 -5
- package/dist/src/image/png-backend.js +0 -1
- package/dist/src/image/png-wasm-loader.d.ts +0 -5
- package/dist/src/image/png-wasm-loader.js +0 -59
- package/dist/src/image/wasm/png_decoder_wasm.d.ts +0 -8
- package/dist/src/image/wasm/png_decoder_wasm.js +0 -24
- package/dist/src/image/wasm/png_decoder_wasm_bg.js +0 -16
- package/dist/src/image/wasm-png-backend.d.ts +0 -6
- package/dist/src/image/wasm-png-backend.js +0 -17
- package/dist/tests/image/png-backend.spec.d.ts +0 -1
- package/dist/tests/image/png-backend.spec.js +0 -34
- package/dist/tests/pdf/font-subset-registry-key.spec.d.ts +0 -1
- package/dist/tests/pdf/font-subset-registry-key.spec.js +0 -66
- package/dist/tests/pdf/header-footer.spec.d.ts +0 -1
- package/dist/tests/pdf/header-footer.spec.js +0 -46
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
let wasmModule = null;
|
|
2
|
-
let initPromise = null;
|
|
3
|
-
let loadFailed = false;
|
|
4
|
-
async function loadWasmModule() {
|
|
5
|
-
if (wasmModule) {
|
|
6
|
-
return wasmModule;
|
|
7
|
-
}
|
|
8
|
-
if (loadFailed) {
|
|
9
|
-
throw new Error("WASM PNG decoder not available (build failed)");
|
|
10
|
-
}
|
|
11
|
-
if (initPromise) {
|
|
12
|
-
return initPromise;
|
|
13
|
-
}
|
|
14
|
-
initPromise = (async () => {
|
|
15
|
-
try {
|
|
16
|
-
const wasmPath = new URL("./wasm/png_decoder_wasm.js", import.meta.url).pathname;
|
|
17
|
-
const wasm = await import(wasmPath);
|
|
18
|
-
if (wasm.decode_png_rgba.toString().includes("throw new Error")) {
|
|
19
|
-
loadFailed = true;
|
|
20
|
-
initPromise = null;
|
|
21
|
-
throw new Error("WASM stub detected");
|
|
22
|
-
}
|
|
23
|
-
await wasm.default();
|
|
24
|
-
wasmModule = wasm;
|
|
25
|
-
return wasm;
|
|
26
|
-
}
|
|
27
|
-
catch (error) {
|
|
28
|
-
loadFailed = true;
|
|
29
|
-
initPromise = null;
|
|
30
|
-
throw new Error(`WASM PNG decoder init failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
31
|
-
}
|
|
32
|
-
})();
|
|
33
|
-
return initPromise;
|
|
34
|
-
}
|
|
35
|
-
export class PngWasmLoader {
|
|
36
|
-
static async getOrInit() {
|
|
37
|
-
return loadWasmModule();
|
|
38
|
-
}
|
|
39
|
-
static async isAvailable() {
|
|
40
|
-
if (wasmModule) {
|
|
41
|
-
return true;
|
|
42
|
-
}
|
|
43
|
-
if (loadFailed) {
|
|
44
|
-
return false;
|
|
45
|
-
}
|
|
46
|
-
try {
|
|
47
|
-
await loadWasmModule();
|
|
48
|
-
return true;
|
|
49
|
-
}
|
|
50
|
-
catch {
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
static reset() {
|
|
55
|
-
wasmModule = null;
|
|
56
|
-
initPromise = null;
|
|
57
|
-
loadFailed = false;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
// Stub TypeScript types for WASM module
|
|
2
|
-
export default function init(): Promise<void>;
|
|
3
|
-
export function init(): Promise<void>;
|
|
4
|
-
export function decode_png_rgba(
|
|
5
|
-
buffer: Uint8Array,
|
|
6
|
-
targetWidth?: number,
|
|
7
|
-
targetHeight?: number,
|
|
8
|
-
): { width: number; height: number; data: Uint8Array };
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
// Stub WASM module for development (fallback to JS decoder)
|
|
2
|
-
// Replace with real WASM build when toolchain is available
|
|
3
|
-
|
|
4
|
-
let initialized = false;
|
|
5
|
-
|
|
6
|
-
async function init() {
|
|
7
|
-
if (initialized) return;
|
|
8
|
-
|
|
9
|
-
try {
|
|
10
|
-
const wasmPath = new URL("./png_decoder_wasm.js", import.meta.url).pathname;
|
|
11
|
-
const wasm = await import(wasmPath);
|
|
12
|
-
await wasm.default();
|
|
13
|
-
initialized = true;
|
|
14
|
-
} catch {
|
|
15
|
-
initialized = false;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function decode_png_rgba(buffer, targetWidth, targetHeight) {
|
|
20
|
-
throw new Error("WASM PNG decoder not built. Run 'npm run build:wasm' or use pngBackend: 'js'");
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export default init;
|
|
24
|
-
export { init, decode_png_rgba };
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
// Stub WASM module for development (fallback to JS decoder)
|
|
2
|
-
// Replace with real WASM build when toolchain is available
|
|
3
|
-
|
|
4
|
-
let initialized = false;
|
|
5
|
-
|
|
6
|
-
async function init() {
|
|
7
|
-
if (initialized) return;
|
|
8
|
-
initialized = true;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function decode_png_rgba(buffer, targetWidth, targetHeight) {
|
|
12
|
-
throw new Error("WASM PNG decoder not built. Run 'npm run build:wasm' or use pngBackend: 'js'");
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export default init;
|
|
16
|
-
export { init, decode_png_rgba };
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { ImageInfo } from "./types.js";
|
|
2
|
-
import type { ImageDecodeOptions } from "./types.js";
|
|
3
|
-
import type { PngBackend } from "./png-backend.js";
|
|
4
|
-
export declare class WasmPngBackend implements PngBackend {
|
|
5
|
-
decode(buffer: ArrayBuffer, options?: ImageDecodeOptions): Promise<ImageInfo>;
|
|
6
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { PngWasmLoader } from "./png-wasm-loader.js";
|
|
2
|
-
export class WasmPngBackend {
|
|
3
|
-
async decode(buffer, options) {
|
|
4
|
-
const wasm = await PngWasmLoader.getOrInit();
|
|
5
|
-
const targetWidth = options?.maxWidth;
|
|
6
|
-
const targetHeight = options?.maxHeight;
|
|
7
|
-
const result = wasm.decode_png_rgba(new Uint8Array(buffer), targetWidth, targetHeight);
|
|
8
|
-
return {
|
|
9
|
-
width: result.width,
|
|
10
|
-
height: result.height,
|
|
11
|
-
format: "png",
|
|
12
|
-
channels: 4,
|
|
13
|
-
bitsPerChannel: 8,
|
|
14
|
-
data: result.data.buffer,
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
-
import { ImageService } from '../../src/image/image-service.js';
|
|
3
|
-
import { PngWasmLoader } from '../../src/image/png-wasm-loader.js';
|
|
4
|
-
import { NodeEnvironment } from '../../src/environment/node-environment.js';
|
|
5
|
-
describe('ImageService PNG Backend', () => {
|
|
6
|
-
beforeEach(() => {
|
|
7
|
-
ImageService['instance'] = undefined;
|
|
8
|
-
PngWasmLoader.reset();
|
|
9
|
-
});
|
|
10
|
-
it('should use JS backend when configured', async () => {
|
|
11
|
-
const env = new NodeEnvironment();
|
|
12
|
-
const service = ImageService.getInstance(env);
|
|
13
|
-
service.setPngBackendConfig({ pngBackend: 'js' });
|
|
14
|
-
expect(service.getPngBackendConfig()).toEqual({ pngBackend: 'js' });
|
|
15
|
-
});
|
|
16
|
-
it('should default to auto backend', async () => {
|
|
17
|
-
const env = new NodeEnvironment();
|
|
18
|
-
const service = ImageService.getInstance(env);
|
|
19
|
-
expect(service.getPngBackendConfig()).toEqual({ pngBackend: 'auto' });
|
|
20
|
-
});
|
|
21
|
-
it('should fallback to JS when WASM is not available', async () => {
|
|
22
|
-
const env = new NodeEnvironment();
|
|
23
|
-
const service = ImageService.getInstance(env);
|
|
24
|
-
service.setPngBackendConfig({ pngBackend: 'auto' });
|
|
25
|
-
const isWasmAvailable = await PngWasmLoader.isAvailable();
|
|
26
|
-
expect(isWasmAvailable).toBe(false);
|
|
27
|
-
});
|
|
28
|
-
it('should throw in strict mode when WASM unavailable', async () => {
|
|
29
|
-
const env = new NodeEnvironment();
|
|
30
|
-
const service = ImageService.getInstance(env);
|
|
31
|
-
service.setPngBackendConfig({ pngBackend: 'wasm', wasm: { strict: true } });
|
|
32
|
-
await expect(PngWasmLoader.getOrInit()).rejects.toThrow();
|
|
33
|
-
});
|
|
34
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { PdfFontRegistry } from "../../src/pdf/font-subset/font-registry.js";
|
|
3
|
-
function makeFont(family, glyphIdForA, rawAccessor) {
|
|
4
|
-
const unicodeMap = new Map([[65, glyphIdForA]]);
|
|
5
|
-
return {
|
|
6
|
-
metrics: {
|
|
7
|
-
metrics: {
|
|
8
|
-
unitsPerEm: 1000,
|
|
9
|
-
ascender: 800,
|
|
10
|
-
descender: -200,
|
|
11
|
-
lineGap: 0,
|
|
12
|
-
capHeight: 700,
|
|
13
|
-
xHeight: 500,
|
|
14
|
-
},
|
|
15
|
-
glyphMetrics: new Map([
|
|
16
|
-
[0, { advanceWidth: 500, leftSideBearing: 0 }],
|
|
17
|
-
[glyphIdForA, { advanceWidth: 600, leftSideBearing: 0 }],
|
|
18
|
-
]),
|
|
19
|
-
cmap: {
|
|
20
|
-
getGlyphId: (cp) => unicodeMap.get(cp) ?? 0,
|
|
21
|
-
hasCodePoint: (cp) => unicodeMap.has(cp),
|
|
22
|
-
unicodeMap,
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
program: {
|
|
26
|
-
sourceFormat: "ttf",
|
|
27
|
-
unitsPerEm: 1000,
|
|
28
|
-
glyphCount: 2048,
|
|
29
|
-
getRawTableData: rawAccessor,
|
|
30
|
-
},
|
|
31
|
-
css: {
|
|
32
|
-
family,
|
|
33
|
-
weight: 700,
|
|
34
|
-
style: "normal",
|
|
35
|
-
},
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
describe("PdfFontRegistry keying", () => {
|
|
39
|
-
it("separates subsets for different actual fonts even with same css family stack", () => {
|
|
40
|
-
const registry = new PdfFontRegistry();
|
|
41
|
-
const familyStack = 'system-ui, -apple-system, "Segoe UI", Roboto, sans-serif';
|
|
42
|
-
const fontA = makeFont(familyStack, 44, () => null);
|
|
43
|
-
const fontB = makeFont(familyStack, 43, () => null);
|
|
44
|
-
const runA = {
|
|
45
|
-
font: fontA,
|
|
46
|
-
glyphIds: [44],
|
|
47
|
-
positions: [{ x: 0, y: 0 }],
|
|
48
|
-
text: "H",
|
|
49
|
-
fontSize: 14,
|
|
50
|
-
};
|
|
51
|
-
const runB = {
|
|
52
|
-
font: fontB,
|
|
53
|
-
glyphIds: [43],
|
|
54
|
-
positions: [{ x: 0, y: 0 }],
|
|
55
|
-
text: "H",
|
|
56
|
-
fontSize: 14,
|
|
57
|
-
};
|
|
58
|
-
registry.registerGlyphRun(runA);
|
|
59
|
-
registry.registerGlyphRun(runB);
|
|
60
|
-
const subsetA = registry.ensureSubsetFor(fontA);
|
|
61
|
-
const subsetB = registry.ensureSubsetFor(fontB);
|
|
62
|
-
expect(subsetA.subset.name).not.toBe(subsetB.subset.name);
|
|
63
|
-
expect(subsetA.subset.encodeGlyph(44)).toBe(44);
|
|
64
|
-
expect(subsetB.subset.encodeGlyph(43)).toBe(43);
|
|
65
|
-
});
|
|
66
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { pickHeaderVariant } from '../../src/pdf/header-footer-layout.js';
|
|
3
|
-
import { LayerMode } from '../../src/pdf/types.js';
|
|
4
|
-
describe('Header/Footer Variant Selection', () => {
|
|
5
|
-
const defaultVariant = { content: 'default', maxHeightPt: 50, maxHeightPx: 50 };
|
|
6
|
-
const firstVariant = { content: 'first', maxHeightPt: 50, maxHeightPx: 50 };
|
|
7
|
-
const evenVariant = { content: 'even', maxHeightPt: 50, maxHeightPx: 50 };
|
|
8
|
-
const oddVariant = { content: 'odd', maxHeightPt: 50, maxHeightPx: 50 };
|
|
9
|
-
const createLayout = (variants) => ({
|
|
10
|
-
headerHeightPt: 50,
|
|
11
|
-
footerHeightPt: 50,
|
|
12
|
-
headerHeightPx: 50,
|
|
13
|
-
footerHeightPx: 50,
|
|
14
|
-
layerMode: LayerMode.Under,
|
|
15
|
-
header: variants,
|
|
16
|
-
footer: { defaultVariant },
|
|
17
|
-
clipOverflow: false
|
|
18
|
-
});
|
|
19
|
-
it('should pick firstVariant on page 1', () => {
|
|
20
|
-
const layout = createLayout({ defaultVariant, firstVariant });
|
|
21
|
-
expect(pickHeaderVariant(layout, 1, 3)?.content).toBe('first');
|
|
22
|
-
});
|
|
23
|
-
it('should pick evenVariant on even pages', () => {
|
|
24
|
-
const layout = createLayout({ defaultVariant, evenVariant, oddVariant });
|
|
25
|
-
expect(pickHeaderVariant(layout, 2, 3)?.content).toBe('even');
|
|
26
|
-
});
|
|
27
|
-
it('should pick oddVariant on odd pages (except first if firstVariant exists)', () => {
|
|
28
|
-
const layout = createLayout({ defaultVariant, firstVariant, evenVariant, oddVariant });
|
|
29
|
-
expect(pickHeaderVariant(layout, 1, 3)?.content).toBe('first');
|
|
30
|
-
expect(pickHeaderVariant(layout, 3, 3)?.content).toBe('odd');
|
|
31
|
-
});
|
|
32
|
-
it('should fall back to defaultVariant if specific variant is missing', () => {
|
|
33
|
-
const layout = createLayout({ defaultVariant, firstVariant });
|
|
34
|
-
expect(pickHeaderVariant(layout, 2, 3)?.content).toBe('default');
|
|
35
|
-
expect(pickHeaderVariant(layout, 3, 3)?.content).toBe('default');
|
|
36
|
-
});
|
|
37
|
-
it('should pick oddVariant on page 1 if firstVariant is missing', () => {
|
|
38
|
-
const layout = createLayout({ defaultVariant, oddVariant });
|
|
39
|
-
expect(pickHeaderVariant(layout, 1, 3)?.content).toBe('odd');
|
|
40
|
-
});
|
|
41
|
-
it('should work on single page documents', () => {
|
|
42
|
-
const layout = createLayout({ defaultVariant, evenVariant, oddVariant });
|
|
43
|
-
// Page 1 is odd
|
|
44
|
-
expect(pickHeaderVariant(layout, 1, 1)?.content).toBe('odd');
|
|
45
|
-
});
|
|
46
|
-
});
|