Offer-motivated features: :hline defaults, ranged :hpos, @date :days, :bottom none
- @table: a writer's :hline/:vline replaces the default lines; new
boundary name 'none' removes all lines
- @table: ranged :hpos argument overrides :cell_hpos per cell
(\multicolumn{1} in tex; positions a colspan anchor's merged cell)
- @date/@datetime: :days offset argument (sks/date/date.py)
- @document: ":bottom none" suppresses the footer (\pagestyle{empty})
Also carries the escape-system generator/renderer fixes, per-cell-range
:format, and uppercase .TTF/.OTF font recognition from klammertext-dev.
This commit is contained in:
@@ -413,6 +413,66 @@ katom_list Machine::apply_klammer(
|
||||
result[i].m_type = katom_t::text;
|
||||
}
|
||||
}
|
||||
// Escape target-specific characters (e.g. tex "&" -> "\&") in the writer
|
||||
// text of a GENERAL klammer's body. Runs BEFORE process_katoms/apply()
|
||||
// below expand the body, so that target-native markup pulled in by nested
|
||||
// klammers (e.g. nl.tex -> "\newline") is left untouched -- only this
|
||||
// klammer's own literal writer text is escaped here; nested klammers escape
|
||||
// theirs when they are applied in turn. Bodies from target-specific
|
||||
// definitions (m_body_generic[target] == false) are already in target form
|
||||
// and skipped. KTESC markers are idempotent, so text already escaped at the
|
||||
// top level passes through unchanged. Two kinds of body content are NOT
|
||||
// writer text and must be skipped:
|
||||
// * ^'...'^ literal spans -- raw target markup the writer typed directly.
|
||||
// At this point they are typed literal_begin/literal_end with plain-text
|
||||
// content (the literal phase runs in process_katoms, below), so track
|
||||
// span depth rather than testing katom type.
|
||||
// * @eval / @read / @cond argument spans -- code, filenames, and
|
||||
// predicates consumed by the primitive, NOT emitted as target text.
|
||||
// (Escaping an underscore in "offer.Price_list(K)" broke @eval.) The
|
||||
// primitive's KLAMMERTEXT result, produced by process_katoms below, is
|
||||
// klammer output and is likewise never escaped -- it is inserted after
|
||||
// this pass and so is untouched, matching the top-level behavior where
|
||||
// @eval is resolved before the escape pass runs.
|
||||
auto gen = klammer.m_body_generic.find(target);
|
||||
if (gen != klammer.m_body_generic.end() && gen->second) {
|
||||
Target tgt = m_targets.get(target, Locator());
|
||||
if (!tgt.m_escapes.empty()) {
|
||||
int literal_depth = 0;
|
||||
int code_depth = 0; // inside an @eval/@read/@cond span
|
||||
std::vector<bool> apply_is_code; // one entry per open application
|
||||
for (auto& k : result) {
|
||||
if (k.m_type == katom_t::literal_begin) { ++literal_depth; continue; }
|
||||
if (k.m_type == katom_t::literal_end) {
|
||||
if (literal_depth > 0) --literal_depth;
|
||||
continue;
|
||||
}
|
||||
if (k.m_type == katom_t::eval_begin ||
|
||||
k.m_type == katom_t::read_begin ||
|
||||
k.m_type == katom_t::cond_begin) {
|
||||
apply_is_code.push_back(true);
|
||||
++code_depth;
|
||||
continue;
|
||||
}
|
||||
if (k.m_type == katom_t::apply_begin) {
|
||||
apply_is_code.push_back(false);
|
||||
continue;
|
||||
}
|
||||
if (k.m_type == katom_t::apply_end) {
|
||||
if (!apply_is_code.empty()) {
|
||||
if (apply_is_code.back()) --code_depth;
|
||||
apply_is_code.pop_back();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (literal_depth == 0 && code_depth == 0 &&
|
||||
(k.m_type == katom_t::text ||
|
||||
k.m_type == katom_t::word ||
|
||||
k.m_type == katom_t::newline))
|
||||
k.m_text = tgt.escape_text(k.m_text);
|
||||
}
|
||||
}
|
||||
}
|
||||
process_katoms(result, klammer.m_name);
|
||||
apply(m_klammers, result, target);
|
||||
m_state.close_frame();
|
||||
|
||||
Reference in New Issue
Block a user