A @table now renders to txt. What it needed settled how plain text sets a block off from the prose around it, which is by INDENTING it: that is the only structural device the medium has, where html and LaTeX have boxes, rules and space. An indented block is one that has already been laid out, and the target leaves its lines alone. A caption is placed above a table or a listing and below a figure, in every target rather than only in plain text. A figure is taken in at a glance, and the caption then names what the reader has already seen; a table is read, and a reader who meets the rows before the subject has to read them twice. Also fixed, all three of them silent: an indented paragraph was dropped from plain-text output rather than left as written; an @indent block was re-flowed, so the indentation of its later lines appeared as gaps in the middle of a line; and a table captioned with ":number false" lost the rule under its caption, while an unnumbered table wrongly consumed a number that a reader counting captions would then find missing. (from dev 8173c371bba6)
93 lines
4.2 KiB
Python
93 lines
4.2 KiB
Python
"""Layout helpers for the txt target — the plain-text sibling of
|
|
html_util.py and latex_util.py.
|
|
|
|
What is target-specific about plain text is that it has no elements to
|
|
hang anything on: a block is set off from the prose around it by being
|
|
INDENTED, and by having laid itself out before the target sees it. Two
|
|
consequences run through this module:
|
|
|
|
* The indent is written with "~", the non-breaking space, never with a
|
|
real space. Eval::eval TRIMS a klammer's result, so leading spaces
|
|
never survive to the output; and the txt target's justification pass
|
|
treats a paragraph beginning with " " or "~" as already laid out and
|
|
leaves it alone (phases.txt_justify_blocks). So the same character
|
|
both survives and declares "do not re-flow this".
|
|
|
|
* A block takes the indent out of its own measure. The page has
|
|
Target_txt_width columns; a block indented by N has that minus N, and
|
|
a block laid out to the page's width runs past the margin with
|
|
nothing to report it.
|
|
"""
|
|
|
|
import os
|
|
import re
|
|
import sys
|
|
import textwrap
|
|
sys.path.append(f'{os.environ.get("KLAMMERTEXT_HOME")}/sks/kutil')
|
|
import kutil
|
|
|
|
# The non-breaking space marker, resolved to a real space at the end of
|
|
# phases.txt_justify_blocks. Named here rather than written as "~" at
|
|
# each use, since it is the whole mechanism of this module.
|
|
NBSP = "~"
|
|
|
|
def indent_text(text, indent):
|
|
"""Every line of text prefixed with `indent` columns of NBSP."""
|
|
if indent <= 0:
|
|
return text
|
|
prefix = NBSP * indent
|
|
return "\n".join(prefix + line for line in text.split("\n"))
|
|
|
|
def block_width(text_width, indent):
|
|
"""The measure available to a block indented by `indent`."""
|
|
return max(1, int(text_width) - indent)
|
|
|
|
def add_caption(element, caption_label, number, caption_text,
|
|
side="top", indent=0):
|
|
"""Attach a caption to an already-laid-out block.
|
|
|
|
`element` is the block's text, indented by `indent` columns of NBSP.
|
|
The caption is placed above it (`side` "top", the default, and
|
|
"left") or below it ("bottom", "right"), on the same indent, so the
|
|
two read as one block and no blank line separates the caption from
|
|
what it captions.
|
|
|
|
Captions go ABOVE a table or a listing and BELOW a figure, and the
|
|
reason is not convention: a figure is taken in at a glance and the
|
|
caption then names what the reader has already seen, while a table or
|
|
a listing is READ, and a reader who meets the rows before the subject
|
|
has to read them twice. ("left"/"right" have no plain-text meaning —
|
|
a caption beside a block would need a second column — so they fall
|
|
back to the vertical side they are nearest.)
|
|
|
|
THE CAPTION IS NOT WRAPPED HERE, and must not be: when `number` is
|
|
true the text placed now is a __CAPTION__ marker, which the numbering
|
|
pass later replaces with something of a quite different length
|
|
("Table 1.2 - ..."), so any line breaks chosen now would be measured
|
|
against text that never reaches the reader. Wrapping the caption is
|
|
the numbering pass's job, once it holds the final text; it can read
|
|
the indent back off the leading NBSP run.
|
|
|
|
`caption_style` and `caption_size` are accepted by the html and tex
|
|
siblings and are absent here on purpose: plain text has one face at
|
|
one size, so there is nothing for them to select. That is stated in
|
|
@table's and @code's descriptions rather than left as a silent
|
|
difference between targets.
|
|
"""
|
|
# BOTH cases go out as a marker, the unnumbered one with an empty
|
|
# label. Everything the numbering pass does to a caption -- wrapping
|
|
# it to the block's measure, ruling it off -- it does inside the
|
|
# marker substitution, so a caption that arrived there as plain text
|
|
# got none of it: ":number false" silently lost its rule. The label
|
|
# is what says "number me", not the presence of the marker.
|
|
if number:
|
|
caption = kutil.caption_marker(caption_label, caption_text, None)
|
|
elif caption_text:
|
|
caption = kutil.caption_marker("", caption_text, None)
|
|
else:
|
|
return element
|
|
caption = indent_text(re.sub(r"\s*\n\s*", " ", caption.strip()), indent)
|
|
if side in {"top", "left"}:
|
|
return caption + "\n" + element
|
|
return element + "\n" + caption
|