exstruct 0.2.2__tar.gz → 0.2.3__tar.gz

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,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: exstruct
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: Excel to structured JSON (tables, shapes, charts) for LLM/RAG pipelines
5
5
  Keywords: excel,structure,data,exstruct
6
6
  Author: harumiWeb
@@ -60,12 +60,12 @@ Description-Content-Type: text/markdown
60
60
 
61
61
  ![ExStruct Image](/docs/assets/icon.webp)
62
62
 
63
- ExStruct reads Excel workbooks and outputs structured data (tables, shapes, charts, hyperlinks) as JSON by default, with optional YAML/TOON formats. It targets both COM/Excel environments (rich extraction) and non-COM environments (cells + table candidates), with tunable detection heuristics and multiple output modes to fit LLM/RAG pipelines.
63
+ ExStruct reads Excel workbooks and outputs structured data (cells, table candidates, shapes, charts, print areas/views, hyperlinks) as JSON by default, with optional YAML/TOON formats. It targets both COM/Excel environments (rich extraction) and non-COM environments (cells + table candidates + print areas), with tunable detection heuristics and multiple output modes to fit LLM/RAG pipelines.
64
64
 
65
65
  ## Features
66
66
 
67
- - **Excel → Structured JSON**: cells, shapes, charts, and table candidates per sheet.
68
- - **Output modes**: `light` (cells + table candidates only), `standard` (texted shapes + arrows, charts), `verbose` (all shapes with width/height). Verbose also emits cell hyperlinks.
67
+ - **Excel → Structured JSON**: cells, shapes, charts, table candidates, and print areas/views per sheet.
68
+ - **Output modes**: `light` (cells + table candidates + print areas; no COM, shapes/charts empty), `standard` (texted shapes + arrows, charts, print areas), `verbose` (all shapes with width/height, charts with size, print areas). Verbose also emits cell hyperlinks. Size output is flag-controlled.
69
69
  - **Formats**: JSON (compact by default, `--pretty` available), YAML, TOON (optional dependencies).
70
70
  - **Table detection tuning**: adjust heuristics at runtime via API.
71
71
  - **CLI rendering** (Excel required): optional PDF and per-sheet PNGs.
@@ -85,7 +85,8 @@ Optional extras:
85
85
  - All extras at once: `pip install exstruct[yaml,toon,render]`
86
86
 
87
87
  Platform note:
88
- - Full extraction (shapes/charts) targets Windows + Excel (COM via xlwings). On other platforms, use `mode=light` to get cells + `table_candidates` safely.
88
+
89
+ - Full extraction (shapes/charts) targets Windows + Excel (COM via xlwings). On other platforms, use `mode=light` to get cells + `table_candidates`.
89
90
 
90
91
  ## Quick Start (CLI)
91
92
 
@@ -95,6 +96,7 @@ exstruct input.xlsx -o out.json --pretty # pretty JSON to a file
95
96
  exstruct input.xlsx --format yaml # YAML (needs pyyaml)
96
97
  exstruct input.xlsx --format toon # TOON (needs python-toon)
97
98
  exstruct input.xlsx --sheets-dir sheets/ # split per sheet in chosen format
99
+ exstruct input.xlsx --print-areas-dir areas/ # split per print area (if any)
98
100
  exstruct input.xlsx --mode light # cells + table candidates only
99
101
  exstruct input.xlsx --pdf --image # PDF and PNGs (Excel required)
100
102
  ```
@@ -112,7 +114,7 @@ set_table_detection_params(table_score_threshold=0.3, density_min=0.04)
112
114
  wb = extract("input.xlsx", mode="standard")
113
115
  export(wb, Path("out.json"), pretty=False) # compact JSON
114
116
 
115
- # Model helpers: iterate, index, and serialize directly from the models
117
+ # Model helpers: iterate, index, and serialize directly
116
118
  first_sheet = wb["Sheet1"] # __getitem__ access
117
119
  for name, sheet in wb: # __iter__ yields (name, SheetData)
118
120
  print(name, len(sheet.rows))
@@ -120,19 +122,23 @@ wb.save("out.json", pretty=True) # WorkbookData → file (by extension)
120
122
  first_sheet.save("sheet.json") # SheetData → file (by extension)
121
123
  print(first_sheet.to_yaml()) # YAML text (requires pyyaml)
122
124
 
123
- # ExStructEngine: per-instance options for extraction/output
124
- from exstruct import ExStructEngine, StructOptions, OutputOptions
125
-
126
- engine = ExStructEngine(
127
- options=StructOptions(mode="verbose"), # verbose includes hyperlinks by default
128
- output=OutputOptions(include_shapes=False, pretty=True),
129
- )
130
- wb2 = engine.extract("input.xlsx")
131
- engine.export(wb2, Path("out_filtered.json")) # drops shapes via OutputOptions
132
-
133
- # Enable hyperlinks in other modes
134
- engine_links = ExStructEngine(options=StructOptions(mode="standard", include_cell_links=True))
135
- with_links = engine_links.extract("input.xlsx")
125
+ # ExStructEngine: per-instance options for extraction/output
126
+ from exstruct import ExStructEngine, StructOptions, OutputOptions
127
+
128
+ engine = ExStructEngine(
129
+ options=StructOptions(mode="verbose"), # verbose includes hyperlinks by default
130
+ output=OutputOptions(include_shapes=False, pretty=True),
131
+ )
132
+ wb2 = engine.extract("input.xlsx")
133
+ engine.export(wb2, Path("out_filtered.json")) # drops shapes via OutputOptions
134
+
135
+ # Enable hyperlinks in other modes
136
+ engine_links = ExStructEngine(options=StructOptions(mode="standard", include_cell_links=True))
137
+ with_links = engine_links.extract("input.xlsx")
138
+
139
+ # Export per print area (if print areas exist)
140
+ from exstruct import export_print_areas_as
141
+ export_print_areas_as(wb, "areas", fmt="json", pretty=True)
136
142
  ```
137
143
 
138
144
  **Note (non-COM environments):** If Excel COM is unavailable, extraction still runs and returns cells + `table_candidates`; `shapes`/`charts` will be empty.
@@ -152,11 +158,11 @@ set_table_detection_params(
152
158
 
153
159
  Use higher thresholds to reduce false positives; lower them if true tables are missed.
154
160
 
155
- ## Output Modes
156
-
157
- - **light**: cells + table candidates (no COM needed).
158
- - **standard**: texted shapes + arrows, charts (COM if available), table candidates. Hyperlinks are off unless `include_cell_links=True`.
159
- - **verbose**: all shapes (with width/height), charts, table candidates, and cell hyperlinks.
161
+ ## Output Modes
162
+
163
+ - **light**: cells + table candidates (no COM needed).
164
+ - **standard**: texted shapes + arrows, charts (COM if available), table candidates. Hyperlinks are off unless `include_cell_links=True`.
165
+ - **verbose**: all shapes (with width/height), charts, table candidates, and cell hyperlinks.
160
166
 
161
167
  ## Error Handling / Fallbacks
162
168
 
@@ -185,6 +191,7 @@ To show how well exstruct can structure Excel, we parse a workbook that combines
185
191
  (Screenshot below is the actual sample Excel sheet)
186
192
  ![Sample Excel](/docs/assets/demo_sheet.png)
187
193
  Sample workbook: `sample/sample.xlsx`
194
+ Sample workbook: `sample/sample.xlsx`
188
195
 
189
196
  ### 1. Input: Excel Sheet Overview
190
197
 
@@ -371,6 +378,12 @@ In short, **exstruct = “an engine that converts Excel into a format AI can und
371
378
  - Default JSON is compact to reduce tokens; use `--pretty` or `pretty=True` when readability matters.
372
379
  - Field `table_candidates` replaces `tables`; adjust downstream consumers accordingly.
373
380
 
381
+ ## Print Areas (PrintArea / PrintAreaView)
382
+
383
+ - `SheetData.print_areas` holds print areas (cell coordinates) in light/standard/verbose.
384
+ - Use `export_print_areas_as(...)` or CLI `--print-areas-dir` to write one file per print area (nothing is written if none exist).
385
+ - `PrintAreaView` includes rows and table candidates inside the area, plus shapes/charts that overlap the area (size-less shapes are treated as points). `normalize=True` rebases row/col indices to the area origin.
386
+
374
387
  ## License
375
388
 
376
389
  BSD-3-Clause. See `LICENSE` for details.
@@ -378,18 +391,3 @@ BSD-3-Clause. See `LICENSE` for details.
378
391
  ## Documentation
379
392
 
380
393
  - API Reference (GitHub Pages): https://harumiweb.github.io/exstruct/
381
- # Engine option cheat sheet
382
-
383
- | Option class | Field | Meaning |
384
- | -------------- | ------------------- | ------- |
385
- | StructOptions | mode | "light"/"standard"/"verbose" |
386
- | | table_params | Dict passed to `set_table_detection_params` (table_score_threshold, density_min, coverage_min, min_nonempty_cells) |
387
- | | include_cell_links | Include cell hyperlinks in `rows[*].links` (None -> auto: verbose=True, others=False) |
388
- | OutputOptions | fmt | Default format ("json"/"yaml"/"yml"/"toon") |
389
- | | pretty / indent | Pretty-print JSON and control indent |
390
- | | include_rows | Include rows (False to drop) |
391
- | | include_shapes | Include shapes |
392
- | | include_charts | Include charts |
393
- | | include_tables | Include table_candidates |
394
- | | sheets_dir | Optional directory for per-sheet exports |
395
- | | stream | Default stream when output_path is None |
@@ -4,12 +4,12 @@
4
4
 
5
5
  ![ExStruct Image](/docs/assets/icon.webp)
6
6
 
7
- ExStruct reads Excel workbooks and outputs structured data (tables, shapes, charts, hyperlinks) as JSON by default, with optional YAML/TOON formats. It targets both COM/Excel environments (rich extraction) and non-COM environments (cells + table candidates), with tunable detection heuristics and multiple output modes to fit LLM/RAG pipelines.
7
+ ExStruct reads Excel workbooks and outputs structured data (cells, table candidates, shapes, charts, print areas/views, hyperlinks) as JSON by default, with optional YAML/TOON formats. It targets both COM/Excel environments (rich extraction) and non-COM environments (cells + table candidates + print areas), with tunable detection heuristics and multiple output modes to fit LLM/RAG pipelines.
8
8
 
9
9
  ## Features
10
10
 
11
- - **Excel → Structured JSON**: cells, shapes, charts, and table candidates per sheet.
12
- - **Output modes**: `light` (cells + table candidates only), `standard` (texted shapes + arrows, charts), `verbose` (all shapes with width/height). Verbose also emits cell hyperlinks.
11
+ - **Excel → Structured JSON**: cells, shapes, charts, table candidates, and print areas/views per sheet.
12
+ - **Output modes**: `light` (cells + table candidates + print areas; no COM, shapes/charts empty), `standard` (texted shapes + arrows, charts, print areas), `verbose` (all shapes with width/height, charts with size, print areas). Verbose also emits cell hyperlinks. Size output is flag-controlled.
13
13
  - **Formats**: JSON (compact by default, `--pretty` available), YAML, TOON (optional dependencies).
14
14
  - **Table detection tuning**: adjust heuristics at runtime via API.
15
15
  - **CLI rendering** (Excel required): optional PDF and per-sheet PNGs.
@@ -29,7 +29,8 @@ Optional extras:
29
29
  - All extras at once: `pip install exstruct[yaml,toon,render]`
30
30
 
31
31
  Platform note:
32
- - Full extraction (shapes/charts) targets Windows + Excel (COM via xlwings). On other platforms, use `mode=light` to get cells + `table_candidates` safely.
32
+
33
+ - Full extraction (shapes/charts) targets Windows + Excel (COM via xlwings). On other platforms, use `mode=light` to get cells + `table_candidates`.
33
34
 
34
35
  ## Quick Start (CLI)
35
36
 
@@ -39,6 +40,7 @@ exstruct input.xlsx -o out.json --pretty # pretty JSON to a file
39
40
  exstruct input.xlsx --format yaml # YAML (needs pyyaml)
40
41
  exstruct input.xlsx --format toon # TOON (needs python-toon)
41
42
  exstruct input.xlsx --sheets-dir sheets/ # split per sheet in chosen format
43
+ exstruct input.xlsx --print-areas-dir areas/ # split per print area (if any)
42
44
  exstruct input.xlsx --mode light # cells + table candidates only
43
45
  exstruct input.xlsx --pdf --image # PDF and PNGs (Excel required)
44
46
  ```
@@ -56,7 +58,7 @@ set_table_detection_params(table_score_threshold=0.3, density_min=0.04)
56
58
  wb = extract("input.xlsx", mode="standard")
57
59
  export(wb, Path("out.json"), pretty=False) # compact JSON
58
60
 
59
- # Model helpers: iterate, index, and serialize directly from the models
61
+ # Model helpers: iterate, index, and serialize directly
60
62
  first_sheet = wb["Sheet1"] # __getitem__ access
61
63
  for name, sheet in wb: # __iter__ yields (name, SheetData)
62
64
  print(name, len(sheet.rows))
@@ -64,19 +66,23 @@ wb.save("out.json", pretty=True) # WorkbookData → file (by extension)
64
66
  first_sheet.save("sheet.json") # SheetData → file (by extension)
65
67
  print(first_sheet.to_yaml()) # YAML text (requires pyyaml)
66
68
 
67
- # ExStructEngine: per-instance options for extraction/output
68
- from exstruct import ExStructEngine, StructOptions, OutputOptions
69
-
70
- engine = ExStructEngine(
71
- options=StructOptions(mode="verbose"), # verbose includes hyperlinks by default
72
- output=OutputOptions(include_shapes=False, pretty=True),
73
- )
74
- wb2 = engine.extract("input.xlsx")
75
- engine.export(wb2, Path("out_filtered.json")) # drops shapes via OutputOptions
76
-
77
- # Enable hyperlinks in other modes
78
- engine_links = ExStructEngine(options=StructOptions(mode="standard", include_cell_links=True))
79
- with_links = engine_links.extract("input.xlsx")
69
+ # ExStructEngine: per-instance options for extraction/output
70
+ from exstruct import ExStructEngine, StructOptions, OutputOptions
71
+
72
+ engine = ExStructEngine(
73
+ options=StructOptions(mode="verbose"), # verbose includes hyperlinks by default
74
+ output=OutputOptions(include_shapes=False, pretty=True),
75
+ )
76
+ wb2 = engine.extract("input.xlsx")
77
+ engine.export(wb2, Path("out_filtered.json")) # drops shapes via OutputOptions
78
+
79
+ # Enable hyperlinks in other modes
80
+ engine_links = ExStructEngine(options=StructOptions(mode="standard", include_cell_links=True))
81
+ with_links = engine_links.extract("input.xlsx")
82
+
83
+ # Export per print area (if print areas exist)
84
+ from exstruct import export_print_areas_as
85
+ export_print_areas_as(wb, "areas", fmt="json", pretty=True)
80
86
  ```
81
87
 
82
88
  **Note (non-COM environments):** If Excel COM is unavailable, extraction still runs and returns cells + `table_candidates`; `shapes`/`charts` will be empty.
@@ -96,11 +102,11 @@ set_table_detection_params(
96
102
 
97
103
  Use higher thresholds to reduce false positives; lower them if true tables are missed.
98
104
 
99
- ## Output Modes
100
-
101
- - **light**: cells + table candidates (no COM needed).
102
- - **standard**: texted shapes + arrows, charts (COM if available), table candidates. Hyperlinks are off unless `include_cell_links=True`.
103
- - **verbose**: all shapes (with width/height), charts, table candidates, and cell hyperlinks.
105
+ ## Output Modes
106
+
107
+ - **light**: cells + table candidates (no COM needed).
108
+ - **standard**: texted shapes + arrows, charts (COM if available), table candidates. Hyperlinks are off unless `include_cell_links=True`.
109
+ - **verbose**: all shapes (with width/height), charts, table candidates, and cell hyperlinks.
104
110
 
105
111
  ## Error Handling / Fallbacks
106
112
 
@@ -129,6 +135,7 @@ To show how well exstruct can structure Excel, we parse a workbook that combines
129
135
  (Screenshot below is the actual sample Excel sheet)
130
136
  ![Sample Excel](/docs/assets/demo_sheet.png)
131
137
  Sample workbook: `sample/sample.xlsx`
138
+ Sample workbook: `sample/sample.xlsx`
132
139
 
133
140
  ### 1. Input: Excel Sheet Overview
134
141
 
@@ -315,6 +322,12 @@ In short, **exstruct = “an engine that converts Excel into a format AI can und
315
322
  - Default JSON is compact to reduce tokens; use `--pretty` or `pretty=True` when readability matters.
316
323
  - Field `table_candidates` replaces `tables`; adjust downstream consumers accordingly.
317
324
 
325
+ ## Print Areas (PrintArea / PrintAreaView)
326
+
327
+ - `SheetData.print_areas` holds print areas (cell coordinates) in light/standard/verbose.
328
+ - Use `export_print_areas_as(...)` or CLI `--print-areas-dir` to write one file per print area (nothing is written if none exist).
329
+ - `PrintAreaView` includes rows and table candidates inside the area, plus shapes/charts that overlap the area (size-less shapes are treated as points). `normalize=True` rebases row/col indices to the area origin.
330
+
318
331
  ## License
319
332
 
320
333
  BSD-3-Clause. See `LICENSE` for details.
@@ -322,18 +335,3 @@ BSD-3-Clause. See `LICENSE` for details.
322
335
  ## Documentation
323
336
 
324
337
  - API Reference (GitHub Pages): https://harumiweb.github.io/exstruct/
325
- # Engine option cheat sheet
326
-
327
- | Option class | Field | Meaning |
328
- | -------------- | ------------------- | ------- |
329
- | StructOptions | mode | "light"/"standard"/"verbose" |
330
- | | table_params | Dict passed to `set_table_detection_params` (table_score_threshold, density_min, coverage_min, min_nonempty_cells) |
331
- | | include_cell_links | Include cell hyperlinks in `rows[*].links` (None -> auto: verbose=True, others=False) |
332
- | OutputOptions | fmt | Default format ("json"/"yaml"/"yml"/"toon") |
333
- | | pretty / indent | Pretty-print JSON and control indent |
334
- | | include_rows | Include rows (False to drop) |
335
- | | include_shapes | Include shapes |
336
- | | include_charts | Include charts |
337
- | | include_tables | Include table_candidates |
338
- | | sheets_dir | Optional directory for per-sheet exports |
339
- | | stream | Default stream when output_path is None |
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "exstruct"
3
- version = "0.2.2"
3
+ version = "0.2.3"
4
4
  description = "Excel to structured JSON (tables, shapes, charts) for LLM/RAG pipelines"
5
5
  readme = "README.md"
6
6
  license = { file = "LICENSE" }
@@ -0,0 +1,215 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+ from typing import Literal, Optional, TextIO
5
+
6
+ from .core.cells import set_table_detection_params
7
+ from .core.integrate import extract_workbook
8
+ from .engine import ExStructEngine, OutputOptions, StructOptions
9
+ from .io import (
10
+ save_as_json,
11
+ save_as_toon,
12
+ save_as_yaml,
13
+ save_print_area_views,
14
+ save_sheets,
15
+ serialize_workbook,
16
+ )
17
+ from .models import (
18
+ CellRow,
19
+ Chart,
20
+ ChartSeries,
21
+ PrintArea,
22
+ PrintAreaView,
23
+ Shape,
24
+ SheetData,
25
+ WorkbookData,
26
+ )
27
+ from .render import export_pdf, export_sheet_images
28
+
29
+ __all__ = [
30
+ "extract",
31
+ "export",
32
+ "export_sheets",
33
+ "export_sheets_as",
34
+ "export_print_areas_as",
35
+ "export_pdf",
36
+ "export_sheet_images",
37
+ "process_excel",
38
+ "ExtractionMode",
39
+ "CellRow",
40
+ "Shape",
41
+ "ChartSeries",
42
+ "Chart",
43
+ "SheetData",
44
+ "WorkbookData",
45
+ "PrintArea",
46
+ "PrintAreaView",
47
+ "set_table_detection_params",
48
+ "ExStructEngine",
49
+ "StructOptions",
50
+ "OutputOptions",
51
+ ]
52
+
53
+
54
+ ExtractionMode = Literal["light", "standard", "verbose"]
55
+
56
+
57
+ def extract(file_path: str | Path, mode: ExtractionMode = "standard") -> WorkbookData:
58
+ """
59
+ Extract an Excel workbook into WorkbookData.
60
+
61
+ Args:
62
+ file_path: Path to .xlsx/.xlsm/.xls.
63
+ mode: "light" / "standard" / "verbose"
64
+ - light: cells + table detection only (no COM, shapes/charts empty). Print areas via openpyxl.
65
+ - standard: texted shapes + arrows + charts (COM if available), print areas included. Shape/chart size is kept but hidden by default in output.
66
+ - verbose: all shapes (including textless) with size, charts with size.
67
+ """
68
+ include_links = True if mode == "verbose" else False
69
+ engine = ExStructEngine(
70
+ options=StructOptions(mode=mode, include_cell_links=include_links)
71
+ )
72
+ return engine.extract(file_path, mode=mode)
73
+
74
+
75
+ def export(
76
+ data: WorkbookData,
77
+ path: str | Path,
78
+ fmt: Optional[Literal["json", "yaml", "yml", "toon"]] = None,
79
+ *,
80
+ pretty: bool = False,
81
+ indent: int | None = None,
82
+ ) -> None:
83
+ """
84
+ Save WorkbookData to a file (format inferred from extension).
85
+
86
+ Args:
87
+ data: WorkbookData from `extract` or similar
88
+ path: destination path; extension is used to infer format
89
+ fmt: explicitly set format if desired (json/yaml/yml/toon)
90
+ pretty: pretty-print JSON
91
+ indent: JSON indent width (defaults to 2 when pretty=True and indent is None)
92
+ """
93
+ dest = Path(path)
94
+ format_hint = (fmt or dest.suffix.lstrip(".") or "json").lower()
95
+ match format_hint:
96
+ case "json":
97
+ save_as_json(data, dest, pretty=pretty, indent=indent)
98
+ case "yaml" | "yml":
99
+ save_as_yaml(data, dest)
100
+ case "toon":
101
+ save_as_toon(data, dest)
102
+ case _:
103
+ raise ValueError(f"Unsupported export format: {format_hint}")
104
+
105
+
106
+ def export_sheets(data: WorkbookData, dir_path: str | Path) -> dict[str, Path]:
107
+ """
108
+ Export each sheet as an individual JSON file.
109
+
110
+ - Payload: {book_name, sheet_name, sheet: SheetData}
111
+ - Returns: {sheet_name: Path}
112
+ """
113
+ return save_sheets(data, Path(dir_path), fmt="json")
114
+
115
+
116
+ def export_sheets_as(
117
+ data: WorkbookData,
118
+ dir_path: str | Path,
119
+ fmt: Literal["json", "yaml", "yml", "toon"] = "json",
120
+ *,
121
+ pretty: bool = False,
122
+ indent: int | None = None,
123
+ ) -> dict[str, Path]:
124
+ """Export each sheet in the given format (json/yaml/toon); returns sheet name to path map."""
125
+ return save_sheets(data, Path(dir_path), fmt=fmt, pretty=pretty, indent=indent)
126
+
127
+
128
+ def export_print_areas_as(
129
+ data: WorkbookData,
130
+ dir_path: str | Path,
131
+ fmt: Literal["json", "yaml", "yml", "toon"] = "json",
132
+ *,
133
+ pretty: bool = False,
134
+ indent: int | None = None,
135
+ normalize: bool = False,
136
+ ) -> dict[str, Path]:
137
+ """
138
+ Export each print area as a PrintAreaView.
139
+
140
+ Args:
141
+ data: WorkbookData that contains print areas
142
+ dir_path: output directory
143
+ fmt: json/yaml/yml/toon
144
+ pretty/indent: JSON formatting options
145
+ normalize: rebase row/col indices to the print-area origin when True
146
+ Returns:
147
+ dict mapping area key to path (e.g., "Sheet1#1": /.../Sheet1_area1_...json)
148
+ """
149
+ return save_print_area_views(
150
+ data,
151
+ Path(dir_path),
152
+ fmt=fmt,
153
+ pretty=pretty,
154
+ indent=indent,
155
+ normalize=normalize,
156
+ )
157
+
158
+
159
+ def process_excel(
160
+ file_path: Path,
161
+ output_path: Path | None = None,
162
+ out_fmt: str = "json",
163
+ image: bool = False,
164
+ pdf: bool = False,
165
+ dpi: int = 72,
166
+ mode: ExtractionMode = "standard",
167
+ pretty: bool = False,
168
+ indent: int | None = None,
169
+ sheets_dir: Path | None = None,
170
+ print_areas_dir: Path | None = None,
171
+ stream: TextIO | None = None,
172
+ ) -> None:
173
+ """
174
+ Convenience wrapper: extract → serialize (file or stdout) → optional PDF/PNG.
175
+
176
+ Args:
177
+ file_path: input Excel
178
+ output_path: None for stdout; otherwise, write to file
179
+ out_fmt: json/yaml/yml/toon
180
+ image/pdf: True to also output PNG/PDF (requires Excel + pypdfium2)
181
+ dpi: DPI for image output
182
+ mode: light/standard/verbose (same meaning as `extract`)
183
+ pretty/indent: JSON formatting
184
+ sheets_dir: directory to write per-sheet files
185
+ print_areas_dir: directory to write per-print-area files
186
+ stream: IO override when output_path is None
187
+ """
188
+ engine = ExStructEngine(
189
+ options=StructOptions(mode=mode),
190
+ output=OutputOptions(
191
+ fmt=out_fmt,
192
+ pretty=pretty,
193
+ indent=indent,
194
+ sheets_dir=sheets_dir,
195
+ print_areas_dir=print_areas_dir,
196
+ include_print_areas=None if mode == "light" else True,
197
+ include_shape_size=True if mode == "verbose" else False,
198
+ include_chart_size=True if mode == "verbose" else False,
199
+ stream=stream,
200
+ ),
201
+ )
202
+ engine.process(
203
+ file_path=file_path,
204
+ output_path=output_path,
205
+ out_fmt=out_fmt,
206
+ image=image,
207
+ pdf=pdf,
208
+ dpi=dpi,
209
+ mode=mode,
210
+ pretty=pretty,
211
+ indent=indent,
212
+ sheets_dir=sheets_dir,
213
+ print_areas_dir=print_areas_dir,
214
+ stream=stream,
215
+ )
@@ -57,6 +57,11 @@ def build_parser() -> argparse.ArgumentParser:
57
57
  type=Path,
58
58
  help="Optional directory to write one file per sheet (format follows --format).",
59
59
  )
60
+ parser.add_argument(
61
+ "--print-areas-dir",
62
+ type=Path,
63
+ help="Optional directory to write one file per print area (format follows --format).",
64
+ )
60
65
  return parser
61
66
 
62
67
 
@@ -80,6 +85,7 @@ def main(argv: list[str] | None = None) -> int:
80
85
  mode=args.mode,
81
86
  pretty=args.pretty,
82
87
  sheets_dir=args.sheets_dir,
88
+ print_areas_dir=args.print_areas_dir,
83
89
  )
84
90
  return 0
85
91
  except Exception as e:
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import logging
4
- from typing import Dict, List, Optional
4
+ from typing import Dict, List, Optional, Literal
5
5
 
6
6
  import xlwings as xw
7
7
 
@@ -166,7 +166,7 @@ def parse_series_formula(formula: str) -> Optional[Dict[str, Optional[str]]]:
166
166
  }
167
167
 
168
168
 
169
- def get_charts(sheet: xw.Sheet) -> List[Chart]:
169
+ def get_charts(sheet: xw.Sheet, mode: Literal["light", "standard", "verbose"] = "standard") -> List[Chart]:
170
170
  """Parse charts in a sheet into Chart models; failed charts carry an error field."""
171
171
  charts: List[Chart] = []
172
172
  for ch in sheet.charts:
@@ -182,6 +182,14 @@ def get_charts(sheet: xw.Sheet) -> List[Chart]:
182
182
  chart_type_label = XL_CHART_TYPE_MAP.get(
183
183
  chart_type_num, f"unknown_{chart_type_num}"
184
184
  )
185
+ chart_width: Optional[int] = None
186
+ chart_height: Optional[int] = None
187
+ try:
188
+ chart_width = int(ch.width)
189
+ chart_height = int(ch.height)
190
+ except Exception:
191
+ chart_width = None
192
+ chart_height = None
185
193
 
186
194
  for s in chart_com.SeriesCollection():
187
195
  parsed = parse_series_formula(getattr(s, "Formula", ""))
@@ -220,6 +228,8 @@ def get_charts(sheet: xw.Sheet) -> List[Chart]:
220
228
  title=title,
221
229
  y_axis_title=y_axis_title,
222
230
  y_axis_range=y_axis_range, # type: ignore
231
+ w=chart_width,
232
+ h=chart_height,
223
233
  series=series_list,
224
234
  l=int(ch.left),
225
235
  t=int(ch.top),