@hirokisakabe/pom 0.1.6 → 0.1.8

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.
@@ -1,10 +1,122 @@
1
1
  import { createCanvas } from "canvas";
2
2
  const canvas = createCanvas(1, 1);
3
3
  const ctx = canvas.getContext("2d");
4
+ // フォント利用可否のキャッシュ
5
+ const fontAvailabilityCache = new Map();
6
+ /**
7
+ * 指定されたフォントが利用可能かどうかをチェックする
8
+ * 既知のフォントと未知のフォントで同じ幅になるかチェックし、
9
+ * 同じなら「フォントが利用できない」と判定する
10
+ */
11
+ function isFontAvailable(fontFamily, fontSizePx) {
12
+ const cacheKey = `${fontFamily}:${fontSizePx}`;
13
+ const cached = fontAvailabilityCache.get(cacheKey);
14
+ if (cached !== undefined) {
15
+ return cached;
16
+ }
17
+ // テスト文字列(日本語と英語を含む)
18
+ const testString = "あいうABC123";
19
+ // 存在しないフォント名でテスト
20
+ const nonExistentFont = "NonExistentFont_12345_XYZ";
21
+ ctx.font = `${fontSizePx}px "${fontFamily}"`;
22
+ const targetWidth = ctx.measureText(testString).width;
23
+ ctx.font = `${fontSizePx}px "${nonExistentFont}"`;
24
+ const fallbackWidth = ctx.measureText(testString).width;
25
+ // 幅が同じなら、フォントが利用できない(フォールバックされている)
26
+ const isAvailable = Math.abs(targetWidth - fallbackWidth) > 0.1;
27
+ fontAvailabilityCache.set(cacheKey, isAvailable);
28
+ return isAvailable;
29
+ }
30
+ /**
31
+ * 文字がCJK(日本語・中国語・韓国語)文字かどうかを判定する
32
+ */
33
+ function isCJKChar(char) {
34
+ const code = char.codePointAt(0);
35
+ if (code === undefined)
36
+ return false;
37
+ // CJK統合漢字
38
+ if (code >= 0x4e00 && code <= 0x9fff)
39
+ return true;
40
+ // CJK統合漢字拡張A
41
+ if (code >= 0x3400 && code <= 0x4dbf)
42
+ return true;
43
+ // CJK統合漢字拡張B-F
44
+ if (code >= 0x20000 && code <= 0x2ebef)
45
+ return true;
46
+ // ひらがな
47
+ if (code >= 0x3040 && code <= 0x309f)
48
+ return true;
49
+ // カタカナ
50
+ if (code >= 0x30a0 && code <= 0x30ff)
51
+ return true;
52
+ // 全角英数字・記号
53
+ if (code >= 0xff00 && code <= 0xffef)
54
+ return true;
55
+ // CJK記号
56
+ if (code >= 0x3000 && code <= 0x303f)
57
+ return true;
58
+ return false;
59
+ }
60
+ /**
61
+ * フォールバック計算で文字の幅を推定する
62
+ * - CJK文字: 1em(= fontSizePx)
63
+ * - 英数字・半角記号: 0.5em
64
+ */
65
+ function estimateCharWidth(char, fontSizePx) {
66
+ if (isCJKChar(char)) {
67
+ return fontSizePx; // 1em
68
+ }
69
+ return fontSizePx * 0.5; // 0.5em
70
+ }
71
+ /**
72
+ * フォールバック計算でテキスト幅を推定する
73
+ */
74
+ function estimateTextWidth(text, fontSizePx) {
75
+ let width = 0;
76
+ for (const char of text) {
77
+ width += estimateCharWidth(char, fontSizePx);
78
+ }
79
+ return width;
80
+ }
81
+ // 現在のテキスト計測モード
82
+ let currentMode = "auto";
83
+ /**
84
+ * テキスト計測モードを設定する
85
+ */
86
+ export function setTextMeasurementMode(mode) {
87
+ currentMode = mode;
88
+ }
89
+ /**
90
+ * 現在のテキスト計測モードを取得する
91
+ */
92
+ export function getTextMeasurementMode() {
93
+ return currentMode;
94
+ }
4
95
  /**
5
96
  * テキストを折り返し付きでレイアウトし、そのサイズを測定する
6
97
  */
7
98
  export function measureText(text, maxWidthPx, opts) {
99
+ const { fontFamily, fontSizePx } = opts;
100
+ // 計測方法を決定
101
+ const shouldUseFallback = (() => {
102
+ switch (currentMode) {
103
+ case "canvas":
104
+ return false;
105
+ case "fallback":
106
+ return true;
107
+ case "auto":
108
+ return !isFontAvailable(fontFamily, fontSizePx);
109
+ }
110
+ })();
111
+ if (shouldUseFallback) {
112
+ return measureTextFallback(text, maxWidthPx, opts);
113
+ }
114
+ return measureTextCanvas(text, maxWidthPx, opts);
115
+ }
116
+ /**
117
+ * canvas を使ったテキスト計測
118
+ */
119
+ function measureTextCanvas(text, maxWidthPx, opts) {
8
120
  applyFontStyle(opts);
9
121
  // まず改行で段落に分割
10
122
  const paragraphs = text.split("\n");
@@ -44,6 +156,49 @@ export function measureText(text, maxWidthPx, opts) {
44
156
  // 端数切り上げ+余裕分 10px を足す
45
157
  return { widthPx: widthPx + 10, heightPx };
46
158
  }
159
+ /**
160
+ * フォールバック計算を使ったテキスト計測
161
+ */
162
+ function measureTextFallback(text, maxWidthPx, opts) {
163
+ const { fontSizePx } = opts;
164
+ // まず改行で段落に分割
165
+ const paragraphs = text.split("\n");
166
+ const lines = [];
167
+ for (const paragraph of paragraphs) {
168
+ // 空の段落(連続した改行)も1行としてカウント
169
+ if (paragraph === "") {
170
+ lines.push({ widthPx: 0 });
171
+ continue;
172
+ }
173
+ const words = splitForWrap(paragraph);
174
+ let current = "";
175
+ let currentWidth = 0;
176
+ for (const word of words) {
177
+ const candidate = current ? current + word : word;
178
+ const w = estimateTextWidth(candidate, fontSizePx);
179
+ if (w <= maxWidthPx || !current) {
180
+ // まだ詰められる
181
+ current = candidate;
182
+ currentWidth = w;
183
+ }
184
+ else {
185
+ // 折り返す
186
+ lines.push({ widthPx: currentWidth });
187
+ current = word;
188
+ currentWidth = estimateTextWidth(word, fontSizePx);
189
+ }
190
+ }
191
+ if (current) {
192
+ lines.push({ widthPx: currentWidth });
193
+ }
194
+ }
195
+ const lineHeightRatio = opts.lineHeight ?? 1.3;
196
+ const lineHeightPx = fontSizePx * lineHeightRatio;
197
+ const widthPx = lines.length ? Math.max(...lines.map((l) => l.widthPx)) : 0;
198
+ const heightPx = lines.length * lineHeightPx;
199
+ // 端数切り上げ+余裕分 10px を足す
200
+ return { widthPx: widthPx + 10, heightPx };
201
+ }
47
202
  function applyFontStyle(opts) {
48
203
  const { fontFamily, fontSizePx, fontWeight = "normal" } = opts;
49
204
  ctx.font = `${fontWeight} ${fontSizePx}px "${fontFamily}"`;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./types";
2
2
  export * from "./inputSchema";
3
3
  export { buildPptx } from "./buildPptx";
4
+ export type { TextMeasurementMode } from "./buildPptx";
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC"}
@@ -88,6 +88,32 @@ export declare const inputTextNodeSchema: z.ZodObject<{
88
88
  bold: z.ZodOptional<z.ZodBoolean>;
89
89
  fontFamily: z.ZodOptional<z.ZodString>;
90
90
  lineSpacingMultiple: z.ZodOptional<z.ZodNumber>;
91
+ bullet: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
92
+ type: z.ZodOptional<z.ZodEnum<{
93
+ number: "number";
94
+ bullet: "bullet";
95
+ }>>;
96
+ indent: z.ZodOptional<z.ZodNumber>;
97
+ numberType: z.ZodOptional<z.ZodEnum<{
98
+ alphaLcParenBoth: "alphaLcParenBoth";
99
+ alphaLcParenR: "alphaLcParenR";
100
+ alphaLcPeriod: "alphaLcPeriod";
101
+ alphaUcParenBoth: "alphaUcParenBoth";
102
+ alphaUcParenR: "alphaUcParenR";
103
+ alphaUcPeriod: "alphaUcPeriod";
104
+ arabicParenBoth: "arabicParenBoth";
105
+ arabicParenR: "arabicParenR";
106
+ arabicPeriod: "arabicPeriod";
107
+ arabicPlain: "arabicPlain";
108
+ romanLcParenBoth: "romanLcParenBoth";
109
+ romanLcParenR: "romanLcParenR";
110
+ romanLcPeriod: "romanLcPeriod";
111
+ romanUcParenBoth: "romanUcParenBoth";
112
+ romanUcParenR: "romanUcParenR";
113
+ romanUcPeriod: "romanUcPeriod";
114
+ }>>;
115
+ numberStartAt: z.ZodOptional<z.ZodNumber>;
116
+ }, z.core.$strip>]>>;
91
117
  }, z.core.$strip>;
92
118
  export declare const inputImageNodeSchema: z.ZodObject<{
93
119
  w: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodLiteral<"max">, z.ZodString]>>;
@@ -150,7 +176,7 @@ export declare const inputTableNodeSchema: z.ZodObject<{
150
176
  }, z.core.$strip>>;
151
177
  type: z.ZodLiteral<"table">;
152
178
  columns: z.ZodArray<z.ZodObject<{
153
- width: z.ZodNumber;
179
+ width: z.ZodOptional<z.ZodNumber>;
154
180
  }, z.core.$strip>>;
155
181
  rows: z.ZodArray<z.ZodObject<{
156
182
  cells: z.ZodArray<z.ZodObject<{
@@ -409,17 +435,62 @@ export declare const inputShapeNodeSchema: z.ZodObject<{
409
435
  color: z.ZodOptional<z.ZodString>;
410
436
  }, z.core.$strip>>;
411
437
  fontPx: z.ZodOptional<z.ZodNumber>;
412
- fontColor: z.ZodOptional<z.ZodString>;
438
+ color: z.ZodOptional<z.ZodString>;
413
439
  alignText: z.ZodOptional<z.ZodEnum<{
414
440
  right: "right";
415
441
  left: "left";
416
442
  center: "center";
417
443
  }>>;
418
444
  }, z.core.$strip>;
445
+ export declare const inputChartNodeSchema: z.ZodObject<{
446
+ w: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodLiteral<"max">, z.ZodString]>>;
447
+ h: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodLiteral<"max">, z.ZodString]>>;
448
+ minW: z.ZodOptional<z.ZodNumber>;
449
+ maxW: z.ZodOptional<z.ZodNumber>;
450
+ minH: z.ZodOptional<z.ZodNumber>;
451
+ maxH: z.ZodOptional<z.ZodNumber>;
452
+ padding: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodObject<{
453
+ top: z.ZodOptional<z.ZodNumber>;
454
+ right: z.ZodOptional<z.ZodNumber>;
455
+ bottom: z.ZodOptional<z.ZodNumber>;
456
+ left: z.ZodOptional<z.ZodNumber>;
457
+ }, z.core.$strip>]>>;
458
+ backgroundColor: z.ZodOptional<z.ZodString>;
459
+ border: z.ZodOptional<z.ZodObject<{
460
+ color: z.ZodOptional<z.ZodString>;
461
+ width: z.ZodOptional<z.ZodNumber>;
462
+ dashType: z.ZodOptional<z.ZodEnum<{
463
+ solid: "solid";
464
+ dash: "dash";
465
+ dashDot: "dashDot";
466
+ lgDash: "lgDash";
467
+ lgDashDot: "lgDashDot";
468
+ lgDashDotDot: "lgDashDotDot";
469
+ sysDash: "sysDash";
470
+ sysDot: "sysDot";
471
+ }>>;
472
+ }, z.core.$strip>>;
473
+ type: z.ZodLiteral<"chart">;
474
+ chartType: z.ZodEnum<{
475
+ line: "line";
476
+ pie: "pie";
477
+ bar: "bar";
478
+ }>;
479
+ data: z.ZodArray<z.ZodObject<{
480
+ name: z.ZodOptional<z.ZodString>;
481
+ labels: z.ZodArray<z.ZodString>;
482
+ values: z.ZodArray<z.ZodNumber>;
483
+ }, z.core.$strip>>;
484
+ showLegend: z.ZodOptional<z.ZodBoolean>;
485
+ showTitle: z.ZodOptional<z.ZodBoolean>;
486
+ title: z.ZodOptional<z.ZodString>;
487
+ chartColors: z.ZodOptional<z.ZodArray<z.ZodString>>;
488
+ }, z.core.$strip>;
419
489
  export type InputTextNode = z.infer<typeof inputTextNodeSchema>;
420
490
  export type InputImageNode = z.infer<typeof inputImageNodeSchema>;
421
491
  export type InputTableNode = z.infer<typeof inputTableNodeSchema>;
422
492
  export type InputShapeNode = z.infer<typeof inputShapeNodeSchema>;
493
+ export type InputChartNode = z.infer<typeof inputChartNodeSchema>;
423
494
  export type InputBoxNode = InputBaseNode & {
424
495
  type: "box";
425
496
  children: InputPOMNode;
@@ -438,7 +509,7 @@ export type InputHStackNode = InputBaseNode & {
438
509
  alignItems?: AlignItems;
439
510
  justifyContent?: JustifyContent;
440
511
  };
441
- export type InputPOMNode = InputTextNode | InputImageNode | InputTableNode | InputBoxNode | InputVStackNode | InputHStackNode | InputShapeNode;
512
+ export type InputPOMNode = InputTextNode | InputImageNode | InputTableNode | InputBoxNode | InputVStackNode | InputHStackNode | InputShapeNode | InputChartNode;
442
513
  export declare const inputBoxNodeSchema: z.ZodType<InputBoxNode>;
443
514
  export declare const inputVStackNodeSchema: z.ZodType<InputVStackNode>;
444
515
  export declare const inputHStackNodeSchema: z.ZodType<InputHStackNode>;
@@ -472,10 +543,7 @@ export declare const inputMasterSlideOptionsSchema: z.ZodObject<{
472
543
  }>;
473
544
  }, z.core.$strip>>;
474
545
  date: z.ZodOptional<z.ZodObject<{
475
- format: z.ZodEnum<{
476
- "YYYY/MM/DD": "YYYY/MM/DD";
477
- locale: "locale";
478
- }>;
546
+ value: z.ZodString;
479
547
  }, z.core.$strip>>;
480
548
  }, z.core.$strip>;
481
549
  export type InputMasterSlideOptions = z.infer<typeof inputMasterSlideOptionsSchema>;
@@ -1 +1 @@
1
- {"version":3,"file":"inputSchema.d.ts","sourceRoot":"","sources":["../src/inputSchema.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"inputSchema.d.ts","sourceRoot":"","sources":["../src/inputSchema.ts"],"names":[],"mappings":"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"}
@@ -17,7 +17,7 @@
17
17
  * ```
18
18
  */
19
19
  import { z } from "zod";
20
- import { lengthSchema, paddingSchema, borderStyleSchema, fillStyleSchema, shadowStyleSchema, alignItemsSchema, justifyContentSchema, shapeTypeSchema, tableColumnSchema, tableRowSchema, pageNumberPositionSchema, dateFormatSchema, } from "./types";
20
+ import { lengthSchema, paddingSchema, borderStyleSchema, fillStyleSchema, shadowStyleSchema, alignItemsSchema, justifyContentSchema, shapeTypeSchema, tableColumnSchema, tableRowSchema, pageNumberPositionSchema, chartTypeSchema, chartDataSchema, bulletOptionsSchema, } from "./types";
21
21
  // ===== Base Node Schema =====
22
22
  export const inputBaseNodeSchema = z.object({
23
23
  w: lengthSchema.optional(),
@@ -40,6 +40,7 @@ export const inputTextNodeSchema = inputBaseNodeSchema.extend({
40
40
  bold: z.boolean().optional(),
41
41
  fontFamily: z.string().optional(),
42
42
  lineSpacingMultiple: z.number().optional(),
43
+ bullet: z.union([z.boolean(), bulletOptionsSchema]).optional(),
43
44
  });
44
45
  export const inputImageNodeSchema = inputBaseNodeSchema.extend({
45
46
  type: z.literal("image"),
@@ -59,9 +60,18 @@ export const inputShapeNodeSchema = inputBaseNodeSchema.extend({
59
60
  line: borderStyleSchema.optional(),
60
61
  shadow: shadowStyleSchema.optional(),
61
62
  fontPx: z.number().optional(),
62
- fontColor: z.string().optional(),
63
+ color: z.string().optional(),
63
64
  alignText: z.enum(["left", "center", "right"]).optional(),
64
65
  });
66
+ export const inputChartNodeSchema = inputBaseNodeSchema.extend({
67
+ type: z.literal("chart"),
68
+ chartType: chartTypeSchema,
69
+ data: z.array(chartDataSchema),
70
+ showLegend: z.boolean().optional(),
71
+ showTitle: z.boolean().optional(),
72
+ title: z.string().optional(),
73
+ chartColors: z.array(z.string()).optional(),
74
+ });
65
75
  // ===== Recursive Node Schemas =====
66
76
  const inputBoxNodeSchemaBase = inputBaseNodeSchema.extend({
67
77
  type: z.literal("box"),
@@ -110,6 +120,7 @@ export const inputPomNodeSchema = z.lazy(() => z.discriminatedUnion("type", [
110
120
  inputVStackNodeSchemaBase,
111
121
  inputHStackNodeSchemaBase,
112
122
  inputShapeNodeSchema,
123
+ inputChartNodeSchema,
113
124
  ]));
114
125
  // ===== Master Slide Options Schema =====
115
126
  export const inputMasterSlideOptionsSchema = z.object({
@@ -122,7 +133,7 @@ export const inputMasterSlideOptionsSchema = z.object({
122
133
  .optional(),
123
134
  date: z
124
135
  .object({
125
- format: dateFormatSchema,
136
+ value: z.string(),
126
137
  })
127
138
  .optional(),
128
139
  });
@@ -1 +1 @@
1
- {"version":3,"file":"renderPptx.d.ts","sourceRoot":"","sources":["../../src/renderPptx/renderPptx.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,aAAa,MAAM,WAAW,CAAC;AAE3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAK/C,KAAK,OAAO,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AACxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEpD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,cAAc,EAAE,EAAE,OAAO,EAAE,OAAO,iBAsLnE"}
1
+ {"version":3,"file":"renderPptx.d.ts","sourceRoot":"","sources":["../../src/renderPptx/renderPptx.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,aAAa,MAAM,WAAW,CAAC;AAE3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAK/C,KAAK,OAAO,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AACxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEpD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,cAAc,EAAE,EAAE,OAAO,EAAE,OAAO,iBA8MnE"}
@@ -1,7 +1,7 @@
1
1
  import { createRequire } from "module";
2
2
  const require = createRequire(import.meta.url);
3
3
  const PptxGenJS = require("pptxgenjs");
4
- import { resolveRowHeights } from "../table/utils";
4
+ import { resolveColumnWidths, resolveRowHeights } from "../table/utils";
5
5
  import { createTextOptions } from "./textOptions";
6
6
  import { pxToIn, pxToPt } from "./units";
7
7
  export { createTextOptions } from "./textOptions";
@@ -104,7 +104,7 @@ export function renderPptx(pages, slidePx) {
104
104
  y: pxToIn(node.y),
105
105
  w: pxToIn(node.w),
106
106
  h: pxToIn(node.h),
107
- colW: node.columns.map((column) => pxToIn(column.width)),
107
+ colW: resolveColumnWidths(node, node.w).map((width) => pxToIn(width)),
108
108
  rowH: resolveRowHeights(node).map((height) => pxToIn(height)),
109
109
  margin: 0,
110
110
  };
@@ -150,7 +150,7 @@ export function renderPptx(pages, slidePx) {
150
150
  shape: node.shapeType,
151
151
  fontSize: pxToPt(node.fontPx ?? 24),
152
152
  fontFace: "Noto Sans JP",
153
- color: node.fontColor,
153
+ color: node.color,
154
154
  align: node.alignText ?? "center",
155
155
  valign: "middle",
156
156
  lineSpacingMultiple: 1.3,
@@ -162,6 +162,25 @@ export function renderPptx(pages, slidePx) {
162
162
  }
163
163
  break;
164
164
  }
165
+ case "chart": {
166
+ const chartData = node.data.map((d) => ({
167
+ name: d.name,
168
+ labels: d.labels,
169
+ values: d.values,
170
+ }));
171
+ const chartOptions = {
172
+ x: pxToIn(node.x),
173
+ y: pxToIn(node.y),
174
+ w: pxToIn(node.w),
175
+ h: pxToIn(node.h),
176
+ showLegend: node.showLegend ?? false,
177
+ showTitle: node.showTitle ?? false,
178
+ title: node.title,
179
+ chartColors: node.chartColors,
180
+ };
181
+ slide.addChart(node.chartType, chartData, chartOptions);
182
+ break;
183
+ }
165
184
  }
166
185
  }
167
186
  renderNode(data);
@@ -1,7 +1,14 @@
1
- import type { PositionedNode } from "../types";
1
+ import type { PositionedNode, BulletOptions } from "../types";
2
2
  type TextNode = Extract<PositionedNode, {
3
3
  type: "text";
4
4
  }>;
5
+ type PptxBulletOptions = {
6
+ type?: "bullet" | "number";
7
+ indent?: number;
8
+ numberType?: BulletOptions["numberType"];
9
+ numberStartAt?: number;
10
+ };
11
+ export declare function createBulletOptions(bullet: boolean | BulletOptions): PptxBulletOptions | boolean;
5
12
  export declare function createTextOptions(node: TextNode): {
6
13
  x: number;
7
14
  y: number;
@@ -15,6 +22,20 @@ export declare function createTextOptions(node: TextNode): {
15
22
  lineSpacingMultiple: number;
16
23
  color: string;
17
24
  bold: boolean;
25
+ } | {
26
+ bullet: boolean | PptxBulletOptions;
27
+ x: number;
28
+ y: number;
29
+ w: number;
30
+ h: number;
31
+ fontSize: number;
32
+ fontFace: string;
33
+ align: "right" | "left" | "center";
34
+ valign: "top";
35
+ margin: number;
36
+ lineSpacingMultiple: number;
37
+ color: string;
38
+ bold: boolean;
18
39
  };
19
40
  export {};
20
41
  //# sourceMappingURL=textOptions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"textOptions.d.ts","sourceRoot":"","sources":["../../src/renderPptx/textOptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAG/C,KAAK,QAAQ,GAAG,OAAO,CAAC,cAAc,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE1D,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,QAAQ;;;;;;;;;;;;;EAmB/C"}
1
+ {"version":3,"file":"textOptions.d.ts","sourceRoot":"","sources":["../../src/renderPptx/textOptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAG9D,KAAK,QAAQ,GAAG,OAAO,CAAC,cAAc,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE1D,KAAK,iBAAiB,GAAG;IACvB,IAAI,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IACzC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,OAAO,GAAG,aAAa,GAC9B,iBAAiB,GAAG,OAAO,CAqB7B;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4B/C"}
@@ -1,9 +1,28 @@
1
1
  import { pxToIn, pxToPt } from "./units";
2
+ export function createBulletOptions(bullet) {
3
+ if (typeof bullet === "boolean") {
4
+ return bullet;
5
+ }
6
+ const options = {};
7
+ if (bullet.type !== undefined) {
8
+ options.type = bullet.type;
9
+ }
10
+ if (bullet.indent !== undefined) {
11
+ options.indent = bullet.indent;
12
+ }
13
+ if (bullet.numberType !== undefined) {
14
+ options.numberType = bullet.numberType;
15
+ }
16
+ if (bullet.numberStartAt !== undefined) {
17
+ options.numberStartAt = bullet.numberStartAt;
18
+ }
19
+ return options;
20
+ }
2
21
  export function createTextOptions(node) {
3
22
  const fontSizePx = node.fontPx ?? 24;
4
23
  const fontFamily = node.fontFamily ?? "Noto Sans JP";
5
24
  const lineSpacingMultiple = node.lineSpacingMultiple ?? 1.3;
6
- return {
25
+ const baseOptions = {
7
26
  x: pxToIn(node.x),
8
27
  y: pxToIn(node.y),
9
28
  w: pxToIn(node.w),
@@ -17,4 +36,11 @@ export function createTextOptions(node) {
17
36
  color: node.color,
18
37
  bold: node.bold,
19
38
  };
39
+ if (node.bullet !== undefined) {
40
+ return {
41
+ ...baseOptions,
42
+ bullet: createBulletOptions(node.bullet),
43
+ };
44
+ }
45
+ return baseOptions;
20
46
  }
@@ -1,8 +1,18 @@
1
1
  import type { TableNode } from "../types";
2
2
  export declare const DEFAULT_TABLE_ROW_HEIGHT = 32;
3
+ export declare const DEFAULT_TABLE_COLUMN_WIDTH = 100;
3
4
  export declare function calcTableIntrinsicSize(node: TableNode): {
4
5
  width: number;
5
6
  height: number;
6
7
  };
7
8
  export declare function resolveRowHeights(node: TableNode): number[];
9
+ /**
10
+ * テーブルの各カラム幅を解決する
11
+ * - 幅が指定されているカラムはその値を使用
12
+ * - 幅が未指定のカラムは、残りの幅を均等分割
13
+ *
14
+ * @param node テーブルノード
15
+ * @param tableWidth テーブル全体の幅(レイアウト計算後の確定値)
16
+ */
17
+ export declare function resolveColumnWidths(node: TableNode, tableWidth: number): number[];
8
18
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/table/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE1C,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAE3C,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,SAAS;;;EAKrD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,YAGhD"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/table/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE1C,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAC3C,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAE9C,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,SAAS;;;EAQrD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,YAGhD;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,SAAS,EACf,UAAU,EAAE,MAAM,GACjB,MAAM,EAAE,CAeV"}
@@ -1,6 +1,7 @@
1
1
  export const DEFAULT_TABLE_ROW_HEIGHT = 32;
2
+ export const DEFAULT_TABLE_COLUMN_WIDTH = 100;
2
3
  export function calcTableIntrinsicSize(node) {
3
- const width = node.columns.reduce((sum, column) => sum + column.width, 0);
4
+ const width = node.columns.reduce((sum, column) => sum + (column.width ?? DEFAULT_TABLE_COLUMN_WIDTH), 0);
4
5
  const height = resolveRowHeights(node).reduce((sum, h) => sum + h, 0);
5
6
  return { width, height };
6
7
  }
@@ -8,3 +9,19 @@ export function resolveRowHeights(node) {
8
9
  const fallbackRowHeight = node.defaultRowHeight ?? DEFAULT_TABLE_ROW_HEIGHT;
9
10
  return node.rows.map((row) => row.height ?? fallbackRowHeight);
10
11
  }
12
+ /**
13
+ * テーブルの各カラム幅を解決する
14
+ * - 幅が指定されているカラムはその値を使用
15
+ * - 幅が未指定のカラムは、残りの幅を均等分割
16
+ *
17
+ * @param node テーブルノード
18
+ * @param tableWidth テーブル全体の幅(レイアウト計算後の確定値)
19
+ */
20
+ export function resolveColumnWidths(node, tableWidth) {
21
+ const specifiedTotal = node.columns.reduce((sum, col) => sum + (col.width ?? 0), 0);
22
+ const unspecifiedCount = node.columns.filter((col) => col.width === undefined).length;
23
+ // 未指定カラムがない場合、または未指定カラムに割り当てる幅を計算
24
+ const remainingWidth = Math.max(0, tableWidth - specifiedTotal);
25
+ const widthPerUnspecified = unspecifiedCount > 0 ? remainingWidth / unspecifiedCount : 0;
26
+ return node.columns.map((col) => col.width ?? widthPerUnspecified);
27
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"toPositioned.d.ts","sourceRoot":"","sources":["../../src/toPositioned/toPositioned.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAExD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,GAAG,EAAE,OAAO,EACZ,OAAO,SAAI,EACX,OAAO,SAAI,GACV,cAAc,CAiFhB"}
1
+ {"version":3,"file":"toPositioned.d.ts","sourceRoot":"","sources":["../../src/toPositioned/toPositioned.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAExD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,GAAG,EAAE,OAAO,EACZ,OAAO,SAAI,EACX,OAAO,SAAI,GACV,cAAc,CA0FhB"}
@@ -49,6 +49,15 @@ export function toPositioned(pom, parentX = 0, parentY = 0) {
49
49
  h: layout.height,
50
50
  };
51
51
  }
52
+ case "chart": {
53
+ return {
54
+ ...pom,
55
+ x: absoluteX,
56
+ y: absoluteY,
57
+ w: layout.width,
58
+ h: layout.height,
59
+ };
60
+ }
52
61
  case "box": {
53
62
  return {
54
63
  ...pom,