From 0f4da8e6956949e97df2f496b5b116f240589be8 Mon Sep 17 00:00:00 2001 From: deeaitch Date: Fri, 28 Aug 2026 00:15:31 -0400 Subject: [PATCH] Add virtual converter output interface --- CMakeLists.txt | 2 + typefactory/converter.cpp | 106 ++++++++--------------------------- typefactory/converter.h | 15 ++++- typefactory/convertlatex.cpp | 88 +++++++++++++++++++++++++++++ typefactory/convertlatex.h | 13 +++++ typefactory/main.cpp | 4 +- typefactory/typefactory.pro | 2 + 7 files changed, 144 insertions(+), 86 deletions(-) create mode 100644 typefactory/convertlatex.cpp create mode 100644 typefactory/convertlatex.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 40aec88..b8c7f61 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,8 @@ function(kindle2latex_enable_warnings target) endfunction() add_executable(kindle2latex_typefactory + typefactory/convertlatex.cpp + typefactory/convertlatex.h typefactory/converter.cpp typefactory/converter.h typefactory/main.cpp diff --git a/typefactory/converter.cpp b/typefactory/converter.cpp index 25a5917..6b14654 100644 --- a/typefactory/converter.cpp +++ b/typefactory/converter.cpp @@ -18,49 +18,6 @@ static std::string rstrip_cr (std::string s) { return s; } -static std::string latex_escape (const std::string &s) { - std::string out; - out.reserve (s.size() + s.size() / 8); - for (unsigned char ch : s) { - switch (ch) { - case '\\': - out += "\\textbackslash{}"; - break; - case '{': - out += "\\{"; - break; - case '}': - out += "\\}"; - break; - case '%': - out += "\\%"; - break; - case '$': - out += "\\$"; - break; - case '#': - out += "\\#"; - break; - case '&': - out += "\\&"; - break; - case '_': - out += "\\_"; - break; - case '^': - out += "\\textasciicircum{}"; - break; - case '~': - out += "\\textasciitilde{}"; - break; - default: - out.push_back (static_cast (ch)); - break; - } - } - return out; -} - struct Quote { std::string meta; std::vector text_lines; @@ -211,6 +168,30 @@ int Converter::error_code() const noexcept { return error_code_; } +void Converter::set_error_code (int error_code) noexcept { + error_code_ = error_code; +} + +const std::vector &Converter::titles() const noexcept { + return impl_->context.order; +} + +std::size_t Converter::quote_count (const std::string &title) const { + const auto it = impl_->context.by_title.find (title); + return it == impl_->context.by_title.end() ? 0 : it->second.size(); +} + +const std::string &Converter::quote_meta (const std::string &title, + std::size_t index) const { + return impl_->context.by_title.at (title).at (index).meta; +} + +const std::vector &Converter::quote_text_lines ( + const std::string &title, + std::size_t index) const { + return impl_->context.by_title.at (title).at (index).text_lines; +} + bool Converter::parse() { std::ifstream input (input_file_name_); if (!input.is_open()) { @@ -256,42 +237,3 @@ bool Converter::parse() { error_code_ = 0; return true; } - -bool Converter::save (const std::string &output_file_name) { - if (!parse()) - return false; - - std::ofstream output (output_file_name, std::ios::trunc); - if (!output.is_open()) { - std::cerr << "Failed to open output file: " - << output_file_name - << " (" << std::strerror (errno) << ")\n"; - error_code_ = 3; - return false; - } - - const ParseContext &ctx = impl_->context; - for (std::size_t i = 0; i < ctx.order.size(); ++i) { - const auto &title = ctx.order[i]; - output << "\\section {" << latex_escape (title) << "}\n"; - - const auto it = ctx.by_title.find (title); - if (it == ctx.by_title.end()) - continue; - - for (const auto "e : it->second) { - output << " \\subsection {" << latex_escape (quote.meta) << "}\n"; - - for (const auto &text_line : quote.text_lines) - output << " " << latex_escape (text_line) << "\n"; - - output << " \\subsubsection{notes}\n\n"; - } - - if (i + 1 < ctx.order.size()) - output << "\n"; - } - - error_code_ = 0; - return true; -} diff --git a/typefactory/converter.h b/typefactory/converter.h index 2420f19..710ba93 100644 --- a/typefactory/converter.h +++ b/typefactory/converter.h @@ -2,21 +2,32 @@ #define CONVERTER_H #include +#include #include +#include class Converter { public: explicit Converter (std::string input_file_name); - ~Converter(); + virtual ~Converter(); Converter (const Converter &) = delete; Converter &operator= (const Converter &) = delete; - bool save (const std::string &output_file_name); + virtual bool save (const std::string &output_file_name) = 0; [[nodiscard]] int error_code() const noexcept; protected: bool parse(); + void set_error_code (int error_code) noexcept; + + [[nodiscard]] const std::vector &titles() const noexcept; + [[nodiscard]] std::size_t quote_count (const std::string &title) const; + [[nodiscard]] const std::string "e_meta (const std::string &title, + std::size_t index) const; + [[nodiscard]] const std::vector "e_text_lines ( + const std::string &title, + std::size_t index) const; private: struct Impl; diff --git a/typefactory/convertlatex.cpp b/typefactory/convertlatex.cpp new file mode 100644 index 0000000..70e1571 --- /dev/null +++ b/typefactory/convertlatex.cpp @@ -0,0 +1,88 @@ +#include "convertlatex.h" + +#include +#include +#include +#include + +static std::string latex_escape (const std::string &s) { + std::string out; + out.reserve (s.size() + s.size() / 8); + for (unsigned char ch : s) { + switch (ch) { + case '\\': + out += "\\textbackslash{}"; + break; + case '{': + out += "\\{"; + break; + case '}': + out += "\\}"; + break; + case '%': + out += "\\%"; + break; + case '$': + out += "\\$"; + break; + case '#': + out += "\\#"; + break; + case '&': + out += "\\&"; + break; + case '_': + out += "\\_"; + break; + case '^': + out += "\\textasciicircum{}"; + break; + case '~': + out += "\\textasciitilde{}"; + break; + default: + out.push_back (static_cast (ch)); + break; + } + } + return out; +} + +bool ConvertLatex::save (const std::string &output_file_name) { + if (!parse()) + return false; + + std::ofstream output (output_file_name, std::ios::trunc); + if (!output.is_open()) { + std::cerr << "Failed to open output file: " + << output_file_name + << " (" << std::strerror (errno) << ")\n"; + set_error_code (3); + return false; + } + + const auto &book_titles = titles(); + for (std::size_t i = 0; i < book_titles.size(); ++i) { + const auto &title = book_titles[i]; + output << "\\section {" << latex_escape (title) << "}\n"; + + for (std::size_t quote_index = 0; + quote_index < quote_count (title); + ++quote_index) { + output << " \\subsection {" + << latex_escape (quote_meta (title, quote_index)) + << "}\n"; + + for (const auto &text_line : quote_text_lines (title, quote_index)) + output << " " << latex_escape (text_line) << "\n"; + + output << " \\subsubsection{notes}\n\n"; + } + + if (i + 1 < book_titles.size()) + output << "\n"; + } + + set_error_code (0); + return true; +} diff --git a/typefactory/convertlatex.h b/typefactory/convertlatex.h new file mode 100644 index 0000000..0086da7 --- /dev/null +++ b/typefactory/convertlatex.h @@ -0,0 +1,13 @@ +#ifndef CONVERTLATEX_H +#define CONVERTLATEX_H + +#include "converter.h" + +class ConvertLatex final : public Converter { +public: + using Converter::Converter; + + bool save (const std::string &output_file_name) override; +}; + +#endif // CONVERTLATEX_H diff --git a/typefactory/main.cpp b/typefactory/main.cpp index 8b15481..aed6dc0 100644 --- a/typefactory/main.cpp +++ b/typefactory/main.cpp @@ -2,7 +2,7 @@ #include #include -#include "converter.h" +#include "convertlatex.h" struct CliArgs { std::string input; @@ -53,7 +53,7 @@ int main (int argc, char **argv) { if (!parse_args (argc, argv, args)) return 1; - Converter converter (args.input); + ConvertLatex converter (args.input); if (!converter.save (args.output)) return converter.error_code(); diff --git a/typefactory/typefactory.pro b/typefactory/typefactory.pro index 50e73a2..bf78f6c 100644 --- a/typefactory/typefactory.pro +++ b/typefactory/typefactory.pro @@ -4,9 +4,11 @@ CONFIG -= app_bundle CONFIG -= qt SOURCES += \ + convertlatex.cpp \ converter.cpp \ main.cpp HEADERS += \ + convertlatex.h \ converter.h \ typefactory.h