#include "transcode.h" #include 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 string& s) { return s != "0"; } void uniq(vector & vec) { sort(vec.begin(), vec.end()); vec.erase(unique(vec.begin(), vec.end()), vec.end()); } inline string& trim(string& s, const char *t = " \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& config_options) { ifstream config(configfile); if (!config.is_open()) return; string line; while(getline(config, line)) { istringstream config_line(line); string key; if(getline(config_line, key, '=')) { trim(key); string value; if(getline(config_line, value)) { trim(value); config_options.insert({key, value}); } } } config.close(); } void write_config_file(const char* configfile, map& config_options) { ofstream config(configfile); for (auto const& config_option: config_options) { string key = config_option.first; string val = config_option.second; config << key << " = " << val << 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& font_chars, vector& 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, const wstring& targ_font, const vector& 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& doc_fonts, vector& 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, const wstring& targ_font, const vector& doc_fonts, const vector& doc_encs, map>& char_map, vector& 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 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 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); }