Files
klammertext/sks/document/document_html.cpp
Andy Kopra 8a2699a253 Typed arguments, calculated tables, spans, closed-world fonts, top-level fnt/ and env/
Sync with klammertext-dev through b90b0e09:

- Argument types end to end: :python_cast values are applied (Python
  @eval receives real bools/numbers/lists), argument values are
  validated against their argtype patterns with the argtype's
  description as the error message, argtypes can declare :default
  (overridable per declaration), and parameterized type families are
  supported: rest(N) casts a rest argument to an N-dimensional list
  (bar-count = dimension).
- Unified indexed_range syntax (selector with parenthesized subsets,
  composable mnemonic names) for table lines and spans.
- Table klammer: caption fonts fixed in both targets, :column_width /
  :leading / :colsep wired, :colspan and :rowspan render (HTML
  attributes; \multicolumn / \multirow), calculated cell values (:calc)
  with prefix operators, display-precision semantics, :calc_format and
  :decimal period|comma.
- Fonts: closed-world resolution on the Klammertext font store
  (infrastructure in mac/font_store; no Google Fonts links or fetch).
  Default fonts live in the top-level fnt/; additional fonts install
  into KLAMMERTEXT_FONTS directories via kdesc --font (list, samples,
  preview, install — classification by font metadata).  CSS font family
  names are quoted (digit-initial families were silently lost).
- Environment files moved from mac/env/ to the top-level env/; shell
  profiles source env/runtime.env.  Dead per-host variants removed.
- Container: fnt/ ships in the image; curl removed (no network use).
2026-07-22 18:17:43 +02:00

767 lines
28 KiB
C++

#include "util.h"
#include "error.h"
#include "log.h"
#include "kutil.h"
#include "html_util.h"
#include "heading.h"
#include "document_class.h"
#include "character.h"
#include "reference.h"
#include "show.h"
#include "file.h"
std::string unescape_newlines(std::string s)
{
return string_replace(s, " ___NL___ ", "\n");
}
int ID = 0;
std::string BASE {};
static const std::regex newline_rgx(R"(\n)");
static const std::regex element_open_rgx(R"(<([\w-]+)(\s+.*?)?>)");
static const std::regex id_attr_rgx("id=\"(.*?)\"");
std::string insert_missing_ids(std::smatch match)
{
(void)K::log(3);
std::string result = match[0];
std::string tag = match[1];
if (html::tag_is_block_element(tag)) {
std::string attrs = match[2];
std::stringstream ss {};
if (attrs.find(" id=") == std::string::npos) {
ss << "id=\"_e" << ID++<< "\"";
}
ss << attrs;
if (!BASE.empty()) {
ss << " data-basename=\"" << BASE << "\"";
}
result = "<" + tag + " " + trim(ss.str()) + ">";
}
return result;
}
std::string insert_ids(std::string html_text)
{
(void)K::log(3);
std::string result = html_text;
result = freplace(result, element_open_rgx, insert_missing_ids);
return result;
}
std::string Document_class::make_help_page()
{
(void)K::log(3);
std::string kt_filename = klammertext_filename("doc/html_help.kt");
std::string basename = "help";
std::string help_page;
//if (!in_modification_order(kt_filename, output_filename, no_cache)) {
if (cache_requires_update(m_cache_dir, kt_filename, basename)) {
Machine Mh = m_machine;
Mh.read(fs::path(klammertext_filename("doc/html_help.kt")));
help_page = Mh.apply("html");
help_page += "\n<div class=\"endspace\"></div>\n";
help_page = html::make_paragraphs(help_page);
write_to_cache(m_cache_dir, basename, help_page);
} else {
help_page = read_from_cache(m_cache_dir, basename);
}
return help_page;
}
std::string Document_class::color_definitions()
{
std::stringstream ss {};
ss << ":root {\n"
<< " --frame_background_color: " << frame_background_color << ";\n"
<< " --frame_text_color: " << frame_text_color << ";\n"
<< " --nav_background_color: " << nav_background_color << ";\n"
<< " --nav_text_color: " << nav_text_color << ";\n}\n";
return ss.str();
}
strings_t Document_class::resolved_font_names()
{
strings_t result {};
auto add = [&](const Resolved_font& rf) {
if (!rf.family_name.empty())
result.push_back(rf.dir_name);
};
add(m_resolved_serif);
add(m_resolved_sans);
add(m_resolved_mono);
return result;
}
std::string Document_class::font_definitions()
{
std::stringstream ss {};
if (!m_serif_font.empty() || !m_sans_font.empty() || !m_mono_font.empty()) {
// Family names are quoted: an unquoted name with a digit-initial
// word ("Source Sans 3") is invalid CSS, and a font-family using
// var() with such a value computes to inherit, silently losing
// the font.
ss << ":root {\n";
if (!m_serif_font.empty())
ss << " --serif: \"" << m_serif_font << "\", serif;\n";
if (!m_sans_font.empty())
ss << " --sans-serif: \"" << m_sans_font << "\", sans-serif;\n";
if (!m_mono_font.empty())
ss << " --monospace: \"" << m_mono_font << "\", monospace;\n";
ss << "}\n";
}
// Emit scale factors so sans and mono fonts match the serif font.
// Three scaling methods (uncomment the desired one):
// x-height: serif_xh / other_xh (matches lowercase, like fontspec MatchLowercase)
// cap-height: serif_ch / other_ch (matches capitals)
// average: mean(serif_xh,serif_ch) / mean(other_xh,other_ch) (compromise)
float serif_xh = m_resolved_serif.xheight_ratio;
float serif_ch = m_resolved_serif.capheight_ratio;
float serif_avg = (serif_xh + serif_ch) / 2.0f;
if (serif_avg > 0.0f) {
auto scale = [&](const Resolved_font& other) -> std::string {
float other_avg = (other.xheight_ratio + other.capheight_ratio) / 2.0f;
if (other_avg > 0.0f && other_avg != serif_avg) {
char buf[16];
// float ratio = serif_xh / other.xheight_ratio; // x-height
// float ratio = serif_ch / other.capheight_ratio; // cap-height
float ratio = serif_avg / other_avg; // average
std::snprintf(buf, sizeof(buf), "%.4f", ratio);
return buf;
}
return "";
};
std::string sans_scale = scale(m_resolved_sans);
std::string mono_scale = scale(m_resolved_mono);
if (!sans_scale.empty() || !mono_scale.empty()) {
ss << ":root {\n";
if (!sans_scale.empty())
ss << " --sans-serif-scale: " << sans_scale << ";\n";
if (!mono_scale.empty())
ss << " --monospace-scale: " << mono_scale << ";\n";
ss << "}\n";
}
}
// Global font scale: applied to body font-size
if (m_font_scale != 1.0f) {
char buf[16];
std::snprintf(buf, sizeof(buf), "%.4f", m_font_scale);
ss << "body { font-size: " << buf << "rem; }\n";
}
return ss.str();
}
void Document_class::write_basenames_js_file(std::string filename, std::vector<std::string> basenames)
{
(void)K::log(3);
std::string tab(" ");
std::stringstream ss {};
ss << "function pages_basenames()\n{\n return [\n";
int i = 0;
int last = basenames.size() - 1;
for (std::string base : basenames) {
ss << tab << "\"" << base << "\"";
if (i < last) {
ss << ",";
}
ss << "\n";
i++;
}
ss << " ];\n}\n";
write(filename, ss.str());
}
std::string Document_class::create_html_output_directories()
{
(void)K::log(3);
std::string output_directory =
absolute_pathname(get("K_output_dir")) + "/" + get("K_output_basename");
if (!m_toc_only) {
if (!file_exists(output_directory)) {
fs::create_directory(output_directory);
}
install_resolved_font(m_resolved_serif, output_directory);
install_resolved_font(m_resolved_sans, output_directory);
install_resolved_font(m_resolved_mono, output_directory);
}
m_cache_dir = cache_directory("_html_pages");
if (m_no_cache && fs::exists(m_cache_dir)) {
for (auto& entry : std::filesystem::directory_iterator(m_cache_dir)) {
msg() << " Removing cache directory: " << entry << "\n";
std::filesystem::remove_all(entry);
}
}
if (!m_toc_only) {
if (!file_exists(output_directory, false, true)) {
fs::create_directory(output_directory);
}
}
return output_directory;
}
strings_t get_css_files(strings_t custom_css_files)
{
(void)K::log(3);
strings_t result = sks_files_of_type("css");
result.insert(result.end(), custom_css_files.begin(), custom_css_files.end());
return result;
}
std::vector<std::string> js_basenames(bool nav)
{
if (nav) {
return { "kutil", "document", "image", "resize_window", "resize_text", "resize_toc",
"toc", "level", "next_last", "help", "show_links", "search", "state", "code", "link" };
} else {
return {"kutil", "document", "image"};
}
}
strings_t Document_class::get_js_files(
bool nav, bool include_sks_js,
std::string pages_basenames_filename)
{
(void)K::log(3);
strings_t result {};
if (include_sks_js) {
for (auto b : js_basenames(nav)) {
std::vector<fs::path> js_files =
find_file_recursive(klammertext_dir() + "/sks", b + ".js");
if (js_files.size() == 0) {
throw File_error("File " + q_(b) + " not found.", Locator());
}
result.push_back(js_files[0].string());
}
if (!pages_basenames_filename.empty()) {
result.push_back(pages_basenames_filename);
}
}
return result;
}
strings_t link_filenames(strings_t& full_filenames, std::string page_dir)
{
strings_t result {};
fs::path page_dir_path(page_dir);
// int i = 0;
std::transform(full_filenames.begin(), full_filenames.end(), std::back_inserter(result),
[&](std::string f) {
fs::path full(f);
//msg() << i << full << " " << full.filename() << "\n";
fs::path path(page_dir_path / full.filename());
//std::cout << i++ << path << "\n";
return path.string(); });
return result;
}
std::pair<std::vector<std::string>, std::vector<std::string>>
Document_class::html_auxiliary_files(std::string output_directory, std::string pages_basenames_filename,
std::vector<std::string> custom_css_files)
{
(void)K::log(3);
strings_t all_css_files = custom_css_files;
strings_t css_link_filenames {};
if (write_files && m_include_sks_css) {
all_css_files = get_css_files(m_css_filenames);
//output_dir, color_definitions(), css_files, write_files);
}
if (write_files) {
copy_preserving_basename(
all_css_files, output_directory, "css");
css_link_filenames = link_filenames(all_css_files, "css");
}
strings_t all_js_files {};
strings_t js_link_filenames {};
if (write_files) {
bool nav = m_structure == docstruct_t::book;
all_js_files = get_js_files(
nav, m_include_sks_js,
pages_basenames_filename); // , output_dir, write_files);
copy_preserving_basename(
all_js_files, output_directory, "js");
js_link_filenames = link_filenames(all_js_files, "js");
}
// msg() << "all_css_files:\n " << join(all_css_files, "\n ") << "\n\n";
// msg() << "all_js_files:\n " << join(all_js_files, "\n ") << "\n\n";
return {css_link_filenames, js_link_filenames};
}
void Document_class::process_input_files()
{
(void)K::log(3);
m_file_components.clear();
size_t basename_width = 0;
for (auto input_filename : m_files) {
fs::path inpath = input_filename;
if (!file_exists(inpath)) {
inpath = parse_input_filename(input_filename, m_input_dir);
}
std::string basename = file_basename(input_filename);
basename_width = std::max(basename_width, basename.size());
std::string html_text;
//if (true || !in_modification_order(inpath, cached_filename, m_no_cache)) {
if (m_no_cache || cache_requires_update(m_cache_dir, inpath, basename)) {
Machine M = m_machine;
M.read(inpath);
html_text = trim(M.apply("html"));
html_text = trim(html::make_paragraphs(html_text)) + "\n";
html_text = unescape_newlines(html_text);
BASE = basename;
html_text = insert_ids(html_text);
m_file_components.push_back({basename, html_text});
write_to_cache(m_cache_dir, basename, html_text);
} else {
html_text = read_from_cache(m_cache_dir, basename);
m_file_components.push_back({basename, html_text});
}
}
// for (auto [src, result] : m_file_components) {
// msg() << std::left << std::setw(basename_width) << src << " " << abbrev(result, 96) << "\n";
// }
}
std::string increment_level(int level, std::vector<int> &levels)
{
levels[level]++;
int zero_offset = level == 0 ? 2 : 1;
for (unsigned int i = level + zero_offset; i < levels.size(); i++) {
levels[i] = 0;
}
std::stringstream result {};
if (level == 0) {
result << levels[0];
} else { // Parts not included in numbering
for (auto i = 1; i <= level; i++) {
result << levels[i];
if (i < level) {
result << ".";
}
}
}
// msg() << level << sp_arrow << result.str() << sp_arrow << "\n";
return result.str();
}
std::string extract_id(std::string attr)
{
std::smatch match {};
if (!std::regex_match(attr, match, id_attr_rgx)) {
throw Internal_error("Pattern for id attribute incorrect: " + q_(attr));
}
return match[1];
}
std::vector<Heading>
Document_class::insert_section_numbers(std::string marker, bool add_to_toc)
{
std::vector<int> levels(9, 0);
std::smatch match {};
std::string pattern = R"(<([\w-]+)\s*(.*?)>(.*?)MARKER\s*</span>\s*(.*?)<.*)";
pattern = string_replace(pattern, "MARKER", marker);
std::regex heading_rgx(pattern);
std::vector<std::pair<std::string, std::string>> modified_components {};
std::vector<Heading> headings;
bool include_toc = m_structure != docstruct_t::plain;
for (auto [src, result] : m_file_components) {
std::stringstream modified {};
for (std::string line : regex_split(result, newline_rgx, false)) {
if (std::regex_match(line, match, heading_rgx)) {
// xheading
std::string tag = match[1];
std::string id = extract_id(match[2]);
std::string title = match[3];
std::string heading = match[4];
const int level = html_tags[match[1]];
// msg() << line << sp_arrow << broken_bar << tag << broken_bar
// << level << broken_bar << "ATTR" << broken_bar << title << broken_bar
// << heading << broken_bar << "\n";
std::string number = increment_level(level, levels);
line = string_replace(line, marker, number);
if (include_toc) {
line = "<a href=\"#_c" + id + "\" draggable=\"false\">" + line + "</a>";
}
//msg() << attr << " " << number << " " << heading << "\n";
if (add_to_toc) {
m_toc_components.push_back({tag, id, number, heading});
}
Heading hd(level, src, number, id, number, heading);
headings.push_back(hd);
// msg() << line << "\n\n";
}
modified << line << "\n";
}
modified_components.push_back({src, modified.str()});
}
m_file_components = modified_components;
return headings;
}
int get_caption_number(std::map<std::string, int>& numbers, std::string label)
{
int result;
if (numbers.count(label) == 0) {
result = 1;
numbers[label] = 2;
} else {
result = numbers[label];
numbers[label]++;
}
return result;
}
void Document_class::insert_html_caption_numbers()
{
// msg() << "insert_html_caption_numbers\n";
std::regex chapter_rgx(R"(.*sectionnumber">(\d+)</span>.*)");
std::regex caption_rgx(R"(__CAPTION__(.*?)__CAPTION__(.*?)__CAPTION__)");
std::string current_chapter {};
std::map<std::string, int> current_caption_number {};
std::vector<std::pair<std::string, std::string>> modified_components {};
for (auto [src, result] : m_file_components) {
std::stringstream modified {};
//std::vector<std::string> references {};
for (std::string line : regex_split(result, newline_rgx, false)) {
std::smatch chapter_match {};
if (std::regex_match(line, chapter_match, chapter_rgx)) {
current_chapter = chapter_match[1];
// msg() << "Chapter " << current_chapter << " " << line << "\n";
current_caption_number.clear();
}
// msg() << "Line: " << line << "\n";
std::smatch caption_match {};
if (std::regex_search(line, caption_match, caption_rgx)) {
std::string label = caption_match[1];
std::string ctext = caption_match[2];
int caption_number = get_caption_number(current_caption_number, label);
// msg() << " caption: " << current_chapter << "." << caption_number << "\n";
// msg() << "Label: " << label << " Text: " << ctext << "\n";
std::stringstream ss {};
ss << label << " ";
if (!current_chapter.empty()) {
ss << current_chapter << ".";
}
ss << caption_number << ctext;
line = std::regex_replace(line, caption_rgx, trim(ss.str()));
// msg() << "line: " << line << "\n";
// references.push_back(line);
}
// if (std::regex_search(line, reference_rgx)) {
// references.push_back(line);
// }
modified << line << "\n";
}
modified_components.push_back({src, modified.str()});
//m_reference_sequences.push_back(references);
}
m_file_components = modified_components;
}
std::vector<std::tuple<std::string, std::string, std::string>>
Document_class::reference_list(std::string target_marker)
{
std::regex reference_rgx("__REF__");
std::regex target_rgx("<div id=\"(.*?)\" data-label=\"(.*?)\" class=\"caption_");
std::vector<std::tuple<std::string, std::string, std::string>> references {};
for (auto [src, result] : m_file_components) {
for (std::string line : regex_split(result, newline_rgx, false)) {
if (std::regex_search(line, reference_rgx)) {
references.push_back({target_marker, src, line});
} else {
std::smatch match {};
if (std::regex_search(line, match, target_rgx)) {
references.push_back({"", match[2], match[1]});
}
}
}
}
return references;
}
std::map<std::string, std::string> Document_class::id_to_caption_number()
{
std::regex target_rgx("<div id=\"(.*?)\" data-label=\"(.*?)\" class=\"caption_");
std::regex caption_rgx(R"(<div id=.*?>(\w+\s+[\w.]+))");
std::map<std::string, std::string> result {};
for (auto [src, ftext] : m_file_components) {
auto lines = regex_split(ftext, newline_rgx, false);
for (unsigned int i = 0; i < lines.size(); i++) {
std::smatch match {};
if (std::regex_search(lines[i], match, target_rgx)) {
std::string id = match[1];
unsigned int j = i + 1;
std::smatch caption_match {};
while (j < lines.size()) {
if (std::regex_search(lines[j], caption_match, caption_rgx)) {
std::string caption = caption_match[1];
// msg() << id << sp_arrow << caption << "\n";
result[id] = caption;
break;
}
j++;
}
}
}
}
return result;
}
std::pair<int, int> offset_spec_to_count(std::string spec)
{
std::regex rgx(R"((\w+)\s*(\d*))");
std::smatch match {};
int offset = 1;
int dir = 1;
if (std::regex_match(spec, match, rgx)) {
std::string desc = match[1];
std::string value = match[2];
if (!value.empty()) {
offset = std::stoi(value);
}
if (desc == "before") {
dir = -1;
}
} else {
throw Internal_error("Reference offset spec incorrect: " + spec);
}
return {offset, dir};
}
std::map<std::string, std::vector<std::pair<std::string, std::string>>>
Document_class::html_references()
{
std::string target_marker = "@";
auto references = reference_list(target_marker);
// msg() << "\n";
for (unsigned int i = 0; i < references.size(); i++) {
auto [mark, name, ref] = references[i];
// msg() << " " << i << ": " << mark << " " << name << " " << ref << "\n";
}
// msg() << "\n";
auto id_captions = id_to_caption_number();
std::regex ref_rgx(R"(__REF__(.*?)__(.*?)__)");
std::string offset_spec;
std::string type;
std::map<std::string, std::vector<std::pair<std::string, std::string>>> result {};
for (unsigned int i = 0; i < references.size(); i++) {
auto [mark, name, ref] = references[i];
// msg() << " " << i << ": " << mark << " " << name << " " << ref << "\n";
if (mark == target_marker) {
std::smatch match {};
if (std::regex_search(ref, match, ref_rgx)) {
offset_spec = match[1];
type = match[2];
} else {
throw Internal_error("Reference form is incorrect: " + ref);
}
auto [offset, dir] = offset_spec_to_count(offset_spec);
int j = i + dir;
while (offset > 0 && j >= 0 && j < (int)references.size()) {
// msg() << " check: " << std::get<2>(references[j]) << "\n";
if (std::get<1>(references[j]) == type) {
offset--;
}
if (offset == 0) {
break;
}
j += dir;
}
if (j < 0 || j >= (int)references.size()) {
// msg() << "Not found: " << std::get<2>(references[i]) << "\n";
} else {
std::string id = std::get<2>(references[j]);
std::string line = std::get<2>(references[i]);
// msg() << "Ref: " << line << boldblack << " found: " << black << id << "\n";
std::string link = "<a href=\"#" + id + "\">" + id_captions[id] + "</a>";
// msg() << link << "\n";
std::string modified_line = std::regex_replace(line, ref_rgx, link);
// msg() << "modified_line: " << name << sp_arrow << modified_line << "\n";
result[name].push_back({line, modified_line});
}
}
}
return result;
}
void Document_class::resolve_html_references()
{
auto references = html_references();
std::vector<std::pair<std::string, std::string>> modified_components {};
for (auto [src, ftext] : m_file_components) {
// msg() << "resolve " << src << ":\n";
std::string modified_text = ftext;
for (auto [old_line, new_line] : references[src]) {
// msg() << old_line << sp_arrow << new_line << "\n";
modified_text = string_replace(modified_text, old_line, new_line);
}
modified_components.push_back({src, modified_text});
}
m_file_components = modified_components;
}
std::string Document_class::single_page_toc()
{
// <h1 id="_e9"><span class="sectionnumber">2.1</span> Section two.one</h1>
std::regex id_rgx("id=\"(.*?)\"");
std::stringstream ss {};
ss << "<div class=\"toctitle\">Contents</div>\n";
int minimum_depth = 7;
// Adjust left minimum margin based on whether parts or chapters are the top level:
for (auto [tag, id, number, heading] : m_toc_components) {
minimum_depth = std::min(minimum_depth, html_tags[tag]);
}
for (auto [tag, id, number, heading] : m_toc_components) {
int depth = html_tags[tag];
// msg() << "TOC: " << depth << " " << id << " " << number << " " << heading << "\n";
ss << "<div id=\"_c" << id << "\" class=\"tab" << depth - minimum_depth << " toc\"><a href=\"#" << id << "\">"
<< "<span class=\"tocnumber\">" << number << "</span>" << heading << "</a></div>\n";
}
// msg() << ss.str();
return ss.str();
}
elements_t Document_class::page(std::string body_text, std::string output_dir, int max_level)
{
(void)K::log(3);
std::string toc {}; // Calculate
bool text_only = m_structure == docstruct_t::plain;
// msg() << "output_dir: " << output_dir << "\n";
auto [css_filenames, js_filenames] =
html_auxiliary_files(output_dir, "", m_css_filenames);
std::string toc_text {};
elements_t body {};
html::add_title(body, m_title, m_logo);
if (m_structure == docstruct_t::book) {
html::add_nav(body, max_level);
}
html::add_text(body, body_text, toc_text, text_only);
// html::add_bottom_spacer(body);
html::add_status_bar(body, m_date, m_version, m_copyright);
if (!text_only) {
html::add_page_cache(body);
}
html::add_js_links(body, js_filenames, output_dir) ;
std::string css_text = color_definitions() + font_definitions() + "\n" + m_css_text;
std::string page_title = m_page_title.empty() ? m_title : m_page_title;
elements_t page = html::make_page(
body, page_title,
css_text, css_filenames, resolved_font_names());
return page;
}
std::string Document_class::make_single_html_page(std::string output_directory)
{
(void)K::log(3);
std::stringstream ss {};;
if (m_structure != docstruct_t::plain) {
ss << single_page_toc();
}
for (auto [basename, page_text] : m_file_components) {
ss << "<!-- " << basename << ".kt -->\n\n" << page_text << "\n";
}
std::string single_page = to_string(page(ss.str(), output_directory, 10));
std::string output_file = output_directory + "/index.html";
// msg() << "single_page to " << output_file << "\n";
string_to_file(output_file, single_page);
return "";
}
std::string Document_class::make_html_navigation_structure(
std::string output_directory, std::vector<Heading> headings)
{
msg() << "Navigation format\n";
std::vector<std::string> pages_basenames {};
for (auto [basename, page_text] : m_file_components) {
pages_basenames.push_back(basename);
msg() << basename << ": " << abbrev(page_text, 96) << "\n";
}
std::string pages_basenames_filename = m_cache_dir + "/_pages_basenames.js";
write_basenames_js_file(pages_basenames_filename, pages_basenames);
std::string help_content = make_help_page();
// Build embedded pagecache to avoid Same Origin Policy errors with file:// protocol
std::stringstream pagecache;
pagecache << "<div id=\"pagecache\" style=\"display:none\">\n";
for (auto [basename, page_text] : m_file_components) {
pagecache << "<div id=\"_page_" << basename
<< "\" class=\"content_page\">"
<< page_text << "</div>\n";
}
pagecache << "<div id=\"help_page\">" << help_content << "</div>\n";
pagecache << "<div id=\"search_page\"></div>\n";
pagecache << "</div>\n";
std::pair<int, std::string> toc_spec = make_navigation_table_of_contents(headings);
int max_level = std::get<0>(toc_spec);
std::string toc_body = std::get<1>(toc_spec);
std::string nav_body = "Navigation";
auto [all_css_files, all_js_files] =
html_auxiliary_files(output_directory, pages_basenames_filename, m_css_filenames);
std::string css_text = color_definitions() + font_definitions() + "\n" + m_css_text;
std::stringstream t {};
t << html::page(
m_title, m_page_title, output_directory, nav_body, max_level, toc_body,
m_file_components[0].second,
m_date, m_version, m_copyright,
css_text, all_css_files, all_js_files,
resolved_font_names(),
m_logo);
std::string result = t.str();
result = string_replace(result,
"<div id=\"pagecache\"></div>",
pagecache.str());
std::string output_basename = m_machine.m_state.value("K_output_basename");
std::string output_filename = output_directory + "/index.html";
write(output_filename, result);
return "";
}
std::string Document_class::html()
{
(void)K::log(3);
// msg() << "HTML document\n";
// m_cache_dir = cache_directory(kt_root_filename, "_html_pages");
std::string output_directory = create_html_output_directories();
save_string_input_as_file();
process_input_files();
auto headings = insert_section_numbers();
insert_section_numbers("___NUM2___", false);
insert_html_caption_numbers();
resolve_html_references();
if (m_structure != docstruct_t::book) {
make_single_html_page(output_directory);
} else {
make_html_navigation_structure(output_directory, headings);
}
return "";
}