summaryrefslogtreecommitdiff
path: root/funcs.cpp
diff options
context:
space:
mode:
authorWalker Thompson <walker.thompson@urz.uni-heidelberg.de>2025-12-18 14:12:35 +0000
committerWalker Thompson <walker.thompson@urz.uni-heidelberg.de>2025-12-18 14:12:35 +0000
commita2d7ebcdc396fcaa505251dd1625df34fdd00771 (patch)
tree40b380b08963df35c2cbf37ccf7ccab1216552b1 /funcs.cpp
Initial commit
Diffstat (limited to 'funcs.cpp')
-rw-r--r--funcs.cpp257
1 files changed, 257 insertions, 0 deletions
diff --git a/funcs.cpp b/funcs.cpp
new file mode 100644
index 0000000..a8e1b25
--- /dev/null
+++ b/funcs.cpp
@@ -0,0 +1,257 @@
+#include "transcode.h"
+
+string random_anum_string(int len)
+{
+ string alphan("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
+
+ random_device rand;
+ mt19937 rgen(rand());
+
+ shuffle(alphan.begin(), alphan.end(), rgen);
+
+ return alphan.substr(0, len);
+}
+
+bool to_bool(const wstring& s)
+{
+ return s != "0";
+}
+
+void uniq(vector <wstring>& vec)
+{
+ sort(vec.begin(), vec.end());
+ vec.erase(unique(vec.begin(), vec.end()), vec.end());
+}
+
+inline wstring& wtrim(wstring& s, const wchar_t* t = L" \t\n\r\f\v")
+{
+ s.erase(s.find_last_not_of(t) + 1);
+ s.erase(0, s.find_first_not_of(t));
+ return s;
+}
+
+void read_config_file(const char* configfile,
+ map<wstring, wstring>& config_options)
+{
+ wifstream config(configfile);
+ if (!config.is_open()) return;
+
+ wstring line;
+ while(getline(config, line))
+ {
+ wistringstream config_line(line);
+ wstring key;
+ if(getline(config_line, key, L'=')) {
+ wtrim(key);
+ wstring value;
+ if(getline(config_line, value)) {
+ wtrim(value);
+ config_options.insert({key, value});
+ }
+ }
+ }
+
+ config.close();
+}
+
+void write_config_file(const char* configfile,
+ map<wstring, wstring>& config_options)
+{
+ wofstream config(configfile);
+
+ for (auto const& config_option: config_options) {
+ config << config_option.first << " = "
+ << config_option.second << endl;
+ }
+
+ config.close();
+}
+
+void replace_chars(wstring& str, const wstring& a, // from
+ const wstring& b) // to
+{
+ size_t pos = 0;
+ while ((pos = str.find(a, pos)) != wstring::npos) {
+ str.replace(pos, a.length(), b);
+ pos += b.length();
+ }
+}
+
+// Replace problematic ASCII characters such as
+// the 'Ohm' sign using our hexadecimal codes map
+void str_ascii_to_utf(wstring& str)
+{
+ wstring char_old, char_new;
+ for (auto const& char_pair: ascii_utf) {
+ char_old = char_pair.first;
+ char_new = char_pair.second;
+ size_t pos = 0;
+ while ((pos = str.find(char_old, pos)) != wstring::npos) {
+ str.replace(pos, 1, char_new);
+ pos++;
+ }
+ }
+}
+
+wstring convert_node_text(const wstring& text,
+ vector<wstring>& font_chars,
+ vector<wstring>& utf_chars,
+ bool do_greek)
+{
+ wstring str_out = text;
+
+ size_t char_max = utf_chars.size();
+ if (font_chars.size() < utf_chars.size())
+ char_max = font_chars.size();
+
+ for (size_t i = 0; i < char_max; i++) {
+ if (font_chars[i].length() > 0) {
+ wstringstream fchstream(font_chars[i]);
+ wstring font_char;
+
+ while(getline(fchstream, font_char, L' '))
+ replace_chars(str_out, font_char, utf_chars[i]);
+ }
+ }
+
+ if (do_greek && greek_precomp.size() <= greek_comb.size()) {
+ for (size_t i = 0; i < greek_comb.size(); i++) {
+ replace_chars(str_out, greek_comb[i], greek_precomp[i]);
+ }
+ }
+
+ return str_out;
+}
+
+void get_default_pfont(wstring& default_pfont, const char* styles_xmlfile)
+{
+ xml_document styles_xmldoc;
+ if (!styles_xmldoc.load_file(styles_xmlfile)) return;
+
+ xpath_node_set style_xns = styles_xmldoc.select_nodes(L"//w:style[@w:styleId='Normal' or @w:styleId='Standard']/w:rPr/w:rFonts");
+
+ for (auto style_xn: style_xns) {
+ if (default_pfont.length() > 0) break;
+ xml_node style_node = style_xn.node();
+ default_pfont = style_node.attribute(L"w:ascii").value();
+ }
+}
+
+
+void update_default_pfont(const char * styles_xmlfile, wstring& targ_font,
+ const vector<wstring>& doc_fonts)
+{
+ xml_document styles_xmldoc;
+ if (!styles_xmldoc.load_file(styles_xmlfile)) return;
+
+ for(const wstring& fstr: doc_fonts) {
+ // change the font attributes to the target
+ xpath_node_set xpf_ns = styles_xmldoc.select_nodes(L"//w:rFonts");
+ for (auto xpf_n: xpf_ns) {
+ xml_node font_node = xpf_n.node();
+ for (auto& attr: font_node.attributes())
+ if (attr.value() == fstr) attr.set_value(targ_font.c_str());
+ }
+ }
+
+ ofstream xmlos(styles_xmlfile);
+ styles_xmldoc.save(xmlos);
+}
+
+void detect_fonts(const char* xmlfile, vector<wstring>& doc_fonts,
+ vector<wstring>& doc_encs)
+{
+ xml_document xmldoc;
+ if (!xmldoc.load_file(xmlfile)) return;
+
+ xpath_node_set xpf_ns = xmldoc.select_nodes(L"//w:rFonts");
+
+ // Find which font tags and encodings are in the document
+ for (auto xpf_n: xpf_ns) {
+ xml_node font_node = xpf_n.node();
+
+ for (const auto& attr: font_node.attributes()) {
+ doc_encs.push_back(attr.name());
+ doc_fonts.push_back(attr.value());
+ }
+ }
+
+ // we only want the unique values
+ uniq(doc_encs);
+ uniq(doc_fonts);
+}
+
+void process_xml(const char * xmlfile, wstring& targ_font,
+ const vector<wstring>& doc_fonts, const vector<wstring>& doc_encs,
+ map<wstring, vector<wstring>>& char_map,
+ vector<wstring>& utf_chars, const wstring& default_font, bool do_greek)
+{
+ xml_document xmldoc;
+ if (!xmldoc.load_file(xmlfile)) return;
+
+ for(const wstring& fstr: doc_fonts) {
+ if (!char_map.count(fstr)) continue;
+ vector<wstring> font_chars = char_map.find(fstr)->second;
+
+ cout << "Doing conversion for font " << fstr << endl;
+
+ // we need to build the XPath query string out of the MS Word
+ // w:rFonts attributes identified, such as w:ascii, w:cs, etc.
+ // e.g. //w:p/w:r[w:rPr/w:rFonts[@w:ascii='Font' or @w:cs='Font']]/w:t
+ size_t enc_ct = doc_encs.size();
+ wstring xpt_str = L"//w:r[w:rPr/w:rFonts[@";
+ if (enc_ct > 1) {
+ for (size_t enc_it = 0; enc_it < enc_ct - 1; enc_it++)
+ xpt_str += doc_encs[enc_it] + L"='" + fstr + L"' or @";
+ }
+ xpt_str += doc_encs[enc_ct - 1] + L"='" + fstr + L"']]/w:t";
+
+ // Get the set of nodes matching XPath query
+ xpath_query xpt(xpt_str.c_str());
+ xpath_node_set xpt_ns = xpt.evaluate_node_set(xmldoc);
+
+ // Do the actual conversion per node
+ for (auto xpt_match: xpt_ns) {
+ xml_node xpt_n = xpt_match.node();
+ xml_text xpt_xmltxt = xpt_n.text();
+ wstring txt = xpt_xmltxt.get();
+ str_ascii_to_utf(txt);
+ wstring txt_new = convert_node_text(txt.c_str(),
+ font_chars, utf_chars, do_greek);
+ xpt_xmltxt.set(txt_new.c_str());
+ }
+
+ // now change the font attributes to the target
+ xpath_node_set xpf_ns = xmldoc.select_nodes(L"//w:rFonts");
+ for (auto xpf_n: xpf_ns) {
+ xml_node font_node = xpf_n.node();
+ for (auto& attr: font_node.attributes())
+ if (attr.value() == fstr) attr.set_value(targ_font.c_str());
+ }
+ }
+
+ // this handles text segments without a set font
+ // i.e. in the default style determined in get_default_font
+ if (char_map.count(default_font)) {
+ xpath_node_set xpt_dfont_ns = xmldoc.select_nodes(L"//w:r[w:rPr and not(w:rPr/w:rFonts[@w:ascii or @w:hAnsi or @w:cs])]/w:t");
+ vector<wstring> default_font_chars = char_map.find(default_font)->second;
+
+ cout << "Converting default paragraph font "
+ << default_font << endl;
+
+ for (auto xpt_dfont_match: xpt_dfont_ns) {
+ xml_node xpt_dfont_n = xpt_dfont_match.node();
+ xml_text xpt_dfont_xmltxt = xpt_dfont_n.text();
+
+ wstring txt = xpt_dfont_xmltxt.get();
+ str_ascii_to_utf(txt);
+ wstring txt_new = convert_node_text(txt.c_str(),
+ default_font_chars, utf_chars, do_greek);
+
+ xpt_dfont_xmltxt.set(txt_new.c_str());
+ }
+ }
+
+ ofstream xmlos(xmlfile);
+ xmldoc.save(xmlos);
+}