reamkit 1.4.0 → 1.6.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 (62) hide show
  1. package/README.md +10 -8
  2. package/dist/esm/core/converter/facade.d.ts +12 -6
  3. package/dist/esm/core/converter/facade.js +34 -5
  4. package/dist/esm/core/converter/ream.d.ts +5 -3
  5. package/dist/esm/core/converter/ream.js +18 -4
  6. package/dist/esm/core/document-model/index.d.ts +1 -1
  7. package/dist/esm/core/document-model/types.d.ts +26 -0
  8. package/dist/esm/core/drawingml/sparkline-geometry.d.ts +8 -0
  9. package/dist/esm/core/drawingml/sparkline-geometry.js +111 -0
  10. package/dist/esm/core/ir/adapters.d.ts +5 -5
  11. package/dist/esm/core/ir/sheet.d.ts +31 -0
  12. package/dist/esm/core/opc/relationships.js +1 -0
  13. package/dist/esm/core/spreadsheet-model/index.d.ts +1 -0
  14. package/dist/esm/core/spreadsheet-model/types.d.ts +189 -0
  15. package/dist/esm/excel/column-bands.d.ts +7 -0
  16. package/dist/esm/excel/column-bands.js +87 -0
  17. package/dist/esm/excel/conditional-format.d.ts +16 -0
  18. package/dist/esm/excel/conditional-format.js +327 -0
  19. package/dist/esm/excel/index.d.ts +4 -3
  20. package/dist/esm/excel/print-model.d.ts +2 -1
  21. package/dist/esm/excel/print-model.js +144 -13
  22. package/dist/esm/excel/sheet-drawing.d.ts +1 -1
  23. package/dist/esm/excel/sheet-to-flow.d.ts +3 -0
  24. package/dist/esm/excel/sheet-to-flow.js +51 -0
  25. package/dist/esm/excel/styles-parser.d.ts +1 -50
  26. package/dist/esm/excel/styles-parser.js +38 -1
  27. package/dist/esm/excel/table-parser.d.ts +2 -0
  28. package/dist/esm/excel/table-parser.js +51 -0
  29. package/dist/esm/excel/workbook-parser.d.ts +1 -5
  30. package/dist/esm/excel/worksheet-parser.d.ts +1 -60
  31. package/dist/esm/excel/worksheet-parser.js +265 -1
  32. package/dist/esm/excel/xlsx-reader.d.ts +3 -1
  33. package/dist/esm/excel/xlsx-reader.js +86 -51
  34. package/dist/esm/excel/xlsx-writer.d.ts +4 -0
  35. package/dist/esm/excel/xlsx-writer.js +278 -0
  36. package/dist/esm/html/html-writer.js +126 -4
  37. package/dist/esm/layout/styled-layout.js +160 -1
  38. package/dist/esm/pdf-reader/cmap.d.ts +5 -0
  39. package/dist/esm/pdf-reader/cmap.js +72 -0
  40. package/dist/esm/pdf-reader/content.d.ts +14 -0
  41. package/dist/esm/pdf-reader/content.js +292 -0
  42. package/dist/esm/pdf-reader/document.d.ts +23 -0
  43. package/dist/esm/pdf-reader/document.js +230 -0
  44. package/dist/esm/pdf-reader/flow-build.d.ts +4 -0
  45. package/dist/esm/pdf-reader/flow-build.js +27 -0
  46. package/dist/esm/pdf-reader/font.d.ts +4 -0
  47. package/dist/esm/pdf-reader/font.js +69 -0
  48. package/dist/esm/pdf-reader/layout.d.ts +3 -0
  49. package/dist/esm/pdf-reader/layout.js +69 -0
  50. package/dist/esm/pdf-reader/lexer.d.ts +43 -0
  51. package/dist/esm/pdf-reader/lexer.js +250 -0
  52. package/dist/esm/pdf-reader/parser.d.ts +10 -0
  53. package/dist/esm/pdf-reader/parser.js +86 -0
  54. package/dist/esm/pdf-reader/reader.d.ts +4 -0
  55. package/dist/esm/pdf-reader/reader.js +43 -0
  56. package/dist/esm/pdf-reader/struct-tree.d.ts +14 -0
  57. package/dist/esm/pdf-reader/struct-tree.js +92 -0
  58. package/dist/esm/pdf-reader/tagged.d.ts +3 -0
  59. package/dist/esm/pdf-reader/tagged.js +88 -0
  60. package/dist/esm/pdf-reader/text.d.ts +3 -0
  61. package/dist/esm/pdf-reader/text.js +18 -0
  62. package/package.json +4 -3
package/README.md CHANGED
@@ -30,11 +30,12 @@ import { Ream } from 'reamkit';
30
30
  // e.g. from an <input type="file"> or a fetch() — anything that yields bytes.
31
31
  const bytes = new Uint8Array(await file.arrayBuffer());
32
32
 
33
- const doc = Ream.parse(bytes); // docx or xlsx — sniffed
33
+ const doc = Ream.parse(bytes); // docx, xlsx or pdf — sniffed
34
34
  const pdf = await doc.convert('pdf'); // async — fetches a font if needed
35
35
  const svg = await doc.convert('svg'); // same parse, different target
36
36
  const html = await doc.convert('html'); // flowed HTML — needs no fonts at all
37
37
  const docx = await doc.convert('docx'); // write WordprocessingML back out
38
+ const xlsx = await doc.convert('xlsx'); // write SpreadsheetML back (xlsx source)
38
39
 
39
40
  // Hand the bytes to the browser: preview, download, upload, …
40
41
  const url = URL.createObjectURL(new Blob([pdf], { type: 'application/pdf' }));
@@ -129,7 +130,7 @@ rendering — inspect or analyze it without converting:
129
130
 
130
131
  ```ts
131
132
  const doc = Ream.parse(bytes);
132
- doc.format; // 'docx' | 'xlsx'
133
+ doc.format; // 'docx' | 'xlsx' | 'pdf'
133
134
  doc.flow.body; // paragraphs / tables / images / charts …
134
135
  doc.losses; // read-time losses
135
136
  ```
@@ -166,14 +167,15 @@ multi-section and multi-column layout/headers-footers (incl. PAGE/NUMPAGES
166
167
  fields)/footnotes and endnotes/hyperlinks and bookmarks/floating drawings/
167
168
  images/tracked changes, SpreadsheetML grids,
168
169
  number formats and the print model (gridlines, print area, fit-to-page,
169
- repeated titles, page breaks), DrawingML shapes and charts, OMML math,
170
- Type0+CIDFontType2 embedding with subsetting, Knuth-Plass line breaking,
171
- Liang hyphenation, OpenType ligatures/kerning + Arabic cursive joining,
172
- BiDi (UAX #9), hyperlinks (PDF link annotations + HTML anchors,
170
+ repeated titles, page breaks), **conditional formatting** (color scales, data
171
+ bars, icon sets), **sparklines** and **Excel tables**, DrawingML shapes and
172
+ charts, OMML math, Type0+CIDFontType2 embedding with subsetting, Knuth-Plass
173
+ line breaking, Liang hyphenation, OpenType ligatures/kerning + Arabic cursive
174
+ joining, BiDi (UAX #9), hyperlinks (PDF link annotations + HTML anchors,
173
175
  scheme-allowlisted), tagged PDF, PDF/A-1/2/3 (a/b/u), PDF/UA-1, AES-256
174
176
  encryption, digital signatures (PKCS#7/ECDSA/PAdES/RFC 3161), SVG page
175
- preview, flowed HTML export, and **docx output** (write WordprocessingML back
176
- out, incl. docx → docx). Reads OOXML Transitional and Strict.
177
+ preview, flowed HTML export, and **docx + xlsx output** (write WordprocessingML
178
+ / SpreadsheetML back out, incl. round-trips). Reads OOXML Transitional and Strict.
177
179
 
178
180
  See `handoff.md` for the full feature matrix and known limitations.
179
181
 
@@ -1,12 +1,16 @@
1
1
  import { ConvertDocxOptions } from '../../word/docx-to-pdf.js';
2
2
  import { DocumentReader } from '../ir/adapters.js';
3
3
  import { FlowDoc } from '../ir/flow.js';
4
+ import { SheetDoc } from '../ir/sheet.js';
4
5
  import { FontProvider } from '../fonts/provider.js';
5
6
  import { Loss, LossReport } from '../ir/index.js';
6
7
  import { FontBytesByVariant } from '../font/index.js';
7
8
  export interface ConvertOptions extends ConvertDocxOptions {
8
- /** Target: 'pdf' (default), 'svg' (page-stack preview), 'html' or 'docx' (flowed). */
9
- readonly to?: 'pdf' | 'svg' | 'html' | 'docx';
9
+ /**
10
+ * Target: 'pdf' (default), 'svg' (page-stack preview), 'html'/'docx' (flowed),
11
+ * or 'xlsx' (the native grid writer — spreadsheet input only).
12
+ */
13
+ readonly to?: 'pdf' | 'svg' | 'html' | 'docx' | 'xlsx';
10
14
  /**
11
15
  * Strict mode (handoff v1 §5): throw ConversionLossError on the first
12
16
  * recorded loss instead of returning it in the report.
@@ -26,18 +30,20 @@ export interface ConvertResult {
26
30
  readonly bytes: Uint8Array;
27
31
  readonly losses: LossReport;
28
32
  }
33
+ export type SourceDoc = FlowDoc | SheetDoc;
34
+ export declare function toFlowDoc(doc: SourceDoc): FlowDoc;
29
35
  export interface Converter {
30
36
  /** The registered readers, in sniffing order. */
31
- readonly readers: ReadonlyArray<DocumentReader<FlowDoc>>;
37
+ readonly readers: ReadonlyArray<DocumentReader<SourceDoc>>;
32
38
  /** Detect the input format by reader sniffing; undefined when unknown. */
33
- detect: (bytes: Uint8Array) => DocumentReader<FlowDoc> | undefined;
39
+ detect: (bytes: Uint8Array) => DocumentReader<SourceDoc> | undefined;
34
40
  convert: (bytes: Uint8Array, options?: ConvertOptions) => Promise<ConvertResult>;
35
41
  }
36
42
  export interface CreateConverterOptions {
37
43
  /** Override / extend the reader registry (defaults to docx + xlsx). */
38
- readonly readers?: ReadonlyArray<DocumentReader<FlowDoc>>;
44
+ readonly readers?: ReadonlyArray<DocumentReader<SourceDoc>>;
39
45
  }
40
- export declare const DEFAULT_READERS: ReadonlyArray<DocumentReader<FlowDoc>>;
46
+ export declare const DEFAULT_READERS: ReadonlyArray<DocumentReader<SourceDoc>>;
41
47
  export declare function createConverter(opts?: CreateConverterOptions): Converter;
42
48
  export declare function resolveFontsViaChain(providers: ReadonlyArray<FontProvider>): Promise<{
43
49
  fonts?: FontBytesByVariant;
@@ -3,16 +3,33 @@ import { FEATURES } from "../ir/features.js";
3
3
  import { ConversionLossError } from "../ir/loss.js";
4
4
  import { layoutStyledDocument } from "../../layout/styled-layout.js";
5
5
  import { docxReader } from "../../word/docx-reader.js";
6
+ import { projectSheetDoc } from "../../excel/sheet-to-flow.js";
6
7
  import { xlsxReader } from "../../excel/xlsx-reader.js";
7
8
  import { chainProviders } from "../fonts/provider.js";
8
9
  import { flowRenderOptions } from "./project.js";
9
10
  import { writeDocx } from "../../word/docx-writer.js";
11
+ import { writeXlsx } from "../../excel/xlsx-writer.js";
10
12
  import { writeHtml } from "../../html/html-writer.js";
11
13
  import { writeSvg } from "../../svg/svg-writer.js";
12
14
  import { convertDocxToPdf } from "../../word/docx-to-pdf.js";
13
15
  import { convertXlsxToPdf } from "../../excel/xlsx-to-pdf.js";
16
+ import { pdfReader } from "../../pdf-reader/reader.js";
14
17
  //#region src/core/converter/facade.ts
15
- var DEFAULT_READERS = [docxReader, xlsxReader];
18
+ function toFlowDoc(doc) {
19
+ return doc.kind === "sheet" ? projectSheetDoc(doc) : doc;
20
+ }
21
+ function readToFlow(reader, bytes) {
22
+ const { doc, losses } = reader.read(bytes);
23
+ return {
24
+ doc: toFlowDoc(doc),
25
+ losses
26
+ };
27
+ }
28
+ var DEFAULT_READERS = [
29
+ docxReader,
30
+ xlsxReader,
31
+ pdfReader
32
+ ];
16
33
  function createConverter(opts = {}) {
17
34
  const readers = opts.readers ?? DEFAULT_READERS;
18
35
  const detect = (bytes) => readers.find((r) => r.sniff(bytes));
@@ -22,7 +39,7 @@ function createConverter(opts = {}) {
22
39
  if (!reader) throw new Error("Unrecognized input format (no registered reader sniffs these bytes)");
23
40
  const losses = [];
24
41
  if (to === "docx") {
25
- const { doc: flow, losses: readLosses } = reader.read(bytes);
42
+ const { doc: flow, losses: readLosses } = readToFlow(reader, bytes);
26
43
  losses.push(...readLosses);
27
44
  const out = writeDocx(flow);
28
45
  losses.push(...out.losses);
@@ -33,7 +50,7 @@ function createConverter(opts = {}) {
33
50
  };
34
51
  }
35
52
  if (to === "html") {
36
- const { doc: flow, losses: readLosses } = reader.read(bytes);
53
+ const { doc: flow, losses: readLosses } = readToFlow(reader, bytes);
37
54
  losses.push(...readLosses);
38
55
  const html = writeHtml(flow);
39
56
  losses.push(...html.losses);
@@ -43,6 +60,18 @@ function createConverter(opts = {}) {
43
60
  losses
44
61
  };
45
62
  }
63
+ if (to === "xlsx") {
64
+ const { doc, losses: readLosses } = reader.read(bytes);
65
+ losses.push(...readLosses);
66
+ if (doc.kind !== "sheet") throw new Error("to: 'xlsx' requires a spreadsheet input; a flow document has no grid");
67
+ const out = writeXlsx(doc);
68
+ losses.push(...out.losses);
69
+ if (strict && losses.length > 0) throw new ConversionLossError(losses[0]);
70
+ return {
71
+ bytes: out.bytes,
72
+ losses
73
+ };
74
+ }
46
75
  let conv = rest;
47
76
  if (fontProviders && fontProviders.length > 0 && !rest.fonts && !rest.fontBytes) {
48
77
  const { fonts, loss } = await resolveFontsViaChain(fontProviders);
@@ -55,7 +84,7 @@ function createConverter(opts = {}) {
55
84
  if (to === "svg") {
56
85
  const fonts = conv.fonts ?? (conv.fontBytes ? { regular: conv.fontBytes } : void 0);
57
86
  if (!fonts) throw new Error("to: 'svg' requires options.fonts/fontBytes or fontProviders");
58
- const { doc: flow } = reader.read(bytes);
87
+ const { doc: flow } = readToFlow(reader, bytes);
59
88
  const svg = writeSvg(layoutStyledDocument(flow.body, {
60
89
  registry: FontRegistry.fromBytes(fonts),
61
90
  ...flowRenderOptions(flow)
@@ -110,4 +139,4 @@ async function resolveFontsViaChain(providers) {
110
139
  };
111
140
  }
112
141
  //#endregion
113
- export { DEFAULT_READERS, createConverter, resolveFontsViaChain };
142
+ export { DEFAULT_READERS, createConverter, resolveFontsViaChain, toFlowDoc };
@@ -1,14 +1,15 @@
1
- import { ConvertResult } from './facade.js';
1
+ import { ConvertResult, SourceDoc } from './facade.js';
2
2
  import { FontBytesByVariant } from '../font/index.js';
3
3
  import { FetchLike } from '../fonts/index.js';
4
4
  import { FontProvider } from '../fonts/provider.js';
5
5
  import { Loss } from '../ir/index.js';
6
6
  import { DocumentReader } from '../ir/adapters.js';
7
7
  import { FlowDoc } from '../ir/flow.js';
8
+ import { SheetDoc } from '../ir/sheet.js';
8
9
  import { SignatureOptions, StyledRenderOptions } from '../../pdf/index.js';
9
- export type ReamTarget = 'pdf' | 'svg' | 'html' | 'docx';
10
+ export type ReamTarget = 'pdf' | 'svg' | 'html' | 'docx' | 'xlsx';
10
11
  export interface ReamParseOptions {
11
- readonly readers?: ReadonlyArray<DocumentReader>;
12
+ readonly readers?: ReadonlyArray<DocumentReader<SourceDoc>>;
12
13
  }
13
14
  export interface ReamConvertOptions extends Omit<StyledRenderOptions, 'registry' | 'styles'> {
14
15
  readonly fonts?: FontBytesByVariant;
@@ -22,6 +23,7 @@ export interface ReamConvertOptions extends Omit<StyledRenderOptions, 'registry'
22
23
  }
23
24
  export declare class Ream {
24
25
  readonly flow: FlowDoc;
26
+ readonly sheet: SheetDoc | undefined;
25
27
  readonly losses: ReadonlyArray<Loss>;
26
28
  private readonly source;
27
29
  private readonly readerId;
@@ -7,18 +7,21 @@ import { renderStyledPdf, renderStyledPdfEncrypted } from "../../pdf/styled-page
7
7
  import "../../pdf/index.js";
8
8
  import { flowRenderOptions } from "./project.js";
9
9
  import { writeDocx } from "../../word/docx-writer.js";
10
+ import { writeXlsx } from "../../excel/xlsx-writer.js";
10
11
  import { writeHtml } from "../../html/html-writer.js";
11
12
  import { writeSvg } from "../../svg/svg-writer.js";
12
13
  import { resolveDocxAutoFonts } from "../../word/docx-to-pdf.js";
13
- import { DEFAULT_READERS, resolveFontsViaChain } from "./facade.js";
14
+ import { DEFAULT_READERS, resolveFontsViaChain, toFlowDoc } from "./facade.js";
14
15
  //#region src/core/converter/ream.ts
15
16
  var SOURCE_MIME = {
16
17
  docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
17
- xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
18
+ xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
19
+ pdf: "application/pdf"
18
20
  };
19
21
  var Ream = class Ream {
20
- constructor(flow, losses, source, readerId) {
22
+ constructor(flow, sheet, losses, source, readerId) {
21
23
  this.flow = flow;
24
+ this.sheet = sheet;
22
25
  this.losses = losses;
23
26
  this.source = source;
24
27
  this.readerId = readerId;
@@ -28,7 +31,8 @@ var Ream = class Ream {
28
31
  const reader = readers.find((r) => r.sniff(bytes));
29
32
  if (!reader) throw new Error(`Unrecognized document format (readers: ${readers.map((r) => r.id).join(", ")})`);
30
33
  const { doc, losses } = reader.read(bytes);
31
- return new Ream(doc, losses, bytes, reader.id);
34
+ const sheet = doc.kind === "sheet" ? doc : void 0;
35
+ return new Ream(toFlowDoc(doc), sheet, losses, bytes, reader.id);
32
36
  }
33
37
  get format() {
34
38
  return this.readerId;
@@ -56,6 +60,16 @@ var Ream = class Ream {
56
60
  losses
57
61
  };
58
62
  }
63
+ if (to === "xlsx") {
64
+ if (!this.sheet) throw new Error("convert('xlsx') requires a spreadsheet source; this document has no grid");
65
+ const xlsx = writeXlsx(this.sheet);
66
+ losses.push(...xlsx.losses);
67
+ this.enforceStrict(options, losses);
68
+ return {
69
+ bytes: xlsx.bytes,
70
+ losses
71
+ };
72
+ }
59
73
  const { fonts, registriesByFamily } = await this.resolveFonts(options, losses);
60
74
  const registry = FontRegistry.fromBytes(fonts);
61
75
  if (to === "svg") {
@@ -1 +1 @@
1
- export type { AbstractNumbering, Alignment, Border, BorderStyle, BodyElement, CellBorders, CellMerge, DocumentInfo, CellMargins, CellProperties, CellShading, Chart, ChartBlock, ChartDataPoint, ChartSeries, ChartType, CustomGeometry, CustomPathCmd, DocumentModel, FontFamilyMap, HeaderFooterReference, HeaderFooterType, ImageBlock, InlineImage, MathAccent, MathBar, MathDelimiter, MathEqArray, MathFraction, MathFunc, MathGroupChr, MathLimit, MathMatrix, MathNary, MathNode, MathRadical, MathRow, MathRun, MathScript, Numbering, NumberingFormat, NumberingInstance, NumberingLevel, NumberingReference, PageMargins, PageSize, Paragraph, ParagraphProperties, Run, RunProperties, RowProperties, Section, FloatAnchor, SectionColumns, SectionProperties, ShapeBlock, ShapeDash, ShapeFill, ShapeFillKind, ShapeGeometry, ShapeLine, ShapeTextBody, ShapeTransform, Style, StyleSheet, StyleType, Table, TableCell, TableLook, TableProperties, TableStyleCondition, TableStyleConditionType, TableStyleLayer, TableRow, UnderlineStyle, VerticalAlign, } from './types.js';
1
+ export type { AbstractNumbering, Alignment, Border, BorderStyle, BodyElement, CellBorders, CellMerge, DocumentInfo, CellMargins, CellProperties, CellShading, CellDataBar, CellIcon, CellIconShape, CellSparkline, Chart, ChartBlock, ChartDataPoint, ChartSeries, ChartType, CustomGeometry, CustomPathCmd, DocumentModel, FontFamilyMap, HeaderFooterReference, HeaderFooterType, ImageBlock, InlineImage, MathAccent, MathBar, MathDelimiter, MathEqArray, MathFraction, MathFunc, MathGroupChr, MathLimit, MathMatrix, MathNary, MathNode, MathRadical, MathRow, MathRun, MathScript, Numbering, NumberingFormat, NumberingInstance, NumberingLevel, NumberingReference, PageMargins, PageSize, Paragraph, ParagraphProperties, Run, RunProperties, RowProperties, Section, FloatAnchor, SectionColumns, SectionProperties, ShapeBlock, ShapeDash, ShapeFill, ShapeFillKind, ShapeGeometry, ShapeLine, ShapeTextBody, ShapeTransform, Style, StyleSheet, StyleType, Table, TableCell, TableLook, TableProperties, TableStyleCondition, TableStyleConditionType, TableStyleLayer, TableRow, UnderlineStyle, VerticalAlign, } from './types.js';
@@ -221,6 +221,25 @@ export interface CellMargins {
221
221
  export interface CellShading {
222
222
  readonly colorHex: string;
223
223
  }
224
+ export interface CellDataBar {
225
+ readonly fraction: number;
226
+ readonly colorHex: string;
227
+ readonly startFraction?: number;
228
+ }
229
+ export type CellIconShape = 'circle' | 'square' | 'diamond' | 'triangleUp' | 'triangleDown' | 'triangleRight' | 'check' | 'cross' | 'exclamation' | 'bars' | 'pie';
230
+ export interface CellIcon {
231
+ readonly shape: CellIconShape;
232
+ readonly colorHex: string;
233
+ readonly fill?: {
234
+ readonly filled: number;
235
+ readonly levels: number;
236
+ };
237
+ }
238
+ export interface CellSparkline {
239
+ readonly kind: 'line' | 'column' | 'winLoss';
240
+ readonly values: ReadonlyArray<number | null>;
241
+ readonly colorHex?: string;
242
+ }
224
243
  export type CellMerge = 'start' | 'middle' | 'end';
225
244
  export interface CellProperties {
226
245
  readonly width?: Pt;
@@ -229,6 +248,9 @@ export interface CellProperties {
229
248
  readonly borders?: CellBorders;
230
249
  readonly margins?: CellMargins;
231
250
  readonly shading?: CellShading;
251
+ readonly dataBar?: CellDataBar;
252
+ readonly icon?: CellIcon;
253
+ readonly sparkline?: CellSparkline;
232
254
  }
233
255
  export interface RowProperties {
234
256
  readonly height?: Pt;
@@ -255,6 +277,10 @@ export interface TableProperties {
255
277
  readonly defaultCellMargins?: CellMargins;
256
278
  readonly borders?: CellBorders;
257
279
  readonly alignment?: 'left' | 'center' | 'right';
280
+ readonly frozen?: {
281
+ readonly rows: number;
282
+ readonly cols: number;
283
+ };
258
284
  }
259
285
  export interface TableCell {
260
286
  readonly properties: CellProperties;
@@ -0,0 +1,8 @@
1
+ import { StrokeStyle, VectorPath } from '../vector.js';
2
+ export type SparklineKind = 'line' | 'column' | 'winLoss';
3
+ export interface SparklinePrim {
4
+ readonly paths: ReadonlyArray<VectorPath>;
5
+ readonly fillColorHex?: string;
6
+ readonly stroke?: StrokeStyle;
7
+ }
8
+ export declare function buildSparkline(kind: SparklineKind, values: ReadonlyArray<number | null>, width: number, height: number, colorHex?: string): Array<SparklinePrim>;
@@ -0,0 +1,111 @@
1
+ import { PathBuilder } from "../vector.js";
2
+ //#region src/core/drawingml/sparkline-geometry.ts
3
+ var DEFAULT_SERIES_HEX = "376092";
4
+ var LOSS_HEX = "D00000";
5
+ function buildSparkline(kind, values, width, height, colorHex) {
6
+ if (width <= 0 || height <= 0 || values.every((v) => v === null)) return [];
7
+ const color = colorHex ?? DEFAULT_SERIES_HEX;
8
+ switch (kind) {
9
+ case "line": return lineSpark(values, width, height, color);
10
+ case "column": return columnSpark(values, width, height, color);
11
+ case "winLoss": return winLossSpark(values, width, height, color);
12
+ }
13
+ }
14
+ function lineSpark(values, w, h, color) {
15
+ const n = values.length;
16
+ const pad = Math.min(1, h * .12);
17
+ const { min, span } = extent(values);
18
+ const x = (i) => n === 1 ? w / 2 : i / (n - 1) * w;
19
+ const y = (v) => pad + (v - min) / span * (h - 2 * pad);
20
+ const b = new PathBuilder();
21
+ let penDown = false;
22
+ for (let i = 0; i < n; i++) {
23
+ const v = values[i];
24
+ if (v === null || v === void 0) {
25
+ penDown = false;
26
+ continue;
27
+ }
28
+ if (penDown) b.lineTo(x(i), y(v));
29
+ else {
30
+ b.moveTo(x(i), y(v));
31
+ penDown = true;
32
+ }
33
+ }
34
+ return [{
35
+ paths: [b.build()],
36
+ stroke: {
37
+ colorHex: color,
38
+ widthPt: .75,
39
+ join: "round"
40
+ }
41
+ }];
42
+ }
43
+ function columnSpark(values, w, h, color) {
44
+ const n = values.length;
45
+ const { min, span } = extent(values);
46
+ const slot = w / n;
47
+ const bw = slot * .72;
48
+ const off = (slot - bw) / 2;
49
+ const pad = h * .06;
50
+ const paths = [];
51
+ for (let i = 0; i < n; i++) {
52
+ const v = values[i];
53
+ if (v === null || v === void 0) continue;
54
+ const frac = (v - min) / span;
55
+ const bh = Math.max(.4, pad + frac * (h - pad));
56
+ paths.push(rectPath(i * slot + off, 0, bw, bh));
57
+ }
58
+ return paths.length > 0 ? [{
59
+ paths,
60
+ fillColorHex: color
61
+ }] : [];
62
+ }
63
+ function winLossSpark(values, w, h, color) {
64
+ const n = values.length;
65
+ const slot = w / n;
66
+ const bw = slot * .72;
67
+ const off = (slot - bw) / 2;
68
+ const mid = h / 2;
69
+ const barH = h * .4;
70
+ const wins = [];
71
+ const losses = [];
72
+ for (let i = 0; i < n; i++) {
73
+ const v = values[i];
74
+ if (v === null || v === void 0 || v === 0) continue;
75
+ const x = i * slot + off;
76
+ if (v > 0) wins.push(rectPath(x, mid, bw, barH));
77
+ else losses.push(rectPath(x, mid - barH, bw, barH));
78
+ }
79
+ const out = [];
80
+ if (wins.length > 0) out.push({
81
+ paths: wins,
82
+ fillColorHex: color
83
+ });
84
+ if (losses.length > 0) out.push({
85
+ paths: losses,
86
+ fillColorHex: LOSS_HEX
87
+ });
88
+ return out;
89
+ }
90
+ function extent(values) {
91
+ let min = Infinity;
92
+ let max = -Infinity;
93
+ for (const v of values) {
94
+ if (v === null) continue;
95
+ if (v < min) min = v;
96
+ if (v > max) max = v;
97
+ }
98
+ if (min === Infinity) return {
99
+ min: 0,
100
+ span: 1
101
+ };
102
+ return {
103
+ min,
104
+ span: max - min || 1
105
+ };
106
+ }
107
+ function rectPath(x, y, w, h) {
108
+ return new PathBuilder().moveTo(x, y).lineTo(x + w, y).lineTo(x + w, y + h).lineTo(x, y + h).close().build();
109
+ }
110
+ //#endregion
111
+ export { buildSparkline };
@@ -12,8 +12,8 @@ export interface ReadResult<TDoc> {
12
12
  export interface DocumentReader<TDoc = FlowDoc> {
13
13
  /** Format id: 'docx', 'xlsx', 'pdf', … */
14
14
  readonly id: string;
15
- /** Which tree this reader produces. */
16
- readonly produces: 'flow' | 'page';
15
+ /** Which tree this reader produces ('sheet' projects to flow at the boundary). */
16
+ readonly produces: 'flow' | 'page' | 'sheet';
17
17
  /** Feature vocabulary this reader understands (capability matrix source). */
18
18
  readonly supports: ReadonlySet<Feature>;
19
19
  /** Cheap format detection (magic bytes / container markers). */
@@ -28,10 +28,10 @@ export interface WriteResult {
28
28
  readonly losses: LossReport;
29
29
  }
30
30
  export interface DocumentWriter<TDoc> {
31
- /** Output format id: 'pdf', 'svg', 'html', 'docx', … */
31
+ /** Output format id: 'pdf', 'svg', 'html', 'docx', 'xlsx', … */
32
32
  readonly id: string;
33
- /** Which tree this writer consumes. */
34
- readonly consumes: 'flow' | 'page';
33
+ /** Which tree this writer consumes ('sheet' for the native grid writer). */
34
+ readonly consumes: 'flow' | 'page' | 'sheet';
35
35
  readonly supports: ReadonlySet<Feature>;
36
36
  write: (doc: TDoc, opts?: WriteOptions) => WriteResult;
37
37
  }
@@ -0,0 +1,31 @@
1
+ import { Chart, DocumentInfo } from '../document-model/index.js';
2
+ import { DefinedName, ParsedWorksheet, XlsxStyles } from '../spreadsheet-model/index.js';
3
+ import { ResourceStore } from './resources.js';
4
+ export interface SheetChartRef {
5
+ readonly chartPartPath: string;
6
+ readonly widthPt: number;
7
+ readonly heightPt: number;
8
+ }
9
+ export interface Sheet {
10
+ readonly name: string;
11
+ readonly grid: ParsedWorksheet;
12
+ readonly charts?: ReadonlyArray<SheetChartRef>;
13
+ }
14
+ export interface SheetDoc {
15
+ readonly kind: 'sheet';
16
+ readonly sheets: ReadonlyArray<Sheet>;
17
+ /** §18.8 workbook style table (cellXfs + fonts/fills/borders/numFmts). */
18
+ readonly styles: XlsxStyles;
19
+ /** §18.4 shared string table; a cell with type 's' indexes into it. */
20
+ readonly sharedStrings: ReadonlyArray<string>;
21
+ /** §18.2.5 workbook defined names (print areas/titles, named ranges). */
22
+ readonly definedNames: ReadonlyArray<DefinedName>;
23
+ /** §18.2.28 1904 date system (serial-to-date epoch). */
24
+ readonly date1904: boolean;
25
+ /** Parsed charts keyed by part path (SheetChartRef.chartPartPath). */
26
+ readonly chartData?: ReadonlyMap<string, Chart>;
27
+ /** Content-addressed binary resources (sheet images). */
28
+ readonly resources: ResourceStore;
29
+ /** Document metadata from docProps/core.xml. */
30
+ readonly info?: DocumentInfo;
31
+ }
@@ -6,6 +6,7 @@ var parser = new XMLParser({
6
6
  attributeNamePrefix: "@_",
7
7
  parseAttributeValue: false,
8
8
  trimValues: false,
9
+ removeNSPrefix: true,
9
10
  isArray: (tagName) => tagName === "Relationship"
10
11
  });
11
12
  function parseRelationships(data) {
@@ -0,0 +1 @@
1
+ export type { CellType, WorksheetCell, ColumnWidth, MergedRange, RowHeight, XlsxPageMargins, XlsxPageSetup, XlsxPrintOptions, ParsedWorksheet, XlsxFont, XlsxFill, XlsxBorderStyleName, XlsxBorderEdge, XlsxBorder, XlsxHorizontalAlign, XlsxVerticalAlign, XlsxCellAlignment, XlsxCellXf, XlsxStyles, DefinedName, Dxf, CfOperator, CfRuleCellIs, CfvoType, Cfvo, CfRuleColorScale, CfRuleDataBar, CfRuleIconSet, CfRule, ConditionalFormat, SparklineKind, ParsedSparkline, ExcelTable, SheetPane, } from './types.js';