summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJi???­ Posp?­??il <jiri.pospisil.certicon@zf.com>2016-12-14 14:46:02 +0100
committerFridrich Štrba <fridrich.strba@bluewin.ch>2016-12-15 17:44:00 +0100
commit80720108347665f6051795f3fbb7d8d0f262513a (patch)
tree06332ec164adfaa10edfcd3e55f9ec946522cae1 /src
parentef4ae42ce738fc2450bfc5dc205a65003c04455b (diff)
SVG generation fixeszf.com
- text wrapping - text decoration - line connectors - path filling - unordered item lists
Diffstat (limited to 'src')
-rw-r--r--src/conv/svg/vsd2xhtml.cpp276
-rw-r--r--src/lib/VSDContentCollector.cpp494
-rw-r--r--src/lib/VSDContentCollector.h7
-rw-r--r--src/lib/VSDMetaData.cpp2
-rw-r--r--src/lib/VSDOutputElementList.cpp282
-rw-r--r--src/lib/VSDOutputElementList.h317
-rw-r--r--src/lib/preprocess/SvgDC.cpp1
-rw-r--r--src/lib/preprocess/SvgDC.h32
-rw-r--r--src/lib/preprocess/SvgFont.cpp61
-rw-r--r--src/lib/preprocess/SvgFont.h38
-rw-r--r--src/lib/preprocess/SvgParagraph.cpp152
-rw-r--r--src/lib/preprocess/SvgParagraph.h69
-rw-r--r--src/lib/preprocess/SvgSpan.cpp74
-rw-r--r--src/lib/preprocess/SvgSpan.h51
-rw-r--r--src/lib/preprocess/SvgTextObject.cpp473
-rw-r--r--src/lib/preprocess/SvgTextObject.h109
-rw-r--r--src/lib/preprocess/SvgUtils.cpp61
-rw-r--r--src/lib/preprocess/SvgUtils.h28
-rw-r--r--src/lib/preprocess/VsdElementListPreprocessor.cpp521
-rw-r--r--src/lib/preprocess/VsdElementListPreprocessor.h71
-rw-r--r--src/lib/preprocess/windows/WindowsSvgDC.cpp188
-rw-r--r--src/lib/preprocess/windows/WindowsSvgDC.h61
-rw-r--r--src/lib/preprocess/windows/WindowsSvgFont.cpp54
-rw-r--r--src/lib/preprocess/windows/WindowsSvgFont.h30
24 files changed, 2939 insertions, 513 deletions
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 <fstream>
#include <iostream>
#include <sstream>
-#include <stdio.h>
-#include <string.h>
+#include <string>
+#include <vector>
+
+#include <boost/algorithm/string.hpp>
#include <librevenge-stream/librevenge-stream.h>
#include <librevenge-generators/librevenge-generators.h>
#include <librevenge/librevenge.h>
#include <libvisio/libvisio.h>
+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<string> &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 << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl;
+ out << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">" << endl;
+ out << "<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:svg=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">" << endl;
+ out << "<body>" << endl;
+ out << "<?import namespace=\"svg\" urn=\"http://www.w3.org/2000/svg\"?>" << endl;
+}
+
+void WriteXhtmlFooter(ostream &out)
+{
+ out << "</body>" << endl;
+ out << "</html>" << endl;
+}
+
+void WriteSvg(const char *svgStr, ostream &out)
+{
+ out << "<!-- " << endl;
+ out << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>" << endl;
+ out << "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"";
+ out << " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">" << endl;
+ 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 <https://bugs.documentfoundation.org/>.\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 <https://bugs.documentfoundation.org/>." << 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<string> 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 << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
- std::cout << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">" << std::endl;
- std::cout << "<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:svg=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">" << std::endl;
- std::cout << "<body>" << std::endl;
- std::cout << "<?import namespace=\"svg\" urn=\"http://www.w3.org/2000/svg\"?>" << std::endl;
+ if (!in.is_open())
+ {
+ cerr << "ERROR: file not found" << endl;
+ wasError = true;
+ continue;
+ }
+ }
- for (unsigned k = 0; k<output.size(); ++k)
- {
- if (k>0)
- std::cout << "<hr/>\n";
+ RVNGFileStream input(fileNames[i].c_str());
- std::cout << "<!-- \n";
- std::cout << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n";
- std::cout << "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"";
- std::cout << " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n";
- 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 << "</body>" << std::endl;
- std::cout << "</html>" << 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 << "<hr/>" << 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 <cassert>
#include <string.h> // for memcpy
#include <set>
#include <stack>
+#include <sstream>
+#include <iomanip>
+
#include <boost/spirit/include/classic.hpp>
#include <unicode/ucnv.h>
#include <unicode/utf8.h>
@@ -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 <sstream>
#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<librevenge::RVNGPropertyList> tmpPath;
- if (m_fillStyle.pattern && !m_currentFillGeometry.empty())
+ PathActionS(const string &name, double x, double y)
+ : name(name),
+ x(x),
+ y(y)
+ {}
+};
+
+typedef deque<PathActionS> PathActionsTp;
+
+void FlushPath(const PathActionsTp &actionsIn, vector<RVNGPropertyList> &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<RVNGPropertyList> &actionsIn, vector<RVNGPropertyList> &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<RVNGPropertyList> &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<RVNGPropertyList> 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<RVNGPropertyList> 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<librevenge::RVNGPropertyList> &segmentVector,
librevenge::RVNGPropertyListVector &path, double rounding);
+ void AddPath(
+ const std::vector<librevenge::RVNGPropertyList> &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<size_t>(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<VSDOutputElement *>::const_iterator iter = m_elements.begin(); iter != m_elements.end(); ++iter)
+ VsdElementListPreprocessorC velp;
+ vector<VSDOutputElement *> outElements;
+ velp.Process(m_elements, outElements);
+
+ for (std::vector<VSDOutputElement *>::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 <string>
+#include <vector>
+
+
+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<double> &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 <string>
+
+
+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 <assert.h>
+
+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 <map>
+#include <string>
+#include <vector>
+
+
+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<double> TextExtentsTp;
+ typedef std::map<int, const libvisio::VSDOpenSpanOutputElement *> 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 <string>
+#include <vector>
+
+
+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<SpanC> 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 <assert.h>
+
+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 <string>
+
+
+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 <librevenge/librevenge.h>
+
+using namespace librevenge;
+using namespace std;
+
+
+int SvgUtilsC::Round(double x)
+{
+ if (x < 0.0)
+ {
+ return static_cast<int>(x - 0.5);
+ }
+
+ return static_cast<int>(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 <string>
+#include <Windows.h>
+
+
+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 <assert.h>
+
+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<VSDStartTextObjectOutputElement *>(*it);
+
+ if (pStartText == NULL) // not StartTextObject element
+ {
+ return it;
+ }
+
+ pStartText = static_cast<VSDStartTextObjectOutputElement *>((*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<VSDEndTextObjectOutputElement *>(*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<VSDOpenParagraphOutputElement *>(*it);
+
+ if (pOpenParagraph == NULL)
+ {
+ return it; // not OpenParagraph element
+ }
+
+ pOpenParagraph = static_cast<VSDOpenParagraphOutputElement *>((*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<VSDCloseParagraphOutputElement *>(*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<VSDOpenUnorderedListLevelOutputElement *>(*it);
+
+ if (pOpenList == NULL)
+ {
+ return it; // not OpenUnorderedList element
+ }
+
+ pOpenList = static_cast<VSDOpenUnorderedListLevelOutputElement *>((*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<VSDCloseUnorderedListLevelOutputElement *>(*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<VSDOpenListElementOutputElement *>(*it);
+
+ if (pOpenListElem == NULL)
+ {
+ return it; // not OpenListElement element
+ }
+
+ pOpenListElem = static_cast<VSDOpenListElementOutputElement *>((*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<VSDCloseListElementOutputElement *>(*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<VSDOpenSpanOutputElement *>(*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<VSDCloseSpanOutputElement *>(*it);
+
+ if (pCloseSpan != NULL)
+ {
+ break;
+ }
+
+ VSDInsertTextOutputElement *pInsertText = dynamic_cast<VSDInsertTextOutputElement *>(*it);
+
+ if (pInsertText != NULL)
+ {
+ pTextObject->InsertText(pInsertText);
+ }
+
+ VSDInsertTabOutputElement *pInsertTab = dynamic_cast<VSDInsertTabOutputElement *>(*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<VSDCloseSpanOutputElement *>(*it);
+
+ if (pCloseSpan != NULL)
+ {
+ outElements.push_back((*it)->clone());
+ break;
+ }
+
+ VSDInsertTextOutputElement *pInsertText = dynamic_cast<VSDInsertTextOutputElement *>(*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<VSDOpenSpanOutputElement *>(
+ const_cast<VSDOpenSpanOutputElement *>(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 <vector>
+
+
+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<libvisio::VSDOutputElement *> 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<double>(pixels)
+ / (horizontal ? GetPixelsPerInchX() : GetPixelsPerInchY());
+}
+
+unsigned int WindowsSvgDC::InchesToPixels(double inches, bool horizontal) const
+{
+ return static_cast<unsigned int>(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<double>(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<double> &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<int> 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 <set>
+#include <map>
+#include <Windows.h>
+
+
+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<double> &extentsInch) const;
+
+ double GetTabCharExtent(unsigned int fontId, double offsetInch) const;
+
+private:
+ typedef std::set<WindowsSvgFontC *, SvgFontCompareS> FontsTp;
+ typedef FontsTp::iterator FontsItTp;
+
+ typedef std::map<unsigned int, WindowsSvgFontC *> 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<int>(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 <Windows.h>
+
+
+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