#include "document_class.h" #include "latex_util.h" #include "machine.h" #include "show.h" #include "log.h" // extern "C" is needed for dlsym name lookup. Returning std::string from // C-linkage functions is technically non-standard but works correctly on // Linux (Itanium ABI) where C and C++ calling conventions are identical. #if defined(__clang__) #pragma clang diagnostic ignored "-Wreturn-type-c-linkage" #endif extern "C" std::string document(Machine& machine) { (void)K::log(3); try { Document_class D(machine); return D.result(); } catch (Error& err) { err.print_message(); std::cout << "\n"; exit(1); } } // Print a console warning for each KT-WIDE-TABLE marker a table's runtime // width check (tex_width_check() in table.py) left in the xelatex log, // followed by a short :column_width primer. Plain line scanning -- no // std::regex over the (arbitrarily large) log text. // One of the two surviving WARNINGS (see the output policy in CLAUDE.md; the // other is warn_unparsed_katoms). It stays a warning because the judgment is // a HEURISTIC and a question of layout QUALITY rather than well-formedness: // the check carries a 2pt tolerance for exactly-full tables, and a document // with one over-wide table still renders. Halting would turn a false positive // into a blocker. Promote it to an error when the measurement is exact. static void warn_wide_tables(const std::string& xelatex_log) { bool any = false; std::istringstream lines(xelatex_log); std::string line; while (std::getline(lines, line)) { auto pos = line.find("KT-WIDE-TABLE "); if (pos == std::string::npos) continue; if (!any) { std::cerr << yellow << "Warning: a table is wider than the text column " << "and extends past the right margin:\n"; any = true; } std::string detail = line.substr(pos + 14); // The log's newline encoding can leave a trailing backslash. while (!detail.empty() && (detail.back() == '\\' || detail.back() == ' ')) detail.pop_back(); std::cerr << " " << detail << "\n"; } if (any) { std::cerr << " The :column_width values and how they interact:\n" " fit the column's widest entry, never wrapped\n" " fill the width left over after the other columns, but\n" " no more than the widest entry; wraps when needed\n" " 0.0-1.0 that fraction of the text column width\n" " * the width left over, unconditionally (the table\n" " always spans the full text column)\n" " A long-text column set to \"fit\" never wraps and pushes\n" " the table off the page; give it \"fill\" instead.\n" << black; } } extern "C" std::string tex_to_pdf(Machine& machine) { (void)K::log(3); try { check_for_xelatex(); std::string outbase = machine.m_state.value("K_output_dir") + "/" + machine.m_state.value("K_output_basename"); std::string tex_filename = outbase + ".tex"; for (auto ext : {"aux", "log", "out", "toc"}) { fs::remove(outbase + "." + ext); } // xelatex writes .pdf/.log/.aux/.toc into its cwd unless told otherwise; // K_output_dir need not be cwd (it follows the input file, or -o). // The embedded paths are quoted defensively: they derive from user // filenames, which may contain spaces. std::string command = "xelatex -interaction=batchmode -halt-on-error -output-directory=\"" + machine.m_state.value("K_output_dir") + "\" \"" + tex_filename + "\""; string_to_file(tex_filename, machine.m_result); std::string xelatex_output = exec(command.c_str()); std::string xelatex_log = string_from_file(outbase + ".log"); std::vector error_lines = find_latex_error_lines(xelatex_log); if (!error_lines.empty()) { std::stringstream ss {}; ss << "Errors reported in xelatex log file:\n"; for (auto line : error_lines) { ss << " " << line << "\n"; } ss << "Check log file: " << outbase << ".log"; throw Definition_error(ss.str(), Locator(), false); } if (std::regex_search(xelatex_log, std::regex("Package rerunfilecheck Warning:"))) { (void)K::log(1, "Rerunning xelatex because document structure has changed"); xelatex_output = exec(command.c_str()); xelatex_log = string_from_file(outbase + ".log"); error_lines = find_latex_error_lines(xelatex_log); if (!error_lines.empty()) { std::stringstream ss {}; ss << "Errors reported in xelatex log file:\n"; for (auto line : error_lines) { ss << " " << line << "\n"; } ss << "Check log file: " << outbase << ".log"; throw Definition_error(ss.str(), Locator(), false); } } warn_wide_tables(xelatex_log); /* if (std::stoi(machine.m_state.value("K_verbose_level")) < 2) { for (auto ext : word_split("tex out aux log toc")) { fs::remove(outbase + "." + ext); } } */ return ""; } catch (Error& err) { std::cout << red; err.print_message(); std::cout << black << "\n"; exit(1); } }