From 80720108347665f6051795f3fbb7d8d0f262513a Mon Sep 17 00:00:00 2001 From: Ji???­ Posp?­??il Date: Wed, 14 Dec 2016 14:46:02 +0100 Subject: SVG generation fixes - text wrapping - text decoration - line connectors - path filling - unordered item lists --- build/win32/libvisio.vcxproj | 26 +- src/conv/svg/vsd2xhtml.cpp | 276 +++++++++--- src/lib/VSDContentCollector.cpp | 494 ++++++++++++-------- src/lib/VSDContentCollector.h | 7 +- src/lib/VSDMetaData.cpp | 2 +- src/lib/VSDOutputElementList.cpp | 282 +----------- src/lib/VSDOutputElementList.h | 317 ++++++++++++- src/lib/preprocess/SvgDC.cpp | 1 + src/lib/preprocess/SvgDC.h | 32 ++ src/lib/preprocess/SvgFont.cpp | 61 +++ src/lib/preprocess/SvgFont.h | 38 ++ src/lib/preprocess/SvgParagraph.cpp | 152 +++++++ src/lib/preprocess/SvgParagraph.h | 69 +++ src/lib/preprocess/SvgSpan.cpp | 74 +++ src/lib/preprocess/SvgSpan.h | 51 +++ src/lib/preprocess/SvgTextObject.cpp | 473 ++++++++++++++++++++ src/lib/preprocess/SvgTextObject.h | 109 +++++ src/lib/preprocess/SvgUtils.cpp | 61 +++ src/lib/preprocess/SvgUtils.h | 28 ++ src/lib/preprocess/VsdElementListPreprocessor.cpp | 521 ++++++++++++++++++++++ src/lib/preprocess/VsdElementListPreprocessor.h | 71 +++ src/lib/preprocess/windows/WindowsSvgDC.cpp | 188 ++++++++ src/lib/preprocess/windows/WindowsSvgDC.h | 61 +++ src/lib/preprocess/windows/WindowsSvgFont.cpp | 54 +++ src/lib/preprocess/windows/WindowsSvgFont.h | 30 ++ 25 files changed, 2964 insertions(+), 514 deletions(-) create mode 100644 src/lib/preprocess/SvgDC.cpp create mode 100644 src/lib/preprocess/SvgDC.h create mode 100644 src/lib/preprocess/SvgFont.cpp create mode 100644 src/lib/preprocess/SvgFont.h create mode 100644 src/lib/preprocess/SvgParagraph.cpp create mode 100644 src/lib/preprocess/SvgParagraph.h create mode 100644 src/lib/preprocess/SvgSpan.cpp create mode 100644 src/lib/preprocess/SvgSpan.h create mode 100644 src/lib/preprocess/SvgTextObject.cpp create mode 100644 src/lib/preprocess/SvgTextObject.h create mode 100644 src/lib/preprocess/SvgUtils.cpp create mode 100644 src/lib/preprocess/SvgUtils.h create mode 100644 src/lib/preprocess/VsdElementListPreprocessor.cpp create mode 100644 src/lib/preprocess/VsdElementListPreprocessor.h create mode 100644 src/lib/preprocess/windows/WindowsSvgDC.cpp create mode 100644 src/lib/preprocess/windows/WindowsSvgDC.h create mode 100644 src/lib/preprocess/windows/WindowsSvgFont.cpp create mode 100644 src/lib/preprocess/windows/WindowsSvgFont.h diff --git a/build/win32/libvisio.vcxproj b/build/win32/libvisio.vcxproj index c8cad9f..4a96a9f 100644 --- a/build/win32/libvisio.vcxproj +++ b/build/win32/libvisio.vcxproj @@ -111,6 +111,16 @@ %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) + + + + + + + + + + %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) @@ -165,6 +175,8 @@ %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) + + %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) @@ -249,6 +261,16 @@ + + + + + + + + + + @@ -261,6 +283,8 @@ + + @@ -290,4 +314,4 @@ - + \ No newline at end of file diff --git a/src/conv/svg/vsd2xhtml.cpp b/src/conv/svg/vsd2xhtml.cpp index cc91c85..dc1b7cc 100644 --- a/src/conv/svg/vsd2xhtml.cpp +++ b/src/conv/svg/vsd2xhtml.cpp @@ -1,4 +1,3 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * This file is part of the libvisio project. * @@ -11,108 +10,275 @@ #include "config.h" #endif +#include #include #include -#include -#include +#include +#include + +#include #include #include #include #include +using namespace librevenge; +using namespace libvisio; +using namespace std; + + #ifndef VERSION #define VERSION "UNKNOWN VERSION" #endif + namespace { +string UnquoteString(const string &str) +{ + unsigned int strSize = str.size(); + unsigned int pos1 = strSize > 0 && str[0] == '"' ? 1 : 0; + unsigned int pos2 = strSize > 1 && str[strSize - 1] == '"' ? strSize - 1 : strSize; + return str.substr(pos1, pos2 - pos1); +} + +bool ExpandFileNameList(const string &listFileName, vector &fileNames) +{ + if (listFileName.find(".lst") == listFileName.size() - 4) + { + ifstream in(listFileName); + + if (in.is_open()) + { + while (in && !in.eof()) + { + string line; + getline(in, line); + + if (in) + { + fileNames.push_back(UnquoteString(line)); + } + } + + return true; + } + } + + return false; +} + +void WriteXhtmlHeader(ostream &out) +{ + out << "" << endl; + out << "" << endl; + out << "" << endl; + out << "" << endl; + out << "" << endl; +} + +void WriteXhtmlFooter(ostream &out) +{ + out << "" << endl; + out << "" << endl; +} + +void WriteSvg(const char *svgStr, ostream &out) +{ + out << "" << endl; + out << svgStr << endl; +} + int printUsage() { - printf("`vsd2xhtml' converts Microsoft Visio documents to SVG.\n"); - printf("\n"); - printf("Usage: vsd2xhtml [OPTION] INPUT\n"); - printf("\n"); - printf("Options:\n"); - printf("\t--help show this help message\n"); - printf("\t--version show version information\n"); - printf("\n"); - printf("Report bugs to .\n"); + cout << "'vsd2xhtml' converts Microsoft Visio documents to SVG." << endl; + cout << endl; + cout << "Usage: vsd2xhtml [OPTION] INPUT {INPUT}" << endl; + cout << endl; + cout << "OPTIONS:" << endl; + cout << " --help show this help message" << endl; + cout << " --version show version information" << endl; + cout << " --onedoc merge all visio pages into one output document" << endl; + cout << endl; + cout << "INPUT: VSD file path or a path to a text file containing list" << endl; + cout << " of VSD file paths, each on a separate line" << endl; + cout << endl; + cout << "Report bugs to ." << endl; return -1; } int printVersion() { - printf("vsd2xhtml " VERSION "\n"); + cout << "vsd2xhtml " << VERSION << endl; return 0; } } // anonymous namespace +/** + * Carries out conversion of specified VSD files in SVG. + * + * @param argc + * number of supplied arguments + * + * @param argv + * an array of arguments: + * + * --version will return the version of this tool + * + * --onedoc all pages of a particular VSD document will be included in a single SVG file, + * otherwise a SVG file is generated for each page, while appending the page + * number to its name + * + * [fileName]+ a name of the VSD file to convert or a name of a file with LST extension that + * contains a list of paths to VSD files to process, each on a separate line. + * Generated output documents will have the same name as the respective input but + * SVG extension, plus the name will be appended by the page number if --onedoc + * option was not specified + * + * @return 0 if conversion (all conversions) was successful + * + * 1 if some (all conversion) failed for any reason, such as because of not recognized + * format of the input file or parsing failure due to invalid contents + * + * -1 if invalid command line arguments were supplied + */ int main(int argc, char *argv[]) { if (argc < 2) + { return printUsage(); + } - char *file = 0; + vector fileNames; + bool oneDoc = false; for (int i = 1; i < argc; i++) { - if (!strcmp(argv[i], "--version")) + if (strcmp(argv[i], "--version") == 0) + { return printVersion(); - else if (!file && strncmp(argv[i], "--", 2)) - file = argv[i]; + } + else if (strcmp(argv[i], "--onedoc") == 0) + { + oneDoc = true; + } + else if (strncmp(argv[i], "--", 2) != 0) + { + string fileName = UnquoteString(argv[i]); + + if (!ExpandFileNameList(fileName, fileNames)) + { + fileNames.push_back(fileName); + } + } else + { return printUsage(); + } } - if (!file) + if (fileNames.size() == 0) + { return printUsage(); + } - librevenge::RVNGFileStream input(file); + bool wasError = false; - if (!libvisio::VisioDocument::isSupported(&input)) + for (unsigned int i = 0; i < fileNames.size(); i++) { - std::cerr << "ERROR: Unsupported file format (unsupported version) or file is encrypted!" << std::endl; - return 1; - } + cout << "#" << (i + 1) << "/" << fileNames.size() << endl; + cout << "Parsing \"" << fileNames[i] << "\"..."; - librevenge::RVNGStringVector output; - librevenge::RVNGSVGDrawingGenerator generator(output, "svg"); - if (!libvisio::VisioDocument::parse(&input, &generator)) - { - std::cerr << "ERROR: SVG Generation failed!" << std::endl; - return 1; - } - if (output.empty()) - { - std::cerr << "ERROR: No SVG document generated!" << std::endl; - return 1; - } + { + ifstream in(fileNames[i].c_str()); - std::cout << "" << std::endl; - std::cout << "" << std::endl; - std::cout << "" << std::endl; - std::cout << "" << std::endl; - std::cout << "" << std::endl; + if (!in.is_open()) + { + cerr << "ERROR: file not found" << endl; + wasError = true; + continue; + } + } - for (unsigned k = 0; k0) - std::cout << "
\n"; + RVNGFileStream input(fileNames[i].c_str()); - std::cout << "\n"; + if (!VisioDocument::isSupported(&input)) + { + cerr << "ERROR: Unsupported file format (unsupported version) or file is encrypted!" << endl; + wasError = true; + continue; + } - std::cout << output[k].cstr() << std::endl; - } + RVNGStringVector output; + RVNGSVGDrawingGenerator generator(output, ""); // do not use SVG namespace - std::cout << "" << std::endl; - std::cout << "" << std::endl; + if (!VisioDocument::parse(&input, &generator)) + { + cerr << "ERROR: SVG Generation failed!" << endl; + wasError = true; + continue; + } - return 0; + if (output.empty()) + { + cerr << "ERROR: No SVG document generated!" << endl; + wasError = true; + continue; + } + + cout << "done" << endl; + + string fileName = fileNames[i]; + boost::algorithm::to_lower(fileName); + + fileName = fileName.find(".vsd") == fileName.size() - 4 + ? fileNames[i].substr(0, fileName.size() - 4) : fileNames[i]; + + if (oneDoc) // render all pages to one file + { + fileName += ".svg"; + ofstream out(fileName.c_str()); + + cout << "Writing \"" << fileName.c_str() << "\"..."; + WriteXhtmlHeader(out); + + for (unsigned k = 0; k < output.size(); k++) + { + if (k > 0) + { + out << "
" << endl; + } + + WriteSvg(output[k].cstr(), out); + } + + WriteXhtmlFooter(out); + cout << "done" << endl; + } + else // render each page to a separate file + { + for (unsigned k = 0; k < output.size(); k++) + { + ostringstream oss; + oss << fileName << "-" << (k + 1) << ".svg"; + ofstream out(oss.str().c_str()); + + cout << "Writing \"" << oss.str().c_str() << "\"..."; + WriteXhtmlHeader(out); + WriteSvg(output[k].cstr(), out); + WriteXhtmlFooter(out); + cout << "done" << endl; + } + } + + cout << endl; + } + + return wasError ? 1 : 0; } -/* vim:set shiftwidth=2 softtabstop=2 expandtab: */ diff --git a/src/lib/VSDContentCollector.cpp b/src/lib/VSDContentCollector.cpp index eb9a366..c2b9784 100644 --- a/src/lib/VSDContentCollector.cpp +++ b/src/lib/VSDContentCollector.cpp @@ -7,10 +7,12 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include #include // for memcpy #include #include +#include +#include + #include #include #include @@ -18,6 +20,12 @@ #include "VSDContentCollector.h" #include "VSDParser.h" #include "VSDInternalStream.h" +#include "librevenge/SvgConstants.h" + +using namespace librevenge; +using namespace std; +using namespace svgconstants; + #ifndef DUMP_BITMAP #define DUMP_BITMAP 0 @@ -25,13 +33,14 @@ #if DUMP_BITMAP static unsigned bitmapId = 0; -#include #endif #ifndef M_PI #define M_PI 3.14159265358979323846 #endif +#define SURROGATE_VALUE(h,l) (((h) - 0xd800) * 0x400 + (l) - 0xdc00 + 0x10000) + namespace { @@ -139,39 +148,90 @@ libvisio::VSDContentCollector::VSDContentCollector( { } -const char *libvisio::VSDContentCollector::_linePropertiesMarkerViewbox(unsigned marker) +const char *libvisio::VSDContentCollector::_linePropertiesMarkerPathTransform(unsigned marker, bool reverse) { switch (marker) { - case 1: - case 2: - case 9: - case 15: - return "0 0 20 10"; - case 8: - return "0 0 20 18"; - case 3: - case 4: - case 5: - case 6: - case 11: - case 16: - case 17: - case 18: - return "0 0 20 20"; - case 12: - case 13: - case 14: - return "0 0 20 30"; - case 22: - case 39: - return "0 0 20 40"; - case 21: - return "0 0 30 30"; - case 10: - return "0 0 1131 1131"; - default: - return "0 0 20 30"; + case 1 : + return reverse + ? "rotate(270, 0, 0) translate(-10, 4)" : "rotate(90, 0, 0) translate(-10, 4)"; + + case 3 : + return reverse + ? "rotate(270, 0, 0) translate(-10, 8)" : "rotate(90, 0, 0) translate(-10, 8)"; + + case 9 : + return "translate(0, 10)"; + + case 10 : + case 20 : + case 42 : + return ""; + + case 11 : + return "translate(-5, -5)"; + + case 12 : + return reverse + ? "rotate(270, 0, 0) translate(-10, 12)" : "rotate(90, 0, 0) translate(-10, 12)"; + + case 21 : + return "translate(-15, -15)"; + + default : + return reverse + ? "rotate(270, 0, 0) translate(-10, 0)" : "rotate(90, 0, 0) translate(-10, 0)"; + } +} + +const char *libvisio::VSDContentCollector::_linePropertiesMarkerViewbox(unsigned marker, bool reverse) +{ + switch (marker) + { + case 1 : + return reverse ? "0 -14 28 28" : "-18 -14 28 28"; + + case 2 : + case 15 : + return reverse ? "0 -10 10 20" : "-10 -10 10 20"; + + case 3 : + return reverse ? "0 -14 31 28" : "-31 -14 31 28"; + + case 4 : + case 5 : + case 16 : + case 17 : + case 22 : + case 39 : + return reverse ? "0 -10 20 20" : "-20 -10 20 20"; + + case 6 : + case 18 : + return reverse ? "0 -10 23 20" : "-23 -10 23 20"; + + case 8 : + return reverse ? "0 -10 18 20" : "-18 -10 18 20"; + + case 9 : + return "-2 -2 24 24"; + + case 11 : + return "-5 -5 10 10"; + + case 12 : + return reverse ? "0 -14 45 28" : "-45 -14 45 28"; + + case 10 : + case 20 : + case 42 : + return "-10 -10 20 20"; + + case 21 : + return "-15 -15 30 30"; + + default : + return reverse ? "0 -10 30 20" : "-30 -10 30 20"; } } @@ -195,8 +255,10 @@ const char *libvisio::VSDContentCollector::_linePropertiesMarkerPath(unsigned ma return "m10 0q-2.6,13.4 -10,18q10,-5 20,0q-7.4,-4.6 -10,-18"; case 9: return "m-2 -8l4 -4l20 20l-4 4z"; - case 10: // Copied from what LO exports when using the "circle" marker - return "m462 1118-102-29-102-51-93-72-72-93-51-102-29-102-13-105 13-102 29-106 51-102 72-89 93-72 102-50 102-34 106-9 101 9 106 34 98 50 93 72 72 89 51 102 29 106 13 102-13 105-29 102-51 102-72 93-93 72-98 51-106 29-101 13z"; + case 10: + case 20: + case 42: + return "M-10,0A10,10 0 0,0 10,0A10,10 0 0,0 -10,0"; case 11: return "m0 0v10h10v-10z"; case 12: @@ -219,6 +281,7 @@ const char *libvisio::VSDContentCollector::_linePropertiesMarkerPath(unsigned ma return "m10 0-10 20l10 20l10 -20z m0 8l-6 12l6 12l6 -12z"; case 39: return "m10 0-10 20h20z m0 20-10 20h20z"; + default: return "m10 0-10 30h20z"; } @@ -228,18 +291,24 @@ double libvisio::VSDContentCollector::_linePropertiesMarkerScale(unsigned marker { switch (marker) { - case 11: - case 10: - return 0.7; - case 14: - case 15: - case 16: - case 17: - case 18: - case 22: + case 11 : + case 14 : + case 15 : + case 16 : + case 17 : + case 18 : + case 22 : return 1.2; - default: - return 1.0; + + case 10 : + case 20 : + return 0.6; + + case 42 : + return 0.4; + + default : + return 0.75; } } @@ -300,174 +369,209 @@ void libvisio::VSDContentCollector::_flushShape() m_isShapeStarted = false; } -void libvisio::VSDContentCollector::_flushCurrentPath(unsigned shapeId) +struct PathActionS { - librevenge::RVNGPropertyList styleProps; - _lineProperties(m_lineStyle, styleProps); - _fillAndShadowProperties(m_fillStyle, styleProps); - librevenge::RVNGPropertyList fillPathProps(styleProps); - fillPathProps.insert("draw:stroke", "none"); - librevenge::RVNGPropertyList linePathProps(styleProps); - linePathProps.insert("draw:fill", "none"); + string name; + double x; + double y; - std::vector tmpPath; - if (m_fillStyle.pattern && !m_currentFillGeometry.empty()) + PathActionS(const string &name, double x, double y) + : name(name), + x(x), + y(y) + {} +}; + +typedef deque PathActionsTp; + +void FlushPath(const PathActionsTp &actionsIn, vector &actionsOut) +{ + for (unsigned int i = 0, cnt = actionsIn.size(); i < cnt; i++) + { + RVNGPropertyList action; + action.insert(PROP_LIBREV_ACTION, actionsIn[i].name.c_str()); + action.insert(PROP_SVG_X, actionsIn[i].x, RVNG_INCH); + action.insert(PROP_SVG_Y, actionsIn[i].y, RVNG_INCH); + actionsOut.push_back(action); + } +} + +/** + * Fixes a specified path by ordering linear segments so that they connect consecutive vertices. + * Addresses a know issue that sometimes segments are not defined in a consecutive order, which + * causes the path to get filled incorrectly/incompletely in the generated document. + * + * @param actionIn + * the original path definition + * + * @param actionsOut + * the modified path + */ +void GetCorrectedPath( + const vector &actionsIn, vector &actionsOut) +{ + PathActionsTp actions; + double x1 = 0.0, y1 = 0.0, x2 = 0.0, y2 = 0.0; // current line segment + string lastActionName; + + for (unsigned int i = 0, cnt = actionsIn.size(); i < cnt; i++) { - bool firstPoint = true; - bool wasMove = false; - for (unsigned i = 0; i < m_currentFillGeometry.size(); i++) + string actionName = actionsIn[i][PROP_LIBREV_ACTION]->getStr().cstr(); + + if (actionName == "M") + { + x1 = actionsIn[i][PROP_SVG_X]->getDouble(); + y1 = actionsIn[i][PROP_SVG_Y]->getDouble(); + } + else if (actionName == "L") { - if (firstPoint) + x2 = actionsIn[i][PROP_SVG_X]->getDouble(); + y2 = actionsIn[i][PROP_SVG_Y]->getDouble(); + + bool connected = false; + + if (lastActionName != "M") // follows anything else than M { - firstPoint = false; - wasMove = true; + connected = true; + actions.push_back(PathActionS("L", x2, y2)); } - else if (m_currentFillGeometry[i]["librevenge:path-action"]->getStr() == "M") + + if (!connected && actions.size() > 1) // connect with the end { - if (!tmpPath.empty()) + unsigned int lastIdx = actions.size() - 1; + + if (actions[lastIdx].x == x1 && actions[lastIdx].y == y1) { - if (!wasMove) - { - if (tmpPath.back()["librevenge:path-action"]->getStr() != "Z") - { - librevenge::RVNGPropertyList closedPath; - closedPath.insert("librevenge:path-action", "Z"); - tmpPath.push_back(closedPath); - } - } - else - { - tmpPath.pop_back(); - } + connected = true; + actions.push_back(PathActionS("L", x2, y2)); } - wasMove = true; - } - else - wasMove = false; - tmpPath.push_back(m_currentFillGeometry[i]); - } - if (!tmpPath.empty()) - { - if (!wasMove) - { - if (tmpPath.back()["librevenge:path-action"]->getStr() != "Z") + else if (actions[lastIdx].x == x2 && actions[lastIdx].y == y2) { - librevenge::RVNGPropertyList closedPath; - closedPath.insert("librevenge:path-action", "Z"); - tmpPath.push_back(closedPath); + connected = true; + actions.push_back(PathActionS("L", x1, y1)); } } - else - tmpPath.pop_back(); - } - if (!tmpPath.empty()) - { - librevenge::RVNGPropertyListVector path; - _convertToPath(tmpPath, path, m_scale*m_lineStyle.rounding); - m_shapeOutputDrawing->addStyle(fillPathProps); - librevenge::RVNGPropertyList propList; - propList.insert("svg:d", path); - if (shapeId && shapeId != MINUS_ONE) - { - librevenge::RVNGString stringId; - stringId.sprintf("id%u", shapeId); - propList.insert("draw:id", stringId); - shapeId = MINUS_ONE; - } - _appendVisibleAndPrintable(propList); - m_shapeOutputDrawing->addPath(propList); - } - } - m_currentFillGeometry.clear(); - tmpPath.clear(); - if (m_lineStyle.pattern && !m_currentLineGeometry.empty()) - { - bool firstPoint = true; - bool wasMove = false; - double x = 0.0; - double y = 0.0; - double prevX = 0.0; - double prevY = 0.0; - for (unsigned i = 0; i < m_currentLineGeometry.size(); i++) - { - if (firstPoint) - { - firstPoint = false; - wasMove = true; - x = m_currentLineGeometry[i]["svg:x"]->getDouble(); - y = m_currentLineGeometry[i]["svg:y"]->getDouble(); - } - else if (m_currentLineGeometry[i]["librevenge:path-action"]->getStr() == "M") + if (!connected && actions.size() > 0 && actions[0].name == "M") // connect with the beginning { - if (!tmpPath.empty()) + if (actions[0].x == x1 && actions[0].y == y1) { - if (!wasMove) - { - if (VSD_ALMOST_ZERO(x - prevX) && VSD_ALMOST_ZERO(y - prevY)) - { - if (tmpPath.back()["librevenge:path-action"]->getStr() != "Z") - { - librevenge::RVNGPropertyList closedPath; - closedPath.insert("librevenge:path-action", "Z"); - tmpPath.push_back(closedPath); - } - } - } - else - { - tmpPath.pop_back(); - } + connected = true; + actions[0].name = "L"; + actions.push_front(PathActionS("M", x2, y2)); + } + else if (actions[0].x == x2 && actions[0].y == y2) + { + connected = true; + actions[0].name = "L"; + actions.push_front(PathActionS("M", x1, y1)); } - x = m_currentLineGeometry[i]["svg:x"]->getDouble(); - y = m_currentLineGeometry[i]["svg:y"]->getDouble(); - wasMove = true; } - else - wasMove = false; - tmpPath.push_back(m_currentLineGeometry[i]); - if (m_currentLineGeometry[i]["svg:x"]) - prevX = m_currentLineGeometry[i]["svg:x"]->getDouble(); - if (m_currentLineGeometry[i]["svg:y"]) - prevY = m_currentLineGeometry[i]["svg:y"]->getDouble(); + + if (!connected) // store the first segment + { + actions.push_back(PathActionS("M", x1, y1)); + actions.push_back(PathActionS("L", x2, y2)); + } + + x1 = x2; + y1 = y2; } - if (!tmpPath.empty()) + else // not M nor L { - if (!wasMove) + if (lastActionName == "M") { - if (VSD_ALMOST_ZERO(x - prevX) && VSD_ALMOST_ZERO(y - prevY)) - { - if (tmpPath.back()["librevenge:path-action"]->getStr() != "Z") - { - librevenge::RVNGPropertyList closedPath; - closedPath.insert("librevenge:path-action", "Z"); - tmpPath.push_back(closedPath); - } - } + actionsOut.push_back(actionsIn[i - 1]); } else { - tmpPath.pop_back(); + FlushPath(actions, actionsOut); } + + actions.clear(); + actionsOut.push_back(actionsIn[i]); } - if (!tmpPath.empty()) + + lastActionName = actionName; + } + + FlushPath(actions, actionsOut); + + int lastIdx = actionsOut.size() - 1; + + // Close the path if the first and last points are (close to) identical. If the points are really + // identical, Z is not necessary and just serves as an indication of the closed path. + // + if (lastIdx > 0 && actionsOut[0][PROP_SVG_X] && actionsOut[0][PROP_SVG_Y] + && actionsOut[lastIdx][PROP_SVG_X] && actionsOut[lastIdx][PROP_SVG_Y]) + { + x1 = actionsOut[0][PROP_SVG_X]->getDouble(); + y1 = actionsOut[0][PROP_SVG_Y]->getDouble(); + x2 = actionsOut[lastIdx][PROP_SVG_X]->getDouble(); + y2 = actionsOut[lastIdx][PROP_SVG_Y]->getDouble(); + + if (VSD_ALMOST_ZERO(x1 - x2) && VSD_ALMOST_ZERO(y1 - y2)) { - librevenge::RVNGPropertyListVector path; - _convertToPath(tmpPath, path, m_scale*m_lineStyle.rounding); - m_shapeOutputDrawing->addStyle(linePathProps); - librevenge::RVNGPropertyList propList; - propList.insert("svg:d", path); - if (shapeId && shapeId != MINUS_ONE) - { - librevenge::RVNGString stringId; - stringId.sprintf("id%u", shapeId); - propList.insert("draw:id", stringId); - shapeId = MINUS_ONE; - } - _appendVisibleAndPrintable(propList); - m_shapeOutputDrawing->addPath(propList); + RVNGPropertyList action; + action.insert(PROP_LIBREV_ACTION, "Z"); + actionsOut.push_back(action); + } + } +} + +void libvisio::VSDContentCollector::AddPath( + const vector &inPath, const RVNGPropertyList &styleProps, unsigned shapeId) +{ + if (!inPath.empty()) + { + RVNGPropertyListVector path; + _convertToPath(inPath, path, m_scale * m_lineStyle.rounding); + m_shapeOutputDrawing->addStyle(styleProps); + + RVNGPropertyList propList; + propList.insert("svg:d", path); + + if (shapeId && shapeId != MINUS_ONE) + { + RVNGString stringId; + stringId.sprintf("id%u", shapeId); + propList.insert("draw:id", stringId); + shapeId = MINUS_ONE; } + + _appendVisibleAndPrintable(propList); + m_shapeOutputDrawing->addPath(propList); + } +} + +void libvisio::VSDContentCollector::_flushCurrentPath(unsigned shapeId) +{ + RVNGPropertyList styleProps; + _lineProperties(m_lineStyle, styleProps); + _fillAndShadowProperties(m_fillStyle, styleProps); + + if (m_fillStyle.pattern && !m_currentFillGeometry.empty()) + { + RVNGPropertyList fillPathProps(styleProps); + fillPathProps.insert("draw:stroke", "none"); + vector outPath; + + GetCorrectedPath(m_currentFillGeometry, outPath); + AddPath(outPath, fillPathProps, shapeId); + } + + m_currentFillGeometry.clear(); + + if (m_lineStyle.pattern && !m_currentLineGeometry.empty()) + { + RVNGPropertyList linePathProps(styleProps); + linePathProps.insert("draw:fill", "none"); + vector outPath; + + GetCorrectedPath(m_currentLineGeometry, outPath); + AddPath(outPath, linePathProps, shapeId); } + m_currentLineGeometry.clear(); } @@ -628,6 +732,12 @@ void libvisio::VSDContentCollector::_flushText() textBlockProps.insert("fo:padding-right", m_textBlockStyle.rightMargin); textBlockProps.insert("librevenge:rotate", angle*180/M_PI, librevenge::RVNG_GENERIC); + if (m_textBlockStyle.isTextBkgndFilled) + { + textBlockProps.insert( + PROP_FO_BACKGROUND_COLOR, getColourString(m_textBlockStyle.textBkgndColour)); + } + switch (m_textBlockStyle.verticalAlign) { case 0: // Top @@ -2657,15 +2767,17 @@ void libvisio::VSDContentCollector::_lineProperties(const VSDLineStyle &style, l // Deal with line markers (arrows, etc.) if (style.startMarker > 0) { - styleProps.insert("draw:marker-start-viewbox", _linePropertiesMarkerViewbox(style.startMarker)); + styleProps.insert("draw:marker-start-viewbox", _linePropertiesMarkerViewbox(style.startMarker, true)); styleProps.insert("draw:marker-start-path", _linePropertiesMarkerPath(style.startMarker)); + styleProps.insert("draw:marker-start-path-transform", _linePropertiesMarkerPathTransform(style.startMarker, true)); double w = m_scale*_linePropertiesMarkerScale(style.startMarker)*(0.1/(style.width*style.width+1)+2.54*style.width); styleProps.insert("draw:marker-start-width", (std::max)(w, 0.05)); } if (style.endMarker > 0) { - styleProps.insert("draw:marker-end-viewbox", _linePropertiesMarkerViewbox(style.endMarker)); + styleProps.insert("draw:marker-end-viewbox", _linePropertiesMarkerViewbox(style.endMarker, false)); styleProps.insert("draw:marker-end-path", _linePropertiesMarkerPath(style.endMarker)); + styleProps.insert("draw:marker-end-path-transform", _linePropertiesMarkerPathTransform(style.endMarker, false)); double w = m_scale*_linePropertiesMarkerScale(style.endMarker)*(0.1/(style.width*style.width+1)+2.54*style.width); styleProps.insert("draw:marker-end-width", (std::max)(w, 0.05)); } @@ -3515,6 +3627,12 @@ void libvisio::VSDContentCollector::collectLayerMem(unsigned level, const VSDNam memcpy(&tmpData[0], layerMem.m_data.getDataBuffer(), layerMem.m_data.size()); appendCharacters(text, tmpData, layerMem.m_format); + if (tmpData.size() > 0) + { + memcpy(&tmpData[0], layerMem.m_data.getDataBuffer(), layerMem.m_data.size()); + appendCharacters(text, tmpData, layerMem.m_format); + } + m_currentLayerMem.clear(); bool bRes = parse(text.cstr(), @@ -3620,7 +3738,7 @@ void libvisio::VSDContentCollector::_listLevelFromBullet(librevenge::RVNGPropert propList.insert("librevenge:level", 1); propList.insert("text:bullet-char", bullet.m_bulletStr); if (!(bullet.m_bulletFont.empty())) - propList.insert("fo:font-family", bullet.m_bulletFont); + propList.insert("fo:font-name", bullet.m_bulletFont); if (bullet.m_bulletFontSize > 0.0) propList.insert("fo:font-size", bullet.m_bulletFontSize*72.0, librevenge::RVNG_POINT); else if (bullet.m_bulletFontSize < 0.0) diff --git a/src/lib/VSDContentCollector.h b/src/lib/VSDContentCollector.h index 4460e35..eb8666c 100644 --- a/src/lib/VSDContentCollector.h +++ b/src/lib/VSDContentCollector.h @@ -205,7 +205,8 @@ private: void _fillAndShadowProperties(const VSDFillStyle &style, librevenge::RVNGPropertyList &styleProps); void _applyLinePattern(); - const char *_linePropertiesMarkerViewbox(unsigned marker); + const char *_linePropertiesMarkerPathTransform(unsigned marker, bool reverse); + const char *_linePropertiesMarkerViewbox(unsigned marker, bool reverse); const char *_linePropertiesMarkerPath(unsigned marker); double _linePropertiesMarkerScale(unsigned marker); @@ -233,6 +234,10 @@ private: void _convertToPath(const std::vector &segmentVector, librevenge::RVNGPropertyListVector &path, double rounding); + void AddPath( + const std::vector &path, + const librevenge::RVNGPropertyList &styleProps, unsigned int shapeId); + bool m_isPageStarted; double m_pageWidth; double m_pageHeight; diff --git a/src/lib/VSDMetaData.cpp b/src/lib/VSDMetaData.cpp index 7241b00..52b6575 100644 --- a/src/lib/VSDMetaData.cpp +++ b/src/lib/VSDMetaData.cpp @@ -307,7 +307,7 @@ bool libvisio::VSDMetaData::parseTimes(librevenge::RVNGInputStream *input) uint32_t firstDirSectorLocation = readU32(input); // Seek to the Root Directory Entry - size_t sectorSize = std::pow(2, sectorShift); + size_t sectorSize = static_cast(std::pow(2.0, sectorShift)); input->seek((firstDirSectorLocation + 1) * sectorSize, librevenge::RVNG_SEEK_SET); // DirectoryEntryName: 64 bytes // DirectoryEntryNameLength: 2 bytes diff --git a/src/lib/VSDOutputElementList.cpp b/src/lib/VSDOutputElementList.cpp index 5b06fb8..928dbe4 100644 --- a/src/lib/VSDOutputElementList.cpp +++ b/src/lib/VSDOutputElementList.cpp @@ -8,6 +8,10 @@ */ #include "VSDOutputElementList.h" +#include "VsdElementListPreprocessor.h" + +using namespace std; + namespace libvisio { @@ -25,7 +29,7 @@ static void separateSpacesAndInsertText(librevenge::RVNGDrawingInterface *iface, return; } librevenge::RVNGString tmpText; - int numConsecutiveSpaces = 0; + int numConsecutiveSpaces = 1; // force initial single space replacement librevenge::RVNGString::Iter i(text); for (i.rewind(); i.next();) { @@ -42,8 +46,7 @@ static void separateSpacesAndInsertText(librevenge::RVNGDrawingInterface *iface, tmpText.clear(); } - if (iface) - iface->insertSpace(); + iface->insertSpace(); } else { @@ -55,269 +58,6 @@ static void separateSpacesAndInsertText(librevenge::RVNGDrawingInterface *iface, } // anonymous namespace -class VSDOutputElement -{ -public: - VSDOutputElement() {} - virtual ~VSDOutputElement() {} - virtual void draw(librevenge::RVNGDrawingInterface *painter) = 0; - virtual VSDOutputElement *clone() = 0; -}; - - -class VSDStyleOutputElement : public VSDOutputElement -{ -public: - VSDStyleOutputElement(const librevenge::RVNGPropertyList &propList); - virtual ~VSDStyleOutputElement() {} - virtual void draw(librevenge::RVNGDrawingInterface *painter); - virtual VSDOutputElement *clone() - { - return new VSDStyleOutputElement(m_propList); - } -private: - librevenge::RVNGPropertyList m_propList; -}; - - -class VSDPathOutputElement : public VSDOutputElement -{ -public: - VSDPathOutputElement(const librevenge::RVNGPropertyList &propList); - virtual ~VSDPathOutputElement() {} - virtual void draw(librevenge::RVNGDrawingInterface *painter); - virtual VSDOutputElement *clone() - { - return new VSDPathOutputElement(m_propList); - } -private: - librevenge::RVNGPropertyList m_propList; -}; - - -class VSDGraphicObjectOutputElement : public VSDOutputElement -{ -public: - VSDGraphicObjectOutputElement(const librevenge::RVNGPropertyList &propList); - virtual ~VSDGraphicObjectOutputElement() {} - virtual void draw(librevenge::RVNGDrawingInterface *painter); - virtual VSDOutputElement *clone() - { - return new VSDGraphicObjectOutputElement(m_propList); - } -private: - librevenge::RVNGPropertyList m_propList; -}; - - -class VSDStartTextObjectOutputElement : public VSDOutputElement -{ -public: - VSDStartTextObjectOutputElement(const librevenge::RVNGPropertyList &propList); - virtual ~VSDStartTextObjectOutputElement() {} - virtual void draw(librevenge::RVNGDrawingInterface *painter); - virtual VSDOutputElement *clone() - { - return new VSDStartTextObjectOutputElement(m_propList); - } -private: - librevenge::RVNGPropertyList m_propList; -}; - - -class VSDOpenParagraphOutputElement : public VSDOutputElement -{ -public: - VSDOpenParagraphOutputElement(const librevenge::RVNGPropertyList &propList); - virtual ~VSDOpenParagraphOutputElement() {} - virtual void draw(librevenge::RVNGDrawingInterface *painter); - virtual VSDOutputElement *clone() - { - return new VSDOpenParagraphOutputElement(m_propList); - } -private: - librevenge::RVNGPropertyList m_propList; -}; - - -class VSDStartLayerOutputElement : public VSDOutputElement -{ -public: - VSDStartLayerOutputElement(const librevenge::RVNGPropertyList &propList); - virtual ~VSDStartLayerOutputElement() {} - virtual void draw(librevenge::RVNGDrawingInterface *painter); - virtual VSDOutputElement *clone() - { - return new VSDStartLayerOutputElement(m_propList); - } -private: - librevenge::RVNGPropertyList m_propList; -}; - - -class VSDEndLayerOutputElement : public VSDOutputElement -{ -public: - VSDEndLayerOutputElement(); - virtual ~VSDEndLayerOutputElement() {} - virtual void draw(librevenge::RVNGDrawingInterface *painter); - virtual VSDOutputElement *clone() - { - return new VSDEndLayerOutputElement(); - } -}; - - -class VSDOpenSpanOutputElement : public VSDOutputElement -{ -public: - VSDOpenSpanOutputElement(const librevenge::RVNGPropertyList &propList); - virtual ~VSDOpenSpanOutputElement() {} - virtual void draw(librevenge::RVNGDrawingInterface *painter); - virtual VSDOutputElement *clone() - { - return new VSDOpenSpanOutputElement(m_propList); - } -private: - librevenge::RVNGPropertyList m_propList; -}; - - -class VSDInsertTextOutputElement : public VSDOutputElement -{ -public: - VSDInsertTextOutputElement(const librevenge::RVNGString &text); - virtual ~VSDInsertTextOutputElement() {} - virtual void draw(librevenge::RVNGDrawingInterface *painter); - virtual VSDOutputElement *clone() - { - return new VSDInsertTextOutputElement(m_text); - } -private: - librevenge::RVNGString m_text; -}; - - -class VSDInsertLineBreakOutputElement : public VSDOutputElement -{ -public: - VSDInsertLineBreakOutputElement(); - virtual ~VSDInsertLineBreakOutputElement() {} - virtual void draw(librevenge::RVNGDrawingInterface *painter); - virtual VSDOutputElement *clone() - { - return new VSDInsertLineBreakOutputElement(); - } -}; - - -class VSDInsertTabOutputElement : public VSDOutputElement -{ -public: - VSDInsertTabOutputElement(); - virtual ~VSDInsertTabOutputElement() {} - virtual void draw(librevenge::RVNGDrawingInterface *painter); - virtual VSDOutputElement *clone() - { - return new VSDInsertTabOutputElement(); - } -}; - - -class VSDCloseSpanOutputElement : public VSDOutputElement -{ -public: - VSDCloseSpanOutputElement(); - virtual ~VSDCloseSpanOutputElement() {} - virtual void draw(librevenge::RVNGDrawingInterface *painter); - virtual VSDOutputElement *clone() - { - return new VSDCloseSpanOutputElement(); - } -}; - - -class VSDCloseParagraphOutputElement : public VSDOutputElement -{ -public: - VSDCloseParagraphOutputElement(); - virtual ~VSDCloseParagraphOutputElement() {} - virtual void draw(librevenge::RVNGDrawingInterface *painter); - virtual VSDOutputElement *clone() - { - return new VSDCloseParagraphOutputElement(); - } -}; - - -class VSDEndTextObjectOutputElement : public VSDOutputElement -{ -public: - VSDEndTextObjectOutputElement(); - virtual ~VSDEndTextObjectOutputElement() {} - virtual void draw(librevenge::RVNGDrawingInterface *painter); - virtual VSDOutputElement *clone() - { - return new VSDEndTextObjectOutputElement(); - } -}; - -class VSDOpenListElementOutputElement : public VSDOutputElement -{ -public: - VSDOpenListElementOutputElement(const librevenge::RVNGPropertyList &propList); - virtual ~VSDOpenListElementOutputElement() {} - virtual void draw(librevenge::RVNGDrawingInterface *painter); - virtual VSDOutputElement *clone() - { - return new VSDOpenListElementOutputElement(m_propList); - } -private: - librevenge::RVNGPropertyList m_propList; -}; - - -class VSDCloseListElementOutputElement : public VSDOutputElement -{ -public: - VSDCloseListElementOutputElement(); - virtual ~VSDCloseListElementOutputElement() {} - virtual void draw(librevenge::RVNGDrawingInterface *painter); - virtual VSDOutputElement *clone() - { - return new VSDCloseListElementOutputElement(); - } -}; - - -class VSDOpenUnorderedListLevelOutputElement : public VSDOutputElement -{ -public: - VSDOpenUnorderedListLevelOutputElement(const librevenge::RVNGPropertyList &propList); - virtual ~VSDOpenUnorderedListLevelOutputElement() {} - virtual void draw(librevenge::RVNGDrawingInterface *painter); - virtual VSDOutputElement *clone() - { - return new VSDOpenUnorderedListLevelOutputElement(m_propList); - } -private: - librevenge::RVNGPropertyList m_propList; -}; - - -class VSDCloseUnorderedListLevelOutputElement : public VSDOutputElement -{ -public: - VSDCloseUnorderedListLevelOutputElement(); - virtual ~VSDCloseUnorderedListLevelOutputElement() {} - virtual void draw(librevenge::RVNGDrawingInterface *painter); - virtual VSDOutputElement *clone() - { - return new VSDCloseUnorderedListLevelOutputElement(); - } -}; - - } // namespace libvisio libvisio::VSDStyleOutputElement::VSDStyleOutputElement(const librevenge::RVNGPropertyList &propList) : @@ -529,8 +269,16 @@ libvisio::VSDOutputElementList::~VSDOutputElementList() void libvisio::VSDOutputElementList::draw(librevenge::RVNGDrawingInterface *painter) const { - for (std::vector::const_iterator iter = m_elements.begin(); iter != m_elements.end(); ++iter) + VsdElementListPreprocessorC velp; + vector outElements; + velp.Process(m_elements, outElements); + + for (std::vector::const_iterator iter = outElements.begin(); + iter != outElements.end(); ++iter) + { (*iter)->draw(painter); + delete *iter; + } } void libvisio::VSDOutputElementList::addStyle(const librevenge::RVNGPropertyList &propList) diff --git a/src/lib/VSDOutputElementList.h b/src/lib/VSDOutputElementList.h index 0a500e2..8d5b68e 100644 --- a/src/lib/VSDOutputElementList.h +++ b/src/lib/VSDOutputElementList.h @@ -18,7 +18,322 @@ namespace libvisio { -class VSDOutputElement; +class VSDOutputElement +{ +public: + VSDOutputElement() {} + virtual ~VSDOutputElement() {} + virtual void draw(librevenge::RVNGDrawingInterface *painter) = 0; + virtual VSDOutputElement *clone() = 0; +}; + + +class VSDStyleOutputElement : public VSDOutputElement +{ +public: + VSDStyleOutputElement(const librevenge::RVNGPropertyList &propList); + virtual ~VSDStyleOutputElement() {} + virtual void draw(librevenge::RVNGDrawingInterface *painter); + virtual VSDOutputElement *clone() + { + return new VSDStyleOutputElement(m_propList); + } +private: + librevenge::RVNGPropertyList m_propList; +}; + + +class VSDPathOutputElement : public VSDOutputElement +{ +public: + VSDPathOutputElement(const librevenge::RVNGPropertyList &propList); + virtual ~VSDPathOutputElement() {} + virtual void draw(librevenge::RVNGDrawingInterface *painter); + virtual VSDOutputElement *clone() + { + return new VSDPathOutputElement(m_propList); + } +private: + librevenge::RVNGPropertyList m_propList; +}; + + +class VSDGraphicObjectOutputElement : public VSDOutputElement +{ +public: + VSDGraphicObjectOutputElement(const librevenge::RVNGPropertyList &propList); + virtual ~VSDGraphicObjectOutputElement() {} + virtual void draw(librevenge::RVNGDrawingInterface *painter); + virtual VSDOutputElement *clone() + { + return new VSDGraphicObjectOutputElement(m_propList); + } +private: + librevenge::RVNGPropertyList m_propList; +}; + + +class VSDStartTextObjectOutputElement : public VSDOutputElement +{ +public: + VSDStartTextObjectOutputElement(const librevenge::RVNGPropertyList &propList); + virtual ~VSDStartTextObjectOutputElement() {} + virtual void draw(librevenge::RVNGDrawingInterface *painter); + virtual VSDOutputElement *clone() + { + return new VSDStartTextObjectOutputElement(m_propList); + } + + librevenge::RVNGPropertyList &GetPropertyList() + { + return m_propList; + } + +private: + librevenge::RVNGPropertyList m_propList; +}; + + +class VSDOpenParagraphOutputElement : public VSDOutputElement +{ +public: + VSDOpenParagraphOutputElement(const librevenge::RVNGPropertyList &propList); + virtual ~VSDOpenParagraphOutputElement() {} + virtual void draw(librevenge::RVNGDrawingInterface *painter); + virtual VSDOutputElement *clone() + { + return new VSDOpenParagraphOutputElement(m_propList); + } + + librevenge::RVNGPropertyList &GetPropertyList() + { + return m_propList; + } + const librevenge::RVNGPropertyList &GetPropertyList() const + { + return m_propList; + } + +private: + librevenge::RVNGPropertyList m_propList; +}; + + +class VSDStartLayerOutputElement : public VSDOutputElement +{ +public: + VSDStartLayerOutputElement(const librevenge::RVNGPropertyList &propList); + virtual ~VSDStartLayerOutputElement() {} + virtual void draw(librevenge::RVNGDrawingInterface *painter); + virtual VSDOutputElement *clone() + { + return new VSDStartLayerOutputElement(m_propList); + } +private: + librevenge::RVNGPropertyList m_propList; +}; + + +class VSDEndLayerOutputElement : public VSDOutputElement +{ +public: + VSDEndLayerOutputElement(); + virtual ~VSDEndLayerOutputElement() {} + virtual void draw(librevenge::RVNGDrawingInterface *painter); + virtual VSDOutputElement *clone() + { + return new VSDEndLayerOutputElement(); + } +}; + + +class VSDOpenSpanOutputElement : public VSDOutputElement +{ +public: + VSDOpenSpanOutputElement(const librevenge::RVNGPropertyList &propList); + virtual ~VSDOpenSpanOutputElement() {} + virtual void draw(librevenge::RVNGDrawingInterface *painter); + virtual VSDOutputElement *clone() + { + return new VSDOpenSpanOutputElement(m_propList); + } + + librevenge::RVNGPropertyList &GetPropertyList() + { + return m_propList; + } + const librevenge::RVNGPropertyList &GetPropertyList() const + { + return m_propList; + } + +private: + librevenge::RVNGPropertyList m_propList; +}; + + +class VSDInsertTextOutputElement : public VSDOutputElement +{ +public: + VSDInsertTextOutputElement(const librevenge::RVNGString &text); + virtual ~VSDInsertTextOutputElement() {} + virtual void draw(librevenge::RVNGDrawingInterface *painter); + virtual VSDOutputElement *clone() + { + return new VSDInsertTextOutputElement(m_text); + } + + const librevenge::RVNGString &GetText() const + { + return m_text; + } + +private: + librevenge::RVNGString m_text; +}; + + +class VSDInsertLineBreakOutputElement : public VSDOutputElement +{ +public: + VSDInsertLineBreakOutputElement(); + virtual ~VSDInsertLineBreakOutputElement() {} + virtual void draw(librevenge::RVNGDrawingInterface *painter); + virtual VSDOutputElement *clone() + { + return new VSDInsertLineBreakOutputElement(); + } +}; + + +class VSDInsertTabOutputElement : public VSDOutputElement +{ +public: + VSDInsertTabOutputElement(); + virtual ~VSDInsertTabOutputElement() {} + virtual void draw(librevenge::RVNGDrawingInterface *painter); + virtual VSDOutputElement *clone() + { + return new VSDInsertTabOutputElement(); + } +}; + + +class VSDCloseSpanOutputElement : public VSDOutputElement +{ +public: + VSDCloseSpanOutputElement(); + virtual ~VSDCloseSpanOutputElement() {} + virtual void draw(librevenge::RVNGDrawingInterface *painter); + virtual VSDOutputElement *clone() + { + return new VSDCloseSpanOutputElement(); + } +}; + + +class VSDCloseParagraphOutputElement : public VSDOutputElement +{ +public: + VSDCloseParagraphOutputElement(); + virtual ~VSDCloseParagraphOutputElement() {} + virtual void draw(librevenge::RVNGDrawingInterface *painter); + virtual VSDOutputElement *clone() + { + return new VSDCloseParagraphOutputElement(); + } +}; + + +class VSDEndTextObjectOutputElement : public VSDOutputElement +{ +public: + VSDEndTextObjectOutputElement(); + virtual ~VSDEndTextObjectOutputElement() {} + virtual void draw(librevenge::RVNGDrawingInterface *painter); + virtual VSDOutputElement *clone() + { + return new VSDEndTextObjectOutputElement(); + } +}; + + +class VSDOpenListElementOutputElement : public VSDOutputElement +{ +public: + VSDOpenListElementOutputElement(const librevenge::RVNGPropertyList &propList); + virtual ~VSDOpenListElementOutputElement() {} + virtual void draw(librevenge::RVNGDrawingInterface *painter); + virtual VSDOutputElement *clone() + { + return new VSDOpenListElementOutputElement(m_propList); + } + + librevenge::RVNGPropertyList &GetPropertyList() + { + return m_propList; + } + const librevenge::RVNGPropertyList &GetPropertyList() const + { + return m_propList; + } + +private: + librevenge::RVNGPropertyList m_propList; +}; + + +class VSDCloseListElementOutputElement : public VSDOutputElement +{ +public: + VSDCloseListElementOutputElement(); + virtual ~VSDCloseListElementOutputElement() {} + virtual void draw(librevenge::RVNGDrawingInterface *painter); + virtual VSDOutputElement *clone() + { + return new VSDCloseListElementOutputElement(); + } + +}; + + +class VSDOpenUnorderedListLevelOutputElement : public VSDOutputElement +{ +public: + VSDOpenUnorderedListLevelOutputElement(const librevenge::RVNGPropertyList &propList); + virtual ~VSDOpenUnorderedListLevelOutputElement() {} + virtual void draw(librevenge::RVNGDrawingInterface *painter); + virtual VSDOutputElement *clone() + { + return new VSDOpenUnorderedListLevelOutputElement(m_propList); + } + + librevenge::RVNGPropertyList &GetPropertyList() + { + return m_propList; + } + const librevenge::RVNGPropertyList &GetPropertyList() const + { + return m_propList; + } + +private: + librevenge::RVNGPropertyList m_propList; +}; + + +class VSDCloseUnorderedListLevelOutputElement : public VSDOutputElement +{ +public: + VSDCloseUnorderedListLevelOutputElement(); + virtual ~VSDCloseUnorderedListLevelOutputElement() {} + virtual void draw(librevenge::RVNGDrawingInterface *painter); + virtual VSDOutputElement *clone() + { + return new VSDCloseUnorderedListLevelOutputElement(); + } +}; + class VSDOutputElementList { diff --git a/src/lib/preprocess/SvgDC.cpp b/src/lib/preprocess/SvgDC.cpp new file mode 100644 index 0000000..a0daa20 --- /dev/null +++ b/src/lib/preprocess/SvgDC.cpp @@ -0,0 +1 @@ +#include "SvgDC.h" diff --git a/src/lib/preprocess/SvgDC.h b/src/lib/preprocess/SvgDC.h new file mode 100644 index 0000000..a852bd8 --- /dev/null +++ b/src/lib/preprocess/SvgDC.h @@ -0,0 +1,32 @@ +#ifndef _SVG_DC_H_INCLUDED_ +#define _SVG_DC_H_INCLUDED_ + + +#include +#include + + +class SvgFontC; + +class SvgDC +{ +public: + virtual ~SvgDC() {} + + virtual const SvgFontC *GetFont( + double heightInch, unsigned int weight, bool isItalic, const std::string &name) const = 0; + + virtual double GetFontBaseLineHeightRatio(unsigned int fontId) const = 0; + + virtual double GetTextPartialExtents( + const std::wstring &text, unsigned int fontId, double offsetInch, + std::vector &extentsInch) const = 0; + + virtual double GetTabCharExtent(unsigned int fontId, double offsetInch) const = 0; +}; + + +extern SvgDC &GetSystemDC(); + + +#endif // _SVG_DC_H_ diff --git a/src/lib/preprocess/SvgFont.cpp b/src/lib/preprocess/SvgFont.cpp new file mode 100644 index 0000000..4309188 --- /dev/null +++ b/src/lib/preprocess/SvgFont.cpp @@ -0,0 +1,61 @@ +#include "SvgFont.h" + +using namespace std; + + +SvgFontC::SvgFontC( + unsigned int id, double heightInch, unsigned int weight, bool isItalic, const string &faceName +) + : m_id(id), + m_heightInch(heightInch), + m_weight(weight), + m_isItalic(isItalic), + m_faceName(faceName) +{ +} + +unsigned int SvgFontC::GetId() const +{ + return m_id; +} + +double SvgFontC::GetHeightInch() const +{ + return m_heightInch; +} + +unsigned int SvgFontC::GetWeight() const +{ + return m_weight; +} + +bool SvgFontC::IsItalic() const +{ + return m_isItalic; +} + +string SvgFontC::GetFaceName() const +{ + return m_faceName; +} + + +bool SvgFontCompareS::operator()(const SvgFontC *pFont1, const SvgFontC *pFont2) const +{ + if (pFont1->GetHeightInch() != pFont2->GetHeightInch()) + { + return pFont1->GetHeightInch() < pFont2->GetHeightInch(); + } + + if (pFont1->GetWeight() != pFont2->GetWeight()) + { + return pFont1->GetWeight() < pFont2->GetWeight(); + } + + if (pFont1->IsItalic() != pFont2->IsItalic()) + { + return !pFont1->IsItalic(); + } + + return pFont1->GetFaceName().compare(pFont2->GetFaceName()) < 0; +} diff --git a/src/lib/preprocess/SvgFont.h b/src/lib/preprocess/SvgFont.h new file mode 100644 index 0000000..af859c4 --- /dev/null +++ b/src/lib/preprocess/SvgFont.h @@ -0,0 +1,38 @@ +#ifndef _SVG_FONT_H_INCLUDED_ +#define _SVG_FONT_H_INCLUDED_ + + +#include + + +class SvgFontC +{ +public: + SvgFontC( + unsigned int id, double heightInch, unsigned int weight, bool isItalic, + const std::string &faceName); + + virtual ~SvgFontC() {} + + unsigned int GetId() const; + double GetHeightInch() const; + unsigned int GetWeight() const; + bool IsItalic() const; + std::string GetFaceName() const; + +protected: + unsigned int m_id; + double m_heightInch; + unsigned int m_weight; + bool m_isItalic; + const std::string m_faceName; +}; + + +struct SvgFontCompareS +{ + bool operator()(const SvgFontC *pFont1, const SvgFontC *pFont2) const; +}; + + +#endif // _SVG_FONT_H_ diff --git a/src/lib/preprocess/SvgParagraph.cpp b/src/lib/preprocess/SvgParagraph.cpp new file mode 100644 index 0000000..47b2f4d --- /dev/null +++ b/src/lib/preprocess/SvgParagraph.cpp @@ -0,0 +1,152 @@ +#include "librevenge/SvgConstants.h" +#include "SvgParagraph.h" +#include "SvgUtils.h" +#include "VSDOutputElementList.h" + +#include + +using namespace librevenge; +using namespace libvisio; +using namespace std; +using namespace svgconstants; + + +ParagraphC::ParagraphC() + : m_margLeftInch(0.0), + m_margRightInch(0.0), + m_margTopInch(0.0), + m_margBottomInch(0.0), + m_lineHeight(1.0), + m_isLineHeightRel(true) +{ +} + +double ParagraphC::GetMarginLeftInch() const +{ + return m_margLeftInch; +} + +double ParagraphC::GetMarginRightInch() const +{ + return m_margRightInch; +} + +double ParagraphC::GetMarginTopInch() const +{ + return m_margTopInch; +} + +double ParagraphC::GetMarginBottomInch() const +{ + return m_margBottomInch; +} + +double ParagraphC::GetLineHeightInch(double fontSizeInch) const +{ + if (m_isLineHeightRel) + { + return fontSizeInch * m_lineHeight; + } + + return m_lineHeight; +} + +void ParagraphC::Reset(const VSDOpenParagraphOutputElement *pOpenParagraph) +{ + Reset(pOpenParagraph->GetPropertyList()); +} + +void ParagraphC::Reset(const VSDOpenListElementOutputElement *pOpenListElem) +{ + Reset(pOpenListElem->GetPropertyList()); +} + +void ParagraphC::Reset(const RVNGPropertyList &properties) +{ + m_margLeftInch = properties[PROP_FO_MARGIN_LEFT] + ? SvgUtilsC::GetInchValue(*properties[PROP_FO_MARGIN_LEFT]) : 0.0; + + m_margRightInch = properties[PROP_FO_MARGIN_RIGHT] + ? SvgUtilsC::GetInchValue(*properties[PROP_FO_MARGIN_RIGHT]) : 0.0; + + m_margTopInch = properties[PROP_FO_MARGIN_TOP] + ? SvgUtilsC::GetInchValue(*properties[PROP_FO_MARGIN_TOP]) : 0.0; + + m_margBottomInch = properties[PROP_FO_MARGIN_BOTTOM] + ? SvgUtilsC::GetInchValue(*properties[PROP_FO_MARGIN_BOTTOM]) : 0.0; + + m_horizontalAlignment = properties[PROP_FO_TEXT_ALIGN] + ? (*properties[PROP_FO_TEXT_ALIGN]).getStr().cstr() : PVAL_FO_TEXT_ALIGN_CENTER; + + if (properties[PROP_FO_LINE_HEIGHT]) + { + switch (properties[PROP_FO_LINE_HEIGHT]->getUnit()) + { + case RVNG_UNIT_ERROR : + m_lineHeight = 1.0; + m_isLineHeightRel = true; + break; + + case RVNG_PERCENT : + m_lineHeight = properties[PROP_FO_LINE_HEIGHT]->getDouble(); + m_isLineHeightRel = true; + break; + + default : + m_lineHeight = SvgUtilsC::GetInchValue(*properties[PROP_FO_LINE_HEIGHT]); + m_isLineHeightRel = false; + break; + } + } + else + { + m_lineHeight = 1.0; + m_isLineHeightRel = true; + } + + m_text.clear(); + m_textExtents.clear(); + m_spanMarks.clear(); +} + +void ParagraphC::AddSpan(const VSDOpenSpanOutputElement *pSpan) +{ + m_spanMarks[m_text.size()] = pSpan; +} + +void ParagraphC::AddText(const wstring &text, const TextExtentsTp &extents) +{ + assert(text.size() == extents.size()); + m_text += text; + m_textExtents.insert(m_textExtents.end(), extents.begin(), extents.end()); +} + +double ParagraphC::GetCurrentTextExtentInch() const +{ + if (m_textExtents.size() > 0) + { + return m_textExtents[m_textExtents.size() - 1]; + } + + return 0; +} + +const string &ParagraphC::GetHorizontalAlignment() const +{ + return m_horizontalAlignment; +} + +const wstring &ParagraphC::GetText() const +{ + return m_text; +} + +const ParagraphC::TextExtentsTp &ParagraphC::GetTextExtents() const +{ + return m_textExtents; +} + +const ParagraphC::SpanMarksTp &ParagraphC::GetSpanMarks() const +{ + return m_spanMarks; +} diff --git a/src/lib/preprocess/SvgParagraph.h b/src/lib/preprocess/SvgParagraph.h new file mode 100644 index 0000000..578c909 --- /dev/null +++ b/src/lib/preprocess/SvgParagraph.h @@ -0,0 +1,69 @@ +#ifndef _SVG_PARAGRAPH_H_INCLUDED_ +#define _SVG_PARAGRAPH_H_INCLUDED_ + + +#include +#include +#include + + +namespace librevenge +{ +class RVNGPropertyList; +} + +namespace libvisio +{ +class VSDOpenParagraphOutputElement; +class VSDOpenListElementOutputElement; +class VSDOpenSpanOutputElement; +} + +/** + * Represents a paragraph of text within the original text box. Manages individual spans of text + * and stores information about partial text extents. + */ +class ParagraphC +{ +public: + typedef std::vector TextExtentsTp; + typedef std::map SpanMarksTp; + typedef SpanMarksTp::const_iterator SpanMarksConstItTp; + + ParagraphC(); + + double GetMarginLeftInch() const; + double GetMarginRightInch() const; + double GetMarginTopInch() const; + double GetMarginBottomInch() const; + double GetLineHeightInch(double fontSizeInch) const; + + void Reset(const libvisio::VSDOpenParagraphOutputElement *pOpenParagraph); + void Reset(const libvisio::VSDOpenListElementOutputElement *pOpenListElem); + void Reset(const librevenge::RVNGPropertyList &properties); + void AddSpan(const libvisio::VSDOpenSpanOutputElement *pSpan); + void AddText(const std::wstring &text, const TextExtentsTp &extents); + + double GetCurrentTextExtentInch() const; + const std::string &GetHorizontalAlignment() const; + const std::wstring &GetText() const; + const TextExtentsTp &GetTextExtents() const; + const SpanMarksTp &GetSpanMarks() const; + const libvisio::VSDOpenSpanOutputElement *GetSpanFromIndex(unsigned int index); + +private: + double m_margLeftInch; ///< left margin in inches + double m_margRightInch; ///< right margin in inches + double m_margTopInch; ///<< top margin in inches + double m_margBottomInch; ///< bottom margin in inches + + double m_lineHeight; ///< line height (relative or absolute in inches) + bool m_isLineHeightRel; ///< indicates if m_lineHeight is a relative value + std::string m_horizontalAlignment; ///< horizontal alignment of the text + std::wstring m_text; ///< paragraph text + TextExtentsTp m_textExtents; ///< contiguous line of extents of characters within the paragraph + SpanMarksTp m_spanMarks; ///< maps indices of characters within paragraph text to Span elements +}; + + +#endif // _SVG_PARAGRAPH_H_INCLUDED_ diff --git a/src/lib/preprocess/SvgSpan.cpp b/src/lib/preprocess/SvgSpan.cpp new file mode 100644 index 0000000..5e33261 --- /dev/null +++ b/src/lib/preprocess/SvgSpan.cpp @@ -0,0 +1,74 @@ +#include "SvgSpan.h" + +using namespace libvisio; +using namespace std; + + +SpanC::SpanC( + const VSDOpenSpanOutputElement *pSpan, const wstring &text, double offsetInch, + double widthInch, double lastCharWidthInch, bool hasNewLine) + : m_pSpan(pSpan), + m_text(text), + m_offsetInch(offsetInch), + m_widthInch(widthInch), + m_lastCharWidthInch(lastCharWidthInch), + m_rowTotalWidthInch(-1.0), + m_endsWithNewLine(hasNewLine) +{ +} + +const VSDOpenSpanOutputElement *SpanC::GetSpan() const +{ + return m_pSpan; +} + +const wstring SpanC::GetText(bool exclTrailWhiteSpace) const +{ + unsigned int textLen = m_text.size(); + + if (m_endsWithNewLine && exclTrailWhiteSpace + && textLen > 0 && iswspace(m_text[textLen - 1])) + { + return m_text.substr(0, textLen - 1); + } + + return m_text; +} + +double SpanC::GetSpanOffsetInch() const +{ + return m_offsetInch; +} + +double SpanC::GetSpanWidthInch(bool exclTrailWhiteSpace) const +{ + unsigned int textLen = m_text.size(); + + if (m_endsWithNewLine && exclTrailWhiteSpace + && textLen > 0 && iswspace(m_text[textLen - 1])) + { + return m_widthInch - m_lastCharWidthInch; + } + + return m_widthInch; +} + +double SpanC::GetRowTotalWidthInch() const +{ + return m_rowTotalWidthInch; +} + +bool SpanC::EndsWithNewLine() const +{ + return m_endsWithNewLine; +} + +void SpanC::SetNewLine() +{ + m_endsWithNewLine = true; +} + +void SpanC::SetRowTotalWidthInch(double width) +{ + m_rowTotalWidthInch = width; +} diff --git a/src/lib/preprocess/SvgSpan.h b/src/lib/preprocess/SvgSpan.h new file mode 100644 index 0000000..f8de70d --- /dev/null +++ b/src/lib/preprocess/SvgSpan.h @@ -0,0 +1,51 @@ +#ifndef _SVG_SPAN_H_INCLUDED_ +#define _SVG_SPAN_H_INCLUDED_ + + +#include +#include + + +namespace libvisio +{ +class VSDOpenSpanOutputElement; +} + + +/** + * Represents an SVG text span and stores its properties required for laying out/wrapping the text + * within the parent text box. + */ +class SpanC +{ +public: + SpanC( + const libvisio::VSDOpenSpanOutputElement *pSpan, const std::wstring &text, + double offsetInch, double widthInch, double lastCharWidthInch, bool hasNewLine); + + const libvisio::VSDOpenSpanOutputElement *GetSpan() const; + const std::wstring GetText(bool exclTrailWhiteSpace = true) const; + double GetSpanOffsetInch() const; + double GetSpanWidthInch(bool exclTrailWhiteSpace = true) const; + double GetRowTotalWidthInch() const; + bool EndsWithNewLine() const; + void SetNewLine(); + void SetRowTotalWidthInch(double width); + +private: + const libvisio::VSDOpenSpanOutputElement *m_pSpan; ///< the original span element + std::wstring m_text; ///< span text (UTF-8 encoded) + double m_offsetInch; ///< horizontal offset of the span counted from the end of the previous span + double m_widthInch; ///< width of the span in inches + double m_lastCharWidthInch; ///< width in inches of the last span character + double m_rowTotalWidthInch; ///< width of all spans in inches on the same row of text + bool m_endsWithNewLine; ///< indicates a new line after this span +}; + + +typedef std::vector SpansTp; +typedef SpansTp::iterator SpansItTp; +typedef SpansTp::const_iterator SpansContItTp; + + +#endif // _SVG_SPAN_H_INCLUDED_H_ diff --git a/src/lib/preprocess/SvgTextObject.cpp b/src/lib/preprocess/SvgTextObject.cpp new file mode 100644 index 0000000..784f709 --- /dev/null +++ b/src/lib/preprocess/SvgTextObject.cpp @@ -0,0 +1,473 @@ +#include "librevenge/SvgConstants.h" +#include "SvgDC.h" +#include "SvgFont.h" +#include "SvgTextObject.h" +#include "SvgUtils.h" +#include "VSDOutputElementList.h" + +#include + +using namespace librevenge; +using namespace libvisio; +using namespace std; +using namespace svgconstants; + + +SvgTextObjectC::SvgTextObjectC(VSDStartTextObjectOutputElement *pStartText, SvgDC &dc) + : m_dc(dc), + m_pCurrentFont(NULL), + m_stage(PS_NONE), + m_firstLineOffsetYInch(0.0), + m_currentLineYInch(0.0), + m_textLeftBoundInch(DBL_MAX), + m_textRightBoundInch(DBL_MIN) +{ + RVNGPropertyList &props = pStartText->GetPropertyList(); + + m_origXInch = props[PROP_SVG_X] ? SvgUtilsC::GetInchValue(*props[PROP_SVG_X]) : 0.0; + m_origYInch = props[PROP_SVG_Y] ? SvgUtilsC::GetInchValue(*props[PROP_SVG_Y]) : 0.0; + m_widthInch = props[PROP_SVG_WIDTH] ? SvgUtilsC::GetInchValue(*props[PROP_SVG_WIDTH]) : 0.0; + m_heightInch = props[PROP_SVG_HEIGHT] ? SvgUtilsC::GetInchValue(*props[PROP_SVG_HEIGHT]) : 0.0; + + m_padLeftInch = props[PROP_FO_PADDING_LEFT] + ? SvgUtilsC::GetInchValue(*props[PROP_FO_PADDING_LEFT]) : 0.0; + + m_padRightInch = props[PROP_FO_PADDING_RIGHT] + ? SvgUtilsC::GetInchValue(*props[PROP_FO_PADDING_RIGHT]) : 0.0; + + m_padTopInch = props[PROP_FO_PADDING_TOP] + ? SvgUtilsC::GetInchValue(*props[PROP_FO_PADDING_TOP]) : 0.0; + + m_padBottomInch = props[PROP_FO_PADDING_BOTTOM] + ? SvgUtilsC::GetInchValue(*props[PROP_FO_PADDING_BOTTOM]) : 0.0; + + m_verticalAlign = props[PROP_DRAW_TEXTAREA_VERTICAL_ALIGN] ? + props[PROP_DRAW_TEXTAREA_VERTICAL_ALIGN]->getStr().cstr() : PVAL_DRAW_VERTICAL_ALIGN_MIDDLE; + + m_bkgColor = props[PROP_FO_BACKGROUND_COLOR] + ? props[PROP_FO_BACKGROUND_COLOR]->getStr().cstr() : ""; +} + +SvgDC &SvgTextObjectC::GetDC() +{ + return m_dc; +} + +SvgTextObjectC::ProcessingStageE SvgTextObjectC::GetProcessingStage() const +{ + return m_stage; +} + +void SvgTextObjectC::StartCalculationStage() +{ + if (m_stage == PS_NONE) + { + m_stage = PS_CALCULATE; + m_currentLineYInch = 0.0; + m_firstLineOffsetYInch = 0.0; + } +} + +void SvgTextObjectC::StartLayoutStage() +{ + if (m_stage == PS_CALCULATE) + { + m_currentLineYInch = GetCurrentTextBoundTopInch() + m_firstLineOffsetYInch; + m_stage = PS_LAYOUT; + } +} + +double SvgTextObjectC::GetOrigXInch() const +{ + return m_origXInch; +} + +double SvgTextObjectC::GetOrigYInch() const +{ + return m_origYInch; +} + +double SvgTextObjectC::GetWidthInch() const +{ + return m_widthInch; +} + +double SvgTextObjectC::GetHeightInch() const +{ + return m_heightInch; +} + +double SvgTextObjectC::GetPaddingLeftInch() const +{ + return m_padLeftInch; +} + +double SvgTextObjectC::GetPaddingRightInch() const +{ + return m_padRightInch; +} + +double SvgTextObjectC::GetPaddingTopInch() const +{ + return m_padTopInch; +} + +double SvgTextObjectC::GetPaddingBottomInch() const +{ + return m_padBottomInch; +} + +double SvgTextObjectC::GetCurrentLineYInch() const +{ + return m_currentLineYInch; +} + +double SvgTextObjectC::RowOfTextAdded() +{ + double fontHeightInch = GetCurrentFont()->GetHeightInch(); + double lineHeightInch = m_currentParagraph.GetLineHeightInch(fontHeightInch); + + m_currentLineYInch += lineHeightInch; + + if (m_stage == PS_CALCULATE && m_firstLineOffsetYInch == 0.0) + { + m_firstLineOffsetYInch = fontHeightInch + * (m_dc.GetFontBaseLineHeightRatio(GetCurrentFont()->GetId())) + + (lineHeightInch - fontHeightInch) / 2.0; + } + + return m_currentLineYInch; +} + +void SvgTextObjectC::OpenParagraph(const VSDOpenParagraphOutputElement *pOpenParagraph) +{ + m_currentParagraph.Reset(pOpenParagraph); +} + +void SvgTextObjectC::OpenUnorderedList(const VSDOpenUnorderedListLevelOutputElement *pList) +{ + const RVNGPropertyList &props = pList->GetPropertyList(); + + m_currentBulletChar = props[PROP_TEXT_BULLET_CHAR] + ? SvgUtilsC::StrToWstr(props[PROP_TEXT_BULLET_CHAR]->getStr().cstr()) : L""; +} + +void SvgTextObjectC::OpenListElement(const VSDOpenListElementOutputElement *pOpenListElem) +{ + m_currentParagraph.Reset(pOpenListElem); +} + +void SvgTextObjectC::OpenSpan(const VSDOpenSpanOutputElement *pOpenSpan) +{ + const RVNGPropertyList &props = pOpenSpan->GetPropertyList(); + + double fontSizeInch = props[PROP_FO_FONT_SIZE] + ? SvgUtilsC::GetInchValue(*props[PROP_FO_FONT_SIZE]) : 12.0 * POINT_SIZE_INCH; + + unsigned int fontWeight = + props[PROP_FO_FONT_WEIGHT] + && strcmp(props[PROP_FO_FONT_WEIGHT]->getStr().cstr(), PVAL_FO_FONT_WEIGHT_BOLD) == 0 + ? FW_BOLD : FW_NORMAL; + + bool isItalic = + props[PROP_FO_FONT_STYLE] + && strcmp(props[PROP_FO_FONT_STYLE]->getStr().cstr(), PVAL_FO_FONT_STYLE_ITALIC) == 0; + + string fontName = props[PROP_STYLE_FONT_NAME] + ? props[PROP_STYLE_FONT_NAME]->getStr().cstr() : "Arial"; + + m_pCurrentFont = m_dc.GetFont(fontSizeInch, fontWeight, isItalic, fontName); + m_currentParagraph.AddSpan(pOpenSpan); + + if (props[PROP_FO_COLOR]) + { + // addresses the issue when the text box background is wrongly indicated as 'filled' + if (!m_bkgColor.empty() && m_bkgColor == props[PROP_FO_COLOR]->getStr().cstr()) + { + m_bkgColor.clear(); // do not fill the background as (some) text has the same color + } + } +} + +void SvgTextObjectC::InsertText(const VSDInsertTextOutputElement *pInsertText) +{ + string text = pInsertText->GetText().cstr(); + wstring wtext = SvgUtilsC::StrToWstr(text); + double curExtent = m_currentParagraph.GetCurrentTextExtentInch(); + ParagraphC::TextExtentsTp textExtents; + + m_dc.GetTextPartialExtents(wtext, GetCurrentFont()->GetId(), curExtent, textExtents); + m_currentParagraph.AddText(wtext, textExtents); +} + +void SvgTextObjectC::InsertTab(const VSDInsertTabOutputElement *pInsertTab) +{ + ParagraphC::TextExtentsTp textExtents; + double curExtent = m_currentParagraph.GetCurrentTextExtentInch(); + + textExtents.push_back(m_dc.GetTabCharExtent(GetCurrentFont()->GetId(), curExtent)); + m_currentParagraph.AddText(L"\t", textExtents); +} + +const string &SvgTextObjectC::GetCurrentHorizontalAlignment() const +{ + return m_currentParagraph.GetHorizontalAlignment(); +} + +const SvgFontC *SvgTextObjectC::GetCurrentFont() const +{ + if (m_pCurrentFont == NULL) + { + m_pCurrentFont = m_dc.GetFont(12.0 * POINT_SIZE_INCH, FW_NORMAL, false, "Arial"); // default font if not yet defined + } + + return m_pCurrentFont; +} + +const string &SvgTextObjectC::GetBackgroundColor() const +{ + return m_bkgColor; +} + +void SvgTextObjectC::SetCurrentTextLeftBoundInch(double x) +{ + if (x < m_textLeftBoundInch) + { + m_textLeftBoundInch = x; + } +} + +void SvgTextObjectC::SetCurrentTextRightBoundInch(double x) +{ + if (x > m_textRightBoundInch) + { + m_textRightBoundInch = x; + } +} + +void SvgTextObjectC::GetCurrentTextBoundsInch( + double &x1, double &y1, double &x2, double &y2) const +{ + x1 = m_textLeftBoundInch; + x2 = m_textRightBoundInch; + y1 = GetCurrentTextBoundTopInch(); + y2 = y1 + GetCurrentLineYInch(); +} + +double SvgTextObjectC::GetCurrentTextBoundTopInch() const +{ + if (m_verticalAlign.compare(PVAL_DRAW_VERTICAL_ALIGN_TOP) == 0) + { + return GetOrigYInch() + m_padTopInch; + } + + if (m_verticalAlign.compare(PVAL_DRAW_VERTICAL_ALIGN_BOTTOM) == 0) + { + return GetOrigYInch() + (GetHeightInch() - GetCurrentLineYInch()) - m_padBottomInch; + } + + return GetOrigYInch() + (GetHeightInch() - GetCurrentLineYInch()) / 2.0; +} + +wstring SvgTextObjectC::GetCurrentBulletCharacter() const +{ + return m_currentBulletChar; +} + +/** + * Takes the currently stored paragraph text and its partial extents and breaks it down into spans + * while adding line breaks when needed, so that each span fits in this text box width. + * + * @param spans (out) + * a list to be filled (appended) by calculated spans of text + */ +void SvgTextObjectC::CalculateCurrentParagraphSpans(SpansTp &spans) +{ + const wstring &text = m_currentParagraph.GetText(); + const ParagraphC::TextExtentsTp &extents = m_currentParagraph.GetTextExtents(); + const ParagraphC::SpanMarksTp &spanMarks = m_currentParagraph.GetSpanMarks(); + + if (text.size() > 0) + { + assert(text.size() == extents.size()); + assert(spanMarks.size() > 0); + assert(spanMarks.begin()->first == 0); + + ParagraphC::SpanMarksConstItTp spanMarkIt = spanMarks.begin(); + const VSDOpenSpanOutputElement *pCurOpenSpan = spanMarks.begin()->second; + spanMarkIt++; + + double leftMargInch = m_currentParagraph.GetMarginLeftInch(); + double rightMargInch = m_currentParagraph.GetMarginRightInch(); + + double rowWidthInch = m_widthInch - m_padLeftInch - m_padRightInch + - leftMargInch - rightMargInch;// + 0.5 / 72.0; // @TODO Revise: 0.5pt correction - empirical only! + + double curSpanIndentInch = leftMargInch; // relative indent of the current (not-yet-written) span + double curRowLeftExtentInch = 0; // left edge of the bounding box mapped to 1-D contiguous coordinate + double lastNonTabCharExtentInch = 0; // extent of the last row character that was not tab + int lastWhiteIdx = -1; // white-space or hyphen break candidate index + bool whiteSpanEnd = false; // the last row span ended with white space + wstring curSpanText; + + for (unsigned int i = 0, charCnt = text.size(); i < charCnt; i++) + { + bool addCurChar = true; + int createNewSpan = 0; // <1 - do not create, 1 - without new line, >1 - with new line + const VSDOpenSpanOutputElement *pNextOpenSpan = pCurOpenSpan; + + if (spanMarkIt != spanMarks.end() && spanMarkIt->first == i) // new span becomes active + { + if (curSpanText.size() > 0) // the text is not empty (shall always be true) + { + createNewSpan = 1; + } + + pNextOpenSpan = spanMarkIt->second; + spanMarkIt++; // advance to the next span to match in subsequent iterations + } + + if (text[i] == L'\t') + { + addCurChar = false; // do not add tabs in the resulting text + + if (curSpanText.size() == 0) // row-leading tab + { + curSpanIndentInch = leftMargInch + extents[i] - lastNonTabCharExtentInch; + } + else // row-interleaved tab + { + createNewSpan = 1; + } + } + else if (text[i] == 0x2028) // unicode line separator + { + addCurChar = false; + createNewSpan = 2; + curRowLeftExtentInch = extents[i]; + lastNonTabCharExtentInch = extents[i]; + } + else // non-tab character + { + lastNonTabCharExtentInch = extents[i]; + } + + if (createNewSpan > 0) + { + AddSpan( + spans, pCurOpenSpan, curSpanText, curSpanIndentInch, i - curSpanText.size(), i - 1, + createNewSpan > 1); + + curSpanIndentInch = + text[i] == L'\t' ? leftMargInch + extents[i] - extents[i - 1] : leftMargInch; + + wchar_t lastChar = curSpanText[curSpanText.size() - 1]; + whiteSpanEnd = !!iswspace(lastChar) || lastChar == L'-'; + lastWhiteIdx = -1; + curSpanText.clear(); + } + + if (curSpanText.size() > 0 // must not be empty, i.e. at least one character per row even it does not fit entirely + && extents[i] > curRowLeftExtentInch + rowWidthInch) // available space exceeded by the current character + { + unsigned int fitCharCnt; // number of characters to fit in the rest of the available space + + if (iswspace(text[i])) // a white space exceeded the boundary + { + fitCharCnt = curSpanText.size(); + addCurChar = false; // do not include the white space in the new row + } + else if (lastWhiteIdx >= 0) + { + fitCharCnt = lastWhiteIdx + 1; // up to last white-space character including + } + else if (whiteSpanEnd) // break after white space end of the last span + { + fitCharCnt = 0; + } + else // no text-break candidate exist + { + fitCharCnt = curSpanText.size(); // up to last fitting character + } + + if (fitCharCnt == 0) // the row breaks right after the previous span + { + curRowLeftExtentInch = extents[i - curSpanText.size() - 1]; // update the row start position + spans[spans.size() - 1].SetNewLine(); // update the previous span + } + else + { + AddSpan( + spans, pCurOpenSpan, curSpanText.substr(0, fitCharCnt), curSpanIndentInch, + i - curSpanText.size(), i - curSpanText.size() + fitCharCnt - 1, true); + + curRowLeftExtentInch = extents[i - curSpanText.size() + fitCharCnt - 1]; // adjust the new beginning of the current row + curSpanText = curSpanText.substr(fitCharCnt); // go on with the remaining part + + if (lastWhiteIdx >= 0) + { + lastWhiteIdx -= fitCharCnt; + } + } + + curSpanIndentInch = leftMargInch; + lastNonTabCharExtentInch = extents[i]; + } + else // current character still fits in + { + if (iswspace(text[i]) || text[i] == L'-') + { + lastWhiteIdx = curSpanText.size(); + } + } + + if (addCurChar) + { + curSpanText += text[i]; + } + + pCurOpenSpan = pNextOpenSpan; + } + + if (curSpanText.size() > 0) // the rest of the text not yet included + { + AddSpan( + spans, pCurOpenSpan, curSpanText, curSpanIndentInch, + text.size() - curSpanText.size(), text.size() - 1, true); + } + + if (spans.size() > 0) + { + spans[spans.size() - 1].SetNewLine(); // assure the line break after the last span + + double totalWidthInch = 0.0; // set sum of spans widths to first spans on each row (used for hor. alignment) + + for (int i = spans.size() - 1; i >= 0; i--) + { + totalWidthInch += spans[i].GetSpanOffsetInch() + spans[i].GetSpanWidthInch(); + + if (i == 0 || spans[i - 1].EndsWithNewLine()) + { + spans[i].SetRowTotalWidthInch(totalWidthInch); + totalWidthInch = 0.0; + } + } + } + } +} + +void SvgTextObjectC::AddSpan( + SpansTp &spans, const VSDOpenSpanOutputElement *pSvgSpan, const wstring &text, + double offsetInch, int firstCharIdx, int lastCharIdx, bool endWithNewLine) +{ + const ParagraphC::TextExtentsTp &extents = m_currentParagraph.GetTextExtents(); + double leftExtentInch = firstCharIdx > 0 ? extents[firstCharIdx - 1] : 0; + double rightExtentInch = extents[lastCharIdx]; + + double lastCharExtentInch = + lastCharIdx > 0 ? extents[lastCharIdx] - extents[lastCharIdx - 1] : extents[lastCharIdx]; + + spans.push_back(SpanC( + pSvgSpan, text, offsetInch, rightExtentInch - leftExtentInch, lastCharExtentInch, + endWithNewLine)); +} diff --git a/src/lib/preprocess/SvgTextObject.h b/src/lib/preprocess/SvgTextObject.h new file mode 100644 index 0000000..3fccfeb --- /dev/null +++ b/src/lib/preprocess/SvgTextObject.h @@ -0,0 +1,109 @@ +#ifndef _SVG_TEXT_OBJECT_H_INCLUDED_ +#define _SVG_TEXT_OBJECT_H_INCLUDED_ + + +#include "SvgParagraph.h" +#include "SvgSpan.h" + +#include + + +namespace libvisio +{ +class VSDStartTextObjectOutputElement; +class VSDOpenParagraphOutputElement; +class VSDOpenUnorderedListLevelOutputElement; +class VSDOpenListElementOutputElement; +class VSDOpenSpanOutputElement; +class VSDInsertTextOutputElement; +class VSDInsertTabOutputElement; +} + +class SvgDC; +class SvgFontC; + + +/** + * Represents an SVG text object and provides methods that reflect its particular structure and + * calculate additional properties of contained elements that are required for proper rendering. + */ +class SvgTextObjectC +{ +public: + enum ProcessingStageE + { + PS_NONE, + PS_CALCULATE, + PS_LAYOUT + }; + + SvgTextObjectC(libvisio::VSDStartTextObjectOutputElement *pStartText, SvgDC &dc); + + SvgDC &GetDC(); + ProcessingStageE GetProcessingStage() const; + + double GetOrigXInch() const; + double GetOrigYInch() const; + double GetWidthInch() const; + double GetHeightInch() const; + double GetPaddingLeftInch() const; + double GetPaddingRightInch() const; + double GetPaddingTopInch() const; + double GetPaddingBottomInch() const; + + double GetCurrentLineYInch() const; + double RowOfTextAdded(); + + const std::string &GetCurrentHorizontalAlignment() const; + const SvgFontC *GetCurrentFont() const; + + const std::string &GetBackgroundColor() const; + void SetCurrentTextLeftBoundInch(double x); + void SetCurrentTextRightBoundInch(double x); + void GetCurrentTextBoundsInch(double &x1, double &y1, double &x2, double &y2) const; + double GetCurrentTextBoundTopInch() const; + std::wstring GetCurrentBulletCharacter() const; + + void OpenParagraph(const libvisio::VSDOpenParagraphOutputElement *pOpenParagraph); + void OpenUnorderedList(const libvisio::VSDOpenUnorderedListLevelOutputElement *pList); + void OpenListElement(const libvisio::VSDOpenListElementOutputElement *pOpenListElem); + void OpenSpan(const libvisio::VSDOpenSpanOutputElement *pOpenSpan); + void InsertText(const libvisio::VSDInsertTextOutputElement *pInsertText); + void InsertTab(const libvisio::VSDInsertTabOutputElement *pInsertTab); + void CalculateCurrentParagraphSpans(SpansTp &spans); + + void StartCalculationStage(); + void StartLayoutStage(); + +private: + void AddSpan( + SpansTp &spans, const libvisio::VSDOpenSpanOutputElement *pSvgSpan, const std::wstring &text, + double offsetInch, int firstCharIdx, int lastCharIdx, bool endWithNewLine); + + double m_origXInch; ///< X coordinate of the top left corner in inches + double m_origYInch; ///< Y coordinate of the top left corner in inches + double m_widthInch; ///< box width in inches + double m_heightInch; ///< box height in inches + double m_padLeftInch; ///< left padding in inches + double m_padRightInch; ///< right padding in inches + double m_padTopInch; ///<< top padding in inches + double m_padBottomInch; ///< bottom padding in inches + + SvgDC &m_dc; ///< device context to use for text size-related calculations + mutable const SvgFontC *m_pCurrentFont; ///< font that applies to the currently processed text + + ProcessingStageE m_stage; ///< stage of processing of textbox contents + double m_firstLineOffsetYInch; ///< vertical offset of the first (base)line in the text box + double m_currentLineYInch; ///< vertical position of the current line of text + std::string m_verticalAlign; ///< vertical alignment of the text box + + double m_textLeftBoundInch; ///< the left bound of the left-most line of the text + double m_textRightBoundInch; ///< the right bound of the right-most line of the text + std::string m_bkgColor; ///< text box background color or empty string for transparent background + + ParagraphC m_currentParagraph; ///< paragraph currently being processed + std::wstring m_currentBulletChar; ///< the character to use for the subsequest list item bullet +}; + + +#endif // _SVG_TEXT_OBJECT_H_INCLUDED_ diff --git a/src/lib/preprocess/SvgUtils.cpp b/src/lib/preprocess/SvgUtils.cpp new file mode 100644 index 0000000..1883903 --- /dev/null +++ b/src/lib/preprocess/SvgUtils.cpp @@ -0,0 +1,61 @@ +#include "SvgUtils.h" +#include + +using namespace librevenge; +using namespace std; + + +int SvgUtilsC::Round(double x) +{ + if (x < 0.0) + { + return static_cast(x - 0.5); + } + + return static_cast(x + 0.5); +} + +double SvgUtilsC::GetInchValue(const RVNGProperty &prop) +{ + double value = prop.getDouble(); + + switch (prop.getUnit()) + { + case RVNG_GENERIC: // assume inch + case RVNG_INCH: + return value; + + case RVNG_POINT: + return value / 72.0; + + case RVNG_TWIP: + return value / 1440.0; + + default : + return value; // non-conversible + } +} + +wstring SvgUtilsC::StrToWstr(const string &str) +{ + int wCharCnt = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, NULL, 0); + wchar_t *wstr = new wchar_t[wCharCnt]; + + MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, wstr, wCharCnt); + wstring outWstr = wstr; + + delete wstr; + return outWstr; +} + +string SvgUtilsC::WstrToStr(const wstring &wstr) +{ + int charCnt = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, NULL, 0, NULL, NULL); + char *str = new char[charCnt]; + + WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, &str[0], charCnt, NULL, NULL); + string outStr = str; + + delete str; + return outStr; +} diff --git a/src/lib/preprocess/SvgUtils.h b/src/lib/preprocess/SvgUtils.h new file mode 100644 index 0000000..cde2096 --- /dev/null +++ b/src/lib/preprocess/SvgUtils.h @@ -0,0 +1,28 @@ +#ifndef _SVG_UTILS_H_INCLUDED_ +#define _SVG_UTILS_H_INCLUDED_ + + +#include +#include + + +namespace librevenge +{ +class RVNGProperty; +} + + +class SvgUtilsC +{ +public: + static int Round(double x); + static double GetInchValue(const librevenge::RVNGProperty &prop); + static std::wstring StrToWstr(const std::string &str); + static std::string WstrToStr(const std::wstring &wstr); + +private: + SvgUtilsC(); +}; + + +#endif // _SVG_UTILS_H_INCLUDED_ diff --git a/src/lib/preprocess/VsdElementListPreprocessor.cpp b/src/lib/preprocess/VsdElementListPreprocessor.cpp new file mode 100644 index 0000000..d40d148 --- /dev/null +++ b/src/lib/preprocess/VsdElementListPreprocessor.cpp @@ -0,0 +1,521 @@ +#include "librevenge/SvgConstants.h" +#include "SvgDC.h" +#include "SvgUtils.h" +#include "SvgTextObject.h" +#include "VsdElementListPreprocessor.h" + +#include + +using namespace librevenge; +using namespace libvisio; +using namespace std; +using namespace svgconstants; + + +void VsdElementListPreprocessorC::Process( + const ElementListTp &elements, ElementListTp &outElements) +{ + SvgDC &dc = GetSystemDC(); + + for (ElementListConstItTp it = elements.begin(); it != elements.end(); it++) + { + ElementListConstItTp oldIt = it; + it = ProcessTextObject(it, elements.end(), outElements, dc); + + if (it == oldIt) + { + outElements.push_back((*it)->clone()); + } + else if (it == elements.end()) + { + break; + } + } +} + +VsdElementListPreprocessorC::ElementListConstItTp VsdElementListPreprocessorC::ProcessTextObject( + ElementListConstItTp startIt, ElementListConstItTp endIt, ElementListTp &outElements, SvgDC &dc) +{ + ElementListConstItTp it = startIt; + + VSDStartTextObjectOutputElement *pStartText = + dynamic_cast(*it); + + if (pStartText == NULL) // not StartTextObject element + { + return it; + } + + pStartText = static_cast((*it)->clone()); // work with the copy! + SvgTextObjectC textObjContext(pStartText, dc); + it++; // move to the next element + + // 1st pass + ElementListTp elementsPass1; + textObjContext.StartCalculationStage(); + it = ProcessTextObjectOnePass(it, endIt, elementsPass1, &textObjContext); + + if (!textObjContext.GetBackgroundColor().empty()) + { + double x1, y1, x2, y2; + textObjContext.GetCurrentTextBoundsInch(x1, y1, x2, y2); + + outElements.push_back(CreateStyleOutput(textObjContext.GetBackgroundColor())); + outElements.push_back(CreateRectPathOutput(x1, y1, x2, y2)); + } + + outElements.push_back(pStartText); + + // 2nd pass + textObjContext.StartLayoutStage(); + + ProcessTextObjectOnePass( + elementsPass1.begin(), elementsPass1.end(), outElements, &textObjContext); + + return it; +} + +VsdElementListPreprocessorC::ElementListConstItTp +VsdElementListPreprocessorC::ProcessTextObjectOnePass( + ElementListConstItTp startIt, ElementListConstItTp endIt, + ElementListTp &outElements, SvgTextObjectC *pTextObject) +{ + assert(pTextObject); + + ElementListConstItTp it; + + for (it = startIt; it != endIt; it++) + { + VSDEndTextObjectOutputElement *pEndTextObject = + dynamic_cast(*it); + + if (pEndTextObject != NULL) // EndTextObject element found + { + outElements.push_back(pEndTextObject->clone()); // make a copy! + break; + } + + ElementListConstItTp oldIt = it; + it = ProcessParagraph(it, endIt, outElements, pTextObject); + + if (it == oldIt) + { + it = ProcessUnorderedList(it, endIt, outElements, pTextObject); + + if (it == oldIt) + { + outElements.push_back((*it)->clone()); + } + } + + if (it == endIt) + { + break; + } + } + + return it; +} + +VsdElementListPreprocessorC::ElementListConstItTp VsdElementListPreprocessorC::ProcessParagraph( + ElementListConstItTp startIt, ElementListConstItTp endIt, ElementListTp &outElements, + SvgTextObjectC *pTextObject) +{ + assert(pTextObject); + + ElementListConstItTp it = startIt; + + VSDOpenParagraphOutputElement *pOpenParagraph = + dynamic_cast(*it); + + if (pOpenParagraph == NULL) + { + return it; // not OpenParagraph element + } + + pOpenParagraph = static_cast((*it)->clone()); // work with the copy! + outElements.push_back(pOpenParagraph); + + SvgTextObjectC::ProcessingStageE stage = pTextObject->GetProcessingStage(); + + if (stage == SvgTextObjectC::PS_CALCULATE) + { + pTextObject->OpenParagraph(pOpenParagraph); + } + + it++; // move to the next element + + VSDCloseParagraphOutputElement *pCloseParagraph = NULL; + + for (; it != endIt; it++) + { + pCloseParagraph = dynamic_cast(*it); + + if (pCloseParagraph != NULL) + { + break; + } + + ElementListConstItTp oldIt = it; + it = ProcessSpan(it, endIt, outElements, pTextObject); + + if (it == oldIt) + { + outElements.push_back((*it)->clone()); + } + else if (it == endIt) + { + break; + } + } + + if (stage == SvgTextObjectC::PS_CALCULATE) + { + SpansTp spans; + pTextObject->CalculateCurrentParagraphSpans(spans); + GenerateSpans(spans, outElements, pTextObject); + } + + if (pCloseParagraph != NULL) + { + outElements.push_back(pCloseParagraph->clone()); // make a copy! + } + + return it; +} + +VsdElementListPreprocessorC::ElementListConstItTp VsdElementListPreprocessorC::ProcessUnorderedList( + ElementListConstItTp startIt, ElementListConstItTp endIt, + ElementListTp &outElements, SvgTextObjectC *pTextObject) +{ + assert(pTextObject); + + ElementListConstItTp it = startIt; + + VSDOpenUnorderedListLevelOutputElement *pOpenList = + dynamic_cast(*it); + + if (pOpenList == NULL) + { + return it; // not OpenUnorderedList element + } + + pOpenList = static_cast((*it)->clone()); // work with the copy! + outElements.push_back(pOpenList); + + if (pTextObject->GetProcessingStage() == SvgTextObjectC::PS_CALCULATE) + { + pTextObject->OpenUnorderedList(pOpenList); + } + + it++; // move to the next element + + for (; it != endIt; it++) + { + VSDCloseUnorderedListLevelOutputElement *pCloseList = + dynamic_cast(*it); + + if (pCloseList != NULL) + { + outElements.push_back(pCloseList->clone()); // make a copy! + break; + } + + ElementListConstItTp oldIt = it; + it = ProcessListElement(it, endIt, outElements, pTextObject); + + if (it == oldIt) + { + outElements.push_back((*it)->clone()); + } + else if (it == endIt) + { + break; + } + } + + return it; +} + +VsdElementListPreprocessorC::ElementListConstItTp VsdElementListPreprocessorC::ProcessListElement( + ElementListConstItTp startIt, ElementListConstItTp endIt, + ElementListTp &outElements, SvgTextObjectC *pTextObject) +{ + assert(pTextObject); + + ElementListConstItTp it = startIt; + + VSDOpenListElementOutputElement *pOpenListElem = + dynamic_cast(*it); + + if (pOpenListElem == NULL) + { + return it; // not OpenListElement element + } + + pOpenListElem = static_cast((*it)->clone()); // work with the copy! + outElements.push_back(pOpenListElem); + + SvgTextObjectC::ProcessingStageE stage = pTextObject->GetProcessingStage(); + + if (stage == SvgTextObjectC::PS_CALCULATE) + { + pTextObject->OpenListElement(pOpenListElem); + } + + it++; // move to the next element + + VSDCloseListElementOutputElement *pCloseListElem = NULL; + + for (; it != endIt; it++) + { + pCloseListElem = dynamic_cast(*it); + + if (pCloseListElem != NULL) + { + break; + } + + ElementListConstItTp oldIt = it; + it = ProcessSpan(it, endIt, outElements, pTextObject); + + if (it == oldIt) + { + outElements.push_back((*it)->clone()); + } + else if (it == endIt) + { + break; + } + } + + if (stage == SvgTextObjectC::PS_CALCULATE) + { + SpansTp spans; + pTextObject->CalculateCurrentParagraphSpans(spans); + + if (spans.size() > 0) // add a span for the list item bullet + { + SpanC span = spans[0]; + double spanOffsetInch = span.GetSpanOffsetInch(); + RVNGPropertyList &props = pOpenListElem->GetPropertyList(); + + double textIndentInch = props[PROP_FO_TEXT_INDENT] + ? SvgUtilsC::GetInchValue(*props[PROP_FO_TEXT_INDENT]) : -spanOffsetInch; + + if (textIndentInch >= 0) + { + textIndentInch = -spanOffsetInch; + } + + spans.insert( + spans.begin(), + SpanC(span.GetSpan(), pTextObject->GetCurrentBulletCharacter(), + spanOffsetInch + textIndentInch, -textIndentInch, -textIndentInch, false)); + } + + GenerateSpans(spans, outElements, pTextObject); + } + + if (pCloseListElem != NULL) + { + outElements.push_back(pCloseListElem->clone()); // make a copy! + } + + return it; +} + +VsdElementListPreprocessorC::ElementListConstItTp VsdElementListPreprocessorC::ProcessSpan( + ElementListConstItTp startIt, ElementListConstItTp endIt, + ElementListTp &outElements, SvgTextObjectC *pTextObject) +{ + assert(pTextObject); + + ElementListConstItTp it = startIt; + VSDOpenSpanOutputElement *pOpenSpan = dynamic_cast(*it); + + if (pOpenSpan == NULL) + { + return it; // not OpenSpan element + } + + SvgTextObjectC::ProcessingStageE stage = pTextObject->GetProcessingStage(); + it++; // move to the next element + + if (stage == SvgTextObjectC::PS_CALCULATE) + { + pTextObject->OpenSpan(pOpenSpan); + + for (; it != endIt; it++) + { + VSDCloseSpanOutputElement *pCloseSpan = dynamic_cast(*it); + + if (pCloseSpan != NULL) + { + break; + } + + VSDInsertTextOutputElement *pInsertText = dynamic_cast(*it); + + if (pInsertText != NULL) + { + pTextObject->InsertText(pInsertText); + } + + VSDInsertTabOutputElement *pInsertTab = dynamic_cast(*it); + + if (pInsertTab != NULL) + { + pTextObject->InsertTab(pInsertTab); + } + } + } + else if (stage == SvgTextObjectC::PS_LAYOUT) + { + RVNGPropertyList &props = pOpenSpan->GetPropertyList(); + double posYInch = SvgUtilsC::GetInchValue(*props[PROP_SVG_Y]); + + posYInch += pTextObject->GetCurrentLineYInch(); + props.insert(PROP_SVG_Y, posYInch, RVNG_INCH); + + outElements.push_back(pOpenSpan->clone()); + + for (; it != endIt; it++) + { + VSDCloseSpanOutputElement *pCloseSpan = dynamic_cast(*it); + + if (pCloseSpan != NULL) + { + outElements.push_back((*it)->clone()); + break; + } + + VSDInsertTextOutputElement *pInsertText = dynamic_cast(*it); + + if (pInsertText != NULL) + { + outElements.push_back((*it)->clone()); + } + } + + } + + return it; +} + +void VsdElementListPreprocessorC::GenerateSpans( + const SpansTp &spans, ElementListTp &outElements, SvgTextObjectC *pTextObject) +{ + if (spans.size() == 0) + { + // @TODO Revise: More accurate results are sometimes achieved when empty paragraph + // is ignorred in the result document, rather than if the a new line is added + pTextObject->RowOfTextAdded(); + } + else + { + double posXInch = 0.0; + bool wasNewRow = true; + + for (SpansContItTp spanIt = spans.begin(); spanIt != spans.end(); spanIt++) + { + VSDOpenSpanOutputElement *pOpenSpan = static_cast( + const_cast(spanIt->GetSpan())->clone()); // const_cast needed as clone() is not const (but should be) + + if (wasNewRow) + { + wasNewRow = false; + string horAlign = pTextObject->GetCurrentHorizontalAlignment(); + + if (horAlign.compare(PVAL_FO_TEXT_ALIGN_LEFT) == 0) + { + posXInch = pTextObject->GetOrigXInch() + pTextObject->GetPaddingLeftInch(); + } + else if (horAlign.compare(PVAL_FO_TEXT_ALIGN_RIGHT) == 0) + { + posXInch = pTextObject->GetOrigXInch() + pTextObject->GetWidthInch() + - pTextObject->GetPaddingRightInch() - spanIt->GetRowTotalWidthInch(); + } + else // PVAL_FO_TEXT_ALIGN_CENTER + { + posXInch = pTextObject->GetOrigXInch() + + (pTextObject->GetWidthInch() - spanIt->GetRowTotalWidthInch()) / 2.0; + } + + posXInch += spanIt->GetSpanOffsetInch(); + } + + pTextObject->SetCurrentTextLeftBoundInch(posXInch); + pTextObject->SetCurrentTextRightBoundInch(posXInch + spanIt->GetSpanWidthInch()); + + RVNGPropertyList &props = pOpenSpan->GetPropertyList(); + props.insert(PROP_SVG_X, posXInch, RVNG_INCH); + props.insert(PROP_SVG_Y, pTextObject->GetCurrentLineYInch(), RVNG_INCH); // relative to the textbox top + + outElements.push_back(pOpenSpan); + + outElements.push_back( + new VSDInsertTextOutputElement(SvgUtilsC::WstrToStr(spanIt->GetText()).c_str())); + + outElements.push_back(new VSDCloseSpanOutputElement()); + + if (spanIt->EndsWithNewLine()) + { + wasNewRow = true; + pTextObject->RowOfTextAdded(); + } + else + { + posXInch += spanIt->GetSpanWidthInch(); + } + } + } +} + +VSDStyleOutputElement *VsdElementListPreprocessorC::CreateStyleOutput(const string &fillColor) +{ + RVNGPropertyList props; + props.insert(PROP_SVG_STROKE_WIDTH, 0.0, RVNG_INCH); + props.insert(PROP_DRAW_FILL, "solid"); + props.insert(PROP_DRAW_FILL_COLOR, fillColor.c_str()); + return new VSDStyleOutputElement(props); +} + +VSDPathOutputElement *VsdElementListPreprocessorC::CreateRectPathOutput( + double x1Inch, double y1Inch, double x2Inch, double y2Inch) +{ + const char *TAG_SVG_D = "svg:d"; + RVNGPropertyListVector actions; + + RVNGPropertyList actionM; + actionM.insert(PROP_LIBREV_ACTION, "M"); + actionM.insert(PROP_SVG_X, x1Inch, RVNG_INCH); + actionM.insert(PROP_SVG_Y, y1Inch, RVNG_INCH); + actions.append(actionM); + + RVNGPropertyList actionL1; + actionL1.insert(PROP_LIBREV_ACTION, "L"); + actionL1.insert(PROP_SVG_X, x2Inch, RVNG_INCH); + actionL1.insert(PROP_SVG_Y, y1Inch, RVNG_INCH); + actions.append(actionL1); + + RVNGPropertyList actionL2; + actionL2.insert(PROP_LIBREV_ACTION, "L"); + actionL2.insert(PROP_SVG_X, x2Inch, RVNG_INCH); + actionL2.insert(PROP_SVG_Y, y2Inch, RVNG_INCH); + actions.append(actionL2); + + RVNGPropertyList actionL3; + actionL3.insert(PROP_LIBREV_ACTION, "L"); + actionL3.insert(PROP_SVG_X, x1Inch, RVNG_INCH); + actionL3.insert(PROP_SVG_Y, y2Inch, RVNG_INCH); + actions.append(actionL3); + + RVNGPropertyList actionZ; + actionZ.insert(PROP_LIBREV_ACTION, "Z"); + actions.append(actionZ); + + RVNGPropertyList props; + props.insert(TAG_SVG_D, actions); + return new VSDPathOutputElement(props); +} diff --git a/src/lib/preprocess/VsdElementListPreprocessor.h b/src/lib/preprocess/VsdElementListPreprocessor.h new file mode 100644 index 0000000..b07d8c0 --- /dev/null +++ b/src/lib/preprocess/VsdElementListPreprocessor.h @@ -0,0 +1,71 @@ +#ifndef _VSD_ELEMENT_LIST_PREPROCESSOR_H_INCLUDED_ +#define _VSD_ELEMENT_LIST_PREPROCESSOR_H_INCLUDED_ + +#include "SvgSpan.h" +#include "VSDOutputElementList.h" + +#include + + +class SvgDC; +class SvgTextObjectC; + +namespace libvisio +{ +class VSDStyleOutputElement; +class VSDPathOutputElement; +} + +/** + * Pre-processes a specified list of VSD output elements before they are rendered to the output + * SVG document. The purpose is to calculate additional data related to the elements and make it + * available to the renderer, so that the resulting SVG is the closest possible match of the + * original. In particular, text aligment and wrapping within parent text boxes are addressed here. + * Preprocessing produces a new element list, which in comparison to the input one may add new + * elements, remove existing ones, or add or modify properties of other elements. + */ +class VsdElementListPreprocessorC +{ +public: + typedef std::vector ElementListTp; + typedef ElementListTp::iterator ElementListItTp; + typedef ElementListTp::const_iterator ElementListConstItTp; + + void Process(const ElementListTp &elements, ElementListTp &outElements); + +private: + ElementListConstItTp ProcessTextObject( + ElementListConstItTp startIt, ElementListConstItTp endIt, ElementListTp &outElements, + SvgDC &dc); + + ElementListConstItTp ProcessTextObjectOnePass( + ElementListConstItTp startIt, ElementListConstItTp endIt, + ElementListTp &outElements, SvgTextObjectC *pTextObject); + + ElementListConstItTp ProcessParagraph( + ElementListConstItTp startIt, ElementListConstItTp endIt, + ElementListTp &outElements, SvgTextObjectC *pTextObject); + + ElementListConstItTp ProcessUnorderedList( + ElementListConstItTp startIt, ElementListConstItTp endIt, + ElementListTp &outElements, SvgTextObjectC *pTextObject); + + ElementListConstItTp ProcessListElement( + ElementListConstItTp startIt, ElementListConstItTp endIt, + ElementListTp &outElements, SvgTextObjectC *pTextObject); + + ElementListConstItTp ProcessSpan( + ElementListConstItTp startIt, ElementListConstItTp endIt, + ElementListTp &outElements, SvgTextObjectC *pTextObject); + + void GenerateSpans( + const SpansTp &spans, ElementListTp &outElements, SvgTextObjectC *pTextObject); + + libvisio::VSDStyleOutputElement *CreateStyleOutput(const std::string &fillColor); + + libvisio::VSDPathOutputElement *CreateRectPathOutput( + double x1Inch, double y1Inch, double x2Inch, double y2Inch); +}; + + +#endif // _VSD_ELEMENT_LIST_PREPROCESSOR_H_INCLUDED_ diff --git a/src/lib/preprocess/windows/WindowsSvgDC.cpp b/src/lib/preprocess/windows/WindowsSvgDC.cpp new file mode 100644 index 0000000..0532239 --- /dev/null +++ b/src/lib/preprocess/windows/WindowsSvgDC.cpp @@ -0,0 +1,188 @@ +#ifdef WIN32 // Windows API-dependent unit + + +#include "SvgUtils.h" +#include "WindowsSvgDC.h" +#include "WindowsSvgFont.h" + +using namespace std; + + +SvgDC &GetSystemDC() +{ + static WindowsSvgDC dc; + return dc; +} + + +WindowsSvgDC::WindowsSvgDC(HDC hDC) + : m_pixelPerInchX(-1), + m_pixelPerInchY(-1) +{ + if (hDC == NULL) + { + m_ownDC = true; + m_hDC = GetWindowDC(NULL); + } + else + { + m_ownDC = false; + } +} + +WindowsSvgDC::~WindowsSvgDC() +{ + if (m_ownDC) + { + ReleaseDC(NULL, m_hDC); + } + + for (FontsItTp it = m_fonts.begin(); it != m_fonts.end(); it++) + { + delete *it; + } +} + +double WindowsSvgDC::GetConversionPrecisionFactor() const +{ + return 1000.0; +} + +double WindowsSvgDC::PixelsToInches(unsigned int pixels, bool horizontal) const +{ + return static_cast(pixels) + / (horizontal ? GetPixelsPerInchX() : GetPixelsPerInchY()); +} + +unsigned int WindowsSvgDC::InchesToPixels(double inches, bool horizontal) const +{ + return static_cast(SvgUtilsC::Round(inches + * (horizontal ? GetPixelsPerInchX() : GetPixelsPerInchY()))); +} + +int WindowsSvgDC::GetPixelsPerInchX() const +{ + if (m_pixelPerInchX < 0) + { + m_pixelPerInchX = + SvgUtilsC::Round(GetConversionPrecisionFactor() * GetDeviceCaps(m_hDC, LOGPIXELSX)); + } + + return m_pixelPerInchX; +} + +int WindowsSvgDC::GetPixelsPerInchY() const +{ + if (m_pixelPerInchY < 0) + { + m_pixelPerInchY = + SvgUtilsC::Round(GetConversionPrecisionFactor() * GetDeviceCaps(m_hDC, LOGPIXELSY)); + } + + return m_pixelPerInchY; +} + +const SvgFontC *WindowsSvgDC::GetFont( + double heightInch, unsigned int weight, bool isItalic, const string &name) const +{ + WindowsSvgFontC font(0, heightInch, weight, isItalic, name); + FontsItTp it = m_fonts.find(&font); + WindowsSvgFontC *pFont; + + if (it == m_fonts.end()) + { + pFont = new WindowsSvgFontC(GetNextFontId(), heightInch, weight, isItalic, name); + pFont->Create(GetPixelsPerInchX()); + m_fonts.insert(pFont); + m_fontIds[pFont->GetId()] = pFont; + } + else + { + pFont = *it; + } + + return pFont; +} + +double WindowsSvgDC::GetFontBaseLineHeightRatio(unsigned int fontId) const +{ + TEXTMETRIC tm; + GetFontMetric(fontId, tm); + return static_cast(tm.tmAscent) / tm.tmHeight; +} + +bool WindowsSvgDC::GetFontMetric(unsigned int fontId, TEXTMETRIC &metric) const +{ + FontIdsItTp it = m_fontIds.find(fontId); + + if (it == m_fontIds.end()) + { + return false; + } + + HGDIOBJ oldObj = SelectObject(m_hDC, *it->second); + BOOL result = GetTextMetrics(m_hDC, &metric); + SelectObject(m_hDC, oldObj); + + return result == TRUE; +} + +double WindowsSvgDC::GetTextPartialExtents( + const wstring &text, unsigned int fontId, double offsetInch, vector &extentsInch) const +{ + unsigned int textLen = text.length(); + + if (textLen > 0) + { + FontIdsItTp it = m_fontIds.find(fontId); + + if (it == m_fontIds.end()) + { + return -1.0; + } + + HGDIOBJ oldObj = SelectObject(m_hDC, *it->second); + + vector extentsPx; + extentsPx.resize(textLen); + + SIZE strSize; + GetTextExtentExPointW(m_hDC, text.c_str(), textLen, 0, NULL, &extentsPx.front(), &strSize); + + extentsInch.resize(textLen); + + for (unsigned int i = 0; i < textLen; i++) + { + extentsInch[i] = PixelsToInches(extentsPx[i], true) + offsetInch; + } + + SelectObject(m_hDC, oldObj); + return offsetInch + PixelsToInches(strSize.cx, true); + } + + return offsetInch; +} + +double WindowsSvgDC::GetTabCharExtent(unsigned int fontId, double offsetInch) const +{ + FontIdsItTp it = m_fontIds.find(fontId); + + if (it == m_fontIds.end()) + { + return -1.0; + } + + HGDIOBJ oldObj = SelectObject(m_hDC, *it->second); + DWORD extent = GetTabbedTextExtentW(m_hDC, L"\t", 1, 0, NULL); + SelectObject(m_hDC, oldObj); + return PixelsToInches(extent & 0xFFFF, true) + offsetInch; +} + +unsigned int WindowsSvgDC::GetNextFontId() const +{ + static unsigned int id = 0; + return ++id; +} + + +#endif // Windows API-dependent unit diff --git a/src/lib/preprocess/windows/WindowsSvgDC.h b/src/lib/preprocess/windows/WindowsSvgDC.h new file mode 100644 index 0000000..5b61a56 --- /dev/null +++ b/src/lib/preprocess/windows/WindowsSvgDC.h @@ -0,0 +1,61 @@ +#ifdef WIN32 // Windows API-dependent unit + + +#ifndef _WINDOWS_SVG_DC_H_INCLUDED_ +#define _WINDOWS_SVG_DC_H_INCLUDED_ + +#include "SvgDC.h" +#include "SvgFont.h" + +#include +#include +#include + + +class WindowsSvgFontC; + +class WindowsSvgDC : public SvgDC +{ +public: + WindowsSvgDC(HDC hDC = NULL); + ~WindowsSvgDC(); + + const SvgFontC *GetFont( + double heightInch, unsigned int weight, bool isItalic, const std::string &name) const; + + double GetFontBaseLineHeightRatio(unsigned int fontId) const; + + double GetTextPartialExtents( + const std::wstring &text, unsigned int fontId, double offsetInch, + std::vector &extentsInch) const; + + double GetTabCharExtent(unsigned int fontId, double offsetInch) const; + +private: + typedef std::set FontsTp; + typedef FontsTp::iterator FontsItTp; + + typedef std::map FontIdsTp; + typedef FontIdsTp::iterator FontIdsItTp; + + bool GetFontMetric(unsigned int fontId, TEXTMETRIC &metric) const; + int GetPixelsPerInchX() const; + int GetPixelsPerInchY() const; + double PixelsToInches(unsigned int pixels, bool horizontal) const; + unsigned int InchesToPixels(double inches, bool horizontal) const; + double GetConversionPrecisionFactor() const; + unsigned int GetNextFontId() const; + + HDC m_hDC; + bool m_ownDC; + + mutable int m_pixelPerInchX; + mutable int m_pixelPerInchY; + mutable FontsTp m_fonts; + mutable FontIdsTp m_fontIds; +}; + + +#endif // _WINDOWS_SVG_DC_H_INCLUDED_ + +#endif WIN32 // Windows API-dependent unit diff --git a/src/lib/preprocess/windows/WindowsSvgFont.cpp b/src/lib/preprocess/windows/WindowsSvgFont.cpp new file mode 100644 index 0000000..fb586f3 --- /dev/null +++ b/src/lib/preprocess/windows/WindowsSvgFont.cpp @@ -0,0 +1,54 @@ +#ifdef WIN32 // Windows API-dependent unit + + +#include "WindowsSvgFont.h" + +using namespace std; + + +WindowsSvgFontC::WindowsSvgFontC( + unsigned int id, double heightInch, unsigned int weight, bool isItalic, const string &faceName +) + : SvgFontC(id, heightInch, weight, isItalic, faceName), + m_hFont(NULL) +{ +} + +WindowsSvgFontC::~WindowsSvgFontC() +{ + DeleteObject(m_hFont); +} + +bool WindowsSvgFontC::Create(double pixelsPerInch) +{ + LOGFONT logFont; + logFont.lfHeight = -static_cast(m_heightInch * pixelsPerInch); + logFont.lfWidth = 0; + logFont.lfEscapement = 0; + logFont.lfOrientation = 0; + logFont.lfWeight = m_weight; + logFont.lfItalic = m_isItalic; + logFont.lfUnderline = false; + logFont.lfStrikeOut = false; + logFont.lfCharSet = DEFAULT_CHARSET; + logFont.lfOutPrecision = OUT_DEFAULT_PRECIS; + logFont.lfClipPrecision = CLIP_DEFAULT_PRECIS; + logFont.lfQuality = DEFAULT_QUALITY; + logFont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; + strncpy(logFont.lfFaceName, m_faceName.c_str(), LF_FACESIZE - 1); + m_hFont = CreateFontIndirect(&logFont); + return m_hFont != NULL; +} + +WindowsSvgFontC::operator HFONT() +{ + return m_hFont; +} + +WindowsSvgFontC::operator HFONT() const +{ + return m_hFont; +} + + +#endif WIN32 // Windows API-dependent unit diff --git a/src/lib/preprocess/windows/WindowsSvgFont.h b/src/lib/preprocess/windows/WindowsSvgFont.h new file mode 100644 index 0000000..8b83e61 --- /dev/null +++ b/src/lib/preprocess/windows/WindowsSvgFont.h @@ -0,0 +1,30 @@ +#ifdef WIN32 // Windows API-dependent unit + +#ifndef _WINDOWS_SVG_FONT_H_INCLUDED_ +#define _WINDOWS_SVG_FONT_H_INCLUDED_ + +#include "SvgFont.h" +#include + + +class WindowsSvgFontC : public SvgFontC +{ +public: + WindowsSvgFontC( + unsigned int id, double heightInch, unsigned int weight, bool isItalic, + const std::string &faceName); + + ~WindowsSvgFontC(); + + bool Create(double pixelsPerInch); + operator HFONT(); + operator HFONT() const; + +private: + HFONT m_hFont; +}; + + +#endif // _WINDOWS_SVG_FONT_H_INCLUDED_ + +#endif WIN32 // Windows API-dependent unit -- cgit v1.2.3