@pptx-studio/cli 0.1.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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,29 @@
1
+ # @pptx-studio/cli
2
+
3
+ ## 0.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 7d905f4: First release.
8
+
9
+ `@pptx-studio/render-svg` turns a slide into an SVG string, and `pptx-studio render`
10
+ does it from the command line with no browser and no LibreOffice — text included,
11
+ measured out of the font's own tables. The other nine packages are what those two
12
+ are built on and are published because they have to be, not because their surface
13
+ is settled.
14
+
15
+ Everything here is pre-1.0 and the API will change. What will not change without a
16
+ very good reason is what the writer does to a package it was not asked to edit: a
17
+ part nobody touched is re-emitted byte for byte, across all 54 decks in the corpus.
18
+
19
+ ### Patch Changes
20
+
21
+ - Updated dependencies [7d905f4]
22
+ - @pptx-studio/census@0.1.0
23
+ - @pptx-studio/model@0.1.0
24
+ - @pptx-studio/opc@0.1.0
25
+ - @pptx-studio/render-svg@0.1.0
26
+ - @pptx-studio/text@0.1.0
27
+ - @pptx-studio/validate@0.1.0
28
+ - @pptx-studio/writer@0.1.0
29
+ - @pptx-studio/xml@0.1.0
package/LICENSE ADDED
@@ -0,0 +1,202 @@
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright 2026 Dewiride Technologies
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
package/NOTICE ADDED
@@ -0,0 +1,43 @@
1
+ PPTX Studio
2
+ Copyright 2026 Dewiride Technologies
3
+
4
+ This product includes software developed at Dewiride Technologies
5
+ (https://github.com/Dewiride-Open-Source/Dewiride-PPTX-Studio).
6
+
7
+ --------------------------------------------------------------------------------
8
+ Third-party material redistributed in this repository
9
+ --------------------------------------------------------------------------------
10
+
11
+ Entries are added here at the moment the material lands, not at release time.
12
+
13
+ * Apache POI
14
+ Copyright The Apache Software Foundation
15
+ Licensed under the Apache License, Version 2.0.
16
+ https://poi.apache.org/
17
+
18
+ @pptx-studio/geometry contains the 187 DrawingML preset shape definitions,
19
+ transcoded at build time from POI's presetShapeDefinitions.xml by
20
+ tools/geometry-codegen. The transcoded form is a re-encoding of that file's
21
+ content and carries POI's copyright.
22
+
23
+ The file itself is not redistributed in this repository. The generated
24
+ modules in packages/geometry/src/presets record the SHA-256 of the exact
25
+ input they were produced from:
26
+
27
+ presetShapeDefinitions.xml, 538970 bytes
28
+ sha256 4a762444d8d85876881c02a5b1dedf6f73006fcd8acb7b4e393435615b37c780
29
+
30
+ --------------------------------------------------------------------------------
31
+ Attributions the plan commits us to, listed so the obligation is visible before
32
+ the code arrives
33
+ --------------------------------------------------------------------------------
34
+
35
+ * Pattern fill tiles (sub-phase 2.7) are generated from Mono libgdiplus.
36
+ libgdiplus - Copyright Novell, Inc. and contributors - MIT
37
+
38
+ * Metric-compatible substitute fonts ship in a separate package,
39
+ @pptx-studio/fonts-metric-compat, under SIL OFL 1.1 with its own
40
+ OFL.txt and reserved-font-name handling. They are deliberately kept out
41
+ of this Apache-2.0 tree so the licence boundary is a package boundary.
42
+
43
+ See LEGAL.md for the clean-room record and the reimplementation basis.
package/README.md ADDED
@@ -0,0 +1,263 @@
1
+ # @pptx-studio/cli
2
+
3
+ **Command line tools for PPTX packages.** As of sub-phase 3.10 that is five
4
+ verbs: `render`, which draws slides as SVG with no browser and no LibreOffice;
5
+ `inspect`, which reads a deck and tells you what is in it; `validate`, which
6
+ checks it against the twenty-nine rules a `.pptx` must not break; `roundtrip`,
7
+ which reads a deck, writes it back, and proves nothing moved; and `bisect`,
8
+ which narrows a broken deck down to the change that breaks it.
9
+
10
+ Apache-2.0 · part of
11
+ [PPTX Studio](https://github.com/Dewiride-Open-Source/Dewiride-PPTX-Studio).
12
+
13
+ ```bash
14
+ npx @pptx-studio/cli render deck.pptx --out slides/
15
+ npx @pptx-studio/cli render deck.pptx --slide 1 --width 640 --out thumb.svg
16
+ npx @pptx-studio/cli render deck.pptx --json --font-dir ./fonts
17
+
18
+ npx @pptx-studio/cli inspect deck.pptx
19
+ npx @pptx-studio/cli inspect deck.pptx --parts --namespaces
20
+ npx @pptx-studio/cli inspect deck.pptx --json --out census.json
21
+
22
+ npx @pptx-studio/cli validate deck.pptx
23
+ npx @pptx-studio/cli validate deck.pptx --explain
24
+
25
+ npx @pptx-studio/cli roundtrip deck.pptx
26
+ npx @pptx-studio/cli roundtrip deck.pptx --write out.pptx
27
+
28
+ npx @pptx-studio/cli bisect deck.pptx
29
+ npx @pptx-studio/cli bisect original.pptx broken.pptx --oracle powerpoint
30
+ ```
31
+
32
+ ```
33
+ PACKAGE
34
+ =======
35
+ archive 188.5 MiB in 1616 entries
36
+ parts 995 (192.0 MiB inflated, 1.0:1 overall)
37
+ compression 1303 deflated, 313 stored
38
+ largest part /ppt/media/image1.png (639.5 KiB)
39
+
40
+ PRESENTATION
41
+ ------------
42
+ slide size 13.33 x 7.50 in (screen16x9)
43
+ sheets 300 slides, 4 layouts, 1 masters
44
+ notes 300 notes slides, 1 notes masters, 0 handout masters
45
+ structure 6 sections, 2 custom shows
46
+ fonts embedTrueTypeFonts=true saveSubsetFonts=true
47
+ ProbeAlpha regular charset 0
48
+
49
+ FEATURES
50
+ --------
51
+ 5194 Shapes phase 2.10
52
+ 30 Tables phase 4.1
53
+ 12 Charts phase 9.1
54
+ 60 Animation timelines carried across, never rendered
55
+ ```
56
+
57
+ Exit status is **1** when the census found a structural error and **0**
58
+ otherwise. Warnings and notes never fail the command, so `inspect` answers
59
+ "did this deck load, and is anything in it structurally broken" as a yes/no
60
+ question a script can branch on.
61
+
62
+ ## `validate`
63
+
64
+ ```
65
+ /ppt/slides/slide1.xml
66
+ V022 fatal
67
+ /p:sld/p:cSld/p:spTree/p:sp/p:nvSpPr/p:nvPr/p:ph/@type
68
+ type="hdr" is a whole-package refusal here, alone and with no other change. …
69
+
70
+ V027 did not run: it compares against the package as it was opened, and none was supplied. …
71
+
72
+ validate: 26 rule(s), 1 finding(s), 1 blocking
73
+ ```
74
+
75
+ Exit status is **1** when anything fatal was found and **0** otherwise; a
76
+ warning never fails the command.
77
+
78
+ Three of the twenty-nine rules compare a package against the package **as it was
79
+ opened**, and a file on the command line has no such history. They are skipped,
80
+ and the report names them rather than counting them as passes. That makes
81
+ `validate` a diagnostic; the export gate is `assertValid`, which the writer calls
82
+ with both packages in hand.
83
+
84
+ | flag | what it does |
85
+ | ----------- | ---------------------------------------------- |
86
+ | `--json` | the report as JSON |
87
+ | `--explain` | append the rationale for every rule that fired |
88
+ | `--quiet` | fatal findings only |
89
+ | `--out` | write to a file instead of stdout |
90
+
91
+ ## `roundtrip`
92
+
93
+ ```
94
+ roundtrip deck.pptx
95
+
96
+ read 47048 bytes, 29 entries
97
+ written 47048 bytes, 29 entries
98
+ export 0 part(s) re-serialized, 28 streamed
99
+ compared 28/28 parts identical (19 xml, 9 rels, 0 binary)
100
+
101
+ no differences
102
+ ```
103
+
104
+ Exit status is **1** when the two packages differ — and also when the export was
105
+ refused, or the file would not open at all, because CI wants one bit. The three
106
+ are distinguished on stderr, since each needs completely different work next.
107
+
108
+ **It is not a byte comparison, and it must not become one.** Entry order,
109
+ deflate level, timestamps and attribute order all differ legitimately between two
110
+ archives holding the same document. Nine of the corpus's PowerPoint-authored
111
+ decks come out exactly 1832 bytes smaller than they went in — every stored byte
112
+ of every entry identical — because Office writes a `0xA220` growth-hint extra
113
+ field on five of their entries and we do not. A byte-equality gate would have
114
+ been red on ten of fifty-one decks before a line of the renderer existed, over
115
+ padding.
116
+
117
+ What is compared instead: the canonical XML of every XML part, the relationship
118
+ graph with ids treated as opaque labels, and the SHA-256 of everything else. So a
119
+ deck PowerPoint has re-saved — which renumbers every `rId` — still compares
120
+ equal, while one where two `r:embed` values swapped what they point at does not.
121
+
122
+ | flag | what it does |
123
+ | ---------------- | ---------------------------------------------- |
124
+ | `--json` | the comparison as JSON, with a digest per part |
125
+ | `--quiet` | the differences only, without the tally |
126
+ | `--write <file>` | also save the package that was written |
127
+ | `--out <file>` | write the report to a file instead of stdout |
128
+
129
+ `--write` is there for the check nothing in this repository can make: open the
130
+ result in real PowerPoint. `bisect --oracle powerpoint` scripts it.
131
+
132
+ ## `bisect`
133
+
134
+ ```
135
+ bisect deck.pptx # against our own export of it
136
+ bisect original.pptx broken.pptx # against a package from somewhere else
137
+ ```
138
+
139
+ The debugger for this project. PowerPoint's whole diagnostic channel is one
140
+ sentence naming no part, no element and no reason, so the only way to find out
141
+ what it objects to is to ask again with less of the change present, and keep
142
+ asking. This is delta debugging — Zeller's `ddmin` over the set of changes
143
+ between the two packages, applied level by level down the tree.
144
+
145
+ ```
146
+ delta 135 change(s) in 7 entry(s)
147
+ oracle powerpoint, 5 run(s)
148
+
149
+ 1 change(s) in 1 entry(s), from a delta of 135, in 5 oracle run(s)
150
+
151
+ removed /[Content_Types].xml /Types/Default[3]
152
+ - <Default Extension="fntdata" ContentType="application/x-fontdata"/>
153
+ + (nothing)
154
+ ```
155
+
156
+ | flag | what it does |
157
+ | ----------------- | -------------------------------------------------- |
158
+ | `--oracle <name>` | `validate` (default), `powerpoint`, or `command` |
159
+ | `--command <cmd>` | for `--oracle command`; `{}` becomes the candidate |
160
+ | `--max-runs <n>` | ceiling on oracle runs (default 2000) |
161
+ | `--timeout <ms>` | per run, for the oracles that spawn something |
162
+ | `--progress` | a line per oracle run; a bisection is not quick |
163
+ | `--write <file>` | save the smallest package that still fails |
164
+ | `--json` | the result as JSON |
165
+ | `--out <file>` | write the report to a file instead of stdout |
166
+
167
+ **`--oracle powerpoint` opens each candidate with `OpenAndRepair` switched off,
168
+ and that is not a detail.** `Presentations.Open` has no repair parameter and
169
+ `Open2007` documents the default as on, while `DisplayAlerts` is documented to
170
+ answer a message box with its default — which for "PowerPoint found a problem
171
+ with content" is Repair. So the obvious script reports success on precisely the
172
+ files this project exists to avoid producing. Measured on one deck: with repair
173
+ off it fails with `0x80CB8002`; with repair on it opens, all three slides
174
+ intact.
175
+
176
+ The harness opens read-only with no window, writes nothing back, and disables
177
+ macros — `AutomationSecurity` defaults to _enabling_ them. If PowerPoint is
178
+ already running it attaches to that instance and never quits it.
179
+
180
+ ## `inspect` options
181
+
182
+ | flag | what it does |
183
+ | -------------- | --------------------------------------------------------------- |
184
+ | `--json` | the census as JSON, for a script or for committing as a fixture |
185
+ | `--parts` | the per-part table: bytes, elements, depth, relationships |
186
+ | `--namespaces` | every namespace, with the prefixes it was spelled with |
187
+ | `--top <n>` | rows per histogram before truncating (default 15) |
188
+ | `--out <file>` | write to a file instead of stdout |
189
+
190
+ ## Everything it knows lives somewhere else
191
+
192
+ `@pptx-studio/census` does the reading, `@pptx-studio/validate` does the judging
193
+ and `@pptx-studio/writer` does the writing and the comparing, and not one of the
194
+ three has any Node in it at all — it runs in a browser tab and in a Web Worker. This package supplies the
195
+ two things a browser cannot: a path off the filesystem and a stream to write to.
196
+
197
+ That split is the point. The drop-a-deck explorer in `apps/studio` computes
198
+ exactly the same object in a Worker, so an answer here and an answer in a tab
199
+ cannot drift apart.
200
+
201
+ ## Not built yet
202
+
203
+ The plan gives this package three more verbs. Asking for one says which sub-phase
204
+ brings it rather than "unknown command":
205
+
206
+ | verb | what it will do | sub-phase |
207
+ | ---------- | --------------------------------------------- | --------- |
208
+ | `fidelity` | score a render against a reference | 3.9 |
209
+ | `render` | render slides to SVG or PNG without a browser | 3.10 |
210
+ | `resolve` | show where a resolved property came from | 7.x |
211
+
212
+ ## `render`
213
+
214
+ Server-side thumbnailing with nothing installed but Node. The SVG stands alone:
215
+ pictures are embedded as `data:` URIs, gradients and patterns are `<defs>` in
216
+ the same document, and there is no external reference of any kind to resolve.
217
+
218
+ ```
219
+ 1 slide(s) at 1920x1080 -> thumb.svg
220
+ 3 typeface(s) from 412 indexed face(s), 1 substituted
221
+ Aptos -> Carlito
222
+ ```
223
+
224
+ **Text is measured, not guessed.** The browser renderer measures with
225
+ `OffscreenCanvas.measureText`; there is no canvas in Node, so this reads the
226
+ face's own `cmap`, `hmtx`, `GPOS` and `OS/2` tables instead. That is a second
227
+ measurement engine, and the risk of a second engine is that it quietly disagrees
228
+ with the first. Experiment T13 settled the arithmetic rather than assuming it:
229
+ seven fonts built so their tables disagree on purpose, 252 widths measured in
230
+ Chromium, and the reader reproduces **all 252 exactly**. The rules it found are
231
+ in [`corpus/ground-truth/font-metrics.json`](../../corpus/ground-truth/font-metrics.json)
232
+ and the reasoning is in
233
+ [ADR 0042](../../docs/adr/phase-3-text/0042-rendering-without-a-browser.md).
234
+
235
+ Fonts are found in this platform's own directories, plus any `--font-dir` you
236
+ name, which are searched first so you can override a face without installing
237
+ one. `--no-system-fonts` limits it to what you named; `--no-text` draws geometry
238
+ only and asks no font questions at all.
239
+
240
+ A typeface the machine does not have is substituted through the same table
241
+ `@pptx-studio/text` uses in the browser, so the two renderers cannot fall back
242
+ differently, and the substitution is reported rather than hidden. A code point
243
+ no indexed face can draw is reported too.
244
+
245
+ | flag | |
246
+ | ------------------- | ------------------------------------------------------------------- |
247
+ | `--slide <n>` | one slide, 1-based; every slide by default |
248
+ | `--width <px>` | the `width` attribute; the height follows the deck's aspect |
249
+ | `--out <path>` | a directory, or a file when rendering one slide; stdout when absent |
250
+ | `--font-dir <d>` | look here first, repeatable |
251
+ | `--no-system-fonts` | do not look in this platform's own font directories |
252
+ | `--no-text` | geometry only |
253
+ | `--json` | what was drawn, and which face drew each typeface |
254
+
255
+ ### What it does not do
256
+
257
+ No PNG. Rasterising would mean shipping a rasteriser, and the point of this verb
258
+ is that it needs nothing but Node — pipe the SVG to whatever you already have.
259
+
260
+ No shaping. Latin, Greek and Cyrillic measure correctly, and so does CJK, whose
261
+ advances do not depend on context. Arabic, Devanagari and the other scripts that
262
+ need a shaper will measure wide, because the reader sums unshaped advances. The
263
+ browser renderer has a real shaper and does not have this limit.
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {}
package/dist/cli.js ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ import { t as main } from "./main-DLx2onii.js";
3
+ //#region src/cli.ts
4
+ /**
5
+ * The bin.
6
+ *
7
+ * Deliberately three lines. Everything testable lives in `main`, which takes
8
+ * its streams as an argument and returns an exit code instead of calling
9
+ * `process.exit` - so the whole command line is exercised in-process by the
10
+ * test suite, with no child processes and no captured stdout.
11
+ */
12
+ process.exitCode = main(process.argv.slice(2));
13
+ //#endregion
14
+ export {};
15
+
16
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { main } from './main.js';\n\n/**\n * The bin.\n *\n * Deliberately three lines. Everything testable lives in `main`, which takes\n * its streams as an argument and returns an exit code instead of calling\n * `process.exit` - so the whole command line is exercised in-process by the\n * test suite, with no child processes and no captured stdout.\n */\nprocess.exitCode = main(process.argv.slice(2));\n"],"mappings":";;;;;;;;;;;AAWA,QAAQ,WAAW,KAAK,QAAQ,KAAK,MAAM,CAAC,CAAC"}