@openpresentation/opf-pptx 0.2.1 → 0.3.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 CHANGED
@@ -121,7 +121,9 @@ Pass the same `textMeasurement` provider used by preview and pagination to `toPp
121
121
 
122
122
  PptxGenJS is pinned to 4.0.1. Its unused `image-size` dependency remains flagged by npm audit; tested OPF operations run with that parser blocked. See [dependency reachability and regression coverage](DEPENDENCY-NOTES.md).
123
123
 
124
- ### Native table fitting (unreleased)
124
+ ### Native table fitting
125
+
126
+ Core 0.5.0 accepts `TextRun[]` cells and headers. The 0.3.0 exporter preserves their resolved fonts, emphasis, color/alpha, hyperlinks, script positions and explicit line breaks as editable native runs. It measures rich content before export without inserting measured soft wraps. These changes are not in 0.2.1; version 0.3.0 requires core 0.5.0 and renderer 0.3.0. Native table import still flattens rich runs to strings, and native PowerPoint rendering remains unverified.
125
127
 
126
128
  The development exporter measures every cell with the same `textMeasurement` provider, font roles and effective nested `minFontSize` used by the SVG preview. Native table cells retain the original strings and values as text, with matching fitted sizes, line spacing, alignment, margins and row/column geometry. Uneven rows receive empty cells for missing columns. Theme border colors now use the same slot as the preview.
127
129
 
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {nativeBackgroundFill} from './background.js';
3
3
  import {importBackground} from './background-import.js';
4
4
  import { webpToPng } from '#image-fallback';
5
5
  import { rasterMetadata, pictureTransform, normalizeImageOrientation } from './image-geometry.js';
6
- import { composeSlide, fitText, textWidthMeasurer, resolveCanvasDimensions, resolveFontFamilies, resolveTextStyle } from "@openpresentation/opf/composition";
6
+ import { composeSlide, fitText, fitRichText, textWidthMeasurer, resolveCanvasDimensions, resolveFontFamilies, resolveTextStyle } from "@openpresentation/opf/composition";
7
7
  import PptxGenJS from "pptxgenjs";
8
8
  import { unzipSync, zipSync } from "fflate";
9
9
  import { XMLParser } from "fast-xml-parser";
@@ -1122,16 +1122,42 @@ function addTablePayload(slide, table, region, context, options, path) {
1122
1122
  const cellPath = header ? `${path}.columns.${columnIndex}` : `${path}.rows.${rowIndex - Number(hasHeaders)}.${columnIndex}`;
1123
1123
  const text = stringifyText(row[columnIndex]);
1124
1124
  const style = resolveTextStyle({ fontFamily: context.fonts.body, fontWeight: header ? 700 : 400, italic: false, path: cellPath }, options.textMeasurement);
1125
- const fit = fitText(text, cellBox, 15 * scale, (context.composition?.minFontSize ?? 16) * scale, textWidthMeasurer(style, options.textMeasurement));
1125
+ const rich = Array.isArray(row[columnIndex]);
1126
+ const fit = rich
1127
+ ? fitRichText(row[columnIndex], cellBox, 15 * scale, (context.composition?.minFontSize ?? 16) * scale, {style,textMeasurement:options.textMeasurement})
1128
+ : fitText(text, cellBox, 15 * scale, (context.composition?.minFontSize ?? 16) * scale, textWidthMeasurer(style, options.textMeasurement));
1129
+ const fragments = rich ? fit.richLines.flatMap(line => line.fragments) : [];
1130
+ const runs = rich ? row[columnIndex].flatMap((value, index) => {
1131
+ const run = typeof value === 'string' ? {text:value} : value;
1132
+ const fragment = fragments.find(item => item.runIndex === index);
1133
+ const runStyle = fragment?.style ?? resolveTextStyle({...style,fontFamily:run.fontFamily ?? style.fontFamily,fontWeight:run.bold === undefined ? style.fontWeight : run.bold ? 700 : 400,italic:run.italic ?? style.italic}, options.textMeasurement);
1134
+ const rawColor = /^#(?:[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i.test(run.color ?? '') ? run.color.slice(1) : header ? 'FFFFFF' : context.colors.text;
1135
+ const color = normalizeHex(rawColor), transparency = rawColor.length === 8 ? (1 - parseInt(rawColor.slice(6), 16) / 255) * 100 : 0;
1136
+ const runOptions = {
1137
+ fontFace:runStyle.fontFamily,fontSize:fragment ? fragment.fontSize * .75 : fit.fontSize * .75,
1138
+ bold:runStyle.fontWeight >= 600,italic:runStyle.italic,
1139
+ underline:run.underline ? {color} : undefined,strike:run.strikethrough ? 'sngStrike' : undefined,
1140
+ color,transparency,baseline:fragment?.baselineShift ? -fragment.baselineShift / fragment.fontSize * 2000 : undefined,
1141
+ hyperlink:run.link && /^(https?:|mailto:)/i.test(run.link) ? {url:run.link} : undefined,
1142
+ };
1143
+ // PptxGenJS marks every part of a newline-containing run as a paragraph
1144
+ // break, including its final part. Split explicitly so the next styled
1145
+ // run stays on that final line, and preserve empty/trailing lines.
1146
+ const parts = run.text.split(/\r*\n/);
1147
+ return parts.map((text, part) => ({text,options:{...runOptions,breakLine:part < parts.length - 1}}));
1148
+ }) : null;
1126
1149
  return {
1127
1150
  // Keep native wrapping and the original cell value: inserting measured
1128
1151
  // soft wraps into the text would change a later import or copy operation.
1129
- text,
1152
+ text: rich ? runs : text,
1130
1153
  options: {
1131
1154
  fontFace: style.fontFamily,
1132
1155
  fontSize: fit.fontSize * 0.75,
1133
- bold: style.fontWeight >= 600,
1134
- italic: style.italic,
1156
+ // PptxGenJS fills falsy run options from cell defaults. Rich runs
1157
+ // carry their resolved weight, so a bold header default must not turn
1158
+ // an explicit bold:false run back on.
1159
+ bold: rich ? false : style.fontWeight >= 600,
1160
+ italic: rich ? false : style.italic,
1135
1161
  lineSpacing: fit.lineHeight * 0.75,
1136
1162
  paraSpaceAfter: 0,
1137
1163
  align: context.contentAlignment,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openpresentation/opf-pptx",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Pure local OPF to PPTX export and PPTX to OPF import tooling.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -35,21 +35,21 @@
35
35
  "sideEffects": false,
36
36
  "scripts": {
37
37
  "build": "node scripts/build.mjs",
38
- "typecheck": "node --check src/index.js && node --check scripts/build.mjs && node --check scripts/validate-package.mjs && node --check test/smoke.mjs && node --check src/image-geometry.js && node --check test/image-fit.mjs && node --check test/image-orientation.mjs && node --check test/table-layout.mjs && node --check test/table-import.mjs && node --check test/object-ids.mjs && node --check test/export-corpus.mjs && node --check test/image-media-type.mjs && node --check src/image-fallback-node.js && node --check src/image-fallback-browser.js && node --check test/webp-fallback.mjs && node --check scripts/build-browser-check.mjs && node --check test/webp-fallback-browser.js && node --check test/image-fallback-boundary.mjs && node --check src/background.js && node --check test/background.mjs && node --check src/image-import.js && node --check test/image-import.mjs && node --check test/native-background.mjs && node --check src/background-import.js && node --check test/background-inheritance.mjs",
38
+ "typecheck": "node --check src/index.js && node --check scripts/build.mjs && node --check scripts/validate-package.mjs && node --check test/smoke.mjs && node --check src/image-geometry.js && node --check test/image-fit.mjs && node --check test/image-orientation.mjs && node --check test/table-layout.mjs && node --check test/table-import.mjs && node --check test/object-ids.mjs && node --check test/export-corpus.mjs && node --check test/image-media-type.mjs && node --check src/image-fallback-node.js && node --check src/image-fallback-browser.js && node --check test/webp-fallback.mjs && node --check scripts/build-browser-check.mjs && node --check test/webp-fallback-browser.js && node --check test/image-fallback-boundary.mjs && node --check src/background.js && node --check test/background.mjs && node --check src/image-import.js && node --check test/image-import.mjs && node --check test/native-background.mjs && node --check src/background-import.js && node --check test/background-inheritance.mjs && node --check test/rich-table.mjs",
39
39
  "test": "npm run build && node test/dependency-boundary.mjs && npm run build:browser-check",
40
40
  "validate": "node scripts/validate-package.mjs",
41
41
  "prepack": "npm run build",
42
42
  "build:browser-check": "node scripts/build-browser-check.mjs"
43
43
  },
44
44
  "dependencies": {
45
- "@openpresentation/opf": "^0.4.1",
45
+ "@openpresentation/opf": "^0.5.0",
46
46
  "fast-xml-parser": "^5.8.0",
47
47
  "fflate": "^0.8.3",
48
48
  "pptxgenjs": "4.0.1",
49
49
  "sharp": "0.35.4"
50
50
  },
51
51
  "peerDependencies": {
52
- "@openpresentation/opf-render": "^0.2.0"
52
+ "@openpresentation/opf-render": "^0.3.0"
53
53
  },
54
54
  "peerDependenciesMeta": {
55
55
  "@openpresentation/opf-render": {
@@ -57,7 +57,7 @@
57
57
  }
58
58
  },
59
59
  "devDependencies": {
60
- "@openpresentation/opf-render": "0.2.0",
60
+ "@openpresentation/opf-render": "0.3.0",
61
61
  "esbuild": "0.28.2"
62
62
  },
63
63
  "imports": {