#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
\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