@hirokisakabe/pom 9.1.2 → 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 (65) hide show
  1. package/dist/buildContext.js +2 -2
  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 -3
  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 +4 -1
  15. package/dist/renderPptx/nodes/flow.js.map +1 -1
  16. package/dist/renderPptx/nodes/icon.js +5 -2
  17. package/dist/renderPptx/nodes/icon.js.map +1 -1
  18. package/dist/renderPptx/nodes/list.js +7 -9
  19. package/dist/renderPptx/nodes/list.js.map +1 -1
  20. package/dist/renderPptx/nodes/matrix.js +4 -1
  21. package/dist/renderPptx/nodes/matrix.js.map +1 -1
  22. package/dist/renderPptx/nodes/processArrow.js +8 -2
  23. package/dist/renderPptx/nodes/processArrow.js.map +1 -1
  24. package/dist/renderPptx/nodes/pyramid.js +4 -1
  25. package/dist/renderPptx/nodes/pyramid.js.map +1 -1
  26. package/dist/renderPptx/nodes/shape.js +5 -2
  27. package/dist/renderPptx/nodes/shape.js.map +1 -1
  28. package/dist/renderPptx/nodes/table.js +89 -59
  29. package/dist/renderPptx/nodes/table.js.map +1 -1
  30. package/dist/renderPptx/nodes/text.js +1 -18
  31. package/dist/renderPptx/nodes/text.js.map +1 -1
  32. package/dist/renderPptx/nodes/timeline.js +9 -3
  33. package/dist/renderPptx/nodes/timeline.js.map +1 -1
  34. package/dist/renderPptx/nodes/tree.js +4 -1
  35. package/dist/renderPptx/nodes/tree.js.map +1 -1
  36. package/dist/renderPptx/pptxAuthoring.js +381 -0
  37. package/dist/renderPptx/pptxAuthoring.js.map +1 -0
  38. package/dist/renderPptx/renderPptx.js +157 -126
  39. package/dist/renderPptx/renderPptx.js.map +1 -1
  40. package/dist/renderPptx/textOptions.js +7 -8
  41. package/dist/renderPptx/textOptions.js.map +1 -1
  42. package/dist/renderPptx/units.js +3 -3
  43. package/dist/renderPptx/units.js.map +1 -1
  44. package/dist/renderPptx/utils/backgroundBorder.js +30 -44
  45. package/dist/renderPptx/utils/backgroundBorder.js.map +1 -1
  46. package/dist/renderPptx/utils/contentArea.js +1 -1
  47. package/dist/renderPptx/utils/contentArea.js.map +1 -1
  48. package/dist/renderPptx/utils/glimpsePicture.js +3 -22
  49. package/dist/renderPptx/utils/glimpsePicture.js.map +1 -1
  50. package/dist/renderPptx/utils/glimpseShape.js +6 -25
  51. package/dist/renderPptx/utils/glimpseShape.js.map +1 -1
  52. package/dist/renderPptx/utils/glimpseTextBox.js +41 -60
  53. package/dist/renderPptx/utils/glimpseTextBox.js.map +1 -1
  54. package/dist/renderPptx/utils/straightLine.js +5 -2
  55. package/dist/renderPptx/utils/straightLine.js.map +1 -1
  56. package/dist/renderPptx/utils/visualStyle.js +3 -33
  57. package/dist/renderPptx/utils/visualStyle.js.map +1 -1
  58. package/dist/renderPptx/writablePptx.d.ts +23 -0
  59. package/dist/renderPptx/writablePptx.d.ts.map +1 -0
  60. package/dist/renderPptx/writablePptx.js +74 -0
  61. package/dist/renderPptx/writablePptx.js.map +1 -0
  62. package/dist/shared/measureImage.js.map +1 -1
  63. package/package.json +4 -5
  64. package/dist/renderPptx/glimpseTextBoxes.js +0 -646
  65. package/dist/renderPptx/glimpseTextBoxes.js.map +0 -1
@@ -1,5 +1,5 @@
1
- import { withContentBounds } from "../utils/contentArea.js";
2
1
  import { parseGradient } from "../../shared/gradient.js";
2
+ import { withContentBounds } from "../utils/contentArea.js";
3
3
  import { addGlimpseTextBox } from "../utils/glimpseTextBox.js";
4
4
  import { addGlimpseShape, createShapeBoundsInput, noShapeOutline, solidShapeFill } from "../utils/glimpseShape.js";
5
5
  import { addStraightLine } from "../utils/straightLine.js";
@@ -82,7 +82,10 @@ function renderHorizontalTimeline(node, ctx, items, options) {
82
82
  const color = item.color ?? defaultColor;
83
83
  const dateColor = resolveItemDateColor(item, options);
84
84
  addGlimpseShape(ctx, {
85
- preset: "ellipse",
85
+ geometry: {
86
+ kind: "preset",
87
+ preset: "ellipse"
88
+ },
86
89
  ...createShapeBoundsInput({
87
90
  x: cx - nodeRadius,
88
91
  y: cy - nodeRadius,
@@ -170,7 +173,10 @@ function renderVerticalTimeline(node, ctx, items, options) {
170
173
  const color = item.color ?? defaultColor;
171
174
  const dateColor = resolveItemDateColor(item, options);
172
175
  addGlimpseShape(ctx, {
173
- preset: "ellipse",
176
+ geometry: {
177
+ kind: "preset",
178
+ preset: "ellipse"
179
+ },
174
180
  ...createShapeBoundsInput({
175
181
  x: cx - nodeRadius,
176
182
  y: cy - nodeRadius,
@@ -1 +1 @@
1
- {"version":3,"file":"timeline.js","names":[],"sources":["../../../src/renderPptx/nodes/timeline.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { stripHash } from \"../utils/visualStyle.ts\";\nimport { measureTimeline } from \"../../calcYogaLayout/measureCompositeNodes.ts\";\nimport { resolveScaledContentArea } from \"../utils/scaleToFit.ts\";\nimport { withContentBounds } from \"../utils/contentArea.ts\";\nimport { parseGradient } from \"../../shared/gradient.ts\";\nimport { addStraightLine } from \"../utils/straightLine.ts\";\nimport {\n addGlimpseShape,\n createShapeBoundsInput,\n noShapeOutline,\n solidShapeFill,\n} from \"../utils/glimpseShape.ts\";\nimport { addGlimpseTextBox } from \"../utils/glimpseTextBox.ts\";\n\ntype TimelinePositionedNode = Extract<PositionedNode, { type: \"timeline\" }>;\n\ntype TimelineTextColors = {\n date: string;\n title: string;\n description: string;\n};\n\ntype TimelineRenderOptions = {\n defaultColor: string;\n nodeRadius: number;\n lineWidth: number;\n scaleFactor: number;\n textColors: TimelineTextColors;\n connectorLineColor: string;\n connectorGradient?: string;\n connectorGradientOpacity?: number;\n fontFace: string;\n useColorForDate: boolean;\n};\n\nexport function renderTimelineNode(\n node: TimelinePositionedNode,\n ctx: RenderContext,\n): void {\n const direction = node.direction ?? \"horizontal\";\n const items = node.items;\n const itemCount = items.length;\n\n if (itemCount === 0) return;\n\n const defaultColor = \"1D4ED8\"; // blue\n const baseNodeRadius = 12; // px\n const baseLineWidth = 4; // px\n\n const textColors: TimelineTextColors = {\n date: stripHash(node.dateColor) ?? \"64748B\",\n title: stripHash(node.titleColor) ?? \"1E293B\",\n description: stripHash(node.descriptionColor) ?? \"64748B\",\n };\n\n const connectorGradientFallback = node.connectorGradient\n ? parseGradient(node.connectorGradient)?.value.stops[0]?.color\n : undefined;\n const connectorLineColor =\n stripHash(connectorGradientFallback) ??\n stripHash(node.connectorColor) ??\n \"E2E8F0\";\n\n const fontFace = node.fontFamily ?? \"Noto Sans JP\";\n const useColorForDate = node.useColorForDate ?? false;\n\n // スケール係数を計算(コンテンツ領域基準)\n const { content, scaleFactor } = resolveScaledContentArea(\n node,\n measureTimeline(node),\n ctx,\n );\n\n const nodeRadius = baseNodeRadius * scaleFactor;\n const lineWidth = baseLineWidth * scaleFactor;\n\n // コンテンツ領域を使用するための仮想ノードを作成\n const contentNode = withContentBounds(node, content);\n\n const options: TimelineRenderOptions = {\n defaultColor,\n nodeRadius,\n lineWidth,\n scaleFactor,\n textColors,\n connectorLineColor,\n connectorGradient: node.connectorGradient,\n connectorGradientOpacity: node.opacity,\n fontFace,\n useColorForDate,\n };\n\n if (direction === \"horizontal\") {\n renderHorizontalTimeline(contentNode, ctx, items, options);\n } else {\n renderVerticalTimeline(contentNode, ctx, items, options);\n }\n}\n\nfunction resolveItemDateColor(\n item: TimelinePositionedNode[\"items\"][number],\n options: TimelineRenderOptions,\n): string {\n // 優先順位: item.dateColor > (useColorForDate && item.color) > Timeline.dateColor\n const perItemDateColor = stripHash(item.dateColor);\n if (perItemDateColor) return perItemDateColor;\n if (options.useColorForDate && item.color) {\n const inherited = stripHash(item.color);\n if (inherited) return inherited;\n }\n return options.textColors.date;\n}\n\nfunction renderHorizontalTimeline(\n node: TimelinePositionedNode,\n ctx: RenderContext,\n items: TimelinePositionedNode[\"items\"],\n options: TimelineRenderOptions,\n): void {\n const {\n defaultColor,\n nodeRadius,\n lineWidth,\n scaleFactor,\n textColors,\n connectorLineColor,\n fontFace,\n } = options;\n const itemCount = items.length;\n const lineY = node.y + node.h / 2;\n const labelW = 120 * scaleFactor;\n // 極端に狭い node.w でも startX <= endX を保つため、インセットを node.w/2 で頭打ちする\n const inset = Math.min(labelW / 2, node.w / 2);\n const startX = node.x + inset;\n const endX = node.x + node.w - inset;\n const lineLength = endX - startX;\n\n // メインの線を描画\n addStraightLine(\n ctx,\n { x1: startX, y1: lineY, x2: startX + lineLength, y2: lineY },\n {\n color: connectorLineColor,\n lineWidth,\n lineGradient: options.connectorGradient,\n lineGradientOpacity: options.connectorGradientOpacity,\n },\n );\n const dateLabelH = 24 * scaleFactor;\n const titleLabelH = 24 * scaleFactor;\n const descLabelH = 32 * scaleFactor;\n const dateOffset = 40 * scaleFactor;\n const titleGap = 8 * scaleFactor;\n const descOffset = 32 * scaleFactor;\n\n // 各アイテムを描画\n items.forEach((item, index) => {\n const progress = itemCount === 1 ? 0.5 : index / (itemCount - 1);\n const cx = startX + lineLength * progress;\n const cy = lineY;\n const color = item.color ?? defaultColor;\n const dateColor = resolveItemDateColor(item, options);\n\n // ノード(円)を描画\n addGlimpseShape(\n ctx,\n {\n preset: \"ellipse\",\n ...createShapeBoundsInput({\n x: cx - nodeRadius,\n y: cy - nodeRadius,\n w: nodeRadius * 2,\n h: nodeRadius * 2,\n }),\n fill: solidShapeFill(color),\n outline: noShapeOutline(),\n },\n {\n x: cx - nodeRadius,\n y: cy - nodeRadius,\n w: nodeRadius * 2,\n h: nodeRadius * 2,\n },\n { fillColor: color },\n );\n\n // 日付を上に表示\n addGlimpseTextBox(\n ctx,\n {\n x: cx - labelW / 2,\n y: cy - nodeRadius - dateOffset,\n w: labelW,\n h: dateLabelH,\n },\n {\n text: item.date,\n fontSize: 12 * scaleFactor,\n fontFace,\n color: dateColor,\n align: \"center\",\n valign: \"bottom\",\n },\n );\n\n // タイトルを下に表示\n addGlimpseTextBox(\n ctx,\n {\n x: cx - labelW / 2,\n y: cy + nodeRadius + titleGap,\n w: labelW,\n h: titleLabelH,\n },\n {\n text: item.title,\n fontSize: 14 * scaleFactor,\n fontFace,\n color: textColors.title,\n bold: true,\n align: \"center\",\n valign: \"top\",\n },\n );\n\n // 説明を表示\n if (item.description) {\n addGlimpseTextBox(\n ctx,\n {\n x: cx - labelW / 2,\n y: cy + nodeRadius + descOffset,\n w: labelW,\n h: descLabelH,\n },\n {\n text: item.description,\n fontSize: 11 * scaleFactor,\n fontFace,\n color: textColors.description,\n align: \"center\",\n valign: \"top\",\n },\n );\n }\n });\n}\n\nfunction renderVerticalTimeline(\n node: TimelinePositionedNode,\n ctx: RenderContext,\n items: TimelinePositionedNode[\"items\"],\n options: TimelineRenderOptions,\n): void {\n const {\n defaultColor,\n nodeRadius,\n lineWidth,\n scaleFactor,\n textColors,\n connectorLineColor,\n fontFace,\n } = options;\n const itemCount = items.length;\n const lineX = node.x + 40 * scaleFactor;\n const startY = node.y + nodeRadius;\n const endY = node.y + node.h - nodeRadius;\n const lineLength = endY - startY;\n\n // メインの線を描画\n addStraightLine(\n ctx,\n { x1: lineX, y1: startY, x2: lineX, y2: startY + lineLength },\n {\n color: connectorLineColor,\n lineWidth,\n lineGradient: options.connectorGradient,\n lineGradientOpacity: options.connectorGradientOpacity,\n },\n );\n\n const labelGap = 16 * scaleFactor;\n const dateLabelW = 100 * scaleFactor;\n const dateLabelH = 20 * scaleFactor;\n const titleLabelH = 24 * scaleFactor;\n const descLabelH = 32 * scaleFactor;\n const titleLabelW = node.w - 80 * scaleFactor;\n const descLabelW = node.w - 80 * scaleFactor;\n\n // 各アイテムを描画\n items.forEach((item, index) => {\n const progress = itemCount === 1 ? 0.5 : index / (itemCount - 1);\n const cx = lineX;\n const cy = startY + lineLength * progress;\n const color = item.color ?? defaultColor;\n const dateColor = resolveItemDateColor(item, options);\n\n // ノード(円)を描画\n addGlimpseShape(\n ctx,\n {\n preset: \"ellipse\",\n ...createShapeBoundsInput({\n x: cx - nodeRadius,\n y: cy - nodeRadius,\n w: nodeRadius * 2,\n h: nodeRadius * 2,\n }),\n fill: solidShapeFill(color),\n outline: noShapeOutline(),\n },\n {\n x: cx - nodeRadius,\n y: cy - nodeRadius,\n w: nodeRadius * 2,\n h: nodeRadius * 2,\n },\n { fillColor: color },\n );\n\n // 日付を左上に表示\n addGlimpseTextBox(\n ctx,\n {\n x: cx + nodeRadius + labelGap,\n y: cy - nodeRadius - 4 * scaleFactor,\n w: dateLabelW,\n h: dateLabelH,\n },\n {\n text: item.date,\n fontSize: 12 * scaleFactor,\n fontFace,\n color: dateColor,\n align: \"left\",\n valign: \"bottom\",\n },\n );\n\n // タイトルを右に表示\n addGlimpseTextBox(\n ctx,\n {\n x: cx + nodeRadius + labelGap,\n y: cy - 4 * scaleFactor,\n w: titleLabelW,\n h: titleLabelH,\n },\n {\n text: item.title,\n fontSize: 14 * scaleFactor,\n fontFace,\n color: textColors.title,\n bold: true,\n align: \"left\",\n valign: \"top\",\n },\n );\n\n // 説明を表示\n if (item.description) {\n addGlimpseTextBox(\n ctx,\n {\n x: cx + nodeRadius + labelGap,\n y: cy + 20 * scaleFactor,\n w: descLabelW,\n h: descLabelH,\n },\n {\n text: item.description,\n fontSize: 11 * scaleFactor,\n fontFace,\n color: textColors.description,\n align: \"left\",\n valign: \"top\",\n },\n );\n }\n });\n}\n"],"mappings":";;;;;;;;;AAqCA,SAAgB,mBACd,MACA,KACM;CACN,MAAM,YAAY,KAAK,aAAa;CACpC,MAAM,QAAQ,KAAK;CAGnB,IAFkB,MAAM,WAEN,GAAG;CAErB,MAAM,eAAe;CACrB,MAAM,iBAAiB;CACvB,MAAM,gBAAgB;CAEtB,MAAM,aAAiC;EACrC,MAAM,UAAU,KAAK,SAAS,KAAK;EACnC,OAAO,UAAU,KAAK,UAAU,KAAK;EACrC,aAAa,UAAU,KAAK,gBAAgB,KAAK;CACnD;CAKA,MAAM,qBACJ,UAJgC,KAAK,oBACnC,cAAc,KAAK,iBAAiB,CAAC,EAAE,MAAM,MAAM,EAAE,EAAE,QACvD,KAAA,CAEiC,KACnC,UAAU,KAAK,cAAc,KAC7B;CAEF,MAAM,WAAW,KAAK,cAAc;CACpC,MAAM,kBAAkB,KAAK,mBAAmB;CAGhD,MAAM,EAAE,SAAS,gBAAgB,yBAC/B,MACA,gBAAgB,IAAI,GACpB,GACF;CAEA,MAAM,aAAa,iBAAiB;CACpC,MAAM,YAAY,gBAAgB;CAGlC,MAAM,cAAc,kBAAkB,MAAM,OAAO;CAEnD,MAAM,UAAiC;EACrC;EACA;EACA;EACA;EACA;EACA;EACA,mBAAmB,KAAK;EACxB,0BAA0B,KAAK;EAC/B;EACA;CACF;CAEA,IAAI,cAAc,cAChB,yBAAyB,aAAa,KAAK,OAAO,OAAO;MAEzD,uBAAuB,aAAa,KAAK,OAAO,OAAO;AAE3D;AAEA,SAAS,qBACP,MACA,SACQ;CAER,MAAM,mBAAmB,UAAU,KAAK,SAAS;CACjD,IAAI,kBAAkB,OAAO;CAC7B,IAAI,QAAQ,mBAAmB,KAAK,OAAO;EACzC,MAAM,YAAY,UAAU,KAAK,KAAK;EACtC,IAAI,WAAW,OAAO;CACxB;CACA,OAAO,QAAQ,WAAW;AAC5B;AAEA,SAAS,yBACP,MACA,KACA,OACA,SACM;CACN,MAAM,EACJ,cACA,YACA,WACA,aACA,YACA,oBACA,aACE;CACJ,MAAM,YAAY,MAAM;CACxB,MAAM,QAAQ,KAAK,IAAI,KAAK,IAAI;CAChC,MAAM,SAAS,MAAM;CAErB,MAAM,QAAQ,KAAK,IAAI,SAAS,GAAG,KAAK,IAAI,CAAC;CAC7C,MAAM,SAAS,KAAK,IAAI;CAExB,MAAM,aADO,KAAK,IAAI,KAAK,IAAI,QACL;CAG1B,gBACE,KACA;EAAE,IAAI;EAAQ,IAAI;EAAO,IAAI,SAAS;EAAY,IAAI;CAAM,GAC5D;EACE,OAAO;EACP;EACA,cAAc,QAAQ;EACtB,qBAAqB,QAAQ;CAC/B,CACF;CACA,MAAM,aAAa,KAAK;CACxB,MAAM,cAAc,KAAK;CACzB,MAAM,aAAa,KAAK;CACxB,MAAM,aAAa,KAAK;CACxB,MAAM,WAAW,IAAI;CACrB,MAAM,aAAa,KAAK;CAGxB,MAAM,SAAS,MAAM,UAAU;EAC7B,MAAM,WAAW,cAAc,IAAI,KAAM,SAAS,YAAY;EAC9D,MAAM,KAAK,SAAS,aAAa;EACjC,MAAM,KAAK;EACX,MAAM,QAAQ,KAAK,SAAS;EAC5B,MAAM,YAAY,qBAAqB,MAAM,OAAO;EAGpD,gBACE,KACA;GACE,QAAQ;GACR,GAAG,uBAAuB;IACxB,GAAG,KAAK;IACR,GAAG,KAAK;IACR,GAAG,aAAa;IAChB,GAAG,aAAa;GAClB,CAAC;GACD,MAAM,eAAe,KAAK;GAC1B,SAAS,eAAe;EAC1B,GACA;GACE,GAAG,KAAK;GACR,GAAG,KAAK;GACR,GAAG,aAAa;GAChB,GAAG,aAAa;EAClB,GACA,EAAE,WAAW,MAAM,CACrB;EAGA,kBACE,KACA;GACE,GAAG,KAAK,SAAS;GACjB,GAAG,KAAK,aAAa;GACrB,GAAG;GACH,GAAG;EACL,GACA;GACE,MAAM,KAAK;GACX,UAAU,KAAK;GACf;GACA,OAAO;GACP,OAAO;GACP,QAAQ;EACV,CACF;EAGA,kBACE,KACA;GACE,GAAG,KAAK,SAAS;GACjB,GAAG,KAAK,aAAa;GACrB,GAAG;GACH,GAAG;EACL,GACA;GACE,MAAM,KAAK;GACX,UAAU,KAAK;GACf;GACA,OAAO,WAAW;GAClB,MAAM;GACN,OAAO;GACP,QAAQ;EACV,CACF;EAGA,IAAI,KAAK,aACP,kBACE,KACA;GACE,GAAG,KAAK,SAAS;GACjB,GAAG,KAAK,aAAa;GACrB,GAAG;GACH,GAAG;EACL,GACA;GACE,MAAM,KAAK;GACX,UAAU,KAAK;GACf;GACA,OAAO,WAAW;GAClB,OAAO;GACP,QAAQ;EACV,CACF;CAEJ,CAAC;AACH;AAEA,SAAS,uBACP,MACA,KACA,OACA,SACM;CACN,MAAM,EACJ,cACA,YACA,WACA,aACA,YACA,oBACA,aACE;CACJ,MAAM,YAAY,MAAM;CACxB,MAAM,QAAQ,KAAK,IAAI,KAAK;CAC5B,MAAM,SAAS,KAAK,IAAI;CAExB,MAAM,aADO,KAAK,IAAI,KAAK,IAAI,aACL;CAG1B,gBACE,KACA;EAAE,IAAI;EAAO,IAAI;EAAQ,IAAI;EAAO,IAAI,SAAS;CAAW,GAC5D;EACE,OAAO;EACP;EACA,cAAc,QAAQ;EACtB,qBAAqB,QAAQ;CAC/B,CACF;CAEA,MAAM,WAAW,KAAK;CACtB,MAAM,aAAa,MAAM;CACzB,MAAM,aAAa,KAAK;CACxB,MAAM,cAAc,KAAK;CACzB,MAAM,aAAa,KAAK;CACxB,MAAM,cAAc,KAAK,IAAI,KAAK;CAClC,MAAM,aAAa,KAAK,IAAI,KAAK;CAGjC,MAAM,SAAS,MAAM,UAAU;EAC7B,MAAM,WAAW,cAAc,IAAI,KAAM,SAAS,YAAY;EAC9D,MAAM,KAAK;EACX,MAAM,KAAK,SAAS,aAAa;EACjC,MAAM,QAAQ,KAAK,SAAS;EAC5B,MAAM,YAAY,qBAAqB,MAAM,OAAO;EAGpD,gBACE,KACA;GACE,QAAQ;GACR,GAAG,uBAAuB;IACxB,GAAG,KAAK;IACR,GAAG,KAAK;IACR,GAAG,aAAa;IAChB,GAAG,aAAa;GAClB,CAAC;GACD,MAAM,eAAe,KAAK;GAC1B,SAAS,eAAe;EAC1B,GACA;GACE,GAAG,KAAK;GACR,GAAG,KAAK;GACR,GAAG,aAAa;GAChB,GAAG,aAAa;EAClB,GACA,EAAE,WAAW,MAAM,CACrB;EAGA,kBACE,KACA;GACE,GAAG,KAAK,aAAa;GACrB,GAAG,KAAK,aAAa,IAAI;GACzB,GAAG;GACH,GAAG;EACL,GACA;GACE,MAAM,KAAK;GACX,UAAU,KAAK;GACf;GACA,OAAO;GACP,OAAO;GACP,QAAQ;EACV,CACF;EAGA,kBACE,KACA;GACE,GAAG,KAAK,aAAa;GACrB,GAAG,KAAK,IAAI;GACZ,GAAG;GACH,GAAG;EACL,GACA;GACE,MAAM,KAAK;GACX,UAAU,KAAK;GACf;GACA,OAAO,WAAW;GAClB,MAAM;GACN,OAAO;GACP,QAAQ;EACV,CACF;EAGA,IAAI,KAAK,aACP,kBACE,KACA;GACE,GAAG,KAAK,aAAa;GACrB,GAAG,KAAK,KAAK;GACb,GAAG;GACH,GAAG;EACL,GACA;GACE,MAAM,KAAK;GACX,UAAU,KAAK;GACf;GACA,OAAO,WAAW;GAClB,OAAO;GACP,QAAQ;EACV,CACF;CAEJ,CAAC;AACH"}
1
+ {"version":3,"file":"timeline.js","names":[],"sources":["../../../src/renderPptx/nodes/timeline.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { stripHash } from \"../utils/visualStyle.ts\";\nimport { measureTimeline } from \"../../calcYogaLayout/measureCompositeNodes.ts\";\nimport { resolveScaledContentArea } from \"../utils/scaleToFit.ts\";\nimport { withContentBounds } from \"../utils/contentArea.ts\";\nimport { parseGradient } from \"../../shared/gradient.ts\";\nimport { addStraightLine } from \"../utils/straightLine.ts\";\nimport {\n addGlimpseShape,\n createShapeBoundsInput,\n noShapeOutline,\n solidShapeFill,\n} from \"../utils/glimpseShape.ts\";\nimport { addGlimpseTextBox } from \"../utils/glimpseTextBox.ts\";\n\ntype TimelinePositionedNode = Extract<PositionedNode, { type: \"timeline\" }>;\n\ntype TimelineTextColors = {\n date: string;\n title: string;\n description: string;\n};\n\ntype TimelineRenderOptions = {\n defaultColor: string;\n nodeRadius: number;\n lineWidth: number;\n scaleFactor: number;\n textColors: TimelineTextColors;\n connectorLineColor: string;\n connectorGradient?: string;\n connectorGradientOpacity?: number;\n fontFace: string;\n useColorForDate: boolean;\n};\n\nexport function renderTimelineNode(\n node: TimelinePositionedNode,\n ctx: RenderContext,\n): void {\n const direction = node.direction ?? \"horizontal\";\n const items = node.items;\n const itemCount = items.length;\n\n if (itemCount === 0) return;\n\n const defaultColor = \"1D4ED8\"; // blue\n const baseNodeRadius = 12; // px\n const baseLineWidth = 4; // px\n\n const textColors: TimelineTextColors = {\n date: stripHash(node.dateColor) ?? \"64748B\",\n title: stripHash(node.titleColor) ?? \"1E293B\",\n description: stripHash(node.descriptionColor) ?? \"64748B\",\n };\n\n const connectorGradientFallback = node.connectorGradient\n ? parseGradient(node.connectorGradient)?.value.stops[0]?.color\n : undefined;\n const connectorLineColor =\n stripHash(connectorGradientFallback) ??\n stripHash(node.connectorColor) ??\n \"E2E8F0\";\n\n const fontFace = node.fontFamily ?? \"Noto Sans JP\";\n const useColorForDate = node.useColorForDate ?? false;\n\n // スケール係数を計算(コンテンツ領域基準)\n const { content, scaleFactor } = resolveScaledContentArea(\n node,\n measureTimeline(node),\n ctx,\n );\n\n const nodeRadius = baseNodeRadius * scaleFactor;\n const lineWidth = baseLineWidth * scaleFactor;\n\n // コンテンツ領域を使用するための仮想ノードを作成\n const contentNode = withContentBounds(node, content);\n\n const options: TimelineRenderOptions = {\n defaultColor,\n nodeRadius,\n lineWidth,\n scaleFactor,\n textColors,\n connectorLineColor,\n connectorGradient: node.connectorGradient,\n connectorGradientOpacity: node.opacity,\n fontFace,\n useColorForDate,\n };\n\n if (direction === \"horizontal\") {\n renderHorizontalTimeline(contentNode, ctx, items, options);\n } else {\n renderVerticalTimeline(contentNode, ctx, items, options);\n }\n}\n\nfunction resolveItemDateColor(\n item: TimelinePositionedNode[\"items\"][number],\n options: TimelineRenderOptions,\n): string {\n // 優先順位: item.dateColor > (useColorForDate && item.color) > Timeline.dateColor\n const perItemDateColor = stripHash(item.dateColor);\n if (perItemDateColor) return perItemDateColor;\n if (options.useColorForDate && item.color) {\n const inherited = stripHash(item.color);\n if (inherited) return inherited;\n }\n return options.textColors.date;\n}\n\nfunction renderHorizontalTimeline(\n node: TimelinePositionedNode,\n ctx: RenderContext,\n items: TimelinePositionedNode[\"items\"],\n options: TimelineRenderOptions,\n): void {\n const {\n defaultColor,\n nodeRadius,\n lineWidth,\n scaleFactor,\n textColors,\n connectorLineColor,\n fontFace,\n } = options;\n const itemCount = items.length;\n const lineY = node.y + node.h / 2;\n const labelW = 120 * scaleFactor;\n // 極端に狭い node.w でも startX <= endX を保つため、インセットを node.w/2 で頭打ちする\n const inset = Math.min(labelW / 2, node.w / 2);\n const startX = node.x + inset;\n const endX = node.x + node.w - inset;\n const lineLength = endX - startX;\n\n // メインの線を描画\n addStraightLine(\n ctx,\n { x1: startX, y1: lineY, x2: startX + lineLength, y2: lineY },\n {\n color: connectorLineColor,\n lineWidth,\n lineGradient: options.connectorGradient,\n lineGradientOpacity: options.connectorGradientOpacity,\n },\n );\n const dateLabelH = 24 * scaleFactor;\n const titleLabelH = 24 * scaleFactor;\n const descLabelH = 32 * scaleFactor;\n const dateOffset = 40 * scaleFactor;\n const titleGap = 8 * scaleFactor;\n const descOffset = 32 * scaleFactor;\n\n // 各アイテムを描画\n items.forEach((item, index) => {\n const progress = itemCount === 1 ? 0.5 : index / (itemCount - 1);\n const cx = startX + lineLength * progress;\n const cy = lineY;\n const color = item.color ?? defaultColor;\n const dateColor = resolveItemDateColor(item, options);\n\n // ノード(円)を描画\n addGlimpseShape(\n ctx,\n {\n geometry: { kind: \"preset\", preset: \"ellipse\" },\n ...createShapeBoundsInput({\n x: cx - nodeRadius,\n y: cy - nodeRadius,\n w: nodeRadius * 2,\n h: nodeRadius * 2,\n }),\n fill: solidShapeFill(color),\n outline: noShapeOutline(),\n },\n {\n x: cx - nodeRadius,\n y: cy - nodeRadius,\n w: nodeRadius * 2,\n h: nodeRadius * 2,\n },\n { fillColor: color },\n );\n\n // 日付を上に表示\n addGlimpseTextBox(\n ctx,\n {\n x: cx - labelW / 2,\n y: cy - nodeRadius - dateOffset,\n w: labelW,\n h: dateLabelH,\n },\n {\n text: item.date,\n fontSize: 12 * scaleFactor,\n fontFace,\n color: dateColor,\n align: \"center\",\n valign: \"bottom\",\n },\n );\n\n // タイトルを下に表示\n addGlimpseTextBox(\n ctx,\n {\n x: cx - labelW / 2,\n y: cy + nodeRadius + titleGap,\n w: labelW,\n h: titleLabelH,\n },\n {\n text: item.title,\n fontSize: 14 * scaleFactor,\n fontFace,\n color: textColors.title,\n bold: true,\n align: \"center\",\n valign: \"top\",\n },\n );\n\n // 説明を表示\n if (item.description) {\n addGlimpseTextBox(\n ctx,\n {\n x: cx - labelW / 2,\n y: cy + nodeRadius + descOffset,\n w: labelW,\n h: descLabelH,\n },\n {\n text: item.description,\n fontSize: 11 * scaleFactor,\n fontFace,\n color: textColors.description,\n align: \"center\",\n valign: \"top\",\n },\n );\n }\n });\n}\n\nfunction renderVerticalTimeline(\n node: TimelinePositionedNode,\n ctx: RenderContext,\n items: TimelinePositionedNode[\"items\"],\n options: TimelineRenderOptions,\n): void {\n const {\n defaultColor,\n nodeRadius,\n lineWidth,\n scaleFactor,\n textColors,\n connectorLineColor,\n fontFace,\n } = options;\n const itemCount = items.length;\n const lineX = node.x + 40 * scaleFactor;\n const startY = node.y + nodeRadius;\n const endY = node.y + node.h - nodeRadius;\n const lineLength = endY - startY;\n\n // メインの線を描画\n addStraightLine(\n ctx,\n { x1: lineX, y1: startY, x2: lineX, y2: startY + lineLength },\n {\n color: connectorLineColor,\n lineWidth,\n lineGradient: options.connectorGradient,\n lineGradientOpacity: options.connectorGradientOpacity,\n },\n );\n\n const labelGap = 16 * scaleFactor;\n const dateLabelW = 100 * scaleFactor;\n const dateLabelH = 20 * scaleFactor;\n const titleLabelH = 24 * scaleFactor;\n const descLabelH = 32 * scaleFactor;\n const titleLabelW = node.w - 80 * scaleFactor;\n const descLabelW = node.w - 80 * scaleFactor;\n\n // 各アイテムを描画\n items.forEach((item, index) => {\n const progress = itemCount === 1 ? 0.5 : index / (itemCount - 1);\n const cx = lineX;\n const cy = startY + lineLength * progress;\n const color = item.color ?? defaultColor;\n const dateColor = resolveItemDateColor(item, options);\n\n // ノード(円)を描画\n addGlimpseShape(\n ctx,\n {\n geometry: { kind: \"preset\", preset: \"ellipse\" },\n ...createShapeBoundsInput({\n x: cx - nodeRadius,\n y: cy - nodeRadius,\n w: nodeRadius * 2,\n h: nodeRadius * 2,\n }),\n fill: solidShapeFill(color),\n outline: noShapeOutline(),\n },\n {\n x: cx - nodeRadius,\n y: cy - nodeRadius,\n w: nodeRadius * 2,\n h: nodeRadius * 2,\n },\n { fillColor: color },\n );\n\n // 日付を左上に表示\n addGlimpseTextBox(\n ctx,\n {\n x: cx + nodeRadius + labelGap,\n y: cy - nodeRadius - 4 * scaleFactor,\n w: dateLabelW,\n h: dateLabelH,\n },\n {\n text: item.date,\n fontSize: 12 * scaleFactor,\n fontFace,\n color: dateColor,\n align: \"left\",\n valign: \"bottom\",\n },\n );\n\n // タイトルを右に表示\n addGlimpseTextBox(\n ctx,\n {\n x: cx + nodeRadius + labelGap,\n y: cy - 4 * scaleFactor,\n w: titleLabelW,\n h: titleLabelH,\n },\n {\n text: item.title,\n fontSize: 14 * scaleFactor,\n fontFace,\n color: textColors.title,\n bold: true,\n align: \"left\",\n valign: \"top\",\n },\n );\n\n // 説明を表示\n if (item.description) {\n addGlimpseTextBox(\n ctx,\n {\n x: cx + nodeRadius + labelGap,\n y: cy + 20 * scaleFactor,\n w: descLabelW,\n h: descLabelH,\n },\n {\n text: item.description,\n fontSize: 11 * scaleFactor,\n fontFace,\n color: textColors.description,\n align: \"left\",\n valign: \"top\",\n },\n );\n }\n });\n}\n"],"mappings":"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"}
@@ -168,7 +168,10 @@ function renderTreeNode(node, ctx) {
168
168
  const drawW = layoutNode.width * sf;
169
169
  const drawH = layoutNode.height * sf;
170
170
  addGlimpseShape(ctx, {
171
- preset: shapeType,
171
+ geometry: {
172
+ kind: "preset",
173
+ preset: shapeType
174
+ },
172
175
  ...createShapeBoundsInput({
173
176
  x: drawX,
174
177
  y: drawY,
@@ -1 +1 @@
1
- {"version":3,"file":"tree.js","names":[],"sources":["../../../src/renderPptx/nodes/tree.ts"],"sourcesContent":["import type {\n PositionedNode,\n TreeDataItem,\n TreeNodeShape,\n TreeConnectorStyle,\n} from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { stripHash } from \"../utils/visualStyle.ts\";\nimport { resolveScaledContentArea } from \"../utils/scaleToFit.ts\";\nimport { addStraightLine } from \"../utils/straightLine.ts\";\nimport {\n addGlimpseShape,\n createShapeBoundsInput,\n shapeOutline,\n solidShapeFill,\n} from \"../utils/glimpseShape.ts\";\nimport { createGlimpseParagraphs } from \"../utils/glimpseTextBox.ts\";\n\ntype TreePositionedNode = Extract<PositionedNode, { type: \"tree\" }>;\n\ninterface LayoutNode {\n item: TreeDataItem;\n x: number;\n y: number;\n width: number;\n height: number;\n children: LayoutNode[];\n}\n\nexport function renderTreeNode(\n node: TreePositionedNode,\n ctx: RenderContext,\n): void {\n const layout = node.layout ?? \"vertical\";\n const nodeShape = node.nodeShape ?? \"rect\";\n const nodeWidth = node.nodeWidth ?? 120;\n const nodeHeight = node.nodeHeight ?? 40;\n const levelGap = node.levelGap ?? 60;\n const siblingGap = node.siblingGap ?? 20;\n const connectorStyle = node.connectorStyle ?? {};\n const defaultColor = \"1D4ED8\";\n const defaultTextColor = stripHash(node.textColor) ?? \"FFFFFF\";\n\n // サブツリーの幅/高さを計算\n function calculateSubtreeSize(item: TreeDataItem): {\n width: number;\n height: number;\n } {\n if (!item.children || item.children.length === 0) {\n return { width: nodeWidth, height: nodeHeight };\n }\n\n const childSizes = item.children.map(calculateSubtreeSize);\n\n if (layout === \"vertical\") {\n const childrenWidth =\n childSizes.reduce((sum, s) => sum + s.width, 0) +\n siblingGap * (childSizes.length - 1);\n const childrenHeight = Math.max(...childSizes.map((s) => s.height));\n return {\n width: Math.max(nodeWidth, childrenWidth),\n height: nodeHeight + levelGap + childrenHeight,\n };\n } else {\n const childrenHeight =\n childSizes.reduce((sum, s) => sum + s.height, 0) +\n siblingGap * (childSizes.length - 1);\n const childrenWidth = Math.max(...childSizes.map((s) => s.width));\n return {\n width: nodeWidth + levelGap + childrenWidth,\n height: Math.max(nodeHeight, childrenHeight),\n };\n }\n }\n\n // ツリーレイアウトを計算(原点(0,0)からの相対座標)\n function calculateTreeLayout(\n item: TreeDataItem,\n x: number,\n y: number,\n ): LayoutNode {\n const subtreeSize = calculateSubtreeSize(item);\n const layoutNode: LayoutNode = {\n item,\n x: 0,\n y: 0,\n width: nodeWidth,\n height: nodeHeight,\n children: [],\n };\n\n if (layout === \"vertical\") {\n // ノードを中央上部に配置\n layoutNode.x = x + subtreeSize.width / 2 - nodeWidth / 2;\n layoutNode.y = y;\n\n // 子ノードを配置\n if (item.children && item.children.length > 0) {\n const childSizes = item.children.map(calculateSubtreeSize);\n const totalChildWidth =\n childSizes.reduce((sum, s) => sum + s.width, 0) +\n siblingGap * (childSizes.length - 1);\n let childX = x + subtreeSize.width / 2 - totalChildWidth / 2;\n const childY = y + nodeHeight + levelGap;\n\n for (let i = 0; i < item.children.length; i++) {\n const child = item.children[i];\n const childLayout = calculateTreeLayout(child, childX, childY);\n layoutNode.children.push(childLayout);\n childX += childSizes[i].width + siblingGap;\n }\n }\n } else {\n // horizontal: ノードを左中央に配置\n layoutNode.x = x;\n layoutNode.y = y + subtreeSize.height / 2 - nodeHeight / 2;\n\n // 子ノードを配置\n if (item.children && item.children.length > 0) {\n const childSizes = item.children.map(calculateSubtreeSize);\n const totalChildHeight =\n childSizes.reduce((sum, s) => sum + s.height, 0) +\n siblingGap * (childSizes.length - 1);\n const childX = x + nodeWidth + levelGap;\n let childY = y + subtreeSize.height / 2 - totalChildHeight / 2;\n\n for (let i = 0; i < item.children.length; i++) {\n const child = item.children[i];\n const childLayout = calculateTreeLayout(child, childX, childY);\n layoutNode.children.push(childLayout);\n childY += childSizes[i].height + siblingGap;\n }\n }\n }\n\n return layoutNode;\n }\n\n // 接続線を描画\n function drawConnector(\n parent: LayoutNode,\n child: LayoutNode,\n style: TreeConnectorStyle,\n sf: number,\n ox: number,\n oy: number,\n ) {\n const lineColor = style.color ?? \"333333\";\n const lineWidth = style.width ?? 2;\n\n if (layout === \"vertical\") {\n // 親の下端中央から子の上端中央へ\n const parentCenterX = ox + (parent.x + parent.width / 2) * sf;\n const parentBottomY = oy + (parent.y + parent.height) * sf;\n const childCenterX = ox + (child.x + child.width / 2) * sf;\n const childTopY = oy + child.y * sf;\n const midY = (parentBottomY + childTopY) / 2;\n\n // 垂直線(親から中間点まで)\n addStraightLine(\n ctx,\n {\n x1: parentCenterX,\n y1: parentBottomY,\n x2: parentCenterX,\n y2: midY,\n },\n { color: lineColor, lineWidth: lineWidth * sf },\n );\n\n // 水平線(中間点で)\n const minX = Math.min(parentCenterX, childCenterX);\n const maxX = Math.max(parentCenterX, childCenterX);\n if (maxX > minX) {\n addStraightLine(\n ctx,\n { x1: minX, y1: midY, x2: maxX, y2: midY },\n { color: lineColor, lineWidth: lineWidth * sf },\n );\n }\n\n // 垂直線(中間点から子まで)\n addStraightLine(\n ctx,\n { x1: childCenterX, y1: midY, x2: childCenterX, y2: childTopY },\n { color: lineColor, lineWidth: lineWidth * sf },\n );\n } else {\n // 親の右端中央から子の左端中央へ\n const parentRightX = ox + (parent.x + parent.width) * sf;\n const parentCenterY = oy + (parent.y + parent.height / 2) * sf;\n const childLeftX = ox + child.x * sf;\n const childCenterY = oy + (child.y + child.height / 2) * sf;\n const midX = (parentRightX + childLeftX) / 2;\n\n // 水平線(親から中間点まで)\n addStraightLine(\n ctx,\n { x1: parentRightX, y1: parentCenterY, x2: midX, y2: parentCenterY },\n { color: lineColor, lineWidth: lineWidth * sf },\n );\n\n // 垂直線(中間点で)\n const minY = Math.min(parentCenterY, childCenterY);\n const maxY = Math.max(parentCenterY, childCenterY);\n if (maxY > minY) {\n addStraightLine(\n ctx,\n { x1: midX, y1: minY, x2: midX, y2: maxY },\n { color: lineColor, lineWidth: lineWidth * sf },\n );\n }\n\n // 水平線(中間点から子まで)\n addStraightLine(\n ctx,\n { x1: midX, y1: childCenterY, x2: childLeftX, y2: childCenterY },\n { color: lineColor, lineWidth: lineWidth * sf },\n );\n }\n }\n\n // ノードを描画\n function drawTreeNode(\n layoutNode: LayoutNode,\n shape: TreeNodeShape,\n defaultNodeColor: string,\n sf: number,\n ox: number,\n oy: number,\n ) {\n const color = layoutNode.item.color ?? defaultNodeColor;\n const shapeType = (() => {\n switch (shape) {\n case \"rect\":\n return \"rect\";\n case \"roundRect\":\n return \"roundRect\";\n case \"ellipse\":\n return \"ellipse\";\n }\n })();\n\n const drawX = ox + layoutNode.x * sf;\n const drawY = oy + layoutNode.y * sf;\n const drawW = layoutNode.width * sf;\n const drawH = layoutNode.height * sf;\n\n addGlimpseShape(\n ctx,\n {\n preset: shapeType,\n ...createShapeBoundsInput({ x: drawX, y: drawY, w: drawW, h: drawH }),\n fill: solidShapeFill(color),\n outline: shapeOutline({ color: \"333333\", width: 1 * sf }),\n body: { anchor: \"middle\" },\n paragraphs: createGlimpseParagraphs(\n layoutNode.item.label,\n {\n fontSize: 12 * sf,\n fontFace: \"Noto Sans JP\",\n color: stripHash(layoutNode.item.textColor) ?? defaultTextColor,\n },\n { align: \"center\" },\n ),\n },\n { x: drawX, y: drawY, w: drawW, h: drawH },\n { fillColor: color },\n );\n }\n\n // すべての接続線を再帰的に描画\n function drawAllConnectors(\n layoutNode: LayoutNode,\n sf: number,\n ox: number,\n oy: number,\n ) {\n for (const child of layoutNode.children) {\n drawConnector(layoutNode, child, connectorStyle, sf, ox, oy);\n drawAllConnectors(child, sf, ox, oy);\n }\n }\n\n // すべてのノードを再帰的に描画\n function drawAllNodes(\n layoutNode: LayoutNode,\n sf: number,\n ox: number,\n oy: number,\n ) {\n drawTreeNode(layoutNode, nodeShape, defaultColor, sf, ox, oy);\n for (const child of layoutNode.children) {\n drawAllNodes(child, sf, ox, oy);\n }\n }\n\n // ツリーのサイズを計算\n const treeSize = calculateSubtreeSize(node.data);\n\n // スケール係数を計算(コンテンツ領域基準)\n const { content, scaleFactor } = resolveScaledContentArea(\n node,\n treeSize,\n ctx,\n );\n\n // スケール後のサイズで中央配置オフセットを計算\n const scaledW = treeSize.width * scaleFactor;\n const scaledH = treeSize.height * scaleFactor;\n const offsetX = content.x + (content.w - scaledW) / 2;\n const offsetY = content.y + (content.h - scaledH) / 2;\n\n // レイアウト計算(原点(0,0)からの相対座標)\n const rootLayout = calculateTreeLayout(node.data, 0, 0);\n\n // 描画(接続線を先に、ノードを後に描画)\n drawAllConnectors(rootLayout, scaleFactor, offsetX, offsetY);\n drawAllNodes(rootLayout, scaleFactor, offsetX, offsetY);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"tree.js","names":[],"sources":["../../../src/renderPptx/nodes/tree.ts"],"sourcesContent":["import type {\n PositionedNode,\n TreeDataItem,\n TreeNodeShape,\n TreeConnectorStyle,\n} from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { stripHash } from \"../utils/visualStyle.ts\";\nimport { resolveScaledContentArea } from \"../utils/scaleToFit.ts\";\nimport { addStraightLine } from \"../utils/straightLine.ts\";\nimport {\n addGlimpseShape,\n createShapeBoundsInput,\n shapeOutline,\n solidShapeFill,\n} from \"../utils/glimpseShape.ts\";\nimport { createGlimpseParagraphs } from \"../utils/glimpseTextBox.ts\";\n\ntype TreePositionedNode = Extract<PositionedNode, { type: \"tree\" }>;\n\ninterface LayoutNode {\n item: TreeDataItem;\n x: number;\n y: number;\n width: number;\n height: number;\n children: LayoutNode[];\n}\n\nexport function renderTreeNode(\n node: TreePositionedNode,\n ctx: RenderContext,\n): void {\n const layout = node.layout ?? \"vertical\";\n const nodeShape = node.nodeShape ?? \"rect\";\n const nodeWidth = node.nodeWidth ?? 120;\n const nodeHeight = node.nodeHeight ?? 40;\n const levelGap = node.levelGap ?? 60;\n const siblingGap = node.siblingGap ?? 20;\n const connectorStyle = node.connectorStyle ?? {};\n const defaultColor = \"1D4ED8\";\n const defaultTextColor = stripHash(node.textColor) ?? \"FFFFFF\";\n\n // サブツリーの幅/高さを計算\n function calculateSubtreeSize(item: TreeDataItem): {\n width: number;\n height: number;\n } {\n if (!item.children || item.children.length === 0) {\n return { width: nodeWidth, height: nodeHeight };\n }\n\n const childSizes = item.children.map(calculateSubtreeSize);\n\n if (layout === \"vertical\") {\n const childrenWidth =\n childSizes.reduce((sum, s) => sum + s.width, 0) +\n siblingGap * (childSizes.length - 1);\n const childrenHeight = Math.max(...childSizes.map((s) => s.height));\n return {\n width: Math.max(nodeWidth, childrenWidth),\n height: nodeHeight + levelGap + childrenHeight,\n };\n } else {\n const childrenHeight =\n childSizes.reduce((sum, s) => sum + s.height, 0) +\n siblingGap * (childSizes.length - 1);\n const childrenWidth = Math.max(...childSizes.map((s) => s.width));\n return {\n width: nodeWidth + levelGap + childrenWidth,\n height: Math.max(nodeHeight, childrenHeight),\n };\n }\n }\n\n // ツリーレイアウトを計算(原点(0,0)からの相対座標)\n function calculateTreeLayout(\n item: TreeDataItem,\n x: number,\n y: number,\n ): LayoutNode {\n const subtreeSize = calculateSubtreeSize(item);\n const layoutNode: LayoutNode = {\n item,\n x: 0,\n y: 0,\n width: nodeWidth,\n height: nodeHeight,\n children: [],\n };\n\n if (layout === \"vertical\") {\n // ノードを中央上部に配置\n layoutNode.x = x + subtreeSize.width / 2 - nodeWidth / 2;\n layoutNode.y = y;\n\n // 子ノードを配置\n if (item.children && item.children.length > 0) {\n const childSizes = item.children.map(calculateSubtreeSize);\n const totalChildWidth =\n childSizes.reduce((sum, s) => sum + s.width, 0) +\n siblingGap * (childSizes.length - 1);\n let childX = x + subtreeSize.width / 2 - totalChildWidth / 2;\n const childY = y + nodeHeight + levelGap;\n\n for (let i = 0; i < item.children.length; i++) {\n const child = item.children[i];\n const childLayout = calculateTreeLayout(child, childX, childY);\n layoutNode.children.push(childLayout);\n childX += childSizes[i].width + siblingGap;\n }\n }\n } else {\n // horizontal: ノードを左中央に配置\n layoutNode.x = x;\n layoutNode.y = y + subtreeSize.height / 2 - nodeHeight / 2;\n\n // 子ノードを配置\n if (item.children && item.children.length > 0) {\n const childSizes = item.children.map(calculateSubtreeSize);\n const totalChildHeight =\n childSizes.reduce((sum, s) => sum + s.height, 0) +\n siblingGap * (childSizes.length - 1);\n const childX = x + nodeWidth + levelGap;\n let childY = y + subtreeSize.height / 2 - totalChildHeight / 2;\n\n for (let i = 0; i < item.children.length; i++) {\n const child = item.children[i];\n const childLayout = calculateTreeLayout(child, childX, childY);\n layoutNode.children.push(childLayout);\n childY += childSizes[i].height + siblingGap;\n }\n }\n }\n\n return layoutNode;\n }\n\n // 接続線を描画\n function drawConnector(\n parent: LayoutNode,\n child: LayoutNode,\n style: TreeConnectorStyle,\n sf: number,\n ox: number,\n oy: number,\n ) {\n const lineColor = style.color ?? \"333333\";\n const lineWidth = style.width ?? 2;\n\n if (layout === \"vertical\") {\n // 親の下端中央から子の上端中央へ\n const parentCenterX = ox + (parent.x + parent.width / 2) * sf;\n const parentBottomY = oy + (parent.y + parent.height) * sf;\n const childCenterX = ox + (child.x + child.width / 2) * sf;\n const childTopY = oy + child.y * sf;\n const midY = (parentBottomY + childTopY) / 2;\n\n // 垂直線(親から中間点まで)\n addStraightLine(\n ctx,\n {\n x1: parentCenterX,\n y1: parentBottomY,\n x2: parentCenterX,\n y2: midY,\n },\n { color: lineColor, lineWidth: lineWidth * sf },\n );\n\n // 水平線(中間点で)\n const minX = Math.min(parentCenterX, childCenterX);\n const maxX = Math.max(parentCenterX, childCenterX);\n if (maxX > minX) {\n addStraightLine(\n ctx,\n { x1: minX, y1: midY, x2: maxX, y2: midY },\n { color: lineColor, lineWidth: lineWidth * sf },\n );\n }\n\n // 垂直線(中間点から子まで)\n addStraightLine(\n ctx,\n { x1: childCenterX, y1: midY, x2: childCenterX, y2: childTopY },\n { color: lineColor, lineWidth: lineWidth * sf },\n );\n } else {\n // 親の右端中央から子の左端中央へ\n const parentRightX = ox + (parent.x + parent.width) * sf;\n const parentCenterY = oy + (parent.y + parent.height / 2) * sf;\n const childLeftX = ox + child.x * sf;\n const childCenterY = oy + (child.y + child.height / 2) * sf;\n const midX = (parentRightX + childLeftX) / 2;\n\n // 水平線(親から中間点まで)\n addStraightLine(\n ctx,\n { x1: parentRightX, y1: parentCenterY, x2: midX, y2: parentCenterY },\n { color: lineColor, lineWidth: lineWidth * sf },\n );\n\n // 垂直線(中間点で)\n const minY = Math.min(parentCenterY, childCenterY);\n const maxY = Math.max(parentCenterY, childCenterY);\n if (maxY > minY) {\n addStraightLine(\n ctx,\n { x1: midX, y1: minY, x2: midX, y2: maxY },\n { color: lineColor, lineWidth: lineWidth * sf },\n );\n }\n\n // 水平線(中間点から子まで)\n addStraightLine(\n ctx,\n { x1: midX, y1: childCenterY, x2: childLeftX, y2: childCenterY },\n { color: lineColor, lineWidth: lineWidth * sf },\n );\n }\n }\n\n // ノードを描画\n function drawTreeNode(\n layoutNode: LayoutNode,\n shape: TreeNodeShape,\n defaultNodeColor: string,\n sf: number,\n ox: number,\n oy: number,\n ) {\n const color = layoutNode.item.color ?? defaultNodeColor;\n const shapeType = (() => {\n switch (shape) {\n case \"rect\":\n return \"rect\";\n case \"roundRect\":\n return \"roundRect\";\n case \"ellipse\":\n return \"ellipse\";\n }\n })();\n\n const drawX = ox + layoutNode.x * sf;\n const drawY = oy + layoutNode.y * sf;\n const drawW = layoutNode.width * sf;\n const drawH = layoutNode.height * sf;\n\n addGlimpseShape(\n ctx,\n {\n geometry: { kind: \"preset\", preset: shapeType },\n ...createShapeBoundsInput({ x: drawX, y: drawY, w: drawW, h: drawH }),\n fill: solidShapeFill(color),\n outline: shapeOutline({ color: \"333333\", width: 1 * sf }),\n body: { anchor: \"middle\" },\n paragraphs: createGlimpseParagraphs(\n layoutNode.item.label,\n {\n fontSize: 12 * sf,\n fontFace: \"Noto Sans JP\",\n color: stripHash(layoutNode.item.textColor) ?? defaultTextColor,\n },\n { align: \"center\" },\n ),\n },\n { x: drawX, y: drawY, w: drawW, h: drawH },\n { fillColor: color },\n );\n }\n\n // すべての接続線を再帰的に描画\n function drawAllConnectors(\n layoutNode: LayoutNode,\n sf: number,\n ox: number,\n oy: number,\n ) {\n for (const child of layoutNode.children) {\n drawConnector(layoutNode, child, connectorStyle, sf, ox, oy);\n drawAllConnectors(child, sf, ox, oy);\n }\n }\n\n // すべてのノードを再帰的に描画\n function drawAllNodes(\n layoutNode: LayoutNode,\n sf: number,\n ox: number,\n oy: number,\n ) {\n drawTreeNode(layoutNode, nodeShape, defaultColor, sf, ox, oy);\n for (const child of layoutNode.children) {\n drawAllNodes(child, sf, ox, oy);\n }\n }\n\n // ツリーのサイズを計算\n const treeSize = calculateSubtreeSize(node.data);\n\n // スケール係数を計算(コンテンツ領域基準)\n const { content, scaleFactor } = resolveScaledContentArea(\n node,\n treeSize,\n ctx,\n );\n\n // スケール後のサイズで中央配置オフセットを計算\n const scaledW = treeSize.width * scaleFactor;\n const scaledH = treeSize.height * scaleFactor;\n const offsetX = content.x + (content.w - scaledW) / 2;\n const offsetY = content.y + (content.h - scaledH) / 2;\n\n // レイアウト計算(原点(0,0)からの相対座標)\n const rootLayout = calculateTreeLayout(node.data, 0, 0);\n\n // 描画(接続線を先に、ノードを後に描画)\n drawAllConnectors(rootLayout, scaleFactor, offsetX, offsetY);\n drawAllNodes(rootLayout, scaleFactor, offsetX, offsetY);\n}\n"],"mappings":"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"}
@@ -0,0 +1,381 @@
1
+ import { parseGradient, parseLinearGradient } from "../shared/gradient.js";
2
+ import { EMU_PER_IN, pxToEmu, pxToPt } from "./units.js";
3
+ import { createTextOptions, resolveSubSup } from "./textOptions.js";
4
+ import { addChart, addPicture, addShape, addTable, addTextBox, asEmu, asHundredthPt, asOoxmlAngle, asOoxmlPercent, asPt, createPptx, setSlideBackground } from "@pptx-glimpse/document";
5
+ //#region src/renderPptx/pptxAuthoring.ts
6
+ function cleanHex(color) {
7
+ const hex = color?.replace(/^#/, "").toUpperCase();
8
+ return hex?.length === 3 ? [...hex].map((character) => character.repeat(2)).join("") : hex;
9
+ }
10
+ function toColorInput(color, opacity) {
11
+ const hex = cleanHex(color);
12
+ if (!hex) return void 0;
13
+ return {
14
+ kind: "srgb",
15
+ hex,
16
+ transforms: opacity === void 0 ? void 0 : [{
17
+ kind: "alpha",
18
+ value: asOoxmlPercent(Math.round(opacity * 1e5))
19
+ }]
20
+ };
21
+ }
22
+ function toUnderlineInput(underline) {
23
+ if (underline === void 0 || underline === false) return void 0;
24
+ if (underline === true) return true;
25
+ return {
26
+ style: underline.style,
27
+ color: toColorInput(underline.color)
28
+ };
29
+ }
30
+ function toBaselineInput(subscript, superscript) {
31
+ if (subscript) return {
32
+ type: "percent",
33
+ value: asOoxmlPercent(-4e4)
34
+ };
35
+ if (superscript) return {
36
+ type: "percent",
37
+ value: asOoxmlPercent(3e4)
38
+ };
39
+ }
40
+ function toGlowInput(glow) {
41
+ if (!glow) return void 0;
42
+ return {
43
+ radius: asEmu(Math.round(pxToEmu(glow.size ?? 8))),
44
+ color: toColorInput(glow.color ?? "FFFFFF", glow.opacity ?? .75)
45
+ };
46
+ }
47
+ function toOutlineInput(outline) {
48
+ if (!outline) return void 0;
49
+ return {
50
+ width: asEmu(Math.round(pxToEmu(outline.size ?? 1))),
51
+ color: toColorInput(outline.color ?? "FFFFFF")
52
+ };
53
+ }
54
+ function toCharSpacing(letterSpacingPx) {
55
+ if (letterSpacingPx === void 0) return void 0;
56
+ return Math.round(pxToPt(letterSpacingPx) * 100);
57
+ }
58
+ function toTextGradientInput(value) {
59
+ if (!value) return void 0;
60
+ const linear = parseLinearGradient(value);
61
+ if (!linear) return void 0;
62
+ const dmlAngle = ((linear.angle - 90) % 360 + 360) % 360;
63
+ return {
64
+ gradientType: "linear",
65
+ angle: asOoxmlAngle(Math.round(dmlAngle * 6e4)),
66
+ stops: linear.stops.map((stop) => ({
67
+ position: asOoxmlPercent(Math.round(stop.position * 1e3)),
68
+ color: toColorInput(stop.color)
69
+ }))
70
+ };
71
+ }
72
+ function stripUndefined(value) {
73
+ return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== void 0));
74
+ }
75
+ function resolveUnderline(node, run) {
76
+ if (run?.underline !== void 0) return run.underline;
77
+ if (node.underline !== void 0) return node.underline;
78
+ return run?.href ? true : void 0;
79
+ }
80
+ function buildRunProperties(node, run, gradientFill) {
81
+ const fontSizePx = run?.fontSize ?? node.fontSize ?? 24;
82
+ const subSup = run ? resolveSubSup(run, node) : node;
83
+ return stripUndefined({
84
+ fontFace: run?.fontFamily ?? node.fontFamily ?? "Noto Sans JP",
85
+ fontSize: asPt(pxToPt(fontSizePx)),
86
+ color: gradientFill ? void 0 : toColorInput(run?.color ?? node.color),
87
+ gradientFill,
88
+ bold: run?.bold ?? node.bold,
89
+ italic: run?.italic ?? node.italic,
90
+ underline: toUnderlineInput(resolveUnderline(node, run)),
91
+ strike: run?.strike ?? node.strike,
92
+ baseline: toBaselineInput(subSup.subscript, subSup.superscript),
93
+ highlight: toColorInput(run?.highlight ?? node.highlight),
94
+ glow: toGlowInput(node.glow),
95
+ outline: toOutlineInput(node.outline),
96
+ charSpacing: toCharSpacing(run?.letterSpacing ?? node.letterSpacing)
97
+ });
98
+ }
99
+ function buildParagraphs(node) {
100
+ const gradientFill = toTextGradientInput(node.textGradient);
101
+ const sourceRuns = node.runs && node.runs.length > 0 ? node.runs.map((run) => ({
102
+ text: run.text,
103
+ properties: buildRunProperties(node, run, gradientFill),
104
+ href: run.href
105
+ })) : [{
106
+ text: node.text ?? "",
107
+ properties: buildRunProperties(node, void 0, gradientFill)
108
+ }];
109
+ const paragraphRuns = [[]];
110
+ for (const run of sourceRuns) run.text.replace(/\r*\n/g, "\n").split("\n").forEach((line, index) => {
111
+ if (index > 0) paragraphRuns.push([]);
112
+ paragraphRuns.at(-1)?.push({
113
+ ...run,
114
+ text: line
115
+ });
116
+ });
117
+ const lineHeight = node.lineHeight ?? 1.3;
118
+ const fontSizePx = node.fontSize ?? 24;
119
+ return paragraphRuns.map((runs) => ({
120
+ properties: {
121
+ align: node.textAlign,
122
+ marginLeft: asEmu(0),
123
+ indent: asEmu(0),
124
+ bullet: { type: "none" },
125
+ lineSpacing: asHundredthPt(Math.round(pxToPt(fontSizePx * lineHeight) * 100))
126
+ },
127
+ runs: runs.map((run) => ({
128
+ text: run.text,
129
+ properties: run.properties,
130
+ hyperlink: run.href
131
+ }))
132
+ }));
133
+ }
134
+ function toGradientFill(value, opacity) {
135
+ const gradient = parseGradient(value);
136
+ if (!gradient) return void 0;
137
+ const stops = gradient.value.stops.map((stop) => ({
138
+ position: asOoxmlPercent(Math.round(stop.position * 1e3)),
139
+ color: toColorInput(stop.color, opacity)
140
+ }));
141
+ if (gradient.kind === "linear") {
142
+ const dmlAngle = ((gradient.value.angle - 90) % 360 + 360) % 360;
143
+ return {
144
+ kind: "gradient",
145
+ gradientType: "linear",
146
+ angle: asOoxmlAngle(Math.round(dmlAngle * 6e4)),
147
+ stops
148
+ };
149
+ }
150
+ return {
151
+ kind: "gradient",
152
+ gradientType: "radial",
153
+ centerX: asOoxmlPercent(Math.round(gradient.value.centerX * 1e3)),
154
+ centerY: asOoxmlPercent(Math.round(gradient.value.centerY * 1e3)),
155
+ stops
156
+ };
157
+ }
158
+ function withFillOpacity(fill, opacity) {
159
+ if (!fill || opacity === void 0 || fill.kind !== "solid") return fill;
160
+ return {
161
+ ...fill,
162
+ color: toColorInput(fill.color.hex, opacity)
163
+ };
164
+ }
165
+ function toShadowEffects(shadow) {
166
+ if (!shadow) return void 0;
167
+ const common = {
168
+ blurRadius: asEmu(Math.round((shadow.blur ?? 3) * 12700)),
169
+ distance: asEmu(Math.round((shadow.offset ?? 23e3 / 12700) * 12700)),
170
+ direction: asOoxmlAngle(Math.round((shadow.angle ?? 90) * 6e4)),
171
+ color: toColorInput(shadow.color ?? "000000", shadow.opacity ?? .35)
172
+ };
173
+ return shadow.type === "inner" ? { innerShadow: common } : { outerShadow: {
174
+ ...common,
175
+ alignment: "bl",
176
+ rotateWithShape: true
177
+ } };
178
+ }
179
+ function customGeometry(input) {
180
+ const commands = [];
181
+ let moved = false;
182
+ for (const point of input.points) {
183
+ if ("close" in point) {
184
+ if (moved) commands.push({ kind: "close" });
185
+ continue;
186
+ }
187
+ commands.push({
188
+ kind: moved ? "lineTo" : "moveTo",
189
+ x: Math.round(point.x * EMU_PER_IN),
190
+ y: Math.round(point.y * EMU_PER_IN)
191
+ });
192
+ moved = true;
193
+ }
194
+ if (!moved) return void 0;
195
+ return {
196
+ kind: "custom",
197
+ paths: [{
198
+ width: Math.round(input.width * EMU_PER_IN),
199
+ height: Math.round(input.height * EMU_PER_IN),
200
+ commands
201
+ }]
202
+ };
203
+ }
204
+ function enrichShapeInput(input, options) {
205
+ const preset = input.geometry.kind === "preset" ? input.geometry.preset : "";
206
+ const geometry = options?.customGeometry ? customGeometry(options.customGeometry) ?? input.geometry : preset === "roundRect" && options?.rectRadius !== void 0 ? {
207
+ ...input.geometry,
208
+ adjustValues: { adj: Math.round(options.rectRadius * 5e4) }
209
+ } : input.geometry;
210
+ const gradientFill = options?.backgroundGradient ? toGradientFill(options.backgroundGradient, options.fillOpacity) : void 0;
211
+ const outlineGradient = options?.outlineGradient ? toGradientFill(options.outlineGradient, options.outlineOpacity ?? options.fillOpacity) : void 0;
212
+ const outline = input.outline ? {
213
+ ...input.outline,
214
+ fill: outlineGradient ?? withFillOpacity(input.outline.fill, options?.outlineOpacity),
215
+ dash: options?.dashType ?? input.outline.dash
216
+ } : outlineGradient ? { fill: outlineGradient } : void 0;
217
+ const glow = options?.glow ? {
218
+ radius: asEmu(Math.round(pxToEmu(options.glow.size ?? 8))),
219
+ color: toColorInput(options.glow.color ?? "FFFFFF", options.glow.opacity ?? .75)
220
+ } : input.effects?.glow;
221
+ const effects = {
222
+ ...input.effects,
223
+ ...toShadowEffects(options?.shadow),
224
+ ...glow ? { glow } : {}
225
+ };
226
+ const width = preset === "line" && options?.zeroWidth ? asEmu(0) : input.width;
227
+ let height = preset === "line" && options?.zeroHeight ? asEmu(0) : input.height;
228
+ if (preset === "line" && width === 0 && height === 0) height = asEmu(1);
229
+ return {
230
+ ...input,
231
+ geometry,
232
+ width,
233
+ height,
234
+ fill: gradientFill ?? withFillOpacity(input.fill, options?.fillOpacity),
235
+ outline,
236
+ effects: Object.keys(effects).length > 0 ? effects : void 0,
237
+ flipHorizontal: options?.flipH ?? input.flipHorizontal,
238
+ flipVertical: options?.flipV ?? input.flipVertical
239
+ };
240
+ }
241
+ function applyHyperlinks(input, hyperlinks) {
242
+ if (!hyperlinks?.some(Boolean) || !input.paragraphs) return input;
243
+ let index = 0;
244
+ return {
245
+ ...input,
246
+ paragraphs: input.paragraphs.map((paragraph) => ({
247
+ ...paragraph,
248
+ runs: paragraph.runs.map((run) => ({
249
+ ...run,
250
+ hyperlink: hyperlinks[index++]
251
+ }))
252
+ }))
253
+ };
254
+ }
255
+ var PptxAuthoringRegistry = class {
256
+ currentSource;
257
+ currentSlideHandle;
258
+ useLayoutTextMargins = false;
259
+ textCount = 0;
260
+ shapeCount = 0;
261
+ pictureCount = 0;
262
+ tableCount = 0;
263
+ chartCount = 0;
264
+ constructor() {
265
+ const source = createPptx();
266
+ this.currentSource = source;
267
+ this.currentSlideHandle = source.slides[0]?.handle;
268
+ }
269
+ initialize(source, useLayoutTextMargins = false) {
270
+ this.currentSource = source;
271
+ this.useLayoutTextMargins = useLayoutTextMargins;
272
+ }
273
+ selectSlide(handle) {
274
+ this.currentSlideHandle = handle;
275
+ }
276
+ get source() {
277
+ if (!this.currentSource) throw new Error("glimpse authoring is not initialized");
278
+ return this.currentSource;
279
+ }
280
+ get entries() {
281
+ return (this.source.edits ?? []).flatMap((edit) => (edit.kind === "addTextBox" || edit.kind === "addShape") && "xml" in edit ? [{
282
+ kind: "shape",
283
+ xml: edit.xml
284
+ }] : []);
285
+ }
286
+ replaceSource(source) {
287
+ this.currentSource = source;
288
+ }
289
+ get target() {
290
+ if (!this.currentSlideHandle) throw new Error("glimpse slide is not selected");
291
+ return this.currentSlideHandle;
292
+ }
293
+ register(node) {
294
+ const textOptions = createTextOptions(node);
295
+ this.registerTextBox({
296
+ offsetX: asEmu(Math.round(textOptions.x * EMU_PER_IN)),
297
+ offsetY: asEmu(Math.round(textOptions.y * EMU_PER_IN)),
298
+ width: asEmu(Math.round(textOptions.w * EMU_PER_IN)),
299
+ height: asEmu(Math.round(textOptions.h * EMU_PER_IN)),
300
+ rotation: node.rotate === void 0 ? void 0 : asOoxmlAngle(Math.round(node.rotate * 6e4)),
301
+ body: this.useLayoutTextMargins ? void 0 : {
302
+ anchor: "top",
303
+ marginLeft: asEmu(0),
304
+ marginRight: asEmu(0),
305
+ marginTop: asEmu(0),
306
+ marginBottom: asEmu(0)
307
+ },
308
+ paragraphs: buildParagraphs(node)
309
+ });
310
+ }
311
+ registerTextBox(input, options) {
312
+ const name = options?.name ?? `Text ${++this.textCount}`;
313
+ this.currentSource = addTextBox(this.source, this.target, applyHyperlinks({
314
+ ...input,
315
+ name
316
+ }, options?.hyperlinks));
317
+ }
318
+ registerShape(input, options) {
319
+ const name = options?.name ?? `Shape ${++this.shapeCount}`;
320
+ this.currentSource = addShape(this.source, this.target, enrichShapeInput({
321
+ ...input,
322
+ name
323
+ }, options));
324
+ }
325
+ registerPicture(input, options) {
326
+ const name = options?.name ?? `Picture ${++this.pictureCount}`;
327
+ this.currentSource = addPicture(this.source, this.target, {
328
+ ...input,
329
+ name,
330
+ effects: toShadowEffects(options?.shadow)
331
+ });
332
+ }
333
+ registerTable(input, options) {
334
+ const name = options?.name ?? `Table ${++this.tableCount}`;
335
+ this.currentSource = addTable(this.source, this.target, {
336
+ ...input,
337
+ name
338
+ });
339
+ }
340
+ registerChart(input, options) {
341
+ const name = options?.name ?? `Chart ${++this.chartCount}`;
342
+ this.currentSource = addChart(this.source, this.target, {
343
+ ...input,
344
+ name
345
+ });
346
+ }
347
+ setSlideBackgroundGradient(backgroundGradient, opacity) {
348
+ const fill = toGradientFill(backgroundGradient, opacity);
349
+ if (!fill || fill.kind !== "gradient") return false;
350
+ const background = fill.gradientType === "linear" ? {
351
+ kind: "gradient",
352
+ gradientType: "linear",
353
+ stops: fill.stops,
354
+ angle: fill.angle ?? asOoxmlAngle(0)
355
+ } : {
356
+ kind: "gradient",
357
+ gradientType: "radial",
358
+ stops: fill.stops,
359
+ centerX: fill.centerX,
360
+ centerY: fill.centerY
361
+ };
362
+ this.currentSource = setSlideBackground(this.source, this.target, background);
363
+ return true;
364
+ }
365
+ setSlideBackgroundSolid(color) {
366
+ this.currentSource = setSlideBackground(this.source, this.target, {
367
+ kind: "solid",
368
+ color: toColorInput(color)
369
+ });
370
+ }
371
+ setSlideBackgroundImage(bytes) {
372
+ this.currentSource = setSlideBackground(this.source, this.target, {
373
+ kind: "image",
374
+ bytes
375
+ });
376
+ }
377
+ };
378
+ //#endregion
379
+ export { PptxAuthoringRegistry, cleanHex, toColorInput };
380
+
381
+ //# sourceMappingURL=pptxAuthoring.js.map
@@ -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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"}