/* * Copyright (C) Ascensio System SIA, 2009-2026 * * This program is a free software product. You can redistribute it and/or * modify it under the terms of the GNU Affero General Public License (AGPL) * version 3 as published by the Free Software Foundation, together with the * additional terms provided in the LICENSE file. * * This program is distributed WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For * details, see the GNU AGPL at: https://www.gnu.org/licenses/agpl-3.0.html * * You can contact Ascensio System SIA by email at info@onlyoffice.com * or by postal mail at 20A-6 Ernesta Birznieka-Upisha Street, Riga, * LV-1050, Latvia, European Union. * * The interactive user interfaces in modified versions of the Program * are required to display Appropriate Legal Notices in accordance with * Section 5 of the GNU AGPL version 3. * * No trademark rights are granted under this License. * * All non-code elements of the Product, including illustrations, * icon sets, and technical writing content, are licensed under the * Creative Commons Attribution-ShareAlike 4.0 International License: * https://creativecommons.org/licenses/by-sa/4.0/legalcode * * This license applies only to such non-code elements and does not * modify or replace the licensing terms applicable to the Program's * source code, which remains licensed under the GNU Affero General * Public License v3. * * SPDX-License-Identifier: AGPL-3.0-only */ #include "formulasconvert.h" #include #include #include"../../OOXML/Base/Unit.h" #include "../Reader/Converter/xlsxconversioncontext.h" #include "../Reader/Converter/xlsx_utils.h" namespace cpdoccore { namespace formulasconvert { class odf2oox_converter::Impl { public: Impl() {} static std::unordered_map & mapExternalLink_; std::wstring convert(const std::wstring& expr); std::wstring convert_chart_distance(const std::wstring& expr); std::wstring convert_list_values(const std::wstring& expr); void split_distance_by(const std::wstring& expr, const std::wstring& by, std::vector& out); std::wstring replace_cells_range(std::wstring expr, bool withTableName, bool bAbsoluteAlways = false); bool check_formula(std::wstring& expr); void replace_semicolons(std::wstring& expr, bool del_quotes = false); void replace_tilda(std::wstring& expr); void replace_vertical(std::wstring& expr); void replace_space(std::wstring& expr); void replace_apersand(std::wstring& expr); std::wstring convert_named_ref(const std::wstring& expr, bool withTableName, std::wstring separator, bool bAbsoluteAlways); std::wstring convert_named_expr(const std::wstring& expr, bool withTableName, bool bAbsoluteAlways); static std::wstring replace_named_ref_formater(boost::wsmatch const & what); static std::wstring replace_named_ref_formater_2(boost::wsmatch const & what); //static std::wstring replace_cell_range_formater(boost::wsmatch const & what); void replace_named_formula(std::wstring & expr, bool w = true); void replace_named_ref(std::wstring & expr, bool withTable = true, bool bAbsoluteAlways = false); bool find_first_ref(std::wstring const & expr, std::wstring & table, std::wstring & ref); bool find_first_last_ref(std::wstring const & expr, std::wstring & table, std::wstring & ref_first,std::wstring & ref_last); static bool convert_with_absolute; static bool convert_with_TableName; static std::wstring table_name_; static std::vector> mapReplacements; //------------------------------------------------------------------------------------------------------------- static std::wstring replace_apersand_formater(boost::wsmatch const & what) { if (what[1].matched) return L"&"; else if (what[2].matched) return what[2].str(); else if (what[3].matched) return what[3].str(); else return L""; } static std::wstring replace_semicolons_formater_del(boost::wsmatch const & what) { if (what[1].matched) return L","; else if (what[2].matched) { std::wstring res = what[2].str(); if (res.length() > 1 && res[0] == L'\"') res = res.substr(1, res.length() - 2); return res; } else return L""; } static std::wstring replace_semicolons_formater(boost::wsmatch const & what) { if (what[1].matched) return L","; else if (what[2].matched) return what[2].str(); else if (what[3].matched) return what[3].str(); else return L""; } static std::wstring replace_tilda_formater(boost::wsmatch const & what) { if (what[1].matched) return L";"; else if (what[2].matched) return what[2].str(); else if (what[3].matched) return what[3].str(); else return L""; } static std::wstring replace_vertical_formater(boost::wsmatch const & what) { if (what[1].matched) { std::wstring inner = what[1].str(); XmlUtils::replace_all( inner, L"|", L";"); return L"{" + inner + L"}"; } else if (what[2].matched) return what[2].str(); else if (what[3].matched) return what[3].str(); return L""; } static void replace_tmp_back(std::wstring &expr) { for (auto key : mapReplacements.back()) { XmlUtils::replace_all(expr, key.first, key.second); } } static void replace_tmp(std::wstring &expr) { //std::random_device genSource; //std::uniform_int_distribution<> generator(0, 23); //for (int index = 0; index < 5; index++) //{ // key += wchar_t(L'a' + generator(genSource)); //} std::wstring key = L"aaaaaaaaaaaaaaaaaaaaaaaa"; for (unsigned i = 0; i < 23; ++i) { unsigned j = rand() % (i + 1); key[i] = key[j]; key[j] = wchar_t(L'a' + i); } mapReplacements.back().insert(std::make_pair(key, expr)); expr = key; } static std::wstring convert_scobci(boost::wsmatch const & what) { if (what[1].matched) { std::wstring inner = what[1].str(); replace_tmp(inner); return inner; } else if (what[2].matched) { std::wstring inner = what[2].str(); replace_tmp(inner); return inner; } else if (what[3].matched) return what[3].str(); return L""; } static std::wstring replace_space_formater(boost::wsmatch const & what) { if (what[1].matched) { std::wstring inner = what[1].str(); XmlUtils::replace_all( inner, L" ", L","); return inner; } else if (what[2].matched) return what[2].str(); else if (what[3].matched) return what[3].str(); return L""; } static std::wstring is_forbidden(const std::wstring & formula) { std::wstring result = formula; std::map forbidden_formulas; forbidden_formulas.insert(std::make_pair(L"FORMULA", L"_xlfn.FORMULATEXT")); for (std::map::iterator it = forbidden_formulas.begin(); it != forbidden_formulas.end(); ++it) { if (boost::algorithm::contains(formula, it->first)) { XmlUtils::replace_all(result, it->first, it->second); } } return result; } }; bool odf2oox_converter::Impl::convert_with_TableName = true; std::wstring odf2oox_converter::Impl::table_name_ = L""; bool odf2oox_converter::Impl::convert_with_absolute = false; std::vector> odf2oox_converter::Impl::mapReplacements; std::unordered_map &odf2oox_converter::Impl::mapExternalLink_ = oox::xlsx_conversion_context::mapExternalLink_; bool odf2oox_converter::Impl::find_first_last_ref(std::wstring const & expr, std::wstring & table,std::wstring & ref_first,std::wstring & ref_last) { std::wstring workstr = expr; mapReplacements.emplace_back(); workstr = boost::regex_replace( workstr, boost::wregex(L"('.*?')|(\".*?\")"), &convert_scobci, boost::match_default | boost::format_all); std::vector< std::wstring > splitted; boost::algorithm::split(splitted, workstr, boost::algorithm::is_any_of(L".:"), boost::algorithm::token_compress_on); bool res = false; if (splitted.size() == 2) { table = splitted[0]; ref_first = splitted[1]; ref_last = splitted[1]; res = true; } if (splitted.size() == 3) { table = splitted[0]; ref_first = splitted[1]; ref_last = splitted[2]; res = true; } if (splitted.size() == 4) { table = splitted[0]; ref_first = splitted[1]; ref_last = splitted[3]; res = true; } replace_tmp_back( table ); mapReplacements.pop_back(); return res; } bool odf2oox_converter::Impl::find_first_ref(std::wstring const & expr, std::wstring & table, std::wstring & ref) { boost::wregex re(L"\\[(?:\\$)?([^\\.]+?){0,1}\\.([\\w^0-9\\$]+\\d+)(?::\\.([\\w^0-9]+\\d+)){0,1}\\]"); boost::wsmatch result; bool b = boost::regex_search(expr, result, re); size_t sz = result.size(); if (sz == 4 && !result[1].matched) { table = L""; ref = result[2].str(); return true; } else if (sz == 4 && result[1].matched) { table = result[1].str(); XmlUtils::replace_all( table, L"$", L""); XmlUtils::replace_all( table, L"'", L""); ref = result[2].str(); return true; } return false; } //std::wstring odf2oox_converter::Impl::replace_cell_range_formater(boost::wsmatch const & what) //{ // const size_t sz = what.size(); // if (sz == 4 && !what[1].matched) // { // const std::wstring c1 = what[2].str(); // const std::wstring c2 = what[3].str(); // const std::wstring s = c1 + (c2.empty() ? L"" : (L":" + c2) ); // return s; // } // else if (sz == 4 && what[1].matched) // { // std::wstring sheet1 = what[1].str(); // XmlUtils::replace_all( sheet1, L"$", L""); // const std::wstring c1 = what[2].str(); // std::wstring c2 = what[3].str(); // if (c2.empty()) c2 = what[4].str(); // const std::wstring s = sheet1 + L"!" + c1 + (c2.empty() ? L"" : (L":" + c2) ); // return s; // } // return L""; //} std::wstring odf2oox_converter::Impl::replace_named_ref_formater(boost::wsmatch const & what) { const size_t sz = what.size(); if (sz < 6) return what[0].str(); std::wstring c0 = what[0].str(); std::wstring external = sz == 7 ? what[1].matched ? what[1].str() : what[2].str() : what[1].str(); std::wstring sheet1 = sz == 7 ? what[3].str() : what[2].str(); std::wstring ref1 = sz == 7 ? what[4].str() : what[3].str(); std::wstring sheet2 = sz == 7 ? what[5].str() : what[4].str(); std::wstring ref2 = sz == 7 ? what[6].str() : what[5].str(); XmlUtils::replace_all( sheet1, L"$", L""); std::wstring result; if (false == external.empty()) { replace_tmp_back(external); int id = -1;//add_external_link(external); if (external[0] == L'\'') external = external.substr(1, external.size() - 2); std::unordered_map::iterator pFind = mapExternalLink_.find(external); if ( pFind == mapExternalLink_.end()) { id = (int)mapExternalLink_.size() + 1; mapExternalLink_.insert(std::make_pair(external, id)); } else { id = pFind->second; } if (sheet1[0] == L'\'') { sheet1 = sheet1.substr(1, sheet1.length() - 2); } replace_tmp_back(sheet1); if (sheet1[0] == L'\'') sheet1 = sheet1.substr(1, sheet1.size() - 2); sheet1 = L"'[" + std::to_wstring(id) + L"]" + sheet1 + L"'"; } else if (std::wstring::npos != sheet1.find(L" ")) { if (sheet1[0] != L'\'') { sheet1 = L"'" + sheet1 + L"'"; } } table_name_ = sheet1; if (convert_with_absolute) { size_t col = 0, row = 0; oox::getCellAddressInv(ref1, col, row); ref1 = oox::getCellAddress(col, row, true); if (false == ref2.empty()) { oox::getCellAddressInv(ref2, col, row); ref2 = oox::getCellAddress(col, row, true); } } if (convert_with_TableName) { return (sheet1.empty() ? L"" : (sheet1 + L"!")) + ref1 + (ref2.empty() ? L"" : (L":" + ref2) ); } else { return ref1 + (ref2.empty() ? L"" : (L":" + ref2) ); } } std::wstring odf2oox_converter::Impl::replace_named_ref_formater_2(boost::wsmatch const & what) { const size_t sz = what.size(); //if (sz < 6) return what[0].str(); std::wstring c0 = what[0].str(); std::wstring sheet1 = what[1].str(); std::wstring ref1 = what[2].str(); std::wstring ref2 = what[3].str(); XmlUtils::replace_all(sheet1, L"$", L""); XmlUtils::replace_all(ref2, L":", L""); std::wstring result; if (std::wstring::npos != sheet1.find(L" ")) { if (sheet1[0] != L'\'') { sheet1 = L"'" + sheet1 + L"'"; } } table_name_ = sheet1; if (convert_with_absolute) { size_t col = 0, row = 0; oox::getCellAddressInv(ref1, col, row); ref1 = oox::getCellAddress(col, row, true); if (false == ref2.empty()) { oox::getCellAddressInv(ref2, col, row); ref2 = oox::getCellAddress(col, row, true); } } if (convert_with_TableName) { return (sheet1.empty() ? L"" : (sheet1 + L"!")) + ref1 + (ref2.empty() ? L"" : (L":" + ref2)); } else { return ref1 + (ref2.empty() ? L"" : (L":" + ref2)); } } // replace cell addressing format // [.A1] -> A1 // [.A1:.B5] -> A1:B5 // [Sheet2.A1:B5] -> Sheet2!A1:B5 // [Sheet2.A1] -> Sheet2!A1 // [$'Sheet2 A'.$B2] -> 'Sheet2 A'!$B2 std::wstring odf2oox_converter::Impl::replace_cells_range(std::wstring expr, bool withTableName, bool bAbsoluteAlways) { if (expr.empty()) return L""; XmlUtils::replace_all( expr, L"#REF !", L"#REF!"); XmlUtils::replace_all( expr, L"#REF!#REF!", L"#REF!"); XmlUtils::replace_all( expr, L"$#REF!$#REF!", L"#REF!"); convert_with_TableName = withTableName; convert_with_absolute = bAbsoluteAlways; //boost::wregex complexRef(L"\\[(?:\'([^\']*)\'#){0,1}\\[{0,1}(?:\\$){0,1}([^\\.]+?){0,1}\\.(\\${0,1}[\\w^0-9]*\\${0,1}\\d*)(?::(\\${0,1}[^\\.]+?){0,1}\\.(\\${0,1}[\\w^0-9]*\\${0,1}\\d*)){0,1}\\]{0,1}"); boost::wregex complexRef(L"(?:(?:(?:(?:\\[(.*)#)|(?:(.*)#\\[)))|(?:\\[))\ (?:\\$){0,1}([^\\.]+?){0,1}\\.(\\${0,1}[\\w^0-9]*\\${0,1}\\d*)(?::(\\${0,1}[^\\.]+?){0,1}\\.(\\${0,1}[\\w^0-9]*\\${0,1}\\d*)){0,1}\\]"); // [ external# [ $ Sheet2 . A1 : ( $ Sheet2)? . B5 ] std::wstring result = boost::regex_replace( expr, complexRef, &replace_named_ref_formater, boost::match_default | boost::format_all); if (result == expr) { //bad formula ??? boost::wregex complexRef_2(L"\\$([^\\.]+?)?\\.(\\$[a-zA-Z]*\\d*)(\\:\\$[a-zA-Z]*\\d*)?"); // $ Sheet2 . $ A1 : ( $ Sheet2)? . $ B5 result = boost::regex_replace( expr, complexRef_2, &replace_named_ref_formater_2, boost::match_default | boost::format_all); } return result; } void odf2oox_converter::Impl::replace_named_ref(std::wstring & expr, bool withTableName, bool bAbsoluteAlways) { if (expr.empty()) return; XmlUtils::replace_all( expr, L"#REF !", L"#REF!"); XmlUtils::replace_all( expr, L"#REF!#REF!", L"#REF!"); XmlUtils::replace_all( expr, L"$#REF!$#REF!", L"#REF!"); convert_with_TableName = withTableName; convert_with_absolute = bAbsoluteAlways; //boost::wregex complexRef(L"\\${0,1}([^\\.]+?){0,1}\\.(\\${0,1}[a-zA-Z]+\\${0,1}\\d+)(?::\\.(\\${0,1}[a-zA-Z]+\\${0,1}\\d+)){0,1}"); boost::wregex complexRef(L"\\[{0,1}(?:(.*)#){0,1}\\${0,1}([^\\.\\s]+?){0,1}\\.(\\${0,1}[\\w^0-9]*\\${0,1}\\d*)(?::\\${0,1}([^\\.\\s]+?){0,1}\\.(\\${0,1}[\\w^0-9]*\\${0,1}\\d*)){0,1}\\]{0,1}"); // external# $ Sheet2 . A1 : ( $ Sheet2)? . B5 const std::wstring res = boost::regex_replace( expr, complexRef, &replace_named_ref_formater, boost::match_default | boost::format_all); expr = res; } // recognize and replace formula format // of:=(Formula) -> (Formula) bool odf2oox_converter::Impl::check_formula(std::wstring& expr) { if (expr.empty()) return false; boost::match_results res; if (boost::regex_search(expr, res, boost::wregex(L"^(?:[\\w]+:)?=(.+)"), boost::match_default)) { expr = res[1].str(); while (expr.find(L"=") == 0) { expr.erase(expr.begin(), expr.begin() + 1); } return true; } else return false; } // replace semicolons in all occurrences except those in quotes --*and in curly braces*-- void odf2oox_converter::Impl::replace_semicolons(std::wstring& expr, bool del_quotes) { const std::wstring res = boost::regex_replace( expr, //boost::wregex(L"(;)|(?:\".*?\")|(?:'.*?')"), boost::wregex(L"(;)|(\".*?\")|('.*?')"), del_quotes ? &replace_semicolons_formater_del : &replace_semicolons_formater, boost::match_default | boost::format_all); expr = res; } void odf2oox_converter::Impl::replace_tilda(std::wstring& expr) { const std::wstring res = boost::regex_replace( expr, //boost::wregex(L"(;)|(?:\".*?\")|(?:'.*?')"), boost::wregex(L"(~)|(\".*?\")|('.*?')"), &replace_semicolons_formater, boost::match_default | boost::format_all); expr = res; } void odf2oox_converter::Impl::replace_apersand(std::wstring& expr) { const std::wstring res = boost::regex_replace( expr, //boost::wregex(L"(;)|(?:\".*?\")|(?:'.*?')"), boost::wregex(L"(&)|(\".*?\")|('.*?')"), &replace_apersand_formater, boost::match_default | boost::format_all); expr = res; } // replace vertical bar in all occurrences within curly braces, but not inside strings void odf2oox_converter::Impl::replace_vertical(std::wstring& expr) { const std::wstring res = boost::regex_replace( expr, boost::wregex(L"(?:(?:\\{)([^\\}]*?)(?:\\}))|(\".*?\")|('.*?')"), &replace_vertical_formater, boost::match_default | boost::format_all); expr = res; } // replace space with comma in all occurrences void odf2oox_converter::Impl::replace_space(std::wstring& expr) { const std::wstring res = boost::regex_replace( expr, boost::wregex(L"(?:(?:\\{)([^\\}]*?)(?:\\}))|(\".*?\")|('.*?')"), &replace_space_formater, boost::match_default | boost::format_all); expr = res; } std::wstring odf2oox_converter::Impl::convert(const std::wstring& expr) { std::wstring workstr = is_forbidden(expr); bool isFormula = check_formula(workstr); // escaping mapReplacements.emplace_back(); workstr = boost::regex_replace(workstr, boost::wregex(L"('.*?')|(\".*?\")"), &convert_scobci, boost::match_default | boost::format_all); workstr = replace_cells_range (workstr, true); replace_semicolons (workstr); replace_tilda (workstr); replace_vertical (workstr); //replace_apersand (workstr); if (isFormula) { XmlUtils::replace_all( workstr, L"FDIST(", L"_xlfn.F.DIST("); // ROUNDUP( - in oox 2 parameters are needed - precision required - ,0) - EV Requirements v2.2.3.ods if (std::wstring::npos != workstr.find(L"CONCATINATE")) { bool l = true; // ranges may be partially specified //todooo } //todooo INDEX((A1:C6~A8:C11),2,2,2) - ???? - INDEX_emb.ods } else { size_t sz_workstr = workstr.length(); if (workstr.substr(0, (std::min)((size_t)3, sz_workstr)) == L"of:")//sample_02neu_crashes.ods workstr = workstr.substr(3); } //----------------------------------------------------------- replace_tmp_back(workstr); mapReplacements.pop_back(); return workstr; } void odf2oox_converter::Impl::split_distance_by(const std::wstring& expr, const std::wstring& by, std::vector& out) { mapReplacements.emplace_back(); std::wstring workstr = boost::regex_replace( expr, boost::wregex(L"('.*?')|(\".*?\")"), &convert_scobci, boost::match_default | boost::format_all); boost::algorithm::split(out, workstr, boost::algorithm::is_any_of(by), boost::algorithm::token_compress_on); for (size_t i = 0; i < out.size(); i++) { replace_tmp_back(out[i]); } mapReplacements.pop_back(); } //Sheet2.C3:Sheet2.C19 Sheet2.L29:Sheet2.L36 // to //Sheet2!C3:C19,Sheet2!L27:L34 std::wstring odf2oox_converter::Impl::convert_list_values(const std::wstring& expr) { std::wstring workstr = expr; replace_semicolons(workstr, true); return workstr; } std::wstring odf2oox_converter::Impl::convert_chart_distance(const std::wstring& expr) { mapReplacements.emplace_back(); std::wstring workstr = boost::regex_replace( is_forbidden(expr), boost::wregex(L"('.*?')|(\".*?\")"), &convert_scobci, boost::match_default | boost::format_all); std::vector distance_inp; std::vector distance_out; boost::algorithm::split(distance_inp, workstr, boost::algorithm::is_any_of(L" "), boost::algorithm::token_compress_on); for (size_t i = 0; i < distance_inp.size(); i++) { std::wstring sheet; std::vector range; std::vector cells; boost::algorithm::split(range, distance_inp[i], boost::algorithm::is_any_of(L":"), boost::algorithm::token_compress_on); for (size_t j = 0; j < range.size(); j++) { const std::string::size_type colon = range[j].find('.'); if (colon != std::wstring::npos) { cells.push_back(range[j].substr(colon + 1)); if (sheet.empty()) { sheet = range[j].substr(0, colon); } } else cells.push_back(range[j]); } std::wstring cells_out; for (size_t j = 0; j < cells.size(); j++) { cells_out.append(cells[j]); cells_out.append(L":"); } if (sheet.find(L"-") != std::wstring::npos && sheet.find(L"'") == std::wstring::npos && sheet.find(L"\"") == std::wstring::npos) { sheet = L"'" + sheet + L"'"; } distance_out.push_back((sheet.empty() ? L"" : (sheet + L"!")) + cells_out.substr(0, cells_out.size() - 1)); } std::wstring result; for (size_t i = 0; i < distance_out.size(); i++) { result.append(distance_out[i]); result.append(L","); } replace_tmp_back( result ); mapReplacements.pop_back(); return result.substr(0, result.size() - 1);// minus the last extra comma } std::wstring odf2oox_converter::Impl::convert_named_ref(const std::wstring& expr, bool withTableName, std::wstring separator, bool bAbsoluteAlways) { boost::wregex complexRef(L"('(?!\\s\\'){0,1}.*?')");// find what's in apostrophes and replace there std::wstring workstr = expr; mapReplacements.emplace_back(); workstr = boost::regex_replace( workstr, boost::wregex(L"('.*?')|(\".*?\")"), &convert_scobci, boost::match_default | boost::format_all); replace_named_ref(workstr, withTableName, bAbsoluteAlways); if (separator != L" ") { XmlUtils::replace_all( workstr, L" ", separator); } replace_tmp_back( workstr ); if (table_name_.empty() == false) { replace_tmp_back( table_name_ ); } mapReplacements.pop_back(); return workstr; } std::wstring odf2oox_converter::Impl::convert_named_expr(const std::wstring& expr, bool withTableName, bool bAbsoluteAlways) { std::wstring workstr = expr; bool isFormula = check_formula(workstr); if (isFormula) { workstr = convert(expr); } else { mapReplacements.emplace_back(); workstr = boost::regex_replace( workstr, boost::wregex(L"('.*?')|(\".*?\")"), &convert_scobci, boost::match_default | boost::format_all); workstr = replace_cells_range(workstr, withTableName, bAbsoluteAlways); replace_semicolons(workstr); replace_vertical(workstr); size_t res_find = 0; if ((res_find = workstr.find(L"CONCATINATE")) != std::wstring::npos) { // ranges may be partially specified //todooo } replace_tmp_back(workstr); if (table_name_.empty() == false) { replace_tmp_back(table_name_); } mapReplacements.pop_back(); } return workstr; } //------------------------------------------------------------------------------------------------------------ odf2oox_converter::odf2oox_converter() : impl_(new odf2oox_converter::Impl()) { } odf2oox_converter::~odf2oox_converter() { } std::wstring odf2oox_converter::get_table_name() { return impl_->table_name_; } std::wstring odf2oox_converter::convert_list_values(std::wstring const & expr) { return impl_->convert_list_values(expr); } std::wstring odf2oox_converter::convert(const std::wstring& expr) { return impl_->convert(expr); } std::wstring odf2oox_converter::convert_chart_distance(const std::wstring& expr) { return impl_->convert_chart_distance(expr); } void odf2oox_converter::split_distance_by(const std::wstring& expr, const std::wstring& by, std::vector& out) { return impl_->split_distance_by(expr, by, out); } std::wstring odf2oox_converter::convert_named_ref(const std::wstring& expr, bool withTableName, std::wstring separator, bool bAbsoluteAlways) { return impl_->convert_named_ref(expr, withTableName, separator, bAbsoluteAlways); } std::wstring odf2oox_converter::convert_named_expr(const std::wstring& expr, bool withTableName, bool bAbsoluteAlways) { return impl_->convert_named_expr(expr, withTableName, bAbsoluteAlways); } std::wstring odf2oox_converter::convert_ref(std::wstring const & expr) { std::wstring workstr = L"[" + expr + L"]"; return impl_->replace_cells_range(workstr, true); } std::wstring odf2oox_converter::convert_spacechar(std::wstring expr) { while(true) { size_t pos = expr.find(L"%20"); if (pos == std::wstring::npos)break; expr.replace(pos,3,L" "); } return expr; } bool odf2oox_converter::find_first_ref(std::wstring const & expr, std::wstring & table, std::wstring & ref) { return impl_->find_first_ref(expr, table, ref); } bool odf2oox_converter::find_first_last_ref(std::wstring const & expr, std::wstring & table, std::wstring & ref_first,std::wstring & ref_last) { return impl_->find_first_last_ref(expr, table, ref_first, ref_last); } } }