nbk2pdf 1.0.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.
data/README.md ADDED
@@ -0,0 +1,490 @@
1
+ # nbk2pdf
2
+
3
+ Converts Kindle Scribe notebooks (`nbk` files) into vector PDFs, including the
4
+ ruled or squared background the notes were written on.
5
+
6
+ ```
7
+ nbk2pdf sync ~/Documents/scribe # straight off a connected Kindle
8
+ nbk2pdf ~/kindle/.notebooks # or from files you already have
9
+ nbk2pdf -o ~/Documents/scribe --titles titles.yml /media/kindle/.notebooks
10
+ ```
11
+
12
+ It also takes a PDF you wrote on with the pen and produces a copy of it with
13
+ the handwriting on the pages, ready to print. See [Writing on a
14
+ PDF](#writing-on-a-pdf).
15
+
16
+ ## Installation
17
+
18
+ ```
19
+ gem install nbk2pdf
20
+ ```
21
+
22
+ Or add it to a Gemfile:
23
+
24
+ ```ruby
25
+ gem "nbk2pdf"
26
+ ```
27
+
28
+ Requires Ruby 3.1 or newer. The `sqlite3` gem ships a prebuilt native
29
+ extension for common platforms; elsewhere it builds against the system SQLite.
30
+
31
+ `nbk2pdf sync` additionally needs `gio`, from gvfs, which is already present on
32
+ most Linux desktops. Everything else works without it.
33
+
34
+ ## Syncing from a connected Kindle
35
+
36
+ ```
37
+ nbk2pdf sync # into the current directory
38
+ nbk2pdf sync ~/notes # into a directory of your choosing
39
+ ```
40
+
41
+ A Kindle speaks MTP rather than presenting a disk, so it cannot be mounted with
42
+ `mount(8)`. On Linux, gvfs exposes MTP devices as FUSE paths once `gio mount`
43
+ has attached them, and that is what `sync` drives:
44
+
45
+ 1. If a Kindle is already mounted, its notebooks are used as they are.
46
+ 2. Otherwise the MTP volume is located with `gio mount -li` and mounted.
47
+ 3. Every notebook under `.notebooks/` on the device is converted into the
48
+ destination directory, which is created if it does not exist.
49
+
50
+ Notebooks and PDFs written on are kept apart. Notebooks go side by side into
51
+ `notebooks/`. Each PDF goes into `documents/`, under the same folders it sits in
52
+ under `documents/` on the Kindle:
53
+
54
+ ```
55
+ ~/notes/
56
+ ├── notebooks/
57
+ │ ├── Reading list.pdf
58
+ │ └── 2026-09-22_dee63122-45e3-c7e2-ba57-7824a0bd244b.pdf
59
+ └── documents/
60
+ └── HoTT/
61
+ └── workbook.pdf
62
+ ```
63
+
64
+ Only `documents/` is looked in for PDFs, since it is the only folder the
65
+ Kindle's own reader lists, and so the only place a PDF can have been written on
66
+ with the pen.
67
+
68
+ The device is left mounted afterwards. Only top-level notebook folders are
69
+ converted, so the clipboard scratch buffer is skipped.
70
+
71
+ If nothing is found, USB is checked directly for Amazon's vendor id (`1949`) so
72
+ the two cases can be told apart:
73
+
74
+ * **not on the bus at all** -- reported immediately, since waiting will not
75
+ help: connect the cable and try again.
76
+ * **plugged in but not offered by gvfs** -- usually the screen is locked, or the
77
+ device is still settling after being attached, so this one is waited on for a
78
+ few seconds before giving up with a suggestion to unlock the screen or
79
+ reconnect the cable.
80
+
81
+ A Kindle that has dropped off the bus does not come back by itself; it needs the
82
+ cable unplugged and reconnected, which is what the message says.
83
+
84
+ The waits are measured against a monotonic clock rather than by counting sleeps,
85
+ because each probe of gvfs costs about half a second and a naive count made a
86
+ ten second budget take thirty.
87
+
88
+ ### Why notebook names have to be supplied
89
+
90
+ The Kindle shows a name for each notebook, but it is not in anything the device
91
+ shares over MTP. Checked on a Scribe:
92
+
93
+ * the `nbk` file does not contain it in UTF-8 or either UTF-16 byte order, and
94
+ its only metadata category holds just `book_id` and `language`;
95
+ * `book_id` decodes to a random version-4 UUID, so it is an opaque handle with
96
+ nothing to derive a name from;
97
+ * `.sync/KSDKNoteSyncDB.sqlite` holds notebook ids and sync bookkeeping only,
98
+ and does not mention the `book_id` either;
99
+ * a sweep of 1115 files and 84 MB across every other directory MTP exposes
100
+ turned up nothing, in any of the three encodings.
101
+
102
+ The names live on the system partition, which MTP does not share -- it offers
103
+ only `Internal Storage`, that is `/mnt/us`. So `sync` names files by date and
104
+ uuid, and offers the unnamed ones to `--titles` to fill in once.
105
+
106
+ ## Writing on a PDF
107
+
108
+ Copy a PDF into `documents/` on the Kindle, open it there, and write on its
109
+ pages. Then:
110
+
111
+ ```
112
+ nbk2pdf sync ~/Documents/scribe # with everything else
113
+ nbk2pdf -o done.pdf "$KINDLE/documents/HoTT/workbook.pdf" # just the one
114
+ nbk2pdf --annotations -o done.pdf "$KINDLE/documents/HoTT/workbook.pdf"
115
+ ```
116
+
117
+ The result is the original PDF, with its text, links and named destinations
118
+ unchanged and the ink drawn on each page it was written on. By default the ink
119
+ becomes part of the page, so every viewer and printer shows it and the pressure
120
+ taper is kept.
121
+
122
+ The copy is rebuilt rather than edited in place, and some document-level
123
+ entries do not survive it. Seen on the sample: the catalog's `/OpenAction` and
124
+ `/PageMode`, and the original dates and producer in the document info. From how
125
+ the PDF library works, `/PageLabels`, XMP metadata, the structure tree used for
126
+ accessibility, and optional-content layers (all layers end up shown) would be
127
+ lost too. Page content, links and named destinations are kept, and the library
128
+ carries the outline across as well.
129
+
130
+ `--annotations` adds each stroke as a PDF ink annotation instead, which a viewer
131
+ can hide, move or delete. Each carries an appearance drawn by the same code, so
132
+ it keeps the taper too, and it is flagged to print. Whether it actually prints
133
+ is up to the viewer: most print dialogs have a setting that leaves annotations
134
+ out. For paper, prefer the default.
135
+
136
+ A single PDF is written into the current directory unless `-o` says otherwise,
137
+ and the tool refuses to write over the original. `sync` writes it into
138
+ `DIR/documents/`, in the folders it has on the Kindle, under its own name.
139
+
140
+ The Kindle never changes the PDF itself, which is why copying it back over USB
141
+ gives you the blank one. What it writes instead is described in [6.
142
+ Documents](#6-documents-documentrb-krdsrb). Ink that cannot be placed on a page
143
+ is reported on stderr rather than dropped silently.
144
+
145
+ ## Naming
146
+
147
+ Standalone Scribe notebooks do not store the name you gave them on the device --
148
+ that lives in the Kindle's own library database, not in the `nbk` file. The
149
+ output name is therefore resolved in this order:
150
+
151
+ 1. an entry in `--titles`, keyed by the notebook's uuid or its book id
152
+ 2. the title inside the notebook, when it has one (notebooks attached to a book do)
153
+ 3. the notebook's modification date followed by its uuid,
154
+ e.g. `2025-09-19_29b6da80-211a-ebf9-56a2-ade81624ebfd.pdf`
155
+
156
+ `--titles` takes a JSON or YAML mapping:
157
+
158
+ ```yaml
159
+ 29b6da80-211a-ebf9-56a2-ade81624ebfd: The Humongous Book of Calculus Problems
160
+ bfa1ee49-16e8-a25e-0852-7273f4ef9e43: Reading list
161
+ ```
162
+
163
+ You do not have to write it by hand. Point `--titles` at a file that does not
164
+ exist yet and every notebook without a name is added to it, with its page count
165
+ and date as a comment so you can tell them apart:
166
+
167
+ ```
168
+ $ nbk2pdf sync --titles titles.yml notes
169
+ ...
170
+ 3 notebooks had no name; added to titles.yml to fill in
171
+
172
+ $ cat titles.yml
173
+ # 20 pages, last written 2026-09-19
174
+ 29b6da80-211a-ebf9-56a2-ade81624ebfd: ""
175
+ ```
176
+
177
+ Fill a name in between the quotes and every later run uses it. An entry left
178
+ blank still counts as answered, so you are not asked about it again.
179
+
180
+ ## Usage
181
+
182
+ ```
183
+ Convert Kindle Scribe notebooks to PDF.
184
+
185
+ Usage: nbk2pdf [options] PATH...
186
+ nbk2pdf sync [options] [DIR]
187
+
188
+ PATH may be an `nbk` file, a notebook directory, or any directory
189
+ containing notebooks (they are found recursively). It may also be a
190
+ PDF on the Kindle, to get a copy with what was written on it.
191
+
192
+ sync converts every notebook on a Kindle attached over USB into
193
+ DIR/notebooks, and every PDF on it that has been written on into
194
+ DIR/documents, in the folders it has under documents/ on the
195
+ Kindle. DIR is the current directory when left out. The device is
196
+ mounted over MTP first if it is not mounted already, and is left
197
+ mounted afterwards.
198
+
199
+ Options:
200
+ -o, --output PATH Output file, or directory for several notebooks
201
+ -n, --name NAME Override the output file name (single notebook only)
202
+ -t, --titles FILE JSON/YAML map of notebook uuid to the name you gave it
203
+ --[no-]template Draw the notebook's ruled background (default: yes)
204
+ -p, --paper SIZE Page size: letter (default), a4, native, ...
205
+ -a, --annotations Add ink to a PDF as annotations, not drawn into the page
206
+ -q, --quiet Only report errors
207
+ -h, --help Show this message
208
+ -v, --version Show the version
209
+ ```
210
+
211
+ A Scribe's `.notebooks` folder also holds a `clipboard/` scratch buffer, which
212
+ recursive discovery picks up like any other notebook. Pass notebook directories
213
+ explicitly if you would rather skip it.
214
+
215
+ ## The file format
216
+
217
+ Nothing about `nbk` is documented by Amazon, so here is what the converter does.
218
+ Each layer has its own module under `lib/kindle_notebook/`.
219
+
220
+ ### 1. The signature container (`container.rb`)
221
+
222
+ An `nbk` file is a SQLite database interleaved with 1024-byte signature blocks:
223
+ one after the first kibibyte, then one after every mebibyte.
224
+
225
+ ```
226
+ [ 1 KiB db ][ 1 KiB sig ][ 1 MiB db ][ 1 KiB sig ][ 1 MiB db ] ...
227
+ ```
228
+
229
+ Strip those and SQLite opens the result cleanly -- `PRAGMA integrity_check`
230
+ passes on every sample. The expected number of blocks is cross-checked against
231
+ the page size and page count in the SQLite header, so a file that does not fit
232
+ the layout is rejected rather than silently mangled.
233
+
234
+ ### 2. Ion fragments (`ion.rb`)
235
+
236
+ The database keeps content as rows of binary [Amazon
237
+ Ion](https://amazon-ion.github.io/ion-docs/) in `fragments` and
238
+ `local_delta_fragments` (the latter overrides the former, and marks deletions).
239
+ One row, `$ion_symbol_table`, holds the shared symbol table: it imports a
240
+ catalogue named `YJ_symbols` (max id 816) and appends the notebook's own
241
+ `nmdl.*` symbols.
242
+
243
+ `YJ_symbols` ships no text, so symbols inside the imported range keep their
244
+ numeric spelling -- which is why field names such as `$258` and `$141` appear in
245
+ the decoded structures, exactly as Amazon's own tooling renders them.
246
+
247
+ Fragment types are the Ion annotation on each row's top-level value:
248
+
249
+ | Type | Meaning |
250
+ |--------|-----------------------------------------------------------|
251
+ | `$258` | reading order: `$169` sections, the `$351` one lists pages |
252
+ | `$260` | a page, or a template |
253
+ | `$259` | a container of child items (`$146`), reached through `$176`|
254
+ | `$490` | book metadata (`book_id`, `language`, sometimes `title`) |
255
+ | `$538` | document data, including the default template |
256
+
257
+ ### 3. Pages and strokes (`notebook.rb`, `stroke_decoder.rb`)
258
+
259
+ A page records its canvas (15624 x 20832 sub-pixels at 2520 ppi on a Scribe) and
260
+ a tree of content items. Walking it through `$176` references and `$146`
261
+ children reaches the leaves, each carrying `nmdl.stroke_points`.
262
+
263
+ Coordinates are stored as a second-order delta stream -- the change *of the
264
+ change* between samples, which stays near zero for a smooth pen movement:
265
+
266
+ ```
267
+ [0x01 0x01] magic
268
+ [uint32-LE] number of values
269
+ [nibble stream] one 4-bit instruction per value, two per byte
270
+ [operand bytes] variable-width operands
271
+
272
+ bits 0-1 operand width: 0 = none, 1..3 = that many little-endian bytes
273
+ bit 2 the increment is the width value itself; no operands consumed
274
+ bit 3 negate the increment
275
+ ```
276
+
277
+ Decoded values are relative to the stroke's `nmdl.stroke_bounds` origin.
278
+
279
+ Strokes also carry pressure, tilt and per-point thickness factors, decoded by
280
+ the same scheme. Pressure drives the stroke taper (below). Tilt turns out to be
281
+ recorded once per stroke rather than per point, and both adjust factors are a
282
+ flat `100` everywhere in the samples, so neither is used.
283
+
284
+ ### 4. Templates
285
+
286
+ Each page names the background it was written on through `nmdl.template_id`.
287
+ It can be:
288
+
289
+ * a reference to a template fragment, e.g. `narrow_rule_margin_1860x2480`
290
+ * a placeholder symbol (`$349`), meaning the page is blank
291
+ * absent, in which case the document's default from `$538` applies
292
+
293
+ A template draws itself in its own 1860 x 2480 space with a flat op list --
294
+ `0 x y` to move, `1 x y` to draw, `4` to close -- plus an ARGB colour and a line
295
+ width. The converter scales that space onto the page.
296
+
297
+ ### 5. Rendering (`pdf_renderer.rb`)
298
+
299
+ Every PDF page matches the Scribe canvas exactly (`canvas / ppi * 72` points, so
300
+ 446.4 x 595.2 pt), with the Y axis flipped. Strokes become round-capped polyline
301
+ paths; a one-point stroke becomes a filled dot; highlighter strokes (brush type
302
+ 1) are drawn at 20% opacity.
303
+
304
+ #### Paper size
305
+
306
+ The device records its pages at 446.4 x 595.2 pt, which is 6.20 x 8.27 in or
307
+ 157.5 x 210 mm, in a 3:4 ratio. That is the Scribe's screen, and it matches no
308
+ standard paper size, so a print dialog offers to scale it.
309
+
310
+ Pages therefore go on **US Letter** by default, scaled to fit and centred so
311
+ nothing is stretched or cropped. `--paper` chooses something else:
312
+
313
+ ```
314
+ nbk2pdf --paper a4 notebook/ # A4 instead
315
+ nbk2pdf --paper native notebook/ # exactly as the device recorded it
316
+ nbk2pdf sync --paper a4 ~/notes
317
+ ```
318
+
319
+ Every size Prawn knows is accepted, including `a3`..`a6`, `letter`, `legal`,
320
+ `tabloid` and `executive`. On Letter the canvas fits the height and leaves 9 pt
321
+ at each side; on A4 it fits the width and leaves 24 pt above and below.
322
+
323
+ Note that the PDF sets no `/ViewerPreferences` and no `/PrintScaling`, so it
324
+ never tells a viewer whether scaling is allowed. If a print dialog greys out its
325
+ scale field, that is the viewer's own setting -- in Evince, the manual scale is
326
+ only editable once *Page Scaling* is set to *None*.
327
+
328
+ #### Stroke taper
329
+
330
+ The pen records a 12-bit pressure per point, and it varies a lot inside a single
331
+ stroke -- a median spread of 3071 out of 4095 across the samples -- so drawing
332
+ every stroke at one width loses the thinning at the ends of each pen movement.
333
+
334
+ Amazon documents no pressure-to-width curve, so it was fitted against the only
335
+ ground truth in the file set: the 600 x 800 thumbnails the device renders for
336
+ its own pages. Rendering each sample page at that size and taking the mean
337
+ absolute difference against the matching thumbnail gives, summed over three
338
+ notebooks:
339
+
340
+ | Model | difference |
341
+ |---------------------------------------------------|------------|
342
+ | flat width (no taper) | 14.8711 |
343
+ | width from *absolute* pressure, best fit | 14.8414 |
344
+ | width from pressure *relative to the stroke's mean*| **14.7932**|
345
+
346
+ The third model wins, which says the overall width of a stroke is already
347
+ carried by `nmdl.thickness`, and pressure shapes the taper within it. So the
348
+ width of each segment is
349
+
350
+ ```
351
+ level = sqrt(pressure / 4095)
352
+ factor = 1 + TAPER_SPAN * (level - mean level of this stroke)
353
+ width = nominal thickness * factor
354
+ ```
355
+
356
+ with `TAPER_SPAN = 1.15`, the flat part of the optimum (1.1 and 1.2 score
357
+ identically, 1.0 and 1.3 are worse).
358
+
359
+ Two cautions. The thumbnail cannot resolve taper directly -- the pen is 0.91 px
360
+ wide there and the taper range is entirely sub-pixel -- so what the measurement
361
+ compares is ink coverage, not shape. And a first attempt at fitting scored every
362
+ taper *worse* than flat, because that parameterisation only ever made strokes
363
+ thinner and the metric was reacting to the missing ink rather than to the taper;
364
+ the numbers above come from mean-preserving variants, where a control sweep of
365
+ plain width multipliers confirms the metric has a real optimum rather than just
366
+ rewarding more ink.
367
+
368
+ Widths are rounded to 8 steps so each stroke becomes a handful of sub-paths
369
+ instead of one per sample point. At 600 dpi (about 8x zoom) 8, 32 and 256 steps
370
+ are indistinguishable, and the runs share their junction points, so no seam
371
+ shows. The highlighter does not taper: its pressure barely moves along a stroke
372
+ while differing widely between strokes, so following it would give each band an
373
+ arbitrary thickness.
374
+
375
+ ### 6. Documents (`document.rb`, `krds.rb`)
376
+
377
+ Opening a PDF on the device and writing on it leaves the PDF untouched and
378
+ writes this beside it:
379
+
380
+ ```
381
+ documents/HoTT/workbook.pdf
382
+ documents/HoTT/workbook.sdr/<id>/book.kdf the device's copy of the layout
383
+ documents/HoTT/workbook.sdr/<id>/book.sdr/book<hash>.yjr the notes, as annotations
384
+ documents/HoTT/workbook.sdr/kpp_<id>/book.kdf the same layout again
385
+ .notebooks/<id>!!PDOC!!notebook/nbk the ink
386
+ ```
387
+
388
+ Only the ink and the notes are needed.
389
+
390
+ **The ink** is an ordinary notebook in the format above, one page per PDF page
391
+ written on. Each page records, besides its canvas, the frame the canvas
392
+ occupies on the PDF page as percentages from the top left: `$59` left, `$58`
393
+ top, `$56` width, `$57` height, each with unit `$314`. On a Letter page the
394
+ Scribe's 3:4 canvas is fitted to the width, so it is 103.03% as tall as the
395
+ page and starts 1.515% above it, 12 points past each edge.
396
+
397
+ **The notes** are in the `.yjr` file, a Kindle reader data store:
398
+
399
+ ```
400
+ [00 00 00 00 00 1A B1 26] signature
401
+ [long] version
402
+ [int] number of top-level fields
403
+ fields FE, name, typed values and nested fields, FF
404
+ ```
405
+
406
+ Values carry a type byte: `00` boolean (one byte), `01` int (4 bytes), `02`
407
+ long (8 bytes), `03` string (a flag byte, `01` for null, otherwise a 2-byte
408
+ length and UTF-8), `07` byte. All big-endian. Field names are strings without
409
+ the type byte. That is enough to read the whole file without knowing what any
410
+ field means.
411
+
412
+ Each note is a field `annotation.personal.handwritten_note`, nested inside
413
+ `annotation.cache.object` > `saved.avl.interval.tree`. It holds a start and an
414
+ end position, creation and modification times, a template string, and the id
415
+ of the ink page. The page is in the number after the colon of the position,
416
+ in its upper 16 bits:
417
+
418
+ | Page written on | Position | After the colon | >> 16 |
419
+ |-----------------|-------------------------|-----------------|-------|
420
+ | 1 | `AcYEAAAAAAAA:2` | `0x000002` | 0 |
421
+ | 2 | `AckEAAAAAAAA:65538` | `0x010002` | 1 |
422
+ | 50 | `AVkFAAAAAAAA:3211266` | `0x310002` | 49 |
423
+ | 183 | `AegGAAAAAAAA:11927554` | `0xB60002` | 182 |
424
+
425
+ The lower 16 bits were 2 every time. What the base64 part encodes is not
426
+ known and is not used. It is not an element of the layout in `book.kdf`: for
427
+ page 1 its bytes happen to contain 4, the id of that page's image there, which
428
+ is what a first attempt keyed on, but the other pages do not follow.
429
+
430
+ Pages with a `/Rotate` entry have not been seen yet, and their ink is placed
431
+ as if they were upright.
432
+
433
+ ## Library use
434
+
435
+ ```ruby
436
+ require "nbk2pdf"
437
+
438
+ notebook = KindleNotebook::Notebook.open("/media/kindle/.notebooks/<uuid>")
439
+ notebook.pages.each do |page|
440
+ puts "#{page.id}: #{page.strokes.size} strokes on #{page.template&.kind || "blank paper"}"
441
+ end
442
+
443
+ KindleNotebook::Converter.new.convert(notebook.path, output: "out/")
444
+
445
+ document = KindleNotebook::Document.open("/media/kindle/documents/HoTT/workbook.pdf")
446
+ document.notes.each { |note| puts "page #{note.page_index + 1}: #{note.page.strokes.size} strokes" }
447
+ KindleNotebook::DocumentRenderer.new.render(document, "workbook.pdf")
448
+ ```
449
+
450
+ ## Development
451
+
452
+ ```
453
+ bundle install
454
+ rake # tests and RuboCop
455
+ rake test
456
+ rake rubocop
457
+ bundle exec exe/nbk2pdf sample-notebooks
458
+ ```
459
+
460
+ The test suite builds synthetic notebooks from scratch -- `test/test_helper.rb`
461
+ contains a small binary Ion *writer* and an encoder for the stroke format -- so
462
+ the unit tests need no binary fixtures. It also writes the device's reader data
463
+ store format, so the same goes for PDFs written on.
464
+ `test/samples_test.rb` additionally runs against `sample-notebooks/` and is
465
+ skipped when that directory is absent. Documents go under
466
+ `sample-notebooks/device/`, laid out as on the device: `documents/...` with the
467
+ PDF and its `.sdr`, and `.notebooks/<id>!!PDOC!!notebook/`.
468
+
469
+ The stroke decoder is checked against values produced by an independent
470
+ implementation of the same encoding, so a round trip through this project's own
471
+ encoder cannot mask a shared mistake.
472
+
473
+ Rendering is checked by eye against the PNG thumbnails the device itself writes
474
+ to `sample-notebooks/thumbnails/`:
475
+
476
+ ```
477
+ bundle exec exe/nbk2pdf -o out sample-notebooks/<uuid>
478
+ pdftoppm -png -r 97 -f 1 -l 1 out/*.pdf page
479
+ ```
480
+
481
+ ## License
482
+
483
+ GNU General Public License v3.0 or later. See [COPYING](COPYING).
484
+
485
+ The encoding this converter decodes was described publicly by
486
+ [kindle_notebook_pdf_converter](https://github.com/rudolfstepan/kindle_notebook_pdf_converter),
487
+ which in turn traces its stroke decoder to `kfxlib` from John Howell's KFX Input
488
+ plugin for Calibre -- GPL v3. This project is an independent Ruby implementation
489
+ written from that description of the format rather than a port of either, but it
490
+ is released under the GPL so that its lineage stays unambiguous.
data/exe/nbk2pdf ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # SPDX-License-Identifier: GPL-3.0-or-later
3
+ # SPDX-FileCopyrightText: 2026 Sergey Avseyev
4
+ # frozen_string_literal: true
5
+
6
+ require "nbk2pdf"
7
+
8
+ exit KindleNotebook::CLI.start(ARGV)