Files
klammertext/sks/document/document_latex.cpp
T
ack af29bbf7ac Unicode sequences, @typeface, and the font/typeface distinction
A run of Unicode code points now needs one pair of carets, not one pair
each: ^9053 53EF 9053^ renders the characters adjacently, and a single
character inside such a run stands for itself.  An invalid code point is
an error naming the position that wrote it, rather than an apology
rendered into the finished document; noncharacters are refused alongside
surrogates; and a code point that lands on a Klammertext special is safe
through the re-reads that assemble a document.  The character tables now
produce the precomposed form, so a diacritic written as ^a" and one typed
directly are the same text to a search or a copy-paste.

"kdesc -u" describes how to write a Unicode character, with tables of
glyphs beside the codes that produce them.  It is new and not yet
complete: it predates the sequence form and the typeface klammer below.

A typeface and a style are no longer both called "font".  A typeface is a
family resolved by name; a style code is the one letter naming a type
style.  So @document takes :serif_typeface and its siblings, a table takes
:style, captions take :caption_style and :caption_size, the uniform scale
is :text_scale, and the command is "kdesc --typeface".  This renames
arguments a document may already use; an old name is a located error
rather than a silent omission.

@typeface sets a passage in another typeface for the length of that
passage:

    @typeface Noto Sans Egyptian Hieroglyphs | ^132AA 133CF^ :scale 1.5 @

Writing the klammer is the whole declaration -- the document resolves the
typeface, embeds it in what it produces, and carries its license along.
Two typefaces for scripts no text font covers now ship with Klammertext:
Noto Sans Egyptian Hieroglyphs and Noto Sans Cuneiform.  Every typeface in
the distribution now carries its license file.

(from dev 61e598698891)
2026-08-25 23:30:55 +02:00

94 lines
4.1 KiB
C++

#include "util.h"
#include "kutil.h"
#include "log.h"
#include "document_class.h"
#include "reference.h"
#include "file.h"
#include "show.h"
std::string Document_class::create_tex_output_directories()
{
(void)K::log(3);
std::string output_directory =
absolute_pathname(get("K_output_dir")); // + "/" + get("K_output_basename");
m_cache_dir = cache_directory("_latex_files");
if (!m_toc_only) {
if (!file_exists(output_directory, false, true)) {
fs::create_directory(output_directory);
}
}
return output_directory;
}
std::string Document_class::tex()
{
(void)K::log(3);
create_tex_output_directories();
// :text content is already processed (klammers applied by the outer Machine).
// Use it directly; don't re-process through a sub-Machine.
std::string latex_text {};
if (!m_text.empty()) {
latex_text = add_latex_caption_numbers(m_text);
}
if (m_files.size() > 0) {
for (auto input_filename : m_files) {
std::string basename = file_basename(input_filename);
std::string cached_filename = m_cache_dir + "/" + basename;
fs::path inpath = parse_input_filename(input_filename, m_input_dir);
// if (!in_modification_order(input_filename, cached_filename, m_no_cache)) {
if (m_no_cache || cache_requires_update(m_cache_dir, inpath, basename)) {
Machine M = m_machine;
M.m_state.set("document_landscape", landscape ? "true" : "false"); // , "Landscape mode");
M.read(inpath);
std::string processed = trim(M.apply("tex", false));
processed = add_latex_caption_numbers(processed);
write_to_cache(m_cache_dir, basename, processed);
latex_text += processed;
} else {
latex_text += read_from_cache(m_cache_dir, basename);
}
}
}
auto split_lines = regex_split(latex_text, std::regex(R"(\n)"), false);
latex_text = resolve_caption_references("latex", split_lines, latex_line_states(split_lines));
std::map<std::string, std::string> section_to_id {};
std::regex section_id_rgx(R"(%__sectionlink__(.*?)__(.*?)__)");
for (std::string id_section : find_all(latex_text, section_id_rgx, 0)) {
std::smatch match {};
std::regex_match(id_section, match, section_id_rgx);
section_to_id[match[2]] = match[1];
}
latex_text = std::regex_replace(latex_text, section_id_rgx, "");
std::regex seclink_rgx("(.*?)__seclink__(.*?)__(.*)");
auto lines = regex_split(latex_text, std::regex(R"(\n)"), false);
for (auto i = 0ul; i < lines.size(); i++) {
if (lines[i].find("__seclink__") != std::string::npos) {
std::smatch match {};
std::regex_match(lines[i], match, seclink_rgx);
std::stringstream ss {};
ss << match[1] << section_to_id[match[2]] << match[3];
lines[i] = ss.str();
}
}
latex_text = join(lines, "\n");
latex_text = string_replace(latex_text, "___AT___", "^@");
// @typeface: collect what the document asked for, declare each as a
// \newfontfamily switch command in the preamble, and replace every
// marker with its command. The declarations are appended to the
// prolog, which latex::page emits AFTER \setmainfont -- so fontspec is
// loaded and \newfontfamily is valid there.
collect_typefaces(latex_text);
latex_text = resolve_typeface_markers(latex_text);
prolog += typeface_preamble();
std::string structure_name =
m_structure == docstruct_t::book ? "book" :
m_structure == docstruct_t::article ? "article" : "plain";
return latex::page(structure_name,
m_title, m_subtitle, m_author, m_date, m_version, m_copyright,
m_bottom, prolog, paper_size, m_two_column, leading,
point_size, ragged_right,
cover, landscape, latex_text,
m_resolved_serif, m_resolved_sans, m_resolved_mono,
m_text_scale);
}