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.
- ctrl_kd-4.5.1/PKG-INFO +112 -0
- ctrl_kd-4.5.1/README.md +93 -0
- ctrl_kd-4.5.1/pyproject.toml +66 -0
- ctrl_kd-4.5.1/src/ctrl_kd.egg-info/PKG-INFO +112 -0
- ctrl_kd-4.5.1/src/ctrl_kd.egg-info/SOURCES.txt +67 -0
- {ctrl_kd-4.0.1 → ctrl_kd-4.5.1}/src/ctrlkd/__init__.py +1 -1
- {ctrl_kd-4.0.1 → ctrl_kd-4.5.1}/src/ctrlkd/cli.py +150 -6
- {ctrl_kd-4.0.1 → ctrl_kd-4.5.1}/src/ctrlkd/core.py +1703 -92
- ctrl_kd-4.5.1/src/ctrlkd/emit.py +3056 -0
- {ctrl_kd-4.0.1 → ctrl_kd-4.5.1}/src/ctrlkd/fontmap.py +98 -8
- ctrl_kd-4.5.1/src/ctrlkd/info.py +176 -0
- ctrl_kd-4.5.1/src/ctrlkd/layout.py +652 -0
- ctrl_kd-4.5.1/src/ctrlkd/pdf.py +6258 -0
- ctrl_kd-4.5.1/src/ctrlkd/pictures.py +180 -0
- ctrl_kd-4.5.1/src/ctrlkd/pix.py +600 -0
- ctrl_kd-4.5.1/src/ctrlkd/piximg.py +194 -0
- ctrl_kd-4.5.1/src/ctrlkd/samples/LYING.WS +0 -0
- ctrl_kd-4.5.1/src/ctrlkd/samples/OCAPTAIN.WS +38 -0
- ctrl_kd-4.5.1/src/ctrlkd/samples/TWAINLET.WS +56 -0
- ctrl_kd-4.5.1/src/ctrlkd/samples/WARPRAYR.WS +0 -0
- {ctrl_kd-4.0.1 → ctrl_kd-4.5.1}/src/ctrlkd/wschange.py +1 -2
- {ctrl_kd-4.0.1 → ctrl_kd-4.5.1}/tests/test_ctrlkd.py +2313 -107
- ctrl_kd-4.5.1/tests/test_endnote_leading_gap.py +107 -0
- ctrl_kd-4.5.1/tests/test_fidelity_gate.py +642 -0
- ctrl_kd-4.5.1/tests/test_flags_toc_inline.py +307 -0
- ctrl_kd-4.5.1/tests/test_glyph_aspect.py +186 -0
- ctrl_kd-4.5.1/tests/test_layout_marks.py +221 -0
- ctrl_kd-4.5.1/tests/test_lj6dtp_bullet_glyph.py +79 -0
- ctrl_kd-4.5.1/tests/test_lj6dtp_char_substitution.py +184 -0
- ctrl_kd-4.5.1/tests/test_lj6dtp_colour_restore.py +77 -0
- ctrl_kd-4.5.1/tests/test_lj6dtp_heading_face.py +277 -0
- ctrl_kd-4.5.1/tests/test_lj6dtp_hp_patterns.py +115 -0
- ctrl_kd-4.5.1/tests/test_lj6dtp_legend_line_spacing.py +68 -0
- ctrl_kd-4.5.1/tests/test_lj6dtp_pcl_rectangles.py +222 -0
- ctrl_kd-4.5.1/tests/test_lj6dtp_shading_table_rules.py +73 -0
- ctrl_kd-4.5.1/tests/test_lj6dtp_table_rule_weight.py +82 -0
- ctrl_kd-4.5.1/tests/test_load_plugins.py +107 -0
- ctrl_kd-4.5.1/tests/test_modern_box_regions.py +197 -0
- ctrl_kd-4.5.1/tests/test_modern_line_spacing.py +234 -0
- ctrl_kd-4.5.1/tests/test_modern_lint.py +1859 -0
- ctrl_kd-4.5.1/tests/test_note_rulings_20260824.py +266 -0
- ctrl_kd-4.5.1/tests/test_paper_verdicts.py +90 -0
- ctrl_kd-4.5.1/tests/test_pcl_fidelity.py +102 -0
- ctrl_kd-4.5.1/tests/test_pcl_tolerance.py +567 -0
- ctrl_kd-4.5.1/tests/test_pictures.py +812 -0
- ctrl_kd-4.5.1/tests/test_pix.py +582 -0
- ctrl_kd-4.5.1/tests/test_polarity_gate.py +197 -0
- ctrl_kd-4.5.1/tests/test_printed_fidelity.py +897 -0
- ctrl_kd-4.5.1/tests/test_samples.py +140 -0
- ctrl_kd-4.5.1/tests/test_sawyer_corpus.py +208 -0
- ctrl_kd-4.5.1/tests/test_screenplay_detection.py +191 -0
- ctrl_kd-4.5.1/tests/test_screenplay_pdf.py +232 -0
- ctrl_kd-4.5.1/tests/test_screenplay_rendering.py +145 -0
- ctrl_kd-4.5.1/tests/test_sentence_spacing_n9.py +299 -0
- ctrl_kd-4.5.1/tests/test_style_leading.py +491 -0
- ctrl_kd-4.5.1/tests/test_verse_quote_couplet.py +109 -0
- ctrl_kd-4.5.1/tests/test_verse_spacing.py +95 -0
- {ctrl_kd-4.0.1 → ctrl_kd-4.5.1}/tests/test_writer.py +6 -78
- ctrl_kd-4.5.1/tests/test_wschange.py +77 -0
- ctrl_kd-4.0.1/PKG-INFO +0 -170
- ctrl_kd-4.0.1/README.md +0 -151
- ctrl_kd-4.0.1/pyproject.toml +0 -36
- ctrl_kd-4.0.1/src/ctrl_kd.egg-info/PKG-INFO +0 -170
- ctrl_kd-4.0.1/src/ctrl_kd.egg-info/SOURCES.txt +0 -25
- ctrl_kd-4.0.1/src/ctrlkd/emit.py +0 -1085
- ctrl_kd-4.0.1/src/ctrlkd/info.py +0 -59
- ctrl_kd-4.0.1/src/ctrlkd/layout.py +0 -341
- ctrl_kd-4.0.1/src/ctrlkd/pdf.py +0 -1938
- ctrl_kd-4.0.1/tests/test_wschange.py +0 -194
- {ctrl_kd-4.0.1 → ctrl_kd-4.5.1}/LICENSE +0 -0
- {ctrl_kd-4.0.1 → ctrl_kd-4.5.1}/setup.cfg +0 -0
- {ctrl_kd-4.0.1 → ctrl_kd-4.5.1}/src/ctrl_kd.egg-info/dependency_links.txt +0 -0
- {ctrl_kd-4.0.1 → ctrl_kd-4.5.1}/src/ctrl_kd.egg-info/entry_points.txt +0 -0
- {ctrl_kd-4.0.1 → ctrl_kd-4.5.1}/src/ctrl_kd.egg-info/top_level.txt +0 -0
- {ctrl_kd-4.0.1 → ctrl_kd-4.5.1}/src/ctrlkd/afm.py +0 -0
- {ctrl_kd-4.0.1 → ctrl_kd-4.5.1}/src/ctrlkd/convert.py +0 -0
- {ctrl_kd-4.0.1 → ctrl_kd-4.5.1}/src/ctrlkd/symbolmap.py +0 -0
- {ctrl_kd-4.0.1 → ctrl_kd-4.5.1}/src/ctrlkd/typestyles.py +0 -0
- {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.
|
ctrl_kd-4.5.1/README.md
ADDED
|
@@ -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
|