@openpresentation/opf-pptx 0.5.2 → 0.6.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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +15 -12
  3. package/package.json +5 -5
package/README.md CHANGED
@@ -4,7 +4,7 @@ Pure local PowerPoint conversion tooling for Open Presentation Format documents.
4
4
 
5
5
  ## Scope
6
6
 
7
- Version 0.5.2 improves native metrics, quotes, code panels, timelines and chart-label contrast, with renderer 0.5.1 compatibility and fitted quote footer separation. See [native content evidence and limitations](docs/native-content-candidate.md). Native chart geometry and general scalar-text wrapping remain different from preview; editability and valid reimport do not establish raster equivalence.
7
+ Version 0.6.0 requires core 0.8.0 and uses renderer 0.6.0 for coordinated preview/font measurement. Quotes export their accepted body/source lines and styles without another fitting pass. [Controlled Windows PowerPoint evidence](docs/evidence/shared-quote-integration/comparison.json) records glyph containment, separation, save/reopen and text reimport against its exact source/font hashes. Native quote import returns editable text blocks and does not restore the original OPF quote structure, typography or readability policy. Native chart geometry and general scalar-text wrapping also remain different from preview; editability and valid reimport do not establish raster equivalence.
8
8
 
9
9
  - Package: `@openpresentation/opf-pptx`
10
10
  - Repository: `OpenPresentation/opf-pptx`
package/dist/index.js CHANGED
@@ -907,7 +907,7 @@ async function addSlide(pptx, presentation, opfSlide, slideIndex, context, optio
907
907
  } else if (item.field === "text" && typeof item.value === "string") {
908
908
  slide.addText(item.text.lines.join("\n"), {...textBoxOptions(region, slideContext, item.text.fontSize * 0.75),fontFace:item.textStyle.fontFamily,bold:item.textStyle.fontWeight>=600,italic:item.textStyle.italic});
909
909
  } else {
910
- await addPayload(slide, presentation, item.payload, region, item.path, { ...slideContext, composition: item.composition, contentAlignment: opfSlide.design?.contentAlignment ?? presentation.design?.contentAlignment ?? "left" }, options);
910
+ await addPayload(slide, presentation, item.payload, region, item.path, { ...slideContext, composition: item.composition, contentAlignment: opfSlide.design?.contentAlignment ?? presentation.design?.contentAlignment ?? "left" }, options, item.quoteLayout);
911
911
  }
912
912
  }
913
913
 
@@ -928,7 +928,7 @@ function fieldToType(field) {
928
928
  return field === "items" || field === "bullets" ? "list" : field;
929
929
  }
930
930
 
931
- async function addPayload(slide, presentation, payload, region, path, context, options) {
931
+ async function addPayload(slide, presentation, payload, region, path, context, options, quoteLayout) {
932
932
  const kind = inferPayloadKind(payload);
933
933
  switch (kind) {
934
934
  case "text":
@@ -956,7 +956,7 @@ async function addPayload(slide, presentation, payload, region, path, context, o
956
956
  addMetricPayload(slide, payload.metric, region, context, options, path);
957
957
  break;
958
958
  case "quote":
959
- addQuotePayload(slide, payload.quote, region, context, options, path);
959
+ addQuotePayload(slide, quoteLayout, context, options, path);
960
960
  break;
961
961
  case "timeline":
962
962
  addTimelinePayload(slide, payload.timeline, region, context, options, path);
@@ -1195,9 +1195,9 @@ function pixelBox(region) {
1195
1195
  // Each fitted line remains native editable text, without PowerPoint rewrapping it.
1196
1196
  function addMeasuredPayloadText(slide, text, box, context, options, config) {
1197
1197
  const scale = Math.min(context.dimensions.widthInches, context.dimensions.heightInches) * 96 / 720;
1198
- const style = resolveTextStyle({fontFamily: config.fontFamily ?? context.fonts.body, fontWeight: config.fontWeight ?? 400, path: config.path}, options.textMeasurement);
1199
- const fit = fitText(String(text ?? ''), box, config.fontSize * scale, (context.composition?.minFontSize ?? 16) * scale, textWidthMeasurer(style, options.textMeasurement));
1200
- if (fit.overflow) {
1198
+ const style = config.textStyle ?? resolveTextStyle({fontFamily: config.fontFamily ?? context.fonts.body, fontWeight: config.fontWeight ?? 400, path: config.path}, options.textMeasurement);
1199
+ const fit = config.fit ?? fitText(String(text ?? ''), box, config.fontSize * scale, (context.composition?.minFontSize ?? 16) * scale, textWidthMeasurer(style, options.textMeasurement));
1200
+ if (fit.overflow && !config.diagnosticsHandled) {
1201
1201
  const diagnostic = {code: 'text-overflow', path: config.path, message: 'Text exceeds its cell at the minimum font size; shorten it, increase its space, or split the slide.'};
1202
1202
  options.onDiagnostic?.(diagnostic);
1203
1203
  if (context.composition?.overflow === 'error') throw new OPFPptxError('layout-overflow', diagnostic.message, {path: config.path, issues: [diagnostic]});
@@ -1231,12 +1231,15 @@ function addMetricPayload(slide, value, region, context, options, path) {
1231
1231
  addMeasuredPayloadText(slide, [metric.label, metric.description, metric.delta].filter(Boolean).join('\n'), {x: box.x, y: box.y + box.height * .45, width: box.width, height: box.height * .55}, context, options, {path, fontSize: 23, fontWeight: 500});
1232
1232
  }
1233
1233
 
1234
- function addQuotePayload(slide, value, region, context, options, path) {
1235
- const quote = isPlainObject(value) ? value : {text: value}, box = pixelBox(region);
1236
- const attribution = [quote.attribution, quote.source].filter(Boolean).join(' - ');
1237
- // Keep the footer and an 18px gap outside the body's text fitting area.
1238
- addMeasuredPayloadText(slide, `"${quote.text ?? ''}"`, {x: box.x + 18, y: box.y + 18, width: Math.max(1, box.width - 36), height: Math.max(1, box.height - (attribution ? 94 : 36))}, context, options, {path: path + '.text', fontSize: 28, fontFamily: context.fonts.heading, fontWeight: 600});
1239
- addMeasuredPayloadText(slide, attribution, {x: box.x + 18, y: box.y + box.height - 58, width: box.width - 36, height: 40}, context, options, {path, fontSize: 17, fontWeight: 500, color: context.colors.mutedText});
1234
+ function addQuotePayload(slide, layout, context, options, path) {
1235
+ if (!layout) throw new OPFPptxError('missing-quote-layout', 'Quote export requires a coordinated core build with shared quote geometry.', {path});
1236
+ for (const part of layout.parts) {
1237
+ if (!part.fit) throw new OPFPptxError('layout-overflow', 'Quote content has no usable internal space; increase its cell size before exporting.', {path:part.path,issues:layout.diagnostics});
1238
+ addMeasuredPayloadText(slide,part.text,part.box,context,options,{
1239
+ path:part.path,fit:part.fit,textStyle:part.style,diagnosticsHandled:true,
1240
+ color:part.role==='footer'?context.colors.mutedText:context.colors.text,
1241
+ });
1242
+ }
1240
1243
  }
1241
1244
 
1242
1245
  function addTimelinePayload(slide, value, region, context, options, path) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openpresentation/opf-pptx",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "Pure local OPF to PPTX export and PPTX to OPF import tooling.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -38,7 +38,7 @@
38
38
  "scripts": {
39
39
  "build": "node scripts/build.mjs",
40
40
  "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 && node --check src/table-import.js && node --check test/rich-table-import.mjs && node --check test/rich-table-import-browser.js && node --check test/table-row-sizing.mjs && node --check test/table-styles.mjs && node --check test/table-styles-browser.js && node --check test/styled-table.mjs && node --check src/table-cell-import.js && node --check test/styled-table-import.mjs && node --check test/styled-table-import-browser.js && node --check src/table-border-import.js && node --check test/table-border-styles.mjs",
41
- "test": "npm run build && node test/dependency-boundary.mjs && npm run build:browser-check && npm run test:styled-table && node test/content-layout.mjs",
41
+ "test": "npm run build && node test/dependency-boundary.mjs && npm run build:browser-check && npm run test:styled-table && node test/content-layout.mjs && node test/shared-quote.mjs",
42
42
  "validate": "node scripts/validate-package.mjs",
43
43
  "test:packed": "node test/packed-install.mjs",
44
44
  "test:compare-published": "node test/compare-published.mjs",
@@ -48,14 +48,14 @@
48
48
  "test:styled-table": "node test/styled-table.mjs && node test/styled-table-import.mjs"
49
49
  },
50
50
  "dependencies": {
51
- "@openpresentation/opf": "^0.7.0",
51
+ "@openpresentation/opf": "^0.8.0",
52
52
  "fast-xml-parser": "^5.8.0",
53
53
  "fflate": "^0.8.3",
54
54
  "jszip": "3.10.1",
55
55
  "sharp": "0.35.4"
56
56
  },
57
57
  "peerDependencies": {
58
- "@openpresentation/opf-render": "^0.5.1"
58
+ "@openpresentation/opf-render": "^0.6.0"
59
59
  },
60
60
  "peerDependenciesMeta": {
61
61
  "@openpresentation/opf-render": {
@@ -63,7 +63,7 @@
63
63
  }
64
64
  },
65
65
  "devDependencies": {
66
- "@openpresentation/opf-render": "0.5.1",
66
+ "@openpresentation/opf-render": "0.6.0",
67
67
  "esbuild": "0.28.2",
68
68
  "playwright": "1.63.0"
69
69
  },