ctrl-kd 4.6.0__tar.gz → 4.7.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 (106) hide show
  1. {ctrl_kd-4.6.0/src/ctrl_kd.egg-info → ctrl_kd-4.7.1}/PKG-INFO +3 -3
  2. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/README.md +2 -2
  3. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/pyproject.toml +1 -1
  4. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1/src/ctrl_kd.egg-info}/PKG-INFO +3 -3
  5. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/src/ctrl_kd.egg-info/SOURCES.txt +23 -1
  6. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/src/ctrlkd/__init__.py +1 -1
  7. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/src/ctrlkd/afm.py +20 -1
  8. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/src/ctrlkd/cli.py +11 -4
  9. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/src/ctrlkd/core.py +1075 -62
  10. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/src/ctrlkd/emit.py +1389 -127
  11. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/src/ctrlkd/layout.py +466 -12
  12. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/src/ctrlkd/pdf.py +1988 -540
  13. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_bare_tab_modulus8.py +100 -5
  14. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_ctrlkd.py +35 -10
  15. ctrl_kd-4.7.1/tests/test_dot_comment_prints_nothing.py +108 -0
  16. ctrl_kd-4.7.1/tests/test_driver_substitutions_exports.py +206 -0
  17. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_fidelity_gate.py +63 -1
  18. ctrl_kd-4.7.1/tests/test_form_feed_line_marks.py +180 -0
  19. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_graphic_cells.py +62 -2
  20. ctrl_kd-4.7.1/tests/test_head_foot_timing.py +143 -0
  21. ctrl_kd-4.7.1/tests/test_html_print_stylesheet.py +179 -0
  22. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_justification.py +1 -1
  23. ctrl_kd-4.7.1/tests/test_mailmerge_data_files.py +283 -0
  24. ctrl_kd-4.7.1/tests/test_merge_page_number_variable.py +414 -0
  25. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_modern_centring_clip.py +110 -1
  26. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_modern_lint.py +127 -24
  27. ctrl_kd-4.7.1/tests/test_modern_rtf_structure_rows.py +197 -0
  28. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_paper_verdicts.py +31 -2
  29. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_pcl_fidelity.py +14 -5
  30. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_pcl_tolerance.py +305 -0
  31. ctrl_kd-4.7.1/tests/test_pcl_v4_column_geometry.py +407 -0
  32. ctrl_kd-4.7.1/tests/test_pcl_v4_untriaged_fixes.py +733 -0
  33. ctrl_kd-4.7.1/tests/test_pf_print_reformat.py +288 -0
  34. ctrl_kd-4.7.1/tests/test_pn_page_timing.py +293 -0
  35. ctrl_kd-4.7.1/tests/test_print_control_labels_invisible.py +211 -0
  36. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_printed_fidelity.py +166 -34
  37. ctrl_kd-4.7.1/tests/test_rtf_facing_pages.py +199 -0
  38. ctrl_kd-4.7.1/tests/test_rtf_head_style_attrs.py +129 -0
  39. ctrl_kd-4.7.1/tests/test_rtf_keep_with_next.py +126 -0
  40. ctrl_kd-4.7.1/tests/test_rtf_page_numbers.py +183 -0
  41. ctrl_kd-4.7.1/tests/test_rtf_section_spine.py +238 -0
  42. ctrl_kd-4.7.1/tests/test_structural_checks.py +367 -0
  43. ctrl_kd-4.7.1/tests/test_trailing_pa_break.py +167 -0
  44. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_verse_spacing.py +33 -5
  45. ctrl_kd-4.7.1/tests/test_width_and_cp1252_memo.py +81 -0
  46. ctrl_kd-4.7.1/tests/test_zero_width_tab.py +107 -0
  47. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/LICENSE +0 -0
  48. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/setup.cfg +0 -0
  49. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/src/ctrl_kd.egg-info/dependency_links.txt +0 -0
  50. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/src/ctrl_kd.egg-info/entry_points.txt +0 -0
  51. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/src/ctrl_kd.egg-info/top_level.txt +0 -0
  52. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/src/ctrlkd/convert.py +0 -0
  53. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/src/ctrlkd/fontmap.py +0 -0
  54. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/src/ctrlkd/info.py +0 -0
  55. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/src/ctrlkd/pictures.py +0 -0
  56. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/src/ctrlkd/pix.py +0 -0
  57. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/src/ctrlkd/piximg.py +0 -0
  58. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/src/ctrlkd/samples/LYING.WS +0 -0
  59. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/src/ctrlkd/samples/OCAPTAIN.WS +0 -0
  60. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/src/ctrlkd/samples/TWAINLET.WS +0 -0
  61. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/src/ctrlkd/samples/WARPRAYR.WS +0 -0
  62. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/src/ctrlkd/symbolmap.py +0 -0
  63. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/src/ctrlkd/typestyles.py +0 -0
  64. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/src/ctrlkd/writer.py +0 -0
  65. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/src/ctrlkd/wschange.py +0 -0
  66. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_endnote_leading_gap.py +0 -0
  67. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_flags_toc_inline.py +0 -0
  68. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_glyph_aspect.py +0 -0
  69. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_graphic_cell_ops.py +0 -0
  70. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_layout_marks.py +0 -0
  71. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_lj6dtp_bullet_glyph.py +0 -0
  72. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_lj6dtp_char_substitution.py +0 -0
  73. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_lj6dtp_colour_restore.py +0 -0
  74. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_lj6dtp_heading_face.py +0 -0
  75. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_lj6dtp_hp_patterns.py +0 -0
  76. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_lj6dtp_legend_line_spacing.py +0 -0
  77. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_lj6dtp_pcl_rectangles.py +0 -0
  78. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_lj6dtp_shading_table_rules.py +0 -0
  79. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_lj6dtp_table_rule_weight.py +0 -0
  80. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_load_plugins.py +0 -0
  81. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_modern_blank_after_tightened_line.py +0 -0
  82. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_modern_box_regions.py +0 -0
  83. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_modern_line_spacing.py +0 -0
  84. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_modern_page_baseline.py +0 -0
  85. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_modern_verse_defrow.py +0 -0
  86. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_note_rulings_20260824.py +0 -0
  87. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_page_parity.py +0 -0
  88. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_pcl_page_membership.py +0 -0
  89. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_pcl_page_size_eject.py +0 -0
  90. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_pcl_transparent_print.py +0 -0
  91. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_pcl_vertical_wraparound.py +0 -0
  92. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_peseta_euro_driver.py +0 -0
  93. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_pictures.py +0 -0
  94. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_pix.py +0 -0
  95. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_polarity_gate.py +0 -0
  96. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_samples.py +0 -0
  97. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_sawyer_corpus.py +0 -0
  98. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_screenplay_detection.py +0 -0
  99. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_screenplay_pdf.py +0 -0
  100. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_screenplay_rendering.py +0 -0
  101. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_sentence_spacing_n9.py +0 -0
  102. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_style_leading.py +0 -0
  103. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_symbol_span_graphic_chars.py +0 -0
  104. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_verse_quote_couplet.py +0 -0
  105. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_writer.py +0 -0
  106. {ctrl_kd-4.6.0 → ctrl_kd-4.7.1}/tests/test_wschange.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ctrl-kd
3
- Version: 4.6.0
3
+ Version: 4.7.1
4
4
  Summary: Convert WordStar 4-7 documents and print-to-disk files to text, Markdown, HTML, RTF, or PDF. ^KD: save and done.
5
5
  Author: Jon Michaels
6
6
  License: MIT
@@ -28,8 +28,8 @@ Dynamic: license-file
28
28
 
29
29
  Convert WordStar for DOS v4-v7 files to modern formats. **^KD: save and done.**
30
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
31
+ `ctrl-kd` reads WordStar for DOS documents, WordStar print stream files, and
32
+ MailMerge data files, and writes plain text, Markdown, HTML, RTF, or PDF (set on a viewer's
33
33
  built-in base-14 fonts — no dependencies, nothing embedded, the page as it
34
34
  would have printed: printed mode follows the document's own font blocks and
35
35
  its own layout arithmetic, while modern mode is the same document reflowed
@@ -9,8 +9,8 @@
9
9
 
10
10
  Convert WordStar for DOS v4-v7 files to modern formats. **^KD: save and done.**
11
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
12
+ `ctrl-kd` reads WordStar for DOS documents, WordStar print stream files, and
13
+ MailMerge data files, and writes plain text, Markdown, HTML, RTF, or PDF (set on a viewer's
14
14
  built-in base-14 fonts — no dependencies, nothing embedded, the page as it
15
15
  would have printed: printed mode follows the document's own font blocks and
16
16
  its own layout arithmetic, while modern mode is the same document reflowed
@@ -61,6 +61,6 @@ testpaths = ["tests"]
61
61
  markers = [
62
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
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",
64
+ "paper: PARKED BY RULING 2026-09-14 (paper_verdicts.PARKED_BY_RULING) -- machine-readable verdicts for the 69 M479fdw paper-scan pages; every page skips with that citation and is reported as parked by ruling, never as a red. Arm with CTRLKD_PRIVATE_CORPUS to see the citation per page; see tools/PAPER-VERDICTS.md and tests/test_paper_verdicts.py",
65
65
  ]
66
66
  addopts = "-m \"not sawyer and not pcl and not paper\""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ctrl-kd
3
- Version: 4.6.0
3
+ Version: 4.7.1
4
4
  Summary: Convert WordStar 4-7 documents and print-to-disk files to text, Markdown, HTML, RTF, or PDF. ^KD: save and done.
5
5
  Author: Jon Michaels
6
6
  License: MIT
@@ -28,8 +28,8 @@ Dynamic: license-file
28
28
 
29
29
  Convert WordStar for DOS v4-v7 files to modern formats. **^KD: save and done.**
30
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
31
+ `ctrl-kd` reads WordStar for DOS documents, WordStar print stream files, and
32
+ MailMerge data files, and writes plain text, Markdown, HTML, RTF, or PDF (set on a viewer's
33
33
  built-in base-14 fonts — no dependencies, nothing embedded, the page as it
34
34
  would have printed: printed mode follows the document's own font blocks and
35
35
  its own layout arithmetic, while modern mode is the same document reflowed
@@ -29,12 +29,17 @@ src/ctrlkd/samples/TWAINLET.WS
29
29
  src/ctrlkd/samples/WARPRAYR.WS
30
30
  tests/test_bare_tab_modulus8.py
31
31
  tests/test_ctrlkd.py
32
+ tests/test_dot_comment_prints_nothing.py
33
+ tests/test_driver_substitutions_exports.py
32
34
  tests/test_endnote_leading_gap.py
33
35
  tests/test_fidelity_gate.py
34
36
  tests/test_flags_toc_inline.py
37
+ tests/test_form_feed_line_marks.py
35
38
  tests/test_glyph_aspect.py
36
39
  tests/test_graphic_cell_ops.py
37
40
  tests/test_graphic_cells.py
41
+ tests/test_head_foot_timing.py
42
+ tests/test_html_print_stylesheet.py
38
43
  tests/test_justification.py
39
44
  tests/test_layout_marks.py
40
45
  tests/test_lj6dtp_bullet_glyph.py
@@ -47,12 +52,15 @@ tests/test_lj6dtp_pcl_rectangles.py
47
52
  tests/test_lj6dtp_shading_table_rules.py
48
53
  tests/test_lj6dtp_table_rule_weight.py
49
54
  tests/test_load_plugins.py
55
+ tests/test_mailmerge_data_files.py
56
+ tests/test_merge_page_number_variable.py
50
57
  tests/test_modern_blank_after_tightened_line.py
51
58
  tests/test_modern_box_regions.py
52
59
  tests/test_modern_centring_clip.py
53
60
  tests/test_modern_line_spacing.py
54
61
  tests/test_modern_lint.py
55
62
  tests/test_modern_page_baseline.py
63
+ tests/test_modern_rtf_structure_rows.py
56
64
  tests/test_modern_verse_defrow.py
57
65
  tests/test_note_rulings_20260824.py
58
66
  tests/test_page_parity.py
@@ -62,21 +70,35 @@ tests/test_pcl_page_membership.py
62
70
  tests/test_pcl_page_size_eject.py
63
71
  tests/test_pcl_tolerance.py
64
72
  tests/test_pcl_transparent_print.py
73
+ tests/test_pcl_v4_column_geometry.py
74
+ tests/test_pcl_v4_untriaged_fixes.py
65
75
  tests/test_pcl_vertical_wraparound.py
66
76
  tests/test_peseta_euro_driver.py
77
+ tests/test_pf_print_reformat.py
67
78
  tests/test_pictures.py
68
79
  tests/test_pix.py
80
+ tests/test_pn_page_timing.py
69
81
  tests/test_polarity_gate.py
82
+ tests/test_print_control_labels_invisible.py
70
83
  tests/test_printed_fidelity.py
84
+ tests/test_rtf_facing_pages.py
85
+ tests/test_rtf_head_style_attrs.py
86
+ tests/test_rtf_keep_with_next.py
87
+ tests/test_rtf_page_numbers.py
88
+ tests/test_rtf_section_spine.py
71
89
  tests/test_samples.py
72
90
  tests/test_sawyer_corpus.py
73
91
  tests/test_screenplay_detection.py
74
92
  tests/test_screenplay_pdf.py
75
93
  tests/test_screenplay_rendering.py
76
94
  tests/test_sentence_spacing_n9.py
95
+ tests/test_structural_checks.py
77
96
  tests/test_style_leading.py
78
97
  tests/test_symbol_span_graphic_chars.py
98
+ tests/test_trailing_pa_break.py
79
99
  tests/test_verse_quote_couplet.py
80
100
  tests/test_verse_spacing.py
101
+ tests/test_width_and_cp1252_memo.py
81
102
  tests/test_writer.py
82
- tests/test_wschange.py
103
+ tests/test_wschange.py
104
+ tests/test_zero_width_tab.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.6.0'
11
+ __version__ = '4.7.1'
@@ -336,6 +336,17 @@ WIDTHS = {
336
336
  }
337
337
 
338
338
 
339
+ # Perf (planning #271 M7): both of these are asked the SAME (text, basefont)
340
+ # question tens of thousands of times over a novel-length document -- one
341
+ # call per Modern token per pass, and a 302-page document's vocabulary is a
342
+ # few thousand distinct words. The answer is a pure function of its two
343
+ # arguments (`WIDTHS` is module-level and never rebuilt), so a bounded memo
344
+ # is exact, not approximate. Measured on -HOLYMAC.WS: 140,242 calls,
345
+ # ~4,800 distinct keys.
346
+ _WIDTH_MEMO = {}
347
+ _WIDTH_MEMO_CAP = 1 << 16
348
+
349
+
339
350
  def string_width_1000(text, basefont):
340
351
  """Natural width of `text` set in `basefont`, in 1/1000 em.
341
352
 
@@ -347,8 +358,16 @@ def string_width_1000(text, basefont):
347
358
  An unknown basefont falls back to Courier's fixed 600: a face this table
348
359
  does not carry cannot be measured, and 600 is this emitter's own default
349
360
  pitch, not a guess at the missing face."""
361
+ key = (text, basefont)
362
+ hit = _WIDTH_MEMO.get(key)
363
+ if hit is not None:
364
+ return hit
350
365
  table = WIDTHS.get(basefont, _COURIER)
351
- return sum(table[b] for b in text.encode('cp1252', 'replace'))
366
+ total = sum(table[b] for b in text.encode('cp1252', 'replace'))
367
+ if len(_WIDTH_MEMO) >= _WIDTH_MEMO_CAP:
368
+ _WIDTH_MEMO.clear()
369
+ _WIDTH_MEMO[key] = total
370
+ return total
352
371
 
353
372
 
354
373
  def string_width_pt(text, basefont, size):
@@ -138,12 +138,13 @@ def main(argv=None):
138
138
  '.pc positions, a separate mechanism from a # the '
139
139
  'author placed inside a real .he/.fo (that always '
140
140
  'prints, unaffected by this flag). Printed PDF '
141
- 'only. auto (DEFAULT): the document\'s own dot '
141
+ 'and both RTF modes. auto (DEFAULT): the '
142
+ 'document\'s own dot '
142
143
  'commands decide -- .pn/.pg turn it on, .op turns '
143
144
  'it off, exactly like real WordStar; a document '
144
- 'that never touches any of the four gets no '
145
- 'number, byte-identical to before this flag '
146
- 'existed. on: force stock default numbering '
145
+ 'that never touches any of them gets stock '
146
+ 'WordStar 7\'s own number, which is ON. '
147
+ 'on: force stock default numbering '
147
148
  '(bottom-center; .pc repositions it) even on a '
148
149
  'silent document. off: suppress it unconditionally. '
149
150
  'A declared footer always pre-empts it, in every '
@@ -284,7 +285,13 @@ def main(argv=None):
284
285
  # modern and the input can only render printed, say so once instead
285
286
  # of silently disobeying the flag. The override itself stands -- a
286
287
  # print stream has no soft returns to unwrap.
288
+ # planning #264, mail-merge scope: a MailMerge DATA file saved in
289
+ # non-document mode classifies as `printstream` on its bytes and
290
+ # reflows perfectly well -- it is a record list, not a captured
291
+ # page -- so the notice must not tell its reader otherwise.
292
+ from .core import MERGE_DATA_KIND
287
293
  if (mode_explicit and a.mode == 'modern'
294
+ and doc.meta.get('kind') != MERGE_DATA_KIND
288
295
  and (doc.meta.get('variant') == 'printstream'
289
296
  or doc.meta.get('columnar'))):
290
297
  kind = ('print stream' if doc.meta.get('variant') == 'printstream'