@hirokisakabe/pom 9.1.2 → 10.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/autoFit/autoFit.js +55 -15
- package/dist/autoFit/autoFit.js.map +1 -1
- package/dist/autoFit/strategies/reduceFontSize.js.map +1 -1
- package/dist/buildContext.js +5 -3
- package/dist/buildContext.js.map +1 -1
- package/dist/buildPptx.d.ts +5 -3
- package/dist/buildPptx.d.ts.map +1 -1
- package/dist/buildPptx.js +5 -4
- package/dist/buildPptx.js.map +1 -1
- package/dist/calcYogaLayout/calcYogaLayout.js +1 -1
- package/dist/calcYogaLayout/calcYogaLayout.js.map +1 -1
- package/dist/calcYogaLayout/fontLoader.d.ts +13 -0
- package/dist/calcYogaLayout/fontLoader.d.ts.map +1 -0
- package/dist/calcYogaLayout/fontLoader.js +70 -3
- package/dist/calcYogaLayout/fontLoader.js.map +1 -1
- package/dist/calcYogaLayout/measureText.d.ts.map +1 -1
- package/dist/calcYogaLayout/measureText.js +6 -6
- package/dist/calcYogaLayout/measureText.js.map +1 -1
- package/dist/diagnostics.d.ts.map +1 -1
- package/dist/extractSlideMastersAsPptx.d.ts.map +1 -1
- package/dist/extractThemeTokensFromPptx.d.ts +0 -1
- package/dist/extractThemeTokensFromPptx.d.ts.map +1 -1
- package/dist/icons/renderIcon.js +12 -10
- package/dist/icons/renderIcon.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/parseXml/coercionRules.js.map +1 -1
- package/dist/parseXml/parseXml.d.ts +0 -1
- package/dist/parseXml/parseXml.d.ts.map +1 -1
- package/dist/parseXml/parseXml.js.map +1 -1
- package/dist/parseXml/serializeXml.d.ts +0 -1
- package/dist/parseXml/serializeXml.d.ts.map +1 -1
- package/dist/parseXml/serializeXml.js.map +1 -1
- package/dist/registry/definitions/icon.js +1 -1
- package/dist/registry/definitions/list.js +1 -1
- package/dist/registry/definitions/list.js.map +1 -1
- package/dist/registry/definitions/shape.js +1 -1
- package/dist/registry/definitions/shape.js.map +1 -1
- package/dist/registry/definitions/text.js +1 -1
- package/dist/registry/definitions/text.js.map +1 -1
- package/dist/renderPptx/nodes/chart.js +172 -47
- package/dist/renderPptx/nodes/chart.js.map +1 -1
- package/dist/renderPptx/nodes/flow.js +4 -1
- package/dist/renderPptx/nodes/flow.js.map +1 -1
- package/dist/renderPptx/nodes/icon.js +5 -2
- package/dist/renderPptx/nodes/icon.js.map +1 -1
- package/dist/renderPptx/nodes/list.js +7 -9
- package/dist/renderPptx/nodes/list.js.map +1 -1
- package/dist/renderPptx/nodes/matrix.js +4 -1
- package/dist/renderPptx/nodes/matrix.js.map +1 -1
- package/dist/renderPptx/nodes/processArrow.js +8 -2
- package/dist/renderPptx/nodes/processArrow.js.map +1 -1
- package/dist/renderPptx/nodes/pyramid.js +4 -1
- package/dist/renderPptx/nodes/pyramid.js.map +1 -1
- package/dist/renderPptx/nodes/shape.js +5 -2
- package/dist/renderPptx/nodes/shape.js.map +1 -1
- package/dist/renderPptx/nodes/table.js +89 -59
- package/dist/renderPptx/nodes/table.js.map +1 -1
- package/dist/renderPptx/nodes/text.js +1 -18
- package/dist/renderPptx/nodes/text.js.map +1 -1
- package/dist/renderPptx/nodes/timeline.js +9 -3
- package/dist/renderPptx/nodes/timeline.js.map +1 -1
- package/dist/renderPptx/nodes/tree.js +4 -1
- package/dist/renderPptx/nodes/tree.js.map +1 -1
- package/dist/renderPptx/pptxAuthoring.js +381 -0
- package/dist/renderPptx/pptxAuthoring.js.map +1 -0
- package/dist/renderPptx/renderPptx.js +157 -126
- package/dist/renderPptx/renderPptx.js.map +1 -1
- package/dist/renderPptx/textOptions.js +7 -8
- package/dist/renderPptx/textOptions.js.map +1 -1
- package/dist/renderPptx/units.js +3 -3
- package/dist/renderPptx/units.js.map +1 -1
- package/dist/renderPptx/utils/backgroundBorder.js +30 -44
- package/dist/renderPptx/utils/backgroundBorder.js.map +1 -1
- package/dist/renderPptx/utils/contentArea.js +1 -1
- package/dist/renderPptx/utils/contentArea.js.map +1 -1
- package/dist/renderPptx/utils/glimpsePicture.js +3 -22
- package/dist/renderPptx/utils/glimpsePicture.js.map +1 -1
- package/dist/renderPptx/utils/glimpseShape.js +6 -25
- package/dist/renderPptx/utils/glimpseShape.js.map +1 -1
- package/dist/renderPptx/utils/glimpseTextBox.js +41 -60
- package/dist/renderPptx/utils/glimpseTextBox.js.map +1 -1
- package/dist/renderPptx/utils/straightLine.js +5 -2
- package/dist/renderPptx/utils/straightLine.js.map +1 -1
- package/dist/renderPptx/utils/visualStyle.js +3 -33
- package/dist/renderPptx/utils/visualStyle.js.map +1 -1
- package/dist/renderPptx/writablePptx.d.ts +23 -0
- package/dist/renderPptx/writablePptx.d.ts.map +1 -0
- package/dist/renderPptx/writablePptx.js +74 -0
- package/dist/renderPptx/writablePptx.js.map +1 -0
- package/dist/shared/measureImage.js.map +1 -1
- package/dist/types.d.ts +0 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +12 -9
- package/dist/renderPptx/glimpseTextBoxes.js +0 -646
- package/dist/renderPptx/glimpseTextBoxes.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"processArrow.js","names":[],"sources":["../../../src/renderPptx/nodes/processArrow.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { stripHash } from \"../utils/visualStyle.ts\";\nimport { pxToIn } from \"../units.ts\";\nimport { measureProcessArrow } from \"../../calcYogaLayout/measureCompositeNodes.ts\";\nimport { resolveScaledContentArea } from \"../utils/scaleToFit.ts\";\nimport {\n ARROW_DEPTH_RATIO,\n DEFAULT_PROCESS_ARROW_ITEM_WIDTH,\n DEFAULT_PROCESS_ARROW_ITEM_HEIGHT,\n} from \"../../shared/processArrowConstants.ts\";\nimport { withContentBounds } from \"../utils/contentArea.ts\";\nimport {\n addGlimpseShape,\n createShapeBoundsInput,\n noShapeOutline,\n solidShapeFill,\n} from \"../utils/glimpseShape.ts\";\nimport { addGlimpseTextBox } from \"../utils/glimpseTextBox.ts\";\n\ntype ProcessArrowPositionedNode = Extract<\n PositionedNode,\n { type: \"processArrow\" }\n>;\n\nexport function renderProcessArrowNode(\n node: ProcessArrowPositionedNode,\n ctx: RenderContext,\n): void {\n const direction = node.direction ?? \"horizontal\";\n const steps = node.steps;\n const stepCount = steps.length;\n\n if (stepCount === 0) return;\n\n const defaultColor = \"4472C4\"; // PowerPoint標準の青\n const defaultTextColor = \"FFFFFF\";\n const itemWidth = node.itemWidth ?? DEFAULT_PROCESS_ARROW_ITEM_WIDTH;\n const itemHeight = node.itemHeight ?? DEFAULT_PROCESS_ARROW_ITEM_HEIGHT;\n const arrowDepth = itemHeight * ARROW_DEPTH_RATIO;\n const gap = node.gap ?? -arrowDepth;\n\n // スケール係数を計算(コンテンツ領域基準)\n const { content, scaleFactor } = resolveScaledContentArea(\n node,\n measureProcessArrow(node),\n ctx,\n );\n\n const scaledItemWidth = itemWidth * scaleFactor;\n const scaledItemHeight = itemHeight * scaleFactor;\n const scaledGap = gap * scaleFactor;\n const scaledArrowDepth = arrowDepth * scaleFactor;\n\n // コンテンツ領域を使用するための仮想ノードを作成\n const contentNode = withContentBounds(node, content);\n\n if (direction === \"horizontal\") {\n renderHorizontalProcessArrow(\n contentNode,\n ctx,\n steps,\n stepCount,\n scaledItemWidth,\n scaledItemHeight,\n scaledGap,\n scaledArrowDepth,\n defaultColor,\n defaultTextColor,\n scaleFactor,\n );\n } else {\n renderVerticalProcessArrow(\n contentNode,\n ctx,\n steps,\n stepCount,\n scaledItemWidth,\n scaledItemHeight,\n scaledGap,\n scaledArrowDepth,\n defaultColor,\n defaultTextColor,\n scaleFactor,\n );\n }\n}\n\nfunction renderHorizontalProcessArrow(\n node: ProcessArrowPositionedNode,\n ctx: RenderContext,\n steps: ProcessArrowPositionedNode[\"steps\"],\n stepCount: number,\n itemWidth: number,\n itemHeight: number,\n gap: number,\n arrowDepth: number,\n defaultColor: string,\n defaultTextColor: string,\n scaleFactor: number,\n): void {\n const totalWidth = stepCount * itemWidth + (stepCount - 1) * gap;\n const startX = node.x + (node.w - totalWidth) / 2;\n const centerY = node.y + node.h / 2;\n\n steps.forEach((step, index) => {\n const stepX = startX + index * (itemWidth + gap);\n const stepY = centerY - itemHeight / 2;\n const fillColor = stripHash(step.color) ?? defaultColor;\n const textColor = stripHash(step.textColor) ?? defaultTextColor;\n\n // custGeom でシェブロン形状を描画\n const isFirst = index === 0;\n const points = isFirst\n ? [\n // homePlate 風: 左辺フラット、右辺が矢印\n { x: 0, y: 0 },\n { x: pxToIn(itemWidth - arrowDepth), y: 0 },\n { x: pxToIn(itemWidth), y: pxToIn(itemHeight / 2) },\n { x: pxToIn(itemWidth - arrowDepth), y: pxToIn(itemHeight) },\n { x: 0, y: pxToIn(itemHeight) },\n { close: true as const },\n ]\n : [\n // chevron 風: 左辺に切り欠き、右辺が矢印\n { x: 0, y: 0 },\n { x: pxToIn(itemWidth - arrowDepth), y: 0 },\n { x: pxToIn(itemWidth), y: pxToIn(itemHeight / 2) },\n { x: pxToIn(itemWidth - arrowDepth), y: pxToIn(itemHeight) },\n { x: 0, y: pxToIn(itemHeight) },\n { x: pxToIn(arrowDepth), y: pxToIn(itemHeight / 2) },\n { close: true as const },\n ];\n\n addGlimpseShape(\n ctx,\n {\n preset: \"rect\",\n ...createShapeBoundsInput({\n x: stepX,\n y: stepY,\n w: itemWidth,\n h: itemHeight,\n }),\n fill: solidShapeFill(fillColor),\n outline: noShapeOutline(),\n },\n { x: stepX, y: stepY, w: itemWidth, h: itemHeight },\n {\n fillColor,\n customGeometry: {\n width: pxToIn(itemWidth),\n height: pxToIn(itemHeight),\n points,\n },\n },\n );\n\n // テキストを図形の中央(矢印部分を除いた領域)に配置\n const textOffsetLeft = isFirst ? 0 : arrowDepth;\n const textWidth = Math.max(1, itemWidth - arrowDepth - textOffsetLeft);\n\n addGlimpseTextBox(\n ctx,\n {\n x: stepX + textOffsetLeft,\n y: stepY,\n w: textWidth,\n h: itemHeight,\n },\n {\n text: step.label,\n fontSize: (node.fontSize ?? 14) * scaleFactor,\n fontFace: node.fontFamily ?? \"Noto Sans JP\",\n color: textColor,\n bold: node.bold ?? false,\n italic: node.italic,\n underline: node.underline,\n strike: node.strike,\n highlight: node.highlight,\n align: \"center\",\n valign: \"middle\",\n },\n );\n });\n}\n\nfunction renderVerticalProcessArrow(\n node: ProcessArrowPositionedNode,\n ctx: RenderContext,\n steps: ProcessArrowPositionedNode[\"steps\"],\n stepCount: number,\n itemWidth: number,\n itemHeight: number,\n gap: number,\n arrowDepth: number,\n defaultColor: string,\n defaultTextColor: string,\n scaleFactor: number,\n): void {\n const totalHeight = stepCount * itemHeight + (stepCount - 1) * gap;\n const startY = node.y + (node.h - totalHeight) / 2;\n const centerX = node.x + node.w / 2;\n\n steps.forEach((step, index) => {\n const stepX = centerX - itemWidth / 2;\n const stepY = startY + index * (itemHeight + gap);\n const fillColor = stripHash(step.color) ?? defaultColor;\n const textColor = stripHash(step.textColor) ?? defaultTextColor;\n\n const isFirst = index === 0;\n const points = isFirst\n ? [\n // rect 風: 上辺フラット、下辺が矢印\n { x: 0, y: 0 },\n { x: pxToIn(itemWidth), y: 0 },\n { x: pxToIn(itemWidth), y: pxToIn(itemHeight - arrowDepth) },\n { x: pxToIn(itemWidth / 2), y: pxToIn(itemHeight) },\n { x: 0, y: pxToIn(itemHeight - arrowDepth) },\n { close: true as const },\n ]\n : [\n // pentagon 風: 上辺に切り欠き、下辺が矢印\n { x: pxToIn(itemWidth / 2), y: 0 },\n { x: pxToIn(itemWidth), y: pxToIn(arrowDepth) },\n { x: pxToIn(itemWidth), y: pxToIn(itemHeight - arrowDepth) },\n { x: pxToIn(itemWidth / 2), y: pxToIn(itemHeight) },\n { x: 0, y: pxToIn(itemHeight - arrowDepth) },\n { x: 0, y: pxToIn(arrowDepth) },\n { close: true as const },\n ];\n\n addGlimpseShape(\n ctx,\n {\n preset: \"rect\",\n ...createShapeBoundsInput({\n x: stepX,\n y: stepY,\n w: itemWidth,\n h: itemHeight,\n }),\n fill: solidShapeFill(fillColor),\n outline: noShapeOutline(),\n },\n { x: stepX, y: stepY, w: itemWidth, h: itemHeight },\n {\n fillColor,\n customGeometry: {\n width: pxToIn(itemWidth),\n height: pxToIn(itemHeight),\n points,\n },\n },\n );\n\n // テキストを図形の中央(矢印部分を除いた領域)に配置\n const textOffsetTop = isFirst ? 0 : arrowDepth;\n const textHeight = Math.max(1, itemHeight - arrowDepth - textOffsetTop);\n\n addGlimpseTextBox(\n ctx,\n {\n x: stepX,\n y: stepY + textOffsetTop,\n w: itemWidth,\n h: textHeight,\n },\n {\n text: step.label,\n fontSize: (node.fontSize ?? 14) * scaleFactor,\n fontFace: node.fontFamily ?? \"Noto Sans JP\",\n color: textColor,\n bold: node.bold ?? false,\n italic: node.italic,\n underline: node.underline,\n strike: node.strike,\n highlight: node.highlight,\n align: \"center\",\n valign: \"middle\",\n },\n );\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"processArrow.js","names":[],"sources":["../../../src/renderPptx/nodes/processArrow.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { stripHash } from \"../utils/visualStyle.ts\";\nimport { pxToIn } from \"../units.ts\";\nimport { measureProcessArrow } from \"../../calcYogaLayout/measureCompositeNodes.ts\";\nimport { resolveScaledContentArea } from \"../utils/scaleToFit.ts\";\nimport {\n ARROW_DEPTH_RATIO,\n DEFAULT_PROCESS_ARROW_ITEM_WIDTH,\n DEFAULT_PROCESS_ARROW_ITEM_HEIGHT,\n} from \"../../shared/processArrowConstants.ts\";\nimport { withContentBounds } from \"../utils/contentArea.ts\";\nimport {\n addGlimpseShape,\n createShapeBoundsInput,\n noShapeOutline,\n solidShapeFill,\n} from \"../utils/glimpseShape.ts\";\nimport { addGlimpseTextBox } from \"../utils/glimpseTextBox.ts\";\n\ntype ProcessArrowPositionedNode = Extract<\n PositionedNode,\n { type: \"processArrow\" }\n>;\n\nexport function renderProcessArrowNode(\n node: ProcessArrowPositionedNode,\n ctx: RenderContext,\n): void {\n const direction = node.direction ?? \"horizontal\";\n const steps = node.steps;\n const stepCount = steps.length;\n\n if (stepCount === 0) return;\n\n const defaultColor = \"4472C4\"; // PowerPoint標準の青\n const defaultTextColor = \"FFFFFF\";\n const itemWidth = node.itemWidth ?? DEFAULT_PROCESS_ARROW_ITEM_WIDTH;\n const itemHeight = node.itemHeight ?? DEFAULT_PROCESS_ARROW_ITEM_HEIGHT;\n const arrowDepth = itemHeight * ARROW_DEPTH_RATIO;\n const gap = node.gap ?? -arrowDepth;\n\n // スケール係数を計算(コンテンツ領域基準)\n const { content, scaleFactor } = resolveScaledContentArea(\n node,\n measureProcessArrow(node),\n ctx,\n );\n\n const scaledItemWidth = itemWidth * scaleFactor;\n const scaledItemHeight = itemHeight * scaleFactor;\n const scaledGap = gap * scaleFactor;\n const scaledArrowDepth = arrowDepth * scaleFactor;\n\n // コンテンツ領域を使用するための仮想ノードを作成\n const contentNode = withContentBounds(node, content);\n\n if (direction === \"horizontal\") {\n renderHorizontalProcessArrow(\n contentNode,\n ctx,\n steps,\n stepCount,\n scaledItemWidth,\n scaledItemHeight,\n scaledGap,\n scaledArrowDepth,\n defaultColor,\n defaultTextColor,\n scaleFactor,\n );\n } else {\n renderVerticalProcessArrow(\n contentNode,\n ctx,\n steps,\n stepCount,\n scaledItemWidth,\n scaledItemHeight,\n scaledGap,\n scaledArrowDepth,\n defaultColor,\n defaultTextColor,\n scaleFactor,\n );\n }\n}\n\nfunction renderHorizontalProcessArrow(\n node: ProcessArrowPositionedNode,\n ctx: RenderContext,\n steps: ProcessArrowPositionedNode[\"steps\"],\n stepCount: number,\n itemWidth: number,\n itemHeight: number,\n gap: number,\n arrowDepth: number,\n defaultColor: string,\n defaultTextColor: string,\n scaleFactor: number,\n): void {\n const totalWidth = stepCount * itemWidth + (stepCount - 1) * gap;\n const startX = node.x + (node.w - totalWidth) / 2;\n const centerY = node.y + node.h / 2;\n\n steps.forEach((step, index) => {\n const stepX = startX + index * (itemWidth + gap);\n const stepY = centerY - itemHeight / 2;\n const fillColor = stripHash(step.color) ?? defaultColor;\n const textColor = stripHash(step.textColor) ?? defaultTextColor;\n\n // custGeom でシェブロン形状を描画\n const isFirst = index === 0;\n const points = isFirst\n ? [\n // homePlate 風: 左辺フラット、右辺が矢印\n { x: 0, y: 0 },\n { x: pxToIn(itemWidth - arrowDepth), y: 0 },\n { x: pxToIn(itemWidth), y: pxToIn(itemHeight / 2) },\n { x: pxToIn(itemWidth - arrowDepth), y: pxToIn(itemHeight) },\n { x: 0, y: pxToIn(itemHeight) },\n { close: true as const },\n ]\n : [\n // chevron 風: 左辺に切り欠き、右辺が矢印\n { x: 0, y: 0 },\n { x: pxToIn(itemWidth - arrowDepth), y: 0 },\n { x: pxToIn(itemWidth), y: pxToIn(itemHeight / 2) },\n { x: pxToIn(itemWidth - arrowDepth), y: pxToIn(itemHeight) },\n { x: 0, y: pxToIn(itemHeight) },\n { x: pxToIn(arrowDepth), y: pxToIn(itemHeight / 2) },\n { close: true as const },\n ];\n\n addGlimpseShape(\n ctx,\n {\n geometry: { kind: \"preset\", preset: \"rect\" },\n ...createShapeBoundsInput({\n x: stepX,\n y: stepY,\n w: itemWidth,\n h: itemHeight,\n }),\n fill: solidShapeFill(fillColor),\n outline: noShapeOutline(),\n },\n { x: stepX, y: stepY, w: itemWidth, h: itemHeight },\n {\n fillColor,\n customGeometry: {\n width: pxToIn(itemWidth),\n height: pxToIn(itemHeight),\n points,\n },\n },\n );\n\n // テキストを図形の中央(矢印部分を除いた領域)に配置\n const textOffsetLeft = isFirst ? 0 : arrowDepth;\n const textWidth = Math.max(1, itemWidth - arrowDepth - textOffsetLeft);\n\n addGlimpseTextBox(\n ctx,\n {\n x: stepX + textOffsetLeft,\n y: stepY,\n w: textWidth,\n h: itemHeight,\n },\n {\n text: step.label,\n fontSize: (node.fontSize ?? 14) * scaleFactor,\n fontFace: node.fontFamily ?? \"Noto Sans JP\",\n color: textColor,\n bold: node.bold ?? false,\n italic: node.italic,\n underline: node.underline,\n strike: node.strike,\n highlight: node.highlight,\n align: \"center\",\n valign: \"middle\",\n },\n );\n });\n}\n\nfunction renderVerticalProcessArrow(\n node: ProcessArrowPositionedNode,\n ctx: RenderContext,\n steps: ProcessArrowPositionedNode[\"steps\"],\n stepCount: number,\n itemWidth: number,\n itemHeight: number,\n gap: number,\n arrowDepth: number,\n defaultColor: string,\n defaultTextColor: string,\n scaleFactor: number,\n): void {\n const totalHeight = stepCount * itemHeight + (stepCount - 1) * gap;\n const startY = node.y + (node.h - totalHeight) / 2;\n const centerX = node.x + node.w / 2;\n\n steps.forEach((step, index) => {\n const stepX = centerX - itemWidth / 2;\n const stepY = startY + index * (itemHeight + gap);\n const fillColor = stripHash(step.color) ?? defaultColor;\n const textColor = stripHash(step.textColor) ?? defaultTextColor;\n\n const isFirst = index === 0;\n const points = isFirst\n ? [\n // rect 風: 上辺フラット、下辺が矢印\n { x: 0, y: 0 },\n { x: pxToIn(itemWidth), y: 0 },\n { x: pxToIn(itemWidth), y: pxToIn(itemHeight - arrowDepth) },\n { x: pxToIn(itemWidth / 2), y: pxToIn(itemHeight) },\n { x: 0, y: pxToIn(itemHeight - arrowDepth) },\n { close: true as const },\n ]\n : [\n // pentagon 風: 上辺に切り欠き、下辺が矢印\n { x: pxToIn(itemWidth / 2), y: 0 },\n { x: pxToIn(itemWidth), y: pxToIn(arrowDepth) },\n { x: pxToIn(itemWidth), y: pxToIn(itemHeight - arrowDepth) },\n { x: pxToIn(itemWidth / 2), y: pxToIn(itemHeight) },\n { x: 0, y: pxToIn(itemHeight - arrowDepth) },\n { x: 0, y: pxToIn(arrowDepth) },\n { close: true as const },\n ];\n\n addGlimpseShape(\n ctx,\n {\n geometry: { kind: \"preset\", preset: \"rect\" },\n ...createShapeBoundsInput({\n x: stepX,\n y: stepY,\n w: itemWidth,\n h: itemHeight,\n }),\n fill: solidShapeFill(fillColor),\n outline: noShapeOutline(),\n },\n { x: stepX, y: stepY, w: itemWidth, h: itemHeight },\n {\n fillColor,\n customGeometry: {\n width: pxToIn(itemWidth),\n height: pxToIn(itemHeight),\n points,\n },\n },\n );\n\n // テキストを図形の中央(矢印部分を除いた領域)に配置\n const textOffsetTop = isFirst ? 0 : arrowDepth;\n const textHeight = Math.max(1, itemHeight - arrowDepth - textOffsetTop);\n\n addGlimpseTextBox(\n ctx,\n {\n x: stepX,\n y: stepY + textOffsetTop,\n w: itemWidth,\n h: textHeight,\n },\n {\n text: step.label,\n fontSize: (node.fontSize ?? 14) * scaleFactor,\n fontFace: node.fontFamily ?? \"Noto Sans JP\",\n color: textColor,\n bold: node.bold ?? false,\n italic: node.italic,\n underline: node.underline,\n strike: node.strike,\n highlight: node.highlight,\n align: \"center\",\n valign: \"middle\",\n },\n );\n });\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pyramid.js","names":[],"sources":["../../../src/renderPptx/nodes/pyramid.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { stripHash } from \"../utils/visualStyle.ts\";\nimport { pxToIn } from \"../units.ts\";\nimport { measurePyramid } from \"../../calcYogaLayout/measureCompositeNodes.ts\";\nimport { resolveScaledContentArea } from \"../utils/scaleToFit.ts\";\nimport {\n addGlimpseShape,\n createShapeBoundsInput,\n noShapeOutline,\n solidShapeFill,\n} from \"../utils/glimpseShape.ts\";\nimport { addGlimpseTextBox } from \"../utils/glimpseTextBox.ts\";\n\ntype PyramidPositionedNode = Extract<PositionedNode, { type: \"pyramid\" }>;\n\nexport function renderPyramidNode(\n node: PyramidPositionedNode,\n ctx: RenderContext,\n): void {\n const direction = node.direction ?? \"up\";\n const levels = node.levels;\n const levelCount = levels.length;\n\n if (levelCount === 0) return;\n\n const defaultColor = \"4472C4\";\n const defaultTextColor = \"FFFFFF\";\n\n // スケール係数を計算(コンテンツ領域基準)\n const { content, scaleFactor } = resolveScaledContentArea(\n node,\n measurePyramid(node),\n ctx,\n );\n\n const baseWidth = 400 * scaleFactor;\n const layerHeight = 50 * scaleFactor;\n const gap = 2 * scaleFactor;\n\n const totalHeight = levelCount * layerHeight + (levelCount - 1) * gap;\n const startX = content.x + (content.w - baseWidth) / 2;\n const startY = content.y + (content.h - totalHeight) / 2;\n\n for (let i = 0; i < levelCount; i++) {\n const level = levels[i];\n const fillColor = stripHash(level.color) ?? defaultColor;\n const textColor = stripHash(level.textColor) ?? defaultTextColor;\n\n const layerY = startY + i * (layerHeight + gap);\n\n // direction=\"up\": i=0 が最上段(最も狭い=三角形)、i=levelCount-1 が最下段(最も広い)\n // direction=\"down\": i=0 が最上段(最も広い)、i=levelCount-1 が最下段(最も狭い=三角形)\n // 頂点層の上辺は幅0(三角形)、それ以外は台形\n let topWidthRatio: number;\n let bottomWidthRatio: number;\n\n if (direction === \"up\") {\n topWidthRatio = i / levelCount;\n bottomWidthRatio = (i + 1) / levelCount;\n } else {\n topWidthRatio = (levelCount - i) / levelCount;\n bottomWidthRatio = (levelCount - i - 1) / levelCount;\n }\n\n const topWidth = baseWidth * topWidthRatio;\n const bottomWidth = baseWidth * bottomWidthRatio;\n\n const topLeftX = startX + (baseWidth - topWidth) / 2;\n const topRightX = topLeftX + topWidth;\n const bottomLeftX = startX + (baseWidth - bottomWidth) / 2;\n const bottomRightX = bottomLeftX + bottomWidth;\n\n // custGeom のバウンディングボックス\n const bboxX = Math.min(topLeftX, bottomLeftX);\n const bboxW = Math.max(topRightX, bottomRightX) - bboxX;\n\n // points はバウンディングボックス内の相対インチ座標\n const points = [\n { x: pxToIn(topLeftX - bboxX), y: 0 },\n { x: pxToIn(topRightX - bboxX), y: 0 },\n { x: pxToIn(bottomRightX - bboxX), y: pxToIn(layerHeight) },\n { x: pxToIn(bottomLeftX - bboxX), y: pxToIn(layerHeight) },\n { close: true as const },\n ];\n\n addGlimpseShape(\n ctx,\n {\n preset: \"rect\",\n ...createShapeBoundsInput({\n x: bboxX,\n y: layerY,\n w: bboxW,\n h: layerHeight,\n }),\n fill: solidShapeFill(fillColor),\n outline: noShapeOutline(),\n },\n { x: bboxX, y: layerY, w: bboxW, h: layerHeight },\n {\n fillColor,\n customGeometry: {\n width: pxToIn(bboxW),\n height: pxToIn(layerHeight),\n points,\n },\n },\n );\n\n // テキストを図形の中央に重ねて描画\n addGlimpseTextBox(\n ctx,\n {\n x: bboxX,\n y: layerY,\n w: bboxW,\n h: layerHeight,\n },\n {\n text: level.label,\n fontSize: (node.fontSize ?? 14) * scaleFactor,\n fontFace: node.fontFamily ?? \"Noto Sans JP\",\n color: textColor,\n bold: node.bold ?? false,\n align: \"center\",\n valign: \"middle\",\n autoFit: true,\n },\n );\n }\n}\n"],"mappings":";;;;;;;AAgBA,SAAgB,kBACd,MACA,KACM;CACN,MAAM,YAAY,KAAK,aAAa;CACpC,MAAM,SAAS,KAAK;CACpB,MAAM,aAAa,OAAO;CAE1B,IAAI,eAAe,GAAG;CAEtB,MAAM,eAAe;CACrB,MAAM,mBAAmB;CAGzB,MAAM,EAAE,SAAS,gBAAgB,yBAC/B,MACA,eAAe,IAAI,GACnB,GACF;CAEA,MAAM,YAAY,MAAM;CACxB,MAAM,cAAc,KAAK;CACzB,MAAM,MAAM,IAAI;CAEhB,MAAM,cAAc,aAAa,eAAe,aAAa,KAAK;CAClE,MAAM,SAAS,QAAQ,KAAK,QAAQ,IAAI,aAAa;CACrD,MAAM,SAAS,QAAQ,KAAK,QAAQ,IAAI,eAAe;CAEvD,KAAK,IAAI,IAAI,GAAG,IAAI,YAAY,KAAK;EACnC,MAAM,QAAQ,OAAO;EACrB,MAAM,YAAY,UAAU,MAAM,KAAK,KAAK;EAC5C,MAAM,YAAY,UAAU,MAAM,SAAS,KAAK;EAEhD,MAAM,SAAS,SAAS,KAAK,cAAc;EAK3C,IAAI;EACJ,IAAI;EAEJ,IAAI,cAAc,MAAM;GACtB,gBAAgB,IAAI;GACpB,oBAAoB,IAAI,KAAK;EAC/B,OAAO;GACL,iBAAiB,aAAa,KAAK;GACnC,oBAAoB,aAAa,IAAI,KAAK;EAC5C;EAEA,MAAM,WAAW,YAAY;EAC7B,MAAM,cAAc,YAAY;EAEhC,MAAM,WAAW,UAAU,YAAY,YAAY;EACnD,MAAM,YAAY,WAAW;EAC7B,MAAM,cAAc,UAAU,YAAY,eAAe;EACzD,MAAM,eAAe,cAAc;EAGnC,MAAM,QAAQ,KAAK,IAAI,UAAU,WAAW;EAC5C,MAAM,QAAQ,KAAK,IAAI,WAAW,YAAY,IAAI;EAGlD,MAAM,SAAS;GACb;IAAE,GAAG,OAAO,WAAW,KAAK;IAAG,GAAG;GAAE;GACpC;IAAE,GAAG,OAAO,YAAY,KAAK;IAAG,GAAG;GAAE;GACrC;IAAE,GAAG,OAAO,eAAe,KAAK;IAAG,GAAG,OAAO,WAAW;GAAE;GAC1D;IAAE,GAAG,OAAO,cAAc,KAAK;IAAG,GAAG,OAAO,WAAW;GAAE;GACzD,EAAE,OAAO,KAAc;EACzB;EAEA,gBACE,KACA;GACE,QAAQ;
|
|
1
|
+
{"version":3,"file":"pyramid.js","names":[],"sources":["../../../src/renderPptx/nodes/pyramid.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { stripHash } from \"../utils/visualStyle.ts\";\nimport { pxToIn } from \"../units.ts\";\nimport { measurePyramid } from \"../../calcYogaLayout/measureCompositeNodes.ts\";\nimport { resolveScaledContentArea } from \"../utils/scaleToFit.ts\";\nimport {\n addGlimpseShape,\n createShapeBoundsInput,\n noShapeOutline,\n solidShapeFill,\n} from \"../utils/glimpseShape.ts\";\nimport { addGlimpseTextBox } from \"../utils/glimpseTextBox.ts\";\n\ntype PyramidPositionedNode = Extract<PositionedNode, { type: \"pyramid\" }>;\n\nexport function renderPyramidNode(\n node: PyramidPositionedNode,\n ctx: RenderContext,\n): void {\n const direction = node.direction ?? \"up\";\n const levels = node.levels;\n const levelCount = levels.length;\n\n if (levelCount === 0) return;\n\n const defaultColor = \"4472C4\";\n const defaultTextColor = \"FFFFFF\";\n\n // スケール係数を計算(コンテンツ領域基準)\n const { content, scaleFactor } = resolveScaledContentArea(\n node,\n measurePyramid(node),\n ctx,\n );\n\n const baseWidth = 400 * scaleFactor;\n const layerHeight = 50 * scaleFactor;\n const gap = 2 * scaleFactor;\n\n const totalHeight = levelCount * layerHeight + (levelCount - 1) * gap;\n const startX = content.x + (content.w - baseWidth) / 2;\n const startY = content.y + (content.h - totalHeight) / 2;\n\n for (let i = 0; i < levelCount; i++) {\n const level = levels[i];\n const fillColor = stripHash(level.color) ?? defaultColor;\n const textColor = stripHash(level.textColor) ?? defaultTextColor;\n\n const layerY = startY + i * (layerHeight + gap);\n\n // direction=\"up\": i=0 が最上段(最も狭い=三角形)、i=levelCount-1 が最下段(最も広い)\n // direction=\"down\": i=0 が最上段(最も広い)、i=levelCount-1 が最下段(最も狭い=三角形)\n // 頂点層の上辺は幅0(三角形)、それ以外は台形\n let topWidthRatio: number;\n let bottomWidthRatio: number;\n\n if (direction === \"up\") {\n topWidthRatio = i / levelCount;\n bottomWidthRatio = (i + 1) / levelCount;\n } else {\n topWidthRatio = (levelCount - i) / levelCount;\n bottomWidthRatio = (levelCount - i - 1) / levelCount;\n }\n\n const topWidth = baseWidth * topWidthRatio;\n const bottomWidth = baseWidth * bottomWidthRatio;\n\n const topLeftX = startX + (baseWidth - topWidth) / 2;\n const topRightX = topLeftX + topWidth;\n const bottomLeftX = startX + (baseWidth - bottomWidth) / 2;\n const bottomRightX = bottomLeftX + bottomWidth;\n\n // custGeom のバウンディングボックス\n const bboxX = Math.min(topLeftX, bottomLeftX);\n const bboxW = Math.max(topRightX, bottomRightX) - bboxX;\n\n // points はバウンディングボックス内の相対インチ座標\n const points = [\n { x: pxToIn(topLeftX - bboxX), y: 0 },\n { x: pxToIn(topRightX - bboxX), y: 0 },\n { x: pxToIn(bottomRightX - bboxX), y: pxToIn(layerHeight) },\n { x: pxToIn(bottomLeftX - bboxX), y: pxToIn(layerHeight) },\n { close: true as const },\n ];\n\n addGlimpseShape(\n ctx,\n {\n geometry: { kind: \"preset\", preset: \"rect\" },\n ...createShapeBoundsInput({\n x: bboxX,\n y: layerY,\n w: bboxW,\n h: layerHeight,\n }),\n fill: solidShapeFill(fillColor),\n outline: noShapeOutline(),\n },\n { x: bboxX, y: layerY, w: bboxW, h: layerHeight },\n {\n fillColor,\n customGeometry: {\n width: pxToIn(bboxW),\n height: pxToIn(layerHeight),\n points,\n },\n },\n );\n\n // テキストを図形の中央に重ねて描画\n addGlimpseTextBox(\n ctx,\n {\n x: bboxX,\n y: layerY,\n w: bboxW,\n h: layerHeight,\n },\n {\n text: level.label,\n fontSize: (node.fontSize ?? 14) * scaleFactor,\n fontFace: node.fontFamily ?? \"Noto Sans JP\",\n color: textColor,\n bold: node.bold ?? false,\n align: \"center\",\n valign: \"middle\",\n autoFit: true,\n },\n );\n }\n}\n"],"mappings":";;;;;;;AAgBA,SAAgB,kBACd,MACA,KACM;CACN,MAAM,YAAY,KAAK,aAAa;CACpC,MAAM,SAAS,KAAK;CACpB,MAAM,aAAa,OAAO;CAE1B,IAAI,eAAe,GAAG;CAEtB,MAAM,eAAe;CACrB,MAAM,mBAAmB;CAGzB,MAAM,EAAE,SAAS,gBAAgB,yBAC/B,MACA,eAAe,IAAI,GACnB,GACF;CAEA,MAAM,YAAY,MAAM;CACxB,MAAM,cAAc,KAAK;CACzB,MAAM,MAAM,IAAI;CAEhB,MAAM,cAAc,aAAa,eAAe,aAAa,KAAK;CAClE,MAAM,SAAS,QAAQ,KAAK,QAAQ,IAAI,aAAa;CACrD,MAAM,SAAS,QAAQ,KAAK,QAAQ,IAAI,eAAe;CAEvD,KAAK,IAAI,IAAI,GAAG,IAAI,YAAY,KAAK;EACnC,MAAM,QAAQ,OAAO;EACrB,MAAM,YAAY,UAAU,MAAM,KAAK,KAAK;EAC5C,MAAM,YAAY,UAAU,MAAM,SAAS,KAAK;EAEhD,MAAM,SAAS,SAAS,KAAK,cAAc;EAK3C,IAAI;EACJ,IAAI;EAEJ,IAAI,cAAc,MAAM;GACtB,gBAAgB,IAAI;GACpB,oBAAoB,IAAI,KAAK;EAC/B,OAAO;GACL,iBAAiB,aAAa,KAAK;GACnC,oBAAoB,aAAa,IAAI,KAAK;EAC5C;EAEA,MAAM,WAAW,YAAY;EAC7B,MAAM,cAAc,YAAY;EAEhC,MAAM,WAAW,UAAU,YAAY,YAAY;EACnD,MAAM,YAAY,WAAW;EAC7B,MAAM,cAAc,UAAU,YAAY,eAAe;EACzD,MAAM,eAAe,cAAc;EAGnC,MAAM,QAAQ,KAAK,IAAI,UAAU,WAAW;EAC5C,MAAM,QAAQ,KAAK,IAAI,WAAW,YAAY,IAAI;EAGlD,MAAM,SAAS;GACb;IAAE,GAAG,OAAO,WAAW,KAAK;IAAG,GAAG;GAAE;GACpC;IAAE,GAAG,OAAO,YAAY,KAAK;IAAG,GAAG;GAAE;GACrC;IAAE,GAAG,OAAO,eAAe,KAAK;IAAG,GAAG,OAAO,WAAW;GAAE;GAC1D;IAAE,GAAG,OAAO,cAAc,KAAK;IAAG,GAAG,OAAO,WAAW;GAAE;GACzD,EAAE,OAAO,KAAc;EACzB;EAEA,gBACE,KACA;GACE,UAAU;IAAE,MAAM;IAAU,QAAQ;GAAO;GAC3C,GAAG,uBAAuB;IACxB,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;GACL,CAAC;GACD,MAAM,eAAe,SAAS;GAC9B,SAAS,eAAe;EAC1B,GACA;GAAE,GAAG;GAAO,GAAG;GAAQ,GAAG;GAAO,GAAG;EAAY,GAChD;GACE;GACA,gBAAgB;IACd,OAAO,OAAO,KAAK;IACnB,QAAQ,OAAO,WAAW;IAC1B;GACF;EACF,CACF;EAGA,kBACE,KACA;GACE,GAAG;GACH,GAAG;GACH,GAAG;GACH,GAAG;EACL,GACA;GACE,MAAM,MAAM;GACZ,WAAW,KAAK,YAAY,MAAM;GAClC,UAAU,KAAK,cAAc;GAC7B,OAAO;GACP,MAAM,KAAK,QAAQ;GACnB,OAAO;GACP,QAAQ;GACR,SAAS;EACX,CACF;CACF;AACF"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { pxToEmu, pxToPt } from "../units.js";
|
|
2
2
|
import { getContentArea } from "../utils/contentArea.js";
|
|
3
|
-
import { toColorInput } from "../
|
|
3
|
+
import { toColorInput } from "../pptxAuthoring.js";
|
|
4
4
|
import { addGlimpseShape, createShapeBoundsInput, createShapeRotationInput, noneShapeFill, shapeOutline, solidShapeFill } from "../utils/glimpseShape.js";
|
|
5
5
|
import { asEmu, asHundredthPt, asPt } from "@pptx-glimpse/document";
|
|
6
6
|
//#region src/renderPptx/nodes/shape.ts
|
|
@@ -65,7 +65,10 @@ function renderShapeNode(node, ctx) {
|
|
|
65
65
|
const boundsPx = getContentArea(node);
|
|
66
66
|
const fillOpacity = node.fill?.transparency !== void 0 ? 1 - node.fill.transparency / 100 : void 0;
|
|
67
67
|
addGlimpseShape(ctx, {
|
|
68
|
-
|
|
68
|
+
geometry: {
|
|
69
|
+
kind: "preset",
|
|
70
|
+
preset: node.shapeType
|
|
71
|
+
},
|
|
69
72
|
...createShapeBoundsInput(boundsPx),
|
|
70
73
|
rotation: createShapeRotationInput(node.rotate),
|
|
71
74
|
fill: node.fill?.color ? solidShapeFill(node.fill.color) : noneShapeFill(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shape.js","names":[],"sources":["../../../src/renderPptx/nodes/shape.ts"],"sourcesContent":["import {\n asEmu,\n asHundredthPt,\n asPt,\n type AddShapeParagraphInput,\n type AddShapeRunPropertiesInput,\n} from \"@pptx-glimpse/document\";\nimport type { BorderStyle, PositionedNode, Underline } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { pxToPt } from \"../units.ts\";\nimport { getContentArea } from \"../utils/contentArea.ts\";\nimport { pxToEmu } from \"../units.ts\";\nimport { toColorInput } from \"../
|
|
1
|
+
{"version":3,"file":"shape.js","names":[],"sources":["../../../src/renderPptx/nodes/shape.ts"],"sourcesContent":["import {\n asEmu,\n asHundredthPt,\n asPt,\n type AddShapeParagraphInput,\n type AddShapeRunPropertiesInput,\n} from \"@pptx-glimpse/document\";\nimport type { BorderStyle, PositionedNode, Underline } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport { pxToPt } from \"../units.ts\";\nimport { getContentArea } from \"../utils/contentArea.ts\";\nimport { pxToEmu } from \"../units.ts\";\nimport { toColorInput } from \"../pptxAuthoring.ts\";\nimport {\n addGlimpseShape,\n createShapeBoundsInput,\n createShapeRotationInput,\n noneShapeFill,\n shapeOutline,\n solidShapeFill,\n} from \"../utils/glimpseShape.ts\";\n\ntype ShapePositionedNode = Extract<PositionedNode, { type: \"shape\" }>;\n\n/**\n * outline (Text と同じ書式の `outline.size` / `outline.color`) と\n * 既存 `line` 属性 (`line.color` / `line.width` / `line.dashType`) を\n * 1 つの BorderStyle にマージする。\n *\n * フィールド単位のマージで、`outline` の指定があるフィールドは `line` を\n * 上書きするが、`outline` 側で省略されたフィールドは `line` の値を引き継ぎ、\n * `line` にも値が無い場合は Text outline と同じ既定値 (`width: 1pt 相当` /\n * `color: FFFFFF`) を採用する。`dashType` は `outline` に対応フィールドが\n * 無いため `line.dashType` をそのまま使う。\n */\nfunction resolveShapeLine(\n line: BorderStyle | undefined,\n outline: { size?: number; color?: string } | undefined,\n): BorderStyle | undefined {\n if (!outline) return line;\n return {\n color: outline.color ?? line?.color ?? \"FFFFFF\",\n width: outline.size ?? line?.width ?? 1,\n dashType: line?.dashType,\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 buildShapeTextProperties(\n node: ShapePositionedNode,\n): AddShapeRunPropertiesInput {\n return {\n fontFace: node.fontFamily ?? \"Noto Sans JP\",\n fontSize: asPt(pxToPt(node.fontSize ?? 24)),\n color: toColorInput(node.color),\n bold: node.bold,\n italic: node.italic,\n underline: toUnderlineInput(node.underline),\n strike: node.strike,\n baseline: node.subscript\n ? \"subscript\"\n : node.superscript\n ? \"superscript\"\n : undefined,\n highlight: toColorInput(node.highlight),\n };\n}\n\nfunction buildShapeParagraphs(\n node: ShapePositionedNode,\n): AddShapeParagraphInput[] | undefined {\n if (!node.text) return undefined;\n const fontSizePx = node.fontSize ?? 24;\n const lineHeight = node.lineHeight ?? 1.3;\n const properties = buildShapeTextProperties(node);\n return node.text\n .replace(/\\r*\\n/g, \"\\n\")\n .split(\"\\n\")\n .map((text) => ({\n properties: {\n align: node.textAlign ?? \"center\",\n lineSpacing: asHundredthPt(\n Math.round(pxToPt(fontSizePx * lineHeight) * 100),\n ),\n },\n runs: [{ text, properties }],\n }));\n}\n\nexport function renderShapeNode(\n node: ShapePositionedNode,\n ctx: RenderContext,\n): void {\n const lineSpec = resolveShapeLine(node.line, node.outline);\n const boundsPx = getContentArea(node);\n const fillOpacity =\n node.fill?.transparency !== undefined\n ? 1 - node.fill.transparency / 100\n : undefined;\n\n addGlimpseShape(\n ctx,\n {\n geometry: { kind: \"preset\", preset: node.shapeType },\n ...createShapeBoundsInput(boundsPx),\n rotation: createShapeRotationInput(node.rotate),\n fill: node.fill?.color\n ? solidShapeFill(node.fill.color)\n : noneShapeFill(),\n outline: shapeOutline(lineSpec),\n effects: node.glow\n ? {\n glow: {\n radius: asEmu(Math.round(pxToEmu(node.glow.size ?? 8))),\n color: toColorInput(node.glow.color ?? \"FFFFFF\")!,\n },\n }\n : undefined,\n body: node.text\n ? {\n anchor: \"middle\",\n }\n : undefined,\n paragraphs: buildShapeParagraphs(node),\n },\n boundsPx,\n {\n fillColor: node.fill?.color,\n fillOpacity,\n glow: node.glow,\n shadow: node.shadow,\n dashType: lineSpec?.dashType,\n },\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAmCA,SAAS,iBACP,MACA,SACyB;CACzB,IAAI,CAAC,SAAS,OAAO;CACrB,OAAO;EACL,OAAO,QAAQ,SAAS,MAAM,SAAS;EACvC,OAAO,QAAQ,QAAQ,MAAM,SAAS;EACtC,UAAU,MAAM;CAClB;AACF;AAEA,SAAS,iBAAiB,WAAkC;CAC1D,IAAI,cAAc,KAAA,KAAa,cAAc,OAAO,OAAO,KAAA;CAC3D,IAAI,cAAc,MAAM,OAAO;CAC/B,OAAO;EACL,OAAO,UAAU;EACjB,OAAO,aAAa,UAAU,KAAK;CACrC;AACF;AAEA,SAAS,yBACP,MAC4B;CAC5B,OAAO;EACL,UAAU,KAAK,cAAc;EAC7B,UAAU,KAAK,OAAO,KAAK,YAAY,EAAE,CAAC;EAC1C,OAAO,aAAa,KAAK,KAAK;EAC9B,MAAM,KAAK;EACX,QAAQ,KAAK;EACb,WAAW,iBAAiB,KAAK,SAAS;EAC1C,QAAQ,KAAK;EACb,UAAU,KAAK,YACX,cACA,KAAK,cACH,gBACA,KAAA;EACN,WAAW,aAAa,KAAK,SAAS;CACxC;AACF;AAEA,SAAS,qBACP,MACsC;CACtC,IAAI,CAAC,KAAK,MAAM,OAAO,KAAA;CACvB,MAAM,aAAa,KAAK,YAAY;CACpC,MAAM,aAAa,KAAK,cAAc;CACtC,MAAM,aAAa,yBAAyB,IAAI;CAChD,OAAO,KAAK,KACT,QAAQ,UAAU,IAAI,CAAC,CACvB,MAAM,IAAI,CAAC,CACX,KAAK,UAAU;EACd,YAAY;GACV,OAAO,KAAK,aAAa;GACzB,aAAa,cACX,KAAK,MAAM,OAAO,aAAa,UAAU,IAAI,GAAG,CAClD;EACF;EACA,MAAM,CAAC;GAAE;GAAM;EAAW,CAAC;CAC7B,EAAE;AACN;AAEA,SAAgB,gBACd,MACA,KACM;CACN,MAAM,WAAW,iBAAiB,KAAK,MAAM,KAAK,OAAO;CACzD,MAAM,WAAW,eAAe,IAAI;CACpC,MAAM,cACJ,KAAK,MAAM,iBAAiB,KAAA,IACxB,IAAI,KAAK,KAAK,eAAe,MAC7B,KAAA;CAEN,gBACE,KACA;EACE,UAAU;GAAE,MAAM;GAAU,QAAQ,KAAK;EAAU;EACnD,GAAG,uBAAuB,QAAQ;EAClC,UAAU,yBAAyB,KAAK,MAAM;EAC9C,MAAM,KAAK,MAAM,QACb,eAAe,KAAK,KAAK,KAAK,IAC9B,cAAc;EAClB,SAAS,aAAa,QAAQ;EAC9B,SAAS,KAAK,OACV,EACE,MAAM;GACJ,QAAQ,MAAM,KAAK,MAAM,QAAQ,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC;GACtD,OAAO,aAAa,KAAK,KAAK,SAAS,QAAQ;EACjD,EACF,IACA,KAAA;EACJ,MAAM,KAAK,OACP,EACE,QAAQ,SACV,IACA,KAAA;EACJ,YAAY,qBAAqB,IAAI;CACvC,GACA,UACA;EACE,WAAW,KAAK,MAAM;EACtB;EACA,MAAM,KAAK;EACX,QAAQ,KAAK;EACb,UAAU,UAAU;CACtB,CACF;AACF"}
|
|
@@ -1,72 +1,102 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { pxToEmu, pxToPt } from "../units.js";
|
|
2
2
|
import { getContentArea } from "../utils/contentArea.js";
|
|
3
|
-
import {
|
|
3
|
+
import { resolveSubSup } from "../textOptions.js";
|
|
4
|
+
import { cleanHex, toColorInput } from "../pptxAuthoring.js";
|
|
4
5
|
import { resolveColumnWidths, resolveRowHeights } from "../../shared/tableUtils.js";
|
|
6
|
+
import { asEmu, asOoxmlPercent, asPt } from "@pptx-glimpse/document";
|
|
5
7
|
//#region src/renderPptx/nodes/table.ts
|
|
6
8
|
function renderTableNode(node, ctx) {
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
italic: run.italic ?? cell.italic,
|
|
36
|
-
underline: convertUnderline(run.underline ?? cell.underline),
|
|
37
|
-
strike: convertStrike(run.strike ?? cell.strike),
|
|
38
|
-
subscript: runSubSup.subscript,
|
|
39
|
-
superscript: runSubSup.superscript,
|
|
40
|
-
highlight: run.highlight ?? cell.highlight,
|
|
41
|
-
...run.href ? { hyperlink: { url: run.href } } : {}
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
}),
|
|
45
|
-
options: {
|
|
9
|
+
const content = getContentArea(node);
|
|
10
|
+
const rowHeights = resolveRowHeights(node);
|
|
11
|
+
const border = node.cellBorder ? {
|
|
12
|
+
width: asEmu(Math.round(pxToEmu(node.cellBorder.width ?? 1 / .75))),
|
|
13
|
+
color: cleanHex(node.cellBorder.color) ?? "000000",
|
|
14
|
+
dash: toTableDash(node.cellBorder.dashType)
|
|
15
|
+
} : void 0;
|
|
16
|
+
ctx.buildContext.pptxAuthoring.registerTable({
|
|
17
|
+
offsetX: asEmu(Math.round(pxToEmu(content.x))),
|
|
18
|
+
offsetY: asEmu(Math.round(pxToEmu(content.y))),
|
|
19
|
+
width: asEmu(Math.round(pxToEmu(Math.max(content.w, 1)))),
|
|
20
|
+
height: asEmu(Math.round(pxToEmu(Math.max(content.h, 1)))),
|
|
21
|
+
columnWidths: resolveColumnWidths(node, content.w).map((width) => asEmu(Math.round(pxToEmu(Math.max(width, 1))))),
|
|
22
|
+
rows: buildTableRows(node, rowHeights, border)
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function buildTableRows(node, rowHeights, border) {
|
|
26
|
+
const columnCount = node.columns.length;
|
|
27
|
+
const continuationCells = /* @__PURE__ */ new Set();
|
|
28
|
+
return node.rows.map((row, rowIndex) => {
|
|
29
|
+
const cells = Array.from({ length: columnCount });
|
|
30
|
+
for (let columnIndex = 0; columnIndex < columnCount; columnIndex += 1) if (continuationCells.has(`${rowIndex}:${columnIndex}`)) cells[columnIndex] = {};
|
|
31
|
+
let columnIndex = 0;
|
|
32
|
+
for (const cell of row.cells) {
|
|
33
|
+
while (cells[columnIndex] !== void 0) columnIndex += 1;
|
|
34
|
+
cells[columnIndex] = {
|
|
35
|
+
runs: buildTableRuns(cell),
|
|
36
|
+
fill: cleanHex(cell.backgroundColor),
|
|
46
37
|
align: cell.textAlign ?? "left",
|
|
47
|
-
|
|
38
|
+
marginLeft: asEmu(0),
|
|
39
|
+
marginRight: asEmu(0),
|
|
40
|
+
marginTop: asEmu(0),
|
|
41
|
+
marginBottom: asEmu(0),
|
|
42
|
+
borders: border ? {
|
|
43
|
+
top: border,
|
|
44
|
+
right: border,
|
|
45
|
+
bottom: border,
|
|
46
|
+
left: border
|
|
47
|
+
} : void 0,
|
|
48
48
|
colspan: cell.colspan,
|
|
49
49
|
rowspan: cell.rowspan
|
|
50
|
+
};
|
|
51
|
+
const colspan = cell.colspan ?? 1;
|
|
52
|
+
const rowspan = cell.rowspan ?? 1;
|
|
53
|
+
for (let y = rowIndex; y < rowIndex + rowspan; y += 1) for (let x = columnIndex; x < columnIndex + colspan; x += 1) {
|
|
54
|
+
if (x === columnIndex && y === rowIndex) continue;
|
|
55
|
+
continuationCells.add(`${y}:${x}`);
|
|
56
|
+
if (y === rowIndex) cells[x] = {};
|
|
50
57
|
}
|
|
51
|
-
|
|
58
|
+
columnIndex += colspan;
|
|
59
|
+
}
|
|
60
|
+
for (let index = 0; index < cells.length; index += 1) if (cells[index] === void 0) cells[index] = {};
|
|
52
61
|
return {
|
|
53
|
-
|
|
54
|
-
|
|
62
|
+
height: asEmu(Math.round(pxToEmu(Math.max(rowHeights[rowIndex] ?? 0, 1)))),
|
|
63
|
+
cells
|
|
55
64
|
};
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
function buildTableRuns(cell) {
|
|
68
|
+
return (cell.runs?.length ? cell.runs : [{ text: cell.text }]).flatMap((run) => {
|
|
69
|
+
const underline = run.underline ?? cell.underline;
|
|
70
|
+
const subSup = resolveSubSup(run, cell);
|
|
71
|
+
const properties = {
|
|
72
|
+
fontSize: asPt(pxToPt(run.fontSize ?? cell.fontSize ?? 18)),
|
|
73
|
+
fontFace: run.fontFamily ?? cell.fontFamily,
|
|
74
|
+
color: cleanHex(run.color ?? cell.color),
|
|
75
|
+
bold: run.bold ?? cell.bold,
|
|
76
|
+
italic: run.italic ?? cell.italic,
|
|
77
|
+
underline: typeof underline === "object" ? {
|
|
78
|
+
style: underline.style,
|
|
79
|
+
color: toColorInput(underline.color)
|
|
80
|
+
} : Boolean(underline),
|
|
81
|
+
strike: run.strike ?? cell.strike,
|
|
82
|
+
baseline: subSup.subscript ? {
|
|
83
|
+
type: "percent",
|
|
84
|
+
value: asOoxmlPercent(-4e4)
|
|
85
|
+
} : subSup.superscript ? {
|
|
86
|
+
type: "percent",
|
|
87
|
+
value: asOoxmlPercent(3e4)
|
|
88
|
+
} : void 0,
|
|
89
|
+
highlight: toColorInput(run.highlight ?? cell.highlight)
|
|
90
|
+
};
|
|
91
|
+
return run.text.replace(/\r*\n/g, "\n").split("\n").map((line, index) => ({
|
|
92
|
+
text: index === 0 ? line : `\n${line}`,
|
|
93
|
+
properties,
|
|
94
|
+
hyperlink: run.href
|
|
95
|
+
}));
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
function toTableDash(dash) {
|
|
99
|
+
return dash;
|
|
70
100
|
}
|
|
71
101
|
//#endregion
|
|
72
102
|
export { renderTableNode };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table.js","names":[],"sources":["../../../src/renderPptx/nodes/table.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport {\n resolveColumnWidths,\n resolveRowHeights,\n} from \"../../shared/tableUtils.ts\";\nimport {
|
|
1
|
+
{"version":3,"file":"table.js","names":[],"sources":["../../../src/renderPptx/nodes/table.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\nimport {\n resolveColumnWidths,\n resolveRowHeights,\n} from \"../../shared/tableUtils.ts\";\nimport {\n asEmu,\n asOoxmlPercent,\n asPt,\n type AddTableCellInput,\n type AddTableRowInput,\n type AddTableRunInput,\n type SourceDashStyle,\n} from \"@pptx-glimpse/document\";\nimport { pxToEmu, pxToPt } from \"../units.ts\";\nimport { getContentArea } from \"../utils/contentArea.ts\";\nimport { cleanHex, toColorInput } from \"../pptxAuthoring.ts\";\nimport { resolveSubSup } from \"../textOptions.ts\";\n\ntype TablePositionedNode = Extract<PositionedNode, { type: \"table\" }>;\n\nexport function renderTableNode(\n node: TablePositionedNode,\n ctx: RenderContext,\n): void {\n const content = getContentArea(node);\n const rowHeights = resolveRowHeights(node);\n const border = node.cellBorder\n ? {\n width: asEmu(Math.round(pxToEmu(node.cellBorder.width ?? 1 / 0.75))),\n color: cleanHex(node.cellBorder.color) ?? \"000000\",\n dash: toTableDash(node.cellBorder.dashType),\n }\n : undefined;\n ctx.buildContext.pptxAuthoring.registerTable({\n offsetX: asEmu(Math.round(pxToEmu(content.x))),\n offsetY: asEmu(Math.round(pxToEmu(content.y))),\n width: asEmu(Math.round(pxToEmu(Math.max(content.w, 1)))),\n height: asEmu(Math.round(pxToEmu(Math.max(content.h, 1)))),\n columnWidths: resolveColumnWidths(node, content.w).map((width) =>\n asEmu(Math.round(pxToEmu(Math.max(width, 1)))),\n ),\n rows: buildTableRows(node, rowHeights, border),\n });\n}\n\nfunction buildTableRows(\n node: TablePositionedNode,\n rowHeights: number[],\n border:\n | {\n width: ReturnType<typeof asEmu>;\n color: string;\n dash: SourceDashStyle | undefined;\n }\n | undefined,\n): AddTableRowInput[] {\n const columnCount = node.columns.length;\n const continuationCells = new Set<string>();\n return node.rows.map((row, rowIndex) => {\n const cells: AddTableCellInput[] = Array.from({ length: columnCount });\n for (let columnIndex = 0; columnIndex < columnCount; columnIndex += 1) {\n if (continuationCells.has(`${rowIndex}:${columnIndex}`)) {\n cells[columnIndex] = {};\n }\n }\n\n let columnIndex = 0;\n for (const cell of row.cells) {\n while (cells[columnIndex] !== undefined) columnIndex += 1;\n cells[columnIndex] = {\n runs: buildTableRuns(cell),\n fill: cleanHex(cell.backgroundColor),\n align: cell.textAlign ?? \"left\",\n marginLeft: asEmu(0),\n marginRight: asEmu(0),\n marginTop: asEmu(0),\n marginBottom: asEmu(0),\n borders: border\n ? { top: border, right: border, bottom: border, left: border }\n : undefined,\n colspan: cell.colspan,\n rowspan: cell.rowspan,\n };\n const colspan = cell.colspan ?? 1;\n const rowspan = cell.rowspan ?? 1;\n for (let y = rowIndex; y < rowIndex + rowspan; y += 1) {\n for (let x = columnIndex; x < columnIndex + colspan; x += 1) {\n if (x === columnIndex && y === rowIndex) continue;\n continuationCells.add(`${y}:${x}`);\n if (y === rowIndex) cells[x] = {};\n }\n }\n columnIndex += colspan;\n }\n\n for (let index = 0; index < cells.length; index += 1) {\n if (cells[index] === undefined) cells[index] = {};\n }\n return {\n height: asEmu(\n Math.round(pxToEmu(Math.max(rowHeights[rowIndex] ?? 0, 1))),\n ),\n cells,\n };\n });\n}\n\nfunction buildTableRuns(\n cell: TablePositionedNode[\"rows\"][number][\"cells\"][number],\n): AddTableRunInput[] {\n const runs = cell.runs?.length ? cell.runs : [{ text: cell.text }];\n return runs.flatMap((run) => {\n const underline = run.underline ?? cell.underline;\n const subSup = resolveSubSup(run, cell);\n const properties = {\n fontSize: asPt(pxToPt(run.fontSize ?? cell.fontSize ?? 18)),\n fontFace: run.fontFamily ?? cell.fontFamily,\n color: cleanHex(run.color ?? cell.color),\n bold: run.bold ?? cell.bold,\n italic: run.italic ?? cell.italic,\n underline:\n typeof underline === \"object\"\n ? {\n style: underline.style,\n color: toColorInput(underline.color),\n }\n : Boolean(underline),\n strike: run.strike ?? cell.strike,\n baseline: subSup.subscript\n ? { type: \"percent\" as const, value: asOoxmlPercent(-40000) }\n : subSup.superscript\n ? { type: \"percent\" as const, value: asOoxmlPercent(30000) }\n : undefined,\n highlight: toColorInput(run.highlight ?? cell.highlight),\n };\n const lines = run.text.replace(/\\r*\\n/g, \"\\n\").split(\"\\n\");\n return lines.map((line, index) => ({\n text: index === 0 ? line : `\\n${line}`,\n properties,\n hyperlink: run.href,\n }));\n });\n}\n\nfunction toTableDash(\n dash: NonNullable<TablePositionedNode[\"cellBorder\"]>[\"dashType\"],\n): SourceDashStyle | undefined {\n return dash;\n}\n"],"mappings":"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"}
|
|
@@ -1,23 +1,6 @@
|
|
|
1
|
-
import { createTextOptions } from "../textOptions.js";
|
|
2
1
|
//#region src/renderPptx/nodes/text.ts
|
|
3
2
|
function renderTextNode(node, ctx) {
|
|
4
|
-
|
|
5
|
-
const marker = ctx.buildContext.glimpseTextBoxes.register(node);
|
|
6
|
-
ctx.slide.addShape(ctx.pptx.ShapeType.rect, {
|
|
7
|
-
x: textOptions.x,
|
|
8
|
-
y: textOptions.y,
|
|
9
|
-
w: textOptions.w,
|
|
10
|
-
h: textOptions.h,
|
|
11
|
-
fill: {
|
|
12
|
-
color: "FFFFFF",
|
|
13
|
-
transparency: 100
|
|
14
|
-
},
|
|
15
|
-
line: {
|
|
16
|
-
color: "FFFFFF",
|
|
17
|
-
transparency: 100
|
|
18
|
-
},
|
|
19
|
-
objectName: marker
|
|
20
|
-
});
|
|
3
|
+
ctx.buildContext.pptxAuthoring.register(node);
|
|
21
4
|
}
|
|
22
5
|
//#endregion
|
|
23
6
|
export { renderTextNode };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text.js","names":[],"sources":["../../../src/renderPptx/nodes/text.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\
|
|
1
|
+
{"version":3,"file":"text.js","names":[],"sources":["../../../src/renderPptx/nodes/text.ts"],"sourcesContent":["import type { PositionedNode } from \"../../types.ts\";\nimport type { RenderContext } from \"../types.ts\";\n\ntype TextPositionedNode = Extract<PositionedNode, { type: \"text\" }>;\n\nexport function renderTextNode(\n node: TextPositionedNode,\n ctx: RenderContext,\n): void {\n ctx.buildContext.pptxAuthoring.register(node);\n}\n"],"mappings":";AAKA,SAAgB,eACd,MACA,KACM;CACN,IAAI,aAAa,cAAc,SAAS,IAAI;AAC9C"}
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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":"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"}
|
|
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
|
-
|
|
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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"}
|