ctrl-kd 4.0.1__tar.gz → 4.5.1__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.
Files changed (79) hide show
  1. ctrl_kd-4.5.1/PKG-INFO +112 -0
  2. ctrl_kd-4.5.1/README.md +93 -0
  3. ctrl_kd-4.5.1/pyproject.toml +66 -0
  4. ctrl_kd-4.5.1/src/ctrl_kd.egg-info/PKG-INFO +112 -0
  5. ctrl_kd-4.5.1/src/ctrl_kd.egg-info/SOURCES.txt +67 -0
  6. {ctrl_kd-4.0.1 → ctrl_kd-4.5.1}/src/ctrlkd/__init__.py +1 -1
  7. {ctrl_kd-4.0.1 → ctrl_kd-4.5.1}/src/ctrlkd/cli.py +150 -6
  8. {ctrl_kd-4.0.1 → ctrl_kd-4.5.1}/src/ctrlkd/core.py +1703 -92
  9. ctrl_kd-4.5.1/src/ctrlkd/emit.py +3056 -0
  10. {ctrl_kd-4.0.1 → ctrl_kd-4.5.1}/src/ctrlkd/fontmap.py +98 -8
  11. ctrl_kd-4.5.1/src/ctrlkd/info.py +176 -0
  12. ctrl_kd-4.5.1/src/ctrlkd/layout.py +652 -0
  13. ctrl_kd-4.5.1/src/ctrlkd/pdf.py +6258 -0
  14. ctrl_kd-4.5.1/src/ctrlkd/pictures.py +180 -0
  15. ctrl_kd-4.5.1/src/ctrlkd/pix.py +600 -0
  16. ctrl_kd-4.5.1/src/ctrlkd/piximg.py +194 -0
  17. ctrl_kd-4.5.1/src/ctrlkd/samples/LYING.WS +0 -0
  18. ctrl_kd-4.5.1/src/ctrlkd/samples/OCAPTAIN.WS +38 -0
  19. ctrl_kd-4.5.1/src/ctrlkd/samples/TWAINLET.WS +56 -0
  20. ctrl_kd-4.5.1/src/ctrlkd/samples/WARPRAYR.WS +0 -0
  21. {ctrl_kd-4.0.1 → ctrl_kd-4.5.1}/src/ctrlkd/wschange.py +1 -2
  22. {ctrl_kd-4.0.1 → ctrl_kd-4.5.1}/tests/test_ctrlkd.py +2313 -107
  23. ctrl_kd-4.5.1/tests/test_endnote_leading_gap.py +107 -0
  24. ctrl_kd-4.5.1/tests/test_fidelity_gate.py +642 -0
  25. ctrl_kd-4.5.1/tests/test_flags_toc_inline.py +307 -0
  26. ctrl_kd-4.5.1/tests/test_glyph_aspect.py +186 -0
  27. ctrl_kd-4.5.1/tests/test_layout_marks.py +221 -0
  28. ctrl_kd-4.5.1/tests/test_lj6dtp_bullet_glyph.py +79 -0
  29. ctrl_kd-4.5.1/tests/test_lj6dtp_char_substitution.py +184 -0
  30. ctrl_kd-4.5.1/tests/test_lj6dtp_colour_restore.py +77 -0
  31. ctrl_kd-4.5.1/tests/test_lj6dtp_heading_face.py +277 -0
  32. ctrl_kd-4.5.1/tests/test_lj6dtp_hp_patterns.py +115 -0
  33. ctrl_kd-4.5.1/tests/test_lj6dtp_legend_line_spacing.py +68 -0
  34. ctrl_kd-4.5.1/tests/test_lj6dtp_pcl_rectangles.py +222 -0
  35. ctrl_kd-4.5.1/tests/test_lj6dtp_shading_table_rules.py +73 -0
  36. ctrl_kd-4.5.1/tests/test_lj6dtp_table_rule_weight.py +82 -0
  37. ctrl_kd-4.5.1/tests/test_load_plugins.py +107 -0
  38. ctrl_kd-4.5.1/tests/test_modern_box_regions.py +197 -0
  39. ctrl_kd-4.5.1/tests/test_modern_line_spacing.py +234 -0
  40. ctrl_kd-4.5.1/tests/test_modern_lint.py +1859 -0
  41. ctrl_kd-4.5.1/tests/test_note_rulings_20260824.py +266 -0
  42. ctrl_kd-4.5.1/tests/test_paper_verdicts.py +90 -0
  43. ctrl_kd-4.5.1/tests/test_pcl_fidelity.py +102 -0
  44. ctrl_kd-4.5.1/tests/test_pcl_tolerance.py +567 -0
  45. ctrl_kd-4.5.1/tests/test_pictures.py +812 -0
  46. ctrl_kd-4.5.1/tests/test_pix.py +582 -0
  47. ctrl_kd-4.5.1/tests/test_polarity_gate.py +197 -0
  48. ctrl_kd-4.5.1/tests/test_printed_fidelity.py +897 -0
  49. ctrl_kd-4.5.1/tests/test_samples.py +140 -0
  50. ctrl_kd-4.5.1/tests/test_sawyer_corpus.py +208 -0
  51. ctrl_kd-4.5.1/tests/test_screenplay_detection.py +191 -0
  52. ctrl_kd-4.5.1/tests/test_screenplay_pdf.py +232 -0
  53. ctrl_kd-4.5.1/tests/test_screenplay_rendering.py +145 -0
  54. ctrl_kd-4.5.1/tests/test_sentence_spacing_n9.py +299 -0
  55. ctrl_kd-4.5.1/tests/test_style_leading.py +491 -0
  56. ctrl_kd-4.5.1/tests/test_verse_quote_couplet.py +109 -0
  57. ctrl_kd-4.5.1/tests/test_verse_spacing.py +95 -0
  58. {ctrl_kd-4.0.1 → ctrl_kd-4.5.1}/tests/test_writer.py +6 -78
  59. ctrl_kd-4.5.1/tests/test_wschange.py +77 -0
  60. ctrl_kd-4.0.1/PKG-INFO +0 -170
  61. ctrl_kd-4.0.1/README.md +0 -151
  62. ctrl_kd-4.0.1/pyproject.toml +0 -36
  63. ctrl_kd-4.0.1/src/ctrl_kd.egg-info/PKG-INFO +0 -170
  64. ctrl_kd-4.0.1/src/ctrl_kd.egg-info/SOURCES.txt +0 -25
  65. ctrl_kd-4.0.1/src/ctrlkd/emit.py +0 -1085
  66. ctrl_kd-4.0.1/src/ctrlkd/info.py +0 -59
  67. ctrl_kd-4.0.1/src/ctrlkd/layout.py +0 -341
  68. ctrl_kd-4.0.1/src/ctrlkd/pdf.py +0 -1938
  69. ctrl_kd-4.0.1/tests/test_wschange.py +0 -194
  70. {ctrl_kd-4.0.1 → ctrl_kd-4.5.1}/LICENSE +0 -0
  71. {ctrl_kd-4.0.1 → ctrl_kd-4.5.1}/setup.cfg +0 -0
  72. {ctrl_kd-4.0.1 → ctrl_kd-4.5.1}/src/ctrl_kd.egg-info/dependency_links.txt +0 -0
  73. {ctrl_kd-4.0.1 → ctrl_kd-4.5.1}/src/ctrl_kd.egg-info/entry_points.txt +0 -0
  74. {ctrl_kd-4.0.1 → ctrl_kd-4.5.1}/src/ctrl_kd.egg-info/top_level.txt +0 -0
  75. {ctrl_kd-4.0.1 → ctrl_kd-4.5.1}/src/ctrlkd/afm.py +0 -0
  76. {ctrl_kd-4.0.1 → ctrl_kd-4.5.1}/src/ctrlkd/convert.py +0 -0
  77. {ctrl_kd-4.0.1 → ctrl_kd-4.5.1}/src/ctrlkd/symbolmap.py +0 -0
  78. {ctrl_kd-4.0.1 → ctrl_kd-4.5.1}/src/ctrlkd/typestyles.py +0 -0
  79. {ctrl_kd-4.0.1 → ctrl_kd-4.5.1}/src/ctrlkd/writer.py +0 -0
ctrl_kd-4.5.1/PKG-INFO ADDED
@@ -0,0 +1,112 @@
1
+ Metadata-Version: 2.4
2
+ Name: ctrl-kd
3
+ Version: 4.5.1
4
+ Summary: Convert WordStar 4-7 documents and print-to-disk files to text, Markdown, HTML, RTF, or PDF. ^KD: save and done.
5
+ Author: Jon Michaels
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/jonmichaels/ctrl-kd
8
+ Keywords: wordstar,converter,retrocomputing,archive,dos
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: Environment :: Console
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Topic :: Text Processing :: Filters
14
+ Classifier: Topic :: System :: Archiving
15
+ Requires-Python: >=3.9
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Dynamic: license-file
19
+
20
+ ```
21
+ __ __ __ __
22
+ _____/ /______/ / / /______/ /
23
+ / ___/ __/ ___/ /_____/ //_/ __ /
24
+ / /__/ /_/ / / /_____/ ,< / /_/ /
25
+ \___/\__/_/ /_/ /_/|_|\__,_/
26
+ ```
27
+ # ctrl-kd
28
+
29
+ Convert WordStar for DOS v4-v7 files to modern formats. **^KD: save and done.**
30
+
31
+ `ctrl-kd` reads WordStar for DOS documents, and WordStar print stream files,
32
+ and writes plain text, Markdown, HTML, RTF, or PDF (set on a viewer's
33
+ built-in base-14 fonts — no dependencies, nothing embedded, the page as it
34
+ would have printed: printed mode follows the document's own font blocks and
35
+ its own layout arithmetic, while modern mode is the same document reflowed
36
+ for today: its fonts, headers, and footnotes all carried.
37
+
38
+ ```console
39
+ $ ctrl-kd ESSAY.WS # -> ESSAY.rtf: modern reflow, the
40
+ # document's own fonts carried
41
+ $ ctrl-kd --mode printed LETTER.WS # -> LETTER.pdf: the 1990 facsimile
42
+ $ ctrl-kd ESSAY.WS -t md # modern markdown instead
43
+ $ ctrl-kd ESSAY.WS -t html -t rtf # multiple formats
44
+ $ ctrl-kd --page-settings sawyer X.WS # a known machine's page defaults
45
+ $ ctrl-kd --diagnose MYSTERY.FIL # what IS this file?
46
+ $ ctrl-kd --comments MEMO.WS # include the author's hidden comments
47
+ $ ctrl-kd --no-notes PAPER.WS # body text only, no notes
48
+ $ ctrl-kd --samples DIR # write 4 bundled public-domain sample .WS files into DIR
49
+ ```
50
+
51
+ ## Modes
52
+
53
+ * `--mode modern` (default; bare runs produce RTF): the document brought to a
54
+ modern audience — reflowed, its own fonts and styles carried, footnotes at
55
+ the page bottom, gaps the file never specified filled with today's
56
+ conventions (a comfortable serif at reading size, one-inch margins).
57
+ * `--mode printed` (bare runs produce PDF): every line as laid out, on the
58
+ era's own page — how it came off the printer. Gaps are filled with 1990's
59
+ conventions instead; `--page-settings` supplies a particular machine's.
60
+
61
+ More information: **[FAQ.md](FAQ.md)**, and **[ERAS.md](ERAS.md)**.
62
+
63
+ ## Install
64
+
65
+ ```console
66
+ $ brew install jonmichaels/tap/ctrl-kd # macOS / Linuxbrew
67
+ $ pipx install ctrl-kd # or: pip install ctrl-kd
68
+ ```
69
+ Download Windows x86_64: [Latest Version](https://github.com/jonmichaels/ctrl-kd/releases/latest/download/ctrl-kd-windows-x86_64.zip)
70
+
71
+ Python ≥ 3.9, no dependencies. Library API: `ctrlkd.convert(data, to='html')`.
72
+
73
+ ## Adding an output format
74
+
75
+ An output format is one function over the parsed document — register it with the
76
+ `@ctrlkd.emitter` decorator, or ship it as a pip-installable plugin via the
77
+ `ctrlkd.emitters` entry-point group and it appears in the CLI automatically.
78
+ **[EXTENDING.md](EXTENDING.md)** has the IR contract, a complete worked example
79
+ (BBCode in ~40 lines), and a checklist.
80
+
81
+ ## Siblings
82
+
83
+ **[Soft Return](https://github.com/jonmichaels/soft-return)** — macOS viewer and converter plus QuickLook extension.
84
+ Includes a Swift command line utility.
85
+
86
+ ## Lineage
87
+
88
+ I wanted to be able to see the 70-some WordStar 4 files I had from junior high
89
+ and high school. In about an hour and half my agent had my files looking
90
+ pretty good. And then I fell down the research rabbit hole...
91
+
92
+ `ctrl-kd` wouldn't have been possible without the tools and documentation that
93
+ kept WordStar readable: Yohanes Nugroho's WS-CON, Michael Petrie's English port,
94
+ the `wsconvert`project, Robert J. Sawyer's WordStar archive, and the WordStar
95
+ format documentation community.
96
+
97
+ My own test files are personal and are not distributed — this repo's tests use
98
+ synthetic fixtures and some public domain docs I retyped in WordStar 4 and
99
+ WordStar 7 in DOSBox-X, plus Robert J. Sawyer's public WS7 archive (opt-in,
100
+ `pytest -m sawyer`; see `tests/SAWYER-CORPUS.md`) you can run your own tests
101
+ against that if you have a copy: download the archive, point
102
+ `CTRLKD_SAWYER_ARCHIVE` at its top-level directory (the one holding
103
+ `CONVERT.WS`, `INSET/`, `ARTICLES/`), and verify the path before arming —
104
+ `tests/SAWYER-CORPUS.md` has a one-line smoke check. A `CTRLKD_PRIVATE_CORPUS`
105
+ variable also exists, for my own private regression fixtures — it has no
106
+ effect unless you're me.
107
+
108
+ ## Credits
109
+
110
+ Written by Jon Michaels — whose 1987–1992 WordStar files, and the need to read
111
+ them again, are the reason this exists — with Athena (Claude, Anthropic) as
112
+ co-author.
@@ -0,0 +1,93 @@
1
+ ```
2
+ __ __ __ __
3
+ _____/ /______/ / / /______/ /
4
+ / ___/ __/ ___/ /_____/ //_/ __ /
5
+ / /__/ /_/ / / /_____/ ,< / /_/ /
6
+ \___/\__/_/ /_/ /_/|_|\__,_/
7
+ ```
8
+ # ctrl-kd
9
+
10
+ Convert WordStar for DOS v4-v7 files to modern formats. **^KD: save and done.**
11
+
12
+ `ctrl-kd` reads WordStar for DOS documents, and WordStar print stream files,
13
+ and writes plain text, Markdown, HTML, RTF, or PDF (set on a viewer's
14
+ built-in base-14 fonts — no dependencies, nothing embedded, the page as it
15
+ would have printed: printed mode follows the document's own font blocks and
16
+ its own layout arithmetic, while modern mode is the same document reflowed
17
+ for today: its fonts, headers, and footnotes all carried.
18
+
19
+ ```console
20
+ $ ctrl-kd ESSAY.WS # -> ESSAY.rtf: modern reflow, the
21
+ # document's own fonts carried
22
+ $ ctrl-kd --mode printed LETTER.WS # -> LETTER.pdf: the 1990 facsimile
23
+ $ ctrl-kd ESSAY.WS -t md # modern markdown instead
24
+ $ ctrl-kd ESSAY.WS -t html -t rtf # multiple formats
25
+ $ ctrl-kd --page-settings sawyer X.WS # a known machine's page defaults
26
+ $ ctrl-kd --diagnose MYSTERY.FIL # what IS this file?
27
+ $ ctrl-kd --comments MEMO.WS # include the author's hidden comments
28
+ $ ctrl-kd --no-notes PAPER.WS # body text only, no notes
29
+ $ ctrl-kd --samples DIR # write 4 bundled public-domain sample .WS files into DIR
30
+ ```
31
+
32
+ ## Modes
33
+
34
+ * `--mode modern` (default; bare runs produce RTF): the document brought to a
35
+ modern audience — reflowed, its own fonts and styles carried, footnotes at
36
+ the page bottom, gaps the file never specified filled with today's
37
+ conventions (a comfortable serif at reading size, one-inch margins).
38
+ * `--mode printed` (bare runs produce PDF): every line as laid out, on the
39
+ era's own page — how it came off the printer. Gaps are filled with 1990's
40
+ conventions instead; `--page-settings` supplies a particular machine's.
41
+
42
+ More information: **[FAQ.md](FAQ.md)**, and **[ERAS.md](ERAS.md)**.
43
+
44
+ ## Install
45
+
46
+ ```console
47
+ $ brew install jonmichaels/tap/ctrl-kd # macOS / Linuxbrew
48
+ $ pipx install ctrl-kd # or: pip install ctrl-kd
49
+ ```
50
+ Download Windows x86_64: [Latest Version](https://github.com/jonmichaels/ctrl-kd/releases/latest/download/ctrl-kd-windows-x86_64.zip)
51
+
52
+ Python ≥ 3.9, no dependencies. Library API: `ctrlkd.convert(data, to='html')`.
53
+
54
+ ## Adding an output format
55
+
56
+ An output format is one function over the parsed document — register it with the
57
+ `@ctrlkd.emitter` decorator, or ship it as a pip-installable plugin via the
58
+ `ctrlkd.emitters` entry-point group and it appears in the CLI automatically.
59
+ **[EXTENDING.md](EXTENDING.md)** has the IR contract, a complete worked example
60
+ (BBCode in ~40 lines), and a checklist.
61
+
62
+ ## Siblings
63
+
64
+ **[Soft Return](https://github.com/jonmichaels/soft-return)** — macOS viewer and converter plus QuickLook extension.
65
+ Includes a Swift command line utility.
66
+
67
+ ## Lineage
68
+
69
+ I wanted to be able to see the 70-some WordStar 4 files I had from junior high
70
+ and high school. In about an hour and half my agent had my files looking
71
+ pretty good. And then I fell down the research rabbit hole...
72
+
73
+ `ctrl-kd` wouldn't have been possible without the tools and documentation that
74
+ kept WordStar readable: Yohanes Nugroho's WS-CON, Michael Petrie's English port,
75
+ the `wsconvert`project, Robert J. Sawyer's WordStar archive, and the WordStar
76
+ format documentation community.
77
+
78
+ My own test files are personal and are not distributed — this repo's tests use
79
+ synthetic fixtures and some public domain docs I retyped in WordStar 4 and
80
+ WordStar 7 in DOSBox-X, plus Robert J. Sawyer's public WS7 archive (opt-in,
81
+ `pytest -m sawyer`; see `tests/SAWYER-CORPUS.md`) you can run your own tests
82
+ against that if you have a copy: download the archive, point
83
+ `CTRLKD_SAWYER_ARCHIVE` at its top-level directory (the one holding
84
+ `CONVERT.WS`, `INSET/`, `ARTICLES/`), and verify the path before arming —
85
+ `tests/SAWYER-CORPUS.md` has a one-line smoke check. A `CTRLKD_PRIVATE_CORPUS`
86
+ variable also exists, for my own private regression fixtures — it has no
87
+ effect unless you're me.
88
+
89
+ ## Credits
90
+
91
+ Written by Jon Michaels — whose 1987–1992 WordStar files, and the need to read
92
+ them again, are the reason this exists — with Athena (Claude, Anthropic) as
93
+ co-author.
@@ -0,0 +1,66 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "ctrl-kd"
7
+ dynamic = ["version"]
8
+ description = "Convert WordStar 4-7 documents and print-to-disk files to text, Markdown, HTML, RTF, or PDF. ^KD: save and done."
9
+ readme = "README.md"
10
+ license = {text = "MIT"}
11
+ authors = [{name = "Jon Michaels"}]
12
+ requires-python = ">=3.9"
13
+ keywords = ["wordstar", "converter", "retrocomputing", "archive", "dos"]
14
+ classifiers = [
15
+ "Development Status :: 5 - Production/Stable",
16
+ "Environment :: Console",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3",
19
+ "Topic :: Text Processing :: Filters",
20
+ "Topic :: System :: Archiving",
21
+ ]
22
+
23
+ [project.urls]
24
+ Homepage = "https://github.com/jonmichaels/ctrl-kd"
25
+
26
+ [project.scripts]
27
+ ctrl-kd = "ctrlkd.cli:main"
28
+
29
+ [tool.setuptools.packages.find]
30
+ where = ["src"]
31
+
32
+ [tool.setuptools.dynamic]
33
+ # single source of truth: src/ctrlkd/__init__.py __version__ -- the 4.0.0
34
+ # release shipped with pyproject still saying 3.0.0 because the version
35
+ # lived in two places and the bump hit one (2026-08-06); never again
36
+ version = {attr = "ctrlkd.__version__"}
37
+
38
+ [tool.setuptools.package-data]
39
+ # The four bundled public-domain sample documents (see samples/README.md and
40
+ # `ctrl-kd --samples DIR`) travel with the wheel/sdist as package data.
41
+ ctrlkd = ["samples/*.WS"]
42
+
43
+ [tool.pytest.ini_options]
44
+ # Two-tier test architecture (K1, 2026-08-26; tier 3/private relocated out
45
+ # of this public repo entirely -- see README). Bare `pytest` must run ONLY
46
+ # tier 1 (tests/test_samples.py and every other test with no marker below)
47
+ # -- green, complete, zero skips, no environment required. Tier 2 is
48
+ # DESELECTED here, not skipped: it never even collects into the report, so
49
+ # a bare run's summary line is an honest denominator.
50
+ #
51
+ # Arming (see tests/sawyer_fixture.py, conftest.py, tools/pcl_tolerance.py):
52
+ # sawyer CTRLKD_SAWYER_ARCHIVE=/path
53
+ # pcl CTRLKD_PRIVATE_CORPUS=/path (2026-09-05, planning #197)
54
+ # paper CTRLKD_PRIVATE_CORPUS=/path (2026-09-06, planning #200; verdicts
55
+ # file path itself overridable with CTRLKD_PAPER_VERDICTS)
56
+ #
57
+ # tools/run-full-suite.sh overrides this filter (`-o addopts=""`) for an
58
+ # armed run, where an unarmed gate FAILS instead of silently not running --
59
+ # see that script's own header for why.
60
+ testpaths = ["tests"]
61
+ markers = [
62
+ "sawyer: opt-in tier 2 -- an explicit, committed list of documents from Robert J. Sawyer's public WordStar 7 archive (arm with CTRLKD_SAWYER_ARCHIVE)",
63
+ "pcl: opt-in -- coordinate-level fidelity gate against real WordStar 7 LaserJet PCL captures (arm with CTRLKD_PRIVATE_CORPUS); see tools/pcl_tolerance.py and tests/test_pcl_fidelity.py",
64
+ "paper: opt-in -- machine-readable verdicts for the 69 M479fdw paper-scan pages (arm with CTRLKD_PRIVATE_CORPUS); see tools/PAPER-VERDICTS.md and tests/test_paper_verdicts.py",
65
+ ]
66
+ addopts = "-m \"not sawyer and not pcl and not paper\""
@@ -0,0 +1,112 @@
1
+ Metadata-Version: 2.4
2
+ Name: ctrl-kd
3
+ Version: 4.5.1
4
+ Summary: Convert WordStar 4-7 documents and print-to-disk files to text, Markdown, HTML, RTF, or PDF. ^KD: save and done.
5
+ Author: Jon Michaels
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/jonmichaels/ctrl-kd
8
+ Keywords: wordstar,converter,retrocomputing,archive,dos
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: Environment :: Console
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Topic :: Text Processing :: Filters
14
+ Classifier: Topic :: System :: Archiving
15
+ Requires-Python: >=3.9
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Dynamic: license-file
19
+
20
+ ```
21
+ __ __ __ __
22
+ _____/ /______/ / / /______/ /
23
+ / ___/ __/ ___/ /_____/ //_/ __ /
24
+ / /__/ /_/ / / /_____/ ,< / /_/ /
25
+ \___/\__/_/ /_/ /_/|_|\__,_/
26
+ ```
27
+ # ctrl-kd
28
+
29
+ Convert WordStar for DOS v4-v7 files to modern formats. **^KD: save and done.**
30
+
31
+ `ctrl-kd` reads WordStar for DOS documents, and WordStar print stream files,
32
+ and writes plain text, Markdown, HTML, RTF, or PDF (set on a viewer's
33
+ built-in base-14 fonts — no dependencies, nothing embedded, the page as it
34
+ would have printed: printed mode follows the document's own font blocks and
35
+ its own layout arithmetic, while modern mode is the same document reflowed
36
+ for today: its fonts, headers, and footnotes all carried.
37
+
38
+ ```console
39
+ $ ctrl-kd ESSAY.WS # -> ESSAY.rtf: modern reflow, the
40
+ # document's own fonts carried
41
+ $ ctrl-kd --mode printed LETTER.WS # -> LETTER.pdf: the 1990 facsimile
42
+ $ ctrl-kd ESSAY.WS -t md # modern markdown instead
43
+ $ ctrl-kd ESSAY.WS -t html -t rtf # multiple formats
44
+ $ ctrl-kd --page-settings sawyer X.WS # a known machine's page defaults
45
+ $ ctrl-kd --diagnose MYSTERY.FIL # what IS this file?
46
+ $ ctrl-kd --comments MEMO.WS # include the author's hidden comments
47
+ $ ctrl-kd --no-notes PAPER.WS # body text only, no notes
48
+ $ ctrl-kd --samples DIR # write 4 bundled public-domain sample .WS files into DIR
49
+ ```
50
+
51
+ ## Modes
52
+
53
+ * `--mode modern` (default; bare runs produce RTF): the document brought to a
54
+ modern audience — reflowed, its own fonts and styles carried, footnotes at
55
+ the page bottom, gaps the file never specified filled with today's
56
+ conventions (a comfortable serif at reading size, one-inch margins).
57
+ * `--mode printed` (bare runs produce PDF): every line as laid out, on the
58
+ era's own page — how it came off the printer. Gaps are filled with 1990's
59
+ conventions instead; `--page-settings` supplies a particular machine's.
60
+
61
+ More information: **[FAQ.md](FAQ.md)**, and **[ERAS.md](ERAS.md)**.
62
+
63
+ ## Install
64
+
65
+ ```console
66
+ $ brew install jonmichaels/tap/ctrl-kd # macOS / Linuxbrew
67
+ $ pipx install ctrl-kd # or: pip install ctrl-kd
68
+ ```
69
+ Download Windows x86_64: [Latest Version](https://github.com/jonmichaels/ctrl-kd/releases/latest/download/ctrl-kd-windows-x86_64.zip)
70
+
71
+ Python ≥ 3.9, no dependencies. Library API: `ctrlkd.convert(data, to='html')`.
72
+
73
+ ## Adding an output format
74
+
75
+ An output format is one function over the parsed document — register it with the
76
+ `@ctrlkd.emitter` decorator, or ship it as a pip-installable plugin via the
77
+ `ctrlkd.emitters` entry-point group and it appears in the CLI automatically.
78
+ **[EXTENDING.md](EXTENDING.md)** has the IR contract, a complete worked example
79
+ (BBCode in ~40 lines), and a checklist.
80
+
81
+ ## Siblings
82
+
83
+ **[Soft Return](https://github.com/jonmichaels/soft-return)** — macOS viewer and converter plus QuickLook extension.
84
+ Includes a Swift command line utility.
85
+
86
+ ## Lineage
87
+
88
+ I wanted to be able to see the 70-some WordStar 4 files I had from junior high
89
+ and high school. In about an hour and half my agent had my files looking
90
+ pretty good. And then I fell down the research rabbit hole...
91
+
92
+ `ctrl-kd` wouldn't have been possible without the tools and documentation that
93
+ kept WordStar readable: Yohanes Nugroho's WS-CON, Michael Petrie's English port,
94
+ the `wsconvert`project, Robert J. Sawyer's WordStar archive, and the WordStar
95
+ format documentation community.
96
+
97
+ My own test files are personal and are not distributed — this repo's tests use
98
+ synthetic fixtures and some public domain docs I retyped in WordStar 4 and
99
+ WordStar 7 in DOSBox-X, plus Robert J. Sawyer's public WS7 archive (opt-in,
100
+ `pytest -m sawyer`; see `tests/SAWYER-CORPUS.md`) you can run your own tests
101
+ against that if you have a copy: download the archive, point
102
+ `CTRLKD_SAWYER_ARCHIVE` at its top-level directory (the one holding
103
+ `CONVERT.WS`, `INSET/`, `ARTICLES/`), and verify the path before arming —
104
+ `tests/SAWYER-CORPUS.md` has a one-line smoke check. A `CTRLKD_PRIVATE_CORPUS`
105
+ variable also exists, for my own private regression fixtures — it has no
106
+ effect unless you're me.
107
+
108
+ ## Credits
109
+
110
+ Written by Jon Michaels — whose 1987–1992 WordStar files, and the need to read
111
+ them again, are the reason this exists — with Athena (Claude, Anthropic) as
112
+ co-author.
@@ -0,0 +1,67 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/ctrl_kd.egg-info/PKG-INFO
5
+ src/ctrl_kd.egg-info/SOURCES.txt
6
+ src/ctrl_kd.egg-info/dependency_links.txt
7
+ src/ctrl_kd.egg-info/entry_points.txt
8
+ src/ctrl_kd.egg-info/top_level.txt
9
+ src/ctrlkd/__init__.py
10
+ src/ctrlkd/afm.py
11
+ src/ctrlkd/cli.py
12
+ src/ctrlkd/convert.py
13
+ src/ctrlkd/core.py
14
+ src/ctrlkd/emit.py
15
+ src/ctrlkd/fontmap.py
16
+ src/ctrlkd/info.py
17
+ src/ctrlkd/layout.py
18
+ src/ctrlkd/pdf.py
19
+ src/ctrlkd/pictures.py
20
+ src/ctrlkd/pix.py
21
+ src/ctrlkd/piximg.py
22
+ src/ctrlkd/symbolmap.py
23
+ src/ctrlkd/typestyles.py
24
+ src/ctrlkd/writer.py
25
+ src/ctrlkd/wschange.py
26
+ src/ctrlkd/samples/LYING.WS
27
+ src/ctrlkd/samples/OCAPTAIN.WS
28
+ src/ctrlkd/samples/TWAINLET.WS
29
+ src/ctrlkd/samples/WARPRAYR.WS
30
+ tests/test_ctrlkd.py
31
+ tests/test_endnote_leading_gap.py
32
+ tests/test_fidelity_gate.py
33
+ tests/test_flags_toc_inline.py
34
+ tests/test_glyph_aspect.py
35
+ tests/test_layout_marks.py
36
+ tests/test_lj6dtp_bullet_glyph.py
37
+ tests/test_lj6dtp_char_substitution.py
38
+ tests/test_lj6dtp_colour_restore.py
39
+ tests/test_lj6dtp_heading_face.py
40
+ tests/test_lj6dtp_hp_patterns.py
41
+ tests/test_lj6dtp_legend_line_spacing.py
42
+ tests/test_lj6dtp_pcl_rectangles.py
43
+ tests/test_lj6dtp_shading_table_rules.py
44
+ tests/test_lj6dtp_table_rule_weight.py
45
+ tests/test_load_plugins.py
46
+ tests/test_modern_box_regions.py
47
+ tests/test_modern_line_spacing.py
48
+ tests/test_modern_lint.py
49
+ tests/test_note_rulings_20260824.py
50
+ tests/test_paper_verdicts.py
51
+ tests/test_pcl_fidelity.py
52
+ tests/test_pcl_tolerance.py
53
+ tests/test_pictures.py
54
+ tests/test_pix.py
55
+ tests/test_polarity_gate.py
56
+ tests/test_printed_fidelity.py
57
+ tests/test_samples.py
58
+ tests/test_sawyer_corpus.py
59
+ tests/test_screenplay_detection.py
60
+ tests/test_screenplay_pdf.py
61
+ tests/test_screenplay_rendering.py
62
+ tests/test_sentence_spacing_n9.py
63
+ tests/test_style_leading.py
64
+ tests/test_verse_quote_couplet.py
65
+ tests/test_verse_spacing.py
66
+ tests/test_writer.py
67
+ tests/test_wschange.py
@@ -8,4 +8,4 @@ from .pdf import emit_pdf # registers the 'pdf' format
8
8
  from .convert import convert, select_notes, DEFAULT_NOTE_KINDS, ALL_NOTE_KINDS
9
9
  from .info import document_info
10
10
 
11
- __version__ = '4.0.1'
11
+ __version__ = '4.5.1'