@file-viewer/pptx 3.3.0 → 3.3.3

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.en.md CHANGED
@@ -10,6 +10,7 @@ Native PPTX rendering engine extracted from Flyfish File Viewer. It is maintaine
10
10
  - Web Worker based PPTX parsing with progressive slide output.
11
11
  - Preserves the latest native Flyfish rendering path for themes, images, shapes, tables, text, and basic charts.
12
12
  - Supports `fitMode`, `zoomPercent`, custom Worker URLs, and Worker factories for private deployment and strict CSP environments.
13
+ - Charts retain authored sizes and text styles through slideshow zoom. CJK labels use independent logical measurements; legends support bottom, right, top, top-right and left positions. Fonts must be installed or supplied by the host; unavailable fonts use browser fallbacks.
13
14
 
14
15
  `resolvePptxPackageWorkerUrl()` lets integrations identify an emitted or directly served package Worker URL. It returns `undefined` when the location is not recognized, not as proof that a file is missing; integrations can then use their asset-copy strategy or explicit Worker configuration.
15
16
 
package/README.md CHANGED
@@ -10,6 +10,7 @@ Native PPTX rendering engine extracted from Flyfish File Viewer. It is maintaine
10
10
  - Web Worker based PPTX parsing with progressive slide output.
11
11
  - Preserves the latest native Flyfish rendering path for themes, images, shapes, tables, text, and basic charts.
12
12
  - Supports `fitMode`, `zoomPercent`, custom Worker URLs, and Worker factories for private deployment and strict CSP environments.
13
+ - Charts retain authored sizes and text styles through slideshow zoom. CJK labels use independent logical measurements; legends support bottom, right, top, top-right and left positions. Fonts must be installed or supplied by the host; unavailable fonts use browser fallbacks.
13
14
 
14
15
  `resolvePptxPackageWorkerUrl()` lets integrations identify an emitted or directly served package Worker URL. It returns `undefined` when the location is not recognized, not as proof that a file is missing; integrations can then use their asset-copy strategy or explicit Worker configuration.
15
16
 
package/README.zh-CN.md CHANGED
@@ -10,6 +10,7 @@ Flyfish File Viewer 的原生 PPTX 渲染引擎包,从历史稳定实现中独
10
10
  - 使用 Web Worker 解析 PPTX,按页渐进输出,避免阻塞主线程。
11
11
  - 保留历史原生解析链路中的主题、图片、形状、表格、文本和基础图表处理。
12
12
  - 支持 `fitMode`、`zoomPercent`、自定义 Worker URL / Worker factory,方便内网、离线和 CSP 场景。
13
+ - 图表在放映缩放时保留原始尺寸和文字样式,中文标签按独立的逻辑尺寸测量,支持底部、右侧、顶部、右上和左侧图例。字体由宿主安装或提供;缺少字体时使用浏览器回退字体。
13
14
 
14
15
  `resolvePptxPackageWorkerUrl()` 可供集成层识别打包器已输出或包目录直接提供的 Worker URL。未识别时返回 `undefined`,不代表文件不存在;集成层仍可使用自己的资产复制策略或显式 Worker 配置。
15
16
 
@@ -1,5 +1,10 @@
1
- import type { PptxChartModel } from "./chart-types.js";
1
+ import type { PptxChartAxis, PptxChartModel } from "./chart-types.js";
2
2
  export declare const formatPptxChartNumber: (value: number, format?: string) => string;
3
+ export declare const resolvePptxChartAxes: (model: PptxChartModel) => {
4
+ category: PptxChartAxis | undefined;
5
+ primary: PptxChartAxis | undefined;
6
+ secondary: PptxChartAxis | undefined;
7
+ };
3
8
  export declare const createPptxCartesianChart: (model: PptxChartModel, library: Record<string, any>) => {
4
9
  interaction: {
5
10
  enabled: boolean;
@@ -44,6 +49,14 @@ export declare const createPptxCartesianChart: (model: PptxChartModel, library:
44
49
  categories: string[];
45
50
  tick: {
46
51
  multiline: boolean;
52
+ culling?: undefined;
53
+ rotate?: undefined;
54
+ autorotate?: undefined;
55
+ } | {
56
+ multiline: boolean;
57
+ culling: boolean;
58
+ rotate: number;
59
+ autorotate: boolean;
47
60
  };
48
61
  };
49
62
  y: {};
@@ -28,8 +28,16 @@ const axisOptions = (axis) => {
28
28
  };
29
29
  };
30
30
  const defined = (labels) => Object.fromEntries(Object.entries(labels || {}).filter(([, value]) => value !== undefined));
31
+ export const resolvePptxChartAxes = (model) => {
32
+ var _a, _b;
33
+ const axesFor = (group) => model.axes.filter(axis => group === null || group === void 0 ? void 0 : group.axisIds.includes(axis.id));
34
+ const primary = (_a = axesFor(model.groups[0]).find(axis => axis.kind === 'value')) !== null && _a !== void 0 ? _a : model.axes.find(axis => axis.kind === 'value');
35
+ const secondary = model.groups.filter(group => group.series.length > 0).flatMap(axesFor).find(axis => axis.kind === 'value' && axis.id !== (primary === null || primary === void 0 ? void 0 : primary.id));
36
+ const category = (_b = axesFor(model.groups[0]).find(axis => axis.kind === 'category' || axis.kind === 'date')) !== null && _b !== void 0 ? _b : model.axes.find(axis => axis.kind === 'category' || axis.kind === 'date');
37
+ return { category, primary, secondary };
38
+ };
31
39
  export const createPptxCartesianChart = (model, library) => {
32
- var _a, _b, _c, _d, _e;
40
+ var _a, _b, _c, _d;
33
41
  if (!model.groups.length || model.groups.some(group => !['lineChart', 'barChart', 'areaChart'].includes(group.chartType)))
34
42
  return null;
35
43
  const rows = model.groups.flatMap((group, groupIndex) => group.series.map((series, index) => ({
@@ -50,10 +58,7 @@ export const createPptxCartesianChart = (model, library) => {
50
58
  const points = new Map(series.values.map(point => [String(point.x), point.y]));
51
59
  return indices.map(index => { var _a; return (_a = points.get(index)) !== null && _a !== void 0 ? _a : null; });
52
60
  };
53
- const axesFor = (group) => model.axes.filter(axis => group.axisIds.includes(axis.id));
54
- const primary = (_b = axesFor(model.groups[0]).find(axis => axis.kind === 'value')) !== null && _b !== void 0 ? _b : model.axes.find(axis => axis.kind === 'value');
55
- const secondary = rows.flatMap(row => axesFor(row.group)).find(axis => axis.kind === 'value' && axis.id !== (primary === null || primary === void 0 ? void 0 : primary.id));
56
- const category = (_c = axesFor(model.groups[0]).find(axis => axis.kind === 'category' || axis.kind === 'date')) !== null && _c !== void 0 ? _c : model.axes.find(axis => axis.kind === 'category' || axis.kind === 'date');
61
+ const { primary, secondary, category } = resolvePptxChartAxes(model);
57
62
  const factory = { barChart: library.bar, lineChart: library.line, areaChart: library.area };
58
63
  if (rows.some(row => typeof factory[row.group.chartType] !== 'function'))
59
64
  return null;
@@ -91,7 +96,7 @@ export const createPptxCartesianChart = (model, library) => {
91
96
  const color = (_b = (_a = row.group.options.series) === null || _a === void 0 ? void 0 : _a[row.index]) === null || _b === void 0 ? void 0 : _b.color;
92
97
  return color && /^(?:#[\da-f]{6}(?:[\da-f]{2})?|transparent)$/i.test(color) ? [[row.id, color]] : [];
93
98
  }));
94
- const gap = (_d = model.groups.find(group => group.chartType === 'barChart')) === null || _d === void 0 ? void 0 : _d.options.gapWidth;
99
+ const gap = (_b = model.groups.find(group => group.chartType === 'barChart')) === null || _b === void 0 ? void 0 : _b.options.gapWidth;
95
100
  const hasLabels = rows.some(row => { var _a, _b; return row.group.options.labels || ((_b = (_a = row.group.options.series) === null || _a === void 0 ? void 0 : _a[row.index]) === null || _b === void 0 ? void 0 : _b.labels); });
96
101
  const byId = new Map(rows.map(row => [row.id, row]));
97
102
  const labelFormat = (value, id, position) => {
@@ -116,7 +121,7 @@ export const createPptxCartesianChart = (model, library) => {
116
121
  };
117
122
  return {
118
123
  data: {
119
- columns, types, type: types[(_e = rows[0]) === null || _e === void 0 ? void 0 : _e.id], axes: dataAxes,
124
+ columns, types, type: types[(_c = rows[0]) === null || _c === void 0 ? void 0 : _c.id], axes: dataAxes,
120
125
  names: Object.fromEntries(rows.map(row => [row.id, String(row.series.key)])),
121
126
  ...(Object.keys(colors).length ? { colors } : {}),
122
127
  ...(groups.length ? { groups, order: null } : {}),
@@ -125,7 +130,9 @@ export const createPptxCartesianChart = (model, library) => {
125
130
  },
126
131
  axis: {
127
132
  rotated: model.groups.some(group => group.barDirection === 'bar'),
128
- x: { ...axisOptions(category), type: 'category', categories, tick: { multiline: true } },
133
+ x: { ...axisOptions(category), type: 'category', categories, tick: model.groups.some(group => group.barDirection === 'bar')
134
+ ? { multiline: true }
135
+ : { multiline: false, culling: false, rotate: (_d = category === null || category === void 0 ? void 0 : category.labelRotation) !== null && _d !== void 0 ? _d : 60, autorotate: (category === null || category === void 0 ? void 0 : category.labelRotation) === undefined } },
129
136
  y: axisOptions(primary),
130
137
  ...(secondary ? { y2: axisOptions(secondary) } : {}),
131
138
  },
@@ -0,0 +1,9 @@
1
+ import type { PptxChartFont, PptxChartOptions } from "./chart-types.js";
2
+ export declare const createPptxChartLegend: (surface: HTMLElement, chart: any, legend?: PptxChartOptions["legend"], font?: PptxChartFont) => {
3
+ size(): {
4
+ width: number;
5
+ height: number;
6
+ };
7
+ bind(api: any): void;
8
+ destroy(): void;
9
+ } | undefined;
@@ -0,0 +1,99 @@
1
+ import { applyPptxChartFont } from "./chart-text.js";
2
+ export const createPptxChartLegend = (surface, chart, legend, font) => {
3
+ var _a, _b;
4
+ const document = surface.ownerDocument;
5
+ const view = document === null || document === void 0 ? void 0 : document.defaultView;
6
+ if (!view || (legend === null || legend === void 0 ? void 0 : legend.show) === false || !((legend === null || legend === void 0 ? void 0 : legend.overlay) || ['t', 'tr', 'l'].includes((legend === null || legend === void 0 ? void 0 : legend.position) || '')))
7
+ return;
8
+ const left = (legend === null || legend === void 0 ? void 0 : legend.position) === 'l' || (legend === null || legend === void 0 ? void 0 : legend.position) === 'r';
9
+ const layout = document.createElement('div');
10
+ layout.className = 'pptx-chart-layout';
11
+ layout.style.cssText = `display:flex;flex-direction:${left ? 'row' : 'column'};width:100%;height:100%;min-width:0;min-height:0;position:relative`;
12
+ surface.replaceWith(layout);
13
+ const list = document.createElement('div');
14
+ list.className = 'pptx-chart-legend';
15
+ list.setAttribute('role', 'group');
16
+ list.setAttribute('aria-label', 'Chart legend');
17
+ list.style.cssText = `display:flex;flex:${left ? '0 1 auto' : '0 0 auto'};flex-direction:${left ? 'column' : 'row'};flex-wrap:${left ? 'nowrap' : 'wrap'};gap:4px 12px;align-items:${left ? 'stretch' : 'center'};justify-content:${(legend === null || legend === void 0 ? void 0 : legend.position) === 'tr' ? 'flex-end' : 'center'};box-sizing:border-box;padding:4px;overflow:auto;${left ? 'max-width:50%' : 'max-height:50%'};font:12px sans-serif`;
18
+ applyPptxChartFont(list.style, font);
19
+ if (legend === null || legend === void 0 ? void 0 : legend.overlay) {
20
+ list.style.position = 'absolute';
21
+ list.style.zIndex = '1';
22
+ list.style[legend.position === 'b' ? 'bottom' : 'top'] = '0';
23
+ list.style[legend.position === 'tr' || legend.position === 'r' ? 'right' : 'left'] = '0';
24
+ if (left)
25
+ list.style.height = '100%';
26
+ else
27
+ list.style.width = '100%';
28
+ }
29
+ surface.style.width = left ? '0' : '100%';
30
+ surface.style.height = left ? '100%' : '0';
31
+ surface.style.alignSelf = 'stretch';
32
+ if (legend === null || legend === void 0 ? void 0 : legend.overlay)
33
+ surface.style.width = surface.style.height = '100%';
34
+ layout.append(list, surface);
35
+ const xs = new Set(Object.values(((_a = chart.data) === null || _a === void 0 ? void 0 : _a.xs) || {}).map(String));
36
+ const entries = (((_b = chart.data) === null || _b === void 0 ? void 0 : _b.columns) || []).filter((column) => !xs.has(String(column[0])))
37
+ .map((column) => {
38
+ var _a, _b, _c;
39
+ const id = String(column[0]);
40
+ const button = document.createElement('button');
41
+ button.type = 'button';
42
+ button.setAttribute('aria-pressed', 'true');
43
+ button.style.cssText = 'display:inline-flex;flex:0 0 auto;align-items:center;gap:4px;min-width:0;max-width:100%;border:0;padding:2px;background:none;color:inherit;font:inherit;text-align:start;cursor:pointer';
44
+ const swatch = document.createElement('span');
45
+ swatch.setAttribute('aria-hidden', 'true');
46
+ swatch.style.cssText = 'display:inline-block;flex:0 0 10px;width:10px;height:10px';
47
+ const text = document.createElement('span');
48
+ text.className = 'pptx-chart-legend-label';
49
+ text.style.cssText = 'min-width:0;overflow-wrap:anywhere';
50
+ text.textContent = String((_c = (_b = (_a = chart.data) === null || _a === void 0 ? void 0 : _a.names) === null || _b === void 0 ? void 0 : _b[id]) !== null && _c !== void 0 ? _c : id) || '\u200b';
51
+ button.append(swatch, text);
52
+ list.append(button);
53
+ return { id, button, swatch };
54
+ });
55
+ chart.legend = { ...chart.legend, show: false };
56
+ let observer;
57
+ let instance;
58
+ let width = 0, height = 0;
59
+ return {
60
+ size() {
61
+ return { width: surface.clientWidth, height: surface.clientHeight };
62
+ },
63
+ bind(api) {
64
+ var _a;
65
+ instance = api;
66
+ for (const { id, button, swatch } of entries) {
67
+ swatch.style.backgroundColor = ((_a = api.color) === null || _a === void 0 ? void 0 : _a.call(api, id)) || 'currentColor';
68
+ button.onclick = () => {
69
+ var _a;
70
+ (_a = api.toggle) === null || _a === void 0 ? void 0 : _a.call(api, id);
71
+ const visible = button.getAttribute('aria-pressed') !== 'true';
72
+ button.setAttribute('aria-pressed', String(visible));
73
+ button.style.opacity = visible ? '1' : '.45';
74
+ };
75
+ button.onmouseenter = () => { var _a; return (_a = api.focus) === null || _a === void 0 ? void 0 : _a.call(api, id); };
76
+ button.onmouseleave = () => { var _a; return (_a = api.revert) === null || _a === void 0 ? void 0 : _a.call(api); };
77
+ }
78
+ if (view.ResizeObserver) {
79
+ observer = new view.ResizeObserver(([entry]) => {
80
+ var _a;
81
+ const { width: nextWidth, height: nextHeight } = entry.contentRect;
82
+ if (nextWidth > 0 && nextHeight > 0 && (Math.abs(nextWidth - width) > .5 || Math.abs(nextHeight - height) > .5)) {
83
+ width = nextWidth;
84
+ height = nextHeight;
85
+ (_a = instance === null || instance === void 0 ? void 0 : instance.resize) === null || _a === void 0 ? void 0 : _a.call(instance, { width, height });
86
+ }
87
+ });
88
+ observer.observe(surface);
89
+ }
90
+ },
91
+ destroy() {
92
+ observer === null || observer === void 0 ? void 0 : observer.disconnect();
93
+ instance = undefined;
94
+ for (const { button } of entries)
95
+ button.onclick = button.onmouseenter = button.onmouseleave = null;
96
+ layout.remove();
97
+ },
98
+ };
99
+ };
@@ -0,0 +1,12 @@
1
+ import type { PptxChartFont, PptxChartModel } from "./chart-types.js";
2
+ export declare const mergePptxChartFonts: (...fonts: Array<PptxChartFont | undefined>) => PptxChartFont;
3
+ export declare const applyPptxChartFont: (style: CSSStyleDeclaration, font?: PptxChartFont) => void;
4
+ export declare const stylePptxChartText: (surface: HTMLElement, model?: PptxChartModel, chart?: any, legendFont?: PptxChartFont) => HTMLStyleElement | undefined;
5
+ export declare const createPptxChartTextPlugin: (surface: HTMLElement) => {
6
+ $$: any;
7
+ $beforeInit(): void;
8
+ $init(): void;
9
+ $afterInit(): void;
10
+ $redraw(): void;
11
+ $willDestroy(): void;
12
+ } | undefined;
@@ -0,0 +1,154 @@
1
+ import { resolvePptxChartAxes } from "./chart-config.js";
2
+ export const mergePptxChartFonts = (...fonts) => Object.assign({}, ...fonts.map(font => Object.fromEntries(Object.entries(font || {}).filter(([, value]) => value !== undefined))));
3
+ export const applyPptxChartFont = (style, font) => {
4
+ if (!font)
5
+ return;
6
+ const families = [...new Set([font.family, font.eastAsianFamily, font.complexScriptFamily]
7
+ .filter((value) => typeof value === 'string' && !!value.trim())
8
+ .filter(value => !/^\+(?:mj|mn)-(?:lt|ea|cs)$/.test(value))
9
+ .map(value => /^(?:serif|sans-serif|monospace|cursive|fantasy|system-ui|ui-serif|ui-sans-serif|ui-monospace|ui-rounded|emoji|math|fangsong)$/i.test(value.trim())
10
+ ? value.trim().toLowerCase() : JSON.stringify(value.slice(0, 512))))];
11
+ if (families.length)
12
+ style.fontFamily = [...families, 'sans-serif'].join(',');
13
+ if (typeof font.sizePt === 'number' && Number.isFinite(font.sizePt) && font.sizePt > 0 && font.sizePt <= 4000) {
14
+ style.fontSize = `${font.sizePt * 96 / 72}px`;
15
+ }
16
+ if (font.bold !== undefined)
17
+ style.fontWeight = font.bold ? 'bold' : 'normal';
18
+ if (font.italic !== undefined)
19
+ style.fontStyle = font.italic ? 'italic' : 'normal';
20
+ if (typeof font.color === 'string' && /^(?:#[\da-f]{6}(?:[\da-f]{2})?|transparent)$/i.test(font.color)) {
21
+ style.color = font.color;
22
+ style.fill = font.color;
23
+ }
24
+ };
25
+ export const stylePptxChartText = (surface, model, chart, legendFont) => {
26
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
27
+ const view = (_a = surface.ownerDocument) === null || _a === void 0 ? void 0 : _a.defaultView;
28
+ if (!view || typeof surface.appendChild !== 'function')
29
+ return;
30
+ const scope = `chart-${Math.random().toString(36).slice(2)}`;
31
+ surface.dataset.pptxChartText = scope;
32
+ surface.classList.add('pptx-chart-typed');
33
+ const selector = `.pptx-chart-surface.pptx-chart-typed[data-pptx-chart-text="${scope}"]`;
34
+ const element = surface.ownerDocument.createElement('style');
35
+ element.dataset.pptxChartFonts = '';
36
+ (_b = surface.parentNode) === null || _b === void 0 ? void 0 : _b.appendChild(element);
37
+ const sheet = element.sheet;
38
+ if (!sheet)
39
+ return element;
40
+ const rule = (suffix, font) => {
41
+ if (!font || !Object.values(font).some(value => value !== undefined))
42
+ return;
43
+ const index = sheet.insertRule(suffix.split(',').map(value => `${selector} ${value.trim()}`).join(',') + '{}', sheet.cssRules.length);
44
+ applyPptxChartFont(sheet.cssRules[index].style, font);
45
+ };
46
+ rule('svg', model === null || model === void 0 ? void 0 : model.font);
47
+ rule('.bb-legend-item,.bb-legend-item text', mergePptxChartFonts(model === null || model === void 0 ? void 0 : model.font, legendFont));
48
+ if (!model)
49
+ return element;
50
+ const { category, primary, secondary } = resolvePptxChartAxes(model);
51
+ for (const [axis, font] of [['x', category === null || category === void 0 ? void 0 : category.font], ['y', primary === null || primary === void 0 ? void 0 : primary.font], ['y2', secondary === null || secondary === void 0 ? void 0 : secondary.font]]) {
52
+ rule(`.bb-axis-${axis},.bb-axis-${axis} text`, mergePptxChartFonts(model.font, font));
53
+ }
54
+ let column = 0;
55
+ const escape = (_c = view.CSS) === null || _c === void 0 ? void 0 : _c.escape;
56
+ if (!escape)
57
+ return element;
58
+ for (const group of model.groups)
59
+ for (let index = 0; index < group.series.length; index++, column++) {
60
+ const id = String((_g = (_f = (_e = (_d = chart === null || chart === void 0 ? void 0 : chart.data) === null || _d === void 0 ? void 0 : _d.columns) === null || _e === void 0 ? void 0 : _e[column]) === null || _f === void 0 ? void 0 : _f[0]) !== null && _g !== void 0 ? _g : '');
61
+ if (!id)
62
+ continue;
63
+ const labels = (_j = (_h = group.options.series) === null || _h === void 0 ? void 0 : _h[index]) === null || _j === void 0 ? void 0 : _j.labels;
64
+ const font = mergePptxChartFonts(model.font, (_k = group.options.labels) === null || _k === void 0 ? void 0 : _k.font, labels === null || labels === void 0 ? void 0 : labels.font);
65
+ const texts = `.bb-texts-${escape(id)}`;
66
+ rule(`${texts},${texts} text`, font);
67
+ const points = { ...(_l = group.options.labels) === null || _l === void 0 ? void 0 : _l.points, ...labels === null || labels === void 0 ? void 0 : labels.points };
68
+ for (const [position, point] of Object.entries(points)) {
69
+ if (!/^\d+$/.test(position) || !point.font)
70
+ continue;
71
+ rule(`${texts} .bb-text-${escape(position)}`, mergePptxChartFonts(font, point.font));
72
+ }
73
+ }
74
+ return element;
75
+ };
76
+ export const createPptxChartTextPlugin = (surface) => {
77
+ const document = surface.ownerDocument;
78
+ const view = document === null || document === void 0 ? void 0 : document.defaultView;
79
+ if (!view || typeof surface.appendChild !== 'function')
80
+ return undefined;
81
+ let original;
82
+ let wrapped;
83
+ let stopped = false;
84
+ let context;
85
+ const plugin = {
86
+ $$: undefined,
87
+ $beforeInit() {
88
+ const internal = this.$$;
89
+ if (typeof (internal === null || internal === void 0 ? void 0 : internal.getTextRect) !== 'function')
90
+ return;
91
+ original = internal.getTextRect;
92
+ wrapped = (source, className) => {
93
+ var _a, _b, _c, _d, _e;
94
+ const svg = ((_c = (_b = (_a = internal.$el) === null || _a === void 0 ? void 0 : _a.svg) === null || _b === void 0 ? void 0 : _b.node) === null || _c === void 0 ? void 0 : _c.call(_b)) || surface.querySelector('svg');
95
+ if (!svg)
96
+ return original.call(internal, source, className);
97
+ let base = ((_d = source === null || source === void 0 ? void 0 : source.node) === null || _d === void 0 ? void 0 : _d.call(source)) || source;
98
+ if (!Array.isArray(source) && !/text/i.test((base === null || base === void 0 ? void 0 : base.tagName) || ''))
99
+ base = (_e = base === null || base === void 0 ? void 0 : base.querySelector) === null || _e === void 0 ? void 0 : _e.call(base, 'text');
100
+ if (!Array.isArray(source) && !base)
101
+ return original.call(internal, source, className);
102
+ const values = Array.isArray(source) ? source : [base.textContent || ''];
103
+ const rectangles = values.map((value) => {
104
+ var _a;
105
+ const text = String(value !== null && value !== void 0 ? value : '');
106
+ const node = document.createElementNS('http://www.w3.org/2000/svg', 'text');
107
+ if (className)
108
+ node.setAttribute('class', className);
109
+ node.style.visibility = 'hidden';
110
+ if (base && !Array.isArray(source))
111
+ node.style.font = view.getComputedStyle(base).font;
112
+ node.textContent = text;
113
+ svg.appendChild(node);
114
+ try {
115
+ const box = node.getBBox();
116
+ if (box.height > 0 && (box.width > 0 || !text)) {
117
+ return { x: box.x, y: box.y, left: box.x, top: box.y, right: box.x + box.width,
118
+ bottom: box.y + box.height, width: box.width, height: box.height };
119
+ }
120
+ const style = view.getComputedStyle(node);
121
+ context !== null && context !== void 0 ? context : (context = document.createElement('canvas').getContext('2d'));
122
+ const size = Number.parseFloat(style.fontSize) || 10;
123
+ if (context)
124
+ context.font = `${style.fontStyle} ${style.fontWeight} ${style.fontSize} ${style.fontFamily}`;
125
+ const metrics = context === null || context === void 0 ? void 0 : context.measureText(text);
126
+ const width = (_a = metrics === null || metrics === void 0 ? void 0 : metrics.width) !== null && _a !== void 0 ? _a : 0;
127
+ const height = ((metrics === null || metrics === void 0 ? void 0 : metrics.fontBoundingBoxAscent) || 0) + ((metrics === null || metrics === void 0 ? void 0 : metrics.fontBoundingBoxDescent) || 0) || size * 1.2;
128
+ return { x: 0, y: 0, left: 0, top: 0, right: width, bottom: height, width, height };
129
+ }
130
+ finally {
131
+ node.remove();
132
+ }
133
+ });
134
+ return rectangles.length > 1 ? rectangles : rectangles[0];
135
+ };
136
+ internal.getTextRect = wrapped;
137
+ },
138
+ $init() { },
139
+ $afterInit() { var _a; (_a = document.fonts) === null || _a === void 0 ? void 0 : _a.addEventListener('loadingdone', refresh); },
140
+ $redraw() { },
141
+ $willDestroy() {
142
+ var _a, _b;
143
+ stopped = true;
144
+ (_a = document.fonts) === null || _a === void 0 ? void 0 : _a.removeEventListener('loadingdone', refresh);
145
+ if (((_b = this.$$) === null || _b === void 0 ? void 0 : _b.getTextRect) === wrapped)
146
+ this.$$.getTextRect = original;
147
+ original = wrapped = context = undefined;
148
+ this.$$ = undefined;
149
+ },
150
+ };
151
+ const refresh = () => { var _a, _b, _c; if (!stopped)
152
+ (_c = (_b = (_a = plugin.$$) === null || _a === void 0 ? void 0 : _a.api) === null || _b === void 0 ? void 0 : _b.flush) === null || _c === void 0 ? void 0 : _c.call(_b); };
153
+ return plugin;
154
+ };
@@ -1,5 +1,15 @@
1
1
  export type PptxChartType = 'lineChart' | 'barChart' | 'areaChart' | 'scatterChart' | 'pieChart' | 'pie3DChart' | 'doughnutChart';
2
+ export interface PptxChartFont {
3
+ family?: string;
4
+ eastAsianFamily?: string;
5
+ complexScriptFamily?: string;
6
+ sizePt?: number;
7
+ bold?: boolean;
8
+ italic?: boolean;
9
+ color?: string;
10
+ }
2
11
  export interface PptxChartLabels {
12
+ font?: PptxChartFont;
3
13
  showValue?: boolean;
4
14
  showCategory?: boolean;
5
15
  showSeries?: boolean;
@@ -19,6 +29,8 @@ export interface PptxChartOptions {
19
29
  legend?: {
20
30
  show?: boolean;
21
31
  position?: string;
32
+ overlay?: boolean;
33
+ font?: PptxChartFont;
22
34
  };
23
35
  labels?: PptxChartLabels;
24
36
  series?: Array<{
@@ -54,10 +66,8 @@ export interface PptxChartAxis {
54
66
  };
55
67
  title?: string;
56
68
  majorGridlines: boolean;
57
- font?: {
58
- family?: string;
59
- sizePt?: number;
60
- };
69
+ labelRotation?: number;
70
+ font?: PptxChartFont;
61
71
  }
62
72
  export interface PptxChartGroup {
63
73
  chartType: PptxChartType;
@@ -71,10 +81,7 @@ export interface PptxChartModel {
71
81
  groups: PptxChartGroup[];
72
82
  axes: PptxChartAxis[];
73
83
  title?: string;
74
- font?: {
75
- family?: string;
76
- sizePt?: number;
77
- };
84
+ font?: PptxChartFont;
78
85
  }
79
86
  export interface PptxChartMessage {
80
87
  type?: string;
package/dist/chart.js CHANGED
@@ -1,4 +1,6 @@
1
1
  import { createPptxCartesianChart } from "./chart-config.js";
2
+ import { createPptxChartTextPlugin, mergePptxChartFonts, stylePptxChartText } from "./chart-text.js";
3
+ import { createPptxChartLegend } from "./chart-legend.js";
2
4
  let chartLibraryLoader = null;
3
5
  let chartRenderer = null;
4
6
  export const registerPptxChartRenderer = (renderer) => {
@@ -217,7 +219,7 @@ const fitOverflowingTextBlocks = (root) => {
217
219
  .forEach(block => fitOverflowingTextBlock(block, false));
218
220
  };
219
221
  const renderChart = async (message, root) => {
220
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
222
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
221
223
  const payload = message.data;
222
224
  if (!(payload === null || payload === void 0 ? void 0 : payload.chartID) || !payload.chartType || !payload.chartData) {
223
225
  return;
@@ -442,22 +444,39 @@ const renderChart = async (message, root) => {
442
444
  surface.style.cssText = 'width:100%;height:100%;min-width:0;min-height:0;flex:1 1 auto';
443
445
  chartTarget.replaceChildren(surface);
444
446
  chart.bindto = surface;
447
+ const legend = createPptxChartLegend(surface, chart, options === null || options === void 0 ? void 0 : options.legend, mergePptxChartFonts((_q = payload.chartModel) === null || _q === void 0 ? void 0 : _q.font, (_r = options === null || options === void 0 ? void 0 : options.legend) === null || _r === void 0 ? void 0 : _r.font));
448
+ const fontStyle = stylePptxChartText(surface, payload.chartModel, chart, (_t = (_s = payload.chartOptions) === null || _s === void 0 ? void 0 : _s.legend) === null || _t === void 0 ? void 0 : _t.font);
449
+ const textPlugin = createPptxChartTextPlugin(surface);
450
+ if (textPlugin)
451
+ chart.plugins = [...(chart.plugins || []), textPlugin];
452
+ const logicalSize = [((_u = chartTarget.style) === null || _u === void 0 ? void 0 : _u.width) || '', ((_v = chartTarget.style) === null || _v === void 0 ? void 0 : _v.height) || '']
453
+ .map(value => /^\d+(?:\.\d+)?px$/.test(value) ? Number.parseFloat(value) : NaN);
454
+ if (logicalSize.every(value => Number.isFinite(value) && value > 0)) {
455
+ const plotSize = legend === null || legend === void 0 ? void 0 : legend.size();
456
+ chart.size = plotSize && plotSize.width > 0 && plotSize.height > 0 ? plotSize : { width: logicalSize[0], height: logicalSize[1] };
457
+ chart.resize = { auto: false };
458
+ }
445
459
  try {
446
460
  const instance = bb.generate(chart);
461
+ legend === null || legend === void 0 ? void 0 : legend.bind(instance);
447
462
  return {
448
463
  destroy() {
449
464
  var _a;
450
465
  try {
466
+ legend === null || legend === void 0 ? void 0 : legend.destroy();
451
467
  (_a = instance.destroy) === null || _a === void 0 ? void 0 : _a.call(instance);
452
468
  }
453
469
  finally {
454
470
  surface.remove();
471
+ fontStyle === null || fontStyle === void 0 ? void 0 : fontStyle.remove();
455
472
  }
456
473
  },
457
474
  };
458
475
  }
459
476
  catch (error) {
460
477
  surface.remove();
478
+ fontStyle === null || fontStyle === void 0 ? void 0 : fontStyle.remove();
479
+ legend === null || legend === void 0 ? void 0 : legend.destroy();
461
480
  throw error;
462
481
  }
463
482
  }
package/dist/index.d.ts CHANGED
@@ -4,6 +4,6 @@ export type { PptxPresentationLabels, PptxPresentationState } from "./presentati
4
4
  export { createPptxWorker, resolvePptxPackageWorkerUrl } from "./worker.js";
5
5
  export { registerPptxChartLibraryLoader, registerPptxChartRenderer } from "./chart.js";
6
6
  export type { PptxChartLibraries, PptxChartLibraryLoader } from "./chart.js";
7
- export type { PptxChartType, PptxChartLabels, PptxChartOptions, PptxChartSeries, PptxChartAxis, PptxChartGroup, PptxChartModel, PptxChartMessage, PptxChartCollection, PptxChartRenderer, } from "./chart-types.js";
7
+ export type { PptxChartType, PptxChartFont, PptxChartLabels, PptxChartOptions, PptxChartSeries, PptxChartAxis, PptxChartGroup, PptxChartModel, PptxChartMessage, PptxChartCollection, PptxChartRenderer, } from "./chart-types.js";
8
8
  export { RECOMMENDED_ZIP_LIMITS, createDefaultPptxOptions, resolvePptxEngineOptions, } from "./options.js";
9
9
  export type { NativePptxEngineOptions, PptxDiagnosticError, PptxDiagnosticErrorCode, PptxFitMode, PptxListOptions, PptxSlideSize, PptxViewerOptions, PptxWorkerFactoryOptions, PptxWorkerMessage, PptxZipLimits, } from "./types.js";