@hirokisakabe/pom 9.1.1 → 10.0.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 (73) hide show
  1. package/dist/buildContext.js +2 -6
  2. package/dist/buildContext.js.map +1 -1
  3. package/dist/buildPptx.d.ts +2 -1
  4. package/dist/buildPptx.d.ts.map +1 -1
  5. package/dist/buildPptx.js +4 -9
  6. package/dist/buildPptx.js.map +1 -1
  7. package/dist/calcYogaLayout/calcYogaLayout.js +1 -1
  8. package/dist/calcYogaLayout/calcYogaLayout.js.map +1 -1
  9. package/dist/index.d.ts +2 -1
  10. package/dist/registry/definitions/icon.js +1 -1
  11. package/dist/registry/definitions/list.js.map +1 -1
  12. package/dist/renderPptx/nodes/chart.js +172 -47
  13. package/dist/renderPptx/nodes/chart.js.map +1 -1
  14. package/dist/renderPptx/nodes/flow.js +98 -90
  15. package/dist/renderPptx/nodes/flow.js.map +1 -1
  16. package/dist/renderPptx/nodes/icon.js +36 -24
  17. package/dist/renderPptx/nodes/icon.js.map +1 -1
  18. package/dist/renderPptx/nodes/image.js +5 -22
  19. package/dist/renderPptx/nodes/image.js.map +1 -1
  20. package/dist/renderPptx/nodes/list.js +94 -111
  21. package/dist/renderPptx/nodes/list.js.map +1 -1
  22. package/dist/renderPptx/nodes/matrix.js +95 -70
  23. package/dist/renderPptx/nodes/matrix.js.map +1 -1
  24. package/dist/renderPptx/nodes/processArrow.js +74 -35
  25. package/dist/renderPptx/nodes/processArrow.js.map +1 -1
  26. package/dist/renderPptx/nodes/pyramid.js +37 -16
  27. package/dist/renderPptx/nodes/pyramid.js.map +1 -1
  28. package/dist/renderPptx/nodes/shape.js +66 -37
  29. package/dist/renderPptx/nodes/shape.js.map +1 -1
  30. package/dist/renderPptx/nodes/svg.js +2 -5
  31. package/dist/renderPptx/nodes/svg.js.map +1 -1
  32. package/dist/renderPptx/nodes/table.js +89 -59
  33. package/dist/renderPptx/nodes/table.js.map +1 -1
  34. package/dist/renderPptx/nodes/text.js +1 -18
  35. package/dist/renderPptx/nodes/text.js.map +1 -1
  36. package/dist/renderPptx/nodes/timeline.js +114 -74
  37. package/dist/renderPptx/nodes/timeline.js.map +1 -1
  38. package/dist/renderPptx/nodes/tree.js +81 -79
  39. package/dist/renderPptx/nodes/tree.js.map +1 -1
  40. package/dist/renderPptx/pptxAuthoring.js +381 -0
  41. package/dist/renderPptx/pptxAuthoring.js.map +1 -0
  42. package/dist/renderPptx/renderPptx.js +158 -128
  43. package/dist/renderPptx/renderPptx.js.map +1 -1
  44. package/dist/renderPptx/textOptions.js +7 -8
  45. package/dist/renderPptx/textOptions.js.map +1 -1
  46. package/dist/renderPptx/units.js +3 -3
  47. package/dist/renderPptx/units.js.map +1 -1
  48. package/dist/renderPptx/utils/backgroundBorder.js +70 -48
  49. package/dist/renderPptx/utils/backgroundBorder.js.map +1 -1
  50. package/dist/renderPptx/utils/contentArea.js +1 -1
  51. package/dist/renderPptx/utils/contentArea.js.map +1 -1
  52. package/dist/renderPptx/utils/glimpsePicture.js +111 -0
  53. package/dist/renderPptx/utils/glimpsePicture.js.map +1 -0
  54. package/dist/renderPptx/utils/glimpseShape.js +71 -0
  55. package/dist/renderPptx/utils/glimpseShape.js.map +1 -0
  56. package/dist/renderPptx/utils/glimpseTextBox.js +107 -0
  57. package/dist/renderPptx/utils/glimpseTextBox.js.map +1 -0
  58. package/dist/renderPptx/utils/straightLine.js +33 -15
  59. package/dist/renderPptx/utils/straightLine.js.map +1 -1
  60. package/dist/renderPptx/utils/visualStyle.js +3 -56
  61. package/dist/renderPptx/utils/visualStyle.js.map +1 -1
  62. package/dist/renderPptx/writablePptx.d.ts +23 -0
  63. package/dist/renderPptx/writablePptx.d.ts.map +1 -0
  64. package/dist/renderPptx/writablePptx.js +74 -0
  65. package/dist/renderPptx/writablePptx.js.map +1 -0
  66. package/dist/shared/measureImage.js.map +1 -1
  67. package/package.json +4 -5
  68. package/dist/renderPptx/glimpseTextBoxes.js +0 -347
  69. package/dist/renderPptx/glimpseTextBoxes.js.map +0 -1
  70. package/dist/renderPptx/glowEffects.js +0 -157
  71. package/dist/renderPptx/glowEffects.js.map +0 -1
  72. package/dist/renderPptx/gradientFills.js +0 -154
  73. package/dist/renderPptx/gradientFills.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pptxAuthoring.js","names":[],"sources":["../../src/renderPptx/pptxAuthoring.ts"],"sourcesContent":["import {\n addChart,\n addPicture,\n addShape,\n addTable,\n addTextBox,\n asEmu,\n asHundredthPt,\n asOoxmlAngle,\n asOoxmlPercent,\n asPt,\n createPptx,\n setSlideBackground,\n type AddChartInput,\n type AddPictureInput,\n type AddShapeColorInput,\n type AddShapeCustomGeometryPathCommandInput,\n type AddShapeEffectsInput,\n type AddShapeFillInput,\n type AddShapeGeometryInput,\n type AddShapeInput,\n type AddShapeOutlineInput,\n type AddTableInput,\n type AddTextBoxGradientFillInput,\n type AddTextBoxInput,\n type AddTextBoxParagraphInput,\n type AddTextBoxRunPropertiesInput,\n type PptxSourceModel,\n type SourceHandle,\n} from \"@pptx-glimpse/document\";\nimport { parseGradient, parseLinearGradient } from \"../shared/gradient.ts\";\nimport type {\n BorderStyle,\n PositionedNode,\n ShadowStyle,\n TextGlow,\n TextOutline,\n Underline,\n} from \"../types.ts\";\nimport { createTextOptions, resolveSubSup } from \"./textOptions.ts\";\nimport { EMU_PER_IN, pxToEmu, pxToPt } from \"./units.ts\";\n\ntype TextPositionedNode = Extract<PositionedNode, { type: \"text\" }>;\n\ninterface GlimpseTextRun {\n text: string;\n properties: AddTextBoxRunPropertiesInput;\n href?: string;\n}\n\nexport function cleanHex(color: string | undefined): string | undefined {\n const hex = color?.replace(/^#/, \"\").toUpperCase();\n return hex?.length === 3\n ? [...hex].map((character) => character.repeat(2)).join(\"\")\n : hex;\n}\n\nexport function toColorInput(\n color: string | undefined,\n opacity?: number,\n): AddShapeColorInput | undefined {\n const hex = cleanHex(color);\n if (!hex) return undefined;\n return {\n kind: \"srgb\",\n hex,\n transforms:\n opacity === undefined\n ? undefined\n : [\n {\n kind: \"alpha\",\n value: asOoxmlPercent(Math.round(opacity * 100000)),\n },\n ],\n };\n}\n\nfunction toUnderlineInput(underline: Underline | undefined) {\n if (underline === undefined || underline === false) return undefined;\n if (underline === true) return true;\n return {\n style: underline.style,\n color: toColorInput(underline.color),\n };\n}\n\nfunction toBaselineInput(\n subscript: boolean | undefined,\n superscript: boolean | undefined,\n) {\n if (subscript) {\n return {\n type: \"percent\" as const,\n value: asOoxmlPercent(-40000),\n };\n }\n if (superscript) {\n return {\n type: \"percent\" as const,\n value: asOoxmlPercent(30000),\n };\n }\n return undefined;\n}\n\nfunction toGlowInput(glow: TextGlow | undefined) {\n if (!glow) return undefined;\n return {\n radius: asEmu(Math.round(pxToEmu(glow.size ?? 8))),\n color: toColorInput(glow.color ?? \"FFFFFF\", glow.opacity ?? 0.75)!,\n };\n}\n\nfunction toOutlineInput(outline: TextOutline | undefined) {\n if (!outline) return undefined;\n return {\n width: asEmu(Math.round(pxToEmu(outline.size ?? 1))),\n color: toColorInput(outline.color ?? \"FFFFFF\"),\n };\n}\n\nfunction toCharSpacing(letterSpacingPx: number | undefined) {\n if (letterSpacingPx === undefined) return undefined;\n return Math.round(pxToPt(letterSpacingPx) * 100);\n}\n\nfunction toTextGradientInput(\n value: string | undefined,\n): AddTextBoxGradientFillInput | undefined {\n if (!value) return undefined;\n const linear = parseLinearGradient(value);\n if (!linear) return undefined;\n const dmlAngle = (((linear.angle - 90) % 360) + 360) % 360;\n return {\n gradientType: \"linear\",\n angle: asOoxmlAngle(Math.round(dmlAngle * 60000)),\n stops: linear.stops.map((stop) => ({\n position: asOoxmlPercent(Math.round(stop.position * 1000)),\n color: toColorInput(stop.color)!,\n })),\n };\n}\n\nfunction stripUndefined<T extends Record<string, unknown>>(value: T): T {\n return Object.fromEntries(\n Object.entries(value).filter(([, entry]) => entry !== undefined),\n ) as T;\n}\n\nfunction resolveUnderline(\n node: TextPositionedNode,\n run: NonNullable<TextPositionedNode[\"runs\"]>[number] | undefined,\n): Underline | undefined {\n if (run?.underline !== undefined) return run.underline;\n if (node.underline !== undefined) return node.underline;\n return run?.href ? true : undefined;\n}\n\nfunction buildRunProperties(\n node: TextPositionedNode,\n run: NonNullable<TextPositionedNode[\"runs\"]>[number] | undefined,\n gradientFill: AddTextBoxGradientFillInput | undefined,\n): AddTextBoxRunPropertiesInput {\n const fontSizePx = run?.fontSize ?? node.fontSize ?? 24;\n const subSup = run ? resolveSubSup(run, node) : node;\n return stripUndefined({\n fontFace: run?.fontFamily ?? node.fontFamily ?? \"Noto Sans JP\",\n fontSize: asPt(pxToPt(fontSizePx)),\n color: gradientFill ? undefined : toColorInput(run?.color ?? node.color),\n gradientFill,\n bold: run?.bold ?? node.bold,\n italic: run?.italic ?? node.italic,\n underline: toUnderlineInput(resolveUnderline(node, run)),\n strike: run?.strike ?? node.strike,\n baseline: toBaselineInput(subSup.subscript, subSup.superscript),\n highlight: toColorInput(run?.highlight ?? node.highlight),\n glow: toGlowInput(node.glow),\n outline: toOutlineInput(node.outline),\n charSpacing: toCharSpacing(run?.letterSpacing ?? node.letterSpacing),\n });\n}\n\nfunction buildParagraphs(node: TextPositionedNode): AddTextBoxParagraphInput[] {\n const gradientFill = toTextGradientInput(node.textGradient);\n const sourceRuns: GlimpseTextRun[] =\n node.runs && node.runs.length > 0\n ? node.runs.map((run) => ({\n text: run.text,\n properties: buildRunProperties(node, run, gradientFill),\n href: run.href,\n }))\n : [\n {\n text: node.text ?? \"\",\n properties: buildRunProperties(node, undefined, gradientFill),\n },\n ];\n const paragraphRuns: GlimpseTextRun[][] = [[]];\n for (const run of sourceRuns) {\n const lines = run.text.replace(/\\r*\\n/g, \"\\n\").split(\"\\n\");\n lines.forEach((line, index) => {\n if (index > 0) paragraphRuns.push([]);\n paragraphRuns.at(-1)?.push({ ...run, text: line });\n });\n }\n const lineHeight = node.lineHeight ?? 1.3;\n const fontSizePx = node.fontSize ?? 24;\n return paragraphRuns.map((runs) => ({\n properties: {\n align: node.textAlign,\n marginLeft: asEmu(0),\n indent: asEmu(0),\n bullet: { type: \"none\" },\n lineSpacing: asHundredthPt(\n Math.round(pxToPt(fontSizePx * lineHeight) * 100),\n ),\n },\n runs: runs.map((run) => ({\n text: run.text,\n properties: run.properties,\n hyperlink: run.href,\n })),\n }));\n}\n\nexport type CustomGeometryXmlInput = {\n width: number;\n height: number;\n points: readonly ({ x: number; y: number } | { close: true })[];\n};\n\nexport type GlimpseShapeXmlOptions = {\n fillColor?: string;\n fillOpacity?: number;\n backgroundGradient?: string;\n outlineGradient?: string;\n outlineOpacity?: number;\n glow?: TextGlow;\n shadow?: ShadowStyle;\n rectRadius?: number;\n dashType?: BorderStyle[\"dashType\"];\n flipH?: boolean;\n flipV?: boolean;\n zeroWidth?: boolean;\n zeroHeight?: boolean;\n customGeometry?: CustomGeometryXmlInput;\n};\n\nfunction toGradientFill(\n value: string,\n opacity?: number,\n): AddShapeFillInput | undefined {\n const gradient = parseGradient(value);\n if (!gradient) return undefined;\n const stops = gradient.value.stops.map((stop) => ({\n position: asOoxmlPercent(Math.round(stop.position * 1000)),\n color: toColorInput(stop.color, opacity)!,\n }));\n if (gradient.kind === \"linear\") {\n const dmlAngle = (((gradient.value.angle - 90) % 360) + 360) % 360;\n return {\n kind: \"gradient\",\n gradientType: \"linear\",\n angle: asOoxmlAngle(Math.round(dmlAngle * 60000)),\n stops,\n };\n }\n return {\n kind: \"gradient\",\n gradientType: \"radial\",\n centerX: asOoxmlPercent(Math.round(gradient.value.centerX * 1000)),\n centerY: asOoxmlPercent(Math.round(gradient.value.centerY * 1000)),\n stops,\n };\n}\n\nfunction withFillOpacity(\n fill: AddShapeFillInput | undefined,\n opacity: number | undefined,\n): AddShapeFillInput | undefined {\n if (!fill || opacity === undefined || fill.kind !== \"solid\") return fill;\n return {\n ...fill,\n color: toColorInput(fill.color.hex, opacity)!,\n };\n}\n\nfunction toShadowEffects(\n shadow: ShadowStyle | undefined,\n): AddShapeEffectsInput | undefined {\n if (!shadow) return undefined;\n const common = {\n blurRadius: asEmu(Math.round((shadow.blur ?? 3) * 12700)),\n distance: asEmu(Math.round((shadow.offset ?? 23000 / 12700) * 12700)),\n direction: asOoxmlAngle(Math.round((shadow.angle ?? 90) * 60000)),\n color: toColorInput(shadow.color ?? \"000000\", shadow.opacity ?? 0.35)!,\n };\n return shadow.type === \"inner\"\n ? { innerShadow: common }\n : {\n outerShadow: {\n ...common,\n alignment: \"bl\",\n rotateWithShape: true,\n },\n };\n}\n\nfunction customGeometry(\n input: CustomGeometryXmlInput,\n): AddShapeGeometryInput | undefined {\n const commands: AddShapeCustomGeometryPathCommandInput[] = [];\n let moved = false;\n for (const point of input.points) {\n if (\"close\" in point) {\n if (moved) commands.push({ kind: \"close\" });\n continue;\n }\n commands.push({\n kind: moved ? \"lineTo\" : \"moveTo\",\n x: Math.round(point.x * EMU_PER_IN),\n y: Math.round(point.y * EMU_PER_IN),\n });\n moved = true;\n }\n if (!moved) return undefined;\n return {\n kind: \"custom\",\n paths: [\n {\n width: Math.round(input.width * EMU_PER_IN),\n height: Math.round(input.height * EMU_PER_IN),\n commands,\n },\n ],\n };\n}\n\nfunction enrichShapeInput(\n input: AddShapeInput,\n options: GlimpseShapeXmlOptions | undefined,\n): AddShapeInput {\n const preset = input.geometry.kind === \"preset\" ? input.geometry.preset : \"\";\n const geometry = options?.customGeometry\n ? (customGeometry(options.customGeometry) ?? input.geometry)\n : preset === \"roundRect\" && options?.rectRadius !== undefined\n ? {\n ...input.geometry,\n adjustValues: {\n adj: Math.round(options.rectRadius * 50000),\n },\n }\n : input.geometry;\n const gradientFill = options?.backgroundGradient\n ? toGradientFill(options.backgroundGradient, options.fillOpacity)\n : undefined;\n const outlineGradient = options?.outlineGradient\n ? toGradientFill(\n options.outlineGradient,\n options.outlineOpacity ?? options.fillOpacity,\n )\n : undefined;\n const outline: AddShapeOutlineInput | undefined = input.outline\n ? {\n ...input.outline,\n fill:\n outlineGradient ??\n withFillOpacity(input.outline.fill, options?.outlineOpacity),\n dash: options?.dashType ?? input.outline.dash,\n }\n : outlineGradient\n ? { fill: outlineGradient }\n : undefined;\n const glow = options?.glow\n ? {\n radius: asEmu(Math.round(pxToEmu(options.glow.size ?? 8))),\n color: toColorInput(\n options.glow.color ?? \"FFFFFF\",\n options.glow.opacity ?? 0.75,\n )!,\n }\n : input.effects?.glow;\n const effects: AddShapeEffectsInput = {\n ...input.effects,\n ...toShadowEffects(options?.shadow),\n ...(glow ? { glow } : {}),\n };\n const width =\n preset === \"line\" && options?.zeroWidth ? asEmu(0) : input.width;\n let height =\n preset === \"line\" && options?.zeroHeight ? asEmu(0) : input.height;\n if (preset === \"line\" && width === 0 && height === 0) height = asEmu(1);\n return {\n ...input,\n geometry,\n width,\n height,\n fill: gradientFill ?? withFillOpacity(input.fill, options?.fillOpacity),\n outline,\n effects: Object.keys(effects).length > 0 ? effects : undefined,\n flipHorizontal: options?.flipH ?? input.flipHorizontal,\n flipVertical: options?.flipV ?? input.flipVertical,\n };\n}\n\nfunction applyHyperlinks(\n input: AddTextBoxInput,\n hyperlinks: readonly (string | undefined)[] | undefined,\n): AddTextBoxInput {\n if (!hyperlinks?.some(Boolean) || !input.paragraphs) return input;\n let index = 0;\n return {\n ...input,\n paragraphs: input.paragraphs.map((paragraph) => ({\n ...paragraph,\n runs: paragraph.runs.map((run) => ({\n ...run,\n hyperlink: hyperlinks[index++],\n })),\n })),\n };\n}\n\nexport class PptxAuthoringRegistry {\n private currentSource: PptxSourceModel | undefined;\n private currentSlideHandle: SourceHandle | undefined;\n private useLayoutTextMargins = false;\n private textCount = 0;\n private shapeCount = 0;\n private pictureCount = 0;\n private tableCount = 0;\n private chartCount = 0;\n\n constructor() {\n const source = createPptx();\n this.currentSource = source;\n this.currentSlideHandle = source.slides[0]?.handle;\n }\n\n initialize(source: PptxSourceModel, useLayoutTextMargins = false): void {\n this.currentSource = source;\n this.useLayoutTextMargins = useLayoutTextMargins;\n }\n\n selectSlide(handle: SourceHandle): void {\n this.currentSlideHandle = handle;\n }\n\n get source(): PptxSourceModel {\n if (!this.currentSource)\n throw new Error(\"glimpse authoring is not initialized\");\n return this.currentSource;\n }\n\n get entries(): readonly { kind: \"shape\"; xml: string }[] {\n return (this.source.edits ?? []).flatMap((edit) =>\n (edit.kind === \"addTextBox\" || edit.kind === \"addShape\") && \"xml\" in edit\n ? [{ kind: \"shape\" as const, xml: edit.xml }]\n : [],\n );\n }\n\n replaceSource(source: PptxSourceModel): void {\n this.currentSource = source;\n }\n\n private get target(): SourceHandle {\n if (!this.currentSlideHandle)\n throw new Error(\"glimpse slide is not selected\");\n return this.currentSlideHandle;\n }\n\n register(node: TextPositionedNode): void {\n const textOptions = createTextOptions(node);\n this.registerTextBox({\n offsetX: asEmu(Math.round(textOptions.x * EMU_PER_IN)),\n offsetY: asEmu(Math.round(textOptions.y * EMU_PER_IN)),\n width: asEmu(Math.round(textOptions.w * EMU_PER_IN)),\n height: asEmu(Math.round(textOptions.h * EMU_PER_IN)),\n rotation:\n node.rotate === undefined\n ? undefined\n : asOoxmlAngle(Math.round(node.rotate * 60000)),\n body: this.useLayoutTextMargins\n ? undefined\n : {\n anchor: \"top\",\n marginLeft: asEmu(0),\n marginRight: asEmu(0),\n marginTop: asEmu(0),\n marginBottom: asEmu(0),\n },\n paragraphs: buildParagraphs(node),\n });\n }\n\n registerTextBox(\n input: AddTextBoxInput,\n options?: {\n name?: string;\n hyperlinks?: readonly (string | undefined)[];\n },\n ): void {\n const name = options?.name ?? `Text ${++this.textCount}`;\n this.currentSource = addTextBox(\n this.source,\n this.target,\n applyHyperlinks({ ...input, name }, options?.hyperlinks),\n );\n }\n\n registerShape(\n input: AddShapeInput,\n options?: GlimpseShapeXmlOptions & { name?: string },\n ): void {\n const name = options?.name ?? `Shape ${++this.shapeCount}`;\n this.currentSource = addShape(\n this.source,\n this.target,\n enrichShapeInput({ ...input, name }, options),\n );\n }\n\n registerPicture(\n input: AddPictureInput,\n options?: { name?: string; shadow?: ShadowStyle },\n ): void {\n const name = options?.name ?? `Picture ${++this.pictureCount}`;\n this.currentSource = addPicture(this.source, this.target, {\n ...input,\n name,\n effects: toShadowEffects(options?.shadow),\n });\n }\n\n registerTable(input: AddTableInput, options?: { name?: string }): void {\n const name = options?.name ?? `Table ${++this.tableCount}`;\n this.currentSource = addTable(this.source, this.target, { ...input, name });\n }\n\n registerChart(input: AddChartInput, options?: { name?: string }): void {\n const name = options?.name ?? `Chart ${++this.chartCount}`;\n this.currentSource = addChart(this.source, this.target, { ...input, name });\n }\n\n setSlideBackgroundGradient(\n backgroundGradient: string,\n opacity?: number,\n ): boolean {\n const fill = toGradientFill(backgroundGradient, opacity);\n if (!fill || fill.kind !== \"gradient\") return false;\n const background =\n fill.gradientType === \"linear\"\n ? {\n kind: \"gradient\" as const,\n gradientType: \"linear\" as const,\n stops: fill.stops,\n angle: fill.angle ?? asOoxmlAngle(0),\n }\n : {\n kind: \"gradient\" as const,\n gradientType: \"radial\" as const,\n stops: fill.stops,\n centerX: fill.centerX,\n centerY: fill.centerY,\n };\n this.currentSource = setSlideBackground(\n this.source,\n this.target,\n background,\n );\n return true;\n }\n\n setSlideBackgroundSolid(color: string): void {\n this.currentSource = setSlideBackground(this.source, this.target, {\n kind: \"solid\",\n color: toColorInput(color)!,\n });\n }\n\n setSlideBackgroundImage(bytes: Uint8Array): void {\n this.currentSource = setSlideBackground(this.source, this.target, {\n kind: \"image\",\n bytes,\n });\n }\n}\n"],"mappings":"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"}
@@ -1,17 +1,16 @@
1
1
  import { getImageData } from "../shared/measureImage.js";
2
2
  import { resolveBoxSpacing } from "../shared/boxSpacing.js";
3
3
  import { getNodeDef } from "../registry/nodeRegistry.js";
4
- import { pxToIn, pxToPt } from "./units.js";
5
- import { convertStrike, convertUnderline } from "./textOptions.js";
6
- import { registerBackgroundGradient } from "./gradientFills.js";
4
+ import { pxToEmu, pxToPt } from "./units.js";
5
+ import { toColorInput } from "./pptxAuthoring.js";
6
+ import { createGlimpseRunProperties } from "./utils/glimpseTextBox.js";
7
+ import { imageBytesFromSource } from "./utils/glimpsePicture.js";
8
+ import { createShapeBoundsInput, noneShapeFill, shapeOutline, solidShapeFill } from "./utils/glimpseShape.js";
7
9
  import "../registry/index.js";
8
10
  import { renderBackgroundAndBorder, renderBorderOnly } from "./utils/backgroundBorder.js";
11
+ import { createWritablePptx } from "./writablePptx.js";
12
+ import { addEmptySlideFromLayout, addSlideNumber, asEmu, asPt, createPptx } from "@pptx-glimpse/document";
9
13
  //#region src/renderPptx/renderPptx.ts
10
- async function loadPptxGenJS() {
11
- const mod = await import("pptxgenjs");
12
- return mod.default?.default ?? mod.default ?? mod;
13
- }
14
- const DEFAULT_MASTER_NAME = "POM_MASTER";
15
14
  function buildIdPositionMap(node, diagnostics) {
16
15
  const map = /* @__PURE__ */ new Map();
17
16
  function traverse(n) {
@@ -35,130 +34,163 @@ function sortByZIndex(children) {
35
34
  if (children.every((c) => c.zIndex === void 0)) return children;
36
35
  return [...children].sort((a, b) => (a.zIndex ?? 0) - (b.zIndex ?? 0));
37
36
  }
38
- /**
39
- * MasterObject pptxgenjs の objects 形式に変換する
40
- */
41
- function convertMasterObject(obj) {
42
- switch (obj.type) {
43
- case "text": return { text: {
44
- text: obj.text,
45
- options: {
46
- x: pxToIn(obj.x),
47
- y: pxToIn(obj.y),
48
- w: pxToIn(obj.w),
49
- h: pxToIn(obj.h),
50
- fontSize: obj.fontSize ? pxToPt(obj.fontSize) : void 0,
51
- fontFace: obj.fontFamily,
52
- color: obj.color,
53
- bold: obj.bold,
54
- italic: obj.italic,
55
- underline: convertUnderline(obj.underline),
56
- strike: convertStrike(obj.strike),
57
- highlight: obj.highlight,
58
- align: obj.textAlign
59
- }
60
- } };
61
- case "image": {
62
- const imageProps = {
63
- x: pxToIn(obj.x),
64
- y: pxToIn(obj.y),
65
- w: pxToIn(obj.w),
66
- h: pxToIn(obj.h)
67
- };
68
- if (obj.src.startsWith("data:")) imageProps.data = obj.src;
69
- else imageProps.path = obj.src;
70
- return { image: imageProps };
71
- }
72
- case "rect": return { rect: {
73
- x: pxToIn(obj.x),
74
- y: pxToIn(obj.y),
75
- w: pxToIn(obj.w),
76
- h: pxToIn(obj.h),
77
- fill: obj.fill ? {
78
- color: obj.fill.color,
79
- transparency: obj.fill.transparency
80
- } : void 0,
81
- line: obj.border ? {
82
- color: obj.border.color,
83
- width: obj.border.width,
84
- dashType: obj.border.dashType
85
- } : void 0
86
- } };
87
- case "line": return { line: {
88
- x: pxToIn(obj.x),
89
- y: pxToIn(obj.y),
90
- w: pxToIn(obj.w),
91
- h: pxToIn(obj.h),
92
- line: obj.line ? {
93
- color: obj.line.color,
94
- width: obj.line.width,
95
- dashType: obj.line.dashType
96
- } : {
97
- color: "000000",
98
- width: 1
99
- }
100
- } };
101
- }
37
+ function masterBackground(master, buildContext) {
38
+ const background = master?.background;
39
+ if (!background) return void 0;
40
+ if ("color" in background) return {
41
+ kind: "solid",
42
+ color: toColorInput(background.color)
43
+ };
44
+ if ("data" in background) return {
45
+ kind: "image",
46
+ bytes: imageBytesFromSource("", background.data)
47
+ };
48
+ const src = "path" in background ? background.path : background.image;
49
+ return {
50
+ kind: "image",
51
+ bytes: imageBytesFromSource(src, getImageData(src, buildContext.imageDataCache))
52
+ };
102
53
  }
103
- /**
104
- * SlideMasterOptions から pptxgenjs の defineSlideMaster を呼び出す
105
- */
106
- function defineSlideMasterFromOptions(pptx, master) {
107
- const masterName = master.title || DEFAULT_MASTER_NAME;
108
- const masterProps = { title: masterName };
109
- if (master.background) {
110
- if ("color" in master.background) masterProps.background = { color: master.background.color };
111
- else if ("path" in master.background) masterProps.background = { path: master.background.path };
112
- else if ("data" in master.background) masterProps.background = { data: master.background.data };
113
- else if ("image" in master.background) masterProps.background = { path: master.background.image };
54
+ function masterBounds(obj) {
55
+ return createShapeBoundsInput(obj);
56
+ }
57
+ function masterTextRunProperties(obj) {
58
+ const properties = createGlimpseRunProperties({
59
+ fontSize: obj.fontSize,
60
+ fontFace: obj.fontFamily,
61
+ color: obj.color,
62
+ bold: obj.bold,
63
+ italic: obj.italic,
64
+ underline: obj.underline,
65
+ strike: obj.strike,
66
+ highlight: obj.highlight
67
+ });
68
+ return {
69
+ ...properties,
70
+ fontFace: obj.fontFamily === void 0 ? void 0 : properties.fontFace,
71
+ fontSize: obj.fontSize === void 0 ? void 0 : properties.fontSize
72
+ };
73
+ }
74
+ function addMasterContent(buildContext, master) {
75
+ const target = buildContext.pptxAuthoring.source.slideMasters[0]?.handle;
76
+ if (!target) throw new Error("createPptx did not create a slide master handle");
77
+ buildContext.pptxAuthoring.selectSlide(target);
78
+ for (const obj of master.objects ?? []) switch (obj.type) {
79
+ case "text":
80
+ buildContext.pptxAuthoring.registerTextBox({
81
+ ...masterBounds(obj),
82
+ body: {
83
+ anchor: "middle",
84
+ marginLeft: asEmu(91440),
85
+ marginRight: asEmu(91440),
86
+ marginTop: asEmu(91440),
87
+ marginBottom: asEmu(91440)
88
+ },
89
+ paragraphs: [{
90
+ properties: {
91
+ align: obj.textAlign,
92
+ marginLeft: asEmu(0),
93
+ indent: asEmu(0),
94
+ bullet: { type: "none" }
95
+ },
96
+ runs: [{
97
+ text: obj.text,
98
+ properties: masterTextRunProperties(obj)
99
+ }]
100
+ }]
101
+ });
102
+ break;
103
+ case "image":
104
+ buildContext.pptxAuthoring.registerPicture({
105
+ ...masterBounds(obj),
106
+ bytes: imageBytesFromSource(obj.src, getImageData(obj.src, buildContext.imageDataCache))
107
+ });
108
+ break;
109
+ case "rect":
110
+ buildContext.pptxAuthoring.registerShape({
111
+ ...masterBounds(obj),
112
+ geometry: {
113
+ kind: "preset",
114
+ preset: "rect"
115
+ },
116
+ fill: obj.fill ? solidShapeFill(obj.fill.color ?? "FFFFFF") : noneShapeFill(),
117
+ outline: obj.border ? shapeOutline(obj.border) : void 0
118
+ }, {
119
+ fillColor: obj.fill?.color,
120
+ fillOpacity: obj.fill?.transparency === void 0 ? void 0 : 1 - obj.fill.transparency / 100
121
+ });
122
+ break;
123
+ case "line":
124
+ buildContext.pptxAuthoring.registerShape({
125
+ ...masterBounds(obj),
126
+ geometry: {
127
+ kind: "preset",
128
+ preset: "line"
129
+ },
130
+ fill: noneShapeFill(),
131
+ outline: shapeOutline(obj.line ?? {
132
+ color: "000000",
133
+ width: 1
134
+ })
135
+ });
136
+ break;
114
137
  }
115
- if (master.margin !== void 0) {
116
- const margin = resolveBoxSpacing(master.margin);
117
- masterProps.margin = [
118
- pxToIn(margin.top),
119
- pxToIn(margin.right),
120
- pxToIn(margin.bottom),
121
- pxToIn(margin.left)
122
- ];
138
+ if (master.slideNumber) {
139
+ const value = master.slideNumber;
140
+ buildContext.pptxAuthoring.replaceSource(addSlideNumber(buildContext.pptxAuthoring.source, target, {
141
+ offsetX: asEmu(Math.round(pxToEmu(value.x))),
142
+ offsetY: asEmu(Math.round(pxToEmu(value.y))),
143
+ width: value.w === void 0 ? asEmu(8e5) : asEmu(Math.round(pxToEmu(value.w))),
144
+ height: value.h === void 0 ? asEmu(3e5) : asEmu(Math.round(pxToEmu(value.h))),
145
+ properties: {
146
+ fontFace: value.fontFamily,
147
+ fontSize: value.fontSize ? asPt(pxToPt(value.fontSize)) : void 0,
148
+ color: toColorInput(value.color)
149
+ }
150
+ }));
123
151
  }
124
- if (master.objects && master.objects.length > 0) masterProps.objects = master.objects.map((obj) => convertMasterObject(obj));
125
- if (master.slideNumber) masterProps.slideNumber = {
126
- x: pxToIn(master.slideNumber.x),
127
- y: pxToIn(master.slideNumber.y),
128
- w: master.slideNumber.w ? pxToIn(master.slideNumber.w) : void 0,
129
- h: master.slideNumber.h ? pxToIn(master.slideNumber.h) : void 0,
130
- fontSize: master.slideNumber.fontSize ? pxToPt(master.slideNumber.fontSize) : void 0,
131
- fontFace: master.slideNumber.fontFamily,
132
- color: master.slideNumber.color
133
- };
134
- pptx.defineSlideMaster(masterProps);
135
- return masterName;
136
152
  }
137
153
  /**
138
- * PositionedNode ツリーを PptxGenJS スライドに変換する
154
+ * PositionedNode ツリーを glimpse の PPTX source model に変換する
139
155
  * @param pages PositionedNode ツリーの配列(各要素が1ページ)
140
156
  * @param slidePx スライド全体のサイズ(px)
141
157
  * @param master スライドマスターオプション(省略可能)
142
- * @returns PptxGenJS インスタンス
158
+ * @returns PPTX の write / writeFile / stream 互換 facade
143
159
  */
144
- async function renderPptx(pages, slidePx, buildContext, master) {
145
- const slideIn = {
146
- w: pxToIn(slidePx.w),
147
- h: pxToIn(slidePx.h)
148
- };
149
- const pptx = new (await (loadPptxGenJS()))();
150
- pptx.defineLayout({
151
- name: "custom",
152
- width: slideIn.w,
153
- height: slideIn.h
160
+ function renderPptx(pages, slidePx, buildContext, master) {
161
+ const margin = master?.margin === void 0 ? void 0 : resolveBoxSpacing(master.margin);
162
+ let source = createPptx({
163
+ slideSize: {
164
+ width: asEmu(Math.round(pxToEmu(slidePx.w))),
165
+ height: asEmu(Math.round(pxToEmu(slidePx.h)))
166
+ },
167
+ slideMaster: {
168
+ name: master?.title ?? "POM_MASTER",
169
+ background: masterBackground(master, buildContext)
170
+ },
171
+ slideLayout: {
172
+ name: "POM_LAYOUT",
173
+ margin: margin ? {
174
+ top: asEmu(Math.round(pxToEmu(margin.top))),
175
+ right: asEmu(Math.round(pxToEmu(margin.right))),
176
+ bottom: asEmu(Math.round(pxToEmu(margin.bottom))),
177
+ left: asEmu(Math.round(pxToEmu(margin.left)))
178
+ } : void 0
179
+ }
154
180
  });
155
- pptx.layout = "custom";
156
- const masterName = master ? defineSlideMasterFromOptions(pptx, master) : void 0;
157
- for (const data of pages) {
158
- const slide = masterName ? pptx.addSlide({ masterName }) : pptx.addSlide();
181
+ buildContext.pptxAuthoring.initialize(source, margin !== void 0);
182
+ if (master) addMasterContent(buildContext, master);
183
+ for (const [pageIndex, data] of pages.entries()) {
184
+ if (pageIndex > 0) {
185
+ const layoutPartPath = buildContext.pptxAuthoring.source.slideLayouts[0]?.partPath;
186
+ if (!layoutPartPath) throw new Error("createPptx did not create a slide layout");
187
+ source = addEmptySlideFromLayout(buildContext.pptxAuthoring.source, { layoutPartPath });
188
+ buildContext.pptxAuthoring.replaceSource(source);
189
+ }
190
+ const slideHandle = buildContext.pptxAuthoring.source.slides[pageIndex]?.handle;
191
+ if (!slideHandle) throw new Error(`slide handle was not found: ${pageIndex + 1}`);
192
+ buildContext.pptxAuthoring.selectSlide(slideHandle);
159
193
  const ctx = {
160
- slide,
161
- pptx,
162
194
  buildContext,
163
195
  idPositionMap: buildIdPositionMap(data, buildContext.diagnostics)
164
196
  };
@@ -166,21 +198,19 @@ async function renderPptx(pages, slidePx, buildContext, master) {
166
198
  const rootBackgroundColor = !isLinelike ? data.backgroundColor : void 0;
167
199
  const rootBackgroundGradient = !isLinelike ? data.backgroundGradient : void 0;
168
200
  const rootHasOpacity = !isLinelike && "opacity" in data && data.opacity !== void 0;
169
- const rootGradientMarker = rootBackgroundGradient && !rootHasOpacity ? registerBackgroundGradient(rootBackgroundGradient, void 0, buildContext.gradientFills) : void 0;
170
- if (rootGradientMarker) slide.background = { color: rootGradientMarker };
171
- else if (rootBackgroundColor && !rootHasOpacity) slide.background = { color: rootBackgroundColor };
201
+ const rootGradientApplied = rootBackgroundGradient && !rootHasOpacity ? buildContext.pptxAuthoring.setSlideBackgroundGradient(rootBackgroundGradient) : false;
202
+ if (!rootGradientApplied && rootBackgroundColor && !rootBackgroundGradient && !rootHasOpacity) buildContext.pptxAuthoring.setSlideBackgroundSolid(rootBackgroundColor);
172
203
  const rootBackgroundImage = !isLinelike ? data.backgroundImage : void 0;
173
204
  if (rootBackgroundImage) {
174
205
  const cachedData = getImageData(rootBackgroundImage.src, buildContext.imageDataCache);
175
- if (cachedData) slide.background = { data: cachedData };
176
- else slide.background = { path: rootBackgroundImage.src };
206
+ buildContext.pptxAuthoring.setSlideBackgroundImage(imageBytesFromSource(rootBackgroundImage.src, cachedData));
177
207
  }
178
208
  /**
179
209
  * node をスライドにレンダリングする
180
210
  * @param isRoot ルートノードかどうか(ルートノードの background は slide.background で処理済み)
181
211
  */
182
212
  function renderNode(node, isRoot = false) {
183
- if (node.type !== "line" && node.type !== "arrow") if (isRoot && (rootBackgroundImage || (rootBackgroundColor || rootBackgroundGradient) && !rootHasOpacity)) renderBorderOnly(node, ctx);
213
+ if (node.type !== "line" && node.type !== "arrow") if (isRoot && (rootBackgroundImage || (rootBackgroundColor || rootGradientApplied) && !rootHasOpacity)) renderBorderOnly(node, ctx);
184
214
  else renderBackgroundAndBorder(node, ctx);
185
215
  const def = getNodeDef(node.type);
186
216
  switch (def.category) {
@@ -198,7 +228,7 @@ async function renderPptx(pages, slidePx, buildContext, master) {
198
228
  }
199
229
  renderNode(data, true);
200
230
  }
201
- return pptx;
231
+ return createWritablePptx(() => buildContext.pptxAuthoring.source);
202
232
  }
203
233
  //#endregion
204
234
  export { renderPptx };
@@ -1 +1 @@
1
- {"version":3,"file":"renderPptx.js","names":[],"sources":["../../src/renderPptx/renderPptx.ts"],"sourcesContent":["// pptxgenjs の型定義\ntype PptxGenJSInstance = import(\"pptxgenjs\").default;\n\n// pptxgenjs は CJS パッケージのため動的 import で読み込む\nasync function loadPptxGenJS(): Promise<new () => PptxGenJSInstance> {\n const pptxModule = await import(\"pptxgenjs\");\n // CJS default export の解決: module.default.default (ESM wrapper) または module.default\n /* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return */\n const mod = pptxModule as any;\n return mod.default?.default ?? mod.default ?? mod;\n /* eslint-enable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return */\n}\ntype SlideMasterProps = Parameters<PptxGenJSInstance[\"defineSlideMaster\"]>[0];\ntype ImageProps = {\n x: number;\n y: number;\n w: number;\n h: number;\n path?: string;\n data?: string;\n};\nimport type {\n PositionedNode,\n SlideMasterOptions,\n MasterObject,\n} from \"../types.ts\";\nimport type { BuildContext } from \"../buildContext.ts\";\nimport type { RenderContext, NodeBounds } from \"./types.ts\";\nimport { pxToIn, pxToPt } from \"./units.ts\";\nimport { convertUnderline, convertStrike } from \"./textOptions.ts\";\nimport { getImageData } from \"../shared/measureImage.ts\";\nimport { resolveBoxSpacing } from \"../shared/boxSpacing.ts\";\nimport {\n renderBackgroundAndBorder,\n renderBorderOnly,\n} from \"./utils/backgroundBorder.ts\";\nimport { registerBackgroundGradient } from \"./gradientFills.ts\";\nimport { getNodeDef } from \"../registry/index.ts\";\n\ntype SlidePx = { w: number; h: number };\n\nconst DEFAULT_MASTER_NAME = \"POM_MASTER\";\n\nfunction buildIdPositionMap(\n node: PositionedNode,\n diagnostics: import(\"../buildContext.ts\").BuildContext[\"diagnostics\"],\n): Map<string, NodeBounds> {\n const map = new Map<string, NodeBounds>();\n\n function traverse(n: PositionedNode) {\n if (n.id) {\n if (map.has(n.id)) {\n diagnostics.add(\n \"DUPLICATE_NODE_ID\",\n `Duplicate node id \"${n.id}\" — only the first occurrence will be used for Arrow references`,\n );\n } else {\n map.set(n.id, { x: n.x, y: n.y, w: n.w, h: n.h });\n }\n }\n if (n.type === \"vstack\" || n.type === \"hstack\" || n.type === \"layer\") {\n for (const child of n.children) {\n traverse(child);\n }\n }\n }\n\n traverse(node);\n return map;\n}\n\n/**\n * zIndex でソートして描画順を制御する(安定ソート)\n * zIndex が小さいノードが先に描画される(PowerPoint は追加順に重ねるため)\n */\nfunction sortByZIndex<T extends { zIndex?: number }>(children: T[]): T[] {\n // すべての子要素に zIndex が未設定の場合はそのまま返す\n if (children.every((c) => c.zIndex === undefined)) return children;\n return [...children].sort((a, b) => (a.zIndex ?? 0) - (b.zIndex ?? 0));\n}\n\n/**\n * MasterObject を pptxgenjs の objects 形式に変換する\n */\nfunction convertMasterObject(\n obj: MasterObject,\n): SlideMasterProps[\"objects\"] extends (infer T)[] | undefined ? T : never {\n switch (obj.type) {\n case \"text\":\n return {\n text: {\n text: obj.text,\n options: {\n x: pxToIn(obj.x),\n y: pxToIn(obj.y),\n w: pxToIn(obj.w),\n h: pxToIn(obj.h),\n fontSize: obj.fontSize ? pxToPt(obj.fontSize) : undefined,\n fontFace: obj.fontFamily,\n color: obj.color,\n bold: obj.bold,\n italic: obj.italic,\n underline: convertUnderline(obj.underline),\n strike: convertStrike(obj.strike),\n highlight: obj.highlight,\n align: obj.textAlign,\n },\n },\n };\n case \"image\": {\n const imageProps: ImageProps = {\n x: pxToIn(obj.x),\n y: pxToIn(obj.y),\n w: pxToIn(obj.w),\n h: pxToIn(obj.h),\n };\n // src が data URI かパスかを判定\n if (obj.src.startsWith(\"data:\")) {\n imageProps.data = obj.src;\n } else {\n imageProps.path = obj.src;\n }\n return { image: imageProps };\n }\n case \"rect\":\n return {\n rect: {\n x: pxToIn(obj.x),\n y: pxToIn(obj.y),\n w: pxToIn(obj.w),\n h: pxToIn(obj.h),\n fill: obj.fill\n ? { color: obj.fill.color, transparency: obj.fill.transparency }\n : undefined,\n line: obj.border\n ? {\n color: obj.border.color,\n width: obj.border.width,\n dashType: obj.border.dashType,\n }\n : undefined,\n },\n };\n case \"line\":\n return {\n line: {\n x: pxToIn(obj.x),\n y: pxToIn(obj.y),\n w: pxToIn(obj.w),\n h: pxToIn(obj.h),\n line: obj.line\n ? {\n color: obj.line.color,\n width: obj.line.width,\n dashType: obj.line.dashType,\n }\n : { color: \"000000\", width: 1 },\n },\n };\n }\n}\n\n/**\n * SlideMasterOptions から pptxgenjs の defineSlideMaster を呼び出す\n */\nfunction defineSlideMasterFromOptions(\n pptx: PptxGenJSInstance,\n master: SlideMasterOptions,\n): string {\n const masterName = master.title || DEFAULT_MASTER_NAME;\n\n const masterProps: SlideMasterProps = {\n title: masterName,\n };\n\n // background の変換\n if (master.background) {\n if (\"color\" in master.background) {\n masterProps.background = { color: master.background.color };\n } else if (\"path\" in master.background) {\n masterProps.background = { path: master.background.path };\n } else if (\"data\" in master.background) {\n masterProps.background = { data: master.background.data };\n } else if (\"image\" in master.background) {\n masterProps.background = { path: master.background.image };\n }\n }\n\n // margin の変換 (px -> inches)\n if (master.margin !== undefined) {\n const margin = resolveBoxSpacing(master.margin);\n masterProps.margin = [\n pxToIn(margin.top),\n pxToIn(margin.right),\n pxToIn(margin.bottom),\n pxToIn(margin.left),\n ];\n }\n\n // objects の変換\n if (master.objects && master.objects.length > 0) {\n masterProps.objects = master.objects.map((obj) => convertMasterObject(obj));\n }\n\n // slideNumber の変換\n if (master.slideNumber) {\n masterProps.slideNumber = {\n x: pxToIn(master.slideNumber.x),\n y: pxToIn(master.slideNumber.y),\n w: master.slideNumber.w ? pxToIn(master.slideNumber.w) : undefined,\n h: master.slideNumber.h ? pxToIn(master.slideNumber.h) : undefined,\n fontSize: master.slideNumber.fontSize\n ? pxToPt(master.slideNumber.fontSize)\n : undefined,\n fontFace: master.slideNumber.fontFamily,\n color: master.slideNumber.color,\n };\n }\n\n pptx.defineSlideMaster(masterProps);\n return masterName;\n}\n\n/**\n * PositionedNode ツリーを PptxGenJS スライドに変換する\n * @param pages PositionedNode ツリーの配列(各要素が1ページ)\n * @param slidePx スライド全体のサイズ(px)\n * @param master スライドマスターオプション(省略可能)\n * @returns PptxGenJS インスタンス\n */\nexport async function renderPptx(\n pages: PositionedNode[],\n slidePx: SlidePx,\n buildContext: BuildContext,\n master?: SlideMasterOptions,\n) {\n const slideIn = { w: pxToIn(slidePx.w), h: pxToIn(slidePx.h) }; // layout(=px) → PptxGenJS(=inch) への最終変換\n\n const PptxGenJS = await loadPptxGenJS();\n const pptx = new PptxGenJS();\n\n pptx.defineLayout({ name: \"custom\", width: slideIn.w, height: slideIn.h });\n pptx.layout = \"custom\";\n\n // マスターが指定されている場合、defineSlideMaster を呼び出す\n const masterName = master\n ? defineSlideMasterFromOptions(pptx, master)\n : undefined;\n\n for (const data of pages) {\n // マスターが指定されている場合は masterName を使用\n const slide = masterName ? pptx.addSlide({ masterName }) : pptx.addSlide();\n const idPositionMap = buildIdPositionMap(data, buildContext.diagnostics);\n const ctx: RenderContext = { slide, pptx, buildContext, idPositionMap };\n\n // ルートノードの backgroundColor はスライドの background プロパティとして適用\n // これにより、マスタースライドのオブジェクトを覆い隠さない\n // line/arrow ノードは backgroundColor を持たないためスキップ\n // ただし opacity が指定されている場合は slide.background では透過を表現できないため、\n // renderBackgroundAndBorder で描画する\n const isLinelike = data.type === \"line\" || data.type === \"arrow\";\n const rootBackgroundColor = !isLinelike ? data.backgroundColor : undefined;\n const rootBackgroundGradient = !isLinelike\n ? data.backgroundGradient\n : undefined;\n const rootHasOpacity =\n !isLinelike && \"opacity\" in data && data.opacity !== undefined;\n // backgroundGradient はマーカー色で slide.background に適用し、\n // 出力時の後処理で gradFill に置換される (gradientFills.ts 参照)\n const rootGradientMarker =\n rootBackgroundGradient && !rootHasOpacity\n ? registerBackgroundGradient(\n rootBackgroundGradient,\n undefined,\n buildContext.gradientFills,\n )\n : undefined;\n if (rootGradientMarker) {\n slide.background = { color: rootGradientMarker };\n } else if (rootBackgroundColor && !rootHasOpacity) {\n slide.background = { color: rootBackgroundColor };\n }\n\n // ルートノードの backgroundImage はスライドの background プロパティとして適用\n // backgroundColor と backgroundImage の両方がある場合、backgroundImage が優先\n const rootBackgroundImage = !isLinelike ? data.backgroundImage : undefined;\n if (rootBackgroundImage) {\n const cachedData = getImageData(\n rootBackgroundImage.src,\n buildContext.imageDataCache,\n );\n if (cachedData) {\n slide.background = { data: cachedData };\n } else {\n slide.background = { path: rootBackgroundImage.src };\n }\n }\n\n /**\n * node をスライドにレンダリングする\n * @param isRoot ルートノードかどうか(ルートノードの background は slide.background で処理済み)\n */\n function renderNode(node: PositionedNode, isRoot = false) {\n // line/arrow ノードは backgroundColor/border を持たないため、background/border の描画をスキップ\n if (node.type !== \"line\" && node.type !== \"arrow\") {\n // ルートノードの backgroundColor/backgroundImage は既に slide.background に適用済みなのでスキップ\n // ただし opacity がある場合は slide.background では透過を表現できないため通常描画\n if (\n isRoot &&\n (rootBackgroundImage ||\n ((rootBackgroundColor || rootBackgroundGradient) &&\n !rootHasOpacity))\n ) {\n // border のみ描画(backgroundColor/backgroundImage はスキップ)\n renderBorderOnly(node, ctx);\n } else {\n renderBackgroundAndBorder(node, ctx);\n }\n }\n\n const def = getNodeDef(node.type);\n\n switch (def.category) {\n case \"leaf\":\n if (!def.render) {\n throw new Error(\n `No render function registered for leaf node: ${node.type}`,\n );\n }\n def.render(node, ctx);\n break;\n\n case \"multi-child\":\n case \"absolute-child\": {\n const containerNode = node as Extract<\n PositionedNode,\n { type: \"vstack\" | \"hstack\" | \"layer\" }\n >;\n // zIndex でソートして描画順を制御(値が小さいものが先に描画される)\n for (const child of sortByZIndex(containerNode.children)) {\n renderNode(child);\n }\n break;\n }\n }\n }\n\n renderNode(data, true); // ルートノードとして処理\n }\n\n return pptx;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"renderPptx.js","names":[],"sources":["../../src/renderPptx/renderPptx.ts"],"sourcesContent":["import {\n addEmptySlideFromLayout,\n addSlideNumber,\n asEmu,\n asPt,\n createPptx,\n type AddTextBoxRunPropertiesInput,\n type CreatePptxBackground,\n} from \"@pptx-glimpse/document\";\nimport type { PositionedNode, SlideMasterOptions } from \"../types.ts\";\nimport type { BuildContext } from \"../buildContext.ts\";\nimport type { RenderContext, NodeBounds } from \"./types.ts\";\nimport { pxToEmu, pxToPt } from \"./units.ts\";\nimport { getImageData } from \"../shared/measureImage.ts\";\nimport { resolveBoxSpacing } from \"../shared/boxSpacing.ts\";\nimport {\n renderBackgroundAndBorder,\n renderBorderOnly,\n} from \"./utils/backgroundBorder.ts\";\nimport { getNodeDef } from \"../registry/index.ts\";\nimport { toColorInput } from \"./pptxAuthoring.ts\";\nimport { createGlimpseRunProperties } from \"./utils/glimpseTextBox.ts\";\nimport {\n createShapeBoundsInput,\n noneShapeFill,\n shapeOutline,\n solidShapeFill,\n} from \"./utils/glimpseShape.ts\";\nimport { imageBytesFromSource } from \"./utils/glimpsePicture.ts\";\nimport { createWritablePptx } from \"./writablePptx.ts\";\n\ntype SlidePx = { w: number; h: number };\n\nfunction buildIdPositionMap(\n node: PositionedNode,\n diagnostics: import(\"../buildContext.ts\").BuildContext[\"diagnostics\"],\n): Map<string, NodeBounds> {\n const map = new Map<string, NodeBounds>();\n\n function traverse(n: PositionedNode) {\n if (n.id) {\n if (map.has(n.id)) {\n diagnostics.add(\n \"DUPLICATE_NODE_ID\",\n `Duplicate node id \"${n.id}\" — only the first occurrence will be used for Arrow references`,\n );\n } else {\n map.set(n.id, { x: n.x, y: n.y, w: n.w, h: n.h });\n }\n }\n if (n.type === \"vstack\" || n.type === \"hstack\" || n.type === \"layer\") {\n for (const child of n.children) {\n traverse(child);\n }\n }\n }\n\n traverse(node);\n return map;\n}\n\n/**\n * zIndex でソートして描画順を制御する(安定ソート)\n * zIndex が小さいノードが先に描画される(PowerPoint は追加順に重ねるため)\n */\nfunction sortByZIndex<T extends { zIndex?: number }>(children: T[]): T[] {\n // すべての子要素に zIndex が未設定の場合はそのまま返す\n if (children.every((c) => c.zIndex === undefined)) return children;\n return [...children].sort((a, b) => (a.zIndex ?? 0) - (b.zIndex ?? 0));\n}\n\nfunction masterBackground(\n master: SlideMasterOptions | undefined,\n buildContext: BuildContext,\n): CreatePptxBackground | undefined {\n const background = master?.background;\n if (!background) return undefined;\n if (\"color\" in background) {\n return { kind: \"solid\", color: toColorInput(background.color)! };\n }\n if (\"data\" in background) {\n return { kind: \"image\", bytes: imageBytesFromSource(\"\", background.data) };\n }\n const src = \"path\" in background ? background.path : background.image;\n return {\n kind: \"image\",\n bytes: imageBytesFromSource(\n src,\n getImageData(src, buildContext.imageDataCache),\n ),\n };\n}\n\nfunction masterBounds(obj: { x: number; y: number; w: number; h: number }) {\n return createShapeBoundsInput(obj);\n}\n\nfunction masterTextRunProperties(\n obj: Extract<\n NonNullable<SlideMasterOptions[\"objects\"]>[number],\n { type: \"text\" }\n >,\n): AddTextBoxRunPropertiesInput {\n const properties = createGlimpseRunProperties({\n fontSize: obj.fontSize,\n fontFace: obj.fontFamily,\n color: obj.color,\n bold: obj.bold,\n italic: obj.italic,\n underline: obj.underline,\n strike: obj.strike,\n highlight: obj.highlight,\n });\n return {\n ...properties,\n fontFace: obj.fontFamily === undefined ? undefined : properties.fontFace,\n fontSize: obj.fontSize === undefined ? undefined : properties.fontSize,\n };\n}\n\nfunction addMasterContent(\n buildContext: BuildContext,\n master: SlideMasterOptions,\n): void {\n const target = buildContext.pptxAuthoring.source.slideMasters[0]?.handle;\n if (!target)\n throw new Error(\"createPptx did not create a slide master handle\");\n buildContext.pptxAuthoring.selectSlide(target);\n for (const obj of master.objects ?? []) {\n switch (obj.type) {\n case \"text\":\n buildContext.pptxAuthoring.registerTextBox({\n ...masterBounds(obj),\n body: {\n anchor: \"middle\",\n marginLeft: asEmu(91440),\n marginRight: asEmu(91440),\n marginTop: asEmu(91440),\n marginBottom: asEmu(91440),\n },\n paragraphs: [\n {\n properties: {\n align: obj.textAlign,\n marginLeft: asEmu(0),\n indent: asEmu(0),\n bullet: { type: \"none\" },\n },\n runs: [\n {\n text: obj.text,\n properties: masterTextRunProperties(obj),\n },\n ],\n },\n ],\n });\n break;\n case \"image\":\n buildContext.pptxAuthoring.registerPicture({\n ...masterBounds(obj),\n bytes: imageBytesFromSource(\n obj.src,\n getImageData(obj.src, buildContext.imageDataCache),\n ),\n });\n break;\n case \"rect\":\n buildContext.pptxAuthoring.registerShape(\n {\n ...masterBounds(obj),\n geometry: { kind: \"preset\", preset: \"rect\" },\n fill: obj.fill\n ? solidShapeFill(obj.fill.color ?? \"FFFFFF\")\n : noneShapeFill(),\n outline: obj.border ? shapeOutline(obj.border) : undefined,\n },\n {\n fillColor: obj.fill?.color,\n fillOpacity:\n obj.fill?.transparency === undefined\n ? undefined\n : 1 - obj.fill.transparency / 100,\n },\n );\n break;\n case \"line\":\n buildContext.pptxAuthoring.registerShape({\n ...masterBounds(obj),\n geometry: { kind: \"preset\", preset: \"line\" },\n fill: noneShapeFill(),\n outline: shapeOutline(obj.line ?? { color: \"000000\", width: 1 }),\n });\n break;\n }\n }\n if (master.slideNumber) {\n const value = master.slideNumber;\n buildContext.pptxAuthoring.replaceSource(\n addSlideNumber(buildContext.pptxAuthoring.source, target, {\n offsetX: asEmu(Math.round(pxToEmu(value.x))),\n offsetY: asEmu(Math.round(pxToEmu(value.y))),\n width:\n value.w === undefined\n ? asEmu(800000)\n : asEmu(Math.round(pxToEmu(value.w))),\n height:\n value.h === undefined\n ? asEmu(300000)\n : asEmu(Math.round(pxToEmu(value.h))),\n properties: {\n fontFace: value.fontFamily,\n fontSize: value.fontSize ? asPt(pxToPt(value.fontSize)) : undefined,\n color: toColorInput(value.color),\n },\n }),\n );\n }\n}\n\n/**\n * PositionedNode ツリーを glimpse の PPTX source model に変換する\n * @param pages PositionedNode ツリーの配列(各要素が1ページ)\n * @param slidePx スライド全体のサイズ(px)\n * @param master スライドマスターオプション(省略可能)\n * @returns PPTX の write / writeFile / stream 互換 facade\n */\nexport function renderPptx(\n pages: PositionedNode[],\n slidePx: SlidePx,\n buildContext: BuildContext,\n master?: SlideMasterOptions,\n) {\n const margin =\n master?.margin === undefined ? undefined : resolveBoxSpacing(master.margin);\n let source = createPptx({\n slideSize: {\n width: asEmu(Math.round(pxToEmu(slidePx.w))),\n height: asEmu(Math.round(pxToEmu(slidePx.h))),\n },\n slideMaster: {\n name: master?.title ?? \"POM_MASTER\",\n background: masterBackground(master, buildContext),\n },\n slideLayout: {\n name: \"POM_LAYOUT\",\n margin: margin\n ? {\n top: asEmu(Math.round(pxToEmu(margin.top))),\n right: asEmu(Math.round(pxToEmu(margin.right))),\n bottom: asEmu(Math.round(pxToEmu(margin.bottom))),\n left: asEmu(Math.round(pxToEmu(margin.left))),\n }\n : undefined,\n },\n });\n buildContext.pptxAuthoring.initialize(source, margin !== undefined);\n if (master) addMasterContent(buildContext, master);\n\n for (const [pageIndex, data] of pages.entries()) {\n if (pageIndex > 0) {\n const layoutPartPath =\n buildContext.pptxAuthoring.source.slideLayouts[0]?.partPath;\n if (!layoutPartPath)\n throw new Error(\"createPptx did not create a slide layout\");\n source = addEmptySlideFromLayout(buildContext.pptxAuthoring.source, {\n layoutPartPath,\n });\n buildContext.pptxAuthoring.replaceSource(source);\n }\n const slideHandle =\n buildContext.pptxAuthoring.source.slides[pageIndex]?.handle;\n if (!slideHandle)\n throw new Error(`slide handle was not found: ${pageIndex + 1}`);\n buildContext.pptxAuthoring.selectSlide(slideHandle);\n const idPositionMap = buildIdPositionMap(data, buildContext.diagnostics);\n const ctx: RenderContext = { buildContext, idPositionMap };\n\n // ルートノードの backgroundColor はスライドの background プロパティとして適用\n // これにより、マスタースライドのオブジェクトを覆い隠さない\n // line/arrow ノードは backgroundColor を持たないためスキップ\n // ただし opacity が指定されている場合は slide.background では透過を表現できないため、\n // renderBackgroundAndBorder で描画する\n const isLinelike = data.type === \"line\" || data.type === \"arrow\";\n const rootBackgroundColor = !isLinelike ? data.backgroundColor : undefined;\n const rootBackgroundGradient = !isLinelike\n ? data.backgroundGradient\n : undefined;\n const rootHasOpacity =\n !isLinelike && \"opacity\" in data && data.opacity !== undefined;\n const rootGradientApplied =\n rootBackgroundGradient && !rootHasOpacity\n ? buildContext.pptxAuthoring.setSlideBackgroundGradient(\n rootBackgroundGradient,\n )\n : false;\n if (\n !rootGradientApplied &&\n rootBackgroundColor &&\n !rootBackgroundGradient &&\n !rootHasOpacity\n ) {\n buildContext.pptxAuthoring.setSlideBackgroundSolid(rootBackgroundColor);\n }\n\n // ルートノードの backgroundImage はスライドの background プロパティとして適用\n // backgroundColor と backgroundImage の両方がある場合、backgroundImage が優先\n const rootBackgroundImage = !isLinelike ? data.backgroundImage : undefined;\n if (rootBackgroundImage) {\n const cachedData = getImageData(\n rootBackgroundImage.src,\n buildContext.imageDataCache,\n );\n buildContext.pptxAuthoring.setSlideBackgroundImage(\n imageBytesFromSource(rootBackgroundImage.src, cachedData),\n );\n }\n\n /**\n * node をスライドにレンダリングする\n * @param isRoot ルートノードかどうか(ルートノードの background は slide.background で処理済み)\n */\n function renderNode(node: PositionedNode, isRoot = false) {\n // line/arrow ノードは backgroundColor/border を持たないため、background/border の描画をスキップ\n if (node.type !== \"line\" && node.type !== \"arrow\") {\n // ルートノードの backgroundColor/backgroundImage は既に slide.background に適用済みなのでスキップ\n // ただし opacity がある場合は slide.background では透過を表現できないため通常描画\n if (\n isRoot &&\n (rootBackgroundImage ||\n ((rootBackgroundColor || rootGradientApplied) && !rootHasOpacity))\n ) {\n // border のみ描画(backgroundColor/backgroundImage はスキップ)\n renderBorderOnly(node, ctx);\n } else {\n renderBackgroundAndBorder(node, ctx);\n }\n }\n\n const def = getNodeDef(node.type);\n\n switch (def.category) {\n case \"leaf\":\n if (!def.render) {\n throw new Error(\n `No render function registered for leaf node: ${node.type}`,\n );\n }\n def.render(node, ctx);\n break;\n\n case \"multi-child\":\n case \"absolute-child\": {\n const containerNode = node as Extract<\n PositionedNode,\n { type: \"vstack\" | \"hstack\" | \"layer\" }\n >;\n // zIndex でソートして描画順を制御(値が小さいものが先に描画される)\n for (const child of sortByZIndex(containerNode.children)) {\n renderNode(child);\n }\n break;\n }\n }\n }\n\n renderNode(data, true); // ルートノードとして処理\n }\n\n return createWritablePptx(() => buildContext.pptxAuthoring.source);\n}\n"],"mappings":"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"}
@@ -3,7 +3,7 @@ import { pxToIn, pxToPt } from "./units.js";
3
3
  import { getContentAreaIn } from "./utils/contentArea.js";
4
4
  //#region src/renderPptx/textOptions.ts
5
5
  /**
6
- * underline プロパティを pptxgenjs 形式に変換する
6
+ * underline プロパティを描画用の形式に変換する
7
7
  */
8
8
  function convertUnderline(underline) {
9
9
  if (underline === void 0) return void 0;
@@ -15,7 +15,7 @@ function convertUnderline(underline) {
15
15
  };
16
16
  }
17
17
  /**
18
- * strike プロパティを pptxgenjs 形式に変換する
18
+ * strike プロパティを描画用の形式に変換する
19
19
  */
20
20
  function convertStrike(strike) {
21
21
  if (strike) return "sngStrike";
@@ -37,10 +37,9 @@ function resolveSubSup(run, parent) {
37
37
  };
38
38
  }
39
39
  /**
40
- * glow プロパティを pptxgenjs 形式に変換する
41
- * size はユーザー入力 px、pptxgenjs glow.size は pt。
42
- * pptxgenjs は省略時デフォルトを Object.assign で合成するため undefined を
43
- * 渡すとデフォルトが消える。ここで pom 側のデフォルトを確定させる。
40
+ * glow プロパティを描画用の形式に変換する。
41
+ * size はユーザー入力 px、描画用の glow.size は pt。
42
+ * ここで既存の出力互換性を保つデフォルトを確定させる。
44
43
  */
45
44
  function convertGlow(glow) {
46
45
  if (glow === void 0) return void 0;
@@ -51,8 +50,8 @@ function convertGlow(glow) {
51
50
  };
52
51
  }
53
52
  /**
54
- * outline プロパティを pptxgenjs 形式に変換する
55
- * size はユーザー入力 px、pptxgenjs outline.size は pt
53
+ * outline プロパティを描画用の形式に変換する
54
+ * size はユーザー入力 px、描画用の outline.size は pt
56
55
  */
57
56
  function convertOutline(outline) {
58
57
  if (outline === void 0) return void 0;
@@ -1 +1 @@
1
- {"version":3,"file":"textOptions.js","names":[],"sources":["../../src/renderPptx/textOptions.ts"],"sourcesContent":["import type {\n PositionedNode,\n TextGlow,\n TextOutline,\n Underline,\n UnderlineStyle,\n} from \"../types.ts\";\nimport { pxToIn, pxToPt } from \"./units.ts\";\nimport { getContentAreaIn } from \"./utils/contentArea.ts\";\nimport { measureFontVerticalMetricsRatio } from \"../calcYogaLayout/fontLoader.ts\";\n\ntype TextNode = Extract<PositionedNode, { type: \"text\" }>;\n\n/**\n * underline プロパティを pptxgenjs 形式に変換する\n */\nexport function convertUnderline(\n underline: Underline | undefined,\n): { style?: UnderlineStyle; color?: string } | undefined {\n if (underline === undefined) return undefined;\n if (underline === false) return undefined;\n if (underline === true) return { style: \"sng\" };\n return {\n style: underline.style,\n color: underline.color,\n };\n}\n\n/**\n * strike プロパティを pptxgenjs 形式に変換する\n */\nexport function convertStrike(\n strike: boolean | undefined,\n): \"sngStrike\" | undefined {\n if (strike) return \"sngStrike\";\n return undefined;\n}\n\n/**\n * run と親 (node / cell / li) の subscript / superscript を相互排他に解決する。\n * OOXML の baseline 値で表現される性質上、両方 true は意味を持たないため、\n * run 側で sub/sup のいずれかが指定された場合は親の指定をまるごと無効化する\n * (片方だけ指定して反対側を親から漏らさない)。\n */\nexport function resolveSubSup(\n run: { subscript?: boolean; superscript?: boolean },\n parent: { subscript?: boolean; superscript?: boolean },\n): { subscript: boolean | undefined; superscript: boolean | undefined } {\n if (run.subscript !== undefined || run.superscript !== undefined) {\n return { subscript: run.subscript, superscript: run.superscript };\n }\n return { subscript: parent.subscript, superscript: parent.superscript };\n}\n\n/**\n * glow プロパティを pptxgenjs 形式に変換する\n * size はユーザー入力 px、pptxgenjs glow.size は pt。\n * pptxgenjs は省略時デフォルトを Object.assign で合成するため undefined を\n * 渡すとデフォルトが消える。ここで pom 側のデフォルトを確定させる。\n */\nexport function convertGlow(\n glow: TextGlow | undefined,\n): { size: number; opacity: number; color: string } | undefined {\n if (glow === undefined) return undefined;\n return {\n size: pxToPt(glow.size ?? 8),\n opacity: glow.opacity ?? 0.75,\n color: glow.color ?? \"FFFFFF\",\n };\n}\n\n/**\n * outline プロパティを pptxgenjs 形式に変換する\n * size はユーザー入力 px、pptxgenjs outline.size は pt\n */\nexport function convertOutline(\n outline: TextOutline | undefined,\n): { size: number; color: string } | undefined {\n if (outline === undefined) return undefined;\n return {\n size: pxToPt(outline.size ?? 1),\n color: outline.color ?? \"FFFFFF\",\n };\n}\n\n/**\n * 行内でグリフ ink が上下中央に来るようにするための描画 y 補正量 (px)\n *\n * レンダラ (LibreOffice / PowerPoint) は固定行送り (spcPts) のとき\n * baseline を「行下端 − winDescent × fontSize」に置くため、グリフ ink\n * (typoAscender + typoDescender) は行内で下寄りになり、同じ gap でも\n * テキスト上側の視覚余白が広く下側が狭く見える (#846)。\n * baseline の実位置と「ink を行内中央に置いたときの baseline 位置」の\n * 差分を返し、呼び出し側でテキストフレームをその分だけ上へずらす。\n */\nexport function calcGlyphCenteringShiftPx(\n fontSizePx: number,\n lineHeight: number,\n fontWeight: \"normal\" | \"bold\",\n): number {\n const lineHeightPx = fontSizePx * lineHeight;\n const metrics = measureFontVerticalMetricsRatio(fontWeight);\n const baselineFromTopPx = lineHeightPx - metrics.winDescent * fontSizePx;\n const inkHeightPx =\n (metrics.typoAscender + metrics.typoDescender) * fontSizePx;\n const centeredBaselineFromTopPx =\n (lineHeightPx - inkHeightPx) / 2 + metrics.typoAscender * fontSizePx;\n return baselineFromTopPx - centeredBaselineFromTopPx;\n}\n\nexport function createTextOptions(node: TextNode) {\n const fontSizePx = node.fontSize ?? 24;\n const fontFamily = node.fontFamily ?? \"Noto Sans JP\";\n const lineHeight = node.lineHeight ?? 1.3;\n\n const area = getContentAreaIn(node);\n const glyphShiftPx = calcGlyphCenteringShiftPx(\n fontSizePx,\n lineHeight,\n node.bold ? \"bold\" : \"normal\",\n );\n\n return {\n ...area,\n y: area.y - pxToIn(glyphShiftPx),\n fontSize: pxToPt(fontSizePx),\n fontFace: fontFamily,\n align: node.textAlign ?? \"left\",\n valign: \"top\" as const,\n margin: 0,\n // 行送りを固定値 (spcPts) で指定する。倍率指定 (spcPct) はレンダラがフォント\n // メトリクスに対する倍率として解釈するため、計測高さ (行数 × fontSize ×\n // lineHeight) と実描画の行高さが一致せず、グリフがボックスからはみ出して\n // 上下余白が非対称になる (#846)\n lineSpacing: pxToPt(fontSizePx * lineHeight),\n rotate: node.rotate,\n color: node.color,\n bold: node.bold,\n italic: node.italic,\n underline: convertUnderline(node.underline),\n strike: convertStrike(node.strike),\n subscript: node.subscript,\n superscript: node.superscript,\n highlight: node.highlight,\n glow: convertGlow(node.glow),\n outline: convertOutline(node.outline),\n // letterSpacing はユーザー入力 px、pptxgenjs charSpacing は pt\n charSpacing:\n node.letterSpacing !== undefined ? pxToPt(node.letterSpacing) : undefined,\n };\n}\n"],"mappings":";;;;;;;AAgBA,SAAgB,iBACd,WACwD;CACxD,IAAI,cAAc,KAAA,GAAW,OAAO,KAAA;CACpC,IAAI,cAAc,OAAO,OAAO,KAAA;CAChC,IAAI,cAAc,MAAM,OAAO,EAAE,OAAO,MAAM;CAC9C,OAAO;EACL,OAAO,UAAU;EACjB,OAAO,UAAU;CACnB;AACF;;;;AAKA,SAAgB,cACd,QACyB;CACzB,IAAI,QAAQ,OAAO;AAErB;;;;;;;AAQA,SAAgB,cACd,KACA,QACsE;CACtE,IAAI,IAAI,cAAc,KAAA,KAAa,IAAI,gBAAgB,KAAA,GACrD,OAAO;EAAE,WAAW,IAAI;EAAW,aAAa,IAAI;CAAY;CAElE,OAAO;EAAE,WAAW,OAAO;EAAW,aAAa,OAAO;CAAY;AACxE;;;;;;;AAQA,SAAgB,YACd,MAC8D;CAC9D,IAAI,SAAS,KAAA,GAAW,OAAO,KAAA;CAC/B,OAAO;EACL,MAAM,OAAO,KAAK,QAAQ,CAAC;EAC3B,SAAS,KAAK,WAAW;EACzB,OAAO,KAAK,SAAS;CACvB;AACF;;;;;AAMA,SAAgB,eACd,SAC6C;CAC7C,IAAI,YAAY,KAAA,GAAW,OAAO,KAAA;CAClC,OAAO;EACL,MAAM,OAAO,QAAQ,QAAQ,CAAC;EAC9B,OAAO,QAAQ,SAAS;CAC1B;AACF;;;;;;;;;;;AAYA,SAAgB,0BACd,YACA,YACA,YACQ;CACR,MAAM,eAAe,aAAa;CAClC,MAAM,UAAU,gCAAgC,UAAU;CAM1D,OAL0B,eAAe,QAAQ,aAAa,eAI3D,gBAFA,QAAQ,eAAe,QAAQ,iBAAiB,cAElB,IAAI,QAAQ,eAAe;AAE9D;AAEA,SAAgB,kBAAkB,MAAgB;CAChD,MAAM,aAAa,KAAK,YAAY;CACpC,MAAM,aAAa,KAAK,cAAc;CACtC,MAAM,aAAa,KAAK,cAAc;CAEtC,MAAM,OAAO,iBAAiB,IAAI;CAClC,MAAM,eAAe,0BACnB,YACA,YACA,KAAK,OAAO,SAAS,QACvB;CAEA,OAAO;EACL,GAAG;EACH,GAAG,KAAK,IAAI,OAAO,YAAY;EAC/B,UAAU,OAAO,UAAU;EAC3B,UAAU;EACV,OAAO,KAAK,aAAa;EACzB,QAAQ;EACR,QAAQ;EAKR,aAAa,OAAO,aAAa,UAAU;EAC3C,QAAQ,KAAK;EACb,OAAO,KAAK;EACZ,MAAM,KAAK;EACX,QAAQ,KAAK;EACb,WAAW,iBAAiB,KAAK,SAAS;EAC1C,QAAQ,cAAc,KAAK,MAAM;EACjC,WAAW,KAAK;EAChB,aAAa,KAAK;EAClB,WAAW,KAAK;EAChB,MAAM,YAAY,KAAK,IAAI;EAC3B,SAAS,eAAe,KAAK,OAAO;EAEpC,aACE,KAAK,kBAAkB,KAAA,IAAY,OAAO,KAAK,aAAa,IAAI,KAAA;CACpE;AACF"}
1
+ {"version":3,"file":"textOptions.js","names":[],"sources":["../../src/renderPptx/textOptions.ts"],"sourcesContent":["import type {\n PositionedNode,\n TextGlow,\n TextOutline,\n Underline,\n UnderlineStyle,\n} from \"../types.ts\";\nimport { pxToIn, pxToPt } from \"./units.ts\";\nimport { getContentAreaIn } from \"./utils/contentArea.ts\";\nimport { measureFontVerticalMetricsRatio } from \"../calcYogaLayout/fontLoader.ts\";\n\ntype TextNode = Extract<PositionedNode, { type: \"text\" }>;\n\n/**\n * underline プロパティを描画用の形式に変換する\n */\nexport function convertUnderline(\n underline: Underline | undefined,\n): { style?: UnderlineStyle; color?: string } | undefined {\n if (underline === undefined) return undefined;\n if (underline === false) return undefined;\n if (underline === true) return { style: \"sng\" };\n return {\n style: underline.style,\n color: underline.color,\n };\n}\n\n/**\n * strike プロパティを描画用の形式に変換する\n */\nexport function convertStrike(\n strike: boolean | undefined,\n): \"sngStrike\" | undefined {\n if (strike) return \"sngStrike\";\n return undefined;\n}\n\n/**\n * run と親 (node / cell / li) の subscript / superscript を相互排他に解決する。\n * OOXML の baseline 値で表現される性質上、両方 true は意味を持たないため、\n * run 側で sub/sup のいずれかが指定された場合は親の指定をまるごと無効化する\n * (片方だけ指定して反対側を親から漏らさない)。\n */\nexport function resolveSubSup(\n run: { subscript?: boolean; superscript?: boolean },\n parent: { subscript?: boolean; superscript?: boolean },\n): { subscript: boolean | undefined; superscript: boolean | undefined } {\n if (run.subscript !== undefined || run.superscript !== undefined) {\n return { subscript: run.subscript, superscript: run.superscript };\n }\n return { subscript: parent.subscript, superscript: parent.superscript };\n}\n\n/**\n * glow プロパティを描画用の形式に変換する。\n * size はユーザー入力 px、描画用の glow.size は pt。\n * ここで既存の出力互換性を保つデフォルトを確定させる。\n */\nexport function convertGlow(\n glow: TextGlow | undefined,\n): { size: number; opacity: number; color: string } | undefined {\n if (glow === undefined) return undefined;\n return {\n size: pxToPt(glow.size ?? 8),\n opacity: glow.opacity ?? 0.75,\n color: glow.color ?? \"FFFFFF\",\n };\n}\n\n/**\n * outline プロパティを描画用の形式に変換する\n * size はユーザー入力 px、描画用の outline.size は pt\n */\nexport function convertOutline(\n outline: TextOutline | undefined,\n): { size: number; color: string } | undefined {\n if (outline === undefined) return undefined;\n return {\n size: pxToPt(outline.size ?? 1),\n color: outline.color ?? \"FFFFFF\",\n };\n}\n\n/**\n * 行内でグリフ ink が上下中央に来るようにするための描画 y 補正量 (px)\n *\n * レンダラ (LibreOffice / PowerPoint) は固定行送り (spcPts) のとき\n * baseline を「行下端 − winDescent × fontSize」に置くため、グリフ ink\n * (typoAscender + typoDescender) は行内で下寄りになり、同じ gap でも\n * テキスト上側の視覚余白が広く下側が狭く見える (#846)。\n * baseline の実位置と「ink を行内中央に置いたときの baseline 位置」の\n * 差分を返し、呼び出し側でテキストフレームをその分だけ上へずらす。\n */\nexport function calcGlyphCenteringShiftPx(\n fontSizePx: number,\n lineHeight: number,\n fontWeight: \"normal\" | \"bold\",\n): number {\n const lineHeightPx = fontSizePx * lineHeight;\n const metrics = measureFontVerticalMetricsRatio(fontWeight);\n const baselineFromTopPx = lineHeightPx - metrics.winDescent * fontSizePx;\n const inkHeightPx =\n (metrics.typoAscender + metrics.typoDescender) * fontSizePx;\n const centeredBaselineFromTopPx =\n (lineHeightPx - inkHeightPx) / 2 + metrics.typoAscender * fontSizePx;\n return baselineFromTopPx - centeredBaselineFromTopPx;\n}\n\nexport function createTextOptions(node: TextNode) {\n const fontSizePx = node.fontSize ?? 24;\n const fontFamily = node.fontFamily ?? \"Noto Sans JP\";\n const lineHeight = node.lineHeight ?? 1.3;\n\n const area = getContentAreaIn(node);\n const glyphShiftPx = calcGlyphCenteringShiftPx(\n fontSizePx,\n lineHeight,\n node.bold ? \"bold\" : \"normal\",\n );\n\n return {\n ...area,\n y: area.y - pxToIn(glyphShiftPx),\n fontSize: pxToPt(fontSizePx),\n fontFace: fontFamily,\n align: node.textAlign ?? \"left\",\n valign: \"top\" as const,\n margin: 0,\n // 行送りを固定値 (spcPts) で指定する。倍率指定 (spcPct) はレンダラがフォント\n // メトリクスに対する倍率として解釈するため、計測高さ (行数 × fontSize ×\n // lineHeight) と実描画の行高さが一致せず、グリフがボックスからはみ出して\n // 上下余白が非対称になる (#846)\n lineSpacing: pxToPt(fontSizePx * lineHeight),\n rotate: node.rotate,\n color: node.color,\n bold: node.bold,\n italic: node.italic,\n underline: convertUnderline(node.underline),\n strike: convertStrike(node.strike),\n subscript: node.subscript,\n superscript: node.superscript,\n highlight: node.highlight,\n glow: convertGlow(node.glow),\n outline: convertOutline(node.outline),\n // letterSpacing はユーザー入力 px、描画用の charSpacing は pt\n charSpacing:\n node.letterSpacing !== undefined ? pxToPt(node.letterSpacing) : undefined,\n };\n}\n"],"mappings":";;;;;;;AAgBA,SAAgB,iBACd,WACwD;CACxD,IAAI,cAAc,KAAA,GAAW,OAAO,KAAA;CACpC,IAAI,cAAc,OAAO,OAAO,KAAA;CAChC,IAAI,cAAc,MAAM,OAAO,EAAE,OAAO,MAAM;CAC9C,OAAO;EACL,OAAO,UAAU;EACjB,OAAO,UAAU;CACnB;AACF;;;;AAKA,SAAgB,cACd,QACyB;CACzB,IAAI,QAAQ,OAAO;AAErB;;;;;;;AAQA,SAAgB,cACd,KACA,QACsE;CACtE,IAAI,IAAI,cAAc,KAAA,KAAa,IAAI,gBAAgB,KAAA,GACrD,OAAO;EAAE,WAAW,IAAI;EAAW,aAAa,IAAI;CAAY;CAElE,OAAO;EAAE,WAAW,OAAO;EAAW,aAAa,OAAO;CAAY;AACxE;;;;;;AAOA,SAAgB,YACd,MAC8D;CAC9D,IAAI,SAAS,KAAA,GAAW,OAAO,KAAA;CAC/B,OAAO;EACL,MAAM,OAAO,KAAK,QAAQ,CAAC;EAC3B,SAAS,KAAK,WAAW;EACzB,OAAO,KAAK,SAAS;CACvB;AACF;;;;;AAMA,SAAgB,eACd,SAC6C;CAC7C,IAAI,YAAY,KAAA,GAAW,OAAO,KAAA;CAClC,OAAO;EACL,MAAM,OAAO,QAAQ,QAAQ,CAAC;EAC9B,OAAO,QAAQ,SAAS;CAC1B;AACF;;;;;;;;;;;AAYA,SAAgB,0BACd,YACA,YACA,YACQ;CACR,MAAM,eAAe,aAAa;CAClC,MAAM,UAAU,gCAAgC,UAAU;CAM1D,OAL0B,eAAe,QAAQ,aAAa,eAI3D,gBAFA,QAAQ,eAAe,QAAQ,iBAAiB,cAElB,IAAI,QAAQ,eAAe;AAE9D;AAEA,SAAgB,kBAAkB,MAAgB;CAChD,MAAM,aAAa,KAAK,YAAY;CACpC,MAAM,aAAa,KAAK,cAAc;CACtC,MAAM,aAAa,KAAK,cAAc;CAEtC,MAAM,OAAO,iBAAiB,IAAI;CAClC,MAAM,eAAe,0BACnB,YACA,YACA,KAAK,OAAO,SAAS,QACvB;CAEA,OAAO;EACL,GAAG;EACH,GAAG,KAAK,IAAI,OAAO,YAAY;EAC/B,UAAU,OAAO,UAAU;EAC3B,UAAU;EACV,OAAO,KAAK,aAAa;EACzB,QAAQ;EACR,QAAQ;EAKR,aAAa,OAAO,aAAa,UAAU;EAC3C,QAAQ,KAAK;EACb,OAAO,KAAK;EACZ,MAAM,KAAK;EACX,QAAQ,KAAK;EACb,WAAW,iBAAiB,KAAK,SAAS;EAC1C,QAAQ,cAAc,KAAK,MAAM;EACjC,WAAW,KAAK;EAChB,aAAa,KAAK;EAClB,WAAW,KAAK;EAChB,MAAM,YAAY,KAAK,IAAI;EAC3B,SAAS,eAAe,KAAK,OAAO;EAEpC,aACE,KAAK,kBAAkB,KAAA,IAAY,OAAO,KAAK,aAAa,IAAI,KAAA;CACpE;AACF"}
@@ -4,12 +4,12 @@ const pxToPt = (px) => px * 72 / 96;
4
4
  /**
5
5
  * px を DrawingML の EMU (English Metric Unit) に変換する。
6
6
  * 1 inch = 914400 EMU、96 DPI 基準で 1 px = 9525 EMU。
7
- * `<a:glow rad="...">` など EMU を直接埋め込む XML 後処理で使う。
7
+ * DrawingML の位置・サイズ指定で使う。
8
8
  */
9
9
  const pxToEmu = (px) => px * EMU_PER_IN / 96;
10
10
  /**
11
- * px 単位の矩形を pptxgenjs の位置オプション (inch 単位の x/y/w/h) に
12
- * まとめて変換する。addShape / addText 等のオプションへ spread して使う。
11
+ * px 単位の矩形を互換用の位置オプション (inch 単位の x/y/w/h) に
12
+ * まとめて変換する。
13
13
  */
14
14
  const rectPxToIn = (rect) => ({
15
15
  x: pxToIn(rect.x),
@@ -1 +1 @@
1
- {"version":3,"file":"units.js","names":[],"sources":["../../src/renderPptx/units.ts"],"sourcesContent":["export const PX_PER_IN = 96;\nexport const EMU_PER_IN = 914400;\n\nexport const pxToIn = (px: number) => px / PX_PER_IN;\n\nexport const pxToPt = (px: number) => (px * 72) / PX_PER_IN;\n\n/**\n * px を DrawingML の EMU (English Metric Unit) に変換する。\n * 1 inch = 914400 EMU、96 DPI 基準で 1 px = 9525 EMU。\n * `<a:glow rad=\"...\">` など EMU を直接埋め込む XML 後処理で使う。\n */\nexport const pxToEmu = (px: number) => (px * EMU_PER_IN) / PX_PER_IN;\n\n/**\n * px 単位の矩形を pptxgenjs の位置オプション (inch 単位の x/y/w/h) に\n * まとめて変換する。addShape / addText 等のオプションへ spread して使う。\n */\nexport const rectPxToIn = (rect: {\n x: number;\n y: number;\n w: number;\n h: number;\n}) => ({\n x: pxToIn(rect.x),\n y: pxToIn(rect.y),\n w: pxToIn(rect.w),\n h: pxToIn(rect.h),\n});\n"],"mappings":"AACA,MAAa,aAAa;AAE1B,MAAa,UAAU,OAAe,KAAA;AAEtC,MAAa,UAAU,OAAgB,KAAK,KAAA;;;;;;AAO5C,MAAa,WAAW,OAAgB,KAAK,aAAA;;;;;AAM7C,MAAa,cAAc,UAKpB;CACL,GAAG,OAAO,KAAK,CAAC;CAChB,GAAG,OAAO,KAAK,CAAC;CAChB,GAAG,OAAO,KAAK,CAAC;CAChB,GAAG,OAAO,KAAK,CAAC;AAClB"}
1
+ {"version":3,"file":"units.js","names":[],"sources":["../../src/renderPptx/units.ts"],"sourcesContent":["export const PX_PER_IN = 96;\nexport const EMU_PER_IN = 914400;\n\nexport const pxToIn = (px: number) => px / PX_PER_IN;\n\nexport const pxToPt = (px: number) => (px * 72) / PX_PER_IN;\n\n/**\n * px を DrawingML の EMU (English Metric Unit) に変換する。\n * 1 inch = 914400 EMU、96 DPI 基準で 1 px = 9525 EMU。\n * DrawingML の位置・サイズ指定で使う。\n */\nexport const pxToEmu = (px: number) => (px * EMU_PER_IN) / PX_PER_IN;\n\n/**\n * px 単位の矩形を互換用の位置オプション (inch 単位の x/y/w/h) に\n * まとめて変換する。\n */\nexport const rectPxToIn = (rect: {\n x: number;\n y: number;\n w: number;\n h: number;\n}) => ({\n x: pxToIn(rect.x),\n y: pxToIn(rect.y),\n w: pxToIn(rect.w),\n h: pxToIn(rect.h),\n});\n"],"mappings":"AACA,MAAa,aAAa;AAE1B,MAAa,UAAU,OAAe,KAAA;AAEtC,MAAa,UAAU,OAAgB,KAAK,KAAA;;;;;;AAO5C,MAAa,WAAW,OAAgB,KAAK,aAAA;;;;;AAM7C,MAAa,cAAc,UAKpB;CACL,GAAG,OAAO,KAAK,CAAC;CAChB,GAAG,OAAO,KAAK,CAAC;CAChB,GAAG,OAAO,KAAK,CAAC;CAChB,GAAG,OAAO,KAAK,CAAC;AAClB"}