@openpresentation/opf-render 0.2.0 → 0.4.0
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 +2 -0
- package/dist/svg.js +19 -44
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -104,6 +104,8 @@ This repo does not require an npm automation token when Trusted Publishing is co
|
|
|
104
104
|
|
|
105
105
|
The current checkout uses `@openpresentation/opf/composition` for portable geometry. Slides can select `auto`, `row`, `column`, or `grid`, set weighted tracks, and request path-specific overflow diagnostics. See the sibling OPF repo's `docs/dynamic-composition.md` for the complete contract.
|
|
106
106
|
|
|
107
|
+
Version 0.3.0 requires core 0.5.0 and renders rich table cells and headers using canonical `TextRun[]`, including editor trace geometry.
|
|
108
|
+
|
|
107
109
|
Version 0.2.x requires the published `@openpresentation/opf@^0.4.1` for composition, pagination and measured text. A clean `npm ci && npm test` uses registry packages and runs the full raster corpus without sibling checkouts. For coordinated source development, build OPF and run `node scripts/link-ecosystem.mjs` there; `pnpm test:ecosystem` checks editing, rendering, editable PowerPoint geometry and import together.
|
|
108
110
|
|
|
109
111
|
For actual font measurement, load `loadBundledFontRegistry()` from `@openpresentation/opf-render/fonts-node`, then pass its `textMeasurement` to rendering and its `embeddedFonts` to SVG export. Pass its `fontFiles` to PNG/PDF conversion. The browser-safe `@openpresentation/opf-render/fonts` entry accepts local font bytes. Missing fonts/glyphs fail explicitly; aliases and fallback are opt-in and recorded in `registry.substitutions`. Bundled font license notices travel with embedded SVG fonts.
|
package/dist/svg.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { fitList, fitRichText, composeSlide, resolveCanvasDimensions, resolveFontFamilies, resolveTextStyle, textWidthMeasurer, fitText } from "@openpresentation/opf/composition";
|
|
1
|
+
import { layoutTable, fitList, fitRichText, composeSlide, resolveCanvasDimensions, resolveFontFamilies, resolveTextStyle, textWidthMeasurer, fitText } from "@openpresentation/opf/composition";
|
|
2
2
|
import {
|
|
3
3
|
catalogs as bundledCatalogs,
|
|
4
4
|
validatePresentation
|
|
@@ -911,47 +911,22 @@ function renderTimeline(item, box, bound, options) {
|
|
|
911
911
|
|
|
912
912
|
function renderTable(item, box, bound, options) {
|
|
913
913
|
const scale = Math.min(bound.design.dimensions.width, bound.design.dimensions.height) / 720;
|
|
914
|
-
const
|
|
915
|
-
const columns = Array.isArray(table.columns) ? table.columns : [];
|
|
916
|
-
const rows = Array.isArray(table.rows) ? table.rows : [];
|
|
917
|
-
const allRows = columns.length ? [columns, ...rows] : rows;
|
|
918
|
-
const columnCount = Math.max(1, ...allRows.map((row) => Array.isArray(row) ? row.length : 1));
|
|
919
|
-
const rowCount = Math.max(1, allRows.length);
|
|
920
|
-
const cellWidth = box.width / columnCount;
|
|
921
|
-
const cellHeight = Math.min(54 * scale, box.height / rowCount);
|
|
914
|
+
const layout = layoutTable(item.value, box, {scale, minFontSize:(bound.composition ?? bound.geometry.composition).minFontSize, fontFamily:bound.design.fonts.body, textMeasurement:options.textMeasurement, path:item.path});
|
|
922
915
|
const children = [];
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
"stroke-width": 1,
|
|
938
|
-
...traceAttrs(options, cellPath)
|
|
939
|
-
}));
|
|
940
|
-
children.push(renderTextBox(flattenText(cells[columnIndex] ?? ""), {
|
|
941
|
-
x: x + 10 * scale,
|
|
942
|
-
y: y + 8 * scale,
|
|
943
|
-
width: Math.max(scale, cellWidth - 20 * scale),
|
|
944
|
-
height: Math.max(scale, cellHeight - 12 * scale)
|
|
945
|
-
}, bound, {
|
|
946
|
-
path: cellPath,
|
|
947
|
-
fontSize: 15,
|
|
948
|
-
fontFamily: bound.design.fonts.body,
|
|
949
|
-
fontWeight: rowIndex === 0 && columns.length ? 700 : 400,
|
|
950
|
-
fill: rowIndex === 0 && columns.length ? "#FFFFFF" : bound.design.colors.text,
|
|
951
|
-
options
|
|
952
|
-
}));
|
|
953
|
-
}
|
|
954
|
-
});
|
|
916
|
+
for (const row of layout.rows) for (const cell of row.cells) {
|
|
917
|
+
children.push(tag("rect", {
|
|
918
|
+
x: stableNumber(cell.box.x), y: stableNumber(cell.box.y),
|
|
919
|
+
width: stableNumber(cell.box.width), height: stableNumber(cell.box.height),
|
|
920
|
+
fill: cell.header ? bound.design.colors.primary : bound.design.colors.surface,
|
|
921
|
+
stroke: bound.design.colors.border, "stroke-width":1,
|
|
922
|
+
...traceAttrs(options, cell.path)
|
|
923
|
+
}));
|
|
924
|
+
children.push((cell.rich ? renderRichTextBox : renderTextBox)(cell.rich ? cell.value : flattenText(cell.value ?? ""), cell.textBox, bound, {
|
|
925
|
+
path:cell.path, fontSize:15, fontFamily:bound.design.fonts.body,
|
|
926
|
+
fontWeight:cell.header ? 700 : 400, textStyle:cell.textStyle, fit:cell.fit,
|
|
927
|
+
fill:cell.header ? "#FFFFFF" : bound.design.colors.text, options
|
|
928
|
+
}));
|
|
929
|
+
}
|
|
955
930
|
|
|
956
931
|
return tag("g", traceAttrs(options, item.path), children.join("\n"));
|
|
957
932
|
}
|
|
@@ -1167,7 +1142,7 @@ function renderEmbeddedFonts(fonts = []) {
|
|
|
1167
1142
|
|
|
1168
1143
|
function renderRichTextBox(value, box, bound, config) {
|
|
1169
1144
|
const scale=Math.min(bound.design.dimensions.width,bound.design.dimensions.height)/720;
|
|
1170
|
-
const fit=fitRichText(value,box,config.fontSize*scale,((bound.composition??bound.geometry.composition).minFontSize??16)*scale,{style:{fontFamily:config.fontFamily,fontWeight:config.fontWeight??400,path:config.path},textMeasurement:config.options.textMeasurement});
|
|
1145
|
+
const fit=config.fit??fitRichText(value,box,config.fontSize*scale,((bound.composition??bound.geometry.composition).minFontSize??16)*scale,{style:{fontFamily:config.fontFamily,fontWeight:config.fontWeight??400,path:config.path},textMeasurement:config.options.textMeasurement});
|
|
1171
1146
|
if(fit.overflow){const diagnostic={code:'text-overflow',path:config.path,message:'Mixed-style text exceeds its cell at the minimum font size.'};reportDiagnostic(diagnostic,config.options);if((bound.composition??bound.geometry.composition).overflow==='error')throw new OPFRenderError('layout-overflow',diagnostic.message,{issues:[diagnostic]});}
|
|
1172
1147
|
return renderRichLines(value,fit,box,bound,config);
|
|
1173
1148
|
}
|
|
@@ -1195,8 +1170,8 @@ function renderRichLines(value,fit,box,bound,config) {
|
|
|
1195
1170
|
|
|
1196
1171
|
function renderTextBox(text, box, bound, config) {
|
|
1197
1172
|
const scale = Math.min(bound.design.dimensions.width, bound.design.dimensions.height) / 720;
|
|
1198
|
-
const style = resolveTextStyle({fontFamily:config.fontFamily,fontWeight:config.fontWeight ?? 400,italic:config.italic ?? false,path:config.path},config.options.textMeasurement);
|
|
1199
|
-
const fit = fitText(String(text ?? ""), box, config.fontSize * scale,
|
|
1173
|
+
const style = config.textStyle ?? resolveTextStyle({fontFamily:config.fontFamily,fontWeight:config.fontWeight ?? 400,italic:config.italic ?? false,path:config.path},config.options.textMeasurement);
|
|
1174
|
+
const fit = config.fit ?? fitText(String(text ?? ""), box, config.fontSize * scale,
|
|
1200
1175
|
((bound.composition ?? bound.geometry.composition).minFontSize ?? 16) * scale, textWidthMeasurer(style,config.options.textMeasurement));
|
|
1201
1176
|
if (fit.overflow) {
|
|
1202
1177
|
const diagnostic = { code: "text-overflow", path: config.path,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openpresentation/opf-render",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Deterministic local OPF renderer for SVG, PNG, and PDF output.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -55,9 +55,9 @@
|
|
|
55
55
|
"sideEffects": false,
|
|
56
56
|
"scripts": {
|
|
57
57
|
"build": "node scripts/build.mjs",
|
|
58
|
-
"typecheck": "node --check src/index.js && node --check scripts/build.mjs && node --check scripts/validate-package.mjs && node --check test/smoke.mjs && node --check test/golden.mjs && node --check src/fonts.js && node --check src/fonts-node.js && node --check src/font-compatibility.js && node --check test/office-fonts.mjs && node --check src/fonts-browser.js && node --check test/fonts-browser.mjs && node --check src/svg.js && node --check src/raster.js && node --check src/raster-images.js && node --check test/webp.mjs && node --check test/jpeg-orientation.mjs && node --check scripts/build-browser-check.mjs && node --check test/jpeg-browser.js",
|
|
58
|
+
"typecheck": "node --check src/index.js && node --check scripts/build.mjs && node --check scripts/validate-package.mjs && node --check test/smoke.mjs && node --check test/golden.mjs && node --check src/fonts.js && node --check src/fonts-node.js && node --check src/font-compatibility.js && node --check test/office-fonts.mjs && node --check src/fonts-browser.js && node --check test/fonts-browser.mjs && node --check src/svg.js && node --check src/raster.js && node --check src/raster-images.js && node --check test/webp.mjs && node --check test/jpeg-orientation.mjs && node --check scripts/build-browser-check.mjs && node --check test/jpeg-browser.js && node --check test/rich-table.mjs",
|
|
59
59
|
"validate": "node scripts/validate-package.mjs",
|
|
60
|
-
"test": "npm run build && node test/smoke.mjs && node test/webp.mjs && node test/jpeg-orientation.mjs && node test/fonts.mjs && node test/office-fonts.mjs && node test/timeline.mjs && node test/rich-trace.mjs && node test/golden-gate.mjs && node test/golden.mjs && node test/fonts-browser.mjs && node test/design-preview.mjs && npm run build:browser-check",
|
|
60
|
+
"test": "npm run build && node test/smoke.mjs && node test/webp.mjs && node test/jpeg-orientation.mjs && node test/fonts.mjs && node test/office-fonts.mjs && node test/timeline.mjs && node test/rich-trace.mjs && node test/rich-table.mjs && node test/golden-gate.mjs && node test/golden.mjs && node test/fonts-browser.mjs && node test/design-preview.mjs && npm run build:browser-check",
|
|
61
61
|
"test:golden": "npm run build && node test/golden.mjs",
|
|
62
62
|
"golden:update": "npm run build && node test/golden.mjs --update",
|
|
63
63
|
"prepack": "npm run build",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"@expo-google-fonts/roboto": "^0.4.3",
|
|
73
73
|
"@expo-google-fonts/roboto-mono": "^0.4.2",
|
|
74
74
|
"@expo-google-fonts/tinos": "0.4.2",
|
|
75
|
-
"@openpresentation/opf": "^0.
|
|
75
|
+
"@openpresentation/opf": "^0.6.0",
|
|
76
76
|
"@resvg/resvg-js": "^2.6.2",
|
|
77
77
|
"fontkit": "^2.0.4",
|
|
78
78
|
"pdf-lib": "^1.17.1",
|