@hirokisakabe/pom 9.1.1 → 10.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/dist/buildContext.js +2 -6
  2. package/dist/buildContext.js.map +1 -1
  3. package/dist/buildPptx.d.ts +2 -1
  4. package/dist/buildPptx.d.ts.map +1 -1
  5. package/dist/buildPptx.js +4 -9
  6. package/dist/buildPptx.js.map +1 -1
  7. package/dist/calcYogaLayout/calcYogaLayout.js +1 -1
  8. package/dist/calcYogaLayout/calcYogaLayout.js.map +1 -1
  9. package/dist/index.d.ts +2 -1
  10. package/dist/registry/definitions/icon.js +1 -1
  11. package/dist/registry/definitions/list.js.map +1 -1
  12. package/dist/renderPptx/nodes/chart.js +172 -47
  13. package/dist/renderPptx/nodes/chart.js.map +1 -1
  14. package/dist/renderPptx/nodes/flow.js +98 -90
  15. package/dist/renderPptx/nodes/flow.js.map +1 -1
  16. package/dist/renderPptx/nodes/icon.js +36 -24
  17. package/dist/renderPptx/nodes/icon.js.map +1 -1
  18. package/dist/renderPptx/nodes/image.js +5 -22
  19. package/dist/renderPptx/nodes/image.js.map +1 -1
  20. package/dist/renderPptx/nodes/list.js +94 -111
  21. package/dist/renderPptx/nodes/list.js.map +1 -1
  22. package/dist/renderPptx/nodes/matrix.js +95 -70
  23. package/dist/renderPptx/nodes/matrix.js.map +1 -1
  24. package/dist/renderPptx/nodes/processArrow.js +74 -35
  25. package/dist/renderPptx/nodes/processArrow.js.map +1 -1
  26. package/dist/renderPptx/nodes/pyramid.js +37 -16
  27. package/dist/renderPptx/nodes/pyramid.js.map +1 -1
  28. package/dist/renderPptx/nodes/shape.js +66 -37
  29. package/dist/renderPptx/nodes/shape.js.map +1 -1
  30. package/dist/renderPptx/nodes/svg.js +2 -5
  31. package/dist/renderPptx/nodes/svg.js.map +1 -1
  32. package/dist/renderPptx/nodes/table.js +89 -59
  33. package/dist/renderPptx/nodes/table.js.map +1 -1
  34. package/dist/renderPptx/nodes/text.js +1 -18
  35. package/dist/renderPptx/nodes/text.js.map +1 -1
  36. package/dist/renderPptx/nodes/timeline.js +114 -74
  37. package/dist/renderPptx/nodes/timeline.js.map +1 -1
  38. package/dist/renderPptx/nodes/tree.js +81 -79
  39. package/dist/renderPptx/nodes/tree.js.map +1 -1
  40. package/dist/renderPptx/pptxAuthoring.js +381 -0
  41. package/dist/renderPptx/pptxAuthoring.js.map +1 -0
  42. package/dist/renderPptx/renderPptx.js +158 -128
  43. package/dist/renderPptx/renderPptx.js.map +1 -1
  44. package/dist/renderPptx/textOptions.js +7 -8
  45. package/dist/renderPptx/textOptions.js.map +1 -1
  46. package/dist/renderPptx/units.js +3 -3
  47. package/dist/renderPptx/units.js.map +1 -1
  48. package/dist/renderPptx/utils/backgroundBorder.js +70 -48
  49. package/dist/renderPptx/utils/backgroundBorder.js.map +1 -1
  50. package/dist/renderPptx/utils/contentArea.js +1 -1
  51. package/dist/renderPptx/utils/contentArea.js.map +1 -1
  52. package/dist/renderPptx/utils/glimpsePicture.js +111 -0
  53. package/dist/renderPptx/utils/glimpsePicture.js.map +1 -0
  54. package/dist/renderPptx/utils/glimpseShape.js +71 -0
  55. package/dist/renderPptx/utils/glimpseShape.js.map +1 -0
  56. package/dist/renderPptx/utils/glimpseTextBox.js +107 -0
  57. package/dist/renderPptx/utils/glimpseTextBox.js.map +1 -0
  58. package/dist/renderPptx/utils/straightLine.js +33 -15
  59. package/dist/renderPptx/utils/straightLine.js.map +1 -1
  60. package/dist/renderPptx/utils/visualStyle.js +3 -56
  61. package/dist/renderPptx/utils/visualStyle.js.map +1 -1
  62. package/dist/renderPptx/writablePptx.d.ts +23 -0
  63. package/dist/renderPptx/writablePptx.d.ts.map +1 -0
  64. package/dist/renderPptx/writablePptx.js +74 -0
  65. package/dist/renderPptx/writablePptx.js.map +1 -0
  66. package/dist/shared/measureImage.js.map +1 -1
  67. package/package.json +4 -5
  68. package/dist/renderPptx/glimpseTextBoxes.js +0 -347
  69. package/dist/renderPptx/glimpseTextBoxes.js.map +0 -1
  70. package/dist/renderPptx/glowEffects.js +0 -157
  71. package/dist/renderPptx/glowEffects.js.map +0 -1
  72. package/dist/renderPptx/gradientFills.js +0 -154
  73. package/dist/renderPptx/gradientFills.js.map +0 -1
@@ -1,72 +1,102 @@
1
- import { pxToIn, pxToPt, rectPxToIn } from "../units.js";
1
+ import { pxToEmu, pxToPt } from "../units.js";
2
2
  import { getContentArea } from "../utils/contentArea.js";
3
- import { convertStrike, convertUnderline, resolveSubSup } from "../textOptions.js";
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 tableRows = node.rows.map((row) => row.cells.map((cell) => {
8
- const cellFontFace = cell.fontFamily;
9
- const cellOptions = {
10
- fontSize: pxToPt(cell.fontSize ?? 18),
11
- fontFace: cellFontFace,
12
- color: cell.color,
13
- bold: cell.bold,
14
- italic: cell.italic,
15
- underline: convertUnderline(cell.underline),
16
- strike: convertStrike(cell.strike),
17
- subscript: cell.subscript,
18
- superscript: cell.superscript,
19
- highlight: cell.highlight,
20
- align: cell.textAlign ?? "left",
21
- fill: cell.backgroundColor ? { color: cell.backgroundColor } : void 0,
22
- colspan: cell.colspan,
23
- rowspan: cell.rowspan
24
- };
25
- if (cell.runs && cell.runs.length > 0) return {
26
- text: cell.runs.map((run) => {
27
- const runSubSup = resolveSubSup(run, cell);
28
- return {
29
- text: run.text,
30
- options: {
31
- fontSize: pxToPt(run.fontSize ?? cell.fontSize ?? 18),
32
- fontFace: run.fontFamily ?? cellFontFace,
33
- color: run.color ?? cell.color,
34
- bold: run.bold ?? cell.bold,
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
- fill: cell.backgroundColor ? { color: cell.backgroundColor } : void 0,
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
- text: cell.text,
54
- options: cellOptions
62
+ height: asEmu(Math.round(pxToEmu(Math.max(rowHeights[rowIndex] ?? 0, 1)))),
63
+ cells
55
64
  };
56
- }));
57
- const content = getContentArea(node);
58
- const tableOptions = {
59
- ...rectPxToIn(content),
60
- colW: resolveColumnWidths(node, content.w).map((width) => pxToIn(width)),
61
- rowH: resolveRowHeights(node).map((height) => pxToIn(height)),
62
- margin: 0
63
- };
64
- if (node.cellBorder) tableOptions.border = {
65
- color: node.cellBorder.color ?? "000000",
66
- pt: node.cellBorder.width !== void 0 ? pxToPt(node.cellBorder.width) : 1,
67
- type: node.cellBorder.dashType ?? "solid"
68
- };
69
- ctx.slide.addTable(tableRows, tableOptions);
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 { pxToIn, pxToPt, rectPxToIn } from \"../units.ts\";\nimport {\n convertUnderline,\n convertStrike,\n resolveSubSup,\n} from \"../textOptions.ts\";\nimport { getContentArea } from \"../utils/contentArea.ts\";\n\ntype TablePositionedNode = Extract<PositionedNode, { type: \"table\" }>;\n\nexport function renderTableNode(\n node: TablePositionedNode,\n ctx: RenderContext,\n): void {\n const tableRows = node.rows.map((row) =>\n row.cells.map((cell) => {\n const cellFontFace = cell.fontFamily;\n const cellOptions: Record<string, unknown> = {\n fontSize: pxToPt(cell.fontSize ?? 18),\n fontFace: cellFontFace,\n color: cell.color,\n bold: cell.bold,\n italic: cell.italic,\n underline: convertUnderline(cell.underline),\n strike: convertStrike(cell.strike),\n subscript: cell.subscript,\n superscript: cell.superscript,\n highlight: cell.highlight,\n align: cell.textAlign ?? \"left\",\n fill: cell.backgroundColor\n ? { color: cell.backgroundColor }\n : undefined,\n colspan: cell.colspan,\n rowspan: cell.rowspan,\n };\n\n if (cell.runs && cell.runs.length > 0) {\n const textItems = cell.runs.map((run) => {\n const runSubSup = resolveSubSup(run, cell);\n return {\n text: run.text,\n options: {\n fontSize: pxToPt(run.fontSize ?? cell.fontSize ?? 18),\n fontFace: run.fontFamily ?? cellFontFace,\n color: run.color ?? cell.color,\n bold: run.bold ?? cell.bold,\n italic: run.italic ?? cell.italic,\n underline: convertUnderline(run.underline ?? cell.underline),\n strike: convertStrike(run.strike ?? cell.strike),\n subscript: runSubSup.subscript,\n superscript: runSubSup.superscript,\n highlight: run.highlight ?? cell.highlight,\n ...(run.href ? { hyperlink: { url: run.href } } : {}),\n },\n };\n });\n return {\n text: textItems,\n options: {\n align: cell.textAlign ?? \"left\",\n fill: cell.backgroundColor\n ? { color: cell.backgroundColor }\n : undefined,\n colspan: cell.colspan,\n rowspan: cell.rowspan,\n },\n };\n }\n\n return {\n text: cell.text,\n options: cellOptions,\n };\n }),\n );\n\n const content = getContentArea(node);\n const tableOptions: Record<string, unknown> = {\n ...rectPxToIn(content),\n colW: resolveColumnWidths(node, content.w).map((width) => pxToIn(width)),\n rowH: resolveRowHeights(node).map((height) => pxToIn(height)),\n margin: 0,\n };\n\n if (node.cellBorder) {\n tableOptions.border = {\n color: node.cellBorder.color ?? \"000000\",\n pt:\n node.cellBorder.width !== undefined ? pxToPt(node.cellBorder.width) : 1,\n type: node.cellBorder.dashType ?? \"solid\",\n };\n }\n\n ctx.slide.addTable(tableRows, tableOptions);\n}\n"],"mappings":";;;;;AAgBA,SAAgB,gBACd,MACA,KACM;CACN,MAAM,YAAY,KAAK,KAAK,KAAK,QAC/B,IAAI,MAAM,KAAK,SAAS;EACtB,MAAM,eAAe,KAAK;EAC1B,MAAM,cAAuC;GAC3C,UAAU,OAAO,KAAK,YAAY,EAAE;GACpC,UAAU;GACV,OAAO,KAAK;GACZ,MAAM,KAAK;GACX,QAAQ,KAAK;GACb,WAAW,iBAAiB,KAAK,SAAS;GAC1C,QAAQ,cAAc,KAAK,MAAM;GACjC,WAAW,KAAK;GAChB,aAAa,KAAK;GAClB,WAAW,KAAK;GAChB,OAAO,KAAK,aAAa;GACzB,MAAM,KAAK,kBACP,EAAE,OAAO,KAAK,gBAAgB,IAC9B,KAAA;GACJ,SAAS,KAAK;GACd,SAAS,KAAK;EAChB;EAEA,IAAI,KAAK,QAAQ,KAAK,KAAK,SAAS,GAoBlC,OAAO;GACL,MApBgB,KAAK,KAAK,KAAK,QAAQ;IACvC,MAAM,YAAY,cAAc,KAAK,IAAI;IACzC,OAAO;KACL,MAAM,IAAI;KACV,SAAS;MACP,UAAU,OAAO,IAAI,YAAY,KAAK,YAAY,EAAE;MACpD,UAAU,IAAI,cAAc;MAC5B,OAAO,IAAI,SAAS,KAAK;MACzB,MAAM,IAAI,QAAQ,KAAK;MACvB,QAAQ,IAAI,UAAU,KAAK;MAC3B,WAAW,iBAAiB,IAAI,aAAa,KAAK,SAAS;MAC3D,QAAQ,cAAc,IAAI,UAAU,KAAK,MAAM;MAC/C,WAAW,UAAU;MACrB,aAAa,UAAU;MACvB,WAAW,IAAI,aAAa,KAAK;MACjC,GAAI,IAAI,OAAO,EAAE,WAAW,EAAE,KAAK,IAAI,KAAK,EAAE,IAAI,CAAC;KACrD;IACF;GACF,CAEgB;GACd,SAAS;IACP,OAAO,KAAK,aAAa;IACzB,MAAM,KAAK,kBACP,EAAE,OAAO,KAAK,gBAAgB,IAC9B,KAAA;IACJ,SAAS,KAAK;IACd,SAAS,KAAK;GAChB;EACF;EAGF,OAAO;GACL,MAAM,KAAK;GACX,SAAS;EACX;CACF,CAAC,CACH;CAEA,MAAM,UAAU,eAAe,IAAI;CACnC,MAAM,eAAwC;EAC5C,GAAG,WAAW,OAAO;EACrB,MAAM,oBAAoB,MAAM,QAAQ,CAAC,CAAC,CAAC,KAAK,UAAU,OAAO,KAAK,CAAC;EACvE,MAAM,kBAAkB,IAAI,CAAC,CAAC,KAAK,WAAW,OAAO,MAAM,CAAC;EAC5D,QAAQ;CACV;CAEA,IAAI,KAAK,YACP,aAAa,SAAS;EACpB,OAAO,KAAK,WAAW,SAAS;EAChC,IACE,KAAK,WAAW,UAAU,KAAA,IAAY,OAAO,KAAK,WAAW,KAAK,IAAI;EACxE,MAAM,KAAK,WAAW,YAAY;CACpC;CAGF,IAAI,MAAM,SAAS,WAAW,YAAY;AAC5C"}
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
- const textOptions = createTextOptions(node);
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\";\nimport { createTextOptions } from \"../textOptions.ts\";\n\ntype TextPositionedNode = Extract<PositionedNode, { type: \"text\" }>;\n\nexport function renderTextNode(\n node: TextPositionedNode,\n ctx: RenderContext,\n): void {\n const textOptions = createTextOptions(node);\n const marker = ctx.buildContext.glimpseTextBoxes.register(node);\n\n ctx.slide.addShape(ctx.pptx.ShapeType.rect, {\n x: textOptions.x,\n y: textOptions.y,\n w: textOptions.w,\n h: textOptions.h,\n fill: { color: \"FFFFFF\", transparency: 100 },\n line: { color: \"FFFFFF\", transparency: 100 },\n objectName: marker,\n });\n}\n"],"mappings":";;AAMA,SAAgB,eACd,MACA,KACM;CACN,MAAM,cAAc,kBAAkB,IAAI;CAC1C,MAAM,SAAS,IAAI,aAAa,iBAAiB,SAAS,IAAI;CAE9D,IAAI,MAAM,SAAS,IAAI,KAAK,UAAU,MAAM;EAC1C,GAAG,YAAY;EACf,GAAG,YAAY;EACf,GAAG,YAAY;EACf,GAAG,YAAY;EACf,MAAM;GAAE,OAAO;GAAU,cAAc;EAAI;EAC3C,MAAM;GAAE,OAAO;GAAU,cAAc;EAAI;EAC3C,YAAY;CACd,CAAC;AACH"}
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,9 +1,11 @@
1
- import { pxToIn, pxToPt } from "../units.js";
1
+ import { parseGradient } from "../../shared/gradient.js";
2
2
  import { withContentBounds } from "../utils/contentArea.js";
3
- import { stripHash } from "../utils/visualStyle.js";
3
+ import { addGlimpseTextBox } from "../utils/glimpseTextBox.js";
4
+ import { addGlimpseShape, createShapeBoundsInput, noShapeOutline, solidShapeFill } from "../utils/glimpseShape.js";
5
+ import { addStraightLine } from "../utils/straightLine.js";
4
6
  import { measureTimeline } from "../../calcYogaLayout/measureCompositeNodes.js";
7
+ import { stripHash } from "../utils/visualStyle.js";
5
8
  import { resolveScaledContentArea } from "../utils/scaleToFit.js";
6
- import { registerBackgroundGradient } from "../gradientFills.js";
7
9
  //#region src/renderPptx/nodes/timeline.ts
8
10
  function renderTimelineNode(node, ctx) {
9
11
  const direction = node.direction ?? "horizontal";
@@ -17,7 +19,7 @@ function renderTimelineNode(node, ctx) {
17
19
  title: stripHash(node.titleColor) ?? "1E293B",
18
20
  description: stripHash(node.descriptionColor) ?? "64748B"
19
21
  };
20
- const connectorLineColor = (node.connectorGradient ? registerBackgroundGradient(node.connectorGradient, node.opacity, ctx.buildContext.gradientFills) : void 0) ?? stripHash(node.connectorColor) ?? "E2E8F0";
22
+ const connectorLineColor = stripHash(node.connectorGradient ? parseGradient(node.connectorGradient)?.value.stops[0]?.color : void 0) ?? stripHash(node.connectorColor) ?? "E2E8F0";
21
23
  const fontFace = node.fontFamily ?? "Noto Sans JP";
22
24
  const useColorForDate = node.useColorForDate ?? false;
23
25
  const { content, scaleFactor } = resolveScaledContentArea(node, measureTimeline(node), ctx);
@@ -31,6 +33,8 @@ function renderTimelineNode(node, ctx) {
31
33
  scaleFactor,
32
34
  textColors,
33
35
  connectorLineColor,
36
+ connectorGradient: node.connectorGradient,
37
+ connectorGradientOpacity: node.opacity,
34
38
  fontFace,
35
39
  useColorForDate
36
40
  };
@@ -54,15 +58,16 @@ function renderHorizontalTimeline(node, ctx, items, options) {
54
58
  const inset = Math.min(labelW / 2, node.w / 2);
55
59
  const startX = node.x + inset;
56
60
  const lineLength = node.x + node.w - inset - startX;
57
- ctx.slide.addShape(ctx.pptx.ShapeType.line, {
58
- x: pxToIn(startX),
59
- y: pxToIn(lineY),
60
- w: pxToIn(lineLength),
61
- h: 0,
62
- line: {
63
- color: connectorLineColor,
64
- width: pxToPt(lineWidth)
65
- }
61
+ addStraightLine(ctx, {
62
+ x1: startX,
63
+ y1: lineY,
64
+ x2: startX + lineLength,
65
+ y2: lineY
66
+ }, {
67
+ color: connectorLineColor,
68
+ lineWidth,
69
+ lineGradient: options.connectorGradient,
70
+ lineGradientOpacity: options.connectorGradientOpacity
66
71
  });
67
72
  const dateLabelH = 24 * scaleFactor;
68
73
  const titleLabelH = 24 * scaleFactor;
@@ -76,43 +81,60 @@ function renderHorizontalTimeline(node, ctx, items, options) {
76
81
  const cy = lineY;
77
82
  const color = item.color ?? defaultColor;
78
83
  const dateColor = resolveItemDateColor(item, options);
79
- ctx.slide.addShape(ctx.pptx.ShapeType.ellipse, {
80
- x: pxToIn(cx - nodeRadius),
81
- y: pxToIn(cy - nodeRadius),
82
- w: pxToIn(nodeRadius * 2),
83
- h: pxToIn(nodeRadius * 2),
84
- fill: { color },
85
- line: { type: "none" }
86
- });
87
- ctx.slide.addText(item.date, {
88
- x: pxToIn(cx - labelW / 2),
89
- y: pxToIn(cy - nodeRadius - dateOffset),
90
- w: pxToIn(labelW),
91
- h: pxToIn(dateLabelH),
92
- fontSize: pxToPt(12 * scaleFactor),
84
+ addGlimpseShape(ctx, {
85
+ geometry: {
86
+ kind: "preset",
87
+ preset: "ellipse"
88
+ },
89
+ ...createShapeBoundsInput({
90
+ x: cx - nodeRadius,
91
+ y: cy - nodeRadius,
92
+ w: nodeRadius * 2,
93
+ h: nodeRadius * 2
94
+ }),
95
+ fill: solidShapeFill(color),
96
+ outline: noShapeOutline()
97
+ }, {
98
+ x: cx - nodeRadius,
99
+ y: cy - nodeRadius,
100
+ w: nodeRadius * 2,
101
+ h: nodeRadius * 2
102
+ }, { fillColor: color });
103
+ addGlimpseTextBox(ctx, {
104
+ x: cx - labelW / 2,
105
+ y: cy - nodeRadius - dateOffset,
106
+ w: labelW,
107
+ h: dateLabelH
108
+ }, {
109
+ text: item.date,
110
+ fontSize: 12 * scaleFactor,
93
111
  fontFace,
94
112
  color: dateColor,
95
113
  align: "center",
96
114
  valign: "bottom"
97
115
  });
98
- ctx.slide.addText(item.title, {
99
- x: pxToIn(cx - labelW / 2),
100
- y: pxToIn(cy + nodeRadius + titleGap),
101
- w: pxToIn(labelW),
102
- h: pxToIn(titleLabelH),
103
- fontSize: pxToPt(14 * scaleFactor),
116
+ addGlimpseTextBox(ctx, {
117
+ x: cx - labelW / 2,
118
+ y: cy + nodeRadius + titleGap,
119
+ w: labelW,
120
+ h: titleLabelH
121
+ }, {
122
+ text: item.title,
123
+ fontSize: 14 * scaleFactor,
104
124
  fontFace,
105
125
  color: textColors.title,
106
126
  bold: true,
107
127
  align: "center",
108
128
  valign: "top"
109
129
  });
110
- if (item.description) ctx.slide.addText(item.description, {
111
- x: pxToIn(cx - labelW / 2),
112
- y: pxToIn(cy + nodeRadius + descOffset),
113
- w: pxToIn(labelW),
114
- h: pxToIn(descLabelH),
115
- fontSize: pxToPt(11 * scaleFactor),
130
+ if (item.description) addGlimpseTextBox(ctx, {
131
+ x: cx - labelW / 2,
132
+ y: cy + nodeRadius + descOffset,
133
+ w: labelW,
134
+ h: descLabelH
135
+ }, {
136
+ text: item.description,
137
+ fontSize: 11 * scaleFactor,
116
138
  fontFace,
117
139
  color: textColors.description,
118
140
  align: "center",
@@ -126,15 +148,16 @@ function renderVerticalTimeline(node, ctx, items, options) {
126
148
  const lineX = node.x + 40 * scaleFactor;
127
149
  const startY = node.y + nodeRadius;
128
150
  const lineLength = node.y + node.h - nodeRadius - startY;
129
- ctx.slide.addShape(ctx.pptx.ShapeType.line, {
130
- x: pxToIn(lineX),
131
- y: pxToIn(startY),
132
- w: 0,
133
- h: pxToIn(lineLength),
134
- line: {
135
- color: connectorLineColor,
136
- width: pxToPt(lineWidth)
137
- }
151
+ addStraightLine(ctx, {
152
+ x1: lineX,
153
+ y1: startY,
154
+ x2: lineX,
155
+ y2: startY + lineLength
156
+ }, {
157
+ color: connectorLineColor,
158
+ lineWidth,
159
+ lineGradient: options.connectorGradient,
160
+ lineGradientOpacity: options.connectorGradientOpacity
138
161
  });
139
162
  const labelGap = 16 * scaleFactor;
140
163
  const dateLabelW = 100 * scaleFactor;
@@ -149,43 +172,60 @@ function renderVerticalTimeline(node, ctx, items, options) {
149
172
  const cy = startY + lineLength * progress;
150
173
  const color = item.color ?? defaultColor;
151
174
  const dateColor = resolveItemDateColor(item, options);
152
- ctx.slide.addShape(ctx.pptx.ShapeType.ellipse, {
153
- x: pxToIn(cx - nodeRadius),
154
- y: pxToIn(cy - nodeRadius),
155
- w: pxToIn(nodeRadius * 2),
156
- h: pxToIn(nodeRadius * 2),
157
- fill: { color },
158
- line: { type: "none" }
159
- });
160
- ctx.slide.addText(item.date, {
161
- x: pxToIn(cx + nodeRadius + labelGap),
162
- y: pxToIn(cy - nodeRadius - 4 * scaleFactor),
163
- w: pxToIn(dateLabelW),
164
- h: pxToIn(dateLabelH),
165
- fontSize: pxToPt(12 * scaleFactor),
175
+ addGlimpseShape(ctx, {
176
+ geometry: {
177
+ kind: "preset",
178
+ preset: "ellipse"
179
+ },
180
+ ...createShapeBoundsInput({
181
+ x: cx - nodeRadius,
182
+ y: cy - nodeRadius,
183
+ w: nodeRadius * 2,
184
+ h: nodeRadius * 2
185
+ }),
186
+ fill: solidShapeFill(color),
187
+ outline: noShapeOutline()
188
+ }, {
189
+ x: cx - nodeRadius,
190
+ y: cy - nodeRadius,
191
+ w: nodeRadius * 2,
192
+ h: nodeRadius * 2
193
+ }, { fillColor: color });
194
+ addGlimpseTextBox(ctx, {
195
+ x: cx + nodeRadius + labelGap,
196
+ y: cy - nodeRadius - 4 * scaleFactor,
197
+ w: dateLabelW,
198
+ h: dateLabelH
199
+ }, {
200
+ text: item.date,
201
+ fontSize: 12 * scaleFactor,
166
202
  fontFace,
167
203
  color: dateColor,
168
204
  align: "left",
169
205
  valign: "bottom"
170
206
  });
171
- ctx.slide.addText(item.title, {
172
- x: pxToIn(cx + nodeRadius + labelGap),
173
- y: pxToIn(cy - 4 * scaleFactor),
174
- w: pxToIn(titleLabelW),
175
- h: pxToIn(titleLabelH),
176
- fontSize: pxToPt(14 * scaleFactor),
207
+ addGlimpseTextBox(ctx, {
208
+ x: cx + nodeRadius + labelGap,
209
+ y: cy - 4 * scaleFactor,
210
+ w: titleLabelW,
211
+ h: titleLabelH
212
+ }, {
213
+ text: item.title,
214
+ fontSize: 14 * scaleFactor,
177
215
  fontFace,
178
216
  color: textColors.title,
179
217
  bold: true,
180
218
  align: "left",
181
219
  valign: "top"
182
220
  });
183
- if (item.description) ctx.slide.addText(item.description, {
184
- x: pxToIn(cx + nodeRadius + labelGap),
185
- y: pxToIn(cy + 20 * scaleFactor),
186
- w: pxToIn(descLabelW),
187
- h: pxToIn(descLabelH),
188
- fontSize: pxToPt(11 * scaleFactor),
221
+ if (item.description) addGlimpseTextBox(ctx, {
222
+ x: cx + nodeRadius + labelGap,
223
+ y: cy + 20 * scaleFactor,
224
+ w: descLabelW,
225
+ h: descLabelH
226
+ }, {
227
+ text: item.description,
228
+ fontSize: 11 * scaleFactor,
189
229
  fontFace,
190
230
  color: textColors.description,
191
231
  align: "left",
@@ -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 { pxToIn, pxToPt } from \"../units.ts\";\nimport { measureTimeline } from \"../../calcYogaLayout/measureCompositeNodes.ts\";\nimport { resolveScaledContentArea } from \"../utils/scaleToFit.ts\";\nimport { withContentBounds } from \"../utils/contentArea.ts\";\nimport { registerBackgroundGradient } from \"../gradientFills.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 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 // 軸線色を解決する。connectorGradient が指定されていれば gradient registry に\n // 登録してマーカー色を採用する (後処理で gradFill に置換される)。\n // connectorColor が指定されていればそれを採用、未指定ならデフォルトの E2E8F0。\n const connectorGradientMarker = node.connectorGradient\n ? registerBackgroundGradient(\n node.connectorGradient,\n node.opacity,\n ctx.buildContext.gradientFills,\n )\n : undefined;\n const connectorLineColor =\n connectorGradientMarker ?? stripHash(node.connectorColor) ?? \"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 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 ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(startX),\n y: pxToIn(lineY),\n w: pxToIn(lineLength),\n h: 0,\n line: { color: connectorLineColor, width: pxToPt(lineWidth) },\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 ctx.slide.addShape(ctx.pptx.ShapeType.ellipse, {\n x: pxToIn(cx - nodeRadius),\n y: pxToIn(cy - nodeRadius),\n w: pxToIn(nodeRadius * 2),\n h: pxToIn(nodeRadius * 2),\n fill: { color },\n line: { type: \"none\" as const },\n });\n\n // 日付を上に表示\n ctx.slide.addText(item.date, {\n x: pxToIn(cx - labelW / 2),\n y: pxToIn(cy - nodeRadius - dateOffset),\n w: pxToIn(labelW),\n h: pxToIn(dateLabelH),\n fontSize: pxToPt(12 * scaleFactor),\n fontFace,\n color: dateColor,\n align: \"center\",\n valign: \"bottom\",\n });\n\n // タイトルを下に表示\n ctx.slide.addText(item.title, {\n x: pxToIn(cx - labelW / 2),\n y: pxToIn(cy + nodeRadius + titleGap),\n w: pxToIn(labelW),\n h: pxToIn(titleLabelH),\n fontSize: pxToPt(14 * scaleFactor),\n fontFace,\n color: textColors.title,\n bold: true,\n align: \"center\",\n valign: \"top\",\n });\n\n // 説明を表示\n if (item.description) {\n ctx.slide.addText(item.description, {\n x: pxToIn(cx - labelW / 2),\n y: pxToIn(cy + nodeRadius + descOffset),\n w: pxToIn(labelW),\n h: pxToIn(descLabelH),\n fontSize: pxToPt(11 * scaleFactor),\n fontFace,\n color: textColors.description,\n align: \"center\",\n valign: \"top\",\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 ctx.slide.addShape(ctx.pptx.ShapeType.line, {\n x: pxToIn(lineX),\n y: pxToIn(startY),\n w: 0,\n h: pxToIn(lineLength),\n line: { color: connectorLineColor, width: pxToPt(lineWidth) },\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 ctx.slide.addShape(ctx.pptx.ShapeType.ellipse, {\n x: pxToIn(cx - nodeRadius),\n y: pxToIn(cy - nodeRadius),\n w: pxToIn(nodeRadius * 2),\n h: pxToIn(nodeRadius * 2),\n fill: { color },\n line: { type: \"none\" as const },\n });\n\n // 日付を左上に表示\n ctx.slide.addText(item.date, {\n x: pxToIn(cx + nodeRadius + labelGap),\n y: pxToIn(cy - nodeRadius - 4 * scaleFactor),\n w: pxToIn(dateLabelW),\n h: pxToIn(dateLabelH),\n fontSize: pxToPt(12 * scaleFactor),\n fontFace,\n color: dateColor,\n align: \"left\",\n valign: \"bottom\",\n });\n\n // タイトルを右に表示\n ctx.slide.addText(item.title, {\n x: pxToIn(cx + nodeRadius + labelGap),\n y: pxToIn(cy - 4 * scaleFactor),\n w: pxToIn(titleLabelW),\n h: pxToIn(titleLabelH),\n fontSize: pxToPt(14 * scaleFactor),\n fontFace,\n color: textColors.title,\n bold: true,\n align: \"left\",\n valign: \"top\",\n });\n\n // 説明を表示\n if (item.description) {\n ctx.slide.addText(item.description, {\n x: pxToIn(cx + nodeRadius + labelGap),\n y: pxToIn(cy + 20 * scaleFactor),\n w: pxToIn(descLabelW),\n h: pxToIn(descLabelH),\n fontSize: pxToPt(11 * scaleFactor),\n fontFace,\n color: textColors.description,\n align: \"left\",\n valign: \"top\",\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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"}