summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Tardon <dtardon@redhat.com>2016-02-24 07:53:13 +0100
committerDavid Tardon <dtardon@redhat.com>2016-02-24 14:21:50 +0100
commit458e616d7c67af5377bbdeddd710b7124268eb67 (patch)
tree50a1f48eb25e46e81484d4fe8fe24a0c4f9419e4
parent2cf59007fd920d8b477a2e19d43ffb5a9262a788 (diff)
copy parser toolkit from libmwaw
-rw-r--r--src/lib/Makefile.am51
-rw-r--r--src/lib/SW602Cell.cpp932
-rw-r--r--src/lib/SW602Cell.h431
-rw-r--r--src/lib/SW602Chart.cpp657
-rw-r--r--src/lib/SW602Chart.h234
-rw-r--r--src/lib/SW602Debug.cpp229
-rw-r--r--src/lib/SW602Debug.h219
-rw-r--r--src/lib/SW602Entry.h180
-rw-r--r--src/lib/SW602Font.cpp340
-rw-r--r--src/lib/SW602Font.h534
-rw-r--r--src/lib/SW602GraphicDecoder.cpp250
-rw-r--r--src/lib/SW602GraphicDecoder.h59
-rw-r--r--src/lib/SW602GraphicEncoder.cpp311
-rw-r--r--src/lib/SW602GraphicEncoder.h115
-rw-r--r--src/lib/SW602GraphicListener.cpp1752
-rw-r--r--src/lib/SW602GraphicListener.h284
-rw-r--r--src/lib/SW602GraphicShape.cpp648
-rw-r--r--src/lib/SW602GraphicShape.h190
-rw-r--r--src/lib/SW602GraphicStyle.cpp652
-rw-r--r--src/lib/SW602GraphicStyle.h439
-rw-r--r--src/lib/SW602List.cpp415
-rw-r--r--src/lib/SW602List.h202
-rw-r--r--src/lib/SW602Listener.h201
-rw-r--r--src/lib/SW602PageSpan.cpp414
-rw-r--r--src/lib/SW602PageSpan.h291
-rw-r--r--src/lib/SW602Paragraph.cpp491
-rw-r--r--src/lib/SW602Paragraph.h170
-rw-r--r--src/lib/SW602Parser.cpp116
-rw-r--r--src/lib/SW602Parser.h229
-rw-r--r--src/lib/SW602Position.h282
-rw-r--r--src/lib/SW602Printer.cpp225
-rw-r--r--src/lib/SW602Printer.h91
-rw-r--r--src/lib/SW602PropertyHandler.cpp381
-rw-r--r--src/lib/SW602PropertyHandler.h97
-rw-r--r--src/lib/SW602Section.cpp139
-rw-r--r--src/lib/SW602Section.h136
-rw-r--r--src/lib/SW602SpreadsheetDecoder.cpp244
-rw-r--r--src/lib/SW602SpreadsheetDecoder.h59
-rw-r--r--src/lib/SW602SpreadsheetEncoder.cpp464
-rw-r--r--src/lib/SW602SpreadsheetEncoder.h170
-rw-r--r--src/lib/SW602SpreadsheetListener.cpp2064
-rw-r--r--src/lib/SW602SpreadsheetListener.h289
-rw-r--r--src/lib/SW602SubDocument.cpp56
-rw-r--r--src/lib/SW602SubDocument.h71
-rw-r--r--src/lib/SW602Table.cpp531
-rw-r--r--src/lib/SW602Table.h186
-rw-r--r--src/lib/SW602TextListener.cpp1940
-rw-r--r--src/lib/SW602TextListener.h268
-rw-r--r--src/lib/SW602Types.cpp559
-rw-r--r--src/lib/SW602Types.h1184
-rw-r--r--src/lib/libsw602_utils.h16
51 files changed, 20487 insertions, 1 deletions
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index 29a00a2..40960dc 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -6,15 +6,64 @@ endif
lib_LTLIBRARIES = libsw602-@SW602_MAJOR_VERSION@.@SW602_MINOR_VERSION@.la
-AM_CXXFLAGS = -I$(top_srcdir)/inc $(REVENGE_CFLAGS) $(REVENGE_STREAM_CFLAGS) $(DEBUG_CXXFLAGS) -DLIBTEXT602_BUILD
+AM_CXXFLAGS = -I$(top_srcdir)/inc $(REVENGE_CFLAGS) $(REVENGE_STREAM_CFLAGS) $(DEBUG_CXXFLAGS) -DLIBTEXT602_BUILD -DDEBUG_WITH_FILES
libsw602_@SW602_MAJOR_VERSION@_@SW602_MINOR_VERSION@_la_LIBADD = $(REVENGE_LIBS) @LIBSW602_WIN32_RESOURCE@
libsw602_@SW602_MAJOR_VERSION@_@SW602_MINOR_VERSION@_la_DEPENDENCIES = @LIBSW602_WIN32_RESOURCE@
libsw602_@SW602_MAJOR_VERSION@_@SW602_MINOR_VERSION@_la_LDFLAGS = $(version_info) -export-dynamic -no-undefined
libsw602_@SW602_MAJOR_VERSION@_@SW602_MINOR_VERSION@_la_SOURCES = \
Software602Document.cpp \
+ SW602Cell.cpp \
+ SW602Cell.h \
+ SW602Chart.cpp \
+ SW602Chart.h \
+ SW602Debug.cpp \
+ SW602Debug.h \
+ SW602Entry.h \
+ SW602Font.cpp \
+ SW602Font.h \
+ SW602GraphicDecoder.cpp \
+ SW602GraphicDecoder.h \
+ SW602GraphicEncoder.cpp \
+ SW602GraphicEncoder.h \
+ SW602GraphicListener.cpp \
+ SW602GraphicListener.h \
+ SW602GraphicShape.cpp \
+ SW602GraphicShape.h \
+ SW602GraphicStyle.cpp \
+ SW602GraphicStyle.h \
+ SW602List.cpp \
+ SW602List.h \
+ SW602Listener.h \
SW602MemoryStream.cpp \
SW602MemoryStream.h \
+ SW602PageSpan.cpp \
+ SW602PageSpan.h \
+ SW602Paragraph.cpp \
+ SW602Paragraph.h \
+ SW602Parser.cpp \
+ SW602Parser.h \
+ SW602Position.h \
+ SW602Printer.cpp \
+ SW602Printer.h \
+ SW602PropertyHandler.cpp \
+ SW602PropertyHandler.h \
+ SW602Section.cpp \
+ SW602Section.h \
+ SW602SpreadsheetDecoder.cpp \
+ SW602SpreadsheetDecoder.h \
+ SW602SpreadsheetEncoder.cpp \
+ SW602SpreadsheetEncoder.h \
+ SW602SpreadsheetListener.cpp \
+ SW602SpreadsheetListener.h \
+ SW602SubDocument.cpp \
+ SW602SubDocument.h \
+ SW602Table.cpp \
+ SW602Table.h \
+ SW602TextListener.cpp \
+ SW602TextListener.h \
+ SW602Types.cpp \
+ SW602Types.h \
WinText602Header.cpp \
WinText602Header.h \
WinText602Parser.cpp \
diff --git a/src/lib/SW602Cell.cpp b/src/lib/SW602Cell.cpp
new file mode 100644
index 0000000..0c38426
--- /dev/null
+++ b/src/lib/SW602Cell.cpp
@@ -0,0 +1,932 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/** \file SW602Cell.cxx
+ * Implements SW602Cell (cell content and format)
+ */
+
+#include "SW602Cell.h"
+
+#include <cmath>
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+
+#include <time.h>
+
+#include <librevenge/librevenge.h>
+
+#include "SW602Listener.h"
+
+namespace libsw602
+{
+
+////////////////////////////////////////////////////////////
+// SW602Cell::Format
+////////////////////////////////////////////////////////////
+std::string SW602Cell::Format::getValueType() const
+{
+ switch (m_format)
+ {
+ case F_NUMBER:
+ if (m_numberFormat==F_NUMBER_CURRENCY) return "currency";
+ if (m_numberFormat==F_NUMBER_PERCENT) return "percent";
+ if (m_numberFormat==F_NUMBER_SCIENTIFIC) return "scientific";
+ return "float";
+ case F_BOOLEAN:
+ return "boolean";
+ case F_DATE:
+ return "date";
+ case F_TIME:
+ return "time";
+ case F_TEXT:
+ case F_UNKNOWN:
+ default:
+ break;
+ }
+ return "float";
+}
+
+bool SW602Cell::Format::getNumberingProperties(librevenge::RVNGPropertyList &propList) const
+{
+ librevenge::RVNGPropertyListVector pVect;
+ switch (m_format)
+ {
+ case F_BOOLEAN:
+ propList.insert("librevenge:value-type", "boolean");
+ break;
+ case F_NUMBER:
+ if (m_digits>0)
+ propList.insert("number:decimal-places", m_digits);
+ if (m_thousandHasSeparator)
+ propList.insert("number:grouping", true);
+ switch (m_numberFormat)
+ {
+ case F_NUMBER_GENERIC:
+ propList.insert("librevenge:value-type", "number");
+ propList.remove("number:decimal-places");
+ break;
+ case F_NUMBER_SCIENTIFIC:
+ propList.insert("librevenge:value-type", "scientific");
+ break;
+ case F_NUMBER_PERCENT:
+ propList.insert("librevenge:value-type", "percentage");
+ break;
+ case F_NUMBER_DECIMAL:
+ propList.insert("librevenge:value-type", "number");
+ if (m_integerDigits>=0)
+ {
+ propList.insert("number:min-integer-digits", m_integerDigits+1);
+ propList.insert("number:decimal-places", 0);
+ }
+ break;
+ case F_NUMBER_FRACTION:
+ propList.insert("librevenge:value-type", "fraction");
+ propList.insert("number:min-integer-digits", 0);
+ propList.insert("number:min-numerator-digits", m_numeratorDigits>0 ? m_numeratorDigits : 1);
+ propList.insert("number:min-denominator-digits", m_denominatorDigits>0 ? m_denominatorDigits : 1);
+ propList.remove("number:decimal-places");
+ break;
+ case F_NUMBER_CURRENCY:
+ {
+ propList.clear();
+ propList.insert("librevenge:value-type", "currency");
+ librevenge::RVNGPropertyList list;
+ list.insert("librevenge:value-type", "currency-symbol");
+ list.insert("number:language","en");
+ list.insert("number:country","US");
+ list.insert("librevenge:currency",m_currencySymbol.c_str());
+ pVect.append(list);
+
+ list.clear();
+ list.insert("librevenge:value-type", "number");
+ if (m_digits>-1000)
+ list.insert("number:decimal-places", m_digits);
+ pVect.append(list);
+ break;
+ }
+ case F_NUMBER_UNKNOWN:
+ default:
+ return false;
+ }
+ break;
+ case F_DATE:
+ propList.insert("librevenge:value-type", "date");
+ propList.insert("number:automatic-order", "true");
+ if (!convertDTFormat(m_DTFormat.empty() ? "%m/%d/%Y" : m_DTFormat, pVect))
+ return false;
+ break;
+ case F_TIME:
+ propList.insert("librevenge:value-type", "time");
+ propList.insert("number:automatic-order", "true");
+ if (!convertDTFormat(m_DTFormat.empty() ? "%H:%M:%S" : m_DTFormat, pVect))
+ return false;
+ break;
+ case F_TEXT:
+ case F_UNKNOWN:
+ default:
+ return false;
+ }
+ if (pVect.count())
+ propList.insert("librevenge:format", pVect);
+ return true;
+}
+
+bool SW602Cell::Format::convertDTFormat(std::string const &dtFormat, librevenge::RVNGPropertyListVector &propVect)
+{
+ propVect.clear();
+ size_t len=dtFormat.size();
+ std::string text("");
+ librevenge::RVNGPropertyList list;
+ for (size_t c=0; c < len; ++c)
+ {
+ if (dtFormat[c]!='%' || c+1==len)
+ {
+ text+=dtFormat[c];
+ continue;
+ }
+ char ch=dtFormat[++c];
+ if (ch=='%')
+ {
+ text += '%';
+ continue;
+ }
+ if (!text.empty())
+ {
+ list.clear();
+ list.insert("librevenge:value-type", "text");
+ list.insert("librevenge:text", text.c_str());
+ propVect.append(list);
+ text.clear();
+ }
+ list.clear();
+ switch (ch)
+ {
+ case 'Y':
+ list.insert("number:style", "long");
+ case 'y':
+ list.insert("librevenge:value-type", "year");
+ propVect.append(list);
+ break;
+ case 'B':
+ list.insert("number:style", "long");
+ case 'b':
+ case 'h':
+ list.insert("librevenge:value-type", "month");
+ list.insert("number:textual", true);
+ propVect.append(list);
+ break;
+ case 'm':
+ list.insert("librevenge:value-type", "month");
+ propVect.append(list);
+ break;
+ case 'e':
+ list.insert("number:style", "long");
+ // fall-through intended
+ case 'd':
+ list.insert("librevenge:value-type", "day");
+ propVect.append(list);
+ break;
+ case 'A':
+ list.insert("number:style", "long");
+ case 'a':
+ list.insert("librevenge:value-type", "day-of-week");
+ propVect.append(list);
+ break;
+
+ case 'H':
+ list.insert("number:style", "long");
+ // fall-through intended
+ case 'I':
+ list.insert("librevenge:value-type", "hours");
+ propVect.append(list);
+ break;
+ case 'M':
+ list.insert("librevenge:value-type", "minutes");
+ list.insert("number:style", "long");
+ propVect.append(list);
+ break;
+ case 'S':
+ list.insert("librevenge:value-type", "seconds");
+ list.insert("number:style", "long");
+ propVect.append(list);
+ break;
+ case 'p':
+ list.insert("librevenge:value-type", "text");
+ list.insert("librevenge:text", " ");
+ propVect.append(list);
+ list.clear();
+ list.insert("librevenge:value-type", "am-pm");
+ propVect.append(list);
+ break;
+ default:
+ SW602_DEBUG_MSG(("SW602Cell::Format::convertDTFormat: find unimplement command %c(ignored)\n", ch));
+ }
+ }
+ if (!text.empty())
+ {
+ list.clear();
+ list.insert("librevenge:value-type", "text");
+ list.insert("librevenge:text", text.c_str());
+ propVect.append(list);
+ }
+ return propVect.count()!=0;
+}
+
+std::ostream &operator<<(std::ostream &o, SW602Cell::Format const &format)
+{
+ switch (format.m_format)
+ {
+ case SW602Cell::F_BOOLEAN:
+ o << "boolean";
+ break;
+ case SW602Cell::F_TEXT:
+ o << "text";
+ break;
+ case SW602Cell::F_NUMBER:
+ o << "number";
+ switch (format.m_numberFormat)
+ {
+ case SW602Cell::F_NUMBER_GENERIC:
+ break;
+ case SW602Cell::F_NUMBER_DECIMAL:
+ o << "[decimal]";
+ break;
+ case SW602Cell::F_NUMBER_SCIENTIFIC:
+ o << "[exp]";
+ break;
+ case SW602Cell::F_NUMBER_PERCENT:
+ o << "[percent]";
+ break;
+ case SW602Cell::F_NUMBER_CURRENCY:
+ o << "[money=" << format.m_currencySymbol << "]";
+ break;
+ case SW602Cell::F_NUMBER_FRACTION:
+ o << "[fraction]";
+ break;
+ case SW602Cell::F_NUMBER_UNKNOWN:
+ default:
+ SW602_DEBUG_MSG(("SW602Cell::operator<<(Format): find unexpected type\n"));
+ o << "###format,";
+ break;
+ }
+ if (format.m_thousandHasSeparator)
+ o << "[thousandSep]";
+ if (format.m_parenthesesForNegative)
+ o << "[parenthesis<0]";
+ break;
+ case SW602Cell::F_DATE:
+ o << "date[" << format.m_DTFormat << "]";
+ break;
+ case SW602Cell::F_TIME:
+ o << "time[" << format.m_DTFormat << "]";
+ break;
+ case SW602Cell::F_UNKNOWN:
+ default:
+ break; // default
+ }
+ o << ",";
+
+ if (format.m_digits != -1) o << "digits=" << format.m_digits << ",";
+ if (format.m_integerDigits != -1) o << "digits[min]=" << format.m_integerDigits << ",";
+ if (format.m_numeratorDigits != -1) o << "digits[num]=" << format.m_numeratorDigits << ",";
+ if (format.m_denominatorDigits != -1) o << "digits[den]=" << format.m_denominatorDigits << ",";
+ return o;
+}
+
+int SW602Cell::Format::compare(SW602Cell::Format const &cell) const
+{
+ if (m_format<cell.m_format) return 1;
+ if (m_format>cell.m_format) return -1;
+ if (m_numberFormat<cell.m_numberFormat) return 1;
+ if (m_numberFormat>cell.m_numberFormat) return -1;
+ if (m_digits<cell.m_digits) return 1;
+ if (m_digits>cell.m_digits) return -1;
+ if (m_integerDigits<cell.m_integerDigits) return 1;
+ if (m_integerDigits>cell.m_integerDigits) return -1;
+ if (m_numeratorDigits<cell.m_numeratorDigits) return 1;
+ if (m_numeratorDigits>cell.m_numeratorDigits) return -1;
+ if (m_denominatorDigits<cell.m_denominatorDigits) return 1;
+ if (m_denominatorDigits>cell.m_denominatorDigits) return -1;
+ if (m_thousandHasSeparator!=cell.m_thousandHasSeparator) return m_thousandHasSeparator ? -1:1;
+ if (m_parenthesesForNegative!=cell.m_parenthesesForNegative) return m_parenthesesForNegative ? -1:1;
+ if (m_DTFormat<cell.m_DTFormat) return 1;
+ if (m_DTFormat>cell.m_DTFormat) return -1;
+ if (m_currencySymbol<cell.m_currencySymbol) return 1;
+ if (m_currencySymbol>cell.m_currencySymbol) return -1;
+ return 0;
+}
+////////////////////////////////////////////////////////////
+// SW602Cell
+////////////////////////////////////////////////////////////
+void SW602Cell::addTo(librevenge::RVNGPropertyList &propList) const
+{
+ propList.insert("librevenge:column", position()[0]);
+ propList.insert("librevenge:row", position()[1]);
+
+ propList.insert("table:number-columns-spanned", numSpannedCells()[0]);
+ propList.insert("table:number-rows-spanned", numSpannedCells()[1]);
+
+ if (m_fontSet)
+ m_font.addTo(propList);
+ for (size_t c = 0; c < m_bordersList.size(); c++)
+ {
+ switch (c)
+ {
+ case libsw602::Left:
+ m_bordersList[c].addTo(propList, "left");
+ break;
+ case libsw602::Right:
+ m_bordersList[c].addTo(propList, "right");
+ break;
+ case libsw602::Top:
+ m_bordersList[c].addTo(propList, "top");
+ break;
+ case libsw602::Bottom:
+ m_bordersList[c].addTo(propList, "bottom");
+ break;
+ default:
+ SW602_DEBUG_MSG(("SW602Cell::addTo: can not send %d border\n",int(c)));
+ break;
+ }
+ }
+ if (!backgroundColor().isWhite())
+ propList.insert("fo:background-color", backgroundColor().str().c_str());
+ if (isProtected())
+ propList.insert("style:cell-protect","protected");
+ // alignment
+ switch (hAlignment())
+ {
+ case HALIGN_LEFT:
+ propList.insert("fo:text-align", "first");
+ propList.insert("style:text-align-source", "fix");
+ break;
+ case HALIGN_CENTER:
+ propList.insert("fo:text-align", "center");
+ propList.insert("style:text-align-source", "fix");
+ break;
+ case HALIGN_RIGHT:
+ propList.insert("fo:text-align", "end");
+ propList.insert("style:text-align-source", "fix");
+ break;
+ case HALIGN_DEFAULT:
+ break; // default
+ case HALIGN_FULL:
+ default:
+ SW602_DEBUG_MSG(("SW602Cell::addTo: called with unknown halign=%d\n", hAlignment()));
+ }
+ // no padding
+ propList.insert("fo:padding", 0, librevenge::RVNG_POINT);
+ // alignment
+ switch (vAlignment())
+ {
+ case VALIGN_TOP:
+ propList.insert("style:vertical-align", "top");
+ break;
+ case VALIGN_CENTER:
+ propList.insert("style:vertical-align", "middle");
+ break;
+ case VALIGN_BOTTOM:
+ propList.insert("style:vertical-align", "bottom");
+ break;
+ case VALIGN_DEFAULT:
+ break; // default
+ default:
+ SW602_DEBUG_MSG(("SW602Cell::addTo: called with unknown valign=%d\n", vAlignment()));
+ }
+}
+
+std::string SW602Cell::getColumnName(int col)
+{
+ std::stringstream f;
+ f << "[.";
+ if (col > 26) f << char('A'+col/26);
+ f << char('A'+(col%26));
+ f << "]";
+ return f.str();
+}
+
+std::string SW602Cell::getBasicCellName(SW602Vec2i const &pos)
+{
+ std::stringstream f;
+ int col = pos[0];
+ if (col > 26*26)
+ {
+ f << char('A'+col/(26*26));
+ col *= 26*26;
+ }
+ if (col > 26)
+ {
+ f << char('A'+col/26);
+ col %= 26;
+ }
+ f << char('A'+col);
+ f << pos[1]+1;
+ return f.str();
+}
+
+std::string SW602Cell::getCellName(SW602Vec2i const &pos, SW602Vec2b const &absolute)
+{
+ std::stringstream f;
+ f << "[.";
+ if (absolute[1]) f << "$";
+ int col = pos[0];
+ if (col > 26*26)
+ {
+ f << char('A'+col/(26*26));
+ col *= 26*26;
+ }
+ if (col > 26)
+ {
+ f << char('A'+col/26);
+ col %= 26;
+ }
+ f << char('A'+col);
+ if (absolute[0]) f << "$";
+ f << pos[1]+1 << ']';
+ return f.str();
+}
+
+void SW602Cell::setBorders(int wh, SW602Border const &border)
+{
+ int const allBits = libsw602::LeftBit|libsw602::RightBit|libsw602::TopBit|libsw602::BottomBit|libsw602::HMiddleBit|libsw602::VMiddleBit;
+ if (wh & (~allBits))
+ {
+ SW602_DEBUG_MSG(("SW602Cell::setBorders: unknown borders\n"));
+ return;
+ }
+ size_t numData = 4;
+ if (wh & (libsw602::HMiddleBit|libsw602::VMiddleBit))
+ numData=6;
+ if (m_bordersList.size() < numData)
+ {
+ SW602Border emptyBorder;
+ emptyBorder.m_style = SW602Border::None;
+ m_bordersList.resize(numData, emptyBorder);
+ }
+ if (wh & libsw602::LeftBit) m_bordersList[libsw602::Left] = border;
+ if (wh & libsw602::RightBit) m_bordersList[libsw602::Right] = border;
+ if (wh & libsw602::TopBit) m_bordersList[libsw602::Top] = border;
+ if (wh & libsw602::BottomBit) m_bordersList[libsw602::Bottom] = border;
+ if (wh & libsw602::HMiddleBit) m_bordersList[libsw602::HMiddle] = border;
+ if (wh & libsw602::VMiddleBit) m_bordersList[libsw602::VMiddle] = border;
+}
+
+std::ostream &operator<<(std::ostream &o, SW602Cell const &cell)
+{
+ o << SW602Cell::getCellName(cell.m_position, SW602Vec2b(false,false)) << ":";
+ if (cell.numSpannedCells()[0] != 1 || cell.numSpannedCells()[1] != 1)
+ o << "span=[" << cell.numSpannedCells()[0] << "," << cell.numSpannedCells()[1] << "],";
+
+ if (cell.m_protected) o << "protected,";
+ if (cell.m_bdBox.size()[0]>0 || cell.m_bdBox.size()[1]>0)
+ o << "bdBox=" << cell.m_bdBox << ",";
+ if (cell.m_bdSize[0]>0 || cell.m_bdSize[1]>0)
+ o << "bdSize=" << cell.m_bdSize << ",";
+ o << cell.m_format;
+ if (cell.m_fontSet) o << "hasFont,";
+ switch (cell.m_hAlign)
+ {
+ case SW602Cell::HALIGN_LEFT:
+ o << "left,";
+ break;
+ case SW602Cell::HALIGN_CENTER:
+ o << "centered,";
+ break;
+ case SW602Cell::HALIGN_RIGHT:
+ o << "right,";
+ break;
+ case SW602Cell::HALIGN_FULL:
+ o << "full,";
+ break;
+ case SW602Cell::HALIGN_DEFAULT:
+ default:
+ break; // default
+ }
+ switch (cell.m_vAlign)
+ {
+ case SW602Cell::VALIGN_TOP:
+ o << "top,";
+ break;
+ case SW602Cell::VALIGN_CENTER:
+ o << "centered[y],";
+ break;
+ case SW602Cell::VALIGN_BOTTOM:
+ o << "bottom,";
+ break;
+ case SW602Cell::VALIGN_DEFAULT:
+ default:
+ break; // default
+ }
+
+ if (!cell.m_backgroundColor.isWhite())
+ o << "backColor=" << cell.m_backgroundColor << ",";
+ for (size_t i = 0; i < cell.m_bordersList.size(); i++)
+ {
+ if (cell.m_bordersList[i].m_style == SW602Border::None)
+ continue;
+ o << "bord";
+ if (i < 6)
+ {
+ static char const *wh[] = { "L", "R", "T", "B", "MiddleH", "MiddleV" };
+ o << wh[i];
+ }
+ else o << "[#wh=" << i << "]";
+ o << "=" << cell.m_bordersList[i] << ",";
+ }
+ switch (cell.m_extraLine)
+ {
+ case SW602Cell::E_None:
+ break;
+ case SW602Cell::E_Line1:
+ o << "line[TL->RB],";
+ break;
+ case SW602Cell::E_Line2:
+ o << "line[BL->RT],";
+ break;
+ case SW602Cell::E_Cross:
+ o << "line[cross],";
+ break;
+ default:
+ break;
+ }
+ if (cell.m_extraLine!=SW602Cell::E_None)
+ o << cell.m_extraLineType << ",";
+ return o;
+}
+
+// send data to listener
+bool SW602Cell::send(SW602ListenerPtr listener, SW602Table &table)
+{
+ if (!listener) return true;
+ listener->openTableCell(*this);
+ bool ok=sendContent(listener, table);
+ listener->closeTableCell();
+ return ok;
+}
+
+bool SW602Cell::sendContent(SW602ListenerPtr, SW602Table &)
+{
+ SW602_DEBUG_MSG(("SW602Cell::sendContent: must not be called!!!\n"));
+ return false;
+}
+
+////////////////////////////////////////////////////////////
+// SW602CellContent
+////////////////////////////////////////////////////////////
+bool SW602CellContent::double2Date(double val, int &Y, int &M, int &D)
+{
+ /* first convert the date in long*/
+ long numDaysSinceOrigin=long(val+0.4);
+ // checkme: do we need to check before for isNan(val) ?
+ if (numDaysSinceOrigin<-10000*365 || numDaysSinceOrigin>10000*365)
+ {
+ /* normally, we can expect documents to contain date between 1904
+ and 2004. So even if such a date can make sense, storing it as
+ a number of days is clearly abnormal */
+ SW602_DEBUG_MSG(("SW602CellContent::double2Date: using a double to represent the date %ld seems odd\n", numDaysSinceOrigin));
+ Y=1904;
+ M=D=1;
+ return false;
+ }
+ // find the century
+ int century=19;
+ while (numDaysSinceOrigin>=36500+24)
+ {
+ long numDaysInCentury=36500+24+((century%4)?0:1);
+ if (numDaysSinceOrigin<numDaysInCentury) break;
+ numDaysSinceOrigin-=numDaysInCentury;
+ ++century;
+ }
+ while (numDaysSinceOrigin<0)
+ {
+ --century;
+ numDaysSinceOrigin+=36500+24+((century%4)?0:1);
+ }
+ // now compute the year
+ Y=int(numDaysSinceOrigin/365);
+ long numDaysToEndY1=Y*365+(Y>0 ? (Y-1)/4+((century%4)?0:1) : 0);
+ if (numDaysToEndY1>numDaysSinceOrigin)
+ {
+ --Y;
+ numDaysToEndY1=Y*365+(Y>0 ? (Y-1)/4+((century%4)?0:1) : 0);
+ }
+ // finish to compute the date
+ int numDaysFrom1Jan=int(numDaysSinceOrigin-numDaysToEndY1);
+ Y+=century*100;
+ bool isLeap=(Y%4)==0 && ((Y%400)==0 || (Y%100)!=0);
+
+ for (M=0; M<12; ++M)
+ {
+ static const int days[2][12] =
+ {
+ { 0,31,59,90,120,151,181,212,243,273,304,334},
+ { 0,31,60,91,121,152,182,213,244,274,305,335}
+ };
+ if (M<11 && days[isLeap ? 1 : 0][M+1]<=numDaysFrom1Jan) continue;
+ D=(numDaysFrom1Jan-days[isLeap ? 1 : 0][M++])+1;
+ break;
+ }
+ return true;
+}
+
+bool SW602CellContent::date2Double(int Y, int M, int D, double &val)
+{
+ --M;
+ --D;
+ if (M>11)
+ {
+ Y += M/12;
+ M %= 12;
+ }
+ else if (M<0)
+ {
+ int yDiff = (-M + 11)/12;
+ Y -= yDiff;
+ M+=12*yDiff;
+ }
+ // sanity check
+ if (M<0||M>11)
+ {
+ SW602_DEBUG_MSG(("SW602CellContent::date2Double: something is bad\n"));
+ return false;
+ }
+ bool isLeap=(Y%4)==0 && ((Y%400)==0 || (Y%100)!=0);
+ int32_t const daysFrom0=365*Y+(Y/400)-(Y/100)+(Y/4);
+ int32_t const daysFrom1900=365*1900+(1900/400)-(1900/100)+(1900/4);
+ static const int32_t days[2][12] =
+ {
+ { 0,31,59,90,120,151,181,212,243,273,304,334},
+ { 0,31,60,91,121,152,182,213,244,274,305,335}
+ };
+ int32_t daysFrom1Jan=days[isLeap ? 1 : 0][M] + D;
+ val=double(daysFrom0-daysFrom1900+daysFrom1Jan);
+ return true;
+}
+
+bool SW602CellContent::double2Time(double val, int &H, int &M, int &S)
+{
+ if (val < 0.0 || val > 1.0) return false;
+ double time = 24.*3600.*val+0.5;
+ H=int(time/3600.);
+ time -= H*3600.;
+ M=int(time/60.);
+ time -= M*60.;
+ S=int(time);
+ return true;
+}
+
+bool SW602CellContent::double2String(double val, SW602Cell::Format const &format, std::string &str)
+{
+ std::stringstream s;
+ switch (format.m_format)
+ {
+ case SW602Cell::F_BOOLEAN:
+ if (val<0 || val >0) s << "true";
+ else s << "false";
+ break;
+ case SW602Cell::F_NUMBER:
+ if (format.m_digits>=0 && format.m_numberFormat!=SW602Cell::F_NUMBER_GENERIC)
+ s << std::setprecision(format.m_digits);
+ switch (format.m_numberFormat)
+ {
+ case SW602Cell::F_NUMBER_CURRENCY:
+ s << std::fixed << val << "$";
+ break;
+ case SW602Cell::F_NUMBER_DECIMAL:
+ s << val;
+ break;
+ case SW602Cell::F_NUMBER_SCIENTIFIC:
+ s << std::scientific << val;
+ break;
+ case SW602Cell::F_NUMBER_PERCENT:
+ s << std::fixed << 100*val << "%";
+ break;
+ case SW602Cell::F_NUMBER_FRACTION:
+ case SW602Cell::F_NUMBER_GENERIC:
+ case SW602Cell::F_NUMBER_UNKNOWN:
+ default:
+ s << val;
+ break;
+ }
+ break;
+ case SW602Cell::F_DATE:
+ {
+ int Y, M, D;
+ if (!double2Date(val, Y, M, D)) return false;
+ struct tm time;
+ time.tm_sec=time.tm_min=time.tm_hour=0;
+ time.tm_mday=D;
+ time.tm_mon=M;
+ time.tm_year=Y;
+ time.tm_wday=time.tm_yday=time.tm_isdst=-1;
+#if HAVE_STRUCT_TM_TM_ZONE
+ time.tm_zone=0;
+#endif
+ char buf[256];
+ if (mktime(&time)==-1 ||
+ !strftime(buf, 256, format.m_DTFormat.empty() ? "%m/%d/%y" : format.m_DTFormat.c_str(), &time))
+ return false;
+ s << buf;
+ break;
+ }
+ case SW602Cell::F_TIME:
+ {
+ if (val<0 || val>=1)
+ val=std::fmod(val,1.);
+ int H, M, S;
+ if (!double2Time(val, H, M, S)) return false;
+ struct tm time;
+ time.tm_sec=S;
+ time.tm_min=M;
+ time.tm_hour=H;
+ time.tm_mday=time.tm_mon=1;
+ time.tm_year=100;
+ time.tm_wday=time.tm_yday=time.tm_isdst=-1;
+#if HAVE_STRUCT_TM_TM_ZONE
+ time.tm_zone=0;
+#endif
+ char buf[256];
+ if (mktime(&time)==-1 ||
+ !strftime(buf, 256, format.m_DTFormat.empty() ? "%H:%M:%S" : format.m_DTFormat.c_str(), &time))
+ return false;
+ s << buf;
+ break;
+ }
+ case SW602Cell::F_TEXT:
+ case SW602Cell::F_UNKNOWN:
+ default:
+ SW602_DEBUG_MSG(("SW602CellContent::double2String: called with bad format\n"));
+ return false;
+ }
+ str=s.str();
+ return true;
+}
+
+std::ostream &operator<<(std::ostream &o, SW602CellContent const &content)
+{
+ switch (content.m_contentType)
+ {
+ case SW602CellContent::C_NONE:
+ break;
+ case SW602CellContent::C_TEXT:
+ o << ",text=\"" << content.m_textEntry << "\"";
+ break;
+ case SW602CellContent::C_NUMBER:
+ {
+ o << ",val=";
+ bool textAndVal = false;
+ if (content.hasText())
+ {
+ o << "entry=" << content.m_textEntry;
+ textAndVal = content.isValueSet();
+ }
+ if (textAndVal) o << "[";
+ if (content.isValueSet()) o << content.m_value;
+ if (textAndVal) o << "]";
+ }
+ break;
+ case SW602CellContent::C_FORMULA:
+ o << ",formula=";
+ for (size_t l=0; l < content.m_formula.size(); ++l)
+ o << content.m_formula[l];
+ if (content.isValueSet()) o << "[" << content.m_value << "]";
+ break;
+ case SW602CellContent::C_UNKNOWN:
+ break;
+ default:
+ o << "###unknown type,";
+ break;
+ }
+ return o;
+}
+
+// ---------- WKSContentListener::FormulaInstruction ------------------
+librevenge::RVNGPropertyList SW602CellContent::FormulaInstruction::getPropertyList() const
+{
+ librevenge::RVNGPropertyList pList;
+ switch (m_type)
+ {
+ case F_Operator:
+ pList.insert("librevenge:type","librevenge-operator");
+ pList.insert("librevenge:operator",m_content.c_str());
+ break;
+ case F_Function:
+ pList.insert("librevenge:type","librevenge-function");
+ pList.insert("librevenge:function",m_content.c_str());
+ break;
+ case F_Text:
+ {
+ // we must use the font converter here to get the final string
+ pList.insert("librevenge:type","librevenge-text");
+ librevenge::RVNGString finalStr("");
+ for (size_t i=0; i<m_content.size(); ++i)
+ {
+ char c=m_content[i];
+ // TODO: handle
+ int unicode=c;
+ if (unicode==-1)
+ {
+ if (c < 0x20 && c!=9)
+ {
+ SW602_DEBUG_MSG(("SW602CellContent::FormulaInstruction: Find odd char %x\n", (unsigned int)c));
+ }
+ else
+ finalStr.append(char(c));
+ }
+ else if (unicode != 0xfffd)
+ libsw602::appendUnicode((uint32_t) unicode, finalStr);
+ }
+ pList.insert("librevenge:text",finalStr);
+ break;
+ }
+ case F_Double:
+ pList.insert("librevenge:type","librevenge-number");
+ pList.insert("librevenge:number",m_doubleValue, librevenge::RVNG_GENERIC);
+ break;
+ case F_Long:
+ pList.insert("librevenge:type","librevenge-number");
+ pList.insert("librevenge:number",m_longValue, librevenge::RVNG_GENERIC);
+ break;
+ case F_Cell:
+ pList.insert("librevenge:type","librevenge-cell");
+ pList.insert("librevenge:column",m_position[0][0], librevenge::RVNG_GENERIC);
+ pList.insert("librevenge:row",m_position[0][1], librevenge::RVNG_GENERIC);
+ pList.insert("librevenge:column-absolute",!m_positionRelative[0][0]);
+ pList.insert("librevenge:row-absolute",!m_positionRelative[0][1]);
+ if (!m_sheet.empty())
+ pList.insert("librevenge:sheet-name",m_sheet.c_str());
+ break;
+ case F_CellList:
+ pList.insert("librevenge:type","librevenge-cells");
+ pList.insert("librevenge:start-column",m_position[0][0], librevenge::RVNG_GENERIC);
+ pList.insert("librevenge:start-row",m_position[0][1], librevenge::RVNG_GENERIC);
+ pList.insert("librevenge:start-column-absolute",!m_positionRelative[0][0]);
+ pList.insert("librevenge:start-row-absolute",!m_positionRelative[0][1]);
+ pList.insert("librevenge:end-column",m_position[1][0], librevenge::RVNG_GENERIC);
+ pList.insert("librevenge:end-row",m_position[1][1], librevenge::RVNG_GENERIC);
+ pList.insert("librevenge:end-column-absolute",!m_positionRelative[1][0]);
+ pList.insert("librevenge:end-row-absolute",!m_positionRelative[1][1]);
+ if (!m_sheet.empty())
+ pList.insert("librevenge:sheet-name",m_sheet.c_str());
+ break;
+ default:
+ SW602_DEBUG_MSG(("SW602CellContent::FormulaInstruction::getPropertyList: unexpected type\n"));
+ }
+ return pList;
+}
+
+std::ostream &operator<<(std::ostream &o, SW602CellContent::FormulaInstruction const &inst)
+{
+ if (inst.m_type==SW602CellContent::FormulaInstruction::F_Double)
+ o << inst.m_doubleValue;
+ else if (inst.m_type==SW602CellContent::FormulaInstruction::F_Long)
+ o << inst.m_longValue;
+ else if (inst.m_type==SW602CellContent::FormulaInstruction::F_Cell)
+ {
+ if (!inst.m_sheet.empty()) o << inst.m_sheet;
+ if (!inst.m_positionRelative[0][0]) o << "$";
+ if (inst.m_position[0][0]<0) o << "C" << inst.m_position[0][0];
+ else
+ {
+ if (inst.m_position[0][0]>=26) o << (char)(inst.m_position[0][0]/26-1 + 'A');
+ o << (char)(inst.m_position[0][0]%26+'A');
+ }
+ if (!inst.m_positionRelative[0][1]) o << "$";
+ if (inst.m_position[0][1]<0) o << "R" << inst.m_position[0][1];
+ else o << inst.m_position[0][1];
+ }
+ else if (inst.m_type==SW602CellContent::FormulaInstruction::F_CellList)
+ {
+ if (!inst.m_sheet.empty()) o << inst.m_sheet;
+ for (int l=0; l<2; ++l)
+ {
+ if (!inst.m_positionRelative[l][0]) o << "$";
+ if (inst.m_position[l][0]<0) o << "C" << inst.m_position[l][0];
+ else
+ {
+ if (inst.m_position[l][0]>=26) o << (char)(inst.m_position[l][0]/26-1 + 'A');
+ o << (char)(inst.m_position[l][0]%26+'A');
+ }
+ if (!inst.m_positionRelative[l][1]) o << "$";
+ if (inst.m_position[l][1]<0) o << "R" << inst.m_position[l][1];
+ else o << inst.m_position[l][1];
+ if (l==0) o << ":";
+ }
+ }
+ else if (inst.m_type==SW602CellContent::FormulaInstruction::F_Text)
+ o << "\"" << inst.m_content << "\"";
+ else
+ o << inst.m_content;
+ return o;
+}
+
+}
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602Cell.h b/src/lib/SW602Cell.h
new file mode 100644
index 0000000..26c81b5
--- /dev/null
+++ b/src/lib/SW602Cell.h
@@ -0,0 +1,431 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/** \file SW602Cell.h
+ * Defines SW602Cell (cell content and format)
+ */
+
+#ifndef INCLUDED_SW602_CELL_H
+#define INCLUDED_SW602_CELL_H
+
+#include <string>
+#include <vector>
+
+#include "SW602Entry.h"
+#include "SW602Font.h"
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+class SW602Table;
+
+/** a structure used to define a cell and its format */
+class SW602Cell
+{
+public:
+ /** the different format of a cell's content */
+ enum FormatType { F_TEXT, F_BOOLEAN, F_NUMBER, F_DATE, F_TIME, F_UNKNOWN };
+ /** the different number format of a cell's content */
+ enum NumberType { F_NUMBER_CURRENCY, F_NUMBER_DECIMAL, F_NUMBER_FRACTION, F_NUMBER_GENERIC, F_NUMBER_SCIENTIFIC, F_NUMBER_PERCENT, F_NUMBER_UNKNOWN };
+ /** a structure uses to define the format of a cell content */
+ struct Format
+ {
+ //! constructor
+ Format() : m_format(F_UNKNOWN), m_numberFormat(F_NUMBER_UNKNOWN), m_digits(-1), m_integerDigits(-1), m_numeratorDigits(-1), m_denominatorDigits(-1),
+ m_thousandHasSeparator(false), m_parenthesesForNegative(false), m_currencySymbol("$"), m_DTFormat("")
+ {
+ }
+ //! destructor
+ virtual ~Format() {}
+ //! returns true if this is a basic format style
+ bool hasBasicFormat() const
+ {
+ return m_format==F_TEXT || m_format==F_UNKNOWN;
+ }
+ //! returns a value type
+ std::string getValueType() const;
+ //! get the numbering style
+ bool getNumberingProperties(librevenge::RVNGPropertyList &propList) const;
+ //! convert a DTFormat in a propertyList
+ static bool convertDTFormat(std::string const &dtFormat, librevenge::RVNGPropertyListVector &propListVector);
+ //! operator<<
+ friend std::ostream &operator<<(std::ostream &o, Format const &format);
+ //! a comparison function
+ int compare(Format const &format) const;
+
+ //! the cell format : by default unknown
+ FormatType m_format;
+ //! the numeric format
+ NumberType m_numberFormat;
+ //! the number of digits
+ int m_digits;
+ //! the number of main digits
+ int m_integerDigits;
+ //! the number of numerator digits
+ int m_numeratorDigits;
+ //! the number of denominator digits
+ int m_denominatorDigits;
+ //! true if we must separate the thousand
+ bool m_thousandHasSeparator;
+ //! true if we use parenthese to print negative number
+ bool m_parenthesesForNegative;
+ //! the currency symbol ( default '$')
+ std::string m_currencySymbol;
+ //! a date/time format ( using a subset of strftime format )
+ std::string m_DTFormat;
+ };
+ //! a comparaison structure used to store data
+ struct CompareFormat
+ {
+ //! constructor
+ CompareFormat() {}
+ //! comparaison function
+ bool operator()(Format const &c1, Format const &c2) const
+ {
+ return c1.compare(c2) < 0;
+ }
+ };
+ /** the default horizontal alignment.
+
+ \note actually mainly used for table/spreadsheet cell, FULL is not yet implemented */
+ enum HorizontalAlignment { HALIGN_LEFT, HALIGN_RIGHT, HALIGN_CENTER,
+ HALIGN_FULL, HALIGN_DEFAULT
+ };
+
+ /** the default vertical alignment.
+ \note actually mainly used for table/spreadsheet cell, not yet implemented */
+ enum VerticalAlignment { VALIGN_TOP, VALIGN_CENTER, VALIGN_BOTTOM, VALIGN_DEFAULT };
+
+ //! an enum to defined potential internal line: E_Line1=TL to RB, E_Line2=BL to RT
+ enum ExtraLine { E_None, E_Line1, E_Line2, E_Cross };
+
+ //! constructor
+ SW602Cell() : m_position(0,0), m_numberCellSpanned(1,1), m_bdBox(), m_bdSize(),
+ m_format(), m_font(3,12), m_fontSet(false), m_hAlign(HALIGN_DEFAULT), m_vAlign(VALIGN_DEFAULT),
+ m_backgroundColor(SW602Color::white()), m_protected(false),
+ m_bordersList(), m_extraLine(E_None), m_extraLineType() { }
+
+ //! destructor
+ virtual ~SW602Cell() {}
+
+ /** adds to the propList*/
+ void addTo(librevenge::RVNGPropertyList &propList) const;
+
+ //! operator<<
+ friend std::ostream &operator<<(std::ostream &o, SW602Cell const &cell);
+
+ // interface with SW602Table:
+
+ /** function called when a cell is send by SW602Table to send a cell to a
+ listener.
+
+ By default: calls openTableCell(*this), sendContent and then closeTableCell() */
+ virtual bool send(SW602ListenerPtr listener, SW602Table &table);
+ /** function called when the content of a cell must be send to the listener,
+ ie. when SW602Table::sendTable or SW602Table::sendAsText is called.
+
+ \note default behavior: does nothing and prints an error in debug mode.*/
+ virtual bool sendContent(SW602ListenerPtr listener, SW602Table &table);
+
+ // position
+
+ //! position accessor
+ SW602Vec2i const &position() const
+ {
+ return m_position;
+ }
+ //! set the cell positions : 0,0 -> A1, 0,1 -> A2
+ void setPosition(SW602Vec2i posi)
+ {
+ m_position = posi;
+ }
+
+ //! returns the number of spanned cells
+ SW602Vec2i const &numSpannedCells() const
+ {
+ return m_numberCellSpanned;
+ }
+ //! sets the number of spanned cells : SW602Vec2i(1,1) means 1 cellule
+ void setNumSpannedCells(SW602Vec2i numSpanned)
+ {
+ m_numberCellSpanned=numSpanned;
+ }
+
+ //! bdbox accessor
+ SW602Box2f const &bdBox() const
+ {
+ return m_bdBox;
+ }
+ //! set the bdbox (unit point)
+ void setBdBox(SW602Box2f box)
+ {
+ m_bdBox = box;
+ }
+
+ //! bdbox size accessor
+ SW602Vec2f const &bdSize() const
+ {
+ return m_bdSize;
+ }
+ //! set the bdbox size(unit point)
+ void setBdSize(SW602Vec2f sz)
+ {
+ m_bdSize = sz;
+ }
+ //! return the name of a cell (given row and column) : 0,0 -> A1, 0,1 -> A2...
+ static std::string getBasicCellName(SW602Vec2i const &pos);
+ //! return the name of a cell (given row and column) : 0,0 -> [.A1], 0,1 -> [.A2]
+ static std::string getCellName(SW602Vec2i const &pos, SW602Vec2b const &absolute);
+
+ //! return the column name
+ static std::string getColumnName(int col);
+
+ // format
+
+ //! returns the cell format
+ Format const &getFormat() const
+ {
+ return m_format;
+ }
+ //! set the cell format
+ void setFormat(Format const &format)
+ {
+ m_format=format;
+ }
+
+ //! returns true if the font has been set
+ bool isFontSet() const
+ {
+ return m_fontSet;
+ }
+ //! returns the font
+ SW602Font getFont() const
+ {
+ return m_font;
+ }
+ //! sets the fonts
+ void setFont(SW602Font const &font, bool isDefault=false)
+ {
+ m_font=font;
+ m_fontSet=!isDefault;
+ }
+
+ //! returns true if the cell is protected
+ bool isProtected() const
+ {
+ return m_protected;
+ }
+ //! sets the cell's protected flag
+ void setProtected(bool fl)
+ {
+ m_protected = fl;
+ }
+
+ //! returns the horizontal alignment
+ HorizontalAlignment hAlignment() const
+ {
+ return m_hAlign;
+ }
+ //! sets the horizontal alignment
+ void setHAlignment(HorizontalAlignment align)
+ {
+ m_hAlign = align;
+ }
+
+ //! returns the vertical alignment
+ VerticalAlignment vAlignment() const
+ {
+ return m_vAlign;
+ }
+ //! sets the vertical alignment
+ void setVAlignment(VerticalAlignment align)
+ {
+ m_vAlign = align;
+ }
+
+ //! return true if the cell has some border
+ bool hasBorders() const
+ {
+ return m_bordersList.size() != 0;
+ }
+ //! return the cell border: libsw602::Left | ...
+ std::vector<SW602Border> const &borders() const
+ {
+ return m_bordersList;
+ }
+
+ //! reset the border
+ void resetBorders()
+ {
+ m_bordersList.resize(0);
+ }
+ //! sets the cell border: wh=libsw602::LeftBit|...
+ void setBorders(int wh, SW602Border const &border);
+
+ //! returns the background color
+ SW602Color backgroundColor() const
+ {
+ return m_backgroundColor;
+ }
+ //! sets the background color
+ void setBackgroundColor(SW602Color color)
+ {
+ m_backgroundColor = color;
+ }
+ //! returns true if we have some extra lines
+ bool hasExtraLine() const
+ {
+ return m_extraLine!=E_None && !m_extraLineType.isEmpty();
+ }
+ //! returns the extra lines
+ ExtraLine extraLine() const
+ {
+ return m_extraLine;
+ }
+ //! returns the extra line border
+ SW602Border const &extraLineType() const
+ {
+ return m_extraLineType;
+ }
+ //! sets the extraline
+ void setExtraLine(ExtraLine extrLine, SW602Border const &type=SW602Border())
+ {
+ m_extraLine = extrLine;
+ m_extraLineType=type;
+ }
+protected:
+ //! the cell row and column : 0,0 -> A1, 0,1 -> A2
+ SW602Vec2i m_position;
+ //! the cell spanned : by default (1,1)
+ SW602Vec2i m_numberCellSpanned;
+ /** the cell bounding box (unit in point)*/
+ SW602Box2f m_bdBox;
+ /** the cell bounding size : unit point */
+ SW602Vec2f m_bdSize;
+
+ //! the cell format
+ Format m_format;
+ //! the cell font
+ SW602Font m_font;
+ //! a flag to know if the font has been set
+ bool m_fontSet;
+ //! the cell alignment : by default nothing
+ HorizontalAlignment m_hAlign;
+ //! the vertical cell alignment : by default nothing
+ VerticalAlignment m_vAlign;
+ //! the backgroung color
+ SW602Color m_backgroundColor;
+ //! cell protected
+ bool m_protected;
+
+ //! the cell border SW602Border::Pos
+ std::vector<SW602Border> m_bordersList;
+ /** extra line */
+ ExtraLine m_extraLine;
+ /** extra line type */
+ SW602Border m_extraLineType;
+};
+
+//! small class use to define a sheet cell content
+class SW602CellContent
+{
+public:
+ //! small class use to define a formula instruction
+ struct FormulaInstruction
+ {
+ enum Type { F_Operator, F_Function, F_Cell, F_CellList, F_Long, F_Double, F_Text };
+ //! constructor
+ FormulaInstruction() : m_type(F_Text), m_content(""), m_longValue(0), m_doubleValue(0), m_sheet("")
+ {
+ for (int i=0; i<2; ++i)
+ {
+ m_position[i]=SW602Vec2i(0,0);
+ m_positionRelative[i]=SW602Vec2b(false,false);
+ }
+ }
+ /** returns a proplist corresponding to a instruction using a font converter to send the t ext */
+ librevenge::RVNGPropertyList getPropertyList() const;
+ //! operator<<
+ friend std::ostream &operator<<(std::ostream &o, FormulaInstruction const &inst);
+ //! the type
+ Type m_type;
+ //! the content ( if type == F_Operator or type = F_Function or type==F_Text)
+ std::string m_content;
+ //! value ( if type==F_Long )
+ double m_longValue;
+ //! value ( if type==F_Double )
+ double m_doubleValue;
+ //! cell position ( if type==F_Cell or F_CellList )
+ SW602Vec2i m_position[2];
+ //! relative cell position ( if type==F_Cell or F_CellList )
+ SW602Vec2b m_positionRelative[2];
+ //! the sheet name (if not empty)
+ std::string m_sheet;
+ };
+
+ /** the different types of cell's field */
+ enum Type { C_NONE, C_TEXT, C_NUMBER, C_FORMULA, C_UNKNOWN };
+ /// constructor
+ SW602CellContent() : m_contentType(C_UNKNOWN), m_value(0.0), m_valueSet(false), m_textEntry(), m_formula() { }
+ /// destructor
+ ~SW602CellContent() {}
+ //! operator<<
+ friend std::ostream &operator<<(std::ostream &o, SW602CellContent const &cell);
+
+ //! returns true if the cell has no content
+ bool empty() const
+ {
+ if (m_contentType == C_NUMBER) return false;
+ if (m_contentType == C_TEXT && m_textEntry.valid()) return false;
+ if (m_contentType == C_FORMULA && (m_formula.size() || isValueSet())) return false;
+ return true;
+ }
+ //! sets the double value
+ void setValue(double value)
+ {
+ m_value = value;
+ m_valueSet = true;
+ }
+ //! returns true if the value has been setted
+ bool isValueSet() const
+ {
+ return m_valueSet;
+ }
+ //! returns true if the text is set
+ bool hasText() const
+ {
+ return m_textEntry.valid();
+ }
+ /** conversion beetween double days since 1900 and a date, ie val=0
+ corresponds to 1/1/1900, val=365 to 1/1/1901, ... */
+ static bool double2Date(double val, int &Y, int &M, int &D);
+ /** conversion beetween double: second since 0:00 and time */
+ static bool double2Time(double val, int &H, int &M, int &S);
+ /** conversion of the value in string knowing the cell format */
+ static bool double2String(double val, SW602Cell::Format const &format, std::string &str);
+ /** conversion beetween date and double days since 1900 date */
+ static bool date2Double(int Y, int M, int D, double &val);
+ //! the content type ( by default unknown )
+ Type m_contentType;
+ //! the cell value
+ double m_value;
+ //! true if the value has been set
+ bool m_valueSet;
+ //! the cell string
+ SW602Entry m_textEntry;
+ //! the formula list of instruction
+ std::vector<FormulaInstruction> m_formula;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602Chart.cpp b/src/lib/SW602Chart.cpp
new file mode 100644
index 0000000..8e7cf98
--- /dev/null
+++ b/src/lib/SW602Chart.cpp
@@ -0,0 +1,657 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/*
+ * Structure to store and construct a chart from an unstructured list
+ * of cell
+ *
+ */
+
+#include <iomanip>
+#include <iostream>
+#include <map>
+#include <sstream>
+
+#include <librevenge/librevenge.h>
+
+#include "SW602Listener.h"
+#include "SW602Position.h"
+#include "SW602SpreadsheetListener.h"
+#include "SW602SubDocument.h"
+
+#include "SW602Chart.h"
+
+namespace libsw602
+{
+
+/** Internal: the structures of a SW602Chart */
+namespace SW602ChartInternal
+{
+////////////////////////////////////////
+//! Internal: the subdocument of a SW602Chart
+class SubDocument : public SW602SubDocument
+{
+public:
+ SubDocument(SW602Chart *chart, SW602Chart::TextZone::Type textZone) :
+ SW602SubDocument(0, boost::shared_ptr<librevenge::RVNGInputStream>(), SW602Entry()), m_chart(chart), m_textZone(textZone)
+ {
+ }
+
+ //! destructor
+ virtual ~SubDocument() {}
+
+ //! operator!=
+ virtual bool operator!=(SW602SubDocument const &doc) const
+ {
+ if (SW602SubDocument::operator!=(doc))
+ return true;
+ SubDocument const *subDoc=dynamic_cast<SubDocument const *>(&doc);
+ if (!subDoc) return true;
+ return m_chart!=subDoc->m_chart || m_textZone!=subDoc->m_textZone;
+ }
+ //! operator!==
+ virtual bool operator==(SW602SubDocument const &doc) const
+ {
+ return !operator!=(doc);
+ }
+
+ //! the parser function
+ void parse(SW602ListenerPtr &listener, libsw602::SubDocumentType type);
+protected:
+ //! the chart
+ SW602Chart *m_chart;
+ //! the textzone type
+ SW602Chart::TextZone::Type m_textZone;
+private:
+ SubDocument(SubDocument const &orig);
+ SubDocument &operator=(SubDocument const &orig);
+};
+
+void SubDocument::parse(SW602ListenerPtr &listener, libsw602::SubDocumentType /*type*/)
+{
+ if (!listener.get())
+ {
+ SW602_DEBUG_MSG(("SW602ChartInternal::SubDocument::parse: no listener\n"));
+ return;
+ }
+
+ if (!m_chart)
+ {
+ SW602_DEBUG_MSG(("SW602ChartInternal::SubDocument::parse: can not find the chart\n"));
+ return;
+ }
+ m_chart->sendTextZoneContent(m_textZone, listener);
+}
+
+}
+
+////////////////////////////////////////////////////////////
+// SW602Chart
+////////////////////////////////////////////////////////////
+SW602Chart::SW602Chart(std::string const &sheetName, SW602Vec2f const &dim) :
+ m_sheetName(sheetName), m_dim(dim), m_type(SW602Chart::Series::S_Bar), m_dataStacked(false), m_legend(), m_seriesList(), m_textZoneMap()
+{
+ for (int i=0; i<3; ++i) m_axis[i]=Axis();
+}
+
+SW602Chart::~SW602Chart()
+{
+}
+
+void SW602Chart::add(int coord, SW602Chart::Axis const &axis)
+{
+ if (coord<0 || coord>2)
+ {
+ SW602_DEBUG_MSG(("SW602Chart::add[Axis]: called with bad coord\n"));
+ return;
+ }
+ m_axis[coord]=axis;
+}
+
+SW602Chart::Axis const &SW602Chart::getAxis(int coord) const
+{
+ if (coord<0 || coord>2)
+ {
+ SW602_DEBUG_MSG(("SW602Chart::getAxis: called with bad coord\n"));
+ static Axis const badAxis;
+ return badAxis;
+ }
+ return m_axis[coord];
+}
+
+void SW602Chart::add(SW602Chart::Series const &series)
+{
+ m_seriesList.push_back(series);
+}
+
+bool SW602Chart::getTextZone(SW602Chart::TextZone::Type type, SW602Chart::TextZone &textZone)
+{
+ if (m_textZoneMap.find(type)==m_textZoneMap.end())
+ return false;
+ textZone=m_textZoneMap.find(type)->second;
+ return true;
+}
+
+void SW602Chart::sendTextZoneContent(SW602Chart::TextZone::Type type, SW602ListenerPtr &listener)
+{
+ if (m_textZoneMap.find(type)==m_textZoneMap.end())
+ {
+ SW602_DEBUG_MSG(("SW602Chart::sendTextZoneContent: called with unknown zone(%d)\n", int(type)));
+ return;
+ }
+ sendContent(m_textZoneMap.find(type)->second, listener);
+}
+
+void SW602Chart::add(SW602Chart::TextZone const &textZone)
+{
+ m_textZoneMap[textZone.m_type]=textZone;
+}
+
+void SW602Chart::sendChart(SW602SpreadsheetListenerPtr &listener, librevenge::RVNGSpreadsheetInterface *interface)
+{
+ if (!listener || !interface)
+ {
+ SW602_DEBUG_MSG(("SW602Chart::sendChart: can not find listener or interface\n"));
+ return;
+ }
+ if (m_seriesList.empty())
+ {
+ SW602_DEBUG_MSG(("SW602Chart::sendChart: can not find the series\n"));
+ return;
+ }
+ boost::shared_ptr<SW602Listener> genericListener=listener;
+ int styleId=0;
+
+ librevenge::RVNGPropertyList style;
+ style.insert("librevenge:chart-id", styleId);
+ style.insert("draw:stroke", "none");
+ style.insert("draw:fill", "none");
+ interface->defineChartStyle(style);
+
+ librevenge::RVNGPropertyList chart;
+ chart.insert("svg:width", m_dim[0], librevenge::RVNG_POINT);
+ chart.insert("svg:height", m_dim[1], librevenge::RVNG_POINT);
+ if (!m_seriesList.empty())
+ chart.insert("chart:class", Series::getSeriesTypeName(m_seriesList[0].m_type).c_str());
+ else
+ chart.insert("chart:class", Series::getSeriesTypeName(m_type).c_str());
+ chart.insert("librevenge:chart-id", styleId++);
+ interface->openChart(chart);
+
+ // legend
+ if (m_legend.m_show)
+ {
+ style=librevenge::RVNGPropertyList();
+ m_legend.addStyleTo(style);
+ style.insert("librevenge:chart-id", styleId);
+ interface->defineChartStyle(style);
+ librevenge::RVNGPropertyList legend;
+ m_legend.addContentTo(legend);
+ legend.insert("librevenge:chart-id", styleId++);
+ legend.insert("librevenge:zone-type", "legend");
+ interface->openChartTextObject(legend);
+ interface->closeChartTextObject();
+ }
+ std::map<TextZone::Type, TextZone>::const_iterator textIt;
+ for (textIt=m_textZoneMap.begin(); textIt!=m_textZoneMap.end();)
+ {
+ TextZone const &zone= textIt++->second;
+ if (zone.m_type != TextZone::T_Title && zone.m_type != TextZone::T_SubTitle)
+ continue;
+ style=librevenge::RVNGPropertyList();
+ zone.addStyleTo(style);
+ style.insert("librevenge:chart-id", styleId);
+ interface->defineChartStyle(style);
+ librevenge::RVNGPropertyList textZone;
+ zone.addContentTo(m_sheetName, textZone);
+ textZone.insert("librevenge:chart-id", styleId++);
+ textZone.insert("librevenge:zone-type", zone.m_type==TextZone::T_Title ? "title":"subtitle");
+ interface->openChartTextObject(textZone);
+ if (zone.m_contentType==TextZone::C_Text)
+ {
+ boost::shared_ptr<SW602SubDocument> doc(new SW602ChartInternal::SubDocument(this, zone.m_type));
+ listener->handleSubDocument(doc, libsw602::DOC_CHART_ZONE);
+ }
+ interface->closeChartTextObject();
+ }
+ // plot area
+ style=librevenge::RVNGPropertyList();
+ style.insert("librevenge:chart-id", styleId);
+ style.insert("chart:include-hidden-cells","false");
+ style.insert("chart:auto-position","true");
+ style.insert("chart:auto-size","true");
+ style.insert("chart:treat-empty-cells","leave-gap");
+ style.insert("chart:right-angled-axes","true");
+ style.insert("chart:stacked", m_dataStacked);
+ interface->defineChartStyle(style);
+
+ librevenge::RVNGPropertyList plotArea;
+ if (m_dim[0]>80)
+ {
+ plotArea.insert("svg:x", 20, librevenge::RVNG_POINT);
+ plotArea.insert("svg:width", m_dim[0]-40, librevenge::RVNG_POINT);
+ }
+ if (m_dim[1]>80)
+ {
+ plotArea.insert("svg:y", 20, librevenge::RVNG_POINT);
+ plotArea.insert("svg:height", m_dim[1]-40, librevenge::RVNG_POINT);
+ }
+ plotArea.insert("librevenge:chart-id", styleId++);
+
+ librevenge::RVNGPropertyList floor, wall;
+ librevenge::RVNGPropertyListVector vect;
+ style=librevenge::RVNGPropertyList();
+ style.insert("draw:stroke","solid");
+ style.insert("svg:stroke-color","#b3b3b3");
+ style.insert("draw:fill","none");
+ style.insert("librevenge:chart-id", styleId);
+ interface->defineChartStyle(style);
+ floor.insert("librevenge:type", "floor");
+ floor.insert("librevenge:chart-id", styleId++);
+ vect.append(floor);
+
+ style.insert("draw:fill","solid");
+ style.insert("draw:fill-color","#ffffff");
+ style.insert("librevenge:chart-id", styleId);
+ interface->defineChartStyle(style);
+ wall.insert("librevenge:type", "wall");
+ wall.insert("librevenge:chart-id", styleId++);
+ vect.append(wall);
+ plotArea.insert("librevenge:childs", vect);
+ interface->openChartPlotArea(plotArea);
+ // axis
+ for (int i=0; i<3; ++i)
+ {
+ if (m_axis[i].m_type==Axis::A_None) continue;
+ style=librevenge::RVNGPropertyList();
+ m_axis[i].addStyleTo(style);
+ style.insert("librevenge:chart-id", styleId);
+ interface->defineChartStyle(style);
+ librevenge::RVNGPropertyList axis;
+ m_axis[i].addContentTo(m_sheetName, i, axis);
+ axis.insert("librevenge:chart-id", styleId++);
+ interface->insertChartAxis(axis);
+ }
+ // label
+ for (textIt=m_textZoneMap.begin(); textIt!=m_textZoneMap.end();)
+ {
+ TextZone const &zone= textIt++->second;
+ if (zone.m_type == TextZone::T_Title || zone.m_type == TextZone::T_SubTitle)
+ continue;
+ style=librevenge::RVNGPropertyList();
+ zone.addStyleTo(style);
+ style.insert("librevenge:chart-id", styleId);
+ interface->defineChartStyle(style);
+ librevenge::RVNGPropertyList textZone;
+ zone.addContentTo(m_sheetName, textZone);
+ textZone.insert("librevenge:chart-id", styleId++);
+ textZone.insert("librevenge:zone-type", "label");
+ interface->openChartTextObject(textZone);
+ if (zone.m_contentType==TextZone::C_Text)
+ {
+ boost::shared_ptr<SW602SubDocument> doc(new SW602ChartInternal::SubDocument(this, zone.m_type));
+ listener->handleSubDocument(doc, libsw602::DOC_CHART_ZONE);
+ }
+ interface->closeChartTextObject();
+ }
+ // series
+ for (size_t i=0; i < m_seriesList.size(); ++i)
+ {
+ style=librevenge::RVNGPropertyList();
+ m_seriesList[i].addStyleTo(style);
+ style.insert("librevenge:chart-id", styleId);
+ interface->defineChartStyle(style);
+ librevenge::RVNGPropertyList series;
+ m_seriesList[i].addContentTo(m_sheetName, series);
+ series.insert("librevenge:chart-id", styleId++);
+ interface->openChartSerie(series);
+ interface->closeChartSerie();
+ }
+ interface->closeChartPlotArea();
+
+ interface->closeChart();
+}
+
+////////////////////////////////////////////////////////////
+// Axis
+////////////////////////////////////////////////////////////
+SW602Chart::Axis::Axis() : m_type(SW602Chart::Axis::A_None), m_showGrid(true), m_showLabel(true),
+ m_labelRange(SW602Vec2f(0,0), SW602Vec2f(-1,-1)), m_style()
+{
+ m_style.m_lineWidth=0;
+}
+
+SW602Chart::Axis::~Axis()
+{
+}
+
+void SW602Chart::Axis::addContentTo(std::string const &sheetName, int coord, librevenge::RVNGPropertyList &propList) const
+{
+ std::string axis("");
+ axis += char('x'+coord);
+ propList.insert("chart:dimension",axis.c_str());
+ axis = "primary-"+axis;
+ propList.insert("chart:name",axis.c_str());
+ if (m_showGrid && (m_type==A_Numeric || m_type==A_Logarithmic))
+ {
+ librevenge::RVNGPropertyList grid;
+ grid.insert("librevenge:type", "grid");
+ grid.insert("chart:class", "major");
+ librevenge::RVNGPropertyListVector childs;
+ childs.append(grid);
+ propList.insert("librevenge:childs", childs);
+ }
+ if (m_showLabel && m_labelRange.size()[0]>=0 && m_labelRange.size()[1]>=0)
+ {
+ librevenge::RVNGPropertyList range;
+ range.insert("librevenge:sheet-name", sheetName.c_str());
+ range.insert("librevenge:start-row", m_labelRange.min()[1]);
+ range.insert("librevenge:start-column", m_labelRange.min()[0]);
+ range.insert("librevenge:end-row", m_labelRange.max()[1]);
+ range.insert("librevenge:end-column", m_labelRange.max()[0]);
+ librevenge::RVNGPropertyListVector vect;
+ vect.append(range);
+ propList.insert("chart:label-cell-address", vect);
+ }
+}
+
+void SW602Chart::Axis::addStyleTo(librevenge::RVNGPropertyList &propList) const
+{
+ propList.insert("chart:display-label", m_showLabel);
+ propList.insert("chart:axis-position", 0, librevenge::RVNG_GENERIC);
+ propList.insert("chart:reverse-direction", false);
+ propList.insert("chart:logarithmic", m_type==SW602Chart::Axis::A_Logarithmic);
+ propList.insert("text:line-break", false);
+ m_style.addTo(propList, true);
+}
+
+std::ostream &operator<<(std::ostream &o, SW602Chart::Axis const &axis)
+{
+ switch (axis.m_type)
+ {
+ case SW602Chart::Axis::A_None:
+ o << "none,";
+ break;
+ case SW602Chart::Axis::A_Numeric:
+ o << "numeric,";
+ break;
+ case SW602Chart::Axis::A_Logarithmic:
+ o << "logarithmic,";
+ break;
+ case SW602Chart::Axis::A_Sequence:
+ o << "sequence,";
+ break;
+ case SW602Chart::Axis::A_Sequence_Skip_Empty:
+ o << "sequence[noEmpty],";
+ break;
+ default:
+ o << "###type,";
+ SW602_DEBUG_MSG(("SW602Chart::Axis: unexpected type\n"));
+ break;
+ }
+ if (axis.m_showGrid) o << "show[grid],";
+ if (axis.m_showLabel) o << "show[label],";
+ if (axis.m_labelRange.size()[0]>=0 && axis.m_labelRange.size()[1]>=0)
+ o << "label[range]=" << axis.m_labelRange << ",";
+ o << axis.m_style;
+ return o;
+}
+
+////////////////////////////////////////////////////////////
+// Legend
+////////////////////////////////////////////////////////////
+void SW602Chart::Legend::addContentTo(librevenge::RVNGPropertyList &propList) const
+{
+ propList.insert("svg:x", m_position[0], librevenge::RVNG_POINT);
+ propList.insert("svg:y", m_position[1], librevenge::RVNG_POINT);
+ if (!m_autoPosition || !m_relativePosition)
+ return;
+ std::stringstream s;
+ if (m_relativePosition&libsw602::TopBit)
+ s << "top";
+ else if (m_relativePosition&libsw602::BottomBit)
+ s << "bottom";
+ if (s.str().length() && (m_relativePosition&(libsw602::LeftBit|libsw602::RightBit)))
+ s << "-";
+ if (m_relativePosition&libsw602::LeftBit)
+ s << "start";
+ else if (m_relativePosition&libsw602::RightBit)
+ s << "end";
+ propList.insert("chart:legend-position", s.str().c_str());
+}
+
+void SW602Chart::Legend::addStyleTo(librevenge::RVNGPropertyList &propList) const
+{
+ propList.insert("chart:auto-position", m_autoPosition);
+ m_font.addTo(propList);
+ m_style.addTo(propList);
+}
+
+std::ostream &operator<<(std::ostream &o, SW602Chart::Legend const &legend)
+{
+ if (legend.m_show)
+ o << "show,";
+ if (legend.m_autoPosition)
+ {
+ o << "automaticPos[";
+ if (legend.m_relativePosition&libsw602::TopBit)
+ o << "t";
+ else if (legend.m_relativePosition&libsw602::RightBit)
+ o << "b";
+ else
+ o << "c";
+ if (legend.m_relativePosition&libsw602::LeftBit)
+ o << "L";
+ else if (legend.m_relativePosition&libsw602::BottomBit)
+ o << "R";
+ else
+ o << "C";
+ o << "]";
+ }
+ else
+ o << "pos=" << legend.m_position << ",";
+ o << legend.m_style;
+ return o;
+}
+
+////////////////////////////////////////////////////////////
+// Serie
+////////////////////////////////////////////////////////////
+SW602Chart::Series::Series() : m_type(SW602Chart::Series::S_Bar), m_range(), m_style()
+{
+ m_style.m_lineWidth=0;
+ m_style.setSurfaceColor(SW602Color(0x80,0x80,0xFF));
+}
+
+SW602Chart::Series::~Series()
+{
+}
+
+std::string SW602Chart::Series::getSeriesTypeName(Type type)
+{
+ switch (type)
+ {
+ case S_Area:
+ return "chart:area";
+ case S_Column:
+ return "chart:column";
+ case S_Line:
+ return "chart:line";
+ case S_Pie:
+ return "chart:pie";
+ case S_Scatter:
+ return "chart:scatter";
+ case S_Stock:
+ return "chart:stock";
+ case S_Bar:
+ return "chart:bar";
+ default:
+ break;
+ }
+ return "chart:bar";
+}
+
+void SW602Chart::Series::addContentTo(std::string const &sheetName, librevenge::RVNGPropertyList &serie) const
+{
+ serie.insert("chart:class",getSeriesTypeName(m_type).c_str());
+ librevenge::RVNGPropertyList range, datapoint;
+ range.insert("librevenge:sheet-name", sheetName.c_str());
+ range.insert("librevenge:start-row", m_range.min()[1]);
+ range.insert("librevenge:start-column", m_range.min()[0]);
+ range.insert("librevenge:end-row", m_range.max()[1]);
+ range.insert("librevenge:end-column", m_range.max()[0]);
+ librevenge::RVNGPropertyListVector vect;
+ vect.append(range);
+ serie.insert("chart:values-cell-range-address", vect);
+ vect.clear();
+ int numPt=m_range.size()[0]>m_range.size()[1] ?
+ m_range.size()[0]+1 : m_range.size()[1]+1;
+ datapoint.insert("librevenge:type", "data-point");
+ datapoint.insert("chart:repeated", numPt);
+ vect.append(datapoint);
+ serie.insert("librevenge:childs", vect);
+}
+
+void SW602Chart::Series::addStyleTo(librevenge::RVNGPropertyList &propList) const
+{
+ m_style.addTo(propList);
+}
+
+std::ostream &operator<<(std::ostream &o, SW602Chart::Series const &series)
+{
+ switch (series.m_type)
+ {
+ case SW602Chart::Series::S_Area:
+ o << "area,";
+ break;
+ case SW602Chart::Series::S_Bar:
+ o << "bar,";
+ break;
+ case SW602Chart::Series::S_Column:
+ o << "column,";
+ break;
+ case SW602Chart::Series::S_Line:
+ o << "line,";
+ break;
+ case SW602Chart::Series::S_Pie:
+ o << "pie,";
+ break;
+ case SW602Chart::Series::S_Scatter:
+ o << "scatter,";
+ break;
+ case SW602Chart::Series::S_Stock:
+ o << "stock,";
+ break;
+ default:
+ o << "###type,";
+ SW602_DEBUG_MSG(("SW602Chart::Series: unexpected type\n"));
+ break;
+ }
+ o << "range=" << series.m_range << ",";
+ o << series.m_style;
+ return o;
+}
+
+////////////////////////////////////////////////////////////
+// TextZone
+////////////////////////////////////////////////////////////
+SW602Chart::TextZone::TextZone() :
+ m_type(SW602Chart::TextZone::T_Title), m_contentType(SW602Chart::TextZone::C_Cell),
+ m_position(-1,-1), m_cell(), m_textEntry(), m_font(), m_style()
+{
+ m_style.m_lineWidth=0;
+}
+
+SW602Chart::TextZone::~TextZone()
+{
+}
+
+void SW602Chart::TextZone::addContentTo(std::string const &sheetName, librevenge::RVNGPropertyList &propList) const
+{
+ if (m_position[0]>=0 && m_position[1]>=0)
+ {
+ propList.insert("svg:x", m_position[0], librevenge::RVNG_POINT);
+ propList.insert("svg:y", m_position[1], librevenge::RVNG_POINT);
+ }
+ switch (m_type)
+ {
+ case T_Title:
+ propList.insert("librevenge:zone-type", "title");
+ break;
+ case T_SubTitle:
+ propList.insert("librevenge:zone-type", "subtitle");
+ break;
+ case T_AxisX:
+ case T_AxisY:
+ case T_AxisZ:
+ propList.insert("librevenge:zone-type", "label");
+ return;
+ default:
+ SW602_DEBUG_MSG(("SW602Chart::TextZone:addContentTo: unexpected type\n"));
+ break;
+ }
+ if (m_contentType==C_Cell)
+ {
+ librevenge::RVNGPropertyList range;
+ librevenge::RVNGPropertyListVector vect;
+ range.insert("librevenge:sheet-name", sheetName.c_str());
+ range.insert("librevenge:row", m_cell[1]);
+ range.insert("librevenge:column", m_cell[0]);
+ vect.append(range);
+ propList.insert("table:cell-range", vect);
+ }
+}
+
+void SW602Chart::TextZone::addStyleTo(librevenge::RVNGPropertyList &propList) const
+{
+ m_font.addTo(propList);
+ m_style.addTo(propList);
+}
+
+std::ostream &operator<<(std::ostream &o, SW602Chart::TextZone const &zone)
+{
+ switch (zone.m_type)
+ {
+ case SW602Chart::TextZone::T_SubTitle:
+ o << "sub";
+ case SW602Chart::TextZone::T_Title:
+ o << "title";
+ if (zone.m_contentType==SW602Chart::TextZone::C_Cell)
+ o << "[" << zone.m_cell << "]";
+ o << ",";
+ break;
+ case SW602Chart::TextZone::T_AxisX:
+ case SW602Chart::TextZone::T_AxisY:
+ case SW602Chart::TextZone::T_AxisZ:
+ if (zone.m_type==SW602Chart::TextZone::T_AxisX)
+ o << "axisX";
+ else if (zone.m_type==SW602Chart::TextZone::T_AxisY)
+ o << "axisY";
+ else
+ o << "axisZ";
+ if (zone.m_contentType==SW602Chart::TextZone::C_Cell)
+ o << "[cells]";
+ o << ",";
+ break;
+ default:
+ o << "###type,";
+ SW602_DEBUG_MSG(("SW602Chart::TextZone: unexpected type\n"));
+ break;
+ }
+ if (zone.m_contentType==SW602Chart::TextZone::C_Text)
+ o << "text,";
+ if (zone.m_position[0]>0 || zone.m_position[1]>0)
+ o << "pos=" << zone.m_position << ",";
+ o << zone.m_style;
+ return o;
+}
+
+}
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602Chart.h b/src/lib/SW602Chart.h
new file mode 100644
index 0000000..391170c
--- /dev/null
+++ b/src/lib/SW602Chart.h
@@ -0,0 +1,234 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/*
+ * Structure to store and construct a chart
+ *
+ */
+
+#ifndef INCLUDED_SW602_CHART_H
+#define INCLUDED_SW602_CHART_H
+
+#include <iostream>
+#include <vector>
+#include <map>
+
+#include "SW602Entry.h"
+#include "SW602Font.h"
+#include "SW602GraphicStyle.h"
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+namespace SW602ChartInternal
+{
+class SubDocument;
+}
+/** a class used to store a chart associated to a spreadsheet .... */
+class SW602Chart
+{
+ friend class SW602ChartInternal::SubDocument;
+public:
+ //! a axis in a chart
+ struct Axis
+ {
+ //! the axis content
+ enum Type { A_None, A_Numeric, A_Logarithmic, A_Sequence, A_Sequence_Skip_Empty };
+ //! constructor
+ Axis();
+ //! destructor
+ ~Axis();
+ //! add content to the propList
+ void addContentTo(std::string const &sheetName, int coord, librevenge::RVNGPropertyList &propList) const;
+ //! add style to the propList
+ void addStyleTo(librevenge::RVNGPropertyList &propList) const;
+ //! operator<<
+ friend std::ostream &operator<<(std::ostream &o, Axis const &axis);
+ //! the sequence type
+ Type m_type;
+ //! show or not the grid
+ bool m_showGrid;
+ //! show or not the label
+ bool m_showLabel;
+ //! the label range if defined
+ SW602Box2i m_labelRange;
+ //! the graphic style
+ SW602GraphicStyle m_style;
+ };
+ //! a legend in a chart
+ struct Legend
+ {
+ //! constructor
+ Legend() : m_show(false), m_autoPosition(true), m_relativePosition(libsw602::RightBit), m_position(0,0), m_font(), m_style()
+ {
+ }
+ //! add content to the propList
+ void addContentTo(librevenge::RVNGPropertyList &propList) const;
+ //! add style to the propList
+ void addStyleTo(librevenge::RVNGPropertyList &propList) const;
+ //! operator<<
+ friend std::ostream &operator<<(std::ostream &o, Legend const &legend);
+ //! show or not the legend
+ bool m_show;
+ //! automatic position
+ bool m_autoPosition;
+ //! the automatic position libsw602::LeftBit|...
+ int m_relativePosition;
+ //! the position in points
+ SW602Vec2f m_position;
+ //! the font
+ SW602Font m_font;
+ //! the graphic style
+ SW602GraphicStyle m_style;
+ };
+ //! a series in a chart
+ struct Series
+ {
+ //! the series type
+ enum Type { S_Area, S_Bar, S_Column, S_Line, S_Pie, S_Scatter, S_Stock };
+ //! constructor
+ Series();
+ //! destructor
+ virtual ~Series();
+ //! add content to the propList
+ void addContentTo(std::string const &sheetName, librevenge::RVNGPropertyList &propList) const;
+ //! add style to the propList
+ void addStyleTo(librevenge::RVNGPropertyList &propList) const;
+ //! returns a string corresponding to a series type
+ static std::string getSeriesTypeName(Type type);
+ //! operator<<
+ friend std::ostream &operator<<(std::ostream &o, Series const &series);
+ //! the type
+ Type m_type;
+ //! the data range
+ SW602Box2i m_range;
+ //! the graphic style
+ SW602GraphicStyle m_style;
+ };
+ //! a text zone a chart
+ struct TextZone
+ {
+ //! the text type
+ enum Type { T_Title, T_SubTitle, T_AxisX, T_AxisY, T_AxisZ };
+ //! the text content type
+ enum ContentType { C_Cell, C_Text };
+
+ //! constructor
+ TextZone();
+ //! destructor
+ ~TextZone();
+ //! add content to the propList
+ void addContentTo(std::string const &sheetName, librevenge::RVNGPropertyList &propList) const;
+ //! add to the propList
+ void addStyleTo(librevenge::RVNGPropertyList &propList) const;
+ //! operator<<
+ friend std::ostream &operator<<(std::ostream &o, TextZone const &zone);
+ //! the zone type
+ Type m_type;
+ //! the content type
+ ContentType m_contentType;
+ //! the position in the zone
+ SW602Vec2f m_position;
+ //! the cell position ( for title and subtitle )
+ SW602Vec2i m_cell;
+ //! the text entry
+ SW602Entry m_textEntry;
+ //! the zone format
+ SW602Font m_font;
+ //! the graphic style
+ SW602GraphicStyle m_style;
+ };
+
+ //! the constructor
+ SW602Chart(std::string const &sheetName, SW602Vec2f const &dim=SW602Vec2f());
+ //! the destructor
+ virtual ~SW602Chart();
+ //! send the chart to the listener
+ void sendChart(SW602SpreadsheetListenerPtr &listener, librevenge::RVNGSpreadsheetInterface *interface);
+ //! send the zone content (called when the zone is of text type)
+ virtual void sendContent(TextZone const &zone, SW602ListenerPtr &listener)=0;
+
+ //! sets the chart type
+ void setDataType(Series::Type type, bool dataStacked)
+ {
+ m_type=type;
+ m_dataStacked=dataStacked;
+ }
+
+ //! return the chart dimension
+ SW602Vec2f const &getDimension() const
+ {
+ return m_dim;
+ }
+ //! return the chart dimension
+ void setDimension(SW602Vec2f const &dim)
+ {
+ m_dim=dim;
+ }
+ //! adds an axis (corresponding to a coord)
+ void add(int coord, Axis const &axis);
+ //! return an axis (corresponding to a coord)
+ Axis const &getAxis(int coord) const;
+
+ //! set the legend
+ void set(Legend const &legend)
+ {
+ m_legend=legend;
+ }
+ //! return the legend
+ Legend const &getLegend() const
+ {
+ return m_legend;
+ }
+
+ //! adds a series
+ void add(Series const &series);
+ //! return the list of series
+ std::vector<Series> const &getSeries() const
+ {
+ return m_seriesList;
+ }
+
+ //! adds a textzone
+ void add(TextZone const &textZone);
+ //! returns a textzone content(if set)
+ bool getTextZone(TextZone::Type type, TextZone &textZone);
+
+protected:
+ //! sends a textzone content
+ void sendTextZoneContent(TextZone::Type type, SW602ListenerPtr &listener);
+
+protected:
+ //! the sheet name
+ std::string m_sheetName;
+ //! the chart dimension in point
+ SW602Vec2f m_dim;
+ //! the chart type (if no series)
+ Series::Type m_type;
+ //! a flag to know if the data are stacked or not
+ bool m_dataStacked;
+ //! the x,y,z axis
+ Axis m_axis[3];
+ //! the legend
+ Legend m_legend;
+ //! the list of series
+ std::vector<Series> m_seriesList;
+ //! a map text zone type to text zone
+ std::map<TextZone::Type, TextZone> m_textZoneMap;
+private:
+ explicit SW602Chart(SW602Chart const &orig);
+ SW602Chart &operator=(SW602Chart const &orig);
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602Debug.cpp b/src/lib/SW602Debug.cpp
new file mode 100644
index 0000000..538876c
--- /dev/null
+++ b/src/lib/SW602Debug.cpp
@@ -0,0 +1,229 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "SW602Debug.h"
+
+#if defined(DEBUG_WITH_FILES)
+
+#include <iomanip>
+#include <iostream>
+#include <set>
+
+#include "SW602Types.h"
+#include "libsw602_utils.h"
+
+namespace libsw602
+{
+bool DebugFile::open(std::string const &filename)
+{
+ m_on=true;
+ m_fileName=filename;
+ return true;
+}
+
+void DebugFile::addPos(long pos)
+{
+ if (!m_on) return;
+ m_actOffset = pos;
+}
+
+void DebugFile::addNote(char const *note)
+{
+ if (!m_on || note == 0L) return;
+
+ size_t numNotes = m_notes.size();
+
+ if (!numNotes || m_notes[numNotes-1].m_pos != m_actOffset)
+ {
+ std::string empty("");
+ m_notes.push_back(NotePos(m_actOffset, empty));
+ numNotes++;
+ }
+ m_notes[numNotes-1].m_text += std::string(note);
+}
+
+void DebugFile::addDelimiter(long pos, char c)
+{
+ if (!m_on) return;
+ std::string s;
+ s+=c;
+ m_notes.push_back(NotePos(pos,s,false));
+}
+
+void DebugFile::sort()
+{
+ if (!m_on) return;
+ size_t numNotes = m_notes.size();
+
+ if (m_actOffset >= 0 && (numNotes == 0 || m_notes[numNotes-1].m_pos != m_actOffset))
+ {
+ std::string empty("");
+ m_notes.push_back(NotePos(m_actOffset, empty));
+ numNotes++;
+ }
+
+ std::set<NotePos, NotePos::NotePosLt> set;
+ for (size_t i = 0; i < numNotes; i++)
+ set.insert(m_notes[i]);
+
+ size_t i = 0;
+ for (std::set<NotePos, NotePos::NotePosLt>::const_iterator it = set.begin();
+ it != set.end(); ++i)
+ m_notes[i] = *(it++);
+ if (i != numNotes) m_notes.resize(i);
+
+ SW602Vec2i::MapX sMap;
+ size_t numSkip = m_skipZones.size();
+ for (i = 0; i < numSkip; i++) sMap[m_skipZones[i]] = 0;
+
+ i = 0;
+ for (SW602Vec2i::MapX::iterator it = sMap.begin(); it != sMap.end(); ++it)
+ m_skipZones[i++] = it->first;
+ if (i < numSkip) m_skipZones.resize(i);
+}
+
+void DebugFile::write()
+{
+ if (!m_on || m_input.get() == 0) return;
+
+ std::string name=Debug::flattenFileName(m_fileName);
+ if (name.empty()) return;
+ name += ".ascii";
+ m_file.open(name.c_str());
+ if (!m_file.is_open()) return;
+ sort();
+
+ long readPos = m_input->tell();
+
+ std::vector<NotePos>::const_iterator noteIter = m_notes.begin();
+
+ //! write the notes which does not have any position
+ while (noteIter != m_notes.end() && noteIter->m_pos < 0)
+ {
+ if (!noteIter->m_text.empty())
+ std::cerr << "DebugFile::write: skipped: " << noteIter->m_text << std::endl;
+ ++noteIter;
+ }
+
+ long actualPos = 0;
+ int numSkip = int(m_skipZones.size()), actSkip = (numSkip == 0) ? -1 : 0;
+ long actualSkipEndPos = (numSkip == 0) ? -1 : m_skipZones[0].x();
+
+ m_input->seek(0,librevenge::RVNG_SEEK_SET);
+ m_file << std::hex << std::right << std::setfill('0') << std::setw(6) << 0 << " ";
+
+ do
+ {
+ bool printAdr = false;
+ bool stop = false;
+ while (actualSkipEndPos != -1 && actualPos >= actualSkipEndPos)
+ {
+ printAdr = true;
+ actualPos = m_skipZones[size_t(actSkip)].y()+1;
+ m_file << "\nSkip : " << std::hex << std::setw(6) << actualSkipEndPos << "-"
+ << actualPos-1 << "\n\n";
+ m_input->seek(actualPos, librevenge::RVNG_SEEK_SET);
+ stop = m_input->isEnd();
+ actSkip++;
+ actualSkipEndPos = (actSkip < numSkip) ? m_skipZones[size_t(actSkip)].x() : -1;
+ }
+ if (stop) break;
+ while (noteIter != m_notes.end() && noteIter->m_pos < actualPos)
+ {
+ if (!noteIter->m_text.empty())
+ m_file << "Skipped: " << noteIter->m_text << std::endl;
+ ++noteIter;
+ }
+ bool printNote = noteIter != m_notes.end() && noteIter->m_pos == actualPos;
+ if (printAdr || (printNote && noteIter->m_breaking))
+ m_file << "\n" << std::setw(6) << actualPos << " ";
+ while (noteIter != m_notes.end() && noteIter->m_pos == actualPos)
+ {
+ if (noteIter->m_text.empty())
+ {
+ ++noteIter;
+ continue;
+ }
+ if (noteIter->m_breaking)
+ m_file << "[" << noteIter->m_text << "]";
+ else
+ m_file << noteIter->m_text;
+ ++noteIter;
+ }
+
+ unsigned ch = readU8(*m_input);
+ m_file << std::setw(2) << ch;
+ actualPos++;
+
+ }
+ while (!m_input->isEnd());
+
+ m_file << "\n\n";
+
+ m_input->seek(readPos,librevenge::RVNG_SEEK_SET);
+
+ m_actOffset=-1;
+ m_notes.resize(0);
+}
+
+////////////////////////////////////////////////////////////
+//
+// save librevenge::RVNGBinaryData in a file
+//
+////////////////////////////////////////////////////////////
+namespace Debug
+{
+bool dumpFile(librevenge::RVNGBinaryData &data, char const *fileName)
+{
+ if (!fileName) return false;
+ std::string fName = Debug::flattenFileName(fileName);
+ if (!data.size() || !data.getDataBuffer())
+ {
+ SW602_DEBUG_MSG(("Debug::dumpFile: can not find data for %s\n", fileName));
+ return false;
+ }
+ FILE *file = fopen(fName.c_str(), "wb");
+ if (!file) return false;
+ fwrite(data.getDataBuffer(), data.size(), 1, file);
+ fclose(file);
+ return true;
+}
+
+std::string flattenFileName(std::string const &name)
+{
+ std::string res;
+ for (size_t i = 0; i < name.length(); i++)
+ {
+ unsigned char c = (unsigned char) name[i];
+ switch (c)
+ {
+ case '\0':
+ case '/':
+ case '\\':
+ case ':': // potential file system separator
+ case ' ':
+ case '\t':
+ case '\n': // potential text separator
+ res += '_';
+ break;
+ default:
+ if (c <= 28) res += '#'; // other trouble potential char
+ else if (c > 0x80) res += '#'; // other trouble potential char
+ else res += char(c);
+ }
+ }
+ return res;
+}
+}
+
+}
+
+#endif
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602Debug.h b/src/lib/SW602Debug.h
new file mode 100644
index 0000000..bfca716
--- /dev/null
+++ b/src/lib/SW602Debug.h
@@ -0,0 +1,219 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SW602_DEBUG_H
+#define INCLUDED_SW602_DEBUG_H
+
+#include <string>
+
+#include <boost/shared_ptr.hpp>
+
+#include <librevenge/librevenge.h>
+#include <librevenge-stream/librevenge-stream.h>
+
+#if defined(DEBUG_WITH_FILES)
+
+#include <fstream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "SW602Types.h"
+
+//! some basic tools
+namespace libsw602
+{
+
+//! debugging tools
+namespace Debug
+{
+//! a debug function to store in a datafile in the current directory
+//! WARNING: this function erase the file fileName if it exists
+//! (if debug_with_files is not defined, does nothing)
+bool dumpFile(librevenge::RVNGBinaryData &data, char const *fileName);
+//! returns a file name from an ole/... name
+std::string flattenFileName(std::string const &name);
+}
+
+//! a basic stream (if debug_with_files is not defined, does nothing)
+typedef std::stringstream DebugStream;
+
+//! an interface used to insert comment in a binary file,
+//! written in ascii form (if debug_with_files is not defined, does nothing)
+class DebugFile
+{
+public:
+ //! constructor given the input file
+ explicit DebugFile(boost::shared_ptr<librevenge::RVNGInputStream> ip)
+ : m_fileName(""), m_file(), m_on(false), m_input(ip), m_actOffset(-1), m_notes(), m_skipZones() { }
+
+ //! resets the input
+ void setStream(boost::shared_ptr<librevenge::RVNGInputStream> ip)
+ {
+ m_input = ip;
+ }
+ //! destructor
+ ~DebugFile()
+ {
+ reset();
+ }
+ //! opens/creates a file to store a result
+ bool open(std::string const &filename);
+ //! writes the current file and reset to zero
+ void reset()
+ {
+ write();
+ m_fileName="";
+ m_file.close();
+ m_on = false;
+ m_notes.resize(0);
+ m_skipZones.resize(0);
+ m_actOffset = -1;
+ }
+ //! flushes the file
+ void write();
+ //! adds a new position in the file
+ void addPos(long pos);
+ //! adds a note in the file, in actual position
+ void addNote(char const *note);
+ //! adds a not breaking delimiter in position \a pos
+ void addDelimiter(long pos, char c);
+
+ //! skips the file zone defined by beginPos-endPos
+ void skipZone(long beginPos, long endPos)
+ {
+ if (m_on) m_skipZones.push_back(SW602Vec2<long>(beginPos, endPos));
+ }
+
+protected:
+ //! sorts the position/note date
+ void sort();
+
+ //! the file name
+ mutable std::string m_fileName;
+ //! a stream which is open to write the file
+ mutable std::ofstream m_file;
+ //! a flag to know if the result stream is open or note
+ mutable bool m_on;
+
+ //! the input
+ boost::shared_ptr<librevenge::RVNGInputStream> m_input;
+
+ //! \brief a note and its position (used to sort all notes)
+ struct NotePos
+ {
+ //! empty constructor used by std::vector
+ NotePos() : m_pos(-1), m_text(""), m_breaking(false) { }
+
+ //! constructor: given position and note
+ NotePos(long p, std::string const &n, bool br=true) : m_pos(p), m_text(n), m_breaking(br) {}
+ //! note offset
+ long m_pos;
+ //! note text
+ std::string m_text;
+ //! flag to indicate a non breaking note
+ bool m_breaking;
+
+ //! comparison operator based on the position
+ bool operator<(NotePos const &p) const
+ {
+ long diff = m_pos-p.m_pos;
+ if (diff) return (diff < 0) ? true : false;
+ if (m_breaking != p.m_breaking) return m_breaking;
+ return m_text < p.m_text;
+ }
+ /*! \struct NotePosLt
+ * \brief internal struct used to sort the notes, sorted by position
+ */
+ struct NotePosLt
+ {
+ //! comparison operator
+ bool operator()(NotePos const &s1, NotePos const &s2) const
+ {
+ return s1 < s2;
+ }
+ };
+ };
+
+ //! the actual offset (used to store note)
+ long m_actOffset;
+ //! list of notes
+ std::vector<NotePos> m_notes;
+ //! list of skipZone
+ std::vector<SW602Vec2<long> > m_skipZones;
+};
+
+}
+
+#else
+
+namespace libsw602
+{
+
+namespace Debug
+{
+
+bool dumpFile(librevenge::RVNGBinaryData &, char const *)
+{
+ return true;
+}
+//! returns a file name from an ole/... name
+std::string flattenFileName(std::string const &name)
+{
+ return name;
+}
+
+}
+
+class DebugStream
+{
+public:
+ template <class T>
+ DebugStream &operator<<(T const &)
+ {
+ return *this;
+ }
+
+ static std::string str()
+ {
+ return std::string("");
+ }
+ static void str(std::string const &) { }
+};
+
+class DebugFile
+{
+public:
+ explicit DebugFile(boost::shared_ptr<librevenge::RVNGInputStream>) {}
+ DebugFile() {}
+ static void setStream(boost::shared_ptr<librevenge::RVNGInputStream>) { }
+ ~DebugFile() { }
+
+ static bool open(std::string const &)
+ {
+ return true;
+ }
+
+ static void addPos(long) {}
+ static void addNote(char const *) {}
+ static void addDelimiter(long, char) {}
+
+ static void write() {}
+ static void reset() { }
+
+ static void skipZone(long , long) {}
+};
+
+}
+
+#endif
+
+#endif
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602Entry.h b/src/lib/SW602Entry.h
new file mode 100644
index 0000000..c8a9b83
--- /dev/null
+++ b/src/lib/SW602Entry.h
@@ -0,0 +1,180 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SW602_ENTRY_H
+#define INCLUDED_SW602_ENTRY_H
+
+#include <ostream>
+#include <string>
+
+namespace libsw602
+{
+
+/** \brief basic class to store an entry in a file
+ * This contained :
+ * - its begin and end positions
+ * - its type, its name and an identificator
+ * - a flag used to know if the file is or not parsed
+ */
+class SW602Entry
+{
+public:
+ //!constructor
+ SW602Entry() : m_begin(-1), m_length(-1), m_type(""), m_name(""), m_id(-1), m_parsed(false), m_extra("") {}
+
+ virtual ~SW602Entry() {}
+
+ //! sets the begin offset
+ void setBegin(long off)
+ {
+ m_begin = off;
+ }
+ //! sets the zone size
+ void setLength(long l)
+ {
+ m_length = l;
+ }
+ //! sets the end offset
+ void setEnd(long off)
+ {
+ m_length = off-m_begin;
+ }
+
+ //! returns the begin offset
+ long begin() const
+ {
+ return m_begin;
+ }
+ //! returns the end offset
+ long end() const
+ {
+ return m_begin+m_length;
+ }
+ //! returns the length of the zone
+ long length() const
+ {
+ return m_length;
+ }
+
+ //! returns true if the zone length is positive
+ bool valid() const
+ {
+ return m_begin >= 0 && m_length > 0;
+ }
+
+ //! basic operator==
+ bool operator==(const SW602Entry &a) const
+ {
+ if (m_begin != a.m_begin) return false;
+ if (m_length != a.m_length) return false;
+ if (m_id != a. m_id) return false;
+ if (m_type != a.m_type) return false;
+ if (m_name != a.m_name) return false;
+ return true;
+ }
+ //! basic operator!=
+ bool operator!=(const SW602Entry &a) const
+ {
+ return !operator==(a);
+ }
+
+ //! a flag to know if the entry was parsed or not
+ bool isParsed() const
+ {
+ return m_parsed;
+ }
+ //! sets the flag m_parsed to true or false
+ void setParsed(bool ok=true) const
+ {
+ m_parsed = ok;
+ }
+
+ //! sets the type of the entry: BTEP,FDPP, BTEC, FDPC, PLC , TEXT, ...
+ void setType(std::string const &newType)
+ {
+ m_type=newType;
+ }
+ //! returns the type of the entry
+ std::string const &type() const
+ {
+ return m_type;
+ }
+ //! returns true if the type entry == \a type
+ bool hasType(std::string const &typ) const
+ {
+ return m_type == typ;
+ }
+
+ //! sets the name of the entry
+ void setName(std::string const &nam)
+ {
+ m_name=nam;
+ }
+ //! name of the entry
+ std::string const &name() const
+ {
+ return m_name;
+ }
+ //! checks if the entry name is equal to \a name
+ bool hasName(std::string const &nam) const
+ {
+ return m_name == nam;
+ }
+
+ /** \brief returns the id */
+ int id() const
+ {
+ return m_id;
+ }
+ //! sets the id
+ void setId(int newId)
+ {
+ m_id = newId;
+ }
+
+ //! retrieves the extra string
+ std::string const &extra() const
+ {
+ return m_extra;
+ }
+ //! sets the extra string
+ void setExtra(std::string const &s)
+ {
+ m_extra = s;
+ }
+
+ friend std::ostream &operator<< (std::ostream &o, SW602Entry const &ent)
+ {
+ o << ent.m_type;
+ if (ent.m_name.length()) o << "|" << ent.m_name;
+ if (ent.m_id >= 0) o << "[" << ent.m_id << "]";
+ if (ent.m_extra.length()) o << "[" << ent.m_extra << "]";
+ return o;
+ }
+
+protected:
+ long m_begin /** the begin of the entry.*/, m_length /** the size of the entry*/;
+
+ //! the entry type
+ std::string m_type;
+ //! the name
+ std::string m_name;
+ //! an identificator
+ int m_id;
+ //! a bool to store if the entry is or not parsed
+ mutable bool m_parsed;
+ //! an extra string
+ std::string m_extra;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602Font.cpp b/src/lib/SW602Font.cpp
new file mode 100644
index 0000000..5d56239
--- /dev/null
+++ b/src/lib/SW602Font.cpp
@@ -0,0 +1,340 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "SW602Font.h"
+
+#include <sstream>
+
+#include <librevenge/librevenge.h>
+
+#include "SW602Position.h"
+#include "SW602TextListener.h"
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+// line function
+
+std::ostream &operator<<(std::ostream &o, SW602Font::Line const &line)
+{
+ if (!line.isSet())
+ return o;
+ switch (line.m_style)
+ {
+ case SW602Font::Line::Dot:
+ o << "dotted";
+ break;
+ case SW602Font::Line::LargeDot:
+ o << "dotted[large]";
+ break;
+ case SW602Font::Line::Dash:
+ o << "dash";
+ break;
+ case SW602Font::Line::Simple:
+ o << "solid";
+ break;
+ case SW602Font::Line::Wave:
+ o << "wave";
+ break;
+ case SW602Font::Line::None:
+ default:
+ break;
+ }
+ switch (line.m_type)
+ {
+ case SW602Font::Line::Double:
+ o << ":double";
+ break;
+ case SW602Font::Line::Triple:
+ o << ":triple";
+ break;
+ case SW602Font::Line::Single:
+ default:
+ break;
+ }
+ if (line.m_word) o << ":byword";
+ if (line.m_width < 1.0 || line.m_width > 1.0)
+ o << ":w=" << line.m_width ;
+ if (line.m_color.isSet())
+ o << ":col=" << line.m_color.get();
+ return o;
+}
+
+void SW602Font::Line::addTo(librevenge::RVNGPropertyList &propList, std::string const &type) const
+{
+ if (!isSet()) return;
+
+ std::stringstream s;
+ s << "style:text-" << type << "-type";
+ propList.insert(s.str().c_str(), (m_type==Single) ? "single" : "double");
+
+ if (m_word)
+ {
+ s.str("");
+ s << "style:text-" << type << "-mode";
+ propList.insert(s.str().c_str(), "skip-white-space");
+ }
+
+ s.str("");
+ s << "style:text-" << type << "-style";
+ switch (m_style)
+ {
+ case Dot:
+ case LargeDot:
+ propList.insert(s.str().c_str(), "dotted");
+ break;
+ case Dash:
+ propList.insert(s.str().c_str(), "dash");
+ break;
+ case Simple:
+ propList.insert(s.str().c_str(), "solid");
+ break;
+ case Wave:
+ propList.insert(s.str().c_str(), "wave");
+ break;
+ case None:
+ default:
+ break;
+ }
+ if (m_color.isSet())
+ {
+ s.str("");
+ s << "style:text-" << type << "-color";
+ propList.insert(s.str().c_str(), m_color.get().str().c_str());
+ }
+ //normal, bold, thin, dash, medium, and thick
+ s.str("");
+ s << "style:text-" << type << "-width";
+ if (m_width <= 0.6)
+ propList.insert(s.str().c_str(), "thin");
+ else if (m_width >= 1.5)
+ propList.insert(s.str().c_str(), "thick");
+}
+
+// script function
+
+std::string SW602Font::Script::str(float fSize) const
+{
+ if (!isSet() || ((m_delta<=0&&m_delta>=0) && m_scale==100))
+ return "";
+ std::stringstream o;
+ if (m_deltaUnit == librevenge::RVNG_GENERIC)
+ {
+ SW602_DEBUG_MSG(("SW602Font::Script::str: can not be called with generic position\n"));
+ return "";
+ }
+ float delta = m_delta;
+ if (m_deltaUnit != librevenge::RVNG_PERCENT)
+ {
+ // first transform in point
+ if (m_deltaUnit != librevenge::RVNG_POINT)
+ delta=SW602Position::getScaleFactor(m_deltaUnit, librevenge::RVNG_POINT)*delta;
+ // now transform in percent
+ if (fSize<=0)
+ {
+ static bool first=true;
+ if (first)
+ {
+ SW602_DEBUG_MSG(("SW602Font::Script::str: can not be find font size (supposed 12pt)\n"));
+ first = false;
+ }
+ fSize=12;
+ }
+ delta=100.f*delta/fSize;
+ if (delta > 100) delta = 100;
+ else if (delta < -100) delta = -100;
+ }
+ o << delta << "% " << m_scale << "%";
+ return o.str();
+}
+
+// font function
+
+std::string SW602Font::getDebugString() const
+{
+ std::stringstream o;
+ o << std::dec;
+ if (id() != -1)
+ {
+ o << "id=" << id() << ",";
+ }
+ if (size() > 0) o << "sz=" << size() << ",";
+ if (m_deltaSpacing.isSet())
+ {
+ if (m_deltaSpacingUnit.get()==librevenge::RVNG_PERCENT)
+ o << "extend/condensed=" << m_deltaSpacing.get() << "%,";
+ else if (m_deltaSpacing.get() > 0)
+ o << "extended=" << m_deltaSpacing.get() << ",";
+ else if (m_deltaSpacing.get() < 0)
+ o << "condensed=" << -m_deltaSpacing.get() << ",";
+ }
+ if (m_widthStreching.isSet())
+ o << "scaling[width]=" << m_widthStreching.get()*100.f << "%,";
+ if (m_scriptPosition.isSet() && m_scriptPosition.get().isSet())
+ o << "script=" << m_scriptPosition.get().str(size()) << ",";
+ if (m_flags.isSet() && m_flags.get())
+ {
+ o << "fl=";
+ uint32_t flag = m_flags.get();
+ if (flag&boldBit) o << "b:";
+ if (flag&italicBit) o << "it:";
+ if (flag&embossBit) o << "emboss:";
+ if (flag&shadowBit) o << "shadow:";
+ if (flag&outlineBit) o << "outline:";
+ if (flag&smallCapsBit) o << "smallCaps:";
+ if (flag&uppercaseBit) o << "uppercase:";
+ if (flag&lowercaseBit) o << "lowercase:";
+ if (flag&initialcaseBit) o << "capitalise:";
+ if (flag&hiddenBit) o << "hidden:";
+ if (flag&reverseVideoBit) o << "reverseVideo:";
+ if (flag&blinkBit) o << "blink:";
+ if (flag&boxedBit) o << "box:";
+ if (flag&boxedRoundedBit) o << "box[rounded]:";
+ if (flag&reverseWritingBit) o << "reverseWriting:";
+ o << ",";
+ }
+ if (m_overline.isSet() && m_overline->isSet())
+ o << "overline=[" << m_overline.get() << "],";
+ if (m_strikeoutline.isSet() && m_strikeoutline->isSet())
+ o << "strikeOut=[" << m_strikeoutline.get() << "],";
+ if (m_underline.isSet() && m_underline->isSet())
+ o << "underline=[" << m_underline.get() << "],";
+ if (hasColor())
+ o << "col=" << m_color.get()<< ",";
+ if (m_backgroundColor.isSet() && !m_backgroundColor.get().isWhite())
+ o << "backCol=" << m_backgroundColor.get() << ",";
+ if (m_language.isSet() && m_language.get().length())
+ o << "lang=" << m_language.get() << ",";
+ o << m_extra;
+ return o.str();
+}
+
+void SW602Font::addTo(librevenge::RVNGPropertyList &pList) const
+{
+ int dSize = 0;
+ std::string fName("");
+#if 0
+ if (!convert)
+ {
+ SW602_DEBUG_MSG(("SW602Font::addTo: called without any font converter\n"));
+ }
+ else
+ convert->getOdtInfo(id(), fName, dSize);
+#endif
+ if (fName.length())
+ pList.insert("style:font-name", fName.c_str());
+ float fSize = size()+float(dSize);
+ pList.insert("fo:font-size", fSize, librevenge::RVNG_POINT);
+
+ uint32_t attributeBits = m_flags.get();
+ if (attributeBits & italicBit)
+ pList.insert("fo:font-style", "italic");
+ if (attributeBits & boldBit)
+ pList.insert("fo:font-weight", "bold");
+ if (attributeBits & outlineBit)
+ pList.insert("style:text-outline", "true");
+ if (attributeBits & blinkBit)
+ pList.insert("style:text-blinking", "true");
+ if (attributeBits & shadowBit)
+ pList.insert("fo:text-shadow", "1pt 1pt");
+ if (attributeBits & hiddenBit)
+ pList.insert("text:display", "none");
+ if (attributeBits & lowercaseBit)
+ pList.insert("fo:text-transform", "lowercase");
+ else if (attributeBits & uppercaseBit)
+ pList.insert("fo:text-transform", "uppercase");
+ else if (attributeBits & initialcaseBit)
+ pList.insert("fo:text-transform", "capitalize");
+ if (attributeBits & smallCapsBit)
+ pList.insert("fo:font-variant", "small-caps");
+ if (attributeBits & embossBit)
+ pList.insert("style:font-relief", "embossed");
+ else if (attributeBits & engraveBit)
+ pList.insert("style:font-relief", "engraved");
+
+ if (m_scriptPosition.isSet() && m_scriptPosition->isSet())
+ {
+ std::string pos=m_scriptPosition->str(fSize);
+ if (pos.length())
+ pList.insert("style:text-position", pos.c_str());
+ }
+
+ if (m_overline.isSet() && m_overline->isSet())
+ m_overline->addTo(pList, "overline");
+ if (m_strikeoutline.isSet() && m_strikeoutline->isSet())
+ m_strikeoutline->addTo(pList, "line-through");
+ if (m_underline.isSet() && m_underline->isSet())
+ m_underline->addTo(pList, "underline");
+ if ((attributeBits & boxedBit) || (attributeBits & boxedRoundedBit))
+ {
+ // do minimum: add a overline and a underline box
+ Line simple(Line::Simple);
+ if (!m_overline.isSet() || !m_overline->isSet())
+ simple.addTo(pList, "overline");
+ if (!m_underline.isSet() || !m_underline->isSet())
+ simple.addTo(pList, "underline");
+ }
+ if (m_deltaSpacing.isSet())
+ {
+ if (m_deltaSpacingUnit.get()==librevenge::RVNG_PERCENT)
+ {
+ if (m_deltaSpacing.get() < 1 || m_deltaSpacing.get()>1)
+ {
+ std::stringstream s;
+ s << m_deltaSpacing.get() << "em";
+ pList.insert("fo:letter-spacing", s.str().c_str());
+ }
+ }
+ else if (m_deltaSpacing.get() < 0 || m_deltaSpacing.get()>0)
+ pList.insert("fo:letter-spacing", m_deltaSpacing.get(), librevenge::RVNG_POINT);
+ }
+ if (m_widthStreching.isSet() && m_widthStreching.get() > 0.0 &&
+ (m_widthStreching.get()>1.0||m_widthStreching.get()<1.0))
+ pList.insert("style:text-scale", m_widthStreching.get(), librevenge::RVNG_PERCENT);
+ if (attributeBits & reverseVideoBit)
+ {
+ pList.insert("fo:color", m_backgroundColor->str().c_str());
+ pList.insert("fo:background-color", m_color->str().c_str());
+ }
+ else
+ {
+ pList.insert("fo:color", m_color->str().c_str());
+ if (m_backgroundColor.isSet() && !m_backgroundColor->isWhite())
+ pList.insert("fo:background-color", m_backgroundColor->str().c_str());
+ }
+ if (m_language.isSet())
+ {
+ size_t len=m_language->length();
+ std::string lang(m_language.get());
+ std::string country("none");
+ if (len > 3 && lang[2]=='_')
+ {
+ country=lang.substr(3);
+ lang=m_language->substr(0,2);
+ }
+ else if (len==0)
+ lang="none";
+ pList.insert("fo:language", lang.c_str());
+ pList.insert("fo:country", country.c_str());
+ }
+ if (attributeBits & reverseWritingBit)
+ {
+ static bool first = true;
+ if (first)
+ {
+ first = false;
+ SW602_DEBUG_MSG(("SW602Font::addTo: sorry, reverse writing is not umplemented\n"));
+ }
+ }
+}
+
+}
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602Font.h b/src/lib/SW602Font.h
new file mode 100644
index 0000000..524d05f
--- /dev/null
+++ b/src/lib/SW602Font.h
@@ -0,0 +1,534 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SW602_FONT_H
+#define INCLUDED_SW602_FONT_H
+
+#include <string>
+#include <vector>
+
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+//! Class to store font
+class SW602Font
+{
+public:
+ /** a small struct to define a line in SW602Font */
+ struct Line
+ {
+ /** the line style */
+ enum Style { None, Simple, Dot, LargeDot, Dash, Wave };
+ /** the line style */
+ enum Type { Single, Double, Triple };
+ //! constructor
+ Line(Style style=None, Type type=Single, bool wordFlag=false, float w=1.0) :
+ m_style(style), m_type(type), m_word(wordFlag), m_width(w), m_color(SW602Color::black()) { }
+ //! return true if the line is not empty
+ bool isSet() const
+ {
+ return m_style != None && m_width>0;
+ }
+ //! add a line to the propList knowing the type (line-through, underline, overline )
+ void addTo(librevenge::RVNGPropertyList &propList, std::string const &type) const;
+ //! operator<<
+ friend std::ostream &operator<<(std::ostream &o, Line const &line);
+ //! operator==
+ bool operator==(Line const &oth) const
+ {
+ return cmp(oth)==0;
+ }
+ //! operator!=
+ bool operator!=(Line const &oth) const
+ {
+ return cmp(oth)!=0;
+ }
+ //! small comparison function
+ int cmp(Line const &oth) const
+ {
+ if (m_style != oth.m_style) return int(m_style)-int(oth.m_style);
+ if (m_type != oth.m_type) return int(m_type)-int(oth.m_type);
+ if (m_word != oth.m_word) return m_word ? -1 : 1;
+ if (m_width < oth.m_width) return -1;
+ if (m_width > oth.m_width) return 1;
+ if (m_color.isSet() != oth.m_color.isSet())
+ return m_color.isSet();
+ if (m_color.get() < oth.m_color.get()) return -1;
+ if (m_color.get() > oth.m_color.get()) return 1;
+ return 0;
+ }
+ /** the style */
+ Style m_style;
+ /** the type */
+ Type m_type;
+ /** word or not word line */
+ bool m_word;
+ /** the width in point */
+ float m_width;
+ /** the color ( if not set, we use the font color )*/
+ SW602Variable<SW602Color> m_color;
+ };
+ /** a small struct to define the script position in SW602Font */
+ struct Script
+ {
+ //! constructor
+ Script(float delta=0, librevenge::RVNGUnit deltaUnit=librevenge::RVNG_PERCENT, int scale=100) :
+ m_delta(delta), m_deltaUnit(deltaUnit), m_scale(scale)
+ {
+ }
+ //! return true if the position is not default
+ bool isSet() const
+ {
+ return *this != Script();
+ }
+ //! return a yposition which correspond to a basic subscript
+ static Script sub()
+ {
+ return Script(-33,librevenge::RVNG_PERCENT,58);
+ }
+ //! return a yposition which correspond to a basic subscript100
+ static Script sub100()
+ {
+ return Script(-20);
+ }
+ //! return a yposition which correspond to a basic superscript
+ static Script super()
+ {
+ return Script(33,librevenge::RVNG_PERCENT,58);
+ }
+ //! return a yposition which correspond to a basic superscript100
+ static Script super100()
+ {
+ return Script(20);
+ }
+ //! return a string which correspond to style:text-position
+ std::string str(float fSize) const;
+
+ //! operator==
+ bool operator==(Script const &oth) const
+ {
+ return cmp(oth)==0;
+ }
+ //! operator!=
+ bool operator!=(Script const &oth) const
+ {
+ return cmp(oth)!=0;
+ }
+ //! operator<
+ bool operator<(Script const &oth) const
+ {
+ return cmp(oth)<0;
+ }
+ //! operator<=
+ bool operator<=(Script const &oth) const
+ {
+ return cmp(oth)<=0;
+ }
+ //! operator>
+ bool operator>(Script const &oth) const
+ {
+ return cmp(oth)>0;
+ }
+ //! operator>=
+ bool operator>=(Script const &oth) const
+ {
+ return cmp(oth)>=0;
+ }
+ //! small comparison function
+ int cmp(Script const &oth) const
+ {
+ if (m_delta > oth.m_delta) return -1;
+ if (m_delta < oth.m_delta) return 1;
+ if (m_deltaUnit != oth.m_deltaUnit) return int(m_deltaUnit)-int(oth.m_deltaUnit);
+ if (m_scale != oth.m_scale) return m_scale-oth.m_scale;
+ return 0;
+ }
+ //! the ydelta
+ float m_delta;
+ //! the ydelta unit ( point or percent )
+ librevenge::RVNGUnit m_deltaUnit;
+ //! the font scaling ( in percent )
+ int m_scale;
+ };
+
+ //! the different font bit
+ enum FontBits { boldBit=1, italicBit=2, blinkBit=4, embossBit=8, engraveBit=0x10,
+ hiddenBit=0x20, outlineBit=0x40, shadowBit=0x80,
+ reverseVideoBit=0x100, smallCapsBit=0x200, uppercaseBit=0x400,
+ lowercaseBit=0x800,
+ initialcaseBit=2*lowercaseBit,
+ boxedBit=2*initialcaseBit,
+ boxedRoundedBit=2*boxedBit,
+ reverseWritingBit=2*boxedRoundedBit
+ };
+ /** constructor
+ *
+ * \param newId system id font
+ * \param sz the font size
+ * \param f the font attributes bold, ... */
+ SW602Font(int newId=-1, float sz=12, uint32_t f = 0) : m_id(newId), m_size(sz), m_deltaSpacing(0), m_deltaSpacingUnit(librevenge::RVNG_POINT), m_widthStreching(1), m_scriptPosition(),
+ m_flags(f), m_overline(Line::None), m_strikeoutline(Line::None), m_underline(Line::None),
+ m_color(SW602Color::black()), m_backgroundColor(SW602Color::white()), m_language(""), m_extra("")
+ {
+ resetColor();
+ };
+ //! returns true if the font id is initialized
+ bool isSet() const
+ {
+ return m_id.isSet();
+ }
+ //! inserts the set value in the current font
+ void insert(SW602Font const &ft)
+ {
+ m_id.insert(ft.m_id);
+ m_size.insert(ft.m_size);
+ m_deltaSpacing.insert(ft.m_deltaSpacing);
+ m_deltaSpacingUnit.insert(ft.m_deltaSpacingUnit);
+ m_widthStreching.insert(ft.m_widthStreching);
+ m_scriptPosition.insert(ft.m_scriptPosition);
+ if (ft.m_flags.isSet())
+ {
+ if (m_flags.isSet())
+ setFlags(flags()| ft.flags());
+ else
+ m_flags = ft.m_flags;
+ }
+ m_overline.insert(ft.m_overline);
+ m_strikeoutline.insert(ft.m_strikeoutline);
+ m_underline.insert(ft.m_underline);
+ m_color.insert(ft.m_color);
+ m_extra += ft.m_extra;
+ }
+ //! sets the font id and resets size to the previous size for this font
+ void setFont(int newId)
+ {
+ resetColor();
+ m_id=newId;
+ }
+
+ //! returns the font id
+ int id() const
+ {
+ return m_id.get();
+ }
+ //! sets the font id
+ void setId(int newId)
+ {
+ m_id = newId;
+ }
+
+ //! returns the font size
+ float size() const
+ {
+ return m_size.get();
+ }
+ //! sets the font size
+ void setSize(float sz)
+ {
+ m_size = sz;
+ }
+
+ //! returns the condensed(negative)/extended(positive) width
+ float deltaLetterSpacing() const
+ {
+ return m_deltaSpacing.get();
+ }
+ //! returns the condensed(negative)/extended(positive) unit
+ librevenge::RVNGUnit deltaLetterSpacingUnit() const
+ {
+ return m_deltaSpacingUnit.get();
+ }
+ //! sets the letter spacing ( delta value in point )
+ void setDeltaLetterSpacing(float d, librevenge::RVNGUnit unit=librevenge::RVNG_POINT)
+ {
+ m_deltaSpacing=d;
+ m_deltaSpacingUnit=unit;
+ }
+ //! returns the text width streching
+ float widthStreching() const
+ {
+ return m_widthStreching.get();
+ }
+ //! sets the text width streching
+ void setWidthStreching(float scale=1.0)
+ {
+ m_widthStreching = scale;
+ }
+ //! returns the script position
+ Script const &script() const
+ {
+ return m_scriptPosition.get();
+ }
+
+ //! sets the script position
+ void set(Script const &newscript)
+ {
+ m_scriptPosition = newscript;
+ }
+
+ //! returns the font flags
+ uint32_t flags() const
+ {
+ return m_flags.get();
+ }
+ //! sets the font attributes bold, ...
+ void setFlags(uint32_t fl)
+ {
+ m_flags = fl;
+ }
+
+ //! returns true if the font color is not black
+ bool hasColor() const
+ {
+ return m_color.isSet() && !m_color.get().isBlack();
+ }
+ //! returns the font color
+ void getColor(SW602Color &c) const
+ {
+ c = m_color.get();
+ }
+ //! sets the font color
+ void setColor(SW602Color color)
+ {
+ m_color = color;
+ }
+
+ //! returns the font background color
+ void getBackgroundColor(SW602Color &c) const
+ {
+ c = m_backgroundColor.get();
+ }
+ //! sets the font background color
+ void setBackgroundColor(SW602Color color)
+ {
+ m_backgroundColor = color;
+ }
+ //! resets the font color to black and the background color to white
+ void resetColor()
+ {
+ m_color = SW602Color::black();
+ m_backgroundColor = SW602Color::white();
+ }
+
+ //! return true if the font has decorations line (overline, strikeout, underline)
+ bool hasDecorationLines() const
+ {
+ return (m_overline.isSet() && m_overline->isSet()) ||
+ (m_strikeoutline.isSet() && m_strikeoutline->isSet()) ||
+ (m_underline.isSet() && m_underline->isSet());
+ }
+ //! reset the decoration
+ void resetDecorationLines()
+ {
+ if (m_overline.isSet()) m_overline=Line(Line::None);
+ if (m_strikeoutline.isSet()) m_strikeoutline=Line(Line::None);
+ if (m_underline.isSet()) m_underline=Line(Line::None);
+ }
+ //! returns the overline
+ Line const &getOverline() const
+ {
+ return m_overline.get();
+ }
+ //! sets the overline
+ void setOverline(Line const &line)
+ {
+ m_overline = line;
+ }
+ //! sets the overline style ( by default, we also reset the style)
+ void setOverlineStyle(Line::Style style=Line::None, bool doReset=true)
+ {
+ if (doReset)
+ m_overline = Line(style);
+ else
+ m_overline->m_style = style;
+ }
+ //! sets the overline type
+ void setOverlineType(Line::Type type=Line::Single)
+ {
+ m_overline->m_type = type;
+ }
+ //! sets the overline word flag
+ void setOverlineWordFlag(bool wordFlag=false)
+ {
+ m_overline->m_word = wordFlag;
+ }
+ //! sets the overline width
+ void setOverlineWidth(float w)
+ {
+ m_overline->m_width = w;
+ }
+ //! sets the overline color
+ void setOverlineColor(SW602Color const &color)
+ {
+ m_overline->m_color = color;
+ }
+
+ //! returns the strikeoutline
+ Line const &getStrikeOut() const
+ {
+ return m_strikeoutline.get();
+ }
+ //! sets the strikeoutline
+ void setStrikeOut(Line const &line)
+ {
+ m_strikeoutline = line;
+ }
+ //! sets the strikeoutline style ( by default, we also reset the style)
+ void setStrikeOutStyle(Line::Style style=Line::None, bool doReset=true)
+ {
+ if (doReset)
+ m_strikeoutline = Line(style);
+ else
+ m_strikeoutline->m_style = style;
+ }
+ //! sets the strikeoutline type
+ void setStrikeOutType(Line::Type type=Line::Single)
+ {
+ m_strikeoutline->m_type = type;
+ }
+ //! sets the strikeoutline word flag
+ void setStrikeOutWordFlag(bool wordFlag=false)
+ {
+ m_strikeoutline->m_word = wordFlag;
+ }
+ //! sets the strikeoutline width
+ void setStrikeOutWidth(float w)
+ {
+ m_strikeoutline->m_width = w;
+ }
+ //! sets the strikeoutline color
+ void setStrikeOutColor(SW602Color const &color)
+ {
+ m_strikeoutline->m_color = color;
+ }
+
+ //! returns the underline
+ Line const &getUnderline() const
+ {
+ return m_underline.get();
+ }
+ //! sets the underline
+ void setUnderline(Line const &line)
+ {
+ m_underline = line;
+ }
+ //! sets the underline style ( by default, we also reset the style)
+ void setUnderlineStyle(Line::Style style=Line::None, bool doReset=true)
+ {
+ if (doReset)
+ m_underline = Line(style);
+ else
+ m_underline->m_style = style;
+ }
+ //! sets the underline type
+ void setUnderlineType(Line::Type type=Line::Single)
+ {
+ m_underline->m_type = type;
+ }
+ //! sets the underline word flag
+ void setUnderlineWordFlag(bool wordFlag=false)
+ {
+ m_underline->m_word = wordFlag;
+ }
+ //! sets the underline width
+ void setUnderlineWidth(float w)
+ {
+ m_underline->m_width = w;
+ }
+ //! sets the underline color
+ void setUnderlineColor(SW602Color const &color)
+ {
+ m_underline->m_color = color;
+ }
+
+ //! returns the language
+ std::string const &language() const
+ {
+ return m_language.get();
+ }
+ //! set the language ( in the for en_US, en_GB, en, ...)
+ void setLanguage(std::string const &lang)
+ {
+ m_language=lang;
+ }
+ //! add to the propList
+ void addTo(librevenge::RVNGPropertyList &propList) const;
+
+ //! returns a string which can be used for debugging
+ std::string getDebugString() const;
+
+ //! operator==
+ bool operator==(SW602Font const &f) const
+ {
+ return cmp(f) == 0;
+ }
+ //! operator!=
+ bool operator!=(SW602Font const &f) const
+ {
+ return cmp(f) != 0;
+ }
+
+ //! a comparison function
+ int cmp(SW602Font const &oth) const
+ {
+ int diff = id() - oth.id();
+ if (diff != 0) return diff;
+ if (size() < oth.size()) return -1;
+ if (size() > oth.size()) return -1;
+ if (flags() < oth.flags()) return -1;
+ if (flags() > oth.flags()) return 1;
+ if (m_deltaSpacing.get() < oth.m_deltaSpacing.get()) return -1;
+ if (m_deltaSpacing.get() > oth.m_deltaSpacing.get()) return 1;
+ if (m_deltaSpacingUnit.get() < oth.m_deltaSpacingUnit.get()) return -1;
+ if (m_deltaSpacingUnit.get() > oth.m_deltaSpacingUnit.get()) return 1;
+ if (m_widthStreching.get() < oth.m_widthStreching.get()) return -1;
+ if (m_widthStreching.get() > oth.m_widthStreching.get()) return 1;
+ diff = script().cmp(oth.script());
+ if (diff != 0) return diff;
+ diff = m_overline.get().cmp(oth.m_overline.get());
+ if (diff != 0) return diff;
+ diff = m_strikeoutline.get().cmp(oth.m_strikeoutline.get());
+ if (diff != 0) return diff;
+ diff = m_underline.get().cmp(oth.m_underline.get());
+ if (diff != 0) return diff;
+ if (m_color.get() < oth.m_color.get()) return -1;
+ if (m_color.get() > oth.m_color.get()) return 1;
+ if (m_backgroundColor.get() < oth.m_backgroundColor.get()) return -1;
+ if (m_backgroundColor.get() > oth.m_backgroundColor.get()) return 1;
+ if (m_language.get() < oth.m_language.get()) return -1;
+ if (m_language.get() > oth.m_language.get()) return 1;
+ return diff;
+ }
+
+protected:
+ SW602Variable<int> m_id /** font identificator*/;
+ SW602Variable<float> m_size /** font size */;
+ SW602Variable<float> m_deltaSpacing /** expand(&gt; 0), condensed(&lt; 0) depl*/;
+ SW602Variable<librevenge::RVNGUnit> m_deltaSpacingUnit /** the delta spacing unit */;
+ SW602Variable<float> m_widthStreching /** the width streching in percent */;
+ SW602Variable<Script> m_scriptPosition /** the sub/super script definition */;
+ SW602Variable<uint32_t> m_flags /** font attributes */;
+ SW602Variable<Line> m_overline /** overline attributes */;
+ SW602Variable<Line> m_strikeoutline /** overline attributes */;
+ SW602Variable<Line> m_underline /** underline attributes */;
+ SW602Variable<SW602Color> m_color /** font color */;
+ SW602Variable<SW602Color> m_backgroundColor /** font background color */;
+ SW602Variable<std::string> m_language /** the language if set */;
+public:
+ //! extra data
+ std::string m_extra;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602GraphicDecoder.cpp b/src/lib/SW602GraphicDecoder.cpp
new file mode 100644
index 0000000..bb25845
--- /dev/null
+++ b/src/lib/SW602GraphicDecoder.cpp
@@ -0,0 +1,250 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "SW602GraphicDecoder.h"
+
+#include <string.h>
+
+#include <librevenge/librevenge.h>
+
+#include "SW602Debug.h"
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+void SW602GraphicDecoder::insertElement(const char *psName)
+{
+ if (!m_output) return;
+ int len=psName ? int(strlen(psName)) : 0;
+ if (!len)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicDecoder::insertElement: called without name\n"));
+ return;
+ }
+
+ bool ok=true;
+ switch (psName[0])
+ {
+ case 'C':
+ if (len>=6 && strncmp(psName,"Close",5)==0)
+ {
+ psName+=5;
+ if (strcmp(psName,"Group")==0)
+ m_output->closeGroup();
+ else if (strcmp(psName,"Link")==0)
+ m_output->closeLink();
+ else if (strcmp(psName,"ListElement")==0)
+ m_output->closeListElement();
+ else if (strcmp(psName,"OrderedListLevel")==0)
+ m_output->closeOrderedListLevel();
+ else if (strcmp(psName,"Paragraph")==0)
+ m_output->closeParagraph();
+ else if (strcmp(psName,"Span")==0)
+ m_output->closeSpan();
+ else if (strcmp(psName,"TableCell")==0)
+ m_output->closeTableCell();
+ else if (strcmp(psName,"TableRow")==0)
+ m_output->closeTableRow();
+ else if (strcmp(psName,"UnorderedListLevel")==0)
+ m_output->closeUnorderedListLevel();
+ else
+ ok=false;
+ }
+ else
+ ok=false;
+ break;
+ case 'E':
+ if (len>=4 && strncmp(psName,"End",3)==0)
+ {
+ psName+=3;
+ if (strcmp(psName,"Document")==0)
+ m_output->endDocument();
+ else if (strcmp(psName,"EmbeddedGraphics")==0)
+ m_output->endEmbeddedGraphics();
+ else if (strcmp(psName,"Layer")==0)
+ m_output->endLayer();
+ else if (strcmp(psName,"MasterPage")==0)
+ m_output->endMasterPage();
+ else if (strcmp(psName,"Page")==0)
+ m_output->endPage();
+ else if (strcmp(psName,"TableObject")==0)
+ m_output->endTableObject();
+ else if (strcmp(psName,"TextObject")==0)
+ m_output->endTextObject();
+ else
+ ok=false;
+ }
+ else
+ ok=false;
+ break;
+ case 'I':
+ if (len>=7 && strncmp(psName,"Insert",6)==0)
+ {
+ psName+=6;
+ if (strcmp(psName,"LineBreak")==0)
+ m_output->insertLineBreak();
+ else if (strcmp(psName,"Space")==0)
+ m_output->insertSpace();
+ else if (strcmp(psName,"Tab")==0)
+ m_output->insertTab();
+ else
+ ok=false;
+ }
+ else
+ ok=false;
+ break;
+ default:
+ ok=false;
+ break;
+ }
+ if (!ok)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicDecoder::insertElement: called with unexpected name %s\n", psName));
+ }
+}
+
+void SW602GraphicDecoder::insertElement(const char *psName, const librevenge::RVNGPropertyList &propList)
+{
+ if (!m_output) return;
+ int len=psName ? int(strlen(psName)) : 0;
+ if (!len)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicDecoder::insertElement: called without any name\n"));
+ return;
+ }
+
+ bool ok=true;
+ switch (psName[0])
+ {
+ case 'D':
+ if (len>=7 && strncmp(psName,"Define",6)==0)
+ {
+ psName+=6;
+ if (strcmp(psName,"CharacterStyle")==0)
+ m_output->defineCharacterStyle(propList);
+ else if (strcmp(psName,"EmbeddedFont")==0)
+ m_output->defineEmbeddedFont(propList);
+ else if (strcmp(psName,"ParagraphStyle")==0)
+ m_output->defineParagraphStyle(propList);
+ else
+ ok=false;
+ }
+ else if (len>=5 && strncmp(psName,"Draw",4)==0)
+ {
+ psName+=4;
+ if (strcmp(psName,"Connector")==0)
+ m_output->drawConnector(propList);
+ else if (strcmp(psName,"Ellipse")==0)
+ m_output->drawEllipse(propList);
+ else if (strcmp(psName,"GraphicObject")==0)
+ m_output->drawGraphicObject(propList);
+ else if (strcmp(psName,"Path")==0)
+ m_output->drawPath(propList);
+ else if (strcmp(psName,"Polygon")==0)
+ m_output->drawPolygon(propList);
+ else if (strcmp(psName,"Polyline")==0)
+ m_output->drawPolyline(propList);
+ else if (strcmp(psName,"Rectangle")==0)
+ m_output->drawRectangle(propList);
+ else
+ ok=false;
+ }
+ else
+ ok=false;
+ break;
+ case 'I':
+ if (len>=7 && strncmp(psName,"Insert",6)==0)
+ {
+ psName+=6;
+ if (strcmp(psName,"CoveredTableCell")==0)
+ m_output->insertCoveredTableCell(propList);
+ else if (strcmp(psName,"Field")==0)
+ m_output->insertField(propList);
+ else
+ ok=false;
+ }
+ else
+ ok=false;
+ break;
+ case 'O':
+ if (len>=5 && strncmp(psName,"Open",4)==0)
+ {
+ psName+=4;
+ if (strcmp(psName,"Group")==0)
+ m_output->openGroup(propList);
+ else if (strcmp(psName,"Link")==0)
+ m_output->openLink(propList);
+ else if (strcmp(psName,"ListElement")==0)
+ m_output->openListElement(propList);
+ else if (strcmp(psName,"OrderedListLevel")==0)
+ m_output->openOrderedListLevel(propList);
+ else if (strcmp(psName,"Paragraph")==0)
+ m_output->openParagraph(propList);
+ else if (strcmp(psName,"Span")==0)
+ m_output->openSpan(propList);
+ else if (strcmp(psName,"TableCell")==0)
+ m_output->openTableCell(propList);
+ else if (strcmp(psName,"TableRow")==0)
+ m_output->openTableRow(propList);
+ else if (strcmp(psName,"UnorderedListLevel")==0)
+ m_output->openUnorderedListLevel(propList);
+ else
+ ok=false;
+ }
+ else
+ ok=false;
+ break;
+ case 'S':
+ if (len>=4 && strncmp(psName,"Set",3)==0)
+ {
+ psName+=3;
+ if (strcmp(psName,"DocumentMetaData")==0)
+ m_output->setDocumentMetaData(propList);
+ else if (strcmp(psName,"Style")==0)
+ m_output->setStyle(propList);
+ else
+ ok=false;
+ }
+ else if (len>=6 && strncmp(psName,"Start",5)==0)
+ {
+ psName+=5;
+ if (strcmp(psName,"Document")==0)
+ m_output->startDocument(propList);
+ else if (strcmp(psName,"EmbeddedGraphics")==0)
+ m_output->startEmbeddedGraphics(propList);
+ else if (strcmp(psName,"Layer")==0)
+ m_output->startLayer(propList);
+ else if (strcmp(psName,"MasterPage")==0)
+ m_output->startMasterPage(propList);
+ else if (strcmp(psName,"Page")==0)
+ m_output->startPage(propList);
+ else if (strcmp(psName,"TableObject")==0)
+ m_output->startTableObject(propList);
+ else if (strcmp(psName,"TextObject")==0)
+ m_output->startTextObject(propList);
+ else
+ ok=false;
+ }
+ else
+ ok=false;
+ break;
+ default:
+ ok=false;
+ break;
+ }
+ if (!ok)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicDecoder::insertElement: called with unexpected name %s\n", psName));
+ }
+}
+
+}
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602GraphicDecoder.h b/src/lib/SW602GraphicDecoder.h
new file mode 100644
index 0000000..8f641f5
--- /dev/null
+++ b/src/lib/SW602GraphicDecoder.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SW602_GRAPHIC_DECODER_H
+#define INCLUDED_SW602_GRAPHIC_DECODER_H
+
+#include <librevenge/librevenge.h>
+
+#include "SW602PropertyHandler.h"
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+/** main class used to decode a librevenge::RVNGBinaryData created by
+ \see SW602GraphicEncoder (with mimeType="image/x-libsw602-odg") and to send
+ it contents to librevenge::RVNGDrawingInterface
+*/
+class SW602GraphicDecoder : public SW602PropertyHandler
+{
+public:
+ /** constructor */
+ explicit SW602GraphicDecoder(librevenge::RVNGDrawingInterface *output) : SW602PropertyHandler(), m_output(output) { }
+ /** destructor */
+ ~SW602GraphicDecoder() {};
+
+ /** insert an element */
+ void insertElement(const char *psName);
+ /** insert an element ( with a librevenge::RVNGPropertyList ) */
+ void insertElement(const char *psName, const librevenge::RVNGPropertyList &xPropList);
+ /** insert an element ( with a librevenge::RVNGPropertyListVector parameter ) */
+ void insertElement(const char *psName, const librevenge::RVNGPropertyList &xPropList,
+ const librevenge::RVNGPropertyListVector &vector);
+ /** insert a sequence of character */
+ void characters(const librevenge::RVNGString &sCharacters)
+ {
+ if (!m_output) return;
+ m_output->insertText(sCharacters);
+ }
+private:
+ /// copy constructor (undefined)
+ SW602GraphicDecoder(SW602GraphicDecoder const &);
+ /// operator= (undefined)
+ SW602GraphicDecoder operator=(SW602GraphicDecoder const &);
+ /** the interface output */
+ librevenge::RVNGDrawingInterface *m_output;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602GraphicEncoder.cpp b/src/lib/SW602GraphicEncoder.cpp
new file mode 100644
index 0000000..009d839
--- /dev/null
+++ b/src/lib/SW602GraphicEncoder.cpp
@@ -0,0 +1,311 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "SW602GraphicEncoder.h"
+
+#include <string.h>
+
+#include <map>
+#include <sstream>
+#include <string>
+
+#include <librevenge/librevenge.h>
+
+#include "SW602PropertyHandler.h"
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+//! a name space used to define internal data of SW602GraphicEncoder
+namespace SW602GraphicEncoderInternal
+{
+//! the state of a SW602GraphicEncoder
+struct State
+{
+ //! constructor
+ State() : m_encoder()
+ {
+ }
+ //! the encoder
+ SW602PropertyHandlerEncoder m_encoder;
+};
+
+}
+
+SW602GraphicEncoder::SW602GraphicEncoder() : librevenge::RVNGDrawingInterface(), m_state(new SW602GraphicEncoderInternal::State)
+{
+}
+
+SW602GraphicEncoder::~SW602GraphicEncoder()
+{
+}
+
+bool SW602GraphicEncoder::getBinaryResult(SW602EmbeddedObject &result)
+{
+ librevenge::RVNGBinaryData data;
+ if (!m_state->m_encoder.getData(data))
+ return false;
+ result=SW602EmbeddedObject(data, "image/x-libsw602-odg");
+ return true;
+}
+
+void SW602GraphicEncoder::startDocument(const ::librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("StartDocument", list);
+}
+
+void SW602GraphicEncoder::endDocument()
+{
+ m_state->m_encoder.insertElement("EndDocument");
+}
+
+void SW602GraphicEncoder::setDocumentMetaData(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("SetDocumentMetaData", list);
+}
+
+void SW602GraphicEncoder::defineEmbeddedFont(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("DefineEmbeddedFont", list);
+}
+
+void SW602GraphicEncoder::startPage(const ::librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("StartPage", list);
+}
+
+void SW602GraphicEncoder::endPage()
+{
+ m_state->m_encoder.insertElement("EndPage");
+}
+
+void SW602GraphicEncoder::startMasterPage(const ::librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("StartMasterPage", list);
+}
+
+void SW602GraphicEncoder::endMasterPage()
+{
+ m_state->m_encoder.insertElement("EndMasterPage");
+}
+
+void SW602GraphicEncoder::setStyle(const ::librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("SetStyle", list);
+}
+
+void SW602GraphicEncoder::startLayer(const ::librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("StartLayer", list);
+}
+
+void SW602GraphicEncoder::endLayer()
+{
+ m_state->m_encoder.insertElement("EndLayer");
+}
+
+void SW602GraphicEncoder::startEmbeddedGraphics(const ::librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("StartEmbeddedGraphics", list);
+}
+
+void SW602GraphicEncoder::endEmbeddedGraphics()
+{
+ m_state->m_encoder.insertElement("StartEmbeddedGraphics");
+}
+
+void SW602GraphicEncoder::openGroup(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenGroup", list);
+}
+
+void SW602GraphicEncoder::closeGroup()
+{
+ m_state->m_encoder.insertElement("CloseGroup");
+}
+
+void SW602GraphicEncoder::drawRectangle(const ::librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("DrawRectangle", list);
+}
+
+void SW602GraphicEncoder::drawEllipse(const ::librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("DrawEllipse", list);
+}
+
+void SW602GraphicEncoder::drawPolygon(const ::librevenge::RVNGPropertyList &vertices)
+{
+ m_state->m_encoder.insertElement("DrawPolygon", vertices);
+}
+
+void SW602GraphicEncoder::drawPolyline(const ::librevenge::RVNGPropertyList &vertices)
+{
+ m_state->m_encoder.insertElement("DrawPolyline", vertices);
+}
+
+void SW602GraphicEncoder::drawPath(const ::librevenge::RVNGPropertyList &path)
+{
+ m_state->m_encoder.insertElement("DrawPath", path);
+}
+
+void SW602GraphicEncoder::drawConnector(const ::librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("DrawConnector", list);
+}
+
+void SW602GraphicEncoder::drawGraphicObject(const ::librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("DrawGraphicObject", list);
+}
+
+void SW602GraphicEncoder::startTextObject(const ::librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("StartTextObject", list);
+}
+
+void SW602GraphicEncoder::endTextObject()
+{
+ m_state->m_encoder.insertElement("EndTextObject");
+}
+
+void SW602GraphicEncoder::startTableObject(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("StartTableObject", list);
+}
+
+void SW602GraphicEncoder::endTableObject()
+{
+ m_state->m_encoder.insertElement("EndTableObject");
+}
+
+void SW602GraphicEncoder::openTableRow(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenTableRow", list);
+}
+
+void SW602GraphicEncoder::closeTableRow()
+{
+ m_state->m_encoder.insertElement("CloseTableRow");
+}
+
+void SW602GraphicEncoder::openTableCell(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenTableCell", list);
+}
+
+void SW602GraphicEncoder::closeTableCell()
+{
+ m_state->m_encoder.insertElement("CloseTableCell");
+}
+
+void SW602GraphicEncoder::insertCoveredTableCell(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("InsertCoveredTableCell", list);
+}
+
+void SW602GraphicEncoder::insertTab()
+{
+ m_state->m_encoder.insertElement("InsertTab");
+}
+
+void SW602GraphicEncoder::insertSpace()
+{
+ m_state->m_encoder.insertElement("InsertSpace");
+}
+
+void SW602GraphicEncoder::insertText(const librevenge::RVNGString &text)
+{
+ m_state->m_encoder.characters(text);
+}
+
+void SW602GraphicEncoder::insertLineBreak()
+{
+ m_state->m_encoder.insertElement("InsertLineBreak");
+}
+
+void SW602GraphicEncoder::insertField(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("InsertField", list);
+}
+
+void SW602GraphicEncoder::openLink(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenLink", list);
+}
+
+void SW602GraphicEncoder::closeLink()
+{
+ m_state->m_encoder.insertElement("CloseLink");
+}
+
+void SW602GraphicEncoder::openOrderedListLevel(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenOrderedListLevel", list);
+}
+
+void SW602GraphicEncoder::openUnorderedListLevel(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenUnorderedListLevel", list);
+}
+
+void SW602GraphicEncoder::closeOrderedListLevel()
+{
+ m_state->m_encoder.insertElement("CloseOrderedListLevel");
+}
+
+void SW602GraphicEncoder::closeUnorderedListLevel()
+{
+ m_state->m_encoder.insertElement("CloseOrderedListLevel");
+}
+
+void SW602GraphicEncoder::openListElement(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenListElement", list);
+}
+
+void SW602GraphicEncoder::closeListElement()
+{
+ m_state->m_encoder.insertElement("CloseListElement");
+}
+
+void SW602GraphicEncoder::defineParagraphStyle(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("DefineParagraphStyle", list);
+}
+
+void SW602GraphicEncoder::openParagraph(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenParagraph", list);
+}
+
+void SW602GraphicEncoder::closeParagraph()
+{
+ m_state->m_encoder.insertElement("CloseParagraph");
+}
+
+void SW602GraphicEncoder::defineCharacterStyle(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("DefineCharacterStyle", list);
+}
+
+void SW602GraphicEncoder::openSpan(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenSpan", list);
+}
+
+void SW602GraphicEncoder::closeSpan()
+{
+ m_state->m_encoder.insertElement("CloseSpan");
+}
+
+}
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602GraphicEncoder.h b/src/lib/SW602GraphicEncoder.h
new file mode 100644
index 0000000..bd6bd19
--- /dev/null
+++ b/src/lib/SW602GraphicEncoder.h
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SW602_GRAPHIC_ENCODER_H
+#define INCLUDED_SW602_GRAPHIC_ENCODER_H
+
+#include <librevenge/librevenge.h>
+
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+class SW602PropertyHandlerEncoder;
+
+namespace SW602GraphicEncoderInternal
+{
+struct State;
+}
+/** main class used to define store librevenge::RVNGDrawingInterface
+ lists of command in a librevenge::RVNGBinaryData. \see SW602GraphicDecoder
+ can be used to decode back the pictures...
+
+ \note as this class implements the functions librevenge::RVNGDrawingInterface,
+ the documentation is not duplicated..
+*/
+class SW602GraphicEncoder : public librevenge::RVNGDrawingInterface
+{
+public:
+ /// constructor
+ SW602GraphicEncoder();
+ /// destructor
+ ~SW602GraphicEncoder();
+ /// return the final graphic
+ bool getBinaryResult(SW602EmbeddedObject &result);
+
+ void startDocument(const ::librevenge::RVNGPropertyList &propList);
+ void endDocument();
+
+ void defineEmbeddedFont(const librevenge::RVNGPropertyList &propList);
+
+ void setDocumentMetaData(const librevenge::RVNGPropertyList &propList);
+ void startPage(const ::librevenge::RVNGPropertyList &propList);
+ void endPage();
+ void startMasterPage(const ::librevenge::RVNGPropertyList &propList);
+ void endMasterPage();
+
+ void setStyle(const ::librevenge::RVNGPropertyList &propList);
+ void startLayer(const ::librevenge::RVNGPropertyList &propList);
+ void endLayer();
+ void startEmbeddedGraphics(const ::librevenge::RVNGPropertyList &propList);
+ void endEmbeddedGraphics();
+ void openGroup(const ::librevenge::RVNGPropertyList &propList);
+ void closeGroup();
+
+
+ void drawRectangle(const ::librevenge::RVNGPropertyList &propList);
+ void drawEllipse(const ::librevenge::RVNGPropertyList &propList);
+ void drawPolygon(const ::librevenge::RVNGPropertyList &vertices);
+ void drawPolyline(const ::librevenge::RVNGPropertyList &vertices);
+ void drawPath(const ::librevenge::RVNGPropertyList &path);
+ void drawConnector(const ::librevenge::RVNGPropertyList &propList);
+
+ void drawGraphicObject(const ::librevenge::RVNGPropertyList &propList);
+
+ void startTextObject(const ::librevenge::RVNGPropertyList &propList);
+ void endTextObject();
+
+ void startTableObject(const librevenge::RVNGPropertyList &propList);
+ void endTableObject();
+ void openTableRow(const librevenge::RVNGPropertyList &propList);
+ void closeTableRow();
+ void openTableCell(const librevenge::RVNGPropertyList &propList);
+ void closeTableCell();
+ void insertCoveredTableCell(const librevenge::RVNGPropertyList &propList);
+
+ void insertTab();
+ void insertSpace();
+ void insertText(const librevenge::RVNGString &text);
+ void insertLineBreak();
+ void insertField(const librevenge::RVNGPropertyList &propList);
+
+ void openLink(const librevenge::RVNGPropertyList &propList);
+ void closeLink();
+ void openOrderedListLevel(const librevenge::RVNGPropertyList &propList);
+ void openUnorderedListLevel(const librevenge::RVNGPropertyList &propList);
+ void closeOrderedListLevel();
+ void closeUnorderedListLevel();
+ void openListElement(const librevenge::RVNGPropertyList &propList);
+ void closeListElement();
+
+ void defineParagraphStyle(const librevenge::RVNGPropertyList &propList);
+ void openParagraph(const librevenge::RVNGPropertyList &propList);
+ void closeParagraph();
+
+ void defineCharacterStyle(const librevenge::RVNGPropertyList &propList);
+ void openSpan(const librevenge::RVNGPropertyList &propList);
+ void closeSpan();
+
+protected:
+ //! the actual state
+ boost::shared_ptr<SW602GraphicEncoderInternal::State> m_state;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602GraphicListener.cpp b/src/lib/SW602GraphicListener.cpp
new file mode 100644
index 0000000..3d61661
--- /dev/null
+++ b/src/lib/SW602GraphicListener.cpp
@@ -0,0 +1,1752 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "SW602GraphicListener.h"
+
+#include <cstring>
+#include <iomanip>
+#include <sstream>
+
+#include <time.h>
+
+#include <librevenge/librevenge.h>
+
+#include "SW602Cell.h"
+#include "SW602Font.h"
+#include "SW602GraphicEncoder.h"
+#include "SW602GraphicShape.h"
+#include "SW602GraphicStyle.h"
+#include "SW602List.h"
+#include "SW602Paragraph.h"
+#include "SW602Parser.h"
+#include "SW602Position.h"
+#include "SW602Section.h"
+#include "SW602SubDocument.h"
+#include "SW602Table.h"
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+//! Internal and low level namespace to define the states of SW602GraphicListener
+namespace SW602GraphicListenerInternal
+{
+/** the global graphic state of SW602GraphicListener */
+struct GraphicState
+{
+ //! constructor
+ explicit GraphicState(std::vector<SW602PageSpan> const &pageList) :
+ m_pageList(pageList), m_metaData(),
+ m_isDocumentStarted(false), m_isPageSpanOpened(false), m_isMasterPageSpanOpened(false), m_isAtLeastOnePageOpened(false),
+ m_isHeaderFooterStarted(false), m_pageSpan(), m_sentListMarkers(), m_subDocuments()
+ {
+ }
+ //! destructor
+ ~GraphicState()
+ {
+ }
+ //! the pages definition
+ std::vector<SW602PageSpan> m_pageList;
+ //! the document meta data
+ librevenge::RVNGPropertyList m_metaData;
+ /** a flag to know if the document is open */
+ bool m_isDocumentStarted;
+ //! true if a page is open
+ bool m_isPageSpanOpened;
+ //! true if a masterpage is open
+ bool m_isMasterPageSpanOpened;
+ /** true if the first page has been open */
+ bool m_isAtLeastOnePageOpened;
+ /** a flag to know if the header footer is started */
+ bool m_isHeaderFooterStarted;
+ ///! the current page span
+ SW602PageSpan m_pageSpan;
+ /// the list of marker corresponding to sent list
+ std::vector<int> m_sentListMarkers;
+ //! the list of actual subdocument
+ std::vector<SW602SubDocumentPtr> m_subDocuments;
+};
+
+/** the state of a SW602GraphicListener */
+struct State
+{
+ //! constructor
+ State();
+ //! destructor
+ ~State() { }
+
+//! returns true if we are in a text zone, ie. either in a textbox or a table cell
+ bool isInTextZone() const
+ {
+ return m_inNote || m_isTextBoxOpened || m_isTableCellOpened;
+ }
+ //! the origin position
+ SW602Vec2f m_origin;
+ //! a buffer to stored the text
+ librevenge::RVNGString m_textBuffer;
+
+ //! the font
+ SW602Font m_font;
+ //! the paragraph
+ SW602Paragraph m_paragraph;
+ //! the list of list
+ boost::shared_ptr<SW602List> m_list;
+
+ //! a flag to know if openFrame was called
+ bool m_isFrameOpened;
+ //! the frame position
+ SW602Position m_framePosition;
+ //! the frame style
+ SW602GraphicStyle m_frameStyle;
+
+ //! a flag to know if we are in a textbox
+ bool m_isTextBoxOpened;
+ //! a flag to know if openGroup was called
+ bool m_isGroupOpened;
+ //! a flag to know if openLayer was called
+ bool m_isLayerOpened;
+ bool m_isSpanOpened;
+ bool m_isParagraphOpened;
+ bool m_isListElementOpened;
+
+ bool m_firstParagraphInPageSpan;
+
+ std::vector<bool> m_listOrderedLevels; //! a stack used to know what is open
+
+ bool m_isTableOpened;
+ bool m_isTableRowOpened;
+ bool m_isTableColumnOpened;
+ bool m_isTableCellOpened;
+
+ unsigned m_currentPage;
+ int m_numPagesRemainingInSpan;
+ int m_currentPageNumber;
+
+ bool m_inLink;
+ bool m_inNote;
+ bool m_inSubDocument;
+ libsw602::SubDocumentType m_subDocumentType;
+
+private:
+ State(const State &);
+ State &operator=(const State &);
+};
+
+State::State() : m_origin(0,0),
+ m_textBuffer(""), m_font(20,12)/* default time 12 */, m_paragraph(), m_list(),
+ m_isFrameOpened(false), m_framePosition(), m_frameStyle(), m_isTextBoxOpened(false),
+ m_isGroupOpened(false), m_isLayerOpened(false),
+ m_isSpanOpened(false), m_isParagraphOpened(false), m_isListElementOpened(false),
+ m_firstParagraphInPageSpan(true), m_listOrderedLevels(),
+ m_isTableOpened(false), m_isTableRowOpened(false), m_isTableColumnOpened(false),
+ m_isTableCellOpened(false),
+ m_currentPage(0), m_numPagesRemainingInSpan(0), m_currentPageNumber(1),
+ m_inLink(false), m_inNote(false), m_inSubDocument(false), m_subDocumentType(libsw602::DOC_NONE)
+{
+}
+}
+
+SW602GraphicListener::SW602GraphicListener(SW602ParserState &parserState, std::vector<SW602PageSpan> const &pageList, librevenge::RVNGDrawingInterface *documentInterface) : SW602Listener(),
+ m_ds(new SW602GraphicListenerInternal::GraphicState(pageList)), m_ps(new SW602GraphicListenerInternal::State),
+ m_psStack(), m_parserState(parserState), m_documentInterface(documentInterface)
+{
+}
+
+SW602GraphicListener::SW602GraphicListener(SW602ParserState &parserState, SW602Box2f const &box, librevenge::RVNGDrawingInterface *documentInterface) : SW602Listener(),
+ m_ds(), m_ps(new SW602GraphicListenerInternal::State), m_psStack(), m_parserState(parserState), m_documentInterface(documentInterface)
+{
+ SW602PageSpan pageSpan;
+ pageSpan.setMargins(0);
+ pageSpan.setPageSpan(1);
+ pageSpan.setFormWidth(box.size().x()/72.);
+ pageSpan.setFormLength(box.size().y()/72.);
+ m_ds.reset(new SW602GraphicListenerInternal::GraphicState(std::vector<SW602PageSpan>(1, pageSpan)));
+ m_ps->m_origin=box[0];
+}
+
+SW602GraphicListener::~SW602GraphicListener()
+{
+}
+
+///////////////////
+// text data
+///////////////////
+void SW602GraphicListener::insertChar(uint8_t character)
+{
+ if (!m_ps->isInTextZone())
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::insertCharacter: called outside a text zone\n"));
+ return;
+ }
+ if (character >= 0x80)
+ {
+ SW602GraphicListener::insertUnicode(character);
+ return;
+ }
+ if (!m_ps->m_isSpanOpened) _openSpan();
+ m_ps->m_textBuffer.append((char) character);
+}
+
+void SW602GraphicListener::insertCharacter(unsigned char c)
+{
+ if (!m_ps->isInTextZone())
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::insertCharacter: called outside a text zone\n"));
+ return;
+ }
+ // TODO: handle
+ int unicode = c;
+ if (unicode == -1)
+ {
+ if (c < 0x20)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::insertCharacter: Find odd char %x\n", (unsigned int)c));
+ }
+ else
+ SW602GraphicListener::insertChar((uint8_t) c);
+ }
+ else
+ SW602GraphicListener::insertUnicode((uint32_t) unicode);
+}
+
+int SW602GraphicListener::insertCharacter(unsigned char c, librevenge::RVNGInputStream &input, long endPos)
+{
+ if (!m_ps->isInTextZone())
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::insertCharacter: called outside a text zone\n"));
+ return 0;
+ }
+ long debPos=input.tell();
+ int unicode = c;
+#if 0
+ int fId = m_ps->m_font.id();
+ int unicode = endPos==debPos ?
+ m_parserState.m_fontConverter->unicode(fId, c) :
+ m_parserState.m_fontConverter->unicode(fId, c, input);
+#endif
+
+ long pos=input.tell();
+ if (endPos > 0 && pos > endPos)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::insertCharacter: problem reading a character\n"));
+ pos = debPos;
+ input.seek(pos, librevenge::RVNG_SEEK_SET);
+ // unicode = m_parserState.m_fontConverter->unicode(fId, c);
+ }
+ if (unicode == -1)
+ {
+ if (c < 0x20)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::insertCharacter: Find odd char %x\n", (unsigned int)c));
+ }
+ else
+ SW602GraphicListener::insertChar((uint8_t) c);
+ }
+ else
+ SW602GraphicListener::insertUnicode((uint32_t) unicode);
+
+ return int(pos-debPos);
+}
+
+void SW602GraphicListener::insertUnicode(uint32_t val)
+{
+ if (!m_ps->isInTextZone())
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::insertUnicode: called outside a text zone\n"));
+ return;
+ }
+ // undef character, we skip it
+ if (val == 0xfffd) return;
+
+ if (!m_ps->m_isSpanOpened) _openSpan();
+ libsw602::appendUnicode(val, m_ps->m_textBuffer);
+}
+
+void SW602GraphicListener::insertUnicodeString(librevenge::RVNGString const &str)
+{
+ if (!m_ps->isInTextZone())
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::insertUnicodeString: called outside a text zone\n"));
+ return;
+ }
+ if (!m_ps->m_isSpanOpened) _openSpan();
+ m_ps->m_textBuffer.append(str);
+}
+
+void SW602GraphicListener::insertEOL(bool soft)
+{
+ if (!m_ps->isInTextZone())
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::insertEOL: called outside a text zone\n"));
+ return;
+ }
+ if (!m_ps->m_isParagraphOpened && !m_ps->m_isListElementOpened)
+ _openSpan();
+ if (soft)
+ {
+ _flushText();
+ m_documentInterface->insertLineBreak();
+ }
+ else if (m_ps->m_isParagraphOpened)
+ _closeParagraph();
+
+ // sub/superscript must not survive a new line
+ m_ps->m_font.set(SW602Font::Script());
+}
+
+void SW602GraphicListener::insertTab()
+{
+ if (!m_ps->isInTextZone())
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::insertTab: called outside a text zone\n"));
+ return;
+ }
+ if (!m_ps->m_isSpanOpened) _openSpan();
+ _flushText();
+ m_documentInterface->insertTab();
+}
+
+///////////////////
+// font/paragraph function
+///////////////////
+void SW602GraphicListener::setFont(SW602Font const &font)
+{
+ if (!m_ps->isInTextZone())
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::setFont: called outside a text zone\n"));
+ return;
+ }
+ if (font == m_ps->m_font) return;
+
+ // check if id and size are defined, if not used the previous fields
+ SW602Font finalFont(font);
+ if (font.id() == -1)
+ finalFont.setId(m_ps->m_font.id());
+ if (font.size() <= 0)
+ finalFont.setSize(m_ps->m_font.size());
+ if (finalFont == m_ps->m_font) return;
+
+ _closeSpan();
+ m_ps->m_font = finalFont;
+}
+
+SW602Font const &SW602GraphicListener::getFont() const
+{
+ return m_ps->m_font;
+}
+
+bool SW602GraphicListener::isParagraphOpened() const
+{
+ return m_ps->m_isParagraphOpened;
+}
+
+void SW602GraphicListener::setParagraph(SW602Paragraph const &para)
+{
+ if (!m_ps->isInTextZone())
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::setParagraph: called outside a text zone\n"));
+ return;
+ }
+ if (para==m_ps->m_paragraph) return;
+
+ m_ps->m_paragraph=para;
+}
+
+SW602Paragraph const &SW602GraphicListener::getParagraph() const
+{
+ return m_ps->m_paragraph;
+}
+
+///////////////////
+// field/link :
+///////////////////
+void SW602GraphicListener::insertField(SW602Field const &field)
+{
+ if (!m_ps->isInTextZone())
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::setParagraph: called outside a text zone\n"));
+ return;
+ }
+ librevenge::RVNGPropertyList propList;
+ if (field.addTo(propList))
+ {
+ _flushText();
+ _openSpan();
+ m_documentInterface->insertField(propList);
+ return;
+ }
+ librevenge::RVNGString text=field.getString();
+ if (!text.empty())
+ SW602GraphicListener::insertUnicodeString(text);
+ else
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::insertField: must not be called with type=%d\n", int(field.m_type)));
+ }
+}
+
+void SW602GraphicListener::openLink(SW602Link const &link)
+{
+ if (!m_ps->isInTextZone())
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::openLink: called outside a textbox\n"));
+ return;
+ }
+ if (m_ps->m_inLink)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::openLink: called inside a link\n"));
+ return;
+ }
+ if (!m_ps->m_isSpanOpened) _openSpan();
+ librevenge::RVNGPropertyList propList;
+ link.addTo(propList);
+ m_documentInterface->openLink(propList);
+ _pushParsingState();
+ m_ps->m_inLink=true;
+// we do not want any close open paragraph in a link
+ m_ps->m_isParagraphOpened=true;
+}
+
+void SW602GraphicListener::closeLink()
+{
+ if (!m_ps->m_inLink)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::closeLink: closed outside a link\n"));
+ return;
+ }
+ m_documentInterface->closeLink();
+ _popParsingState();
+}
+
+///////////////////
+// document
+///////////////////
+void SW602GraphicListener::startDocument()
+{
+ if (m_ds->m_isDocumentStarted)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::startDocument: the document is already started\n"));
+ return;
+ }
+ m_ds->m_isDocumentStarted=true;
+ m_documentInterface->startDocument(librevenge::RVNGPropertyList());
+ m_documentInterface->setDocumentMetaData(m_ds->m_metaData);
+}
+
+void SW602GraphicListener::endDocument(bool /*delayed*/)
+{
+ if (!m_ds->m_isDocumentStarted)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::endDocument: the document is not started\n"));
+ return;
+ }
+ if (!m_ds->m_isAtLeastOnePageOpened)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::endDocument: no data have been send\n"));
+ _openPageSpan();
+ }
+ if (m_ds->m_isPageSpanOpened)
+ _closePageSpan(m_ds->m_isMasterPageSpanOpened);
+ m_documentInterface->endDocument();
+ m_ds->m_isDocumentStarted=false;
+ *m_ds=SW602GraphicListenerInternal::GraphicState(std::vector<SW602PageSpan>());
+}
+
+///////////////////
+// document
+///////////////////
+void SW602GraphicListener::setDocumentLanguage(std::string locale)
+{
+ if (!locale.length()) return;
+ m_ds->m_metaData.insert("librevenge:language", locale.c_str());
+}
+
+bool SW602GraphicListener::isDocumentStarted() const
+{
+ return m_ds->m_isDocumentStarted;
+}
+
+bool SW602GraphicListener::canWriteText() const
+{
+ return m_ds->m_isPageSpanOpened && m_ps->isInTextZone();
+}
+
+///////////////////
+// page
+///////////////////
+bool SW602GraphicListener::isPageSpanOpened() const
+{
+ return m_ds->m_isPageSpanOpened;
+}
+
+SW602PageSpan const &SW602GraphicListener::getPageSpan()
+{
+ if (!m_ds->m_isPageSpanOpened)
+ _openPageSpan();
+ return m_ds->m_pageSpan;
+}
+
+bool SW602GraphicListener::openMasterPage(SW602PageSpan &masterPage)
+{
+ if (m_ds->m_isMasterPageSpanOpened)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::openMasterPage: a master page is already opened\n"));
+ return false;
+ }
+ if (!m_ds->m_isDocumentStarted)
+ startDocument();
+ if (m_ds->m_isPageSpanOpened)
+ _closePageSpan();
+
+ librevenge::RVNGPropertyList propList;
+ masterPage.getPageProperty(propList);
+ propList.insert("svg:width",72.*masterPage.getFormWidth(), librevenge::RVNG_POINT);
+ propList.insert("svg:height",72.*masterPage.getFormLength(), librevenge::RVNG_POINT);
+
+ m_documentInterface->startMasterPage(propList);
+ m_ds->m_isPageSpanOpened = m_ds->m_isMasterPageSpanOpened = true;
+
+ // checkme: can we send some header/footer if some exists
+ return true;
+}
+
+void SW602GraphicListener::_openPageSpan(bool sendHeaderFooters)
+{
+ if (m_ds->m_isPageSpanOpened)
+ return;
+
+ if (!m_ds->m_isDocumentStarted)
+ startDocument();
+
+ if (m_ds->m_pageList.size()==0)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::_openPageSpan: can not find any page\n"));
+ throw libsw602::ParseException();
+ }
+ m_ds->m_isAtLeastOnePageOpened=true;
+ unsigned actPage = 0;
+ std::vector<SW602PageSpan>::iterator it = m_ds->m_pageList.begin();
+ m_ps->m_currentPage++;
+ while (true)
+ {
+ actPage+=(unsigned)it->getPageSpan();
+ if (actPage >= m_ps->m_currentPage) break;
+ if (++it == m_ds->m_pageList.end())
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::_openPageSpan: can not find current page, use the previous one\n"));
+ --it;
+ break;
+ }
+ }
+
+ SW602PageSpan &currentPage = *it;
+ librevenge::RVNGPropertyList propList;
+ currentPage.getPageProperty(propList);
+ propList.insert("librevenge:is-last-page-span", ++it == m_ds->m_pageList.end());
+ // now add data for embedded graph
+ propList.insert("svg:x",m_ps->m_origin.x(), librevenge::RVNG_POINT);
+ propList.insert("svg:y",m_ps->m_origin.y(), librevenge::RVNG_POINT);
+ propList.insert("svg:width",72.*currentPage.getFormWidth(), librevenge::RVNG_POINT);
+ propList.insert("svg:height",72.*currentPage.getFormLength(), librevenge::RVNG_POINT);
+ propList.insert("librevenge:enforce-frame",true);
+
+ if (!m_ds->m_isPageSpanOpened)
+ m_documentInterface->startPage(propList);
+ m_ds->m_isPageSpanOpened = true;
+ m_ds->m_pageSpan = currentPage;
+
+ // we insert the header footer
+ if (sendHeaderFooters)
+ currentPage.sendHeaderFooters(this, (m_ps->m_currentPage%2) ? SW602HeaderFooter::EVEN : SW602HeaderFooter::ODD);
+
+ // first paragraph in span (necessary for resetting page number)
+ m_ps->m_firstParagraphInPageSpan = true;
+ m_ps->m_numPagesRemainingInSpan = (currentPage.getPageSpan() - 1);
+}
+
+void SW602GraphicListener::_closePageSpan(bool masterPage)
+{
+ if (!m_ds->m_isPageSpanOpened)
+ return;
+
+ if (masterPage && !m_ds->m_isMasterPageSpanOpened)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::endDocument:no master page are opened\n"));
+ return;
+ }
+ if (!masterPage && m_ds->m_isMasterPageSpanOpened)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::endDocument:a master page are opened\n"));
+ return;
+ }
+ if (m_ps->m_inSubDocument)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::endDocument: we are in a sub document\n"));
+ _endSubDocument();
+ _popParsingState();
+ }
+ if (m_ps->m_isTableOpened)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::_closePageSpan: we are in a table zone\n"));
+ closeTable();
+ }
+ if (m_ps->isInTextZone())
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::_closePageSpan: we are in a text zone\n"));
+ if (m_ps->m_isParagraphOpened)
+ _closeParagraph();
+ m_ps->m_paragraph.m_listLevelIndex = 0;
+ _changeList(); // flush the list exterior
+ }
+ m_ds->m_isPageSpanOpened = m_ds->m_isMasterPageSpanOpened = false;
+ if (masterPage)
+ m_documentInterface->endMasterPage();
+ else
+ m_documentInterface->endPage();
+}
+
+///////////////////
+// paragraph
+///////////////////
+void SW602GraphicListener::_openParagraph()
+{
+ if (m_ps->m_inNote || (m_ps->m_isTableOpened && !m_ps->m_isTableCellOpened))
+ return;
+ if (!m_ps->isInTextZone())
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::_openParagraph: called outsize a text zone\n"));
+ return;
+ }
+ if (m_ps->m_isParagraphOpened || m_ps->m_isListElementOpened)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::_openParagraph: a paragraph (or a list) is already opened"));
+ return;
+ }
+
+ librevenge::RVNGPropertyList propList;
+ m_ps->m_paragraph.addTo(propList, m_ps->m_isTableCellOpened);
+ m_documentInterface->openParagraph(propList);
+
+ _resetParagraphState();
+ m_ps->m_firstParagraphInPageSpan = false;
+}
+
+void SW602GraphicListener::_closeParagraph()
+{
+ if (!m_ps->isInTextZone())
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::_closeParagraph: called outsize a text zone\n"));
+ return;
+ }
+ if (m_ps->m_inLink) return;
+ if (m_ps->m_isListElementOpened)
+ {
+ _closeListElement();
+ return;
+ }
+
+ if (m_ps->m_isParagraphOpened)
+ {
+ if (m_ps->m_isSpanOpened)
+ _closeSpan();
+ m_documentInterface->closeParagraph();
+ }
+
+ m_ps->m_isParagraphOpened = false;
+ m_ps->m_paragraph.m_listLevelIndex = 0;
+}
+
+void SW602GraphicListener::_resetParagraphState(const bool isListElement)
+{
+ m_ps->m_isListElementOpened = isListElement;
+ m_ps->m_isParagraphOpened = true;
+}
+
+///////////////////
+// list
+///////////////////
+void SW602GraphicListener::_openListElement()
+{
+ if (m_ps->m_inNote || (m_ps->m_isTableOpened && !m_ps->m_isTableCellOpened))
+ return;
+ if (!m_ps->isInTextZone())
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::_openListElement: called outsize a text zone\n"));
+ return;
+ }
+ if (m_ps->m_isParagraphOpened || m_ps->m_isListElementOpened)
+ return;
+
+ librevenge::RVNGPropertyList propList;
+ m_ps->m_paragraph.addTo(propList,m_ps->m_isTableOpened);
+
+ // check if we must change the start value
+ int startValue=m_ps->m_paragraph.m_listStartValue.get();
+ if (startValue > 0 && m_ps->m_list && m_ps->m_list->getStartValueForNextElement() != startValue)
+ {
+ propList.insert("text:start-value", startValue);
+ m_ps->m_list->setStartValueForNextElement(startValue);
+ }
+
+ if (m_ps->m_list) m_ps->m_list->openElement();
+ m_documentInterface->openListElement(propList);
+ _resetParagraphState(true);
+}
+
+void SW602GraphicListener::_closeListElement()
+{
+ if (m_ps->m_isListElementOpened)
+ {
+ if (m_ps->m_isSpanOpened)
+ _closeSpan();
+
+ if (m_ps->m_list) m_ps->m_list->closeElement();
+ m_documentInterface->closeListElement();
+ }
+
+ m_ps->m_isListElementOpened = m_ps->m_isParagraphOpened = false;
+}
+
+int SW602GraphicListener::_getListId() const
+{
+ size_t newLevel= (size_t) m_ps->m_paragraph.m_listLevelIndex.get();
+ if (newLevel == 0) return -1;
+ int newListId = m_ps->m_paragraph.m_listId.get();
+ if (newListId > 0) return newListId;
+ static bool first = true;
+ if (first)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::_getListId: the list id is not set, try to find a new one\n"));
+ first = false;
+ }
+ boost::shared_ptr<SW602List> list=m_parserState.m_listManager->getNewList
+ (m_ps->m_list, int(newLevel), *m_ps->m_paragraph.m_listLevel);
+ if (!list) return -1;
+ return list->getId();
+}
+
+void SW602GraphicListener::_changeList()
+{
+ if (m_ps->m_inNote || !m_ps->isInTextZone())
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::_changeList: called outsize a text zone\n"));
+ return;
+ }
+ if (m_ps->m_isParagraphOpened)
+ _closeParagraph();
+
+ size_t actualLevel = m_ps->m_listOrderedLevels.size();
+ size_t newLevel= (size_t) m_ps->m_paragraph.m_listLevelIndex.get();
+ int newListId = newLevel>0 ? _getListId() : -1;
+ bool changeList = newLevel &&
+ (m_ps->m_list && m_ps->m_list->getId()!=newListId);
+ size_t minLevel = changeList ? 0 : newLevel;
+ while (actualLevel > minLevel)
+ {
+ if (m_ps->m_listOrderedLevels[--actualLevel])
+ m_documentInterface->closeOrderedListLevel();
+ else
+ m_documentInterface->closeUnorderedListLevel();
+ }
+
+ if (newLevel)
+ {
+ boost::shared_ptr<SW602List> theList;
+
+ theList=m_parserState.m_listManager->getList(newListId);
+ if (!theList)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::_changeList: can not find any list\n"));
+ m_ps->m_listOrderedLevels.resize(actualLevel);
+ return;
+ }
+ m_parserState.m_listManager->needToSend(newListId, m_ds->m_sentListMarkers);
+ m_ps->m_list = theList;
+ m_ps->m_list->setLevel((int)newLevel);
+ }
+
+ m_ps->m_listOrderedLevels.resize(newLevel, false);
+ if (actualLevel == newLevel) return;
+
+ librevenge::RVNGPropertyList propList;
+ propList.insert("librevenge:list-id", m_ps->m_list->getId());
+ for (size_t i=actualLevel+1; i<= newLevel; i++)
+ {
+ bool ordered = m_ps->m_list->isNumeric(int(i));
+ m_ps->m_listOrderedLevels[i-1] = ordered;
+
+ librevenge::RVNGPropertyList level;
+ m_ps->m_list->addTo(int(i), level);
+ if (ordered)
+ m_documentInterface->openOrderedListLevel(level);
+ else
+ m_documentInterface->openUnorderedListLevel(level);
+ }
+}
+
+///////////////////
+// span
+///////////////////
+void SW602GraphicListener::_openSpan()
+{
+ if (m_ps->m_isTableOpened && !m_ps->m_isTableCellOpened)
+ return;
+ if (!m_ps->isInTextZone())
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::_openSpan: called outsize a text zone\n"));
+ return;
+ }
+ if (m_ps->m_isSpanOpened)
+ return;
+
+ if (!m_ps->m_isParagraphOpened && !m_ps->m_isListElementOpened)
+ {
+ _changeList();
+ if (*m_ps->m_paragraph.m_listLevelIndex == 0)
+ _openParagraph();
+ else
+ _openListElement();
+ }
+
+ librevenge::RVNGPropertyList propList;
+ m_ps->m_font.addTo(propList);
+
+ m_documentInterface->openSpan(propList);
+
+ m_ps->m_isSpanOpened = true;
+}
+
+void SW602GraphicListener::_closeSpan()
+{
+ if (m_ps->m_isTableOpened && !m_ps->m_isTableCellOpened)
+ return;
+ if (!m_ps->isInTextZone())
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::_closeSpan: called outsize a text zone\n"));
+ return;
+ }
+ if (!m_ps->m_isSpanOpened)
+ return;
+
+ _flushText();
+ m_documentInterface->closeSpan();
+ m_ps->m_isSpanOpened = false;
+}
+
+///////////////////
+// text (send data)
+///////////////////
+void SW602GraphicListener::_flushText()
+{
+ if (m_ps->m_textBuffer.len() == 0) return;
+
+ // when some many ' ' follows each other, call insertSpace
+ librevenge::RVNGString tmpText("");
+ int numConsecutiveSpaces = 0;
+ librevenge::RVNGString::Iter i(m_ps->m_textBuffer);
+ for (i.rewind(); i.next();)
+ {
+ if (*(i()) == 0x20) // this test is compatible with unicode format
+ numConsecutiveSpaces++;
+ else
+ numConsecutiveSpaces = 0;
+
+ if (numConsecutiveSpaces > 1)
+ {
+ if (tmpText.len() > 0)
+ {
+ m_documentInterface->insertText(tmpText);
+ tmpText.clear();
+ }
+ m_documentInterface->insertSpace();
+ }
+ else
+ tmpText.append(i());
+ }
+ m_documentInterface->insertText(tmpText);
+ m_ps->m_textBuffer.clear();
+}
+
+///////////////////
+// header/footer
+///////////////////
+bool SW602GraphicListener::isHeaderFooterOpened() const
+{
+ return m_ds->m_isHeaderFooterStarted;
+}
+
+bool SW602GraphicListener::insertHeader(SW602SubDocumentPtr subDocument, librevenge::RVNGPropertyList const &extras)
+{
+ if (m_ds->m_isHeaderFooterStarted)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::insertHeader: Oops a header/footer is already opened\n"));
+ return false;
+ }
+ // we do not have any header interface, so mimick it by creating a textbox
+ SW602Position pos(SW602Vec2f(20,20), SW602Vec2f(-20,-10), librevenge::RVNG_POINT); // fixme
+ pos.m_anchorTo=SW602Position::Page;
+ if (!openFrame(pos))
+ return false;
+ librevenge::RVNGPropertyList propList(extras);
+ _handleFrameParameters(propList, pos, SW602GraphicStyle::emptyStyle());
+
+ m_documentInterface->startTextObject(propList);
+ handleSubDocument(pos.origin(), subDocument, libsw602::DOC_HEADER_FOOTER);
+ m_documentInterface->endTextObject();
+ closeFrame();
+ return true;
+}
+
+bool SW602GraphicListener::insertFooter(SW602SubDocumentPtr subDocument, librevenge::RVNGPropertyList const &extras)
+{
+ if (m_ds->m_isHeaderFooterStarted)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::insertFooter: Oops a header/footer is already opened\n"));
+ return false;
+ }
+ SW602_DEBUG_MSG(("SW602GraphicListener::insertFooter: inserting footer is very experimental\n"));
+
+ // we do not have any header interface, so mimick it by creating a textbox
+ SW602PageSpan page(getPageSpan()); // fixme
+ SW602Position pos(SW602Vec2f(20,72.f*float(page.getFormLength())-40.f), SW602Vec2f(-20,-10), librevenge::RVNG_POINT);
+ pos.m_anchorTo=SW602Position::Page;
+ if (!openFrame(pos))
+ return false;
+ librevenge::RVNGPropertyList propList(extras);
+ _handleFrameParameters(propList, pos, SW602GraphicStyle::emptyStyle());
+
+ m_documentInterface->startTextObject(propList);
+ handleSubDocument(pos.origin(), subDocument, libsw602::DOC_HEADER_FOOTER);
+ m_documentInterface->endTextObject();
+ closeFrame();
+ return true;
+}
+
+///////////////////
+// section
+///////////////////
+SW602Section const &SW602GraphicListener::getSection() const
+{
+ SW602_DEBUG_MSG(("SW602GraphicListener::getSection: must not be called\n"));
+ static SW602Section s_section;
+ return s_section;
+}
+
+bool SW602GraphicListener::openSection(SW602Section const &)
+{
+ SW602_DEBUG_MSG(("SW602GraphicListener::openSection: must not be called\n"));
+ return false;
+}
+
+void SW602GraphicListener::insertBreak(BreakType breakType)
+{
+ if (m_ps->m_inSubDocument)
+ return;
+
+ switch (breakType)
+ {
+ case ColumnBreak:
+ SW602_DEBUG_MSG(("SW602GraphicListener::insertBreak: must not be called on column\n"));
+ break;
+ case SoftPageBreak:
+ case PageBreak:
+ if (m_ds->m_isMasterPageSpanOpened)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::insertBreak: can not insert a page break in master page definition\n"));
+ break;
+ }
+ if (!m_ds->m_isPageSpanOpened)
+ _openPageSpan();
+ _closePageSpan();
+ break;
+ default:
+ break;
+ }
+}
+
+///////////////////
+// note/comment ( we inserted them as text between -- -- )
+///////////////////
+void SW602GraphicListener::insertComment(SW602SubDocumentPtr &subDocument)
+{
+ if (!canWriteText() || m_ps->m_inNote)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::insertComment try to insert recursively or outside a text zone\n"));
+ return;
+ }
+ // first check that a paragraph is already open
+ if (!m_ps->m_isParagraphOpened && !m_ps->m_isListElementOpened)
+ _openParagraph();
+ insertChar(' ');
+ insertUnicode(0x2014); // -
+ insertChar(' ');
+ handleSubDocument(subDocument, libsw602::DOC_COMMENT_ANNOTATION);
+ insertChar(' ');
+ insertUnicode(0x2014); // -
+ insertChar(' ');
+}
+
+void SW602GraphicListener::insertNote(SW602Note const &, SW602SubDocumentPtr &subDocument)
+{
+ if (!canWriteText() || m_ps->m_inNote)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::insertNote try to insert recursively or outside a text zone\n"));
+ return;
+ }
+ // first check that a paragraph is already open
+ if (!m_ps->m_isParagraphOpened && !m_ps->m_isListElementOpened)
+ _openParagraph();
+ insertChar(' ');
+ insertUnicode(0x2014); // -
+ insertChar(' ');
+ handleSubDocument(subDocument, libsw602::DOC_NOTE);
+ insertChar(' ');
+ insertUnicode(0x2014); // -
+ insertChar(' ');
+}
+
+///////////////////
+// picture/textbox
+///////////////////
+
+void SW602GraphicListener::insertShape
+(SW602Position const &pos, SW602GraphicShape const &shape, SW602GraphicStyle const &style)
+{
+ if (!m_ds->m_isDocumentStarted)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::insertShape: the document is not started\n"));
+ return;
+ }
+ if (!m_ds->m_isPageSpanOpened)
+ _openPageSpan();
+ if (m_ps->m_isFrameOpened)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::insertShape: a frame is already open\n"));
+ return;
+ }
+
+ librevenge::RVNGPropertyList list, shapePList;
+ style.addTo(list, shape.getType()==SW602GraphicShape::Line);
+ m_documentInterface->setStyle(list);
+ switch (shape.addTo(1.f/pos.getInvUnitScale(librevenge::RVNG_POINT)*pos.origin()-m_ps->m_origin, style.hasSurface(), shapePList))
+ {
+ case SW602GraphicShape::C_Ellipse:
+ m_documentInterface->drawEllipse(shapePList);
+ break;
+ case SW602GraphicShape::C_Path:
+ m_documentInterface->drawPath(shapePList);
+ break;
+ case SW602GraphicShape::C_Polyline:
+ m_documentInterface->drawPolyline(shapePList);
+ break;
+ case SW602GraphicShape::C_Polygon:
+ m_documentInterface->drawPolygon(shapePList);
+ break;
+ case SW602GraphicShape::C_Rectangle:
+ m_documentInterface->drawRectangle(shapePList);
+ break;
+ case SW602GraphicShape::C_Bad:
+ break;
+ default:
+ SW602_DEBUG_MSG(("SW602GraphicListener::insertShape: unexpected shape\n"));
+ break;
+ }
+}
+
+void SW602GraphicListener::insertPicture(SW602Position const &pos, SW602EmbeddedObject const &picture, SW602GraphicStyle const &style)
+{
+ if (!m_ds->m_isDocumentStarted)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::insertPicture: the document is not started\n"));
+ return;
+ }
+ if (m_ps->m_isFrameOpened)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::insertPicture: a frame is already open\n"));
+ return;
+ }
+ if (!m_ds->m_isPageSpanOpened)
+ _openPageSpan();
+ librevenge::RVNGPropertyList list;
+ style.addTo(list);
+ m_documentInterface->setStyle(list);
+
+ list.clear();
+ _handleFrameParameters(list, pos, style);
+ float rotate = style.m_rotate;
+ if (style.m_flip[0]&&style.m_flip[1]) rotate += 180.f;
+ if (rotate<0||rotate>0)
+ {
+ list.insert("librevenge:rotate", rotate);
+ float pointFactor =1.f/pos.getInvUnitScale(librevenge::RVNG_POINT);
+ SW602Vec2f size=pointFactor*pos.size();
+ if (size[0]<0) size[0]=-size[0];
+ if (size[1]<0) size[1]=-size[1];
+ SW602Vec2f center=pointFactor*pos.origin()-m_ps->m_origin+0.5f*size;
+ list.insert("librevenge:rotate-cx",center[0], librevenge::RVNG_POINT);
+ list.insert("librevenge:rotate-cy",center[1], librevenge::RVNG_POINT);
+ }
+ if (picture.addTo(list))
+ m_documentInterface->drawGraphicObject(list);
+}
+
+void SW602GraphicListener::insertTextBox
+(SW602Position const &pos, SW602SubDocumentPtr subDocument, SW602GraphicStyle const &style)
+{
+ if (!m_ds->m_isDocumentStarted)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::insertTextBox: the document is not started\n"));
+ return;
+ }
+ if (!m_ds->m_isPageSpanOpened)
+ _openPageSpan();
+ float pointFactor =1.f/pos.getInvUnitScale(librevenge::RVNG_POINT);
+ if (m_ps->m_isTextBoxOpened)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::insertTextBox: can not insert a textbox in a textbox\n"));
+ handleSubDocument(pointFactor*pos.origin(), subDocument, libsw602::DOC_TEXT_BOX);
+ return;
+ }
+ if (!openFrame(pos))
+ return;
+ librevenge::RVNGPropertyList propList;
+ _handleFrameParameters(propList, pos, style);
+ float rotate = style.m_rotate;
+ // flip does not works on text, so we ignore it...
+ if (style.m_flip[0]&&style.m_flip[1]) rotate += 180.f;
+ if (rotate<0||rotate>0)
+ {
+ propList.insert("librevenge:rotate", rotate);
+ SW602Vec2f size=pointFactor*pos.size();
+ if (size[0]<0) size[0]=-size[0];
+ if (size[1]<0) size[1]=-size[1];
+ SW602Vec2f center=pointFactor*pos.origin()-m_ps->m_origin+0.5f*size;
+ propList.insert("librevenge:rotate-cx",center[0], librevenge::RVNG_POINT);
+ propList.insert("librevenge:rotate-cy",center[1], librevenge::RVNG_POINT);
+ }
+ m_documentInterface->startTextObject(propList);
+ handleSubDocument(pointFactor*pos.origin(), subDocument, libsw602::DOC_TEXT_BOX);
+ m_documentInterface->endTextObject();
+ closeFrame();
+}
+
+void SW602GraphicListener::insertGroup(SW602Box2f const &bdbox, SW602SubDocumentPtr subDocument)
+{
+ if (!m_ds->m_isDocumentStarted || m_ps->isInTextZone())
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::insertGroup: can not insert a group\n"));
+ return;
+ }
+ if (!m_ds->m_isPageSpanOpened)
+ _openPageSpan();
+ handleSubDocument(bdbox[0], subDocument, libsw602::DOC_GRAPHIC_GROUP);
+}
+
+///////////////////
+// table
+///////////////////
+void SW602GraphicListener::insertTable
+(SW602Position const &pos, SW602Table &table, SW602GraphicStyle const &style)
+{
+ if (!m_ds->m_isDocumentStarted || m_ps->m_inSubDocument)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::insertTable insert a table in a subdocument is not implemented\n"));
+ return;
+ }
+ if (!openFrame(pos, style)) return;
+
+ _pushParsingState();
+ _startSubDocument();
+ m_ps->m_subDocumentType = libsw602::DOC_TABLE;
+
+ boost::shared_ptr<SW602Listener> listen(this, SW602_shared_ptr_noop_deleter<SW602GraphicListener>());
+ try
+ {
+ table.sendTable(listen);
+ }
+ catch (...)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::insertTable exception catched \n"));
+ }
+ _endSubDocument();
+ _popParsingState();
+
+ closeFrame();
+}
+
+void SW602GraphicListener::openTable(SW602Table const &table)
+{
+ if (!m_ps->m_isFrameOpened)
+ {
+ if (m_ps->m_isTextBoxOpened)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::openTable: must not be called inside a textbox\n"));
+ SW602Position pos(m_ps->m_origin, SW602Vec2f(400,100), librevenge::RVNG_POINT);
+ pos.m_anchorTo=SW602Position::Page;
+ openTable(pos, table, SW602GraphicStyle::emptyStyle());
+ return;
+ }
+ SW602_DEBUG_MSG(("SW602GraphicListener::openTable: called outside openFrame\n"));
+ return;
+ }
+ openTable(m_ps->m_framePosition, table, m_ps->m_frameStyle);
+}
+
+void SW602GraphicListener::openTable(SW602Position const &pos, SW602Table const &table, SW602GraphicStyle const &style)
+{
+ if (!m_ps->m_isFrameOpened || m_ps->m_isTableOpened)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::openTable: no frame is already open...\n"));
+ return;
+ }
+
+ if (m_ps->m_isParagraphOpened)
+ _closeParagraph();
+
+ librevenge::RVNGPropertyList propList;
+ // default value: which can be redefined by table
+ propList.insert("table:align", "left");
+ propList.insert("fo:margin-left", *m_ps->m_paragraph.m_margins[1], *m_ps->m_paragraph.m_marginsUnit);
+ _pushParsingState();
+ _startSubDocument();
+ m_ps->m_subDocumentType = libsw602::DOC_TABLE;
+
+ _handleFrameParameters(propList, pos, style);
+ table.addTablePropertiesTo(propList);
+ m_documentInterface->startTableObject(propList);
+ m_ps->m_isTableOpened = true;
+}
+
+void SW602GraphicListener::closeTable()
+{
+ if (!m_ps->m_isTableOpened)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::closeTable: called with m_isTableOpened=false\n"));
+ return;
+ }
+
+ m_ps->m_isTableOpened = false;
+ _endSubDocument();
+ m_documentInterface->endTableObject();
+
+ _popParsingState();
+}
+
+void SW602GraphicListener::openTableRow(float h, librevenge::RVNGUnit unit, bool headerRow)
+{
+ if (m_ps->m_isTableRowOpened)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::openTableRow: called with m_isTableRowOpened=true\n"));
+ return;
+ }
+ if (!m_ps->m_isTableOpened)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::openTableRow: called with m_isTableOpened=false\n"));
+ return;
+ }
+ librevenge::RVNGPropertyList propList;
+ propList.insert("librevenge:is-header-row", headerRow);
+
+ if (h > 0)
+ propList.insert("style:row-height", h, unit);
+ else if (h < 0)
+ propList.insert("style:min-row-height", -h, unit);
+ m_documentInterface->openTableRow(propList);
+ m_ps->m_isTableRowOpened = true;
+}
+
+void SW602GraphicListener::closeTableRow()
+{
+ if (!m_ps->m_isTableRowOpened)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::closeTableRow: called with m_isTableRowOpened=false\n"));
+ return;
+ }
+ m_ps->m_isTableRowOpened = false;
+ m_documentInterface->closeTableRow();
+}
+
+void SW602GraphicListener::addEmptyTableCell(SW602Vec2i const &pos, SW602Vec2i span)
+{
+ if (!m_ps->m_isTableRowOpened)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::addEmptyTableCell: called with m_isTableRowOpened=false\n"));
+ return;
+ }
+ if (m_ps->m_isTableCellOpened)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::addEmptyTableCell: called with m_isTableCellOpened=true\n"));
+ closeTableCell();
+ }
+ librevenge::RVNGPropertyList propList;
+ propList.insert("librevenge:column", pos[0]);
+ propList.insert("librevenge:row", pos[1]);
+ propList.insert("table:number-columns-spanned", span[0]);
+ propList.insert("table:number-rows-spanned", span[1]);
+ m_documentInterface->openTableCell(propList);
+ m_documentInterface->closeTableCell();
+}
+
+void SW602GraphicListener::openTableCell(SW602Cell const &cell)
+{
+ if (!m_ps->m_isTableRowOpened)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::openTableCell: called with m_isTableRowOpened=false\n"));
+ return;
+ }
+ if (m_ps->m_isTableCellOpened)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::openTableCell: called with m_isTableCellOpened=true\n"));
+ closeTableCell();
+ }
+
+ librevenge::RVNGPropertyList propList;
+ cell.addTo(propList);
+ m_ps->m_isTableCellOpened = true;
+ m_documentInterface->openTableCell(propList);
+}
+
+void SW602GraphicListener::closeTableCell()
+{
+ if (!m_ps->m_isTableCellOpened)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::closeTableCell: called with m_isTableCellOpened=false\n"));
+ return;
+ }
+
+ _closeParagraph();
+ m_ps->m_paragraph.m_listLevelIndex=0;
+ _changeList(); // flush the list exterior
+
+ m_documentInterface->closeTableCell();
+ m_ps->m_isTableCellOpened = false;
+}
+
+///////////////////
+// frame/group
+///////////////////
+bool SW602GraphicListener::openFrame(SW602Position const &pos, SW602GraphicStyle const &style)
+{
+ if (!m_ds->m_isDocumentStarted)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::openFrame: the document is not started\n"));
+ return false;
+ }
+ if (m_ps->m_isTableOpened && !m_ps->m_isTableCellOpened)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::openFrame: called in table but cell is not opened\n"));
+ return false;
+ }
+ if (m_ps->m_isFrameOpened)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::openFrame: called but a frame is already opened\n"));
+ return false;
+ }
+ if (!m_ds->m_isPageSpanOpened)
+ _openPageSpan();
+ m_ps->m_isFrameOpened = true;
+ m_ps->m_framePosition=pos;
+ m_ps->m_frameStyle=style;
+ return true;
+}
+
+void SW602GraphicListener::closeFrame()
+{
+ if (!m_ps->m_isFrameOpened)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::closeFrame: called but no frame is already opened\n"));
+ return;
+ }
+ m_ps->m_isFrameOpened = false;
+}
+
+bool SW602GraphicListener::openGroup(SW602Position const &pos)
+{
+ if (!m_ds->m_isDocumentStarted)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::openGroup: the document is not started\n"));
+ return false;
+ }
+ if (m_ps->m_isTableOpened || m_ps->isInTextZone())
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::openGroup: called in table or in a text zone\n"));
+ return false;
+ }
+ if (!m_ds->m_isPageSpanOpened)
+ _openPageSpan();
+
+ librevenge::RVNGPropertyList propList;
+ _handleFrameParameters(propList, pos, SW602GraphicStyle::emptyStyle());
+
+ _pushParsingState();
+ _startSubDocument();
+ m_ps->m_isGroupOpened = true;
+
+ m_documentInterface->openGroup(propList);
+
+ return true;
+}
+
+void SW602GraphicListener::closeGroup()
+{
+ if (!m_ps->m_isGroupOpened)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::closeGroup: called but no group is already opened\n"));
+ return;
+ }
+ _endSubDocument();
+ _popParsingState();
+ m_documentInterface->closeGroup();
+}
+
+bool SW602GraphicListener::openLayer(librevenge::RVNGString const &layerName)
+{
+ if (!m_ds->m_isDocumentStarted)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::openLayer: the document is not started\n"));
+ return false;
+ }
+ if (m_ps->m_isTableOpened || m_ps->isInTextZone())
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::openLayer: called in table or in a text zone\n"));
+ return false;
+ }
+ if (m_ps->m_isLayerOpened)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::openLayer: called but layer is already opened\n"));
+ return false;
+ }
+ if (!m_ds->m_isPageSpanOpened)
+ _openPageSpan();
+
+ _pushParsingState();
+ _startSubDocument();
+ m_ps->m_isLayerOpened = true;
+
+ librevenge::RVNGPropertyList propList;
+ propList.insert("draw:layer", layerName);
+ m_documentInterface->startLayer(propList);
+ return true;
+}
+
+void SW602GraphicListener::closeLayer()
+{
+ if (!m_ps->m_isLayerOpened)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::closeLayer: called but no layer is already opened\n"));
+ return;
+ }
+ m_documentInterface->endLayer();
+ _endSubDocument();
+ _popParsingState();
+}
+
+void SW602GraphicListener::_handleFrameParameters(librevenge::RVNGPropertyList &list, SW602Position const &pos, SW602GraphicStyle const &style)
+{
+ if (!m_ds->m_isDocumentStarted)
+ return;
+
+ librevenge::RVNGUnit unit = pos.unit();
+ float pointFactor = pos.getInvUnitScale(librevenge::RVNG_POINT);
+ float inchFactor=pos.getInvUnitScale(librevenge::RVNG_INCH);
+ // first compute the origin ( in given unit and in point)
+ SW602Vec2f origin = pos.origin()-pointFactor*m_ps->m_origin;
+ SW602Vec2f originPt = (1.f/pointFactor)*pos.origin()-m_ps->m_origin;
+ SW602Vec2f size = pos.size();
+ // checkme: do we still need to do that ?
+ if (style.hasGradient(true))
+ {
+ if (style.m_rotate<0 || style.m_rotate>0)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::_handleFrameParameters: rotation is not implemented\n"));
+ }
+ // ok, first send a background rectangle
+ librevenge::RVNGPropertyList rectList;
+ m_documentInterface->setStyle(rectList);
+ rectList.clear();
+ rectList.insert("svg:x",originPt[0], librevenge::RVNG_POINT);
+ rectList.insert("svg:y",originPt[1], librevenge::RVNG_POINT);
+ rectList.insert("svg:width",size.x()>0 ? size.x() : -size.x(), unit);
+ rectList.insert("svg:height",size.y()>0 ? size.y() : -size.y(), unit);
+ m_documentInterface->drawRectangle(rectList);
+
+ list.insert("draw:stroke", "none");
+ list.insert("draw:fill", "none");
+ }
+ else
+ style.addTo(list);
+
+ list.insert("svg:x",originPt[0], librevenge::RVNG_POINT);
+ list.insert("svg:y",originPt[1], librevenge::RVNG_POINT);
+ if (size.x()>0)
+ list.insert("svg:width",size.x(), unit);
+ else if (size.x()<0)
+ list.insert("fo:min-width",-size.x(), unit);
+ if (size.y()>0)
+ list.insert("svg:height",size.y(), unit);
+ else if (size.y()<0)
+ list.insert("fo:min-height",-size.y(), unit);
+ if (pos.order() > 0)
+ list.insert("draw:z-index", pos.order());
+ if (pos.naturalSize().x() > 4*pointFactor && pos.naturalSize().y() > 4*pointFactor)
+ {
+ list.insert("librevenge:naturalWidth", pos.naturalSize().x(), pos.unit());
+ list.insert("librevenge:naturalHeight", pos.naturalSize().y(), pos.unit());
+ }
+ SW602Vec2f TLClip = (1.f/pointFactor)*pos.leftTopClipping();
+ SW602Vec2f RBClip = (1.f/pointFactor)*pos.rightBottomClipping();
+ if (TLClip[0] > 0 || TLClip[1] > 0 || RBClip[0] > 0 || RBClip[1] > 0)
+ {
+ // in ODF1.2 we need to separate the value with ,
+ std::stringstream s;
+ s << "rect(" << TLClip[1] << "pt " << RBClip[0] << "pt "
+ << RBClip[1] << "pt " << TLClip[0] << "pt)";
+ list.insert("fo:clip", s.str().c_str());
+ }
+
+ if (pos.m_wrapping == SW602Position::WDynamic)
+ list.insert("style:wrap", "dynamic");
+ else if (pos.m_wrapping == SW602Position::WBackground)
+ {
+ list.insert("style:wrap", "run-through");
+ list.insert("style:run-through", "background");
+ }
+ else if (pos.m_wrapping == SW602Position::WForeground)
+ {
+ list.insert("style:wrap", "run-through");
+ list.insert("style:run-through", "foreground");
+ }
+ else if (pos.m_wrapping == SW602Position::WParallel)
+ {
+ list.insert("style:wrap", "parallel");
+ list.insert("style:run-through", "foreground");
+ }
+ else if (pos.m_wrapping == SW602Position::WRunThrough)
+ list.insert("style:wrap", "run-through");
+ else
+ list.insert("style:wrap", "none");
+ if (pos.m_anchorTo != SW602Position::Page)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::_handleFrameParameters: only page anchor is implemented\n"));
+ }
+ else
+ {
+ double w = m_ds->m_pageSpan.getFormWidth();
+ double h = m_ds->m_pageSpan.getFormLength();
+ w *= inchFactor;
+ h *= inchFactor;
+ double newPosition;
+ switch (pos.m_yPos)
+ {
+ case SW602Position::YFull:
+ list.insert("svg:height", double(h), unit);
+ // fallthrough intended
+ case SW602Position::YTop:
+ if (origin[1] < 0.0 || origin[1] > 0.0)
+ {
+ list.insert("style:vertical-pos", "from-top");
+ newPosition = origin[1];
+ if (newPosition > h -pos.size()[1])
+ newPosition = h - pos.size()[1];
+ list.insert("svg:y", double(newPosition), unit);
+ }
+ else
+ list.insert("style:vertical-pos", "top");
+ break;
+ case SW602Position::YCenter:
+ if (origin[1] < 0.0 || origin[1] > 0.0)
+ {
+ list.insert("style:vertical-pos", "from-top");
+ newPosition = (h - pos.size()[1])/2.0;
+ if (newPosition > h -pos.size()[1]) newPosition = h - pos.size()[1];
+ list.insert("svg:y", double(newPosition), unit);
+ }
+ else
+ list.insert("style:vertical-pos", "middle");
+ break;
+ case SW602Position::YBottom:
+ if (origin[1] < 0.0 || origin[1] > 0.0)
+ {
+ list.insert("style:vertical-pos", "from-top");
+ newPosition = h - pos.size()[1]-origin[1];
+ if (newPosition > h -pos.size()[1]) newPosition = h -pos.size()[1];
+ else if (newPosition < 0) newPosition = 0;
+ list.insert("svg:y", double(newPosition), unit);
+ }
+ else
+ list.insert("style:vertical-pos", "bottom");
+ break;
+ default:
+ break;
+ }
+
+ switch (pos.m_xPos)
+ {
+ case SW602Position::XFull:
+ list.insert("svg:width", double(w), unit);
+ // fallthrough intended
+ case SW602Position::XLeft:
+ if (origin[0] < 0.0 || origin[0] > 0.0)
+ {
+ list.insert("style:horizontal-pos", "from-left");
+ list.insert("svg:x", double(origin[0]), unit);
+ }
+ else
+ list.insert("style:horizontal-pos", "left");
+ break;
+ case SW602Position::XRight:
+ if (origin[0] < 0.0 || origin[0] > 0.0)
+ {
+ list.insert("style:horizontal-pos", "from-left");
+ list.insert("svg:x",double(w - pos.size()[0] + origin[0]), unit);
+ }
+ else
+ list.insert("style:horizontal-pos", "right");
+ break;
+ case SW602Position::XCenter:
+ if (origin[0] < 0.0 || origin[0] > 0.0)
+ {
+ list.insert("style:horizontal-pos", "from-left");
+ list.insert("svg:x", double((w - pos.size()[0])/2. + origin[0]), unit);
+ }
+ else
+ list.insert("style:horizontal-pos", "center");
+ break;
+ default:
+ break;
+ }
+
+ }
+ float const padding = 0; // fillme
+ list.insert("fo:padding-top",padding, librevenge::RVNG_POINT);
+ list.insert("fo:padding-bottom",padding, librevenge::RVNG_POINT);
+ list.insert("fo:padding-left",padding, librevenge::RVNG_POINT);
+ list.insert("fo:padding-right",padding, librevenge::RVNG_POINT);
+}
+
+///////////////////
+// subdocument
+///////////////////
+void SW602GraphicListener::handleSubDocument(SW602Vec2f const &orig, SW602SubDocumentPtr subDocument, libsw602::SubDocumentType subDocumentType)
+{
+ if (!m_ds->m_isDocumentStarted)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::handleSubDocument: the graphic is not started\n"));
+ return;
+ }
+ if (!m_ds->m_isPageSpanOpened)
+ _openPageSpan();
+ SW602Vec2f actOrigin=m_ps->m_origin;
+ _pushParsingState();
+ m_ps->m_origin=actOrigin-orig;
+ _startSubDocument();
+ m_ps->m_subDocumentType = subDocumentType;
+
+ m_ps->m_list.reset();
+ if (subDocumentType==libsw602::DOC_TEXT_BOX)
+ m_ps->m_isTextBoxOpened=true;
+ else if (subDocumentType==libsw602::DOC_HEADER_FOOTER)
+ {
+ m_ps->m_isTextBoxOpened=true;
+ m_ds->m_isHeaderFooterStarted = true;
+ }
+ else if (subDocumentType==libsw602::DOC_COMMENT_ANNOTATION || subDocumentType==libsw602::DOC_NOTE)
+ m_ps->m_inNote=true;
+ // Check whether the document is calling itself
+ bool sendDoc = true;
+ for (size_t i = 0; i < m_ds->m_subDocuments.size(); i++)
+ {
+ if (!subDocument)
+ break;
+ if (subDocument == m_ds->m_subDocuments[i])
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::handleSubDocument: recursif call, stop...\n"));
+ sendDoc = false;
+ break;
+ }
+ }
+ if (sendDoc)
+ {
+ if (subDocument)
+ {
+ m_ds->m_subDocuments.push_back(subDocument);
+ boost::shared_ptr<SW602Listener> listen(this, SW602_shared_ptr_noop_deleter<SW602Listener>());
+ try
+ {
+ subDocument->parse(listen, subDocumentType);
+ }
+ catch (...)
+ {
+ SW602_DEBUG_MSG(("Works: SW602GraphicListener::handleSubDocument exception catched \n"));
+ }
+ m_ds->m_subDocuments.pop_back();
+ }
+ }
+
+ _endSubDocument();
+ _popParsingState();
+
+ if (subDocumentType==libsw602::DOC_HEADER_FOOTER)
+ m_ds->m_isHeaderFooterStarted = false;
+}
+
+bool SW602GraphicListener::isSubDocumentOpened(libsw602::SubDocumentType &subdocType) const
+{
+ if (!m_ds->m_isDocumentStarted || !m_ps->m_inSubDocument)
+ return false;
+ subdocType = m_ps->m_subDocumentType;
+ return true;
+}
+
+void SW602GraphicListener::_startSubDocument()
+{
+ if (!m_ds->m_isDocumentStarted) return;
+ m_ps->m_inSubDocument = true;
+}
+
+void SW602GraphicListener::_endSubDocument()
+{
+ if (!m_ds->m_isDocumentStarted) return;
+ if (m_ps->m_isTableOpened)
+ closeTable();
+ if (m_ps->m_isParagraphOpened)
+ _closeParagraph();
+ if (m_ps->isInTextZone())
+ {
+ m_ps->m_paragraph.m_listLevelIndex=0;
+ _changeList(); // flush the list exterior
+ }
+}
+
+///////////////////
+// others
+///////////////////
+
+// ---------- state stack ------------------
+boost::shared_ptr<SW602GraphicListenerInternal::State> SW602GraphicListener::_pushParsingState()
+{
+ boost::shared_ptr<SW602GraphicListenerInternal::State> actual = m_ps;
+ m_psStack.push_back(actual);
+ m_ps.reset(new SW602GraphicListenerInternal::State);
+ return actual;
+}
+
+void SW602GraphicListener::_popParsingState()
+{
+ if (m_psStack.size()==0)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicListener::_popParsingState: psStack is empty()\n"));
+ throw libsw602::ParseException();
+ }
+ m_ps = m_psStack.back();
+ m_psStack.pop_back();
+}
+
+}
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602GraphicListener.h b/src/lib/SW602GraphicListener.h
new file mode 100644
index 0000000..95b3939
--- /dev/null
+++ b/src/lib/SW602GraphicListener.h
@@ -0,0 +1,284 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SW602_GRAPHIC_LISTENER_H
+#define INCLUDED_SW602_GRAPHIC_LISTENER_H
+
+#include <vector>
+
+#include "SW602GraphicStyle.h"
+#include "SW602Listener.h"
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+class SW602GraphicShape;
+
+namespace SW602GraphicListenerInternal
+{
+struct GraphicState;
+struct State;
+}
+
+/** This class contains the code needed to create Graphic document.
+
+ \note All units are specified in librevenge::RVNG_POINT
+ */
+class SW602GraphicListener : public SW602Listener
+{
+public:
+ /** constructor */
+ SW602GraphicListener(SW602ParserState &parserState, std::vector<SW602PageSpan> const &pageList, librevenge::RVNGDrawingInterface *documentInterface);
+ /** simplified constructor (can be used for a embedded graphic with one page).
+
+ \note the box coordinates must be given in point.*/
+ SW602GraphicListener(SW602ParserState &parserState, SW602Box2f const &box, librevenge::RVNGDrawingInterface *documentInterface);
+ /** destructor */
+ virtual ~SW602GraphicListener();
+
+ /** returns the listener type */
+ Type getType() const
+ {
+ return Graphic;
+ }
+
+ /** sets the documents language */
+ void setDocumentLanguage(std::string locale);
+ /** starts a new document */
+ void startDocument();
+ /** ends the actual document */
+ void endDocument(bool delayed=true);
+
+ // ------ general information --------
+ /** returns true if a text zone is opened */
+ bool canWriteText() const;
+ /** returns true if a document is opened */
+ bool isDocumentStarted() const;
+
+ /** function called to add a subdocument and modify the origin*/
+ void handleSubDocument(SW602Vec2f const &orig, SW602SubDocumentPtr subDocument, libsw602::SubDocumentType subDocumentType);
+ /** function called to add a subdocument */
+ void handleSubDocument(SW602SubDocumentPtr subDocument, libsw602::SubDocumentType subDocumentType)
+ {
+ handleSubDocument(SW602Vec2f(0,0), subDocument, subDocumentType);
+ }
+ /** returns try if a subdocument is open */
+ bool isSubDocumentOpened(libsw602::SubDocumentType &subdocType) const;
+ /** store the position and the style (which will be needed further to insert a textbox or a table with openTable) */
+ bool openFrame(SW602Position const &pos, SW602GraphicStyle const &style=SW602GraphicStyle::emptyStyle());
+ /** close a frame */
+ void closeFrame();
+ /** open a group */
+ bool openGroup(SW602Position const &pos);
+ /** close a group */
+ void closeGroup();
+ /** open a layer */
+ bool openLayer(librevenge::RVNGString const &name);
+ /** close a layer */
+ void closeLayer();
+
+ // ------ page --------
+ /** opens a master page */
+ bool openMasterPage(SW602PageSpan &masterPage);
+ /** close a master page */
+ void closeMasterPage()
+ {
+ _closePageSpan(true);
+ }
+ /** returns true if a page is opened */
+ bool isPageSpanOpened() const;
+ /** returns the current page span
+
+ \note this forces the opening of a new page if no page is opened.*/
+ SW602PageSpan const &getPageSpan();
+
+ // ------ header/footer --------
+ /** insert a header */
+ bool insertHeader(SW602SubDocumentPtr subDocument, librevenge::RVNGPropertyList const &extras);
+ /** insert a footer */
+ bool insertFooter(SW602SubDocumentPtr subDocument, librevenge::RVNGPropertyList const &extras);
+ /** returns true if the header/footer is open */
+ bool isHeaderFooterOpened() const;
+
+ // ------ text data -----------
+ //! adds a basic character, ..
+ void insertChar(uint8_t character);
+ /** insert a character using the font converter to find the utf8
+ character */
+ void insertCharacter(unsigned char c);
+ /** insert a character using the font converter to find the utf8
+ character and if needed, input to read extra character.
+
+ \return the number of extra character read
+ */
+ int insertCharacter(unsigned char c, librevenge::RVNGInputStream &input, long endPos=-1);
+ /** adds an unicode character.
+ * By convention if \a character=0xfffd(undef), no character is added */
+ void insertUnicode(uint32_t character);
+ //! adds a unicode string
+ void insertUnicodeString(librevenge::RVNGString const &str);
+
+ //! adds a tab
+ void insertTab();
+ //! adds an end of line ( by default an hard one)
+ void insertEOL(bool softBreak=false);
+
+ // ------ text format -----------
+ //! sets the font
+ void setFont(SW602Font const &font);
+ //! returns the actual font
+ SW602Font const &getFont() const;
+
+ // ------ paragraph format -----------
+ //! returns true if a paragraph or a list is opened
+ bool isParagraphOpened() const;
+ //! sets the paragraph
+ void setParagraph(SW602Paragraph const &paragraph);
+ //! returns the actual paragraph
+ SW602Paragraph const &getParagraph() const;
+
+ // ------- fields ----------------
+ //! adds a field type
+ void insertField(SW602Field const &field);
+
+ // ------- link ----------------
+ //! open a link
+ void openLink(SW602Link const &link);
+ //! close a link
+ void closeLink();
+
+ // ------- subdocument -----------------
+ /** adds a picture with potential various representationin given position */
+ void insertPicture(SW602Position const &pos, SW602EmbeddedObject const &picture,
+ SW602GraphicStyle const &style=SW602GraphicStyle::emptyStyle());
+ /** adds a shape picture in given position */
+ void insertShape(SW602Position const &pos, SW602GraphicShape const &shape, SW602GraphicStyle const &style);
+ /** adds a textbox in given position */
+ void insertTextBox(SW602Position const &pos, SW602SubDocumentPtr subDocument, SW602GraphicStyle const &style);
+ /** adds a group: ie. next insertion will be done relative to this bdbox[0] position */
+ void insertGroup(SW602Box2f const &bdbox, SW602SubDocumentPtr subDocument);
+ /** insert a note
+
+ \note as RVNGDrawingInterface does not accept note, note can only be inserted in a text zone (and are inserted between --) */
+ void insertNote(SW602Note const &note, SW602SubDocumentPtr &subDocument);
+ /** adds comment
+
+ \note as RVNGDrawingInterface does not accept comment, comment can only be inserted in a text zone (and are inserted between --) */
+ void insertComment(SW602SubDocumentPtr &subDocument);
+
+ // ------- table -----------------
+
+ /** adds a table in given position */
+ void insertTable(SW602Position const &pos, SW602Table &table, SW602GraphicStyle const &style=SW602GraphicStyle::emptyStyle());
+ /** open a table (using the last parameters of openFrame for the position ) */
+ void openTable(SW602Table const &table);
+ /** open a table in a given position */
+ void openTable(SW602Position const &pos, SW602Table const &table, SW602GraphicStyle const &style);
+ /** closes this table */
+ void closeTable();
+ /** open a row with given height ( if h < 0.0, set min-row-height = -h )*/
+ void openTableRow(float h, librevenge::RVNGUnit unit, bool headerRow=false);
+ /** closes this row */
+ void closeTableRow();
+ /** open a cell */
+ void openTableCell(SW602Cell const &cell);
+ /** close a cell */
+ void closeTableCell();
+ /** add empty cell */
+ void addEmptyTableCell(SW602Vec2i const &pos, SW602Vec2i span=SW602Vec2i(1,1));
+
+ // ------- section ---------------
+
+ /** returns true if we can add open a section, add page break, ... */
+ bool canOpenSectionAddBreak() const
+ {
+ return false;
+ }
+ //! returns true if a section is opened
+ bool isSectionOpened() const
+ {
+ return false;
+ }
+ //! returns the actual section
+ SW602Section const &getSection() const;
+ //! open a section if possible
+ bool openSection(SW602Section const &section);
+ //! close a section
+ bool closeSection()
+ {
+ return false;
+ }
+ //! inserts a break type: ColumBreak, PageBreak, ..
+ void insertBreak(BreakType breakType);
+
+protected:
+ //! does open a new page (low level)
+ void _openPageSpan(bool sendHeaderFooters=true);
+ //! does close a page (low level)
+ void _closePageSpan(bool masterPage=false);
+
+ void _startSubDocument();
+ void _endSubDocument();
+
+ /** adds in propList the frame parameters.
+
+ \note if there is some gradient, first draw a rectangle to print the gradient and them update propList */
+ void _handleFrameParameters(librevenge::RVNGPropertyList &propList, SW602Position const &pos, SW602GraphicStyle const &style);
+
+ void _openParagraph();
+ void _closeParagraph();
+ void _resetParagraphState(const bool isListElement=false);
+
+ /** open a list level */
+ void _openListElement();
+ /** close a list level */
+ void _closeListElement();
+ /** update the list so that it corresponds to the actual level */
+ void _changeList();
+ /** low level: find a list id which corresponds to actual list and a change of level.
+
+ \note called when the list id is not set
+ */
+ int _getListId() const;
+
+ void _openSpan();
+ void _closeSpan();
+
+ void _flushText();
+
+ /** creates a new parsing state (copy of the actual state)
+ *
+ * \return the old one */
+ boost::shared_ptr<SW602GraphicListenerInternal::State> _pushParsingState();
+ //! resets the previous parsing state
+ void _popParsingState();
+
+protected:
+ //! the actual global state
+ boost::shared_ptr<SW602GraphicListenerInternal::GraphicState> m_ds;
+ //! the actual local parse state
+ boost::shared_ptr<SW602GraphicListenerInternal::State> m_ps;
+ //! stack of local state
+ std::vector<boost::shared_ptr<SW602GraphicListenerInternal::State> > m_psStack;
+ //! the parser state
+ SW602ParserState &m_parserState;
+ //! the document interface
+ librevenge::RVNGDrawingInterface *m_documentInterface;
+
+private:
+ SW602GraphicListener(const SW602GraphicListener &);
+ SW602GraphicListener &operator=(const SW602GraphicListener &);
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602GraphicShape.cpp b/src/lib/SW602GraphicShape.cpp
new file mode 100644
index 0000000..0dee0b1
--- /dev/null
+++ b/src/lib/SW602GraphicShape.cpp
@@ -0,0 +1,648 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/* This header contains code specific to a pict mac file
+ */
+
+#include "SW602GraphicShape.h"
+
+#include <string.h>
+
+#include <cmath>
+#include <cstring>
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <librevenge/librevenge.h>
+
+#include "SW602GraphicEncoder.h"
+#include "SW602GraphicStyle.h"
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+////////////////////////////////////////////////////////////
+// SW602GraphicShape::PathData
+////////////////////////////////////////////////////////////
+std::ostream &operator<<(std::ostream &o, SW602GraphicShape::PathData const &path)
+{
+ o << path.m_type;
+ switch (path.m_type)
+ {
+ case 'H':
+ o << ":" << path.m_x[0];
+ break;
+ case 'V':
+ o << ":" << path.m_x[1];
+ break;
+ case 'M':
+ case 'L':
+ case 'T':
+ o << ":" << path.m_x;
+ break;
+ case 'Q':
+ case 'S':
+ o << ":" << path.m_x << ":" << path.m_x1;
+ break;
+ case 'C':
+ o << ":" << path.m_x << ":" << path.m_x1 << ":" << path.m_x2;
+ break;
+ case 'A':
+ o << ":" << path.m_x << ":r=" << path.m_r;
+ if (path.m_largeAngle) o << ":largeAngle";
+ if (path.m_sweep) o << ":sweep";
+ if (path.m_rotate<0 || path.m_rotate>0) o << ":rot=" << path.m_rotate;
+ case 'Z':
+ break;
+ default:
+ o << "###";
+ }
+ return o;
+}
+
+void SW602GraphicShape::PathData::translate(SW602Vec2f const &decal)
+{
+ if (m_type=='Z')
+ return;
+ m_x += decal;
+ if (m_type=='H' || m_type=='V' || m_type=='M' || m_type=='L' || m_type=='T' || m_type=='A')
+ return;
+ m_x1 += decal;
+ if (m_type=='Q' || m_type=='S')
+ return;
+ m_x2 += decal;
+}
+
+void SW602GraphicShape::PathData::scale(SW602Vec2f const &scaling)
+{
+ if (m_type=='Z')
+ return;
+ m_x = SW602Vec2f(m_x[0]*scaling[0], m_x[1]*scaling[1]);
+ if (m_type=='H' || m_type=='V' || m_type=='M' || m_type=='L' || m_type=='T' || m_type=='A')
+ return;
+ m_x1 = SW602Vec2f(m_x1[0]*scaling[0], m_x1[1]*scaling[1]);
+ if (m_type=='Q' || m_type=='S')
+ return;
+ m_x2 = SW602Vec2f(m_x2[0]*scaling[0], m_x2[1]*scaling[1]);
+}
+
+void SW602GraphicShape::PathData::rotate(float angle, SW602Vec2f const &decal)
+{
+ if (m_type=='Z')
+ return;
+ float angl=angle*float(M_PI/180.);
+ m_x = SW602Vec2f(std::cos(angl)*m_x[0]-std::sin(angl)*m_x[1],
+ std::sin(angl)*m_x[0]+std::cos(angl)*m_x[1])+decal;
+ if (m_type=='A')
+ {
+ m_rotate += angle;
+ return;
+ }
+ if (m_type=='H' || m_type=='V' || m_type=='M' || m_type=='L' || m_type=='T')
+ return;
+ m_x1 = SW602Vec2f(std::cos(angl)*m_x1[0]-std::sin(angl)*m_x1[1],
+ std::sin(angl)*m_x1[0]+std::cos(angl)*m_x1[1])+decal;
+ if (m_type=='Q' || m_type=='S')
+ return;
+ m_x2 = SW602Vec2f(std::cos(angl)*m_x2[0]-std::sin(angl)*m_x2[1],
+ std::sin(angl)*m_x2[0]+std::cos(angl)*m_x2[1])+decal;
+}
+
+bool SW602GraphicShape::PathData::get(librevenge::RVNGPropertyList &list, SW602Vec2f const &orig) const
+{
+ list.clear();
+ std::string type("");
+ type += m_type;
+ list.insert("librevenge:path-action", type.c_str());
+ if (m_type=='Z')
+ return true;
+ if (m_type=='H')
+ {
+ list.insert("svg:x",m_x[0]-orig[0], librevenge::RVNG_POINT);
+ return true;
+ }
+ if (m_type=='V')
+ {
+ list.insert("svg:y",m_x[1]-orig[1], librevenge::RVNG_POINT);
+ return true;
+ }
+ list.insert("svg:x",m_x[0]-orig[0], librevenge::RVNG_POINT);
+ list.insert("svg:y",m_x[1]-orig[1], librevenge::RVNG_POINT);
+ if (m_type=='M' || m_type=='L' || m_type=='T')
+ return true;
+ if (m_type=='A')
+ {
+ list.insert("svg:rx",m_r[0], librevenge::RVNG_POINT);
+ list.insert("svg:ry",m_r[1], librevenge::RVNG_POINT);
+ list.insert("librevenge:large-arc", m_largeAngle);
+ list.insert("librevenge:sweep", m_sweep);
+ list.insert("librevenge:rotate", m_rotate, librevenge::RVNG_GENERIC);
+ return true;
+ }
+ list.insert("svg:x1",m_x1[0]-orig[0], librevenge::RVNG_POINT);
+ list.insert("svg:y1",m_x1[1]-orig[1], librevenge::RVNG_POINT);
+ if (m_type=='Q' || m_type=='S')
+ return true;
+ list.insert("svg:x2",m_x2[0]-orig[0], librevenge::RVNG_POINT);
+ list.insert("svg:y2",m_x2[1]-orig[1], librevenge::RVNG_POINT);
+ if (m_type=='C')
+ return true;
+ SW602_DEBUG_MSG(("SW602GraphicShape::PathData::get: unknown command %c\n", m_type));
+ list.clear();
+ return false;
+}
+
+int SW602GraphicShape::PathData::cmp(SW602GraphicShape::PathData const &a) const
+{
+ if (m_type < a.m_type) return 1;
+ if (m_type > a.m_type) return 1;
+ int diff = m_x.cmp(a.m_x);
+ if (diff) return diff;
+ diff = m_x1.cmp(a.m_x1);
+ if (diff) return diff;
+ diff = m_x2.cmp(a.m_x2);
+ if (diff) return diff;
+ diff = m_r.cmp(a.m_r);
+ if (diff) return diff;
+ if (m_rotate < a.m_rotate) return 1;
+ if (m_rotate > a.m_rotate) return -1;
+ if (m_largeAngle != a.m_largeAngle)
+ return m_largeAngle ? 1 : -1;
+ if (m_sweep != a.m_sweep)
+ return m_sweep ? 1 : -1;
+ return 0;
+}
+
+////////////////////////////////////////////////////////////
+// SW602GraphicShape
+////////////////////////////////////////////////////////////
+SW602GraphicShape SW602GraphicShape::line(SW602Vec2f const &orig, SW602Vec2f const &dest)
+{
+ SW602GraphicShape res;
+ res.m_type = SW602GraphicShape::Line;
+ res.m_vertices.resize(2);
+ res.m_vertices[0]=orig;
+ res.m_vertices[1]=dest;
+ SW602Vec2f minPt(orig), maxPt(orig);
+ for (int c=0; c<2; ++c)
+ {
+ if (orig[c] < dest[c])
+ maxPt[c]=dest[c];
+ else
+ minPt[c]=dest[c];
+ }
+ res.m_bdBox=SW602Box2f(minPt,maxPt);
+ return res;
+}
+
+SW602GraphicShape SW602GraphicShape::measure(SW602Vec2f const &orig, SW602Vec2f const &dest)
+{
+ SW602GraphicShape res=line(orig,dest);
+ res.m_type= SW602GraphicShape::Measure;
+ return res;
+}
+
+std::ostream &operator<<(std::ostream &o, SW602GraphicShape const &sh)
+{
+ o << "box=" << sh.m_bdBox << ",";
+ switch (sh.m_type)
+ {
+ case SW602GraphicShape::Line:
+ o << "line,";
+ if (sh.m_vertices.size()!=2)
+ o << "###pts,";
+ else
+ o << "pts=" << sh.m_vertices[0] << "<->" << sh.m_vertices[1] << ",";
+ break;
+ case SW602GraphicShape::Measure:
+ o << "measure,";
+ if (sh.m_vertices.size()!=2)
+ o << "###pts,";
+ else
+ o << "pts=" << sh.m_vertices[0] << "<->" << sh.m_vertices[1] << ",";
+ break;
+ case SW602GraphicShape::Rectangle:
+ o << "rect,";
+ if (sh.m_formBox!=sh.m_bdBox)
+ o << "box[rect]=" << sh.m_formBox << ",";
+ if (sh.m_cornerWidth!=SW602Vec2f(0,0))
+ o << "corners=" << sh.m_cornerWidth << ",";
+ break;
+ case SW602GraphicShape::Circle:
+ o << "circle,";
+ break;
+ case SW602GraphicShape::Arc:
+ case SW602GraphicShape::Pie:
+ o << (sh.m_type == SW602GraphicShape::Arc ? "arc," : "pie,");
+ o << "box[ellipse]=" << sh.m_formBox << ",";
+ o << "angle=" << sh.m_arcAngles << ",";
+ break;
+ case SW602GraphicShape::Polygon:
+ o << "polygons,pts=[";
+ for (size_t pt=0; pt < sh.m_vertices.size(); ++pt)
+ o << sh.m_vertices[pt] << ",";
+ o << "],";
+ break;
+ case SW602GraphicShape::Path:
+ o << "path,pts=[";
+ for (size_t pt=0; pt < sh.m_path.size(); ++pt)
+ o << sh.m_path[pt] << ",";
+ o << "],";
+ break;
+ case SW602GraphicShape::ShapeUnknown:
+ default:
+ o << "###unknown[shape],";
+ break;
+ }
+ o << sh.m_extra;
+ return o;
+}
+
+int SW602GraphicShape::cmp(SW602GraphicShape const &a) const
+{
+ if (m_type < a.m_type) return 1;
+ if (m_type > a.m_type) return -1;
+ int diff = m_bdBox.cmp(a.m_bdBox);
+ if (diff) return diff;
+ diff = m_formBox.cmp(a.m_formBox);
+ if (diff) return diff;
+ diff = m_cornerWidth.cmp(a.m_cornerWidth);
+ if (diff) return diff;
+ diff = m_arcAngles.cmp(a.m_arcAngles);
+ if (diff) return diff;
+ if (m_vertices.size()<a.m_vertices.size()) return -1;
+ if (m_vertices.size()>a.m_vertices.size()) return -1;
+ for (size_t pt=0; pt < m_vertices.size(); ++pt)
+ {
+ diff = m_vertices[pt].cmp(a.m_vertices[pt]);
+ if (diff) return diff;
+ }
+ if (m_path.size()<a.m_path.size()) return -1;
+ if (m_path.size()>a.m_path.size()) return -1;
+ for (size_t pt=0; pt < m_path.size(); ++pt)
+ {
+ diff = m_path[pt].cmp(a.m_path[pt]);
+ if (diff) return diff;
+ }
+ return 0;
+}
+
+SW602Box2f SW602GraphicShape::getBdBox(SW602GraphicStyle const &style, bool moveToO) const
+{
+ SW602Box2f bdBox=m_bdBox;
+ if (moveToO)
+ bdBox=SW602Box2f(SW602Vec2f(0,0),m_bdBox.size());
+ if (style.hasLine())
+ bdBox.extend(style.m_lineWidth/2.f);
+ if (m_type==Line)
+ {
+ // fixme: add 4pt for each arrows
+ int numArrows=(style.m_arrows[0].isEmpty() ? 0 : 1)+(style.m_arrows[1].isEmpty() ? 0 : 1);
+ if (numArrows) bdBox.extend(float(2*numArrows));
+ }
+ return bdBox;
+}
+
+void SW602GraphicShape::translate(SW602Vec2f const &decal)
+{
+ if (decal==SW602Vec2f(0,0))
+ return;
+ m_bdBox=SW602Box2f(m_bdBox.min()+decal, m_bdBox.max()+decal);
+ m_formBox=SW602Box2f(m_formBox.min()+decal, m_formBox.max()+decal);
+ for (size_t pt=0; pt<m_vertices.size(); ++pt)
+ m_vertices[pt]+=decal;
+ for (size_t pt=0; pt<m_path.size(); ++pt)
+ m_path[pt].translate(decal);
+}
+
+void SW602GraphicShape::scale(SW602Vec2f const &scaling)
+{
+ // checkme: does not work for symetry if shape is an arc...
+ m_bdBox=SW602Box2f(SW602Vec2f(scaling[0]*m_bdBox.min()[0],scaling[1]*m_bdBox.min()[1]),
+ SW602Vec2f(scaling[0]*m_bdBox.max()[0],scaling[1]*m_bdBox.max()[1]));
+ m_formBox=SW602Box2f(SW602Vec2f(scaling[0]*m_formBox.min()[0],scaling[1]*m_formBox.min()[1]),
+ SW602Vec2f(scaling[0]*m_formBox.max()[0],scaling[1]*m_formBox.max()[1]));
+ for (size_t pt=0; pt<m_vertices.size(); ++pt)
+ m_vertices[pt]=SW602Vec2f(scaling[0]*m_vertices[pt][0],
+ scaling[1]*m_vertices[pt][1]);
+ for (size_t pt=0; pt<m_path.size(); ++pt)
+ m_path[pt].scale(scaling);
+}
+
+SW602GraphicShape SW602GraphicShape::rotate(float angle, SW602Vec2f const &center) const
+{
+ while (angle >= 360) angle -= 360;
+ while (angle <= -360) angle += 360;
+ if (angle >= -1e-3 && angle <= 1e-3) return *this;
+ float angl=angle*float(M_PI/180.);
+ SW602Vec2f decal=center-SW602Vec2f(std::cos(angl)*center[0]-std::sin(angl)*center[1],
+ std::sin(angl)*center[0]+std::cos(angl)*center[1]);
+ SW602Box2f fBox;
+ for (int i=0; i < 4; ++i)
+ {
+ SW602Vec2f pt=SW602Vec2f(m_bdBox[i%2][0],m_bdBox[i/2][1]);
+ pt = SW602Vec2f(std::cos(angl)*pt[0]-std::sin(angl)*pt[1],
+ std::sin(angl)*pt[0]+std::cos(angl)*pt[1])+decal;
+ if (i==0) fBox=SW602Box2f(pt,pt);
+ else fBox=fBox.getUnion(SW602Box2f(pt,pt));
+ }
+ SW602GraphicShape res = path(fBox);
+ res.m_path=getPath();
+ for (size_t p=0; p < res.m_path.size(); p++)
+ res.m_path[p].rotate(angle, decal);
+ return res;
+}
+
+bool SW602GraphicShape::addPathTo(SW602Vec2f const &orig, librevenge::RVNGPropertyListVector &vect) const
+{
+ SW602Vec2f decal=orig-m_bdBox[0];
+ std::vector<SW602GraphicShape::PathData> fPath=getPath();
+ size_t n=fPath.size();
+ if (!n)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicShape::addPathTo: can not find the path\n"));
+ return false;
+ }
+ librevenge::RVNGPropertyList list;
+ for (size_t c=0; c < n; ++c)
+ {
+ list.clear();
+ if (fPath[c].get(list, -1.0f*decal))
+ vect.append(list);
+ }
+ if (fPath[n-1].m_type != 'Z')
+ {
+ // odg need a closed path to draw surface, so ...
+ list.clear();
+ list.insert("librevenge:path-action", "Z");
+ vect.append(list);
+ }
+ return true;
+}
+
+SW602GraphicShape::Command SW602GraphicShape::addTo(SW602Vec2f const &orig, bool asSurface, librevenge::RVNGPropertyList &propList) const
+{
+ SW602Vec2f pt;
+ librevenge::RVNGPropertyList list;
+ librevenge::RVNGPropertyListVector vect;
+ SW602Vec2f decal=orig-m_bdBox[0];
+ switch (m_type)
+ {
+ case Line:
+ case Measure:
+ if (m_vertices.size()!=2) break;
+ if (m_type==Measure)
+ propList.insert("draw:show-unit", true);
+ pt=m_vertices[0]+decal;
+ list.insert("svg:x",pt.x(), librevenge::RVNG_POINT);
+ list.insert("svg:y",pt.y(), librevenge::RVNG_POINT);
+ vect.append(list);
+ pt=m_vertices[1]+decal;
+ list.insert("svg:x",pt.x(), librevenge::RVNG_POINT);
+ list.insert("svg:y",pt.y(), librevenge::RVNG_POINT);
+ vect.append(list);
+ propList.insert("svg:points", vect);
+ return C_Polyline;
+ case Rectangle:
+ if (m_cornerWidth[0] > 0 && m_cornerWidth[1] > 0)
+ {
+ propList.insert("svg:rx",double(m_cornerWidth[0]), librevenge::RVNG_POINT);
+ propList.insert("svg:ry",double(m_cornerWidth[1]), librevenge::RVNG_POINT);
+ }
+ pt=m_formBox[0]+decal;
+ propList.insert("svg:x",pt.x(), librevenge::RVNG_POINT);
+ propList.insert("svg:y",pt.y(), librevenge::RVNG_POINT);
+ pt=m_formBox.size();
+ propList.insert("svg:width",pt.x(), librevenge::RVNG_POINT);
+ propList.insert("svg:height",pt.y(), librevenge::RVNG_POINT);
+ return C_Rectangle;
+ case Circle:
+ pt=0.5*(m_formBox[0]+m_formBox[1])+decal;
+ propList.insert("svg:cx",pt.x(), librevenge::RVNG_POINT);
+ propList.insert("svg:cy",pt.y(), librevenge::RVNG_POINT);
+ pt=0.5*(m_formBox[1]-m_formBox[0]);
+ propList.insert("svg:rx",pt.x(), librevenge::RVNG_POINT);
+ propList.insert("svg:ry",pt.y(), librevenge::RVNG_POINT);
+ return C_Ellipse;
+ case Arc:
+ case Pie:
+ {
+ SW602Vec2f center=0.5*(m_formBox[0]+m_formBox[1])+decal;
+ SW602Vec2f rad=0.5*(m_formBox[1]-m_formBox[0]);
+ float angl0=m_arcAngles[0];
+ float angl1=m_arcAngles[1];
+ if (rad[1]<0)
+ {
+ static bool first=true;
+ if (first)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicShape::addTo: oops radiusY for arc is negative, inverse it\n"));
+ first=false;
+ }
+ rad[1]=-rad[1];
+ }
+ while (angl1<angl0)
+ angl1+=360.f;
+ while (angl1>angl0+360.f)
+ angl1-=360.f;
+ if (angl1-angl0>=180.f && angl1-angl0<=180.f)
+ angl1+=0.01f;
+ float angl=angl0*float(M_PI/180.);
+ bool addCenter=m_type==Pie && asSurface;
+ if (addCenter)
+ {
+ pt=center;
+ list.insert("librevenge:path-action", "M");
+ list.insert("svg:x",pt.x(), librevenge::RVNG_POINT);
+ list.insert("svg:y",pt.y(), librevenge::RVNG_POINT);
+ vect.append(list);
+ }
+ list.clear();
+ pt=center+SW602Vec2f(std::cos(angl)*rad[0],-std::sin(angl)*rad[1]);
+ list.insert("librevenge:path-action", addCenter ? "L" : "M");
+ list.insert("svg:x",pt.x(), librevenge::RVNG_POINT);
+ list.insert("svg:y",pt.y(), librevenge::RVNG_POINT);
+ vect.append(list);
+
+ list.clear();
+ angl=angl1*float(M_PI/180.);
+ pt=center+SW602Vec2f(std::cos(angl)*rad[0],-std::sin(angl)*rad[1]);
+ list.insert("librevenge:path-action", "A");
+ list.insert("librevenge:large-arc", !(angl1-angl0<180.f));
+ list.insert("librevenge:sweep", false);
+ list.insert("svg:rx",rad.x(), librevenge::RVNG_POINT);
+ list.insert("svg:ry",rad.y(), librevenge::RVNG_POINT);
+ list.insert("svg:x",pt.x(), librevenge::RVNG_POINT);
+ list.insert("svg:y",pt.y(), librevenge::RVNG_POINT);
+ vect.append(list);
+ if (asSurface)
+ {
+ list.clear();
+ list.insert("librevenge:path-action", "Z");
+ vect.append(list);
+ }
+
+ propList.insert("svg:d", vect);
+ return C_Path;
+ }
+ case Polygon:
+ {
+ size_t n=m_vertices.size();
+ if (n<2) break;
+ for (size_t i = 0; i < n; ++i)
+ {
+ list.clear();
+ pt=m_vertices[i]+decal;
+ list.insert("svg:x", pt.x(), librevenge::RVNG_POINT);
+ list.insert("svg:y", pt.y(), librevenge::RVNG_POINT);
+ vect.append(list);
+ }
+ propList.insert("svg:points", vect);
+ return asSurface ? C_Polygon : C_Polyline;
+ }
+ case Path:
+ {
+ size_t n=m_path.size();
+ if (!n) break;
+ for (size_t c=0; c < n; ++c)
+ {
+ list.clear();
+ if (m_path[c].get(list, -1.0f*decal))
+ vect.append(list);
+ }
+ if (asSurface && m_path[n-1].m_type != 'Z')
+ {
+ // odg need a closed path to draw surface, so ...
+ list.clear();
+ list.insert("librevenge:path-action", "Z");
+ vect.append(list);
+ }
+ propList.insert("svg:d", vect);
+ return C_Path;
+ }
+ case ShapeUnknown:
+ default:
+ break;
+ }
+ SW602_DEBUG_MSG(("SW602GraphicShape::addTo: can not send a shape with type=%d\n", int(m_type)));
+ return C_Bad;
+}
+
+std::vector<SW602GraphicShape::PathData> SW602GraphicShape::getPath() const
+{
+ std::vector<SW602GraphicShape::PathData> res;
+ switch (m_type)
+ {
+ case Measure:
+ SW602_DEBUG_MSG(("SW602GraphicShape::getPath: called on a measure, transform it in line\n"));
+ // fall through expected
+ case Line:
+ case Polygon:
+ {
+ size_t n=m_vertices.size();
+ if (n<2) break;
+ res.push_back(PathData('M',m_vertices[0]));
+ for (size_t i = 1; i < n; ++i)
+ res.push_back(PathData('L', m_vertices[i]));
+ break;
+ }
+ case Rectangle:
+ if (m_cornerWidth[0] > 0 && m_cornerWidth[1] > 0)
+ {
+ SW602Box2f box=m_formBox;
+ SW602Vec2f c=m_cornerWidth;
+ res.push_back(PathData('M',SW602Vec2f(box[1][0]-c[0],box[0][1])));
+ PathData data('A',SW602Vec2f(box[1][0],box[0][1]+c[1]));
+ data.m_r=c;
+ data.m_sweep=true;
+ res.push_back(data);
+ res.push_back(PathData('L',SW602Vec2f(box[1][0],box[1][1]-c[1])));
+ data.m_x=SW602Vec2f(box[1][0]-c[0],box[1][1]);
+ res.push_back(data);
+ res.push_back(PathData('L',SW602Vec2f(box[0][0]+c[0],box[1][1])));
+ data.m_x=SW602Vec2f(box[0][0],box[1][1]-c[1]);
+ res.push_back(data);
+ res.push_back(PathData('L',SW602Vec2f(box[0][0],box[0][1]+c[1])));
+ data.m_x=SW602Vec2f(box[0][0]+c[0],box[0][1]);
+ res.push_back(data);
+ res.push_back(PathData('Z'));
+ break;
+ }
+ res.push_back(PathData('M',m_formBox[0]));
+ res.push_back(PathData('L',SW602Vec2f(m_formBox[0][0],m_formBox[1][1])));
+ res.push_back(PathData('L',m_formBox[1]));
+ res.push_back(PathData('L',SW602Vec2f(m_formBox[1][0],m_formBox[0][1])));
+ res.push_back(PathData('Z'));
+ break;
+ case Circle:
+ {
+ SW602Vec2f pt0 = SW602Vec2f(m_formBox[0][0],0.5f*(m_formBox[0][1]+m_formBox[1][1]));
+ SW602Vec2f pt1 = SW602Vec2f(m_formBox[1][0],pt0[1]);
+ res.push_back(PathData('M',pt0));
+ PathData data('A',pt1);
+ data.m_r=0.5*(m_formBox[1]-m_formBox[0]);
+ data.m_largeAngle=true;
+ res.push_back(data);
+ data.m_x=pt0;
+ res.push_back(data);
+ break;
+ }
+ case Arc:
+ case Pie:
+ {
+ SW602Vec2f center=0.5*(m_formBox[0]+m_formBox[1]);
+ SW602Vec2f rad=0.5*(m_formBox[1]-m_formBox[0]);
+ float angl0=m_arcAngles[0];
+ float angl1=m_arcAngles[1];
+ if (rad[1]<0)
+ {
+ static bool first=true;
+ if (first)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicShape::getPath: oops radiusY for arc is negative, inverse it\n"));
+ first=false;
+ }
+ rad[1]=-rad[1];
+ }
+ while (angl1<angl0)
+ angl1+=360.f;
+ while (angl1>angl0+360.f)
+ angl1-=360.f;
+ if (angl1-angl0>=180.f && angl1-angl0<=180.f)
+ angl1+=0.01f;
+ float angl=angl0*float(M_PI/180.);
+ bool addCenter=m_type==Pie;
+ if (addCenter)
+ res.push_back(PathData('M', center));
+ SW602Vec2f pt=center+SW602Vec2f(std::cos(angl)*rad[0],-std::sin(angl)*rad[1]);
+ res.push_back(PathData(addCenter ? 'L' : 'M', pt));
+ angl=angl1*float(M_PI/180.);
+ pt=center+SW602Vec2f(std::cos(angl)*rad[0],-std::sin(angl)*rad[1]);
+ PathData data('A',pt);
+ data.m_largeAngle=(angl1-angl0>=180.f);
+ data.m_r=rad;
+ res.push_back(data);
+ break;
+ }
+ case Path:
+ return m_path;
+ case ShapeUnknown:
+ default:
+ SW602_DEBUG_MSG(("SW602GraphicShape::getPath: unexpected type\n"));
+ break;
+ }
+ return res;
+}
+
+}
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602GraphicShape.h b/src/lib/SW602GraphicShape.h
new file mode 100644
index 0000000..edfbbaf
--- /dev/null
+++ b/src/lib/SW602GraphicShape.h
@@ -0,0 +1,190 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+#ifndef INCLUDED_SW602_GRAPHIC_SHAPE_H
+#define INCLUDED_SW602_GRAPHIC_SHAPE_H
+
+#include <ostream>
+#include <string>
+#include <vector>
+
+#include "librevenge/librevenge.h"
+
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+class SW602GraphicStyle;
+
+/** a structure used to define a picture shape */
+class SW602GraphicShape
+{
+public:
+ //! an enum used to define the shape type
+ enum Type { Arc, Circle, Line, Measure, Rectangle, Path, Pie, Polygon, ShapeUnknown };
+ //! an enum used to define the interface command
+ enum Command { C_Ellipse, C_Polyline, C_Rectangle, C_Path, C_Polygon, C_Bad };
+ //! a simple path component
+ struct PathData
+ {
+ //! constructor
+ PathData(char type, SW602Vec2f const &x=SW602Vec2f(), SW602Vec2f const &x1=SW602Vec2f(), SW602Vec2f const &x2=SW602Vec2f()):
+ m_type(type), m_x(x), m_x1(x1), m_x2(x2), m_r(), m_rotate(0), m_largeAngle(false), m_sweep(false)
+ {
+ }
+ //! translate all the coordinate by delta
+ void translate(SW602Vec2f const &delta);
+ //! scale all the coordinate by a factor
+ void scale(SW602Vec2f const &factor);
+ //! rotate all the coordinate by angle (origin rotation) then translate coordinate
+ void rotate(float angle, SW602Vec2f const &delta);
+ //! update the property list to correspond to a command
+ bool get(librevenge::RVNGPropertyList &pList, SW602Vec2f const &orig) const;
+ //! a print operator
+ friend std::ostream &operator<<(std::ostream &o, PathData const &path);
+ //! comparison function
+ int cmp(PathData const &a) const;
+ //! the type: M, L, ...
+ char m_type;
+ //! the main x value
+ SW602Vec2f m_x;
+ //! x1 value
+ SW602Vec2f m_x1;
+ //! x2 value
+ SW602Vec2f m_x2;
+ //! the radius ( A command)
+ SW602Vec2f m_r;
+ //! the rotate ( A command)
+ float m_rotate;
+ //! large angle ( A command)
+ bool m_largeAngle;
+ //! sweep value ( A command)
+ bool m_sweep;
+ };
+
+ //! constructor
+ SW602GraphicShape() : m_type(ShapeUnknown), m_bdBox(), m_formBox(), m_cornerWidth(0,0), m_arcAngles(0,0),
+ m_vertices(), m_path(), m_extra("")
+ {
+ }
+ //! virtual destructor
+ virtual ~SW602GraphicShape() { }
+ //! static constructor to create a line
+ static SW602GraphicShape line(SW602Vec2f const &orign, SW602Vec2f const &dest);
+ //! static constructor to create a measure
+ static SW602GraphicShape measure(SW602Vec2f const &orign, SW602Vec2f const &dest);
+ //! static constructor to create a rectangle
+ static SW602GraphicShape rectangle(SW602Box2f const &box, SW602Vec2f const &corners=SW602Vec2f(0,0))
+ {
+ SW602GraphicShape res;
+ res.m_type=Rectangle;
+ res.m_bdBox=res.m_formBox=box;
+ res.m_cornerWidth=corners;
+ return res;
+ }
+ //! static constructor to create a circle
+ static SW602GraphicShape circle(SW602Box2f const &box)
+ {
+ SW602GraphicShape res;
+ res.m_type=Circle;
+ res.m_bdBox=res.m_formBox=box;
+ return res;
+ }
+ //! static constructor to create a arc
+ static SW602GraphicShape arc(SW602Box2f const &box, SW602Box2f const &circleBox, SW602Vec2f const &angles)
+ {
+ SW602GraphicShape res;
+ res.m_type=Arc;
+ res.m_bdBox=box;
+ res.m_formBox=circleBox;
+ res.m_arcAngles=angles;
+ return res;
+ }
+ //! static constructor to create a pie
+ static SW602GraphicShape pie(SW602Box2f const &box, SW602Box2f const &circleBox, SW602Vec2f const &angles)
+ {
+ SW602GraphicShape res;
+ res.m_type=Pie;
+ res.m_bdBox=box;
+ res.m_formBox=circleBox;
+ res.m_arcAngles=angles;
+ return res;
+ }
+ //! static constructor to create a polygon
+ static SW602GraphicShape polygon(SW602Box2f const &box)
+ {
+ SW602GraphicShape res;
+ res.m_type=Polygon;
+ res.m_bdBox=box;
+ return res;
+ }
+ //! static constructor to create a path
+ static SW602GraphicShape path(SW602Box2f const &box)
+ {
+ SW602GraphicShape res;
+ res.m_type=Path;
+ res.m_bdBox=box;
+ return res;
+ }
+
+ //! translate all the coordinate by delta
+ void translate(SW602Vec2f const &delta);
+ //! rescale all the coordinate
+ void scale(SW602Vec2f const &factor);
+ /** return a new shape corresponding to a rotation from center.
+
+ \note the final bdbox is not tight */
+ SW602GraphicShape rotate(float angle, SW602Vec2f const &center) const;
+ //! returns the type corresponding to a shape
+ Type getType() const
+ {
+ return m_type;
+ }
+ //! returns the basic bdbox
+ SW602Box2f getBdBox() const
+ {
+ return m_bdBox;
+ }
+ //! returns the bdbox corresponding to a style
+ SW602Box2f getBdBox(SW602GraphicStyle const &style, bool moveToO=false) const;
+ //! updates the propList to send to an interface
+ Command addTo(SW602Vec2f const &orig, bool asSurface, librevenge::RVNGPropertyList &propList) const;
+ //! adds the shape path to a propListVector
+ bool addPathTo(SW602Vec2f const &orig, librevenge::RVNGPropertyListVector &propList) const;
+ //! a print operator
+ friend std::ostream &operator<<(std::ostream &o, SW602GraphicShape const &sh);
+ /** compare two shapes */
+ int cmp(SW602GraphicShape const &a) const;
+protected:
+ //! return a path corresponding to the shape
+ std::vector<PathData> getPath() const;
+public:
+ //! the type
+ Type m_type;
+ //! the shape bdbox
+ SW602Box2f m_bdBox;
+ //! the internal shape bdbox ( used for arc, circle to store the circle bdbox )
+ SW602Box2f m_formBox;
+ //! the rectangle round corner
+ SW602Vec2f m_cornerWidth;
+ //! the start and end value which defines an arc
+ SW602Vec2f m_arcAngles;
+ //! the list of vertices for lines or polygons
+ std::vector<SW602Vec2f> m_vertices;
+ //! the list of path component
+ std::vector<PathData> m_path;
+ //! extra data
+ std::string m_extra;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602GraphicStyle.cpp b/src/lib/SW602GraphicStyle.cpp
new file mode 100644
index 0000000..9925459
--- /dev/null
+++ b/src/lib/SW602GraphicStyle.cpp
@@ -0,0 +1,652 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/* This header contains code specific to a pict mac file
+ */
+
+#include "SW602GraphicStyle.h"
+
+#include <string.h>
+
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <librevenge/librevenge.h>
+
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+////////////////////////////////////////////////////////////
+// arrow
+////////////////////////////////////////////////////////////
+void SW602GraphicStyle::Arrow::addTo(librevenge::RVNGPropertyList &propList, std::string const &type) const
+{
+ if (isEmpty())
+ return;
+ if (type!="start" && type!="end")
+ {
+ SW602_DEBUG_MSG(("SW602GraphicStyle::Arrow::addTo: oops, find unexpected type\n"));
+ return;
+ }
+ std::stringstream s, s2;
+ s << "draw:marker-" << type << "-path";
+ propList.insert(s.str().c_str(), m_path.c_str());
+ s.str("");
+ s << "draw:marker-" << type << "-viewbox";
+ s2 << m_viewBox[0][0] << " " << m_viewBox[0][1] << " " << m_viewBox[1][0] << " " << m_viewBox[1][1];
+ propList.insert(s.str().c_str(), s2.str().c_str());
+ s.str("");
+ s << "draw:marker-" << type << "-center";
+ propList.insert(s.str().c_str(), m_isCentered);
+ s.str("");
+ s << "draw:marker-" << type << "-width";
+ propList.insert(s.str().c_str(), m_width, librevenge::RVNG_POINT);
+}
+
+////////////////////////////////////////////////////////////
+// pattern
+////////////////////////////////////////////////////////////
+bool SW602GraphicStyle::Pattern::getUniqueColor(SW602Color &col) const
+{
+ if (empty() || !m_picture.isEmpty() || m_data.empty()) return false;
+ if (m_colors[0]==m_colors[1])
+ {
+ col = m_colors[0];
+ return true;
+ }
+ unsigned char def=m_data[0];
+ if (def!=0 && def!=0xFF) return false;
+ for (size_t c=1; c < m_data.size(); ++c)
+ if (m_data[c]!=def) return false;
+ col = m_colors[def ? 1 : 0];
+ return true;
+}
+
+bool SW602GraphicStyle::Pattern::getAverageColor(SW602Color &color) const
+{
+ if (empty()) return false;
+ if (!m_picture.isEmpty())
+ {
+ color=m_pictureAverageColor;
+ return true;
+ }
+ if (m_data.empty()) return false;
+ if (m_colors[0]==m_colors[1])
+ {
+ color = m_colors[0];
+ return true;
+ }
+ int numOne=0, numZero=0;
+ for (size_t c=0; c < m_data.size(); ++c)
+ {
+ for (int depl=1, b=0; b < 8; ++b, depl*=2)
+ {
+ if (m_data[c] & depl)
+ numOne++;
+ else
+ numZero++;
+ }
+ }
+ if (!numOne && !numZero) return false;
+ float percent=float(numOne)/float(numOne+numZero);
+ color = SW602Color::barycenter(1.f-percent,m_colors[0],percent,m_colors[1]);
+ return true;
+}
+
+bool SW602GraphicStyle::Pattern::getBinary(SW602EmbeddedObject &picture) const
+{
+ if (empty())
+ {
+ SW602_DEBUG_MSG(("SW602GraphicStyle::Pattern::getBinary: called on invalid pattern\n"));
+ return false;
+ }
+ if (!m_picture.isEmpty())
+ {
+ picture=m_picture;
+ return true;
+ }
+ /* We create a indexed bitmap to obtain a final binary data.
+
+ But it will probably better to recode that differently
+ */
+#if 0
+ SW602PictBitmapIndexed bitmap(m_dim);
+ std::vector<SW602Color> colors;
+ for (int i=0; i < 2; ++i)
+ colors.push_back(m_colors[i]);
+ bitmap.setColors(colors);
+ int numBytesByLines = m_dim[0]/8;
+ unsigned char const *ptr = &m_data[0];
+ std::vector<int> rowValues((size_t)m_dim[0]);
+ for (int h=0; h < m_dim[1]; ++h)
+ {
+ size_t i=0;
+ for (int b=0; b < numBytesByLines; ++b)
+ {
+ unsigned char c=*(ptr++);
+ unsigned char depl=0x80;
+ for (int byt=0; byt<8; ++byt)
+ {
+ rowValues[i++] = (c&depl) ? 1 : 0;
+ depl=(unsigned char)(depl>>1);
+ }
+ }
+ bitmap.setRow(h, &rowValues[0]);
+ }
+ return bitmap.getBinary(picture);
+#endif
+ return false;
+}
+
+////////////////////////////////////////////////////////////
+// style
+////////////////////////////////////////////////////////////
+void SW602GraphicStyle::setBorders(int wh, SW602Border const &border)
+{
+ int const allBits = libsw602::LeftBit|libsw602::RightBit|libsw602::TopBit|libsw602::BottomBit;
+ if (wh & (~allBits))
+ {
+ SW602_DEBUG_MSG(("SW602GraphicStyle::setBorders: unknown borders\n"));
+ return;
+ }
+ size_t numData = 4;
+ if (m_bordersList.size() < numData)
+ {
+ SW602Border emptyBorder;
+ emptyBorder.m_style = SW602Border::None;
+ m_bordersList.resize(numData, emptyBorder);
+ }
+ if (wh & libsw602::LeftBit) m_bordersList[libsw602::Left] = border;
+ if (wh & libsw602::RightBit) m_bordersList[libsw602::Right] = border;
+ if (wh & libsw602::TopBit) m_bordersList[libsw602::Top] = border;
+ if (wh & libsw602::BottomBit) m_bordersList[libsw602::Bottom] = border;
+}
+
+void SW602GraphicStyle::addTo(librevenge::RVNGPropertyList &list, bool only1D) const
+{
+ if (!hasLine())
+ list.insert("draw:stroke", "none");
+ else if (m_lineDashWidth.size()>=2)
+ {
+ int nDots1=0, nDots2=0;
+ float size1=0, size2=0, totalGap=0.0;
+ for (size_t c=0; c+1 < m_lineDashWidth.size();)
+ {
+ float sz=m_lineDashWidth[c++];
+ if (nDots2 && (sz<size2||sz>size2))
+ {
+ static bool first=true;
+ if (first)
+ {
+ SW602_DEBUG_MSG(("SW602GraphicStyle::addTo: can set some dash\n"));
+ first = false;
+ }
+ break;
+ }
+ if (nDots2)
+ nDots2++;
+ else if (!nDots1 || (sz>=size1 && sz <= size1))
+ {
+ nDots1++;
+ size1=sz;
+ }
+ else
+ {
+ nDots2=1;
+ size2=sz;
+ }
+ totalGap += m_lineDashWidth[c++];
+ }
+ list.insert("draw:stroke", "dash");
+ list.insert("draw:dots1", nDots1);
+ list.insert("draw:dots1-length", size1, librevenge::RVNG_POINT);
+ if (nDots2)
+ {
+ list.insert("draw:dots2", nDots2);
+ list.insert("draw:dots2-length", size2, librevenge::RVNG_POINT);
+ }
+ const double distance = ((nDots1 + nDots2) > 0) ? totalGap/float(nDots1+nDots2) : totalGap;
+ list.insert("draw:distance", distance, librevenge::RVNG_POINT);;
+ }
+ else
+ list.insert("draw:stroke", "solid");
+ list.insert("svg:stroke-color", m_lineColor.str().c_str());
+ list.insert("svg:stroke-width", m_lineWidth,librevenge::RVNG_POINT);
+
+ if (m_lineOpacity < 1)
+ list.insert("svg:stroke-opacity", m_lineOpacity, librevenge::RVNG_PERCENT);
+ switch (m_lineCap)
+ {
+ case C_Round:
+ list.insert("svg:stroke-linecap", "round");
+ break;
+ case C_Square:
+ list.insert("svg:stroke-linecap", "square");
+ break;
+ case C_Butt:
+ default:
+ break;
+ }
+ switch (m_lineJoin)
+ {
+ case J_Round:
+ list.insert("svg:stroke-linejoin", "round");
+ break;
+ case J_Bevel:
+ list.insert("svg:stroke-linejoin", "bevel");
+ break;
+ case J_Miter:
+ default:
+ break;
+ }
+ if (!m_arrows[0].isEmpty()) m_arrows[0].addTo(list,"start");
+ if (!m_arrows[1].isEmpty()) m_arrows[1].addTo(list,"end");
+ if (hasShadow())
+ {
+ list.insert("draw:shadow", "visible");
+ list.insert("draw:shadow-color", m_shadowColor.str().c_str());
+ list.insert("draw:shadow-opacity", m_shadowOpacity, librevenge::RVNG_PERCENT);
+ // in cm
+ list.insert("draw:shadow-offset-x", double(m_shadowOffset[0])/72.*2.54, librevenge::RVNG_GENERIC); // cm
+ list.insert("draw:shadow-offset-y", double(m_shadowOffset[1])/72.*2.54, librevenge::RVNG_GENERIC); // cm
+ }
+ if (only1D || !hasSurface())
+ {
+ list.insert("draw:fill", "none");
+ return;
+ }
+ list.insert("svg:fill-rule", m_fillRuleEvenOdd ? "evenodd" : "nonzero");
+ if (hasGradient())
+ {
+ list.insert("draw:fill", "gradient");
+ switch (m_gradientType)
+ {
+ case G_Axial:
+ list.insert("draw:style", "axial");
+ break;
+ case G_Radial:
+ list.insert("draw:style", "radial");
+ break;
+ case G_Rectangular:
+ list.insert("draw:style", "rectangular");
+ break;
+ case G_Square:
+ list.insert("draw:style", "square");
+ break;
+ case G_Ellipsoid:
+ list.insert("draw:style", "ellipsoid");
+ break;
+ case G_Linear:
+ case G_None:
+ default:
+ list.insert("draw:style", "linear");
+ break;
+ }
+ if (m_gradientStopList.size()==2 && m_gradientStopList[0].m_offset <= 0 &&
+ m_gradientStopList[1].m_offset >=1)
+ {
+ size_t first=(m_gradientType==G_Linear || m_gradientType==G_Axial) ? 0 : 1;
+ list.insert("draw:start-color", m_gradientStopList[first].m_color.str().c_str());
+ list.insert("librevenge:start-opacity", m_gradientStopList[first].m_opacity, librevenge::RVNG_PERCENT);
+ list.insert("draw:end-color", m_gradientStopList[1-first].m_color.str().c_str());
+ list.insert("librevenge:end-opacity", m_gradientStopList[1-first].m_opacity, librevenge::RVNG_PERCENT);
+ }
+ else
+ {
+ librevenge::RVNGPropertyListVector gradient;
+ for (size_t s=0; s < m_gradientStopList.size(); ++s)
+ {
+ librevenge::RVNGPropertyList grad;
+ grad.insert("svg:offset", m_gradientStopList[s].m_offset, librevenge::RVNG_PERCENT);
+ grad.insert("svg:stop-color", m_gradientStopList[s].m_color.str().c_str());
+ grad.insert("svg:stop-opacity", m_gradientStopList[s].m_opacity, librevenge::RVNG_PERCENT);
+ gradient.append(grad);
+ }
+ list.insert("svg:linearGradient", gradient);
+ }
+ list.insert("draw:angle", m_gradientAngle, librevenge::RVNG_GENERIC);
+ list.insert("draw:border", m_gradientBorder, librevenge::RVNG_PERCENT);
+ if (m_gradientType != G_Linear)
+ {
+ list.insert("svg:cx", m_gradientPercentCenter[0], librevenge::RVNG_PERCENT);
+ list.insert("svg:cy", m_gradientPercentCenter[1], librevenge::RVNG_PERCENT);
+ }
+ if (m_gradientType == G_Radial)
+ list.insert("svg:r", m_gradientRadius, librevenge::RVNG_PERCENT); // checkme
+ }
+ else
+ {
+ bool done = false;
+ SW602Color surfaceColor=m_surfaceColor;
+ float surfaceOpacity = m_surfaceOpacity;
+ if (hasPattern())
+ {
+ SW602Color col;
+ if (m_pattern.getUniqueColor(col))
+ {
+ // no need to create a uniform pattern
+ surfaceColor = col;
+ surfaceOpacity = 1;
+ }
+ else
+ {
+ SW602EmbeddedObject picture;
+ if (m_pattern.getBinary(picture) && !picture.m_dataList.empty() && !picture.m_dataList[0].empty())
+ {
+ list.insert("draw:fill", "bitmap");
+ list.insert("draw:fill-image", picture.m_dataList[0].getBase64Data());
+ list.insert("draw:fill-image-width", m_pattern.m_dim[0], librevenge::RVNG_POINT);
+ list.insert("draw:fill-image-height", m_pattern.m_dim[1], librevenge::RVNG_POINT);
+ list.insert("draw:fill-image-ref-point-x",0, librevenge::RVNG_POINT);
+ list.insert("draw:fill-image-ref-point-y",0, librevenge::RVNG_POINT);
+ if (surfaceOpacity<1)
+ list.insert("draw:opacity", surfaceOpacity, librevenge::RVNG_PERCENT);
+ list.insert("librevenge:mime-type", picture.m_typeList.empty() ? "image/pict" : picture.m_typeList[0].c_str());
+ done = true;
+ }
+ else
+ {
+ SW602_DEBUG_MSG(("SW602GraphicStyle::addTo: can not set the pattern\n"));
+ }
+ }
+ }
+ if (!done)
+ {
+ list.insert("draw:fill", "solid");
+ list.insert("draw:fill-color", surfaceColor.str().c_str());
+ list.insert("draw:opacity", surfaceOpacity, librevenge::RVNG_PERCENT);
+ }
+ }
+}
+
+void SW602GraphicStyle::addFrameTo(librevenge::RVNGPropertyList &list) const
+{
+ if (m_backgroundOpacity>=0)
+ {
+ if (m_backgroundOpacity>0)
+ list.insert("fo:background-color", m_backgroundColor.str().c_str());
+ if (m_backgroundOpacity<1)
+ list.insert("style:background-transparency", 1.-m_backgroundOpacity, librevenge::RVNG_PERCENT);
+ }
+ if (hasBorders())
+ {
+ if (hasSameBorders())
+ m_bordersList[0].addTo(list, "");
+ else
+ {
+ for (size_t c = 0; c < m_bordersList.size(); c++)
+ {
+ if (c >= 4) break;
+ switch (c)
+ {
+ case libsw602::Left:
+ m_bordersList[c].addTo(list, "left");
+ break;
+ case libsw602::Right:
+ m_bordersList[c].addTo(list, "right");
+ break;
+ case libsw602::Top:
+ m_bordersList[c].addTo(list, "top");
+ break;
+ case libsw602::Bottom:
+ m_bordersList[c].addTo(list, "bottom");
+ break;
+ default:
+ SW602_DEBUG_MSG(("SW602GraphicStyle::addFrameTo: can not send %d border\n",int(c)));
+ break;
+ }
+ }
+ }
+ }
+ if (hasShadow())
+ {
+ list.insert("draw:shadow", "visible");
+ list.insert("draw:shadow-color", m_shadowColor.str().c_str());
+ list.insert("draw:shadow-opacity", m_shadowOpacity, librevenge::RVNG_PERCENT);
+ // in cm
+ list.insert("draw:shadow-offset-x", double(m_shadowOffset[0])/72.*2.54, librevenge::RVNG_GENERIC); // cm
+ list.insert("draw:shadow-offset-y", double(m_shadowOffset[1])/72.*2.54, librevenge::RVNG_GENERIC); // cm
+ }
+ if (!m_frameName.empty())
+ list.insert("librevenge:frame-name",m_frameName.c_str());
+}
+
+int SW602GraphicStyle::cmp(SW602GraphicStyle const &a) const
+{
+ if (m_lineWidth < a.m_lineWidth) return -1;
+ if (m_lineWidth > a.m_lineWidth) return 1;
+ if (m_lineCap < a.m_lineCap) return -1;
+ if (m_lineCap > a.m_lineCap) return 1;
+ if (m_lineJoin < a.m_lineJoin) return -1;
+ if (m_lineJoin > a.m_lineJoin) return 1;
+ if (m_lineOpacity < a.m_lineOpacity) return -1;
+ if (m_lineOpacity > a.m_lineOpacity) return 1;
+ if (m_lineColor < a.m_lineColor) return -1;
+ if (m_lineColor > a.m_lineColor) return 1;
+
+ if (m_lineDashWidth.size() < a.m_lineDashWidth.size()) return -1;
+ if (m_lineDashWidth.size() > a.m_lineDashWidth.size()) return 1;
+ for (size_t d=0; d < m_lineDashWidth.size(); ++d)
+ {
+ if (m_lineDashWidth[d] > a.m_lineDashWidth[d]) return -1;
+ if (m_lineDashWidth[d] < a.m_lineDashWidth[d]) return 1;
+ }
+ for (int i=0; i<2; ++i)
+ {
+ if (m_arrows[i]!=a.m_arrows[i])
+ return m_arrows[i]<a.m_arrows[i] ? -1 : 1;
+ if (m_flip[i]!=a.m_flip[i])
+ return m_flip[i] ? 1 : -1;
+ }
+
+ if (m_fillRuleEvenOdd != a.m_fillRuleEvenOdd) return m_fillRuleEvenOdd ? 1: -1;
+
+ if (m_surfaceColor < a.m_surfaceColor) return -1;
+ if (m_surfaceColor > a.m_surfaceColor) return 1;
+ if (m_surfaceOpacity < a.m_surfaceOpacity) return -1;
+ if (m_surfaceOpacity > a.m_surfaceOpacity) return 1;
+
+ if (m_shadowColor < a.m_shadowColor) return -1;
+ if (m_shadowColor > a.m_shadowColor) return 1;
+ if (m_shadowOpacity < a.m_shadowOpacity) return -1;
+ if (m_shadowOpacity > a.m_shadowOpacity) return 1;
+ int diff=m_shadowOffset.cmp(a.m_shadowOffset);
+ if (diff) return diff;
+
+ diff = m_pattern.cmp(a.m_pattern);
+ if (diff) return diff;
+
+ if (m_gradientType < a.m_gradientType) return -1;
+ if (m_gradientType > a.m_gradientType) return 1;
+ if (m_gradientAngle < a.m_gradientAngle) return -1;
+ if (m_gradientAngle > a.m_gradientAngle) return 1;
+ if (m_gradientStopList.size() < a.m_gradientStopList.size()) return 1;
+ if (m_gradientStopList.size() > a.m_gradientStopList.size()) return -1;
+ for (size_t c=0; c < m_gradientStopList.size(); ++c)
+ {
+ diff = m_gradientStopList[c].cmp(m_gradientStopList[c]);
+ if (diff) return diff;
+ }
+ if (m_gradientBorder < a.m_gradientBorder) return -1;
+ if (m_gradientBorder > a.m_gradientBorder) return 1;
+ diff=m_gradientPercentCenter.cmp(a.m_gradientPercentCenter);
+ if (diff) return diff;
+
+ size_t numBorders=m_bordersList.size();
+ if (a.m_bordersList.size()>numBorders)
+ numBorders=a.m_bordersList.size();
+ for (size_t b=0; b<numBorders; ++b)
+ {
+ bool empty=b>=m_bordersList.size() || m_bordersList[b].isEmpty();
+ bool aEmpty=b>=a.m_bordersList.size() || a.m_bordersList[b].isEmpty();
+ if (empty!=aEmpty) return empty ? 1 : -1;
+ diff=m_bordersList[b].compare(a.m_bordersList[b]);
+ if (diff) return diff;
+ }
+ if (m_backgroundColor < a.m_backgroundColor) return -1;
+ if (m_backgroundColor > a.m_backgroundColor) return 1;
+ if (m_backgroundOpacity < a.m_backgroundOpacity) return -1;
+ if (m_backgroundOpacity > a.m_backgroundOpacity) return 1;
+ if (m_frameName < a.m_frameName) return -1;
+ if (m_frameName > a.m_frameName) return 1;
+ if (m_frameNextName < a.m_frameNextName) return -1;
+ if (m_frameNextName > a.m_frameNextName) return 1;
+
+ if (m_gradientRadius < a.m_gradientRadius) return -1;
+ if (m_gradientRadius > a.m_gradientRadius) return 1;
+ if (m_rotate < a.m_rotate) return -1;
+ if (m_rotate > a.m_rotate) return 1;
+ return 0;
+}
+
+std::ostream &operator<<(std::ostream &o, SW602GraphicStyle const &st)
+{
+ if (st.m_rotate<0 || st.m_rotate>0)
+ o << "rot=" << st.m_rotate << ",";
+ if (st.m_flip[0]) o << "flipX,";
+ if (st.m_flip[1]) o << "flipY,";
+ o << "line=[";
+ if (st.m_lineWidth<1 || st.m_lineWidth>1)
+ o << "width=" << st.m_lineWidth << ",";
+ if (!st.m_lineDashWidth.empty())
+ {
+ o << "dash=[";
+ for (size_t d=0; d < st.m_lineDashWidth.size(); ++d)
+ o << st.m_lineDashWidth[d] << ",";
+ o << "],";
+ }
+ switch (st.m_lineCap)
+ {
+ case SW602GraphicStyle::C_Square:
+ o << "cap=square,";
+ break;
+ case SW602GraphicStyle::C_Round:
+ o << "cap=round,";
+ break;
+ case SW602GraphicStyle::C_Butt:
+ default:
+ break;
+ }
+ switch (st.m_lineJoin)
+ {
+ case SW602GraphicStyle::J_Bevel:
+ o << "join=bevel,";
+ break;
+ case SW602GraphicStyle::J_Round:
+ o << "join=round,";
+ break;
+ case SW602GraphicStyle::J_Miter:
+ default:
+ break;
+ }
+ if (st.m_lineOpacity<1)
+ o << "opacity=" << st.m_lineOpacity << ",";
+ if (!st.m_lineColor.isBlack())
+ o << "color=" << st.m_lineColor << ",";
+ if (!st.m_arrows[0].isEmpty()) o << "arrow[start]=[" << st.m_arrows[0] << "],";
+ if (!st.m_arrows[1].isEmpty()) o << "arrow[end]=[" << st.m_arrows[1] << "],";
+ o << "],";
+ if (st.hasSurfaceColor())
+ {
+ o << "surf=[";
+ if (!st.m_surfaceColor.isWhite())
+ o << "color=" << st.m_surfaceColor << ",";
+ if (st.m_surfaceOpacity > 0)
+ o << "opacity=" << st.m_surfaceOpacity << ",";
+ o << "],";
+ if (st.m_fillRuleEvenOdd)
+ o << "fill[evenOdd],";
+ }
+ if (st.hasPattern())
+ o << "pattern=[" << st.m_pattern << "],";
+ if (st.hasGradient())
+ {
+ o << "grad=[";
+ switch (st.m_gradientType)
+ {
+ case SW602GraphicStyle::G_Axial:
+ o << "axial,";
+ break;
+ case SW602GraphicStyle::G_Linear:
+ o << "linear,";
+ break;
+ case SW602GraphicStyle::G_Radial:
+ o << "radial,";
+ break;
+ case SW602GraphicStyle::G_Rectangular:
+ o << "rectangular,";
+ break;
+ case SW602GraphicStyle::G_Square:
+ o << "square,";
+ break;
+ case SW602GraphicStyle::G_Ellipsoid:
+ o << "ellipsoid,";
+ break;
+ case SW602GraphicStyle::G_None:
+ default:
+ break;
+ }
+ if (st.m_gradientAngle>0 || st.m_gradientAngle<0) o << "angle=" << st.m_gradientAngle << ",";
+ if (st.m_gradientStopList.size() >= 2)
+ {
+ o << "stops=[";
+ for (size_t s=0; s < st.m_gradientStopList.size(); ++s)
+ o << "[" << st.m_gradientStopList[s] << "],";
+ o << "],";
+ }
+ if (st.m_gradientBorder>0) o << "border=" << st.m_gradientBorder*100 << "%,";
+ if (st.m_gradientPercentCenter != SW602Vec2f(0.5f,0.5f)) o << "center=" << st.m_gradientPercentCenter << ",";
+ if (st.m_gradientRadius<1) o << "radius=" << st.m_gradientRadius << ",";
+ o << "],";
+ }
+ if (st.hasShadow())
+ {
+ o << "shadow=[";
+ if (!st.m_shadowColor.isBlack())
+ o << "color=" << st.m_shadowColor << ",";
+ if (st.m_shadowOpacity > 0)
+ o << "opacity=" << st.m_shadowOpacity << ",";
+ o << "offset=" << st.m_shadowOffset << ",";
+ o << "],";
+ }
+ if (st.hasBorders())
+ {
+ for (size_t i = 0; i < st.m_bordersList.size(); i++)
+ {
+ if (st.m_bordersList[i].m_style == SW602Border::None)
+ continue;
+ o << "bord";
+ if (i < 4)
+ {
+ static char const *wh[] = { "L", "R", "T", "B"};
+ o << wh[i];
+ }
+ else o << "[#wh=" << i << "]";
+ o << "=" << st.m_bordersList[i] << ",";
+ }
+ }
+ if (!st.m_backgroundColor.isWhite())
+ o << "background[color]=" << st.m_backgroundColor << ",";
+ if (st.m_backgroundOpacity>=0)
+ o << "background[opacity]=" << 100.f *st.m_backgroundOpacity << "%,";
+ if (!st.m_frameName.empty())
+ o << "frame[name]=" << st.m_frameName << ",";
+ if (!st.m_frameNextName.empty())
+ o << "frame[linkedto]=" << st.m_frameNextName << ",";
+ o << st.m_extra;
+ return o;
+}
+
+}
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602GraphicStyle.h b/src/lib/SW602GraphicStyle.h
new file mode 100644
index 0000000..43282c8
--- /dev/null
+++ b/src/lib/SW602GraphicStyle.h
@@ -0,0 +1,439 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+#ifndef INCLUDED_SW602_GRAPHIC_STYLE_H
+#define INCLUDED_SW602_GRAPHIC_STYLE_H
+
+#include <ostream>
+#include <string>
+#include <vector>
+
+#include "librevenge/librevenge.h"
+
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+/** a structure used to define a picture style
+
+ \note in order to define the internal surface style, first it looks for
+ a gradient, if so it uses it. Then it looks for a pattern. Finally if
+ it found nothing, it uses surfaceColor and surfaceOpacity.*/
+class SW602GraphicStyle
+{
+public:
+ //! an enum used to define the basic line cap
+ enum LineCap { C_Butt, C_Square, C_Round };
+ //! an enum used to define the basic line join
+ enum LineJoin { J_Miter, J_Round, J_Bevel };
+ //! an enum used to define the gradient type
+ enum GradientType { G_None, G_Axial, G_Linear, G_Radial, G_Rectangular, G_Square, G_Ellipsoid };
+
+ //! a structure used to define an arrow
+ struct Arrow
+ {
+ //! constructor ( no arrow)
+ Arrow() : m_width(0), m_viewBox(), m_path(""), m_isCentered(false)
+ {
+ }
+ //! constructor
+ Arrow(float w, SW602Box2i const &box, std::string path, bool centered=false) :
+ m_width(w), m_viewBox(box), m_path(path), m_isCentered(centered)
+ {
+ }
+ //! returns a basic plain arrow
+ static Arrow plain()
+ {
+ return Arrow(5, SW602Box2i(SW602Vec2i(0,0),SW602Vec2i(20,30)), "m10 0-10 30h20z", false);
+ }
+ //! operator<<
+ friend std::ostream &operator<<(std::ostream &o, Arrow const &arrow)
+ {
+ if (arrow.isEmpty()) return o;
+ o << "w=" << arrow.m_width << ",";
+ o << "viewbox=" << arrow.m_viewBox << ",";
+ o << "path=" << arrow.m_path << ",";
+ if (arrow.m_isCentered) o << "centered,";
+ return o;
+ }
+ //! operator==
+ bool operator==(Arrow const &arrow) const
+ {
+ return m_width>=arrow.m_width && m_width<=arrow.m_width &&
+ m_viewBox==arrow.m_viewBox && m_path==arrow.m_path && m_isCentered==arrow.m_isCentered;
+ }
+ //! operator!=
+ bool operator!=(Arrow const &arrow) const
+ {
+ return !(*this==arrow);
+ }
+ //! operator<
+ bool operator<(Arrow const &arrow) const
+ {
+ if (m_isCentered<arrow.m_isCentered) return m_isCentered ? true : false;
+ return m_width<arrow.m_width && m_viewBox<arrow.m_viewBox && m_path < arrow.m_path;
+ }
+ //! operator<=
+ bool operator<=(Arrow const &arrow) const
+ {
+ return *this<arrow || *this==arrow;
+ }
+ //! operator>
+ bool operator>(Arrow const &arrow) const
+ {
+ return !(*this<=arrow);
+ }
+ //! operator>=
+ bool operator>=(Arrow const &arrow) const
+ {
+ return !(*this<arrow);
+ }
+ //! returns true if there is no arrow
+ bool isEmpty() const
+ {
+ return m_width<=0 || m_path.empty();
+ }
+ //! add a arrow to the propList knowing the type (start, end)
+ void addTo(librevenge::RVNGPropertyList &propList, std::string const &type) const;
+
+ //! the arrow width in point
+ float m_width;
+ //! the arrow viewbox
+ SW602Box2i m_viewBox;
+ //! the arrow path
+ std::string m_path;
+ //! flag to know if the arrow is centered
+ bool m_isCentered;
+ };
+
+ //! a structure used to define the gradient limit in SW602GraphicStyle
+ struct GradientStop
+ {
+ //! constructor
+ GradientStop(float offset=0.0, SW602Color const &col=SW602Color::black(), float opacity=1.0) :
+ m_offset(offset), m_color(col), m_opacity(opacity)
+ {
+ }
+ /** compare two gradient */
+ int cmp(GradientStop const &a) const
+ {
+ if (m_offset < a.m_offset) return -1;
+ if (m_offset > a.m_offset) return 1;
+ if (m_color < a.m_color) return -1;
+ if (m_color > a.m_color) return 1;
+ if (m_opacity < a.m_opacity) return -1;
+ if (m_opacity > a.m_opacity) return 1;
+ return 0;
+ }
+ //! a print operator
+ friend std::ostream &operator<<(std::ostream &o, GradientStop const &st)
+ {
+ o << "offset=" << st.m_offset << ",";
+ o << "color=" << st.m_color << ",";
+ if (st.m_opacity<1.0)
+ o << "opacity=" << st.m_opacity*100.f << "%,";
+ return o;
+ }
+ //! the offset
+ float m_offset;
+ //! the color
+ SW602Color m_color;
+ //! the opacity
+ float m_opacity;
+ };
+ /** a basic pattern used in a SW602GraphicStyle:
+ - either given a list of 8x8, 16x16, 32x32 bytes with two colors
+ - or with a picture ( and an average color)
+ */
+ struct Pattern
+ {
+ //! constructor
+ Pattern() : m_dim(0,0), m_data(), m_picture(), m_pictureAverageColor(SW602Color::white())
+ {
+ m_colors[0]=SW602Color::black();
+ m_colors[1]=SW602Color::white();
+ }
+ //! constructor from a binary data
+ Pattern(SW602Vec2i dim, SW602EmbeddedObject const &picture, SW602Color const &avColor) :
+ m_dim(dim), m_data(), m_picture(picture), m_pictureAverageColor(avColor)
+ {
+ m_colors[0]=SW602Color::black();
+ m_colors[1]=SW602Color::white();
+ }
+ //! virtual destructor
+ virtual ~Pattern() {}
+ //! return true if we does not have a pattern
+ bool empty() const
+ {
+ if (m_dim[0]==0 || m_dim[1]==0) return true;
+ if (!m_picture.m_dataList.empty()) return false;
+ if (m_dim[0]!=8 && m_dim[0]!=16 && m_dim[0]!=32) return true;
+ return m_data.size()!=size_t((m_dim[0]/8)*m_dim[1]);
+ }
+ //! return the average color
+ bool getAverageColor(SW602Color &col) const;
+ //! check if the pattern has only one color; if so returns true...
+ bool getUniqueColor(SW602Color &col) const;
+ /** tries to convert the picture in a binary data ( ppm) */
+ bool getBinary(SW602EmbeddedObject &picture) const;
+
+ /** compare two patterns */
+ int cmp(Pattern const &a) const
+ {
+ int diff = m_dim.cmp(a.m_dim);
+ if (diff) return diff;
+ if (m_data.size() < a.m_data.size()) return -1;
+ if (m_data.size() > a.m_data.size()) return 1;
+ for (size_t h=0; h < m_data.size(); ++h)
+ {
+ if (m_data[h]<a.m_data[h]) return 1;
+ if (m_data[h]>a.m_data[h]) return -1;
+ }
+ for (int i=0; i<2; ++i)
+ {
+ if (m_colors[i] < a.m_colors[i]) return 1;
+ if (m_colors[i] > a.m_colors[i]) return -1;
+ }
+ if (m_pictureAverageColor < a.m_pictureAverageColor) return 1;
+ if (m_pictureAverageColor > a.m_pictureAverageColor) return -1;
+ diff=m_picture.cmp(a.m_picture);
+ if (diff) return diff;
+ return 0;
+ }
+ //! a print operator
+ friend std::ostream &operator<<(std::ostream &o, Pattern const &pat)
+ {
+ o << "dim=" << pat.m_dim << ",";
+ if (!pat.m_picture.isEmpty())
+ {
+ o << "pict=" << pat.m_picture << ",";
+ o << "col[average]=" << pat.m_pictureAverageColor << ",";
+ }
+ else
+ {
+ if (!pat.m_colors[0].isBlack()) o << "col0=" << pat.m_colors[0] << ",";
+ if (!pat.m_colors[1].isWhite()) o << "col1=" << pat.m_colors[1] << ",";
+ o << "[";
+ for (size_t h=0; h < pat.m_data.size(); ++h)
+ o << std::hex << (int) pat.m_data[h] << std::dec << ",";
+ o << "],";
+ }
+ return o;
+ }
+ //! the dimension width x height
+ SW602Vec2i m_dim;
+
+ //! the two indexed colors
+ SW602Color m_colors[2];
+ //! the pattern data: a sequence of data: p[0..7,0],p[8..15,0]...p[0..7,1],p[8..15,1], ...
+ std::vector<unsigned char> m_data;
+ protected:
+ //! a picture
+ SW602EmbeddedObject m_picture;
+ //! the picture average color
+ SW602Color m_pictureAverageColor;
+ };
+ //! constructor
+ SW602GraphicStyle() : m_lineWidth(1), m_lineDashWidth(), m_lineCap(C_Butt), m_lineJoin(J_Miter), m_lineOpacity(1), m_lineColor(SW602Color::black()),
+ m_fillRuleEvenOdd(false), m_surfaceColor(SW602Color::white()), m_surfaceOpacity(0),
+ m_shadowColor(SW602Color::black()), m_shadowOpacity(0), m_shadowOffset(1,1),
+ m_pattern(),
+ m_gradientType(G_None), m_gradientStopList(), m_gradientAngle(0), m_gradientBorder(0), m_gradientPercentCenter(0.5f,0.5f), m_gradientRadius(1),
+ m_backgroundColor(SW602Color::white()), m_backgroundOpacity(-1), m_bordersList(), m_frameName(""), m_frameNextName(""),
+ m_rotate(0), m_extra("")
+ {
+ m_arrows[0]=m_arrows[1]=Arrow();
+ m_flip[0]=m_flip[1]=false;
+ m_gradientStopList.push_back(GradientStop(0.0, SW602Color::white()));
+ m_gradientStopList.push_back(GradientStop(1.0, SW602Color::black()));
+ }
+ /** returns an empty style. Can be used to initialize a default frame style...*/
+ static SW602GraphicStyle emptyStyle()
+ {
+ SW602GraphicStyle res;
+ res.m_lineWidth=0;
+ return res;
+ }
+ //! virtual destructor
+ virtual ~SW602GraphicStyle() { }
+ //! returns true if the border is defined
+ bool hasLine() const
+ {
+ return m_lineWidth>0 && m_lineOpacity>0;
+ }
+ //! set the surface color
+ void setSurfaceColor(SW602Color const &col, float opacity = 1)
+ {
+ m_surfaceColor = col;
+ m_surfaceOpacity = opacity;
+ }
+ //! returns true if the surface is defined
+ bool hasSurfaceColor() const
+ {
+ return m_surfaceOpacity > 0;
+ }
+ //! set the pattern
+ void setPattern(Pattern const &pat, float opacity = 1)
+ {
+ m_pattern=pat;
+ m_surfaceOpacity = opacity;
+ }
+ //! returns true if the pattern is defined
+ bool hasPattern() const
+ {
+ return !m_pattern.empty() && m_surfaceOpacity > 0;
+ }
+ //! returns true if the gradient is defined
+ bool hasGradient(bool complex=false) const
+ {
+ return m_gradientType != G_None && (int) m_gradientStopList.size() >= (complex ? 3 : 2);
+ }
+ //! returns true if the interior surface is defined
+ bool hasSurface() const
+ {
+ return hasSurfaceColor() || hasPattern() || hasGradient();
+ }
+ //! set the background color
+ void setBackgroundColor(SW602Color const &col, float opacity = 1)
+ {
+ m_backgroundColor = col;
+ m_backgroundOpacity = opacity;
+ }
+ //! returns true if the background is defined
+ bool hasBackgroundColor() const
+ {
+ return m_backgroundOpacity > 0;
+ }
+ //! set the shadow color
+ void setShadowColor(SW602Color const &col, float opacity = 1)
+ {
+ m_shadowColor = col;
+ m_shadowOpacity = opacity;
+ }
+ //! returns true if the shadow is defined
+ bool hasShadow() const
+ {
+ return m_shadowOpacity > 0;
+ }
+ //! return true if the frame has some border
+ bool hasBorders() const
+ {
+ return !m_bordersList.empty();
+ }
+ //! return true if the frame has some border
+ bool hasSameBorders() const
+ {
+ if (m_bordersList.empty()) return true;
+ if (m_bordersList.size()!=4) return false;
+ for (size_t i=1; i<m_bordersList.size(); ++i)
+ {
+ if (m_bordersList[i]!=m_bordersList[0])
+ return false;
+ }
+ return true;
+ }
+ //! return the frame border: libsw602::Left | ...
+ std::vector<SW602Border> const &borders() const
+ {
+ return m_bordersList;
+ }
+ //! reset the border
+ void resetBorders()
+ {
+ m_bordersList.resize(0);
+ }
+ //! sets the cell border: wh=libsw602::LeftBit|...
+ void setBorders(int wh, SW602Border const &border);
+ //! a print operator
+ friend std::ostream &operator<<(std::ostream &o, SW602GraphicStyle const &st);
+ //! add all the parameters to the propList excepted the frame parameter: the background and the borders
+ void addTo(librevenge::RVNGPropertyList &pList, bool only1d=false) const;
+ //! add all the frame parameters to propList: the background and the borders
+ void addFrameTo(librevenge::RVNGPropertyList &pList) const;
+ /** compare two styles */
+ int cmp(SW602GraphicStyle const &a) const;
+
+ //! the linewidth
+ float m_lineWidth;
+ //! the dash array: a sequence of (fullsize, emptysize)
+ std::vector<float> m_lineDashWidth;
+ //! the line cap
+ LineCap m_lineCap;
+ //! the line join
+ LineJoin m_lineJoin;
+ //! the line opacity: 0=transparent
+ float m_lineOpacity;
+ //! the line color
+ SW602Color m_lineColor;
+ //! true if the fill rule is evenod
+ bool m_fillRuleEvenOdd;
+ //! the surface color
+ SW602Color m_surfaceColor;
+ //! true if the surface has some color
+ float m_surfaceOpacity;
+
+ //! the shadow color
+ SW602Color m_shadowColor;
+ //! true if the shadow has some color
+ float m_shadowOpacity;
+ //! the shadow offset
+ SW602Vec2f m_shadowOffset;
+
+ //! the pattern if it exists
+ Pattern m_pattern;
+
+ //! the gradient type
+ GradientType m_gradientType;
+ //! the list of gradient limits
+ std::vector<GradientStop> m_gradientStopList;
+ //! the gradient angle
+ float m_gradientAngle;
+ //! the gradient border opacity
+ float m_gradientBorder;
+ //! the gradient center
+ SW602Vec2f m_gradientPercentCenter;
+ //! the gradient radius
+ float m_gradientRadius;
+
+ //! the two arrows corresponding to start and end extremity
+ Arrow m_arrows[2];
+
+ //
+ // related to the frame
+ //
+
+ //! the background color
+ SW602Color m_backgroundColor;
+ //! true if the background has some color
+ float m_backgroundOpacity;
+ //! the borders SW602Border::Pos (for a frame)
+ std::vector<SW602Border> m_bordersList;
+ //! the frame name
+ std::string m_frameName;
+ //! the frame next name (if there is a link)
+ std::string m_frameNextName;
+
+ //
+ // some transformation: must probably be somewhere else
+ //
+
+ //! the rotation
+ float m_rotate;
+ //! two bool to indicated we need to flip the shape or not
+ bool m_flip[2];
+
+ //! extra data
+ std::string m_extra;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602List.cpp b/src/lib/SW602List.cpp
new file mode 100644
index 0000000..10e0a66
--- /dev/null
+++ b/src/lib/SW602List.cpp
@@ -0,0 +1,415 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "SW602List.h"
+
+#include <cstring>
+#include <iostream>
+
+#include <librevenge/librevenge.h>
+
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+////////////////////////////////////////////////////////////
+// list level functions
+////////////////////////////////////////////////////////////
+void SW602ListLevel::addTo(librevenge::RVNGPropertyList &propList) const
+{
+ propList.insert("text:min-label-width", m_labelWidth, librevenge::RVNG_INCH);
+ propList.insert("text:space-before", m_labelBeforeSpace, librevenge::RVNG_INCH);
+ if (m_labelAfterSpace > 0)
+ propList.insert("text:min-label-distance", m_labelAfterSpace, librevenge::RVNG_INCH);
+ if (m_numBeforeLabels)
+ propList.insert("text:display-levels", m_numBeforeLabels+1);
+ switch (m_alignment)
+ {
+ case LEFT:
+ break;
+ case CENTER:
+ propList.insert("fo:text-align", "center");
+ break;
+ case RIGHT:
+ propList.insert("fo:text-align", "end");
+ break;
+ default:
+ break;
+ }
+
+ switch (m_type)
+ {
+ case NONE:
+ propList.insert("text:bullet-char", " ");
+ break;
+ case BULLET:
+ if (m_bullet.len())
+ propList.insert("text:bullet-char", m_bullet.cstr());
+ else
+ {
+ SW602_DEBUG_MSG(("SW602ListLevel::addTo: the bullet char is not defined\n"));
+ propList.insert("text:bullet-char", "*");
+ }
+ break;
+ case LABEL:
+ if (m_label.len())
+ propList.insert("style:num-suffix", librevenge::RVNGPropertyFactory::newStringProp(m_label));
+ propList.insert("style:num-format", "");
+ break;
+ case DECIMAL:
+ case LOWER_ALPHA:
+ case UPPER_ALPHA:
+ case LOWER_ROMAN:
+ case UPPER_ROMAN:
+ if (m_prefix.len())
+ propList.insert("style:num-prefix",librevenge::RVNGPropertyFactory::newStringProp(m_prefix));
+ if (m_suffix.len())
+ propList.insert("style:num-suffix", librevenge::RVNGPropertyFactory::newStringProp(m_suffix));
+ if (m_type==DECIMAL) propList.insert("style:num-format", "1");
+ else if (m_type==LOWER_ALPHA) propList.insert("style:num-format", "a");
+ else if (m_type==UPPER_ALPHA) propList.insert("style:num-format", "A");
+ else if (m_type==LOWER_ROMAN) propList.insert("style:num-format", "i");
+ else propList.insert("style:num-format", "I");
+ propList.insert("text:start-value", getStartValue());
+ break;
+ case DEFAULT:
+ default:
+ SW602_DEBUG_MSG(("SW602ListLevel::addTo: the level type is not set\n"));
+ }
+}
+
+int SW602ListLevel::cmp(SW602ListLevel const &levl) const
+{
+ int diff = int(m_type)-int(levl.m_type);
+ if (diff) return diff;
+ double fDiff = m_labelBeforeSpace-levl.m_labelBeforeSpace;
+ if (fDiff < 0.0) return -1;
+ if (fDiff > 0.0) return 1;
+ fDiff = m_labelWidth-levl.m_labelWidth;
+ if (fDiff < 0.0) return -1;
+ if (fDiff > 0.0) return 1;
+ diff = int(m_alignment)-levl.m_alignment;
+ if (diff) return diff;
+ fDiff = m_labelAfterSpace-levl.m_labelAfterSpace;
+ if (fDiff < 0.0) return -1;
+ if (fDiff > 0.0) return 1;
+ diff = m_numBeforeLabels-levl.m_numBeforeLabels;
+ if (diff) return diff;
+ diff = strcmp(m_label.cstr(),levl.m_label.cstr());
+ if (diff) return diff;
+ diff = strcmp(m_prefix.cstr(),levl.m_prefix.cstr());
+ if (diff) return diff;
+ diff = strcmp(m_suffix.cstr(),levl.m_suffix.cstr());
+ if (diff) return diff;
+ diff = strcmp(m_bullet.cstr(),levl.m_bullet.cstr());
+ if (diff) return diff;
+ return 0;
+}
+
+std::ostream &operator<<(std::ostream &o, SW602ListLevel const &level)
+{
+ o << "ListLevel[";
+ switch (level.m_type)
+ {
+ case SW602ListLevel::BULLET:
+ o << "bullet='" << level.m_bullet.cstr() <<"'";
+ break;
+ case SW602ListLevel::LABEL:
+ o << "text='" << level.m_label.cstr() << "'";
+ break;
+ case SW602ListLevel::DECIMAL:
+ o << "decimal";
+ break;
+ case SW602ListLevel::LOWER_ALPHA:
+ o << "alpha";
+ break;
+ case SW602ListLevel::UPPER_ALPHA:
+ o << "ALPHA";
+ break;
+ case SW602ListLevel::LOWER_ROMAN:
+ o << "roman";
+ break;
+ case SW602ListLevel::UPPER_ROMAN:
+ o << "ROMAN";
+ break;
+ case SW602ListLevel::NONE:
+ break;
+ case SW602ListLevel::DEFAULT:
+ default:
+ o << "####type";
+ }
+ switch (level.m_alignment)
+ {
+ case SW602ListLevel::LEFT:
+ break;
+ case SW602ListLevel::CENTER:
+ o << ",center";
+ break;
+ case SW602ListLevel::RIGHT:
+ o << ",right";
+ break;
+ default:
+ o << "###align=" << int(level.m_alignment) << ",";
+ }
+ if (level.m_type != SW602ListLevel::BULLET && level.m_startValue)
+ o << ",startVal= " << level.m_startValue;
+ if (level.m_prefix.len()) o << ", prefix='" << level.m_prefix.cstr()<<"'";
+ if (level.m_suffix.len()) o << ", suffix='" << level.m_suffix.cstr()<<"'";
+ if (level.m_labelBeforeSpace < 0 || level.m_labelBeforeSpace > 0) o << ", indent=" << level.m_labelBeforeSpace;
+ if (level.m_labelWidth < 0 || level.m_labelWidth > 0) o << ", width=" << level.m_labelWidth;
+ if (level.m_labelAfterSpace > 0) o << ", labelTextW=" << level.m_labelAfterSpace;
+ if (level.m_numBeforeLabels) o << ", show=" << level.m_numBeforeLabels << "[before]";
+ o << "]";
+ if (level.m_extra.length()) o << ", " << level.m_extra;
+ return o;
+}
+
+////////////////////////////////////////////////////////////
+// list functions
+////////////////////////////////////////////////////////////
+void SW602List::resize(int level)
+{
+ if (level < 0)
+ {
+ SW602_DEBUG_MSG(("SW602List::resize: level %d can not be negatif\n",level));
+ return;
+ }
+ if (level == int(m_levels.size()))
+ return;
+ m_levels.resize(size_t(level));
+ m_actualIndices.resize(size_t(level), 0);
+ m_nextIndices.resize(size_t(level), 1);
+ if (m_actLevel >= level)
+ m_actLevel=level-1;
+ m_modifyMarker++;
+}
+
+void SW602List::updateIndicesFrom(SW602List const &list)
+{
+ size_t maxLevel=list.m_levels.size();
+ if (maxLevel>m_levels.size())
+ maxLevel=m_levels.size();
+ for (size_t level=0 ; level < maxLevel; level++)
+ {
+ m_actualIndices[size_t(level)]=m_levels[size_t(level)].getStartValue()-1;
+ m_nextIndices[level]=list.m_nextIndices[level];
+ }
+ m_modifyMarker++;
+}
+
+bool SW602List::isCompatibleWith(int levl, SW602ListLevel const &level) const
+{
+ if (levl < 1)
+ {
+ SW602_DEBUG_MSG(("SW602List::isCompatibleWith: level %d must be positive\n",levl));
+ return false;
+ }
+
+ return levl > int(m_levels.size()) ||
+ level.cmp(m_levels[size_t(levl-1)])==0;
+}
+
+bool SW602List::isCompatibleWith(SW602List const &newList) const
+{
+ size_t maxLevel=newList.m_levels.size();
+ if (maxLevel>m_levels.size())
+ maxLevel=m_levels.size();
+ for (size_t level=0 ; level < maxLevel; level++)
+ {
+ if (m_levels[level].cmp(newList.m_levels[level])!=0)
+ return false;
+ }
+ return true;
+}
+
+void SW602List::setId(int newId) const
+{
+ m_id[0] = newId;
+ m_id[1] = newId+1;
+}
+
+bool SW602List::addTo(int level, librevenge::RVNGPropertyList &pList) const
+{
+ if (level <= 0 || level > int(m_levels.size()) ||
+ m_levels[size_t(level-1)].isDefault())
+ {
+ SW602_DEBUG_MSG(("SW602List::addTo: level %d is not defined\n",level));
+ return false;
+ }
+
+ if (getId()==-1)
+ {
+ SW602_DEBUG_MSG(("SW602List::addTo: the list id is not set\n"));
+ static int falseId = 1000;
+ setId(falseId+=2);
+ }
+ pList.insert("librevenge:list-id", getId());
+ pList.insert("librevenge:level", level);
+ m_levels[size_t(level-1)].addTo(pList);
+ return true;
+}
+
+void SW602List::set(int levl, SW602ListLevel const &level)
+{
+ if (levl < 1)
+ {
+ SW602_DEBUG_MSG(("SW602List::set: can not set level %d\n",levl));
+ return;
+ }
+ if (levl > int(m_levels.size())) resize(levl);
+ int needReplace = m_levels[size_t(levl-1)].cmp(level) != 0 ||
+ (level.m_startValue && m_nextIndices[size_t(levl-1)] !=level.getStartValue());
+ if (level.m_startValue > 0 || level.m_type != m_levels[size_t(levl-1)].m_type)
+ {
+ m_nextIndices[size_t(levl-1)]=level.getStartValue();
+ m_modifyMarker++;
+ }
+ if (needReplace)
+ {
+ m_levels[size_t(levl-1)] = level;
+ m_modifyMarker++;
+ }
+}
+
+void SW602List::setLevel(int levl) const
+{
+ if (levl < 1 || levl > int(m_levels.size()))
+ {
+ SW602_DEBUG_MSG(("SW602List::setLevel: can not set level %d\n",levl));
+ return;
+ }
+
+ if (levl < int(m_levels.size()))
+ m_actualIndices[size_t(levl)]=
+ (m_nextIndices[size_t(levl)]=m_levels[size_t(levl)].getStartValue())-1;
+
+ m_actLevel=levl-1;
+}
+
+void SW602List::setStartValueForNextElement(int value)
+{
+ if (m_actLevel < 0 || m_actLevel >= int(m_levels.size()))
+ {
+ SW602_DEBUG_MSG(("SW602List::setStartValueForNextElement: can not find level %d\n",m_actLevel));
+ return;
+ }
+ if (m_nextIndices[size_t(m_actLevel)]==value)
+ return;
+ m_nextIndices[size_t(m_actLevel)]=value;
+ m_modifyMarker++;
+}
+
+int SW602List::getStartValueForNextElement() const
+{
+ if (m_actLevel < 0 || m_actLevel >= int(m_levels.size()))
+ {
+ SW602_DEBUG_MSG(("SW602List::getStartValueForNextElement: can not find level %d\n",m_actLevel));
+ return -1;
+ }
+ if (!m_levels[size_t(m_actLevel)].isNumeric())
+ return -1;
+ return m_nextIndices[size_t(m_actLevel)];
+}
+
+void SW602List::openElement() const
+{
+ if (m_actLevel < 0 || m_actLevel >= int(m_levels.size()))
+ {
+ SW602_DEBUG_MSG(("SW602List::openElement: can not set level %d\n",m_actLevel));
+ return;
+ }
+ if (m_levels[size_t(m_actLevel)].isNumeric())
+ m_actualIndices[size_t(m_actLevel)]=m_nextIndices[size_t(m_actLevel)]++;
+}
+
+bool SW602List::isNumeric(int levl) const
+{
+ if (levl < 1 || levl > int(m_levels.size()))
+ {
+ SW602_DEBUG_MSG(("SW602List::isNumeric: the level does not exist\n"));
+ return false;
+ }
+
+ return m_levels[size_t(levl-1)].isNumeric();
+}
+
+////////////////////////////////////////////////////////////
+// list manager function
+////////////////////////////////////////////////////////////
+bool SW602ListManager::needToSend(int index, std::vector<int> &idMarkerList) const
+{
+ if (index <= 0) return false;
+ if (index >= int(idMarkerList.size()))
+ idMarkerList.resize(size_t(index)+1,0);
+ size_t mainId=size_t(index-1)/2;
+ if (mainId >= m_listList.size())
+ {
+ SW602_DEBUG_MSG(("SW602ListManager::needToSend: can not find list %d\n", index));
+ return false;
+ }
+ SW602List const &list=m_listList[mainId];
+ if (idMarkerList[size_t(index)] == list.getMarker())
+ return false;
+ idMarkerList[size_t(index)]=list.getMarker();
+ if (list.getId()!=index) list.swapId();
+ return true;
+}
+
+boost::shared_ptr<SW602List> SW602ListManager::getList(int index) const
+{
+ boost::shared_ptr<SW602List> res;
+ if (index <= 0) return res;
+ size_t mainId=size_t(index-1)/2;
+ if (mainId < m_listList.size())
+ {
+ res.reset(new SW602List(m_listList[mainId]));
+ if (res->getId()!=index)
+ res->swapId();
+ }
+ return res;
+}
+
+boost::shared_ptr<SW602List> SW602ListManager::getNewList(boost::shared_ptr<SW602List> actList, int levl, SW602ListLevel const &level)
+{
+ if (actList && actList->getId()>=0 && actList->isCompatibleWith(levl, level))
+ {
+ actList->set(levl, level);
+ int index=actList->getId();
+ size_t mainId=size_t(index-1)/2;
+ if (mainId < m_listList.size() && m_listList[mainId].numLevels() < levl)
+ m_listList[mainId].set(levl, level);
+ return actList;
+ }
+ SW602List res;
+ if (actList)
+ {
+ res = *actList;
+ res.resize(levl);
+ }
+ size_t numList=m_listList.size();
+ res.setId(int(2*numList+1));
+ res.set(levl, level);
+ for (size_t l=0; l < numList; l++)
+ {
+ if (!m_listList[l].isCompatibleWith(res))
+ continue;
+ if (m_listList[l].numLevels() < levl)
+ m_listList[l].set(levl, level);
+ boost::shared_ptr<SW602List> copy(new SW602List(m_listList[l]));
+ copy->updateIndicesFrom(res);
+ return copy;
+ }
+ m_listList.push_back(res);
+ return boost::shared_ptr<SW602List>(new SW602List(res));
+}
+
+}
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602List.h b/src/lib/SW602List.h
new file mode 100644
index 0000000..e636f6f
--- /dev/null
+++ b/src/lib/SW602List.h
@@ -0,0 +1,202 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SW602_LIST_H
+#define INCLUDED_SW602_LIST_H
+
+#include <iostream>
+#include <vector>
+
+#include <boost/shared_ptr.hpp>
+
+#include <librevenge/librevenge.h>
+
+#include "libsw602_utils.h"
+
+namespace libsw602
+{
+
+/** small structure to keep information about a list level */
+struct SW602ListLevel
+{
+ /** the type of the level */
+ enum Type { DEFAULT, NONE, BULLET, DECIMAL, LOWER_ALPHA, UPPER_ALPHA,
+ LOWER_ROMAN, UPPER_ROMAN, LABEL
+ };
+ //! the item alignment
+ enum Alignment { LEFT, RIGHT, CENTER };
+
+ /** basic constructor */
+ SW602ListLevel() : m_type(NONE), m_labelBeforeSpace(0.0), m_labelWidth(0.1), m_labelAfterSpace(0.0), m_numBeforeLabels(0), m_alignment(LEFT), m_startValue(0),
+ m_label(""), m_prefix(""), m_suffix(""), m_bullet(""), m_extra("")
+ {
+ }
+ /** destructor */
+ ~SW602ListLevel() {}
+
+ /** returns true if the level type was not set */
+ bool isDefault() const
+ {
+ return m_type ==DEFAULT;
+ }
+ /** returns true if the list is decimal, alpha or roman */
+ bool isNumeric() const
+ {
+ return m_type !=DEFAULT && m_type !=NONE && m_type != BULLET;
+ }
+ /** add the information of this level in the propList */
+ void addTo(librevenge::RVNGPropertyList &propList) const;
+
+ /** returns the start value (if set) or 1 */
+ int getStartValue() const
+ {
+ return m_startValue <= 0 ? 1 : m_startValue;
+ }
+
+ /** comparison function ( compare all values excepted m_startValues */
+ int cmp(SW602ListLevel const &levl) const;
+
+ //! operator<<
+ friend std::ostream &operator<<(std::ostream &o, SW602ListLevel const &ft);
+
+ /** the type of the level */
+ Type m_type;
+ double m_labelBeforeSpace /** the extra space between inserting a label */;
+ double m_labelWidth /** the minimum label width */;
+ double m_labelAfterSpace /** the minimum distance between the label and the text */;
+ /** the number of label to show before this */
+ int m_numBeforeLabels;
+ //! the alignment ( left, center, ...)
+ Alignment m_alignment;
+ /** the actual value (if this is an ordered level ) */
+ int m_startValue;
+ librevenge::RVNGString m_label /** the text label */,
+ m_prefix /** string which preceedes the number if we have an ordered level*/,
+ m_suffix/** string which follows the number if we have an ordered level*/,
+ m_bullet /** the bullet if we have an bullet level */;
+ //! extra data
+ std::string m_extra;
+};
+
+/** a small structure used to store the informations about a list */
+class SW602List
+{
+public:
+ /** default constructor */
+ SW602List() : m_levels(), m_actLevel(-1), m_actualIndices(), m_nextIndices(), m_modifyMarker(1)
+ {
+ m_id[0] = m_id[1] = -1;
+ }
+
+ /** returns the list id */
+ int getId() const
+ {
+ return m_id[0];
+ }
+
+ /** returns the actual modify marker */
+ int getMarker() const
+ {
+ return m_modifyMarker;
+ }
+ /** resize the number of level of the list (keeping only n level) */
+ void resize(int levl);
+ /** returns true if we can add a new level in the list without changing is meaning */
+ bool isCompatibleWith(int levl, SW602ListLevel const &level) const;
+ /** returns true if the list is compatible with the defined level of new list */
+ bool isCompatibleWith(SW602List const &newList) const;
+ /** update the indices, the actual level from newList */
+ void updateIndicesFrom(SW602List const &list);
+
+ /** swap the list id
+
+ \note a cheat because writerperfect imposes to get a new id if the level 1 changes
+ */
+ void swapId() const
+ {
+ int tmp = m_id[0];
+ m_id[0] = m_id[1];
+ m_id[1] = tmp;
+ }
+
+ /** set the list id */
+ void setId(int newId) const;
+
+ /** returns a level if it exists */
+ SW602ListLevel getLevel(int levl) const
+ {
+ if (levl >= 0 && levl < int(m_levels.size()))
+ return m_levels[size_t(levl)];
+ SW602_DEBUG_MSG(("SW602List::getLevel: can not find level %d\n", levl));
+ return SW602ListLevel();
+ }
+ /** returns the number of level */
+ int numLevels() const
+ {
+ return int(m_levels.size());
+ }
+ /** sets a level */
+ void set(int levl, SW602ListLevel const &level);
+
+ /** set the list level */
+ void setLevel(int levl) const;
+ /** open the list element */
+ void openElement() const;
+ /** close the list element */
+ void closeElement() const {}
+ /** returns the startvalue corresponding to the actual level ( or -1 for an unknown/unordered list) */
+ int getStartValueForNextElement() const;
+ /** set the startvalue corresponding to the actual level*/
+ void setStartValueForNextElement(int value);
+
+ /** returns true is a level is numeric */
+ bool isNumeric(int levl) const;
+
+ /// retrieve the list level property
+ bool addTo(int level, librevenge::RVNGPropertyList &pList) const;
+
+protected:
+ //! the different levels
+ std::vector<SW602ListLevel> m_levels;
+
+ //! the actual levels
+ mutable int m_actLevel;
+ mutable std::vector<int> m_actualIndices, m_nextIndices;
+ //! the identificator ( actual and auxilliar )
+ mutable int m_id[2];
+ //! a modification marker ( can be used to check if a list has been send to a interface )
+ mutable int m_modifyMarker;
+};
+
+/** a manager which manages the lists, keeps the different kind of lists, to assure the unicity of each list */
+class SW602ListManager
+{
+public:
+ //! the constructor
+ SW602ListManager() : m_listList(), m_sendIdMarkerList() { }
+ //! the destructor
+ ~SW602ListManager() { }
+ /** check if a list need to be send/resend to the interface */
+ bool needToSend(int index, std::vector<int> &idMarkerList) const;
+ //! returns a list with given index ( if found )
+ boost::shared_ptr<SW602List> getList(int index) const;
+ //! returns a new list corresponding to a list where we have a new level
+ boost::shared_ptr<SW602List> getNewList(boost::shared_ptr<SW602List> actList, int levl, SW602ListLevel const &level);
+protected:
+ //! the list of created list
+ std::vector<SW602List> m_listList;
+ //! the list of send list to interface
+ mutable std::vector<int> m_sendIdMarkerList;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602Listener.h b/src/lib/SW602Listener.h
new file mode 100644
index 0000000..083e2bd
--- /dev/null
+++ b/src/lib/SW602Listener.h
@@ -0,0 +1,201 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SW602_LISTENER_H
+#define INCLUDED_SW602_LISTENER_H
+
+#include <vector>
+
+#include <librevenge/librevenge.h>
+#include <librevenge-stream/librevenge-stream.h>
+
+#include "SW602GraphicStyle.h"
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+class SW602Cell;
+class SW602Font;
+class SW602GraphicShape;
+class SW602PageSpan;
+class SW602Paragraph;
+class SW602Position;
+class SW602Section;
+class SW602Table;
+
+/** This class contains a virtual interface to all listener */
+class SW602Listener
+{
+public:
+ //! destructor
+ virtual ~SW602Listener() {}
+
+ //! the listener type
+ enum Type { Graphic, Presentation, Spreadsheet, Text };
+ /** the different break type */
+ enum BreakType { PageBreak=0, SoftPageBreak, ColumnBreak };
+
+ //------- generic accessor ---
+ /** returns the listener type */
+ virtual Type getType() const = 0;
+ /** returns true if we can add text data */
+ virtual bool canWriteText() const =0;
+
+ // ------ main document -------
+ /** sets the documents language */
+ virtual void setDocumentLanguage(std::string locale) = 0;
+ /** starts the document */
+ virtual void startDocument() = 0;
+ /** returns true if a document is opened */
+ virtual bool isDocumentStarted() const =0;
+ /** ends the document */
+ virtual void endDocument(bool sendDelayedSubDoc=true) = 0;
+
+ // ------ page --------
+ /** returns true if a page is opened */
+ virtual bool isPageSpanOpened() const = 0;
+ /** returns the current page span
+
+ \note this forces the opening of a new page if no page is opened.*/
+ virtual SW602PageSpan const &getPageSpan() = 0;
+
+ // ------ header/footer --------
+ /** insert a header (interaction with SW602PageSpan which fills the parameters for openHeader) */
+ virtual bool insertHeader(SW602SubDocumentPtr subDocument, librevenge::RVNGPropertyList const &extras) = 0;
+ /** insert a footer (interaction with SW602PageSpan which fills the parameters for openFooter) */
+ virtual bool insertFooter(SW602SubDocumentPtr subDocument, librevenge::RVNGPropertyList const &extras) = 0;
+ /** returns true if the header/footer is open */
+ virtual bool isHeaderFooterOpened() const = 0;
+
+ // ------ text data -----------
+ //! adds a basic character, ..
+ virtual void insertChar(uint8_t character)=0;
+ /** insert a character using the font converter to find the utf8
+ character */
+ virtual void insertCharacter(unsigned char c)=0;
+ /** insert a character using the font converter to find the utf8
+ character and if needed, input to read extra character.
+
+ \return the number of extra character read
+ */
+ virtual int insertCharacter(unsigned char c, librevenge::RVNGInputStream &input, long endPos=-1)=0;
+ /** adds an unicode character.
+ * By convention if \a character=0xfffd(undef), no character is added */
+ virtual void insertUnicode(uint32_t character)=0;
+ //! adds a unicode string
+ virtual void insertUnicodeString(librevenge::RVNGString const &str)=0;
+
+ //! adds a tab
+ virtual void insertTab()=0;
+ //! adds an end of line ( by default an hard one)
+ virtual void insertEOL(bool softBreak=false)=0;
+
+ // ------ text format -----------
+ //! sets the font
+ virtual void setFont(SW602Font const &font)=0;
+ //! returns the actual font
+ virtual SW602Font const &getFont() const=0;
+
+ // ------ paragraph format -----------
+ //! returns true if a paragraph or a list is opened
+ virtual bool isParagraphOpened() const=0;
+ //! sets the paragraph
+ virtual void setParagraph(SW602Paragraph const &paragraph)=0;
+ //! returns the actual paragraph
+ virtual SW602Paragraph const &getParagraph() const=0;
+
+ // ------- fields ----------------
+ //! adds a field type
+ virtual void insertField(SW602Field const &field)=0;
+
+ // ------- link ----------------
+
+ //! open a link
+ virtual void openLink(SW602Link const &link)=0;
+ //! close a link
+ virtual void closeLink()=0;
+
+ // ------- table -----------------
+ /** open a table*/
+ virtual void openTable(SW602Table const &table) = 0;
+ /** closes this table */
+ virtual void closeTable() = 0;
+ /** open a row with given height ( if h < 0.0, set min-row-height = -h )*/
+ virtual void openTableRow(float h, librevenge::RVNGUnit unit, bool headerRow=false) = 0;
+ /** closes this row */
+ virtual void closeTableRow() = 0;
+ /** open a cell */
+ virtual void openTableCell(SW602Cell const &cell) = 0;
+ /** close a cell */
+ virtual void closeTableCell() = 0;
+ /** add empty cell */
+ virtual void addEmptyTableCell(SW602Vec2i const &pos, SW602Vec2i span=SW602Vec2i(1,1)) = 0;
+
+ // ------- section ---------------
+ /** returns true if we can add open a section, add page break, ... */
+ virtual bool canOpenSectionAddBreak() const =0;
+ //! returns true if a section is opened
+ virtual bool isSectionOpened() const=0;
+ //! returns the actual section
+ virtual SW602Section const &getSection() const=0;
+ //! open a section if possible
+ virtual bool openSection(SW602Section const &section)=0;
+ //! close a section
+ virtual bool closeSection()=0;
+ //! inserts a break type: ColumBreak, PageBreak, ..
+ virtual void insertBreak(BreakType breakType)=0;
+
+ // ------- subdocument ---------------
+ /** insert a note */
+ virtual void insertNote(SW602Note const &note, SW602SubDocumentPtr &subDocument)=0;
+ /** adds comment */
+ virtual void insertComment(SW602SubDocumentPtr &subDocument) = 0;
+ /** adds a picture with various representationin given position.
+ \note by default only send the first picture*/
+ virtual void insertPicture(SW602Position const &pos, SW602EmbeddedObject const &picture,
+ SW602GraphicStyle const &style=SW602GraphicStyle::emptyStyle())=0;
+ /** adds a shape picture in given position */
+ virtual void insertShape(SW602Position const &pos, SW602GraphicShape const &shape,
+ SW602GraphicStyle const &style) = 0;
+ /** adds a textbox in given position */
+ virtual void insertTextBox(SW602Position const &pos, SW602SubDocumentPtr subDocument,
+ SW602GraphicStyle const &frameStyle=SW602GraphicStyle::emptyStyle()) = 0;
+ /** adds a textbox in given position */
+ virtual void insertTextBoxInShape(SW602Position const &pos, SW602SubDocumentPtr subDocument,
+ SW602GraphicShape const &/*shape*/,
+ SW602GraphicStyle const &frameStyle=SW602GraphicStyle::emptyStyle())
+ {
+ static bool first=true;
+ if (first)
+ {
+ SW602_DEBUG_MSG(("SW602Listener::insertTextBoxInShape: umimplemented, revert to basic insertTextBox\n"));
+ first=false;
+ }
+ insertTextBox(pos, subDocument, frameStyle);
+ }
+ /** low level: tries to open a frame */
+ virtual bool openFrame(SW602Position const &pos, SW602GraphicStyle const &style=SW602GraphicStyle::emptyStyle()) = 0;
+ /** low level: tries to close the last opened frame */
+ virtual void closeFrame() = 0;
+ /** low level: tries to open a group */
+ virtual bool openGroup(SW602Position const &pos) = 0;
+ /** low level: tries to close the last opened group */
+ virtual void closeGroup() = 0;
+ /** low level: function called to add a subdocument */
+ virtual void handleSubDocument(SW602SubDocumentPtr subDocument, libsw602::SubDocumentType subDocumentType) = 0;
+ /** returns true if a subdocument is open */
+ virtual bool isSubDocumentOpened(libsw602::SubDocumentType &subdocType) const = 0;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602PageSpan.cpp b/src/lib/SW602PageSpan.cpp
new file mode 100644
index 0000000..fae0fe9
--- /dev/null
+++ b/src/lib/SW602PageSpan.cpp
@@ -0,0 +1,414 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "SW602PageSpan.h"
+
+#include <librevenge/librevenge.h>
+
+#include "SW602Listener.h"
+#include "SW602Paragraph.h"
+#include "SW602SubDocument.h"
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+/** Internal: the structures of a SW602PageSpan */
+namespace SW602PageSpanInternal
+{
+////////////////////////////////////////
+//! Internal: the subdocument of a SW602Parser
+class SubDocument : public SW602SubDocument
+{
+public:
+ //! constructor
+ explicit SubDocument(SW602HeaderFooter const &headerFooter) :
+ SW602SubDocument(0, boost::shared_ptr<librevenge::RVNGInputStream>(), SW602Entry()), m_headerFooter(headerFooter) {}
+
+ //! destructor
+ virtual ~SubDocument() {}
+
+ //! operator!=
+ virtual bool operator!=(SW602SubDocument const &doc) const
+ {
+ if (SW602SubDocument::operator!=(doc)) return true;
+ SubDocument const *sDoc = dynamic_cast<SubDocument const *>(&doc);
+ if (!sDoc) return true;
+ if (m_headerFooter != sDoc->m_headerFooter) return true;
+ return false;
+ }
+
+ //! operator!==
+ virtual bool operator==(SW602SubDocument const &doc) const
+ {
+ return !operator!=(doc);
+ }
+
+ //! the parser function
+ void parse(SW602ListenerPtr &listener, libsw602::SubDocumentType type);
+
+protected:
+ //! the header footer
+ SW602HeaderFooter const &m_headerFooter;
+};
+
+void SubDocument::parse(SW602ListenerPtr &listener, libsw602::SubDocumentType type)
+{
+ if (!listener.get())
+ {
+ SW602_DEBUG_MSG(("SW602PageSpanInternal::SubDocument::parse: no listener\n"));
+ return;
+ }
+ if (m_headerFooter.m_pageNumberPosition >= SW602HeaderFooter::TopLeft &&
+ m_headerFooter.m_pageNumberPosition <= SW602HeaderFooter::TopRight)
+ m_headerFooter.insertPageNumberParagraph(listener.get());
+ if (m_headerFooter.m_subDocument)
+ m_headerFooter.m_subDocument->parse(listener, type);
+ if (m_headerFooter.m_pageNumberPosition >= SW602HeaderFooter::BottomLeft &&
+ m_headerFooter.m_pageNumberPosition <= SW602HeaderFooter::BottomRight)
+ m_headerFooter.insertPageNumberParagraph(listener.get());
+}
+}
+
+// ----------------- SW602HeaderFooter ------------------------
+SW602HeaderFooter::SW602HeaderFooter(SW602HeaderFooter::Type const type, SW602HeaderFooter::Occurrence const occurrence) :
+ m_type(type), m_occurrence(occurrence), m_height(0),
+ m_pageNumberPosition(SW602HeaderFooter::None), m_pageNumberType(libsw602::ARABIC),
+ m_pageNumberFont(20,12), m_subDocument()
+{
+}
+
+SW602HeaderFooter::~SW602HeaderFooter()
+{
+}
+
+bool SW602HeaderFooter::operator==(SW602HeaderFooter const &hf) const
+{
+ if (&hf==this) return true;
+ if (m_type != hf.m_type)
+ return false;
+ if (m_type == UNDEF)
+ return true;
+ if (m_occurrence != hf.m_occurrence)
+ return false;
+ if (m_height < hf.m_height || m_height > hf.m_height)
+ return false;
+ if (m_pageNumberPosition != hf.m_pageNumberPosition ||
+ m_pageNumberType != hf.m_pageNumberType ||
+ m_pageNumberFont != hf.m_pageNumberFont)
+ return false;
+ if (!m_subDocument)
+ return !hf.m_subDocument;
+ if (*m_subDocument.get() != hf.m_subDocument)
+ return false;
+ return true;
+}
+
+// send data to the listener
+void SW602HeaderFooter::send(SW602Listener *listener) const
+{
+ if (m_type == UNDEF)
+ return;
+ if (!listener)
+ {
+ SW602_DEBUG_MSG(("SW602HeaderFooter::send: called without listener\n"));
+ return;
+ }
+ librevenge::RVNGPropertyList propList;
+ switch (m_occurrence)
+ {
+ case ODD:
+ propList.insert("librevenge:occurrence", "odd");
+ break;
+ case EVEN:
+ propList.insert("librevenge:occurrence", "even");
+ break;
+ case ALL:
+ propList.insert("librevenge:occurrence", "all");
+ break;
+ case NEVER:
+ default:
+ break;
+ }
+ if (m_pageNumberPosition!=None)
+ {
+ boost::shared_ptr<SW602PageSpanInternal::SubDocument> doc
+ (new SW602PageSpanInternal::SubDocument(*this));
+ if (m_type == HEADER)
+ listener->insertHeader(doc,propList);
+ else
+ listener->insertFooter(doc,propList);
+ return;
+ }
+ if (m_type == HEADER)
+ listener->insertHeader(m_subDocument,propList);
+ else
+ listener->insertFooter(m_subDocument,propList);
+}
+
+void SW602HeaderFooter::insertPageNumberParagraph(SW602Listener *listener) const
+{
+ SW602Paragraph para;
+ para.m_justify = SW602Paragraph::JustificationCenter;
+ switch (m_pageNumberPosition)
+ {
+ case TopLeft:
+ case BottomLeft:
+ para.m_justify = SW602Paragraph::JustificationLeft;
+ break;
+ case TopRight:
+ case BottomRight:
+ para.m_justify = SW602Paragraph::JustificationRight;
+ break;
+ case TopCenter:
+ case BottomCenter:
+ case None:
+ break;
+ default:
+ SW602_DEBUG_MSG(("SW602HeaderFooter::insertPageNumberParagraph: unexpected value\n"));
+ break;
+ }
+ listener->setParagraph(para);
+ listener->setFont(m_pageNumberFont);
+ if (listener->isParagraphOpened())
+ listener->insertEOL();
+
+ SW602Field field(SW602Field::PageNumber);
+ field.m_numberingType=m_pageNumberType;
+ listener->insertField(field);
+}
+
+// ----------------- SW602PageSpan ------------------------
+SW602PageSpan::SW602PageSpan() :
+ m_formLength(11.0), m_formWidth(8.5), m_formOrientation(SW602PageSpan::PORTRAIT),
+ m_name(""), m_masterName(""),
+ m_backgroundColor(SW602Color::white()),
+ m_pageNumber(-1),
+ m_headerFooterList(),
+ m_pageSpan(1)
+{
+ for (int i = 0; i < 4; i++) m_margins[i] = 1.0;
+}
+
+SW602PageSpan::~SW602PageSpan()
+{
+}
+
+void SW602PageSpan::setHeaderFooter(SW602HeaderFooter const &hF)
+{
+ SW602HeaderFooter::Type const type=hF.m_type;
+ switch (hF.m_occurrence)
+ {
+ case SW602HeaderFooter::NEVER:
+ removeHeaderFooter(type, SW602HeaderFooter::ALL);
+ case SW602HeaderFooter::ALL:
+ removeHeaderFooter(type, SW602HeaderFooter::ODD);
+ removeHeaderFooter(type, SW602HeaderFooter::EVEN);
+ break;
+ case SW602HeaderFooter::ODD:
+ removeHeaderFooter(type, SW602HeaderFooter::ALL);
+ break;
+ case SW602HeaderFooter::EVEN:
+ removeHeaderFooter(type, SW602HeaderFooter::ALL);
+ break;
+ default:
+ break;
+ }
+ int pos = getHeaderFooterPosition(hF.m_type, hF.m_occurrence);
+ if (pos != -1)
+ m_headerFooterList[size_t(pos)]=hF;
+
+ bool containsHFLeft = containsHeaderFooter(type, SW602HeaderFooter::ODD);
+ bool containsHFRight = containsHeaderFooter(type, SW602HeaderFooter::EVEN);
+
+ if (containsHFLeft && !containsHFRight)
+ {
+ SW602_DEBUG_MSG(("Inserting dummy header right\n"));
+ SW602HeaderFooter dummy(type, SW602HeaderFooter::EVEN);
+ pos = getHeaderFooterPosition(type, SW602HeaderFooter::EVEN);
+ if (pos != -1)
+ m_headerFooterList[size_t(pos)]=SW602HeaderFooter(type, SW602HeaderFooter::EVEN);
+ }
+ else if (!containsHFLeft && containsHFRight)
+ {
+ SW602_DEBUG_MSG(("Inserting dummy header left\n"));
+ pos = getHeaderFooterPosition(type, SW602HeaderFooter::ODD);
+ if (pos != -1)
+ m_headerFooterList[size_t(pos)]=SW602HeaderFooter(type, SW602HeaderFooter::ODD);
+ }
+}
+
+void SW602PageSpan::checkMargins()
+{
+ if (m_margins[libsw602::Left]+m_margins[libsw602::Right] > 0.95*m_formWidth)
+ {
+ SW602_DEBUG_MSG(("SW602PageSpan::checkMargins: left/right margins seems bad\n"));
+ m_margins[libsw602::Left] = m_margins[libsw602::Right] = 0.05*m_formWidth;
+ }
+ if (m_margins[libsw602::Top]+m_margins[libsw602::Bottom] > 0.95*m_formLength)
+ {
+ SW602_DEBUG_MSG(("SW602PageSpan::checkMargins: top/bottom margins seems bad\n"));
+ m_margins[libsw602::Top] = m_margins[libsw602::Bottom] = 0.05*m_formLength;
+ }
+}
+
+void SW602PageSpan::sendHeaderFooters(SW602Listener *listener) const
+{
+ if (!listener)
+ {
+ SW602_DEBUG_MSG(("SW602PageSpan::sendHeaderFooters: no listener\n"));
+ return;
+ }
+
+ for (size_t i = 0; i < m_headerFooterList.size(); i++)
+ {
+ SW602HeaderFooter const &hf = m_headerFooterList[i];
+ if (!hf.isDefined()) continue;
+ hf.send(listener);
+ }
+}
+
+void SW602PageSpan::sendHeaderFooters(SW602Listener *listener, SW602HeaderFooter::Occurrence occurrence) const
+{
+ if (!listener)
+ {
+ SW602_DEBUG_MSG(("SW602PageSpan::sendHeaderFooters: no listener\n"));
+ return;
+ }
+
+ for (size_t i = 0; i < m_headerFooterList.size(); i++)
+ {
+ SW602HeaderFooter const &hf = m_headerFooterList[i];
+ if (!hf.isDefined()) continue;
+ if (hf.m_occurrence==occurrence || hf.m_occurrence==SW602HeaderFooter::ALL)
+ hf.send(listener);
+ }
+}
+
+void SW602PageSpan::getPageProperty(librevenge::RVNGPropertyList &propList) const
+{
+ propList.insert("librevenge:num-pages", getPageSpan());
+
+ if (hasPageName())
+ propList.insert("draw:name", getPageName());
+ if (hasMasterPageName())
+ propList.insert("librevenge:master-page-name", getMasterPageName());
+ propList.insert("fo:page-height", getFormLength(), librevenge::RVNG_INCH);
+ propList.insert("fo:page-width", getFormWidth(), librevenge::RVNG_INCH);
+ if (getFormOrientation() == LANDSCAPE)
+ propList.insert("style:print-orientation", "landscape");
+ else
+ propList.insert("style:print-orientation", "portrait");
+ propList.insert("fo:margin-left", getMarginLeft(), librevenge::RVNG_INCH);
+ propList.insert("fo:margin-right", getMarginRight(), librevenge::RVNG_INCH);
+ propList.insert("fo:margin-top", getMarginTop(), librevenge::RVNG_INCH);
+ propList.insert("fo:margin-bottom", getMarginBottom(), librevenge::RVNG_INCH);
+ if (!m_backgroundColor.isWhite())
+ propList.insert("fo:background-color", m_backgroundColor.str().c_str());
+}
+
+
+bool SW602PageSpan::operator==(const SW602PageSpan &page2) const
+{
+ if (&page2 == this) return true;
+ if (m_formLength < page2.m_formLength || m_formLength > page2.m_formLength ||
+ m_formWidth < page2.m_formWidth || m_formWidth > page2.m_formWidth ||
+ m_formOrientation != page2.m_formOrientation)
+ return false;
+ if (getMarginLeft() < page2.getMarginLeft() || getMarginLeft() > page2.getMarginLeft() ||
+ getMarginRight() < page2.getMarginRight() || getMarginRight() > page2.getMarginRight() ||
+ getMarginTop() < page2.getMarginTop() || getMarginTop() > page2.getMarginTop() ||
+ getMarginBottom() < page2.getMarginBottom() || getMarginBottom() > page2.getMarginBottom())
+ return false;
+ if (getPageName() != page2.getPageName() || getMasterPageName() != page2.getMasterPageName() ||
+ backgroundColor() != page2.backgroundColor())
+ return false;
+
+ if (getPageNumber() != page2.getPageNumber())
+ return false;
+
+ size_t numHF = m_headerFooterList.size();
+ size_t numHF2 = page2.m_headerFooterList.size();
+ for (size_t i = numHF; i < numHF2; i++)
+ {
+ if (page2.m_headerFooterList[i].isDefined())
+ return false;
+ }
+ for (size_t i = numHF2; i < numHF; i++)
+ {
+ if (m_headerFooterList[i].isDefined())
+ return false;
+ }
+ if (numHF2 < numHF) numHF = numHF2;
+ for (size_t i = 0; i < numHF; i++)
+ {
+ if (m_headerFooterList[i] != page2.m_headerFooterList[i])
+ return false;
+ }
+ SW602_DEBUG_MSG(("WordPerfect: SW602PageSpan == comparison finished, found no differences\n"));
+
+ return true;
+}
+
+// -------------- manage header footer list ------------------
+void SW602PageSpan::removeHeaderFooter(SW602HeaderFooter::Type type, SW602HeaderFooter::Occurrence occurrence)
+{
+ int pos = getHeaderFooterPosition(type, occurrence);
+ if (pos == -1) return;
+ m_headerFooterList[size_t(pos)]=SW602HeaderFooter();
+}
+
+bool SW602PageSpan::containsHeaderFooter(SW602HeaderFooter::Type type, SW602HeaderFooter::Occurrence occurrence)
+{
+ int pos = getHeaderFooterPosition(type, occurrence);
+ if (pos == -1 || !m_headerFooterList[size_t(pos)].isDefined()) return false;
+ return true;
+}
+
+int SW602PageSpan::getHeaderFooterPosition(SW602HeaderFooter::Type type, SW602HeaderFooter::Occurrence occurrence)
+{
+ int typePos = 0, occurrencePos = 0;
+ switch (type)
+ {
+ case SW602HeaderFooter::HEADER:
+ typePos = 0;
+ break;
+ case SW602HeaderFooter::FOOTER:
+ typePos = 1;
+ break;
+ case SW602HeaderFooter::UNDEF:
+ default:
+ SW602_DEBUG_MSG(("SW602PageSpan::getVectorPosition: unknown type\n"));
+ return -1;
+ }
+ switch (occurrence)
+ {
+ case SW602HeaderFooter::ALL:
+ occurrencePos = 0;
+ break;
+ case SW602HeaderFooter::ODD:
+ occurrencePos = 1;
+ break;
+ case SW602HeaderFooter::EVEN:
+ occurrencePos = 2;
+ break;
+ case SW602HeaderFooter::NEVER:
+ default:
+ SW602_DEBUG_MSG(("SW602PageSpan::getVectorPosition: unknown occurrence\n"));
+ return -1;
+ }
+ size_t res = size_t(typePos*3+occurrencePos);
+ if (res >= m_headerFooterList.size())
+ m_headerFooterList.resize(res+1);
+ return int(res);
+}
+
+}
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602PageSpan.h b/src/lib/SW602PageSpan.h
new file mode 100644
index 0000000..038cc61
--- /dev/null
+++ b/src/lib/SW602PageSpan.h
@@ -0,0 +1,291 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef SW602PAGESPAN_H
+#define SW602PAGESPAN_H
+
+#include <vector>
+
+#include "SW602Font.h"
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+/** a class which stores the header/footer data */
+class SW602HeaderFooter
+{
+public:
+ /** the header/footer type */
+ enum Type { HEADER, FOOTER, UNDEF };
+ /** the header/footer occurrence in the file */
+ enum Occurrence { ODD, EVEN, ALL, NEVER };
+ /** a fixed page number position */
+ enum PageNumberPosition { None = 0, TopLeft, TopCenter, TopRight, BottomLeft, BottomCenter, BottomRight };
+
+ //! constructor
+ SW602HeaderFooter(Type const type=UNDEF, Occurrence const occurrence=NEVER);
+ //! destructor
+ ~SW602HeaderFooter();
+ //! returns true if the header footer is defined
+ bool isDefined() const
+ {
+ return m_type != UNDEF;
+ }
+ /** send to header to the listener */
+ void send(SW602Listener *listener) const;
+ //! operator==
+ bool operator==(SW602HeaderFooter const &headerFooter) const;
+ //! operator!=
+ bool operator!=(SW602HeaderFooter const &headerFooter) const
+ {
+ return !operator==(headerFooter);
+ }
+ //! insert a page number
+ void insertPageNumberParagraph(SW602Listener *listener) const;
+
+public:
+ //! the type header/footer
+ Type m_type;
+ //! the actual occurrence
+ Occurrence m_occurrence;
+ //! the height ( if known )
+ double m_height;
+ //! the page number position ( or none)
+ PageNumberPosition m_pageNumberPosition;
+ //! the page numbering type
+ libsw602::NumberingType m_pageNumberType;
+ //! the page numbering font
+ SW602Font m_pageNumberFont;
+ //! the document data
+ SW602SubDocumentPtr m_subDocument;
+};
+
+typedef boost::shared_ptr<SW602HeaderFooter> SW602HeaderFooterPtr;
+
+/** A class which defines the page properties */
+class SW602PageSpan
+{
+public:
+ /** the page orientation */
+ enum FormOrientation { PORTRAIT, LANDSCAPE };
+ /** a fixed page number position */
+ enum PageNumberPosition { None = 0, TopLeft, TopCenter, TopRight,
+ BottomLeft, BottomCenter, BottomRight
+ };
+public:
+ //! constructor
+ SW602PageSpan();
+ //! destructor
+ virtual ~SW602PageSpan();
+
+ //! returns the page length
+ double getFormLength() const
+ {
+ return m_formLength;
+ }
+ //! returns the page width
+ double getFormWidth() const
+ {
+ return m_formWidth;
+ }
+ //! returns the page orientation
+ FormOrientation getFormOrientation() const
+ {
+ return m_formOrientation;
+ }
+ //! returns the left margin
+ double getMarginLeft() const
+ {
+ return m_margins[libsw602::Left];
+ }
+ //! returns the right margin
+ double getMarginRight() const
+ {
+ return m_margins[libsw602::Right];
+ }
+ //! returns the top margin
+ double getMarginTop() const
+ {
+ return m_margins[libsw602::Top];
+ }
+ //! returns the bottom margin
+ double getMarginBottom() const
+ {
+ return m_margins[libsw602::Bottom];
+ }
+ //! returns the page length (form width without margin )
+ double getPageLength() const
+ {
+ return m_formLength-m_margins[libsw602::Top]-m_margins[libsw602::Bottom];
+ }
+ //! returns the page width (form width without margin )
+ double getPageWidth() const
+ {
+ return m_formWidth-m_margins[libsw602::Left]-m_margins[libsw602::Right];
+ }
+ //! returns the background color
+ SW602Color backgroundColor() const
+ {
+ return m_backgroundColor;
+ }
+ int getPageNumber() const
+ {
+ return m_pageNumber;
+ }
+ int getPageSpan() const
+ {
+ return m_pageSpan;
+ }
+
+ //! add a header/footer on some page
+ void setHeaderFooter(SW602HeaderFooter const &headerFooter);
+ //! set the total page length
+ void setFormLength(const double formLength)
+ {
+ m_formLength = formLength;
+ }
+ //! set the total page width
+ void setFormWidth(const double formWidth)
+ {
+ m_formWidth = formWidth;
+ }
+ //! set the form orientation
+ void setFormOrientation(const FormOrientation formOrientation)
+ {
+ m_formOrientation = formOrientation;
+ }
+ //! set the page left margin
+ void setMarginLeft(const double marginLeft)
+ {
+ m_margins[libsw602::Left] = (marginLeft >= 0) ? marginLeft : 0.01;
+ }
+ //! set the page right margin
+ void setMarginRight(const double marginRight)
+ {
+ m_margins[libsw602::Right] = (marginRight >= 0) ? marginRight : 0.01;
+ }
+ //! set the page top margin
+ void setMarginTop(const double marginTop)
+ {
+ m_margins[libsw602::Top] =(marginTop >= 0) ? marginTop : 0.01;
+ }
+ //! set the page bottom margin
+ void setMarginBottom(const double marginBottom)
+ {
+ m_margins[libsw602::Bottom] = (marginBottom >= 0) ? marginBottom : 0.01;
+ }
+ //! set all the margins
+ void setMargins(double margin, int wh=libsw602::LeftBit|libsw602::RightBit|libsw602::TopBit|libsw602::BottomBit)
+ {
+ if (margin < 0.0) margin = 0.01;
+ if (wh&libsw602::LeftBit)
+ m_margins[libsw602::Left]=margin;
+ if (wh&libsw602::RightBit)
+ m_margins[libsw602::Right]=margin;
+ if (wh&libsw602::TopBit)
+ m_margins[libsw602::Top]=margin;
+ if (wh&libsw602::BottomBit)
+ m_margins[libsw602::Bottom]=margin;
+ }
+ //! check if the page margins are consistent with the page dimension, if not update them
+ void checkMargins();
+ //! set the page name
+ void setPageName(librevenge::RVNGString const &name)
+ {
+ m_name=name;
+ }
+ //! return true if the page has a name
+ bool hasPageName() const
+ {
+ return !m_name.empty();
+ }
+ //! return the page name
+ librevenge::RVNGString const &getPageName() const
+ {
+ return m_name;
+ }
+ //! set the page master name
+ void setMasterPageName(librevenge::RVNGString const &name)
+ {
+ m_masterName=name;
+ }
+ //! return true if the masterPage has a name
+ bool hasMasterPageName() const
+ {
+ return !m_masterName.empty();
+ }
+ //! return the page master name
+ librevenge::RVNGString const &getMasterPageName() const
+ {
+ return m_masterName;
+ }
+ //! set the background color
+ void setBackgroundColor(SW602Color color=SW602Color::white())
+ {
+ m_backgroundColor=color;
+ }
+ //! set the page number
+ void setPageNumber(const int pageNumber)
+ {
+ m_pageNumber = pageNumber;
+ }
+ //! set the page span ( default 1)
+ void setPageSpan(const int pageSpan)
+ {
+ m_pageSpan = pageSpan;
+ }
+ //! operator==
+ bool operator==(const SW602PageSpan &pageSpan) const;
+ //! operator!=
+ bool operator!=(const SW602PageSpan &pageSpan) const
+ {
+ return !operator==(pageSpan);
+ }
+
+ // interface with SW602Listener
+ //! add the page properties in pList
+ void getPageProperty(librevenge::RVNGPropertyList &pList) const;
+ //! send the page's headers/footers if some exists
+ void sendHeaderFooters(SW602Listener *listener) const;
+ //! send the page's headers/footers corresponding to an occurrence if some exists
+ void sendHeaderFooters(SW602Listener *listener, SW602HeaderFooter::Occurrence occurrence) const;
+
+protected:
+ //! return the header footer positions in m_headerFooterList
+ int getHeaderFooterPosition(SW602HeaderFooter::Type type, SW602HeaderFooter::Occurrence occurrence);
+ //! remove a header footer
+ void removeHeaderFooter(SW602HeaderFooter::Type type, SW602HeaderFooter::Occurrence occurrence);
+ //! return true if we have a header footer in this position
+ bool containsHeaderFooter(SW602HeaderFooter::Type type, SW602HeaderFooter::Occurrence occurrence);
+private:
+ double m_formLength/** the form length*/, m_formWidth /** the form width */;
+ /** the form orientation */
+ FormOrientation m_formOrientation;
+ /** the margins: libsw602::Left, ... */
+ double m_margins[4];
+ //! the page name
+ librevenge::RVNGString m_name;
+ //! the page master name
+ librevenge::RVNGString m_masterName;
+ /** the page background color: default white */
+ SW602Color m_backgroundColor;
+ //! the page number ( or -1)
+ int m_pageNumber;
+ //! the list of header
+ std::vector<SW602HeaderFooter> m_headerFooterList;
+ //! the number of page
+ int m_pageSpan;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602Paragraph.cpp b/src/lib/SW602Paragraph.cpp
new file mode 100644
index 0000000..2264692
--- /dev/null
+++ b/src/lib/SW602Paragraph.cpp
@@ -0,0 +1,491 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "SW602Paragraph.h"
+
+#include <map>
+
+#include <librevenge/librevenge.h>
+
+#include "SW602List.h"
+#include "SW602Position.h"
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+////////////////////////////////////////////////////////////
+// tabstop
+////////////////////////////////////////////////////////////
+void SW602TabStop::addTo(librevenge::RVNGPropertyListVector &propList, double decalX) const
+{
+ librevenge::RVNGPropertyList tab;
+
+ // type
+ switch (m_alignment)
+ {
+ case RIGHT:
+ tab.insert("style:type", "right");
+ break;
+ case CENTER:
+ tab.insert("style:type", "center");
+ break;
+ case DECIMAL:
+ tab.insert("style:type", "char");
+ if (m_decimalCharacter)
+ {
+ librevenge::RVNGString sDecimal;
+ libsw602::appendUnicode(m_decimalCharacter, sDecimal);
+ tab.insert("style:char", sDecimal);
+ }
+ else
+ tab.insert("style:char", ".");
+ break;
+ case LEFT:
+ case BAR: // BAR is not handled in OO
+ default:
+ break;
+ }
+
+ // leader character
+ if (m_leaderCharacter != 0x0000)
+ {
+ librevenge::RVNGString sLeader;
+ libsw602::appendUnicode(m_leaderCharacter, sLeader);
+ tab.insert("style:leader-text", sLeader);
+ tab.insert("style:leader-style", "solid");
+ }
+
+ // position
+ double position = m_position+decalX;
+ if (position < 0.00005 && position > -0.00005)
+ position = 0.0;
+ tab.insert("style:position", position, librevenge::RVNG_INCH);
+
+ propList.append(tab);
+}
+
+int SW602TabStop::cmp(SW602TabStop const &tabs) const
+{
+ if (m_position < tabs.m_position) return -1;
+ if (m_position > tabs.m_position) return 1;
+ if (m_alignment < tabs.m_alignment) return -1;
+ if (m_alignment > tabs.m_alignment) return 1;
+ if (m_leaderCharacter < tabs.m_leaderCharacter) return -1;
+ if (m_leaderCharacter > tabs.m_leaderCharacter) return 1;
+ if (m_decimalCharacter < tabs.m_decimalCharacter) return -1;
+ if (m_decimalCharacter > tabs.m_decimalCharacter) return 1;
+ return 0;
+}
+
+// operator<<
+std::ostream &operator<<(std::ostream &o, SW602TabStop const &tab)
+{
+ o << tab.m_position;
+
+ switch (tab.m_alignment)
+ {
+ case SW602TabStop::LEFT:
+ o << "L";
+ break;
+ case SW602TabStop::CENTER:
+ o << "C";
+ break;
+ case SW602TabStop::RIGHT:
+ o << "R";
+ break;
+ case SW602TabStop::DECIMAL:
+ o << ":decimal";
+ break;
+ case SW602TabStop::BAR:
+ o << ":bar";
+ break;
+ default:
+ o << ":#type=" << int(tab.m_alignment);
+ break;
+ }
+ if (tab.m_leaderCharacter != '\0')
+ o << ":sep='"<< (char) tab.m_leaderCharacter << "'";
+ if (tab.m_decimalCharacter && tab.m_decimalCharacter != '.')
+ o << ":dec='" << (char) tab.m_decimalCharacter << "'";
+ return o;
+}
+
+////////////////////////////////////////////////////////////
+// paragraph
+////////////////////////////////////////////////////////////
+SW602Paragraph::SW602Paragraph() : m_marginsUnit(librevenge::RVNG_INCH), m_spacingsInterlineUnit(librevenge::RVNG_PERCENT), m_spacingsInterlineType(Fixed),
+ m_tabs(), m_tabsRelativeToLeftMargin(false), m_justify(JustificationLeft), m_breakStatus(0),
+ m_listLevelIndex(0), m_listId(-1), m_listStartValue(-1), m_listLevel(), m_backgroundColor(SW602Color::white()),
+ m_borders(), m_styleName(""), m_extra("")
+{
+ for (int i = 0; i < 3; i++) m_margins[i] = m_spacings[i] = 0.0;
+ m_spacings[0] = 1.0; // interline normal
+ for (int i = 0; i < 3; i++)
+ {
+ m_margins[i].setSet(false);
+ m_spacings[i].setSet(false);
+ }
+}
+
+SW602Paragraph::~SW602Paragraph()
+{
+}
+
+int SW602Paragraph::cmp(SW602Paragraph const &para) const
+{
+ for (int i = 0; i < 3; i++)
+ {
+ if (*(m_margins[i]) < *(para.m_margins[i])) return -1;
+ if (*(m_margins[i]) > *(para.m_margins[i])) return 1;
+ if (*(m_spacings[i]) < *(para.m_spacings[i])) return -1;
+ if (*(m_spacings[i]) > *(para.m_spacings[i])) return 1;
+ }
+ if (*m_justify < *para.m_justify) return -1;
+ if (*m_justify > *para.m_justify) return -1;
+ if (*m_marginsUnit < *para.m_marginsUnit) return -1;
+ if (*m_marginsUnit > *para.m_marginsUnit) return -1;
+ if (*m_spacingsInterlineUnit < *para.m_spacingsInterlineUnit) return -1;
+ if (*m_spacingsInterlineUnit > *para.m_spacingsInterlineUnit) return -1;
+ if (*m_spacingsInterlineType < *para.m_spacingsInterlineType) return -1;
+ if (*m_spacingsInterlineType > *para.m_spacingsInterlineType) return -1;
+ if (*m_tabsRelativeToLeftMargin < *para.m_tabsRelativeToLeftMargin) return -1;
+ if (*m_tabsRelativeToLeftMargin > *para.m_tabsRelativeToLeftMargin) return -1;
+
+ if (m_tabs->size() < para.m_tabs->size()) return -1;
+ if (m_tabs->size() > para.m_tabs->size()) return -1;
+
+ for (size_t i=0; i < m_tabs->size(); i++)
+ {
+ int diff=(*m_tabs)[i].cmp((*para.m_tabs)[i]);
+ if (diff) return diff;
+ }
+ if (*m_breakStatus < *para.m_breakStatus) return -1;
+ if (*m_breakStatus > *para.m_breakStatus) return -1;
+ if (*m_listLevelIndex < *para.m_listLevelIndex) return -1;
+ if (*m_listLevelIndex > *para.m_listLevelIndex) return -1;
+ if (*m_listId < *para.m_listId) return -1;
+ if (*m_listId > *para.m_listId) return -1;
+ if (*m_listStartValue < *para.m_listStartValue) return -1;
+ if (*m_listStartValue > *para.m_listStartValue) return -1;
+ int diff=m_listLevel->cmp(*para.m_listLevel);
+ if (diff) return diff;
+ if (*m_backgroundColor < *para.m_backgroundColor) return -1;
+ if (*m_backgroundColor > *para.m_backgroundColor) return -1;
+
+ if (m_borders.size() < para.m_borders.size()) return -1;
+ if (m_borders.size() > para.m_borders.size()) return 1;
+ for (size_t i=0; i < m_borders.size(); i++)
+ {
+ if (m_borders[i].isSet() != para.m_borders[i].isSet())
+ return m_borders[i].isSet() ? 1 : -1;
+ diff = m_borders[i]->compare(*(para.m_borders[i]));
+ if (diff) return diff;
+ }
+ if (m_styleName<para.m_styleName) return -1;
+ if (m_styleName>para.m_styleName) return 1;
+ return 0;
+}
+
+void SW602Paragraph::insert(SW602Paragraph const &para)
+{
+ for (int i = 0; i < 3; i++)
+ {
+ m_margins[i].insert(para.m_margins[i]);
+ m_spacings[i].insert(para.m_spacings[i]);
+ }
+ m_marginsUnit.insert(para.m_marginsUnit);
+ m_spacingsInterlineUnit.insert(para.m_spacingsInterlineUnit);
+ m_spacingsInterlineType.insert(para.m_spacingsInterlineType);
+ if (para.m_tabs.isSet() && m_tabs.isSet())
+ {
+ std::map<double, SW602TabStop> all;
+ for (size_t t=0; t <m_tabs->size(); ++t)
+ all[(*m_tabs)[t].m_position]=(*m_tabs)[t];
+ for (size_t t=0; t <para.m_tabs->size(); ++t)
+ all[(*para.m_tabs)[t].m_position]=(*para.m_tabs)[t];
+
+ m_tabs->resize(0);
+ std::map<double, SW602TabStop>::const_iterator it=all.begin();
+ for (; it!=all.end(); ++it)
+ m_tabs->push_back(it->second);
+ }
+ else if (para.m_tabs.isSet())
+ m_tabs=para.m_tabs;
+ m_tabsRelativeToLeftMargin.insert(para.m_tabsRelativeToLeftMargin);
+ m_justify.insert(para.m_justify);
+ m_breakStatus.insert(para.m_breakStatus);
+ m_listLevelIndex.insert(para.m_listLevelIndex);
+ m_listId.insert(para.m_listId);
+ m_listStartValue.insert(m_listStartValue);
+ m_listLevel.insert(para.m_listLevel);
+ m_backgroundColor.insert(para.m_backgroundColor);
+ if (m_borders.size() < para.m_borders.size())
+ m_borders.resize(para.m_borders.size());
+ for (size_t i = 0; i < para.m_borders.size(); i++)
+ m_borders[i].insert(para.m_borders[i]);
+ m_styleName=para.m_styleName; // checkme
+ m_extra += para.m_extra;
+}
+
+bool SW602Paragraph::hasBorders() const
+{
+ for (size_t i = 0; i < m_borders.size() && i < 4; i++)
+ {
+ if (!m_borders[i].isSet())
+ continue;
+ if (!m_borders[i]->isEmpty())
+ return true;
+ }
+ return false;
+}
+
+bool SW602Paragraph::hasDifferentBorders() const
+{
+ if (!hasBorders()) return false;
+ if (m_borders.size() < 4) return true;
+ for (size_t i = 1; i < m_borders.size(); i++)
+ {
+ if (m_borders[i].isSet() != m_borders[0].isSet())
+ return true;
+ if (*(m_borders[i]) != *(m_borders[0]))
+ return true;
+ }
+ return false;
+}
+
+double SW602Paragraph::getMarginsWidth() const
+{
+ double factor = (double) SW602Position::getScaleFactor(*m_marginsUnit, librevenge::RVNG_INCH);
+ return factor*(*(m_margins[1])+*(m_margins[2]));
+}
+
+void SW602Paragraph::addTo(librevenge::RVNGPropertyList &propList, bool inTable) const
+{
+ switch (*m_justify)
+ {
+ case JustificationLeft:
+ // doesn't require a paragraph prop - it is the default
+ propList.insert("fo:text-align", "left");
+ break;
+ case JustificationCenter:
+ propList.insert("fo:text-align", "center");
+ break;
+ case JustificationRight:
+ propList.insert("fo:text-align", "end");
+ break;
+ case JustificationFull:
+ propList.insert("fo:text-align", "justify");
+ break;
+ case JustificationFullAllLines:
+ propList.insert("fo:text-align", "justify");
+ propList.insert("fo:text-align-last", "justify");
+ break;
+ default:
+ break;
+ }
+ if (!inTable)
+ {
+ propList.insert("fo:margin-left", *m_margins[1], *m_marginsUnit);
+ propList.insert("fo:text-indent", *m_margins[0], *m_marginsUnit);
+ propList.insert("fo:margin-right", *m_margins[2], *m_marginsUnit);
+ if (!m_styleName.empty())
+ propList.insert("style:display-name", m_styleName.c_str());
+ if (!m_backgroundColor->isWhite())
+ propList.insert("fo:background-color", m_backgroundColor->str().c_str());
+ if (hasBorders())
+ {
+ bool setAll = !hasDifferentBorders();
+ for (size_t w = 0; w < m_borders.size() && w < 4; w++)
+ {
+ if (w && setAll)
+ break;
+ if (!m_borders[w].isSet())
+ continue;
+ SW602Border const &border = *(m_borders[w]);
+ if (border.isEmpty())
+ continue;
+ if (setAll)
+ {
+ border.addTo(propList);
+ break;
+ }
+ switch (w)
+ {
+ case libsw602::Left:
+ border.addTo(propList,"left");
+ break;
+ case libsw602::Right:
+ border.addTo(propList,"right");
+ break;
+ case libsw602::Top:
+ border.addTo(propList,"top");
+ break;
+ case libsw602::Bottom:
+ border.addTo(propList,"bottom");
+ break;
+ default:
+ SW602_DEBUG_MSG(("SW602Paragraph::addTo: can not send %d border\n",int(w)));
+ break;
+ }
+ }
+ }
+ }
+ propList.insert("fo:margin-top", *(m_spacings[1]), librevenge::RVNG_INCH);
+ propList.insert("fo:margin-bottom", *(m_spacings[2]), librevenge::RVNG_INCH);
+ switch (*m_spacingsInterlineType)
+ {
+ case Fixed:
+ propList.insert("fo:line-height", *(m_spacings[0]), *m_spacingsInterlineUnit);
+ break;
+ case AtLeast:
+ if (*(m_spacings[0]) <= 0 && *(m_spacings[0]) >= 0)
+ break;
+ if (*(m_spacings[0]) < 0)
+ {
+ static bool first = true;
+ if (first)
+ {
+ SW602_DEBUG_MSG(("SW602Paragraph::addTo: interline spacing seems bad\n"));
+ first = false;
+ }
+ }
+ else if (*m_spacingsInterlineUnit != librevenge::RVNG_PERCENT)
+ propList.insert("style:line-height-at-least", *(m_spacings[0]), *m_spacingsInterlineUnit);
+ else
+ {
+ propList.insert("style:line-height-at-least", *(m_spacings[0])*12.0, librevenge::RVNG_POINT);
+ static bool first = true;
+ if (first)
+ {
+ first = false;
+ SW602_DEBUG_MSG(("SW602Paragraph::addTo: assume height=12 to set line spacing at least with percent type\n"));
+ }
+ }
+ break;
+ default:
+ SW602_DEBUG_MSG(("SW602Paragraph::addTo: can not set line spacing type: %d\n",int(*m_spacingsInterlineType)));
+ break;
+ }
+ if (*m_breakStatus & NoBreakBit)
+ propList.insert("fo:keep-together", "always");
+ if (*m_breakStatus & NoBreakWithNextBit)
+ propList.insert("fo:keep-with-next", "always");
+
+ if (!m_tabs->empty())
+ {
+ double decalX=0;
+ librevenge::RVNGPropertyListVector tabs;
+ if (!*m_tabsRelativeToLeftMargin)
+ {
+ // tabs are absolute, we must remove left margin
+ double factor = (double) SW602Position::getScaleFactor(*m_marginsUnit, librevenge::RVNG_INCH);
+ decalX -= m_margins[1].get()*factor;
+ }
+
+ for (size_t i=0; i<m_tabs->size(); i++)
+ m_tabs.get()[i].addTo(tabs, decalX);
+ propList.insert("style:tab-stops", tabs);
+ }
+}
+
+std::ostream &operator<<(std::ostream &o, SW602Paragraph const &pp)
+{
+ if (!pp.m_styleName.empty())
+ o << "style=\"" << pp.m_styleName << "\",";
+ if (pp.m_margins[0].get()<0||pp.m_margins[0].get()>0)
+ o << "textIndent=" << pp.m_margins[0].get() << ",";
+ if (pp.m_margins[1].get()<0||pp.m_margins[1].get()>0)
+ o << "leftMarg=" << pp.m_margins[1].get() << ",";
+ if (pp.m_margins[2].get()<0||pp.m_margins[2].get()>0)
+ o << "rightMarg=" << pp.m_margins[2].get() << ",";
+
+ if (pp.m_spacingsInterlineUnit.get()==librevenge::RVNG_PERCENT)
+ {
+ if (pp.m_spacings[0].get() < 1.0 || pp.m_spacings[0].get() > 1.0)
+ {
+ o << "interLineSpacing=" << pp.m_spacings[0].get() << "%";
+ if (pp.m_spacingsInterlineType.get()==SW602Paragraph::AtLeast)
+ o << "[atLeast]";
+ o << ",";
+ }
+ }
+ else if (pp.m_spacings[0].get() > 0.0)
+ {
+ o << "interLineSpacing=" << pp.m_spacings[0].get();
+ if (pp.m_spacingsInterlineType.get()==SW602Paragraph::AtLeast)
+ o << "[atLeast]";
+ o << ",";
+ }
+ if (pp.m_spacings[1].get()<0||pp.m_spacings[1].get()>0)
+ o << "befSpacing=" << pp.m_spacings[1].get() << ",";
+ if (pp.m_spacings[2].get()<0||pp.m_spacings[2].get()>0)
+ o << "aftSpacing=" << pp.m_spacings[2].get() << ",";
+
+ if (pp.m_breakStatus.get() & SW602Paragraph::NoBreakBit) o << "dontbreak,";
+ if (pp.m_breakStatus.get() & SW602Paragraph::NoBreakWithNextBit) o << "dontbreakafter,";
+
+ switch (pp.m_justify.get())
+ {
+ case SW602Paragraph::JustificationLeft:
+ break;
+ case SW602Paragraph::JustificationCenter:
+ o << "just=centered, ";
+ break;
+ case SW602Paragraph::JustificationRight:
+ o << "just=right, ";
+ break;
+ case SW602Paragraph::JustificationFull:
+ o << "just=full, ";
+ break;
+ case SW602Paragraph::JustificationFullAllLines:
+ o << "just=fullAllLines, ";
+ break;
+ default:
+ o << "just=" << pp.m_justify.get() << ", ";
+ break;
+ }
+
+ if (pp.m_tabs->size())
+ {
+ o << "tabs=(";
+ for (size_t i = 0; i < pp.m_tabs->size(); i++)
+ o << pp.m_tabs.get()[i] << ",";
+ o << "),";
+ }
+ if (!pp.m_backgroundColor.get().isWhite())
+ o << "backgroundColor=" << pp.m_backgroundColor.get() << ",";
+ if (*pp.m_listId >= 0) o << "listId=" << *pp.m_listId << ",";
+ if (pp.m_listLevelIndex.get() >= 1)
+ o << pp.m_listLevel.get() << ":" << pp.m_listLevelIndex.get() <<",";
+
+ for (size_t i = 0; i < pp.m_borders.size(); i++)
+ {
+ if (!pp.m_borders[i].isSet())
+ continue;
+ SW602Border const &border = pp.m_borders[i].get();
+ if (border.isEmpty())
+ continue;
+ o << "bord";
+ if (i < 6)
+ {
+ static char const *wh[] = { "L", "R", "T", "B", "MiddleH", "MiddleV" };
+ o << wh[i];
+ }
+ else o << "[#wh=" << i << "]";
+ o << "=" << border << ",";
+ }
+
+ if (!pp.m_extra.empty()) o << "extras=(" << pp.m_extra << ")";
+ return o;
+}
+
+}
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602Paragraph.h b/src/lib/SW602Paragraph.h
new file mode 100644
index 0000000..2622847
--- /dev/null
+++ b/src/lib/SW602Paragraph.h
@@ -0,0 +1,170 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SW602_PARAGRAPH_H
+#define INCLUDED_SW602_PARAGRAPH_H
+
+#include <iostream>
+#include <vector>
+
+#include <librevenge/librevenge.h>
+
+#include "SW602List.h"
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+/** class to store a tab use by SW602Paragraph */
+struct SW602TabStop
+{
+ //! the tab alignment
+ enum Alignment { LEFT, RIGHT, CENTER, DECIMAL, BAR };
+ //! constructor
+ SW602TabStop(double position = 0.0, Alignment alignment = LEFT, uint16_t leaderCharacter='\0', uint16_t decimalCharacter = '.') :
+ m_position(position), m_alignment(alignment), m_leaderCharacter(leaderCharacter), m_decimalCharacter(decimalCharacter)
+ {
+ }
+ //! add a tab to the propList
+ void addTo(librevenge::RVNGPropertyListVector &propList, double decalX=0.0) const;
+ //! operator==
+ bool operator==(SW602TabStop const &tabs) const
+ {
+ return cmp(tabs)==0;
+ }
+ //! operator!=
+ bool operator!=(SW602TabStop const &tabs) const
+ {
+ return cmp(tabs)!=0;
+ }
+ //! operator <<
+ friend std::ostream &operator<<(std::ostream &o, SW602TabStop const &ft);
+ //! a comparison function
+ int cmp(SW602TabStop const &tabs) const;
+ //! the tab position
+ double m_position;
+ //! the alignment ( left, center, ...)
+ Alignment m_alignment;
+ //! the leader char
+ uint16_t m_leaderCharacter;
+ //! the decimal char
+ uint16_t m_decimalCharacter;
+};
+
+//! class to store the paragraph properties
+class SW602Paragraph
+{
+public:
+ /** some bit use to defined the break status */
+ enum { NoBreakBit = 0x1, NoBreakWithNextBit=0x2 };
+ /** an enum used to defined the paragraph justification: left, center, right, full ... */
+ enum Justification { JustificationLeft, JustificationFull, JustificationCenter,
+ JustificationRight, JustificationFullAllLines
+ };
+ /** the line spacing type: fixed or at least */
+ enum LineSpacingType { Fixed, AtLeast};
+
+ //! constructor
+ SW602Paragraph();
+ //! destructor
+ virtual ~SW602Paragraph();
+ //! operator==
+ bool operator==(SW602Paragraph const &p) const
+ {
+ return cmp(p)==0;
+ }
+ //! operator!=
+ bool operator!=(SW602Paragraph const &p) const
+ {
+ return cmp(p)!=0;
+ }
+ //! a comparison function
+ int cmp(SW602Paragraph const &p) const;
+ //! return the paragraph margin width (in inches)
+ double getMarginsWidth() const;
+ //! check if the paragraph has some borders
+ bool hasBorders() const;
+ //! check if the paragraph has different borders
+ bool hasDifferentBorders() const;
+ //! a function used to resize the borders list ( adding empty borders if needed )
+ void resizeBorders(size_t newSize)
+ {
+ SW602Border empty;
+ empty.m_style=SW602Border::None;
+ m_borders.resize(newSize, SW602Variable<SW602Border>(empty));
+ }
+ //! set the interline
+ void setInterline(double value, librevenge::RVNGUnit unit, LineSpacingType type=Fixed)
+ {
+ m_spacings[0]=value;
+ m_spacingsInterlineUnit=unit;
+ m_spacingsInterlineType=type;
+ }
+ //! add to the propList
+ void addTo(librevenge::RVNGPropertyList &propList, bool inTable) const;
+
+ //! insert the set values of para in the actual paragraph
+ void insert(SW602Paragraph const &para);
+ //! operator <<
+ friend std::ostream &operator<<(std::ostream &o, SW602Paragraph const &ft);
+
+ /** the margins
+ *
+ * - 0: first line left margin
+ * - 1: left margin
+ * - 2: right margin*/
+ SW602Variable<double> m_margins[3]; // 0: first line left, 1: left, 2: right
+ /** the margins INCH, ... */
+ SW602Variable<librevenge::RVNGUnit> m_marginsUnit;
+ /** the line spacing
+ *
+ * - 0: interline
+ * - 1: before
+ * - 2: after */
+ SW602Variable<double> m_spacings[3]; // 0: interline, 1: before, 2: after
+ /** the interline unit PERCENT or INCH, ... */
+ SW602Variable<librevenge::RVNGUnit> m_spacingsInterlineUnit;
+ /** the interline type: fixed, atLeast, ... */
+ SW602Variable<LineSpacingType> m_spacingsInterlineType;
+ //! the tabulations
+ SW602Variable<std::vector<SW602TabStop> > m_tabs;
+ //! true if the tabs are relative to left margin, false if there are relative to the page margin (default)
+ SW602Variable<bool> m_tabsRelativeToLeftMargin;
+
+ /** the justification */
+ SW602Variable<Justification> m_justify;
+ /** a list of bits: 0x1 (unbreakable), 0x2 (do not break after) */
+ SW602Variable<int> m_breakStatus; // BITS: 1: unbreakable, 2: dont break after
+
+ /** the actual level index */
+ SW602Variable<int> m_listLevelIndex;
+ /** the list id (if know ) */
+ SW602Variable<int> m_listId;
+ /** the list start value (if set ) */
+ SW602Variable<int> m_listStartValue;
+ /** the actual level */
+ SW602Variable<SW602ListLevel> m_listLevel;
+
+ //! the background color
+ SW602Variable<SW602Color> m_backgroundColor;
+
+ //! list of border ( order SW602Border::Pos)
+ std::vector<SW602Variable<SW602Border> > m_borders;
+
+ //! the style name
+ std::string m_styleName;
+ //! a string to store some errors
+ std::string m_extra;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602Parser.cpp b/src/lib/SW602Parser.cpp
new file mode 100644
index 0000000..52202cc
--- /dev/null
+++ b/src/lib/SW602Parser.cpp
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "SW602Parser.h"
+
+#include "SW602GraphicListener.h"
+#include "SW602GraphicStyle.h"
+#include "SW602List.h"
+#include "SW602SpreadsheetListener.h"
+#include "SW602TextListener.h"
+
+namespace libsw602
+{
+
+SW602ParserState::SW602ParserState(SW602ParserState::Type type, boost::shared_ptr<librevenge::RVNGInputStream> input) :
+ m_type(type), m_kind(Software602Document::KIND_TEXT), m_version(0), m_input(input), m_pageSpan(),
+ m_graphicListener(), m_listManager(), m_spreadsheetListener(), m_textListener(), m_asciiFile(input)
+{
+ m_listManager.reset(new SW602ListManager);
+}
+
+SW602ParserState::~SW602ParserState()
+{
+ if (getMainListener()) try
+ {
+ /* must never happen, only sanity check....
+
+ Ie. the parser which creates a listener, must delete it.
+ */
+ SW602_DEBUG_MSG(("SW602ParserState::~SW602ParserState: the listener is NOT closed, call enddocument without any subdoc\n"));
+ getMainListener()->endDocument(false);
+ }
+ catch (const libsw602::ParseException &)
+ {
+ SW602_DEBUG_MSG(("SW602ParserState::~SW602ParserState: endDocument FAILS\n"));
+ /* must never happen too...
+
+ Ie. the different parsers are responsable to create enough pages,
+ if we have exception here, this will indicate a second error in code
+ */
+ }
+}
+
+SW602ListenerPtr SW602ParserState::getMainListener()
+{
+ switch (m_type)
+ {
+ case Graphic:
+ return m_graphicListener;
+ case Spreadsheet:
+ return m_spreadsheetListener;
+ case Text:
+ return m_textListener;
+ default:
+ SW602_DEBUG_MSG(("SW602ParserState:::getMainListener unexpected document type\n"));
+ }
+ return SW602ListenerPtr();
+}
+
+SW602Parser::SW602Parser(SW602ParserState::Type type, boost::shared_ptr<librevenge::RVNGInputStream> input):
+ m_parserState(), m_asciiName("")
+{
+ m_parserState.reset(new SW602ParserState(type, input));
+}
+
+SW602Parser::~SW602Parser()
+{
+}
+
+SW602ListenerPtr SW602Parser::getMainListener()
+{
+ return m_parserState->getMainListener();
+}
+
+void SW602Parser::setGraphicListener(SW602GraphicListenerPtr &listener)
+{
+ m_parserState->m_graphicListener=listener;
+}
+
+void SW602Parser::resetGraphicListener()
+{
+ if (getGraphicListener()) getGraphicListener()->endDocument();
+ m_parserState->m_graphicListener.reset();
+}
+
+void SW602Parser::setSpreadsheetListener(SW602SpreadsheetListenerPtr &listener)
+{
+ m_parserState->m_spreadsheetListener=listener;
+}
+
+void SW602Parser::resetSpreadsheetListener()
+{
+ if (getSpreadsheetListener()) getSpreadsheetListener()->endDocument();
+ m_parserState->m_spreadsheetListener.reset();
+}
+
+void SW602Parser::setTextListener(SW602TextListenerPtr &listener)
+{
+ m_parserState->m_textListener=listener;
+}
+
+void SW602Parser::resetTextListener()
+{
+ if (getTextListener()) getTextListener()->endDocument();
+ m_parserState->m_textListener.reset();
+}
+
+}
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602Parser.h b/src/lib/SW602Parser.h
new file mode 100644
index 0000000..dd20d41
--- /dev/null
+++ b/src/lib/SW602Parser.h
@@ -0,0 +1,229 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SW602_PARSER_H
+#define INCLUDED_SW602_PARSER_H
+
+#include <ostream>
+#include <string>
+#include <vector>
+
+#include <libsw602/libsw602.h>
+
+#include "SW602Debug.h"
+#include "SW602Entry.h"
+#include "SW602PageSpan.h"
+
+namespace libsw602
+{
+
+/** a class to define the parser state */
+class SW602ParserState
+{
+public:
+ //! the parser state type
+ enum Type { Graphic, Spreadsheet, Text };
+ //! Constructor
+ SW602ParserState(Type type, boost::shared_ptr<librevenge::RVNGInputStream> input);
+ //! destructor
+ ~SW602ParserState();
+ //! returns the main listener
+ SW602ListenerPtr getMainListener();
+ //! the state type
+ Type m_type;
+ //! the document kind
+ Software602Document::Kind m_kind;
+ //! the actual version
+ int m_version;
+ //! the input
+ boost::shared_ptr<librevenge::RVNGInputStream> m_input;
+ //! the actual document size
+ SW602PageSpan m_pageSpan;
+
+ //! the graphic listener
+ SW602GraphicListenerPtr m_graphicListener;
+ //! the list manager
+ SW602ListManagerPtr m_listManager;
+ //! the spreadsheet listener
+ SW602SpreadsheetListenerPtr m_spreadsheetListener;
+ //! the text listener
+ SW602TextListenerPtr m_textListener;
+
+ //! the debug file
+ libsw602::DebugFile m_asciiFile;
+
+private:
+ SW602ParserState(SW602ParserState const &orig);
+ SW602ParserState &operator=(SW602ParserState const &orig);
+};
+
+/** virtual class which defines the ancestor of all main zone parser */
+class SW602Parser
+{
+public:
+ //! virtual destructor
+ virtual ~SW602Parser();
+
+ //! returns the version
+ int version() const
+ {
+ return m_parserState->m_version;
+ }
+ //! returns the parser state
+ SW602ParserStatePtr getParserState()
+ {
+ return m_parserState;
+ }
+ //! returns the actual input
+ const boost::shared_ptr<librevenge::RVNGInputStream> &getInput()
+ {
+ return m_parserState->m_input;
+ }
+ //! returns the main listener
+ SW602ListenerPtr getMainListener();
+ //! returns the graphic listener
+ SW602GraphicListenerPtr &getGraphicListener()
+ {
+ return m_parserState->m_graphicListener;
+ }
+ //! returns the spreadsheet listener
+ SW602SpreadsheetListenerPtr &getSpreadsheetListener()
+ {
+ return m_parserState->m_spreadsheetListener;
+ }
+ //! returns the text listener
+ SW602TextListenerPtr &getTextListener()
+ {
+ return m_parserState->m_textListener;
+ }
+ //! returns the actual page dimension
+ SW602PageSpan const &getPageSpan() const
+ {
+ return m_parserState->m_pageSpan;
+ }
+ //! returns the actual page dimension
+ SW602PageSpan &getPageSpan()
+ {
+ return m_parserState->m_pageSpan;
+ }
+ //! returns the form length
+ double getFormLength() const
+ {
+ return m_parserState->m_pageSpan.getFormLength();
+ }
+ //! returns the form width
+ double getFormWidth() const
+ {
+ return m_parserState->m_pageSpan.getFormWidth();
+ }
+ //! returns the page length (form length without margin )
+ double getPageLength() const
+ {
+ return m_parserState->m_pageSpan.getPageLength();
+ }
+ //! returns the page width (form width without margin )
+ double getPageWidth() const
+ {
+ return m_parserState->m_pageSpan.getPageWidth();
+ }
+ //! a DebugFile used to write what we recognize when we parse the document
+ libsw602::DebugFile &ascii()
+ {
+ return m_parserState->m_asciiFile;
+ }
+protected:
+ //! constructor (protected)
+ SW602Parser(SW602ParserState::Type type, boost::shared_ptr<librevenge::RVNGInputStream> input);
+ //! constructor using a state
+ explicit SW602Parser(SW602ParserStatePtr state) : m_parserState(state), m_asciiName("") { }
+
+ //! sets the document's version
+ void setVersion(int vers)
+ {
+ m_parserState->m_version = vers;
+ }
+ //! sets the graphic listener
+ void setGraphicListener(SW602GraphicListenerPtr &listener);
+ //! resets the listener
+ void resetGraphicListener();
+ //! sets the spreadsheet listener
+ void setSpreadsheetListener(SW602SpreadsheetListenerPtr &listener);
+ //! resets the listener
+ void resetSpreadsheetListener();
+ //! sets the text listener
+ void setTextListener(SW602TextListenerPtr &listener);
+ //! resets the listener
+ void resetTextListener();
+ //! Debugging: change the default ascii file
+ void setAsciiName(char const *name)
+ {
+ m_asciiName = name;
+ }
+ //! return the ascii file name
+ std::string const &asciiName() const
+ {
+ return m_asciiName;
+ }
+
+private:
+ //! private copy constructor: forbidden
+ explicit SW602Parser(const SW602Parser &);
+ //! private operator=: forbidden
+ SW602Parser &operator=(const SW602Parser &);
+
+ //! the parser state
+ SW602ParserStatePtr m_parserState;
+ //! the debug file name
+ std::string m_asciiName;
+};
+
+/** virtual class which defines the ancestor of all graphic zone parser */
+class SW602GraphicParser : public SW602Parser
+{
+public:
+ //! virtual function used to parse the input
+ virtual void parse(librevenge::RVNGDrawingInterface *documentInterface) = 0;
+protected:
+ //! constructor (protected)
+ SW602GraphicParser(boost::shared_ptr<librevenge::RVNGInputStream> input) : SW602Parser(SW602ParserState::Graphic, input) {}
+ //! constructor using a state
+ explicit SW602GraphicParser(SW602ParserStatePtr state) : SW602Parser(state) {}
+};
+
+/** virtual class which defines the ancestor of all spreadsheet zone parser */
+class SW602SpreadsheetParser : public SW602Parser
+{
+public:
+ //! virtual function used to parse the input
+ virtual void parse(librevenge::RVNGSpreadsheetInterface *documentInterface) = 0;
+protected:
+ //! constructor (protected)
+ SW602SpreadsheetParser(boost::shared_ptr<librevenge::RVNGInputStream> input) : SW602Parser(SW602ParserState::Spreadsheet, input) {}
+ //! constructor using a state
+ explicit SW602SpreadsheetParser(SW602ParserStatePtr state) : SW602Parser(state) {}
+};
+
+/** virtual class which defines the ancestor of all text zone parser */
+class SW602TextParser : public SW602Parser
+{
+public:
+ //! virtual function used to parse the input
+ virtual void parse(librevenge::RVNGTextInterface *documentInterface) = 0;
+protected:
+ //! constructor (protected)
+ SW602TextParser(boost::shared_ptr<librevenge::RVNGInputStream> input) : SW602Parser(SW602ParserState::Text, input) {}
+ //! constructor using a state
+ explicit SW602TextParser(SW602ParserStatePtr state) : SW602Parser(state) {}
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602Position.h b/src/lib/SW602Position.h
new file mode 100644
index 0000000..bdc717e
--- /dev/null
+++ b/src/lib/SW602Position.h
@@ -0,0 +1,282 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SW602_POSITION_H
+#define INCLUDED_SW602_POSITION_H
+
+#include <ostream>
+
+#include <librevenge/librevenge.h>
+
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+/** Class to define the position of an object (textbox, picture, ..) in the document
+ *
+ * Stores the page, object position, object size, anchor, wrapping, ...
+ */
+class SW602Position
+{
+public:
+ //! a list of enum used to defined the anchor
+ enum AnchorTo { Char, CharBaseLine, Frame, Paragraph, Page, Cell, Unknown };
+ //! an enum used to define the wrapping: none, ...
+ enum Wrapping { WNone, WBackground, WDynamic, WForeground, WParallel, WRunThrough };
+ //! an enum used to define the relative X position
+ enum XPos { XRight, XLeft, XCenter, XFull };
+ //! an enum used to define the relative Y position
+ enum YPos { YTop, YBottom, YCenter, YFull };
+
+public:
+ //! constructor
+ SW602Position(SW602Vec2f const &orig=SW602Vec2f(), SW602Vec2f const &sz=SW602Vec2f(), librevenge::RVNGUnit theUnit=librevenge::RVNG_INCH):
+ m_anchorTo(Unknown), m_anchorCellName(""), m_xPos(XLeft), m_yPos(YTop), m_wrapping(WNone),
+ m_page(0), m_orig(orig), m_size(sz), m_naturalSize(), m_LTClip(), m_RBClip(), m_unit(theUnit), m_order(0) {}
+
+ virtual ~SW602Position() {}
+ //! operator<<
+ friend std::ostream &operator<<(std::ostream &o, SW602Position const &pos)
+ {
+ SW602Vec2f dest(pos.m_orig+pos.m_size);
+ o << "Pos=(" << pos.m_orig << ")x(" << dest << ")";
+ switch (pos.m_unit)
+ {
+ case librevenge::RVNG_INCH:
+ o << "(inch)";
+ break;
+ case librevenge::RVNG_POINT:
+ o << "(pt)";
+ break;
+ case librevenge::RVNG_TWIP:
+ o << "(tw)";
+ break;
+ case librevenge::RVNG_PERCENT:
+ case librevenge::RVNG_GENERIC:
+ case librevenge::RVNG_UNIT_ERROR:
+ default:
+ break;
+ }
+ if (pos.page()>0) o << ", page=" << pos.page();
+ return o;
+ }
+ //! basic operator==
+ bool operator==(SW602Position const &f) const
+ {
+ return cmp(f) == 0;
+ }
+ //! basic operator!=
+ bool operator!=(SW602Position const &f) const
+ {
+ return cmp(f) != 0;
+ }
+ //! basic operator<
+ bool operator<(SW602Position const &f) const
+ {
+ return cmp(f) < 0;
+ }
+
+ //! returns the frame page
+ int page() const
+ {
+ return m_page;
+ }
+ //! return the frame origin
+ SW602Vec2f const &origin() const
+ {
+ return m_orig;
+ }
+ //! returns the frame size
+ SW602Vec2f const &size() const
+ {
+ return m_size;
+ }
+ //! returns the natural size (if known)
+ SW602Vec2f const &naturalSize() const
+ {
+ return m_naturalSize;
+ }
+ //! returns the left top clipping
+ SW602Vec2f const &leftTopClipping() const
+ {
+ return m_LTClip;
+ }
+ //! returns the right bottom clipping
+ SW602Vec2f const &rightBottomClipping() const
+ {
+ return m_RBClip;
+ }
+ //! returns the unit
+ librevenge::RVNGUnit unit() const
+ {
+ return m_unit;
+ }
+ static float getScaleFactor(librevenge::RVNGUnit orig, librevenge::RVNGUnit dest)
+ {
+ float actSc = 1.0, newSc = 1.0;
+ switch (orig)
+ {
+ case librevenge::RVNG_TWIP:
+ break;
+ case librevenge::RVNG_POINT:
+ actSc=20;
+ break;
+ case librevenge::RVNG_INCH:
+ actSc = 1440;
+ break;
+ case librevenge::RVNG_PERCENT:
+ case librevenge::RVNG_GENERIC:
+ case librevenge::RVNG_UNIT_ERROR:
+ default:
+ SW602_DEBUG_MSG(("SW602Position::getScaleFactor %d unit must not appear\n", int(orig)));
+ }
+ switch (dest)
+ {
+ case librevenge::RVNG_TWIP:
+ break;
+ case librevenge::RVNG_POINT:
+ newSc=20;
+ break;
+ case librevenge::RVNG_INCH:
+ newSc = 1440;
+ break;
+ case librevenge::RVNG_PERCENT:
+ case librevenge::RVNG_GENERIC:
+ case librevenge::RVNG_UNIT_ERROR:
+ default:
+ SW602_DEBUG_MSG(("SW602Position::getScaleFactor %d unit must not appear\n", int(dest)));
+ }
+ return actSc/newSc;
+ }
+ //! returns a float which can be used to scale some data in object unit
+ float getInvUnitScale(librevenge::RVNGUnit fromUnit) const
+ {
+ return getScaleFactor(fromUnit, m_unit);
+ }
+
+ //! sets the page
+ void setPage(int pg) const
+ {
+ const_cast<SW602Position *>(this)->m_page = pg;
+ }
+ //! sets the frame origin
+ void setOrigin(SW602Vec2f const &orig)
+ {
+ m_orig = orig;
+ }
+ //! sets the frame size
+ void setSize(SW602Vec2f const &sz)
+ {
+ m_size = sz;
+ }
+ //! sets the natural size (if known)
+ void setNaturalSize(SW602Vec2f const &naturalSz)
+ {
+ m_naturalSize = naturalSz;
+ }
+ //! sets the dimension unit
+ void setUnit(librevenge::RVNGUnit newUnit)
+ {
+ m_unit = newUnit;
+ }
+ //! sets/resets the page and the origin
+ void setPagePos(int pg, SW602Vec2f const &newOrig) const
+ {
+ const_cast<SW602Position *>(this)->m_page = pg;
+ const_cast<SW602Position *>(this)->m_orig = newOrig;
+ }
+
+ //! sets the relative position
+ void setRelativePosition(AnchorTo anchor, XPos X = XLeft, YPos Y=YTop)
+ {
+ m_anchorTo = anchor;
+ m_xPos = X;
+ m_yPos = Y;
+ }
+ //! sets the anchor to a cell position
+ void setAnchorToCell(librevenge::RVNGString const &cellName)
+ {
+ m_anchorTo = Cell;
+ m_xPos = XLeft;
+ m_yPos = YTop;
+ m_anchorCellName=cellName;
+ }
+ //! sets the clipping position
+ void setClippingPosition(SW602Vec2f lTop, SW602Vec2f rBottom)
+ {
+ m_LTClip = lTop;
+ m_RBClip = rBottom;
+ }
+
+ //! returns background/foward order
+ int order() const
+ {
+ return m_order;
+ }
+ //! set background/foward order
+ void setOrder(int ord) const
+ {
+ m_order = ord;
+ }
+
+ //! anchor position
+ AnchorTo m_anchorTo;
+ //! the anchor cell name
+ librevenge::RVNGString m_anchorCellName;
+ //! X relative position
+ XPos m_xPos;
+ //! Y relative position
+ YPos m_yPos;
+ //! Wrapping
+ Wrapping m_wrapping;
+
+protected:
+ //! basic function to compare two positions
+ int cmp(SW602Position const &f) const
+ {
+ int diff = int(m_anchorTo) - int(f.m_anchorTo);
+ if (diff) return diff < 0 ? -1 : 1;
+ diff = int(m_xPos) - int(f.m_xPos);
+ if (diff) return diff < 0 ? -1 : 1;
+ diff = int(m_yPos) - int(f.m_yPos);
+ if (diff) return diff < 0 ? -1 : 1;
+ diff = page() - f.page();
+ if (diff) return diff < 0 ? -1 : 1;
+ diff = int(m_unit) - int(f.m_unit);
+ if (diff) return diff < 0 ? -1 : 1;
+ diff = m_orig.cmpY(f.m_orig);
+ if (diff) return diff;
+ diff = m_size.cmpY(f.m_size);
+ if (diff) return diff;
+ diff = m_naturalSize.cmpY(f.m_naturalSize);
+ if (diff) return diff;
+ diff = m_LTClip.cmpY(f.m_LTClip);
+ if (diff) return diff;
+ diff = m_RBClip.cmpY(f.m_RBClip);
+ if (diff) return diff;
+
+ return 0;
+ }
+
+ //! the page
+ int m_page;
+ SW602Vec2f m_orig /** the origin position in a page */, m_size /* the size of the data*/, m_naturalSize /** the natural size of the data (if known) */;
+ SW602Vec2f m_LTClip /** the left top clip position */, m_RBClip /* the right bottom clip position */;
+ //! the unit used in \a orig, in \a m_size and in \a m_LTClip , .... Default: in inches
+ librevenge::RVNGUnit m_unit;
+ //! background/foward order
+ mutable int m_order;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602Printer.cpp b/src/lib/SW602Printer.cpp
new file mode 100644
index 0000000..142155b
--- /dev/null
+++ b/src/lib/SW602Printer.cpp
@@ -0,0 +1,225 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "SW602Printer.h"
+
+#include <iostream>
+
+#include "SW602Types.h"
+#include "libsw602_utils.h"
+
+namespace libsw602
+{
+//------------------------------------------------------------
+//
+// The printer information
+// see http://www.mactech.com/articles/mactech/Vol.01/01.09/AllAboutPrinting/index.html
+//
+//------------------------------------------------------------
+
+
+//
+// PrinterRect : a rectangle
+//
+bool PrinterRect::read(librevenge::RVNGInputStream &input, SW602Vec2i const &res)
+{
+ for (int st = 0; st < 2; st++)
+ {
+ int y = int(readU16(input));
+ y = int(float(y)*72./float(res.y()));
+ int x = int(readU16(input));
+ x = int(float(x)*72./float(res.x()));
+ m_pos[st].set(x,y);
+ }
+
+ if (input.isEnd()) return false;
+
+ if (m_pos[0].x() > m_pos[1].x() || m_pos[0].y() > m_pos[1].y())
+ return false;
+ return true;
+}
+
+//! Internal: structure used to keep a rectangle with its resolution
+struct PrinterRectResolution
+{
+ PrinterRectResolution() : m_rect(), m_resolution(), m_iDev(-1) {}
+ //! page dimension
+ PrinterRect page() const
+ {
+ return m_rect;
+ }
+ //! resolution
+ SW602Vec2i const &resolution() const
+ {
+ return m_resolution;
+ }
+
+ //! operator <<
+ friend std::ostream &operator<< (std::ostream &o, PrinterRectResolution const &r)
+ {
+ o << r.m_rect << ":" << r.m_resolution;
+ return o;
+ }
+ //! reads the data from file
+ bool read(librevenge::RVNGInputStream &input)
+ {
+ m_iDev = int(readU16(input));
+ int y = int(readU16(input));
+ int x = int(readU16(input));
+ if (x <= 0 || y <= 0) return false;
+ m_resolution.set(x,y);
+ return m_rect.read(input, m_resolution);
+ }
+
+protected:
+ //! returns the main rectangle
+ PrinterRect m_rect;
+ //! returns the resolution
+ SW602Vec2i m_resolution;
+ //! a field which is reserved
+ int m_iDev;
+};
+
+//! Internal: structure used to keep the printer style information
+struct PrinterStyle
+{
+ /** operator<<
+
+ \note print nothing*/
+ friend std::ostream &operator<< (std::ostream &o, PrinterStyle const &)
+ {
+ return o;
+ }
+ //! reads data from file
+ bool read(librevenge::RVNGInputStream &input)
+ {
+ m_wDev = (int) readU16(input);
+ m_pageWidth = (int) readU16(input);
+ m_pageHeight = (int) readU16(input);
+ if (m_pageWidth < 0 || m_pageHeight < 0) return false;
+ m_port = (int) readU8(input);
+ m_feed = (int) readU8(input);
+ if (input.isEnd()) return false;
+ return true;
+ }
+
+protected:
+ int m_wDev /** used internally */;
+ int m_feed /** paper type: cut, fanfold, mechcut or other */;
+ int m_pageHeight /** paper height */, m_pageWidth /** paper width*/, m_port /** printer or modem port */;
+};
+
+//! Internal: structure used to keep a printer job
+struct PrinterJob
+{
+ //! operator<<
+ friend std::ostream &operator<< (std::ostream &o, PrinterJob const &r)
+ {
+ o << "fP=" << r.m_firstPage << ", lP=" << r.m_lastPage << ", copies=" << r.m_copies;
+ if (r.m_fileVol || r.m_fileVers) o << ", fVol=" << r.m_fileVol << ", fVers=" << r.m_fileVers;
+ return o;
+ }
+ //! read data from file
+ bool read(librevenge::RVNGInputStream &input)
+ {
+ m_firstPage = (int) readU16(input);
+ m_lastPage = (int) readU16(input);
+ m_copies = (int) readU16(input);
+ m_jobDocLoop = (int) readU8(input);
+ m_fromUser = (int) readU8(input);
+ // skip pIdleProc
+ if (input.seek(4, librevenge::RVNG_SEEK_CUR) != 0 || input.isEnd()) return false;
+ // skip pFileName
+ if (input.seek(4, librevenge::RVNG_SEEK_CUR) != 0 || input.isEnd()) return false;
+ m_fileVol = (int) readU16(input);
+ m_fileVers = (int) readU8(input);
+ return true;
+ }
+
+protected:
+ int m_firstPage /** first page*/, m_lastPage/** last page*/, m_copies /** number of copies */;
+ int m_jobDocLoop/** printing method: draft or defered */;
+ int m_fileVol /** volume reference number*/ , m_fileVers /** version of spool file */;
+ //! reserved
+ int m_fromUser;
+};
+
+//
+// PrinterInfo storage class
+//
+struct PrinterInfoData
+{
+ PrinterInfoData(): m_info(), m_paper(), m_feed(), m_info2(), m_job(), m_version(-1) {}
+
+ //! printer information
+ PrinterRectResolution m_info;
+ //! paper
+ PrinterRect m_paper;
+ //! printer style
+ PrinterStyle m_feed;
+ //! printer information
+ PrinterRectResolution m_info2;
+ //! jobs
+ PrinterJob m_job;
+ //! reserved
+ int m_version;
+};
+
+//
+// PrinterInfo : ie. apple TPrint
+//
+PrinterInfo::PrinterInfo() : m_data(new PrinterInfoData) {}
+PrinterInfo::~PrinterInfo()
+{
+}
+
+PrinterRect PrinterInfo::page() const
+{
+ return m_data->m_info.page();
+}
+PrinterRect PrinterInfo::paper() const
+{
+ return m_data->m_paper;
+}
+
+//! operator<< for a PrinterInfo
+std::ostream &operator<< (std::ostream &o, PrinterInfo const &r)
+{
+ o << "page = " << r.m_data->m_info << ", paper = " << r.m_data->m_paper
+ << ", infoPt: " << r.m_data->m_info2 << ", jobs: [" << r.m_data->m_job << "]";
+ return o;
+}
+
+bool PrinterInfo::read(librevenge::RVNGInputStream &input)
+{
+ m_data->m_version = (int) readU16(input);
+ if (!m_data->m_info.read(input)) return false;
+ if (!m_data->m_paper.read(input, m_data->m_info.resolution())) return false;
+ if (!m_data->m_feed.read(input)) return false;
+ long pos = input.tell();
+ if (!m_data->m_info2.read(input))
+ {
+ // can be left unfilled, so as we do not use the result, ...
+ input.seek(pos+14, librevenge::RVNG_SEEK_SET);
+ if (input.tell() != pos+14) return false;
+ }
+ // skip unknown structure prXInfo
+ if (input.seek(16, librevenge::RVNG_SEEK_CUR) != 0 || input.isEnd()) return false;
+
+ if (!m_data->m_job.read(input)) return false;
+ readU8(input);
+
+ // skip printX 19 short + 2 align
+ pos = input.tell();
+ if (input.seek(19*2,librevenge::RVNG_SEEK_CUR) != 0 || input.tell()!=pos+19*2) return false;
+ return true;
+}
+}
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602Printer.h b/src/lib/SW602Printer.h
new file mode 100644
index 0000000..64778b2
--- /dev/null
+++ b/src/lib/SW602Printer.h
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/* This header contains code specific to a mac file needed to read
+ * TPrint structure (ie. the structure which keeps the printer parameters)
+ * see http://developer.apple.com/documentation/Mac/QuickDraw/QuickDraw-411.html
+ * or http://www.mactech.com/articles/mactech/Vol.01/01.09/AllAboutPrinting/index.html
+ */
+
+#ifndef INCLUDED_SW602_PRINTER_H
+#define INCLUDED_SW602_PRINTER_H
+
+#include <ostream>
+
+#include <librevenge/librevenge.h>
+
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+/** \struct PrinterInfoData
+ \brief internal structure used to keep TPrint content */
+struct PrinterInfoData;
+
+//! the Apple© rectangle : Rect
+struct PrinterRect
+{
+ //! returns the size
+ SW602Vec2i size() const
+ {
+ return m_pos[1]-m_pos[0];
+ }
+ //! returns the position ( 0: leftTop, 1:rightBottom )
+ SW602Vec2i pos(int i) const
+ {
+ return m_pos[i];
+ }
+
+ //! operator <<
+ friend std::ostream &operator<< (std::ostream &o, PrinterRect const &r)
+ {
+ o << "[" << r.m_pos[0] << " " << r.m_pos[1] << "]";
+ return o;
+ }
+
+ //! read value in a file, knowing the resolution
+ bool read(librevenge::RVNGInputStream &input, SW602Vec2i const &res);
+
+protected:
+ //! the LT and RB positions
+ SW602Vec2i m_pos[2];
+};
+
+//! the Apple© printer information : TPrint
+struct PrinterInfo
+{
+ //! constructor
+ PrinterInfo();
+ //! destructor
+ ~PrinterInfo();
+
+ //! returns the page rectangle
+ PrinterRect page() const;
+ //! returns the paper rectangle
+ PrinterRect paper() const;
+
+ friend std::ostream &operator<< (std::ostream &o, PrinterInfo const &r);
+
+ //! reads the struture in a file
+ bool read(librevenge::RVNGInputStream &input);
+
+protected:
+ //! internal data
+ boost::shared_ptr<PrinterInfoData> m_data;
+private:
+ PrinterInfo(PrinterInfo const &orig);
+ PrinterInfo &operator=(PrinterInfo const &orig);
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602PropertyHandler.cpp b/src/lib/SW602PropertyHandler.cpp
new file mode 100644
index 0000000..513de7f
--- /dev/null
+++ b/src/lib/SW602PropertyHandler.cpp
@@ -0,0 +1,381 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/* This header contains code specific to a small picture
+ */
+
+#include "SW602PropertyHandler.h"
+
+#include <iostream>
+#include <sstream>
+#include <string.h>
+#include <stack>
+
+#include <librevenge/librevenge.h>
+#include <librevenge-stream/librevenge-stream.h>
+
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+////////////////////////////////////////////////////
+//
+// SW602PropertyHandlerEncoder
+//
+////////////////////////////////////////////////////
+SW602PropertyHandlerEncoder::SW602PropertyHandlerEncoder()
+ : m_f(std::ios::in | std::ios::out | std::ios::binary)
+{
+}
+
+void SW602PropertyHandlerEncoder::insertElement(const char *psName)
+{
+ m_f << 'E';
+ writeString(librevenge::RVNGString(psName));
+}
+
+void SW602PropertyHandlerEncoder::insertElement
+(const char *psName, const librevenge::RVNGPropertyList &xPropList)
+{
+ m_f << 'S';
+ writeString(librevenge::RVNGString(psName));
+ writePropertyList(xPropList);
+}
+
+void SW602PropertyHandlerEncoder::characters(librevenge::RVNGString const &sCharacters)
+{
+ if (sCharacters.len()==0) return;
+ m_f << 'T';
+ writeString(sCharacters);
+}
+
+void SW602PropertyHandlerEncoder::writeString(const librevenge::RVNGString &string)
+{
+ unsigned long sz = string.size()+1;
+ writeLong((long) sz);
+ m_f.write(string.cstr(), (int) sz);
+}
+
+void SW602PropertyHandlerEncoder::writeLong(long val)
+{
+ int32_t value=(int32_t) val;
+ unsigned char const allValue[]= {(unsigned char)(value&0xFF), (unsigned char)((value>>8)&0xFF), (unsigned char)((value>>16)&0xFF), (unsigned char)((value>>24)&0xFF)};
+ m_f.write((const char *)allValue, 4);
+}
+
+void SW602PropertyHandlerEncoder::writeProperty(const char *key, const librevenge::RVNGProperty &prop)
+{
+ if (!key)
+ {
+ SW602_DEBUG_MSG(("SW602PropertyHandlerEncoder::writeProperty: key is NULL\n"));
+ return;
+ }
+ writeString(librevenge::RVNGString(key));
+ writeString(prop.getStr());
+}
+
+void SW602PropertyHandlerEncoder::writePropertyList(const librevenge::RVNGPropertyList &xPropList)
+{
+ librevenge::RVNGPropertyList::Iter i(xPropList);
+ long numElt = 0;
+ for (i.rewind(); i.next();) numElt++;
+ writeLong(numElt);
+ for (i.rewind(); i.next();)
+ {
+ librevenge::RVNGPropertyListVector const *child=xPropList.child(i.key());
+ if (!child)
+ {
+ m_f << 'p';
+ writeProperty(i.key(),*i());
+ continue;
+ }
+ m_f << 'v';
+ writeString(librevenge::RVNGString(i.key()));
+ writePropertyListVector(*child);
+ }
+}
+
+void SW602PropertyHandlerEncoder::writePropertyListVector(const librevenge::RVNGPropertyListVector &vect)
+{
+ writeLong((long)vect.count());
+ for (unsigned long i=0; i < vect.count(); i++)
+ writePropertyList(vect[i]);
+}
+
+bool SW602PropertyHandlerEncoder::getData(librevenge::RVNGBinaryData &data)
+{
+ data.clear();
+ std::string d=m_f.str();
+ if (d.length() == 0) return false;
+ data.append((const unsigned char *)d.c_str(), d.length());
+ return true;
+}
+
+/* \brief Internal: the property decoder
+ *
+ * \note see SW602PropertyHandlerEncoder for the format
+*/
+class SW602PropertyHandlerDecoder
+{
+public:
+ //! constructor given a SW602PropertyHandler
+ explicit SW602PropertyHandlerDecoder(SW602PropertyHandler *hdl=0L):m_handler(hdl) {}
+
+ //! tries to read the data
+ bool readData(librevenge::RVNGBinaryData const &encoded)
+ {
+ try
+ {
+ librevenge::RVNGInputStream *inp = const_cast<librevenge::RVNGInputStream *>(encoded.getDataStream());
+ if (!inp) return false;
+
+ while (!inp->isEnd())
+ {
+ unsigned const char *c;
+ unsigned long numRead;
+
+ c = inp->read(1,numRead);
+ if (!c || numRead != 1)
+ {
+ SW602_DEBUG_MSG(("SW602PropertyHandlerDecoder: can not read data type \n"));
+ return false;
+ }
+ switch (*c)
+ {
+ case 'E':
+ if (!readInsertElement(*inp)) return false;
+ break;
+ case 'S':
+ if (!readInsertElementWithList(*inp)) return false;
+ break;
+ case 'T':
+ if (!readCharacters(*inp)) return false;
+ break;
+ default:
+ SW602_DEBUG_MSG(("SW602PropertyHandlerDecoder: unknown type='%c' \n", *c));
+ return false;
+ }
+ }
+ }
+ catch (...)
+ {
+ return false;
+ }
+ return true;
+ }
+
+protected:
+ //! reads an simple element
+ bool readInsertElement(librevenge::RVNGInputStream &input)
+ {
+ librevenge::RVNGString s;
+ if (!readString(input, s)) return false;
+
+ if (s.empty())
+ {
+ SW602_DEBUG_MSG(("SW602PropertyHandlerDecoder::readInsertElement find empty tag\n"));
+ return false;
+ }
+ if (m_handler) m_handler->insertElement(s.cstr());
+ return true;
+ }
+
+ //! reads an element with a property list
+ bool readInsertElementWithList(librevenge::RVNGInputStream &input)
+ {
+ librevenge::RVNGString s;
+ if (!readString(input, s)) return false;
+
+ if (s.empty())
+ {
+ SW602_DEBUG_MSG(("SW602PropertyHandlerDecoder::readInsertElementWithProperty: find empty tag\n"));
+ return false;
+ }
+ librevenge::RVNGPropertyList lists;
+ if (!readPropertyList(input, lists))
+ {
+ SW602_DEBUG_MSG(("SW602PropertyHandlerDecoder::readInsertElementWithProperty: can not read propertyList for tag %s\n",
+ s.cstr()));
+ return false;
+ }
+
+ if (m_handler) m_handler->insertElement(s.cstr(), lists);
+ return true;
+ }
+
+ //! reads a set of characters
+ bool readCharacters(librevenge::RVNGInputStream &input)
+ {
+ librevenge::RVNGString s;
+ if (!readString(input, s)) return false;
+ if (!s.size()) return true;
+ if (m_handler) m_handler->characters(s);
+ return true;
+ }
+
+ //
+ // low level
+ //
+
+ //! low level: reads a property vector: number of properties list followed by list of properties list
+ bool readPropertyListVector(librevenge::RVNGInputStream &input, librevenge::RVNGPropertyListVector &vect)
+ {
+ long numElt;
+ if (!readLong(input, numElt)) return false;
+
+ if (numElt < 0)
+ {
+ SW602_DEBUG_MSG(("SW602PropertyHandlerDecoder::readPropertyListVector: can not read numElt=%ld\n",
+ numElt));
+ return false;
+ }
+ for (long i = 0; i < numElt; i++)
+ {
+ librevenge::RVNGPropertyList lists;
+ if (readPropertyList(input, lists))
+ {
+ vect.append(lists);
+ continue;
+ }
+ SW602_DEBUG_MSG(("SW602PropertyHandlerDecoder::readPropertyListVector: can not read property list %ld\n", i));
+ return false;
+ }
+ return true;
+ }
+
+ //! low level: reads a property list: number of properties followed by list of properties
+ bool readPropertyList(librevenge::RVNGInputStream &input, librevenge::RVNGPropertyList &lists)
+ {
+ long numElt;
+ if (!readLong(input, numElt)) return false;
+
+ if (numElt < 0)
+ {
+ SW602_DEBUG_MSG(("SW602PropertyHandlerDecoder::readPropertyList: can not read numElt=%ld\n",
+ numElt));
+ return false;
+ }
+ for (long i = 0; i < numElt; i++)
+ {
+ unsigned const char *c;
+ unsigned long numRead;
+ c = input.read(1,numRead);
+ if (!c || numRead != 1)
+ {
+ SW602_DEBUG_MSG(("SW602PropertyHandlerDecoder:readPropertyList can not read data type for child %ld\n", i));
+ return false;
+ }
+ switch (*c)
+ {
+ case 'p':
+ if (readProperty(input, lists)) break;
+ SW602_DEBUG_MSG(("SW602PropertyHandlerDecoder::readPropertyList: can not read property %ld\n", i));
+ return false;
+ case 'v':
+ {
+ librevenge::RVNGString key;
+ librevenge::RVNGPropertyListVector vect;
+ if (!readString(input, key) || key.empty() || !readPropertyListVector(input, vect))
+ {
+ SW602_DEBUG_MSG(("SW602PropertyHandlerDecoder::readPropertyList: can not read propertyVector for child %ld\n", i));
+ return false;
+ }
+ lists.insert(key.cstr(),vect);
+ break;
+ }
+ default:
+ SW602_DEBUG_MSG(("SW602PropertyHandlerDecoder:readPropertyList find unknown type %c for child %ld\n", (char) *c, i));
+ return false;
+ }
+ }
+ return true;
+ }
+
+ //! low level: reads a property and its value, adds it to \a list
+ bool readProperty(librevenge::RVNGInputStream &input, librevenge::RVNGPropertyList &list)
+ {
+ librevenge::RVNGString key, val;
+ if (!readString(input, key)) return false;
+ if (!readString(input, val)) return false;
+
+ list.insert(key.cstr(), val);
+ librevenge::RVNGProperty const *prop=list[key.cstr()];
+ if (!prop) return true;
+ librevenge::RVNGUnit unit=prop->getUnit();
+ if (unit==librevenge::RVNG_POINT)
+ list.insert(key.cstr(), prop->getDouble()/72., librevenge::RVNG_INCH);
+ else if (unit==librevenge::RVNG_TWIP)
+ list.insert(key.cstr(), prop->getDouble()/1440., librevenge::RVNG_INCH);
+ return true;
+ }
+
+ //! low level: reads a string : size and string
+ bool readString(librevenge::RVNGInputStream &input, librevenge::RVNGString &s)
+ {
+ long numC = 0;
+ if (!readLong(input, numC)) return false;
+ if (numC==0)
+ {
+ s = librevenge::RVNGString("");
+ return true;
+ }
+ unsigned long numRead;
+ const unsigned char *dt = input.read((unsigned long)numC, numRead);
+ if (dt == 0L || numRead != (unsigned long) numC)
+ {
+ SW602_DEBUG_MSG(("SW602PropertyHandlerDecoder::readString: can not read a string\n"));
+ return false;
+ }
+ s = librevenge::RVNGString((const char *)dt);
+ return true;
+ }
+
+ //! low level: reads an long value
+ static bool readLong(librevenge::RVNGInputStream &input, long &val)
+ {
+ unsigned long numRead = 0;
+ const unsigned char *dt = input.read(4, numRead);
+ if (dt == 0L || numRead != 4)
+ {
+ SW602_DEBUG_MSG(("SW602PropertyHandlerDecoder::readLong: can not read long\n"));
+ return false;
+ }
+ val = long((dt[3]<<24)|(dt[2]<<16)|(dt[1]<<8)|dt[0]);
+ return true;
+ }
+private:
+ SW602PropertyHandlerDecoder(SW602PropertyHandlerDecoder const &orig);
+ SW602PropertyHandlerDecoder &operator=(SW602PropertyHandlerDecoder const &);
+
+protected:
+ //! the streamfile
+ SW602PropertyHandler *m_handler;
+};
+
+////////////////////////////////////////////////////
+//
+// SW602PropertyHandler
+//
+////////////////////////////////////////////////////
+bool SW602PropertyHandler::checkData(librevenge::RVNGBinaryData const &encoded)
+{
+ SW602PropertyHandlerDecoder decod;
+ return decod.readData(encoded);
+}
+
+bool SW602PropertyHandler::readData(librevenge::RVNGBinaryData const &encoded)
+{
+ SW602PropertyHandlerDecoder decod(this);
+ return decod.readData(encoded);
+}
+
+}
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602PropertyHandler.h b/src/lib/SW602PropertyHandler.h
new file mode 100644
index 0000000..4df6bac
--- /dev/null
+++ b/src/lib/SW602PropertyHandler.h
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SW602_PROPERTY_HANDLER
+#define INCLUDED_SW602_PROPERTY_HANDLER
+
+#include <ostream>
+#include <sstream>
+#include <string>
+
+#include <librevenge/librevenge.h>
+
+namespace libsw602
+{
+
+//! a generic property handler
+class SW602PropertyHandler
+{
+public:
+ //! constructor
+ SW602PropertyHandler() {}
+ //! destructor
+ virtual ~SW602PropertyHandler() {}
+
+ //! inserts a simple element
+ virtual void insertElement(const char *psName) = 0;
+ //! inserts an element ( given a property list )
+ virtual void insertElement(const char *psName, const librevenge::RVNGPropertyList &xPropList) = 0;
+ //! writes a list of characters
+ virtual void characters(librevenge::RVNGString const &sCharacters) = 0;
+
+ //! checks a encoded librevenge::RVNGBinaryData created by SW602PropertyHandlerEncoder
+ bool checkData(librevenge::RVNGBinaryData const &encoded);
+ //! reads a encoded librevenge::RVNGBinaryData created by SW602PropertyHandlerEncoder
+ bool readData(librevenge::RVNGBinaryData const &encoded);
+};
+
+/*! \brief write in librevenge::RVNGBinaryData a list of tags/and properties
+ *
+ * In order to be read by writerperfect, we must code document consisting in
+ * tag and propertyList in an intermediar format:
+ * - [string:s]: an int length(s) follow by the length(s) characters of string s
+ * - [property:p]: a string value p.getStr() ( for a basic property )
+ * - [propertyList:pList]: a int: \#pList followed by
+ * -+ 'p',pList[i].key(),pList[i] for a basic child
+ * -+ 'v',pList[i].key(),*(pList.child(pList[i].key())) for a vector child
+ * - [propertyListVector:v]: a int: \#v followed by v[0], v[1], ...
+ * - [binaryData:d]: a int32 d.size() followed by the data content
+ *
+ * - [insertElement:name]: char 'E', [string] name
+ * - [insertElement:name proplist:prop]: char 'S', [string] name, prop
+ * - [characters:s ]: char 'T', [string] s
+ * - if len(s)==0, we write nothing
+ * - the string is written as is (ie. we do not escaped any characters).
+*/
+class SW602PropertyHandlerEncoder
+{
+public:
+ //! constructor
+ SW602PropertyHandlerEncoder();
+
+ //! inserts an element
+ void insertElement(const char *psName);
+ //! inserts an element given a property list
+ void insertElement(const char *psName, const librevenge::RVNGPropertyList &xPropList);
+ //! writes a list of characters
+ void characters(librevenge::RVNGString const &sCharacters);
+ //! retrieves the data
+ bool getData(librevenge::RVNGBinaryData &data);
+
+protected:
+ //! adds a long value if f
+ void writeLong(long val);
+ //! adds a string: size and string
+ void writeString(const librevenge::RVNGString &name);
+ //! adds a property: a string key, a string corresponding to value
+ void writeProperty(const char *key, const librevenge::RVNGProperty &prop);
+ //! adds a property list: int \#prop followed by the different properties
+ void writePropertyList(const librevenge::RVNGPropertyList &prop);
+ //! adds a property vector: a int: \#vect followed by vect[0], vect[1], ...
+ void writePropertyListVector(const librevenge::RVNGPropertyListVector &vect);
+
+ //! the streamfile
+ std::stringstream m_f;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602Section.cpp b/src/lib/SW602Section.cpp
new file mode 100644
index 0000000..102dc4b
--- /dev/null
+++ b/src/lib/SW602Section.cpp
@@ -0,0 +1,139 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "SW602Section.h"
+
+#include <librevenge/librevenge.h>
+
+#include "SW602Position.h"
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+////////////////////////////////////////////////////////////
+// Columns
+////////////////////////////////////////////////////////////
+std::ostream &operator<<(std::ostream &o, SW602Section::Column const &col)
+{
+ if (col.m_width > 0) o << "w=" << col.m_width << ",";
+ static char const *(wh[4])= {"L", "R", "T", "B"};
+ for (int i = 0; i < 4; i++)
+ {
+ if (col.m_margins[i]>0)
+ o << "col" << wh[i] << "=" << col.m_margins[i] << ",";
+ }
+ return o;
+}
+
+bool SW602Section::Column::addTo(librevenge::RVNGPropertyList &propList) const
+{
+ // The "style:rel-width" is expressed in twips (1440 twips per inch) and includes the left and right Gutter
+ double factor = 1.0;
+ switch (m_widthUnit)
+ {
+ case librevenge::RVNG_POINT:
+ case librevenge::RVNG_INCH:
+ factor = SW602Position::getScaleFactor(m_widthUnit, librevenge::RVNG_TWIP);
+ case librevenge::RVNG_TWIP:
+ break;
+ case librevenge::RVNG_PERCENT:
+ case librevenge::RVNG_GENERIC:
+ case librevenge::RVNG_UNIT_ERROR:
+ default:
+ SW602_DEBUG_MSG(("SW602Section::Column::addTo: unknown unit\n"));
+ return false;
+ }
+ propList.insert("style:rel-width", m_width * factor, librevenge::RVNG_TWIP);
+ propList.insert("fo:start-indent", m_margins[libsw602::Left], librevenge::RVNG_INCH);
+ propList.insert("fo:end-indent", m_margins[libsw602::Right], librevenge::RVNG_INCH);
+ static bool first = true;
+ if (first && (m_margins[libsw602::Top]>0||m_margins[libsw602::Bottom]>0))
+ {
+ first=false;
+ SW602_DEBUG_MSG(("SW602Section::Column::addTo: sending before/after margins is not implemented\n"));
+ }
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// Section
+////////////////////////////////////////////////////////////
+std::ostream &operator<<(std::ostream &o, SW602Section const &sec)
+{
+ if (sec.m_width>0)
+ o << "width=" << sec.m_width << ",";
+ if (!sec.m_backgroundColor.isWhite())
+ o << "bColor=" << sec.m_backgroundColor << ",";
+ if (sec.m_balanceText)
+ o << "text[balance],";
+ for (size_t c=0; c < sec.m_columns.size(); c++)
+ o << "col" << c << "=[" << sec.m_columns[c] << "],";
+ if (sec.m_columnSeparator.m_style != SW602Border::None &&
+ sec.m_columnSeparator.m_width > 0)
+ o << "colSep=[" << sec.m_columnSeparator << "],";
+ return o;
+}
+
+void SW602Section::setColumns(int num, double width, librevenge::RVNGUnit widthUnit, double colSep)
+{
+ if (num<0)
+ {
+ SW602_DEBUG_MSG(("SW602Section::setColumns: called with negative number of column\n"));
+ num=1;
+ }
+ else if (num > 1 && width<=0)
+ {
+ SW602_DEBUG_MSG(("SW602Section::setColumns: called without width\n"));
+ num=1;
+ }
+ m_columns.resize(0);
+ if (num==1 && (width<=0 || colSep<=0))
+ return;
+
+ Column column;
+ column.m_width=width;
+ column.m_widthUnit = widthUnit;
+ column.m_margins[libsw602::Left] = column.m_margins[libsw602::Right] = colSep/2.;
+ m_columns.resize(size_t(num), column);
+}
+
+void SW602Section::addTo(librevenge::RVNGPropertyList &propList) const
+{
+ propList.insert("fo:margin-left", 0.0, librevenge::RVNG_INCH);
+ propList.insert("fo:margin-right", 0.0, librevenge::RVNG_INCH);
+ if (m_columns.size() > 1)
+ propList.insert("text:dont-balance-text-columns", !m_balanceText);
+ if (!m_backgroundColor.isWhite())
+ propList.insert("fo:background-color", m_backgroundColor.str().c_str());
+ if (m_columnSeparator.m_style != SW602Border::None &&
+ m_columnSeparator.m_width > 0)
+ {
+ propList.insert("librevenge:colsep-width", m_columnSeparator.m_width, librevenge::RVNG_POINT);
+ propList.insert("librevenge:colsep-color", m_columnSeparator.m_color.str().c_str());
+ propList.insert("librevenge:colsep-height", "100%");
+ propList.insert("librevenge:colsep-vertical-align", "middle");
+ }
+}
+
+void SW602Section::addColumnsTo(librevenge::RVNGPropertyListVector &propVec) const
+{
+ size_t numCol = m_columns.size();
+ if (!numCol) return;
+ for (size_t c=0; c < numCol; c++)
+ {
+ librevenge::RVNGPropertyList propList;
+ if (m_columns[c].addTo(propList))
+ propVec.append(propList);
+ }
+}
+
+}
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602Section.h b/src/lib/SW602Section.h
new file mode 100644
index 0000000..e73c074
--- /dev/null
+++ b/src/lib/SW602Section.h
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SW602_SECTION_H
+#define INCLUDED_SW602_SECTION_H
+
+#include <iostream>
+#include <vector>
+
+#include <librevenge/librevenge.h>
+
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+/** a class which stores section properties */
+class SW602Section
+{
+public:
+ struct Column;
+ //! constructor
+ SW602Section() : m_columns(), m_width(0), m_columnSeparator(), m_balanceText(false), m_backgroundColor(SW602Color::white())
+ {
+ m_columnSeparator.m_style=SW602Border::None;
+ }
+ //! destructor
+ virtual ~SW602Section()
+ {
+ }
+ /** a function which sets n uniform columns
+
+ \note: this erases previous columns and border if there are some
+ */
+ void setColumns(int num, double width, librevenge::RVNGUnit widthUnit, double colSep=0);
+ //! returns the number of columns
+ int numColumns() const
+ {
+ return m_columns.size() <= 1 ? 1 : int(m_columns.size());
+ }
+ //! returns the true if the section has only one columns
+ bool hasSingleColumns() const
+ {
+ return m_columns.size() <= 1;
+ }
+ //! add to the propList
+ void addTo(librevenge::RVNGPropertyList &propList) const;
+ //! add tabs to the propList
+ void addColumnsTo(librevenge::RVNGPropertyListVector &propList) const;
+ //! operator <<
+ friend std::ostream &operator<<(std::ostream &o, SW602Section const &sec);
+ //! operator!=
+ bool operator!=(SW602Section const &sec) const
+ {
+ if (m_columns.size()!=sec.m_columns.size())
+ return true;
+ for (size_t c=0; c < m_columns.size(); c++)
+ {
+ if (m_columns[c] != sec.m_columns[c])
+ return true;
+ }
+ if (m_columnSeparator != sec.m_columnSeparator)
+ return true;
+ if (m_balanceText!=sec.m_balanceText || m_backgroundColor!=sec.m_backgroundColor)
+ return true;
+ return false;
+ }
+ //! operator==
+ bool operator==(SW602Section const &sec) const
+ {
+ return !operator!=(sec);
+ }
+
+ //! the different column
+ std::vector<Column> m_columns;
+ //! the total section width ( if set )
+ double m_width;
+ /** the vertical separator between columns */
+ SW602Border m_columnSeparator;
+ //! true if the text is balanced between different columns
+ bool m_balanceText;
+ //! the background color
+ SW602Color m_backgroundColor;
+
+public:
+ /** struct to store the columns properties */
+ struct Column
+ {
+ //! constructor
+ Column() : m_width(0), m_widthUnit(librevenge::RVNG_INCH)
+ {
+ for (int i = 0; i < 4; i++)
+ m_margins[i]=0;
+ }
+ //! add a column to the propList
+ bool addTo(librevenge::RVNGPropertyList &propList) const;
+ //! operator <<
+ friend std::ostream &operator<<(std::ostream &o, Column const &column);
+ //! operator!=
+ bool operator!=(Column const &col) const
+ {
+ if (m_width<col.m_width || m_width>col.m_width || m_widthUnit!=col.m_widthUnit)
+ return true;
+ for (int i = 0; i < 4; i++)
+ {
+ if (m_margins[i]<col.m_margins[i] || m_margins[i]>col.m_margins[i])
+ return true;
+ }
+ return false;
+ }
+ //! operator==
+ bool operator==(Column const &col) const
+ {
+ return !operator!=(col);
+ }
+
+ //! the columns width
+ double m_width;
+ /** the width unit (default inches) */
+ librevenge::RVNGUnit m_widthUnit;
+ //! the margins in inches using libsw602::Position orders
+ double m_margins[4];
+ };
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602SpreadsheetDecoder.cpp b/src/lib/SW602SpreadsheetDecoder.cpp
new file mode 100644
index 0000000..e375918
--- /dev/null
+++ b/src/lib/SW602SpreadsheetDecoder.cpp
@@ -0,0 +1,244 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "SW602SpreadsheetDecoder.h"
+
+#include <string.h>
+
+#include <librevenge/librevenge.h>
+
+#include "SW602Debug.h"
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+void SW602SpreadsheetDecoder::insertElement(const char *psName)
+{
+ if (!m_output) return;
+ if (!psName || !*psName)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetDecoder::insertElement: called without any name\n"));
+ return;
+ }
+
+ bool ok=true;
+ switch (psName[0])
+ {
+ case 'C':
+ if (strcmp(psName,"CloseChart")==0)
+ m_output->closeChart();
+ else if (strcmp(psName,"CloseChartPlotArea")==0)
+ m_output->closeChartPlotArea();
+ else if (strcmp(psName,"CloseChartSerie")==0)
+ m_output->closeChartSerie();
+ else if (strcmp(psName,"CloseChartTextObject")==0)
+ m_output->closeChartTextObject();
+ else if (strcmp(psName,"CloseComment")==0)
+ m_output->closeComment();
+ else if (strcmp(psName,"CloseFooter")==0)
+ m_output->closeFooter();
+ else if (strcmp(psName,"CloseFootnote")==0)
+ m_output->closeFootnote();
+ else if (strcmp(psName,"CloseFrame")==0)
+ m_output->closeFrame();
+ else if (strcmp(psName,"CloseGroup")==0)
+ m_output->closeGroup();
+ else if (strcmp(psName,"CloseHeader")==0)
+ m_output->closeHeader();
+ else if (strcmp(psName,"CloseLink")==0)
+ m_output->closeLink();
+ else if (strcmp(psName,"CloseListElement")==0)
+ m_output->closeListElement();
+ else if (strcmp(psName,"CloseOrderedListLevel")==0)
+ m_output->closeOrderedListLevel();
+ else if (strcmp(psName,"ClosePageSpan")==0)
+ m_output->closePageSpan();
+ else if (strcmp(psName,"CloseParagraph")==0)
+ m_output->closeParagraph();
+ else if (strcmp(psName,"CloseSection")==0)
+ m_output->closeSection();
+ else if (strcmp(psName,"CloseSheet")==0)
+ m_output->closeSheet();
+ else if (strcmp(psName,"CloseSheetCell")==0)
+ m_output->closeSheetCell();
+ else if (strcmp(psName,"CloseSheetRow")==0)
+ m_output->closeSheetRow();
+ else if (strcmp(psName,"CloseSpan")==0)
+ m_output->closeSpan();
+ else if (strcmp(psName,"CloseTableCell")==0)
+ m_output->closeTableCell();
+ else if (strcmp(psName,"CloseTableRow")==0)
+ m_output->closeTableRow();
+ else if (strcmp(psName,"CloseTextBox")==0)
+ m_output->closeTextBox();
+ else if (strcmp(psName,"CloseUnorderedListLevel")==0)
+ m_output->closeUnorderedListLevel();
+ else
+ ok=false;
+ break;
+ case 'E':
+ if (strcmp(psName,"EndDocument")==0)
+ m_output->endDocument();
+ else
+ ok=false;
+ break;
+ case 'I':
+ if (strcmp(psName,"InsertTab")==0)
+ m_output->insertTab();
+ else if (strcmp(psName,"InsertSpace")==0)
+ m_output->insertSpace();
+ else if (strcmp(psName,"InsertLineBreak")==0)
+ m_output->insertLineBreak();
+ else
+ ok=false;
+ break;
+ default:
+ ok=false;
+ break;
+ }
+ if (!ok)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetDecoder::insertElement: called with unexpected name %s\n", psName));
+ }
+}
+
+void SW602SpreadsheetDecoder::insertElement(const char *psName, const librevenge::RVNGPropertyList &propList)
+{
+ if (!m_output) return;
+ if (!psName || !*psName)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetDecoder::insertElement: called without any name\n"));
+ return;
+ }
+
+ bool ok=true;
+ switch (psName[0])
+ {
+ case 'D':
+ if (strcmp(psName,"DefineCharacterStyle")==0)
+ m_output->defineCharacterStyle(propList);
+ else if (strcmp(psName,"DefineChartStyle")==0)
+ m_output->defineChartStyle(propList);
+ else if (strcmp(psName,"DefineEmbeddedFont")==0)
+ m_output->defineEmbeddedFont(propList);
+ else if (strcmp(psName,"DefineGraphicStyle")==0)
+ m_output->defineGraphicStyle(propList);
+ else if (strcmp(psName,"DefinePageStyle")==0)
+ m_output->definePageStyle(propList);
+ else if (strcmp(psName,"DefineParagraphStyle")==0)
+ m_output->defineParagraphStyle(propList);
+ else if (strcmp(psName,"DefineSectionStyle")==0)
+ m_output->defineSectionStyle(propList);
+ else if (strcmp(psName,"DefineSheetNumberingStyle")==0)
+ m_output->defineSheetNumberingStyle(propList);
+
+ else if (strcmp(psName,"DrawConnector")==0)
+ m_output->drawConnector(propList);
+ else if (strcmp(psName,"DrawEllipse")==0)
+ m_output->drawEllipse(propList);
+ else if (strcmp(psName,"DrawPath")==0)
+ m_output->drawPath(propList);
+ else if (strcmp(psName,"DrawPolygon")==0)
+ m_output->drawPolygon(propList);
+ else if (strcmp(psName,"DrawPolyline")==0)
+ m_output->drawPolyline(propList);
+ else if (strcmp(psName,"DrawRectangle")==0)
+ m_output->drawRectangle(propList);
+ else
+ ok=false;
+ break;
+ case 'I':
+ if (strcmp(psName,"InsertBinaryObject")==0)
+ m_output->insertBinaryObject(propList);
+ else if (strcmp(psName,"InsertChartAxis")==0)
+ m_output->insertChartAxis(propList);
+ else if (strcmp(psName,"InsertCoveredTableCell")==0)
+ m_output->insertCoveredTableCell(propList);
+ else if (strcmp(psName,"InsertEquation")==0)
+ m_output->insertEquation(propList);
+ else if (strcmp(psName,"InsertField")==0)
+ m_output->insertField(propList);
+ else
+ ok=false;
+ break;
+ case 'O':
+ if (strcmp(psName,"OpenChart")==0)
+ m_output->openChart(propList);
+ else if (strcmp(psName,"OpenChartPlotArea")==0)
+ m_output->openChartPlotArea(propList);
+ else if (strcmp(psName,"OpenChartSerie")==0)
+ m_output->openChartSerie(propList);
+ else if (strcmp(psName,"OpenChartTextObject")==0)
+ m_output->openChartTextObject(propList);
+ else if (strcmp(psName,"OpenComment")==0)
+ m_output->openComment(propList);
+ else if (strcmp(psName,"OpenFooter")==0)
+ m_output->openFooter(propList);
+ else if (strcmp(psName,"OpenFootnote")==0)
+ m_output->openFootnote(propList);
+ else if (strcmp(psName,"OpenFrame")==0)
+ m_output->openFrame(propList);
+ else if (strcmp(psName,"OpenGroup")==0)
+ m_output->openGroup(propList);
+ else if (strcmp(psName,"OpenHeader")==0)
+ m_output->openHeader(propList);
+ else if (strcmp(psName,"OpenLink")==0)
+ m_output->openLink(propList);
+ else if (strcmp(psName,"OpenListElement")==0)
+ m_output->openListElement(propList);
+ else if (strcmp(psName,"OpenOrderedListLevel")==0)
+ m_output->openOrderedListLevel(propList);
+ else if (strcmp(psName,"OpenPageSpan")==0)
+ m_output->openPageSpan(propList);
+ else if (strcmp(psName,"OpenParagraph")==0)
+ m_output->openParagraph(propList);
+ else if (strcmp(psName,"OpenSheet")==0)
+ m_output->openSheet(propList);
+ else if (strcmp(psName,"OpenSection")==0)
+ m_output->openSection(propList);
+ else if (strcmp(psName,"OpenSheetCell")==0)
+ m_output->openSheetCell(propList);
+ else if (strcmp(psName,"OpenSheetRow")==0)
+ m_output->openSheetRow(propList);
+ else if (strcmp(psName,"OpenSpan")==0)
+ m_output->openSpan(propList);
+ else if (strcmp(psName,"OpenTableCell")==0)
+ m_output->openTableCell(propList);
+ else if (strcmp(psName,"OpenTableRow")==0)
+ m_output->openTableRow(propList);
+ else if (strcmp(psName,"OpenTextBox")==0)
+ m_output->openTextBox(propList);
+ else if (strcmp(psName,"OpenUnorderedListLevel")==0)
+ m_output->openUnorderedListLevel(propList);
+ else
+ ok=false;
+ break;
+ case 'S':
+ if (strcmp(psName,"SetDocumentMetaData")==0)
+ m_output->setDocumentMetaData(propList);
+ else if (strcmp(psName,"StartDocument")==0)
+ m_output->startDocument(propList);
+
+ else
+ ok=false;
+ break;
+ default:
+ ok=false;
+ break;
+ }
+ if (!ok)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetDecoder::insertElement: called with unexpected name %s\n", psName));
+ }
+}
+
+}
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602SpreadsheetDecoder.h b/src/lib/SW602SpreadsheetDecoder.h
new file mode 100644
index 0000000..60cb346
--- /dev/null
+++ b/src/lib/SW602SpreadsheetDecoder.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SW602_SPREADSHEET_DECODER_H
+#define INCLUDED_SW602_SPREADSHEET_DECODER_H
+
+#include <librevenge/librevenge.h>
+
+#include "SW602PropertyHandler.h"
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+/** main class used to decode a librevenge::RVNGBinaryData created by
+ \see SW602SpreadsheetEncoder (with mimeType="image/x-libsw602-odg") and to send
+ it contents to librevenge::RVNGSpreadsheetInterface
+*/
+class SW602SpreadsheetDecoder : public SW602PropertyHandler
+{
+public:
+ /** constructor */
+ explicit SW602SpreadsheetDecoder(librevenge::RVNGSpreadsheetInterface *output) : SW602PropertyHandler(), m_output(output) { }
+ /** destructor */
+ ~SW602SpreadsheetDecoder() {};
+
+ /** insert an element */
+ void insertElement(const char *psName);
+ /** insert an element ( with a librevenge::RVNGPropertyList ) */
+ void insertElement(const char *psName, const librevenge::RVNGPropertyList &xPropList);
+ /** insert an element ( with a librevenge::RVNGPropertyListVector parameter ) */
+ void insertElement(const char *psName, const librevenge::RVNGPropertyList &xPropList,
+ const librevenge::RVNGPropertyListVector &vector);
+ /** insert a sequence of character */
+ void characters(const librevenge::RVNGString &sCharacters)
+ {
+ if (!m_output) return;
+ m_output->insertText(sCharacters);
+ }
+private:
+ /// copy constructor (undefined)
+ SW602SpreadsheetDecoder(SW602SpreadsheetDecoder const &);
+ /// operator= (undefined)
+ SW602SpreadsheetDecoder operator=(SW602SpreadsheetDecoder const &);
+ /** the interface output */
+ librevenge::RVNGSpreadsheetInterface *m_output;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602SpreadsheetEncoder.cpp b/src/lib/SW602SpreadsheetEncoder.cpp
new file mode 100644
index 0000000..4f5fb6b
--- /dev/null
+++ b/src/lib/SW602SpreadsheetEncoder.cpp
@@ -0,0 +1,464 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "SW602SpreadsheetEncoder.h"
+
+#include <string.h>
+
+#include <map>
+#include <sstream>
+#include <string>
+
+#include <librevenge/librevenge.h>
+
+#include "SW602PropertyHandler.h"
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+//! a name space used to define internal data of SW602SpreadsheetEncoder
+namespace SW602SpreadsheetEncoderInternal
+{
+//! the state of a SW602SpreadsheetEncoder
+struct State
+{
+ //! constructor
+ State() : m_encoder()
+ {
+ }
+ //! the encoder
+ SW602PropertyHandlerEncoder m_encoder;
+};
+
+}
+
+SW602SpreadsheetEncoder::SW602SpreadsheetEncoder() : librevenge::RVNGSpreadsheetInterface(), m_state(new SW602SpreadsheetEncoderInternal::State)
+{
+}
+
+SW602SpreadsheetEncoder::~SW602SpreadsheetEncoder()
+{
+}
+
+bool SW602SpreadsheetEncoder::getBinaryResult(SW602EmbeddedObject &object)
+{
+ librevenge::RVNGBinaryData data;
+ if (!m_state->m_encoder.getData(data))
+ return false;
+ object=SW602EmbeddedObject(data, "image/x-libsw602-ods");
+ return true;
+}
+
+void SW602SpreadsheetEncoder::setDocumentMetaData(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("SetDocumentMetaData", list);
+}
+
+void SW602SpreadsheetEncoder::startDocument(const ::librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("StartDocument", list);
+}
+
+void SW602SpreadsheetEncoder::endDocument()
+{
+ m_state->m_encoder.insertElement("EndDocument");
+}
+
+//
+// page
+//
+void SW602SpreadsheetEncoder::definePageStyle(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("DefinePageStyle", list);
+}
+
+void SW602SpreadsheetEncoder::defineEmbeddedFont(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("DefineEmbeddedFont", list);
+}
+
+void SW602SpreadsheetEncoder::openPageSpan(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenPageSpan", list);
+}
+void SW602SpreadsheetEncoder::closePageSpan()
+{
+ m_state->m_encoder.insertElement("ClosePageSpan");
+}
+
+void SW602SpreadsheetEncoder::openHeader(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenHeader", list);
+}
+void SW602SpreadsheetEncoder::closeHeader()
+{
+ m_state->m_encoder.insertElement("CloseHeader");
+}
+
+void SW602SpreadsheetEncoder::openFooter(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenFooter", list);
+}
+void SW602SpreadsheetEncoder::closeFooter()
+{
+ m_state->m_encoder.insertElement("CloseFooter");
+}
+
+//
+// spreadsheet
+//
+void SW602SpreadsheetEncoder::defineSheetNumberingStyle(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("DefineSheetNumberingStyle", list);
+}
+void SW602SpreadsheetEncoder::openSheet(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenSheet", list);
+}
+void SW602SpreadsheetEncoder::closeSheet()
+{
+ m_state->m_encoder.insertElement("CloseSheet");
+}
+void SW602SpreadsheetEncoder::openSheetRow(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenSheetRow", list);
+}
+
+void SW602SpreadsheetEncoder::closeSheetRow()
+{
+ m_state->m_encoder.insertElement("CloseSheetRow");
+}
+
+void SW602SpreadsheetEncoder::openSheetCell(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenSheetCell", list);
+}
+
+void SW602SpreadsheetEncoder::closeSheetCell()
+{
+ m_state->m_encoder.insertElement("CloseSheetCell");
+}
+
+//
+// chart
+//
+
+void SW602SpreadsheetEncoder::defineChartStyle(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("DefineChartStyle", list);
+}
+
+void SW602SpreadsheetEncoder::openChart(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenChart", list);
+}
+
+void SW602SpreadsheetEncoder::closeChart()
+{
+ m_state->m_encoder.insertElement("CloseChart");
+}
+
+void SW602SpreadsheetEncoder::openChartTextObject(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenChartTextObject", list);
+}
+
+void SW602SpreadsheetEncoder::closeChartTextObject()
+{
+ m_state->m_encoder.insertElement("CloseChartTextObject");
+}
+
+void SW602SpreadsheetEncoder::openChartPlotArea(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenChartPlotArea", list);
+}
+
+void SW602SpreadsheetEncoder::closeChartPlotArea()
+{
+ m_state->m_encoder.insertElement("CloseChartPlotArea");
+}
+
+void SW602SpreadsheetEncoder::insertChartAxis(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("InsertChartAxis", list);
+}
+
+void SW602SpreadsheetEncoder::openChartSerie(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenChartSerie", list);
+}
+
+void SW602SpreadsheetEncoder::closeChartSerie()
+{
+ m_state->m_encoder.insertElement("CloseChartSerie");
+}
+
+
+//
+// para styles + character styles + link
+//
+void SW602SpreadsheetEncoder::defineParagraphStyle(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("DefineParagraphStyle", list);
+}
+
+void SW602SpreadsheetEncoder::openParagraph(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenParagraph", list);
+}
+
+void SW602SpreadsheetEncoder::closeParagraph()
+{
+ m_state->m_encoder.insertElement("CloseParagraph");
+}
+
+void SW602SpreadsheetEncoder::defineCharacterStyle(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("DefineCharacterStyle", list);
+}
+
+void SW602SpreadsheetEncoder::openSpan(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenSpan", list);
+}
+
+void SW602SpreadsheetEncoder::closeSpan()
+{
+ m_state->m_encoder.insertElement("CloseSpan");
+}
+
+void SW602SpreadsheetEncoder::openLink(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenLink", list);
+}
+
+void SW602SpreadsheetEncoder::closeLink()
+{
+ m_state->m_encoder.insertElement("CloseLink");
+}
+
+//
+// section + add basic char
+//
+void SW602SpreadsheetEncoder::defineSectionStyle(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("DefineSectionStyle", list);
+}
+
+void SW602SpreadsheetEncoder::openSection(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenSection", list);
+}
+
+void SW602SpreadsheetEncoder::closeSection()
+{
+ m_state->m_encoder.insertElement("CloseSection");
+}
+
+void SW602SpreadsheetEncoder::insertTab()
+{
+ m_state->m_encoder.insertElement("InsertTab");
+}
+
+void SW602SpreadsheetEncoder::insertSpace()
+{
+ m_state->m_encoder.insertElement("InsertSpace");
+}
+
+void SW602SpreadsheetEncoder::insertText(const librevenge::RVNGString &text)
+{
+ m_state->m_encoder.characters(text);
+}
+
+void SW602SpreadsheetEncoder::insertLineBreak()
+{
+ m_state->m_encoder.insertElement("InsertLineBreak");
+}
+
+void SW602SpreadsheetEncoder::insertField(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("InsertField", list);
+}
+
+//
+// list
+//
+void SW602SpreadsheetEncoder::openOrderedListLevel(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenOrderedListLevel", list);
+}
+
+void SW602SpreadsheetEncoder::openUnorderedListLevel(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenUnorderedListLevel", list);
+}
+
+void SW602SpreadsheetEncoder::closeOrderedListLevel()
+{
+ m_state->m_encoder.insertElement("CloseOrderedListLevel");
+}
+
+void SW602SpreadsheetEncoder::closeUnorderedListLevel()
+{
+ m_state->m_encoder.insertElement("CloseOrderedListLevel");
+}
+
+void SW602SpreadsheetEncoder::openListElement(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenListElement", list);
+}
+
+void SW602SpreadsheetEncoder::closeListElement()
+{
+ m_state->m_encoder.insertElement("CloseListElement");
+}
+
+//
+// footnote, comment, frame
+//
+
+void SW602SpreadsheetEncoder::openFootnote(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenFootnote", list);
+}
+
+void SW602SpreadsheetEncoder::closeFootnote()
+{
+ m_state->m_encoder.insertElement("CloseFootnote");
+}
+
+void SW602SpreadsheetEncoder::openComment(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenComment", list);
+}
+void SW602SpreadsheetEncoder::closeComment()
+{
+ m_state->m_encoder.insertElement("CloseComment");
+}
+
+void SW602SpreadsheetEncoder::openFrame(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenFrame", list);
+}
+void SW602SpreadsheetEncoder::closeFrame()
+{
+ m_state->m_encoder.insertElement("CloseFrame");
+}
+void SW602SpreadsheetEncoder::insertBinaryObject(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("InsertBinaryObject", list);
+}
+
+//
+// specific text/table
+//
+void SW602SpreadsheetEncoder::openTextBox(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenTextBox", list);
+}
+
+void SW602SpreadsheetEncoder::closeTextBox()
+{
+ m_state->m_encoder.insertElement("CloseTextBox");
+}
+
+void SW602SpreadsheetEncoder::openTable(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenTable", list);
+}
+void SW602SpreadsheetEncoder::closeTable()
+{
+ m_state->m_encoder.insertElement("CloseTable");
+}
+void SW602SpreadsheetEncoder::openTableRow(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenTableRow", list);
+}
+
+void SW602SpreadsheetEncoder::closeTableRow()
+{
+ m_state->m_encoder.insertElement("CloseTableRow");
+}
+
+void SW602SpreadsheetEncoder::openTableCell(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenTableCell", list);
+}
+
+void SW602SpreadsheetEncoder::closeTableCell()
+{
+ m_state->m_encoder.insertElement("CloseTableCell");
+}
+
+void SW602SpreadsheetEncoder::insertCoveredTableCell(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("InsertCoveredTableCell", list);
+}
+
+//
+// simple Graphic
+//
+
+void SW602SpreadsheetEncoder::openGroup(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("OpenGroup", list);
+}
+
+void SW602SpreadsheetEncoder::closeGroup()
+{
+ m_state->m_encoder.insertElement("CloseGroup");
+}
+
+void SW602SpreadsheetEncoder::defineGraphicStyle(const librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("DefineGraphicStyle", list);
+}
+
+void SW602SpreadsheetEncoder::drawRectangle(const ::librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("DrawRectangle", list);
+}
+
+void SW602SpreadsheetEncoder::drawEllipse(const ::librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("DrawEllipse", list);
+}
+
+void SW602SpreadsheetEncoder::drawPolygon(const ::librevenge::RVNGPropertyList &vertices)
+{
+ m_state->m_encoder.insertElement("DrawPolygon", vertices);
+}
+
+void SW602SpreadsheetEncoder::drawPolyline(const ::librevenge::RVNGPropertyList &vertices)
+{
+ m_state->m_encoder.insertElement("DrawPolyline", vertices);
+}
+
+void SW602SpreadsheetEncoder::drawPath(const ::librevenge::RVNGPropertyList &path)
+{
+ m_state->m_encoder.insertElement("DrawPath", path);
+}
+
+void SW602SpreadsheetEncoder::drawConnector(const ::librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("DrawConnector", list);
+}
+
+//
+// equation
+//
+void SW602SpreadsheetEncoder::insertEquation(const ::librevenge::RVNGPropertyList &list)
+{
+ m_state->m_encoder.insertElement("InsertEquation", list);
+}
+
+}
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602SpreadsheetEncoder.h b/src/lib/SW602SpreadsheetEncoder.h
new file mode 100644
index 0000000..b36e0cb
--- /dev/null
+++ b/src/lib/SW602SpreadsheetEncoder.h
@@ -0,0 +1,170 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SW602_SPREADSHEET_ENCODER_H
+#define INCLUDED_SW602_SPREADSHEET_ENCODER_H
+
+#include <librevenge/librevenge.h>
+
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+class SW602PropertyHandlerEncoder;
+
+namespace SW602SpreadsheetEncoderInternal
+{
+struct State;
+}
+
+/** main class used to define store librevenge::RVNGSpreadsheetInterface
+ lists of command in a librevenge::RVNGBinaryData. \see SW602SpreadsheetDecoder
+ can be used to decode back the spreadsheet...
+
+ \note as this class implements the functions librevenge::RVNGSpreadsheetInterface,
+ the documentation is not duplicated..
+*/
+class SW602SpreadsheetEncoder : public librevenge::RVNGSpreadsheetInterface
+{
+public:
+ /// constructor
+ SW602SpreadsheetEncoder();
+ /// destructor
+ ~SW602SpreadsheetEncoder();
+ /// return the final spreadsheet
+ bool getBinaryResult(SW602EmbeddedObject &object);
+
+ void setDocumentMetaData(const librevenge::RVNGPropertyList &propList);
+
+ void startDocument(const librevenge::RVNGPropertyList &propList);
+ void endDocument();
+
+ void definePageStyle(const librevenge::RVNGPropertyList &propList);
+ void defineEmbeddedFont(const librevenge::RVNGPropertyList &propList);
+
+ void openPageSpan(const librevenge::RVNGPropertyList &propList);
+ void closePageSpan();
+
+ void openHeader(const librevenge::RVNGPropertyList &propList);
+ void closeHeader();
+
+ void openFooter(const librevenge::RVNGPropertyList &propList);
+ void closeFooter();
+
+ void defineSheetNumberingStyle(const librevenge::RVNGPropertyList &propList);
+ void openSheet(const librevenge::RVNGPropertyList &propList);
+ void closeSheet();
+ void openSheetRow(const librevenge::RVNGPropertyList &propList);
+ void closeSheetRow();
+ void openSheetCell(const librevenge::RVNGPropertyList &propList);
+ void closeSheetCell();
+
+ void defineChartStyle(const librevenge::RVNGPropertyList &propList);
+
+ void openChart(const librevenge::RVNGPropertyList &propList);
+ void closeChart();
+
+ void openChartTextObject(const librevenge::RVNGPropertyList &propList);
+ void closeChartTextObject();
+
+ void openChartPlotArea(const librevenge::RVNGPropertyList &propList);
+ void closeChartPlotArea();
+ void insertChartAxis(const librevenge::RVNGPropertyList &axis);
+ void openChartSerie(const librevenge::RVNGPropertyList &series);
+ void closeChartSerie();
+
+ void defineParagraphStyle(const librevenge::RVNGPropertyList &propList);
+
+ void openParagraph(const librevenge::RVNGPropertyList &propList);
+ void closeParagraph();
+
+ void defineCharacterStyle(const librevenge::RVNGPropertyList &propList);
+
+ void openSpan(const librevenge::RVNGPropertyList &propList);
+ void closeSpan();
+ void openLink(const librevenge::RVNGPropertyList &propList);
+ void closeLink();
+
+ void defineSectionStyle(const librevenge::RVNGPropertyList &propList);
+
+ void openSection(const librevenge::RVNGPropertyList &propList);
+ void closeSection();
+
+ void insertTab();
+ void insertSpace();
+ void insertText(const librevenge::RVNGString &text);
+ void insertLineBreak();
+
+ void insertField(const librevenge::RVNGPropertyList &propList);
+
+ void openOrderedListLevel(const librevenge::RVNGPropertyList &propList);
+ void openUnorderedListLevel(const librevenge::RVNGPropertyList &propList);
+ void closeOrderedListLevel();
+ void closeUnorderedListLevel();
+ void openListElement(const librevenge::RVNGPropertyList &propList);
+ void closeListElement();
+
+ void openFootnote(const librevenge::RVNGPropertyList &propList);
+ void closeFootnote();
+
+ void openComment(const librevenge::RVNGPropertyList &propList);
+ void closeComment();
+
+ void openFrame(const librevenge::RVNGPropertyList &propList);
+ void closeFrame();
+ void insertBinaryObject(const librevenge::RVNGPropertyList &propList);
+
+ //
+ // specific text/table
+ //
+
+ void openTextBox(const librevenge::RVNGPropertyList &propList);
+ void closeTextBox();
+
+ void openTable(const librevenge::RVNGPropertyList &propList);
+ void closeTable();
+ void openTableRow(const librevenge::RVNGPropertyList &propList);
+ void closeTableRow();
+ void openTableCell(const librevenge::RVNGPropertyList &propList);
+ void closeTableCell();
+ void insertCoveredTableCell(const librevenge::RVNGPropertyList &propList);
+
+ //
+ // simple Graphic
+ //
+
+ void openGroup(const librevenge::RVNGPropertyList &propList);
+ void closeGroup();
+
+ void defineGraphicStyle(const librevenge::RVNGPropertyList &propList);
+
+ void drawRectangle(const librevenge::RVNGPropertyList &propList);
+ void drawEllipse(const librevenge::RVNGPropertyList &propList);
+ void drawPolygon(const librevenge::RVNGPropertyList &propList);
+ void drawPolyline(const librevenge::RVNGPropertyList &propList);
+ void drawPath(const librevenge::RVNGPropertyList &propList);
+ void drawConnector(const ::librevenge::RVNGPropertyList &propList);
+
+ //
+ // Equation
+ //
+
+ void insertEquation(const librevenge::RVNGPropertyList &propList);
+
+protected:
+ //! the actual state
+ boost::shared_ptr<SW602SpreadsheetEncoderInternal::State> m_state;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602SpreadsheetListener.cpp b/src/lib/SW602SpreadsheetListener.cpp
new file mode 100644
index 0000000..6f0125d
--- /dev/null
+++ b/src/lib/SW602SpreadsheetListener.cpp
@@ -0,0 +1,2064 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/** \file SW602SpreadsheetListener.cxx
+ * Implements SW602SpreadsheetListener: the libsw602 spreadsheet processor listener
+ *
+ * \note this class is the only class which does the interface with
+ * the librevenge::RVNGSpreadsheetInterface
+ */
+
+#include "SW602SpreadsheetListener.h"
+
+#include <cmath>
+#include <cstring>
+#include <iomanip>
+#include <sstream>
+#include <time.h>
+
+#include <librevenge/librevenge.h>
+
+#include "SW602Cell.h"
+#include "SW602Chart.h"
+#include "SW602Font.h"
+#include "SW602GraphicListener.h"
+#include "SW602GraphicShape.h"
+#include "SW602GraphicStyle.h"
+#include "SW602List.h"
+#include "SW602PageSpan.h"
+#include "SW602Paragraph.h"
+#include "SW602Parser.h"
+#include "SW602Position.h"
+#include "SW602Section.h"
+#include "SW602SubDocument.h"
+#include "SW602Table.h"
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+//! Internal and low level namespace to define the states of SW602SpreadsheetListener
+namespace SW602SpreadsheetListenerInternal
+{
+//! a enum to define basic break bit
+enum { PageBreakBit=0x1, ColumnBreakBit=0x2 };
+//! a class to store the document state of a SW602SpreadsheetListener
+struct DocumentState
+{
+ //! constructor
+ explicit DocumentState(std::vector<SW602PageSpan> const &pageList) :
+ m_pageList(pageList), m_pageSpan(), m_metaData(), m_footNoteNumber(0), m_smallPictureNumber(0),
+ m_isDocumentStarted(false), m_isSheetOpened(false), m_isSheetRowOpened(false),
+ m_sentListMarkers(), m_numberingIdMap(),
+ m_subDocuments()
+ {
+ }
+ //! destructor
+ ~DocumentState()
+ {
+ }
+
+ //! the pages definition
+ std::vector<SW602PageSpan> m_pageList;
+ //! the current page span
+ SW602PageSpan m_pageSpan;
+ //! the document meta data
+ librevenge::RVNGPropertyList m_metaData;
+
+ int m_footNoteNumber /** footnote number*/;
+
+ int m_smallPictureNumber /** number of small picture */;
+ bool m_isDocumentStarted /** a flag to know if the document is open */;
+ bool m_isSheetOpened /** a flag to know if a sheet is open */;
+ bool m_isSheetRowOpened /** a flag to know if a row is open */;
+ /// the list of marker corresponding to sent list
+ std::vector<int> m_sentListMarkers;
+ /** a map cell's format to id */
+ std::map<SW602Cell::Format,int,SW602Cell::CompareFormat> m_numberingIdMap;
+ std::vector<SW602SubDocumentPtr> m_subDocuments; /** list of document actually open */
+
+private:
+ DocumentState(const DocumentState &);
+ DocumentState &operator=(const DocumentState &);
+};
+
+/** the state of a SW602SpreadsheetListener */
+struct State
+{
+ //! constructor
+ State();
+ //! destructor
+ ~State() { }
+ //! returns true if we are in a text zone
+ bool canWriteText() const
+ {
+ if (m_isSheetCellOpened || m_isHeaderFooterOpened) return true;
+ return m_isTextboxOpened || m_isTableCellOpened || m_isNote;
+ }
+
+ //! a buffer to stored the text
+ librevenge::RVNGString m_textBuffer;
+ //! the number of tabs to add
+ int m_numDeferredTabs;
+
+ //! the font
+ SW602Font m_font;
+ //! the paragraph
+ SW602Paragraph m_paragraph;
+
+ boost::shared_ptr<SW602List> m_list;
+
+ bool m_isPageSpanOpened;
+ bool m_isHeaderFooterOpened /** a flag to know if the header footer is started */;
+ bool m_isFrameOpened;
+ bool m_isTextboxOpened;
+
+ bool m_isHeaderFooterWithoutParagraph;
+
+ bool m_isSpanOpened;
+ bool m_isParagraphOpened;
+ bool m_isListElementOpened;
+
+ bool m_firstParagraphInPageSpan;
+
+ bool m_isSheetColumnOpened;
+ bool m_isSheetCellOpened;
+
+ bool m_isTableOpened;
+ bool m_isTableRowOpened;
+ bool m_isTableColumnOpened;
+ bool m_isTableCellOpened;
+
+ unsigned m_currentPage;
+ int m_numPagesRemainingInSpan;
+ int m_currentPageNumber;
+
+ std::vector<bool> m_listOrderedLevels; //! a stack used to know what is open
+
+ bool m_inSubDocument;
+
+ bool m_isNote;
+ bool m_inLink;
+ libsw602::SubDocumentType m_subDocumentType;
+
+private:
+ State(const State &);
+ State &operator=(const State &);
+};
+
+State::State() :
+ m_textBuffer(""), m_numDeferredTabs(0),
+
+ m_font(20,12), // default time 12
+
+ m_paragraph(),
+
+ m_list(),
+
+ m_isPageSpanOpened(false), m_isHeaderFooterOpened(false),
+ m_isFrameOpened(false), m_isTextboxOpened(false),
+ m_isHeaderFooterWithoutParagraph(false),
+
+ m_isSpanOpened(false), m_isParagraphOpened(false), m_isListElementOpened(false),
+
+ m_firstParagraphInPageSpan(true),
+
+ m_isSheetColumnOpened(false),
+ m_isSheetCellOpened(false),
+
+ m_isTableOpened(false), m_isTableRowOpened(false), m_isTableColumnOpened(false),
+ m_isTableCellOpened(false),
+
+ m_currentPage(0), m_numPagesRemainingInSpan(0), m_currentPageNumber(1),
+
+ m_listOrderedLevels(),
+
+ m_inSubDocument(false),
+ m_isNote(false), m_inLink(false),
+ m_subDocumentType(libsw602::DOC_NONE)
+{
+}
+}
+
+SW602SpreadsheetListener::SW602SpreadsheetListener(SW602ParserState &parserState, std::vector<SW602PageSpan> const &pageList, librevenge::RVNGSpreadsheetInterface *documentInterface) : SW602Listener(),
+ m_ds(new SW602SpreadsheetListenerInternal::DocumentState(pageList)), m_ps(new SW602SpreadsheetListenerInternal::State), m_psStack(),
+ m_parserState(parserState), m_documentInterface(documentInterface)
+{
+}
+
+SW602SpreadsheetListener::SW602SpreadsheetListener(SW602ParserState &parserState, SW602Box2f const &box, librevenge::RVNGSpreadsheetInterface *documentInterface) : SW602Listener(),
+ m_ds(), m_ps(new SW602SpreadsheetListenerInternal::State), m_psStack(), m_parserState(parserState), m_documentInterface(documentInterface)
+{
+ SW602PageSpan pageSpan;
+ pageSpan.setMargins(0);
+ pageSpan.setPageSpan(1);
+ pageSpan.setFormWidth(box.size().x()/72.);
+ pageSpan.setFormLength(box.size().y()/72.);
+ m_ds.reset(new SW602SpreadsheetListenerInternal::DocumentState(std::vector<SW602PageSpan>(1, pageSpan)));
+}
+
+
+SW602SpreadsheetListener::~SW602SpreadsheetListener()
+{
+}
+
+///////////////////
+// text data
+///////////////////
+bool SW602SpreadsheetListener::canWriteText() const
+{
+ return m_ps->canWriteText();
+}
+
+void SW602SpreadsheetListener::insertChar(uint8_t character)
+{
+ if (!m_ps->canWriteText())
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::insertChar: called outside a text zone\n"));
+ return;
+ }
+ if (character >= 0x80)
+ {
+ SW602SpreadsheetListener::insertUnicode(character);
+ return;
+ }
+ _flushDeferredTabs();
+ if (!m_ps->m_isSpanOpened) _openSpan();
+ m_ps->m_textBuffer.append((char) character);
+}
+
+void SW602SpreadsheetListener::insertCharacter(unsigned char c)
+{
+ if (!m_ps->canWriteText())
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::insertCharacter: called outside a text zone\n"));
+ return;
+ }
+ // TODO: handle
+ // int unicode = m_parserState.m_fontConverter->unicode(m_ps->m_font.id(), c);
+ int unicode = c;
+ if (unicode == -1)
+ {
+ if (c < 0x20)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::insertCharacter: Find odd char %x\n", (unsigned int)c));
+ }
+ else
+ SW602SpreadsheetListener::insertChar((uint8_t) c);
+ }
+ else
+ SW602SpreadsheetListener::insertUnicode((uint32_t) unicode);
+}
+
+int SW602SpreadsheetListener::insertCharacter(unsigned char c, librevenge::RVNGInputStream &input, long endPos)
+{
+ if (!m_ps->canWriteText())
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::insertCharacter: called outside a text zone\n"));
+ return 0;
+ }
+ long debPos=input.tell();
+ // int fId = m_ps->m_font.id();
+#if 0
+ int unicode = endPos==debPos ?
+ m_parserState.m_fontConverter->unicode(fId, c) :
+ m_parserState.m_fontConverter->unicode(fId, c, input);
+#endif
+ int unicode = c;
+
+ long pos=input.tell();
+ if (endPos > 0 && pos > endPos)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::insertCharacter: problem reading a character\n"));
+ pos = debPos;
+ input.seek(pos, librevenge::RVNG_SEEK_SET);
+ // unicode = m_parserState.m_fontConverter->unicode(fId, c);
+ unicode = c;
+ }
+ if (unicode == -1)
+ {
+ if (c < 0x20)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::insertCharacter: Find odd char %x\n", (unsigned int)c));
+ }
+ else
+ SW602SpreadsheetListener::insertChar((uint8_t) c);
+ }
+ else
+ SW602SpreadsheetListener::insertUnicode((uint32_t) unicode);
+
+ return int(pos-debPos);
+}
+
+void SW602SpreadsheetListener::insertUnicode(uint32_t val)
+{
+ if (!m_ps->canWriteText())
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::insertUnicode: called outside a text zone\n"));
+ return;
+ }
+
+ // undef character, we skip it
+ if (val == 0xfffd) return;
+
+ _flushDeferredTabs();
+ if (!m_ps->m_isSpanOpened) _openSpan();
+ libsw602::appendUnicode(val, m_ps->m_textBuffer);
+}
+
+void SW602SpreadsheetListener::insertUnicodeString(librevenge::RVNGString const &str)
+{
+ if (!m_ps->canWriteText())
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::insertUnicodeString: called outside a text zone\n"));
+ return;
+ }
+
+ _flushDeferredTabs();
+ if (!m_ps->m_isSpanOpened) _openSpan();
+ m_ps->m_textBuffer.append(str);
+}
+
+void SW602SpreadsheetListener::insertEOL(bool soft)
+{
+ if (!m_ps->canWriteText())
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::insertEOL: called outside a text zone\n"));
+ return;
+ }
+
+ if (!m_ps->m_isParagraphOpened && !m_ps->m_isListElementOpened)
+ _openSpan();
+ _flushDeferredTabs();
+
+ if (soft)
+ {
+ if (m_ps->m_isSpanOpened)
+ _flushText();
+ m_documentInterface->insertLineBreak();
+ }
+ else if (m_ps->m_isParagraphOpened)
+ _closeParagraph();
+
+ // sub/superscript must not survive a new line
+ m_ps->m_font.set(SW602Font::Script());
+}
+
+void SW602SpreadsheetListener::insertTab()
+{
+ if (!m_ps->canWriteText())
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::insertTab: called outside a text zone\n"));
+ return;
+ }
+
+ if (!m_ps->m_isParagraphOpened)
+ {
+ m_ps->m_numDeferredTabs++;
+ return;
+ }
+ if (m_ps->m_isSpanOpened) _flushText();
+ m_ps->m_numDeferredTabs++;
+ _flushDeferredTabs();
+}
+
+void SW602SpreadsheetListener::insertBreak(SW602SpreadsheetListener::BreakType)
+{
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::insertBreak: make not sense\n"));
+ return;
+}
+
+///////////////////
+// font/paragraph function
+///////////////////
+void SW602SpreadsheetListener::setFont(SW602Font const &font)
+{
+ if (font == m_ps->m_font) return;
+
+ // check if id and size are defined, if not used the previous fields
+ SW602Font finalFont(font);
+ if (font.id() == -1)
+ finalFont.setId(m_ps->m_font.id());
+ if (font.size() <= 0)
+ finalFont.setSize(m_ps->m_font.size());
+ if (finalFont == m_ps->m_font) return;
+
+ _closeSpan();
+ m_ps->m_font = finalFont;
+}
+
+SW602Font const &SW602SpreadsheetListener::getFont() const
+{
+ return m_ps->m_font;
+}
+
+bool SW602SpreadsheetListener::isParagraphOpened() const
+{
+ return m_ps->m_isParagraphOpened;
+}
+
+void SW602SpreadsheetListener::setParagraph(SW602Paragraph const &para)
+{
+ if (para==m_ps->m_paragraph) return;
+
+ m_ps->m_paragraph=para;
+}
+
+SW602Paragraph const &SW602SpreadsheetListener::getParagraph() const
+{
+ return m_ps->m_paragraph;
+}
+
+///////////////////
+// field/link :
+///////////////////
+void SW602SpreadsheetListener::insertField(SW602Field const &field)
+{
+ if (!m_ps->canWriteText())
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::insertField: called outside a text zone\n"));
+ return;
+ }
+
+ librevenge::RVNGPropertyList propList;
+ if (field.addTo(propList))
+ {
+ _flushDeferredTabs();
+ _flushText();
+ _openSpan();
+ m_documentInterface->insertField(propList);
+ return;
+ }
+ librevenge::RVNGString text=field.getString();
+ if (!text.empty())
+ SW602SpreadsheetListener::insertUnicodeString(text);
+ else
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::insertField: must not be called with type=%d\n", int(field.m_type)));
+ }
+}
+
+void SW602SpreadsheetListener::openLink(SW602Link const &link)
+{
+ if (!m_ps->canWriteText())
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::openLink: called outside a text zone\n"));
+ return;
+ }
+ if (m_ps->m_inLink)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener:openLink: a link is already opened\n"));
+ return;
+ }
+ if (!m_ps->m_isSpanOpened) _openSpan();
+ librevenge::RVNGPropertyList propList;
+ link.addTo(propList);
+ m_documentInterface->openLink(propList);
+ _pushParsingState();
+ m_ps->m_inLink=true;
+// we do not want any close open paragraph in a link
+ m_ps->m_isParagraphOpened=true;
+}
+
+void SW602SpreadsheetListener::closeLink()
+{
+ if (!m_ps->m_inLink)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener:closeLink: can not close a link\n"));
+ return;
+ }
+ if (m_ps->m_isSpanOpened) _closeSpan();
+ m_documentInterface->closeLink();
+ _popParsingState();
+}
+
+///////////////////
+// document
+///////////////////
+void SW602SpreadsheetListener::setDocumentLanguage(std::string locale)
+{
+ if (!locale.length()) return;
+ m_ds->m_metaData.insert("librevenge:language", locale.c_str());
+}
+
+bool SW602SpreadsheetListener::isDocumentStarted() const
+{
+ return m_ds->m_isDocumentStarted;
+}
+
+void SW602SpreadsheetListener::startDocument()
+{
+ if (m_ds->m_isDocumentStarted)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::startDocument: the document is already started\n"));
+ return;
+ }
+
+ m_documentInterface->startDocument(librevenge::RVNGPropertyList());
+ m_ds->m_isDocumentStarted = true;
+
+ m_documentInterface->setDocumentMetaData(m_ds->m_metaData);
+}
+
+void SW602SpreadsheetListener::endDocument(bool sendDelayedSubDoc)
+{
+ if (!m_ds->m_isDocumentStarted)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::endDocument: the document is not started\n"));
+ return;
+ }
+
+ if (!m_ps->m_isPageSpanOpened)
+ {
+ // we must call by hand openPageSpan to avoid sending any header/footer documents
+ if (!sendDelayedSubDoc) _openPageSpan(false);
+ _openSpan();
+ }
+
+ if (m_ps->m_isTableOpened)
+ closeTable();
+ if (m_ps->m_isParagraphOpened)
+ _closeParagraph();
+
+ m_ps->m_paragraph.m_listLevelIndex = 0;
+ _changeList(); // flush the list exterior
+
+ // close the document nice and tight
+ if (m_ds->m_isSheetOpened)
+ closeSheet();
+
+ _closePageSpan();
+ m_documentInterface->endDocument();
+ m_ds->m_isDocumentStarted = false;
+}
+
+///////////////////
+// page
+///////////////////
+bool SW602SpreadsheetListener::isPageSpanOpened() const
+{
+ return m_ps->m_isPageSpanOpened;
+}
+
+SW602PageSpan const &SW602SpreadsheetListener::getPageSpan()
+{
+ if (!m_ps->m_isPageSpanOpened)
+ _openPageSpan();
+ return m_ds->m_pageSpan;
+}
+
+
+void SW602SpreadsheetListener::_openPageSpan(bool sendHeaderFooters)
+{
+ if (m_ps->m_isPageSpanOpened)
+ return;
+
+ if (!m_ds->m_isDocumentStarted)
+ startDocument();
+
+ if (m_ds->m_pageList.size()==0)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::_openPageSpan: can not find any page\n"));
+ throw libsw602::ParseException();
+ }
+ unsigned actPage = 0;
+ std::vector<SW602PageSpan>::iterator it = m_ds->m_pageList.begin();
+ ++m_ps->m_currentPage;
+ while (true)
+ {
+ actPage+=(unsigned)it->getPageSpan();
+ if (actPage >= m_ps->m_currentPage)
+ break;
+ if (++it == m_ds->m_pageList.end())
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::_openPageSpan: can not find current page, use the previous one\n"));
+ --it;
+ break;
+ }
+ }
+ SW602PageSpan &currentPage = *it;
+
+ librevenge::RVNGPropertyList propList;
+ currentPage.getPageProperty(propList);
+ propList.insert("librevenge:is-last-page-span", ++it==m_ds->m_pageList.end());
+
+ if (!m_ps->m_isPageSpanOpened)
+ m_documentInterface->openPageSpan(propList);
+
+ m_ps->m_isPageSpanOpened = true;
+ m_ds->m_pageSpan = currentPage;
+
+ // we insert the header footer
+ if (sendHeaderFooters)
+ currentPage.sendHeaderFooters(this);
+
+ // first paragraph in span (necessary for resetting page number)
+ m_ps->m_firstParagraphInPageSpan = true;
+ m_ps->m_numPagesRemainingInSpan = (currentPage.getPageSpan() - 1);
+}
+
+void SW602SpreadsheetListener::_closePageSpan()
+{
+ if (!m_ps->m_isPageSpanOpened)
+ return;
+
+ m_documentInterface->closePageSpan();
+ m_ps->m_isPageSpanOpened = false;
+}
+
+///////////////////
+// header/footer
+///////////////////
+bool SW602SpreadsheetListener::isHeaderFooterOpened() const
+{
+ return m_ps->m_isHeaderFooterOpened;
+}
+
+bool SW602SpreadsheetListener::insertHeader(SW602SubDocumentPtr subDocument, librevenge::RVNGPropertyList const &extras)
+{
+ if (m_ps->m_isHeaderFooterOpened)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::insertHeader: Oops a header/footer is already opened\n"));
+ return false;
+ }
+ librevenge::RVNGPropertyList propList(extras);
+ m_documentInterface->openHeader(propList);
+ handleSubDocument(subDocument, libsw602::DOC_HEADER_FOOTER);
+ m_documentInterface->closeHeader();
+ return true;
+}
+
+bool SW602SpreadsheetListener::insertFooter(SW602SubDocumentPtr subDocument, librevenge::RVNGPropertyList const &extras)
+{
+ if (m_ps->m_isHeaderFooterOpened)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::insertFooter: Oops a header/footer is already opened\n"));
+ return false;
+ }
+ librevenge::RVNGPropertyList propList(extras);
+ m_documentInterface->openFooter(propList);
+ handleSubDocument(subDocument, libsw602::DOC_HEADER_FOOTER);
+ m_documentInterface->closeFooter();
+ return true;
+}
+
+///////////////////
+// section
+///////////////////
+SW602Section const &SW602SpreadsheetListener::getSection() const
+{
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::getSection: make no sense\n"));
+ static SW602Section const badSection;
+ return badSection;
+}
+
+bool SW602SpreadsheetListener::openSection(SW602Section const &)
+{
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::openSection: make no sense\n"));
+ return false;
+}
+
+bool SW602SpreadsheetListener::closeSection()
+{
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::closeSection: make no sense\n"));
+ return false;
+}
+
+///////////////////
+// paragraph
+///////////////////
+void SW602SpreadsheetListener::_openParagraph()
+{
+ if (!m_ps->canWriteText())
+ return;
+
+ if (m_ps->m_isParagraphOpened || m_ps->m_isListElementOpened)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::_openParagraph: a paragraph (or a list) is already opened"));
+ return;
+ }
+
+ librevenge::RVNGPropertyList propList;
+ m_ps->m_paragraph.addTo(propList, false);
+ if (!m_ps->m_isParagraphOpened)
+ m_documentInterface->openParagraph(propList);
+
+ _resetParagraphState();
+ m_ps->m_firstParagraphInPageSpan = false;
+}
+
+void SW602SpreadsheetListener::_closeParagraph()
+{
+ // we can not close a paragraph in a link
+ if (m_ps->m_inLink)
+ return;
+ if (m_ps->m_isListElementOpened)
+ {
+ _closeListElement();
+ return;
+ }
+
+ if (m_ps->m_isParagraphOpened)
+ {
+ if (m_ps->m_isSpanOpened)
+ _closeSpan();
+
+ m_documentInterface->closeParagraph();
+ }
+
+ m_ps->m_isParagraphOpened = false;
+ m_ps->m_paragraph.m_listLevelIndex = 0;
+}
+
+void SW602SpreadsheetListener::_resetParagraphState(const bool isListElement)
+{
+ m_ps->m_isListElementOpened = isListElement;
+ m_ps->m_isParagraphOpened = true;
+ m_ps->m_isHeaderFooterWithoutParagraph = false;
+}
+
+///////////////////
+// list
+///////////////////
+void SW602SpreadsheetListener::_openListElement()
+{
+ if (!m_ps->canWriteText())
+ return;
+
+ if (m_ps->m_isParagraphOpened || m_ps->m_isListElementOpened)
+ return;
+
+ librevenge::RVNGPropertyList propList;
+ m_ps->m_paragraph.addTo(propList, false);
+ // check if we must change the start value
+ int startValue=m_ps->m_paragraph.m_listStartValue.get();
+ if (startValue > 0 && m_ps->m_list && m_ps->m_list->getStartValueForNextElement() != startValue)
+ {
+ propList.insert("text:start-value", startValue);
+ m_ps->m_list->setStartValueForNextElement(startValue);
+ }
+
+ if (m_ps->m_list) m_ps->m_list->openElement();
+ m_documentInterface->openListElement(propList);
+ _resetParagraphState(true);
+}
+
+void SW602SpreadsheetListener::_closeListElement()
+{
+ if (m_ps->m_isListElementOpened)
+ {
+ if (m_ps->m_isSpanOpened)
+ _closeSpan();
+
+ if (m_ps->m_list) m_ps->m_list->closeElement();
+ m_documentInterface->closeListElement();
+ }
+
+ m_ps->m_isListElementOpened = m_ps->m_isParagraphOpened = false;
+}
+
+int SW602SpreadsheetListener::_getListId() const
+{
+ size_t newLevel= (size_t) m_ps->m_paragraph.m_listLevelIndex.get();
+ if (newLevel == 0) return -1;
+ int newListId = m_ps->m_paragraph.m_listId.get();
+ if (newListId > 0) return newListId;
+ static bool first = true;
+ if (first)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::_getListId: the list id is not set, try to find a new one\n"));
+ first = false;
+ }
+ boost::shared_ptr<SW602List> list=m_parserState.m_listManager->getNewList
+ (m_ps->m_list, int(newLevel), *m_ps->m_paragraph.m_listLevel);
+ if (!list) return -1;
+ return list->getId();
+}
+
+void SW602SpreadsheetListener::_changeList()
+{
+ if (!m_ps->canWriteText())
+ return;
+
+ if (m_ps->m_isParagraphOpened)
+ _closeParagraph();
+
+ size_t actualLevel = m_ps->m_listOrderedLevels.size();
+ size_t newLevel= (size_t) m_ps->m_paragraph.m_listLevelIndex.get();
+ int newListId = newLevel>0 ? _getListId() : -1;
+ bool changeList = newLevel &&
+ (m_ps->m_list && m_ps->m_list->getId()!=newListId);
+ size_t minLevel = changeList ? 0 : newLevel;
+ while (actualLevel > minLevel)
+ {
+ if (m_ps->m_listOrderedLevels[--actualLevel])
+ m_documentInterface->closeOrderedListLevel();
+ else
+ m_documentInterface->closeUnorderedListLevel();
+ }
+
+ if (newLevel)
+ {
+ boost::shared_ptr<SW602List> theList;
+
+ theList=m_parserState.m_listManager->getList(newListId);
+ if (!theList)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::_changeList: can not find any list\n"));
+ m_ps->m_listOrderedLevels.resize(actualLevel);
+ return;
+ }
+ m_parserState.m_listManager->needToSend(newListId, m_ds->m_sentListMarkers);
+ m_ps->m_list = theList;
+ m_ps->m_list->setLevel((int)newLevel);
+ }
+
+ m_ps->m_listOrderedLevels.resize(newLevel, false);
+ if (actualLevel == newLevel) return;
+
+ for (size_t i=actualLevel+1; i<= newLevel; i++)
+ {
+ bool ordered = m_ps->m_list->isNumeric(int(i));
+ m_ps->m_listOrderedLevels[i-1] = ordered;
+
+ librevenge::RVNGPropertyList level;
+ m_ps->m_list->addTo(int(i), level);
+ if (ordered)
+ m_documentInterface->openOrderedListLevel(level);
+ else
+ m_documentInterface->openUnorderedListLevel(level);
+ }
+}
+
+///////////////////
+// span
+///////////////////
+void SW602SpreadsheetListener::_openSpan()
+{
+ if (m_ps->m_isSpanOpened || !m_ps->canWriteText())
+ return;
+
+ if (!m_ps->m_isParagraphOpened && !m_ps->m_isListElementOpened)
+ {
+ _changeList();
+ if (*m_ps->m_paragraph.m_listLevelIndex == 0)
+ _openParagraph();
+ else
+ _openListElement();
+ }
+
+ librevenge::RVNGPropertyList propList;
+ m_ps->m_font.addTo(propList);
+
+ m_documentInterface->openSpan(propList);
+
+ m_ps->m_isSpanOpened = true;
+}
+
+void SW602SpreadsheetListener::_closeSpan()
+{
+ // better not to close a link...
+ if (!m_ps->m_isSpanOpened)
+ return;
+
+ _flushText();
+ m_documentInterface->closeSpan();
+ m_ps->m_isSpanOpened = false;
+}
+
+///////////////////
+// text (send data)
+///////////////////
+void SW602SpreadsheetListener::_flushDeferredTabs()
+{
+ if (m_ps->m_numDeferredTabs == 0 || !m_ps->canWriteText())
+ return;
+ if (!m_ps->m_font.hasDecorationLines())
+ {
+ if (!m_ps->m_isSpanOpened) _openSpan();
+ for (; m_ps->m_numDeferredTabs > 0; m_ps->m_numDeferredTabs--)
+ m_documentInterface->insertTab();
+ return;
+ }
+
+ SW602Font oldFont(m_ps->m_font);
+ m_ps->m_font.resetDecorationLines();
+ _closeSpan();
+ _openSpan();
+ for (; m_ps->m_numDeferredTabs > 0; m_ps->m_numDeferredTabs--)
+ m_documentInterface->insertTab();
+ setFont(oldFont);
+}
+
+void SW602SpreadsheetListener::_flushText()
+{
+ if (m_ps->m_textBuffer.len() == 0 || !m_ps->canWriteText()) return;
+
+ // when some many ' ' follows each other, call insertSpace
+ librevenge::RVNGString tmpText;
+ int numConsecutiveSpaces = 0;
+ librevenge::RVNGString::Iter i(m_ps->m_textBuffer);
+ for (i.rewind(); i.next();)
+ {
+ if (*(i()) == 0x20) // this test is compatible with unicode format
+ numConsecutiveSpaces++;
+ else
+ numConsecutiveSpaces = 0;
+
+ if (numConsecutiveSpaces > 1)
+ {
+ if (tmpText.len() > 0)
+ {
+ m_documentInterface->insertText(tmpText);
+ tmpText.clear();
+ }
+ m_documentInterface->insertSpace();
+ }
+ else
+ tmpText.append(i());
+ }
+ m_documentInterface->insertText(tmpText);
+ m_ps->m_textBuffer.clear();
+}
+
+///////////////////
+// Note/Comment/picture/textbox
+///////////////////
+void SW602SpreadsheetListener::insertNote(SW602Note const &note, SW602SubDocumentPtr &subDocument)
+{
+ if (m_ps->m_isNote)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::insertNote try to insert a note recursively (ignored)\n"));
+ return;
+ }
+ if (!canWriteText())
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::insertNote called outside a text zone (ignored)\n"));
+ return;
+ }
+ m_ps->m_isNote = true;
+ if (m_ps->m_isHeaderFooterOpened)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::insertNote try to insert a note in a header/footer\n"));
+ /** Must not happen excepted in corrupted document, so we do the minimum.
+ Note that we have no choice, either we begin by closing the paragraph,
+ ... or we reprogram handleSubDocument.
+ */
+ if (m_ps->m_isParagraphOpened)
+ _closeParagraph();
+ int prevListLevel = *m_ps->m_paragraph.m_listLevelIndex;
+ m_ps->m_paragraph.m_listLevelIndex = 0;
+ _changeList(); // flush the list exterior
+ handleSubDocument(subDocument, libsw602::DOC_NOTE);
+ m_ps->m_paragraph.m_listLevelIndex = prevListLevel;
+ }
+ else
+ {
+ if (!m_ps->m_isParagraphOpened)
+ _openParagraph();
+ else
+ {
+ _flushText();
+ _closeSpan();
+ }
+
+ librevenge::RVNGPropertyList propList;
+ if (note.m_label.len())
+ propList.insert("text:label", librevenge::RVNGPropertyFactory::newStringProp(note.m_label));
+ if (note.m_type == SW602Note::FootNote)
+ {
+ if (note.m_number >= 0)
+ m_ds->m_footNoteNumber = note.m_number;
+ else
+ m_ds->m_footNoteNumber++;
+ propList.insert("librevenge:number", m_ds->m_footNoteNumber);
+ m_documentInterface->openFootnote(propList);
+ handleSubDocument(subDocument, libsw602::DOC_NOTE);
+ m_documentInterface->closeFootnote();
+ }
+ else
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::insertNote try to insert a unexpected note\n"));
+ }
+ }
+ m_ps->m_isNote = false;
+}
+
+void SW602SpreadsheetListener::insertComment(SW602SubDocumentPtr &subDocument)
+{
+ if (m_ps->m_isNote)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::insertComment try to insert a comment in a note (ignored)\n"));
+ return;
+ }
+ if (!canWriteText())
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::insertComment called outside a text zone (ignored)\n"));
+ return;
+ }
+
+ if (!m_ps->m_isSheetCellOpened)
+ {
+ if (!m_ps->m_isParagraphOpened)
+ _openParagraph();
+ else
+ {
+ _flushText();
+ _closeSpan();
+ }
+ }
+ else if (m_ps->m_isParagraphOpened)
+ _closeParagraph();
+
+ librevenge::RVNGPropertyList propList;
+ m_documentInterface->openComment(propList);
+
+ m_ps->m_isNote = true;
+ handleSubDocument(subDocument, libsw602::DOC_COMMENT_ANNOTATION);
+
+ m_documentInterface->closeComment();
+ m_ps->m_isNote = false;
+}
+
+void SW602SpreadsheetListener::insertTextBox
+(SW602Position const &pos, SW602SubDocumentPtr subDocument, SW602GraphicStyle const &frameStyle)
+{
+ if (!m_ds->m_isSheetOpened || (m_ds->m_isSheetRowOpened && pos.m_anchorTo!=SW602Position::Cell))
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::insertTextBox insert a textbox outside a sheet is not implemented\n"));
+ return;
+ }
+ if (!openFrame(pos, frameStyle)) return;
+
+ librevenge::RVNGPropertyList propList;
+ if (!frameStyle.m_frameNextName.empty())
+ propList.insert("librevenge:next-frame-name",frameStyle.m_frameNextName.c_str());
+ m_documentInterface->openTextBox(propList);
+ handleSubDocument(subDocument, libsw602::DOC_TEXT_BOX);
+ m_documentInterface->closeTextBox();
+
+ closeFrame();
+}
+
+void SW602SpreadsheetListener::insertShape
+(SW602Position const &pos, SW602GraphicShape const &shape, SW602GraphicStyle const &style)
+{
+ if (!m_ds->m_isSheetOpened || (m_ds->m_isSheetRowOpened && pos.m_anchorTo!=SW602Position::Cell))
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::insertShape insert a picture outside a sheet is not implemented\n"));
+ return;
+ }
+ // sanity check: avoid to send to many small pict
+ float factor=pos.getScaleFactor(pos.unit(), librevenge::RVNG_POINT);
+ if (pos.size()[0]*factor <= 8 && pos.size()[1]*factor <= 8 && m_ds->m_smallPictureNumber++ > 200)
+ {
+ static bool first = true;
+ if (first)
+ {
+ first = false;
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::insertShape: find too much small pictures, skip them from now\n"));
+ }
+ return;
+ }
+
+ // now check that the anchor is coherent with the actual state
+ switch (pos.m_anchorTo)
+ {
+ case SW602Position::Page:
+ break;
+ case SW602Position::Paragraph:
+ if (m_ps->m_isParagraphOpened)
+ _flushText();
+ else
+ _openParagraph();
+ break;
+ case SW602Position::Unknown:
+ default:
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::insertShape: UNKNOWN position, insert as char position\n"));
+ // fallthrough intended
+ case SW602Position::CharBaseLine:
+ case SW602Position::Char:
+ if (m_ps->m_isSpanOpened)
+ _flushText();
+ else
+ _openSpan();
+ break;
+ case SW602Position::Cell:
+ case SW602Position::Frame:
+ break;
+ }
+
+ librevenge::RVNGPropertyList shapePList;
+ _handleFrameParameters(shapePList, pos);
+ shapePList.remove("svg:x");
+ shapePList.remove("svg:y");
+
+ librevenge::RVNGPropertyList list;
+ style.addTo(list, shape.getType()==SW602GraphicShape::Line);
+
+ SW602Vec2f decal = factor*pos.origin();
+ switch (shape.addTo(decal, style.hasSurface(), shapePList))
+ {
+ case SW602GraphicShape::C_Ellipse:
+ m_documentInterface->defineGraphicStyle(list);
+ m_documentInterface->drawEllipse(shapePList);
+ break;
+ case SW602GraphicShape::C_Path:
+ m_documentInterface->defineGraphicStyle(list);
+ m_documentInterface->drawPath(shapePList);
+ break;
+ case SW602GraphicShape::C_Polyline:
+ m_documentInterface->defineGraphicStyle(list);
+ m_documentInterface->drawPolyline(shapePList);
+ break;
+ case SW602GraphicShape::C_Polygon:
+ m_documentInterface->defineGraphicStyle(list);
+ m_documentInterface->drawPolygon(shapePList);
+ break;
+ case SW602GraphicShape::C_Rectangle:
+ m_documentInterface->defineGraphicStyle(list);
+ m_documentInterface->drawRectangle(shapePList);
+ break;
+ case SW602GraphicShape::C_Bad:
+ break;
+ default:
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::insertShape: unexpected shape\n"));
+ break;
+ }
+}
+
+void SW602SpreadsheetListener::insertPicture(SW602Position const &pos, SW602EmbeddedObject const &picture, SW602GraphicStyle const &style)
+{
+ if (!m_ds->m_isSheetOpened || (m_ds->m_isSheetRowOpened && pos.m_anchorTo!=SW602Position::Cell))
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::insertPicture insert a picture outside a sheet is not implemented\n"));
+ return;
+ }
+ // sanity check: avoid to send to many small pict
+ float factor=pos.getScaleFactor(pos.unit(), librevenge::RVNG_POINT);
+ if (pos.size()[0]*factor <= 8 && pos.size()[1]*factor <= 8 && m_ds->m_smallPictureNumber++ > 200)
+ {
+ static bool first = true;
+ if (first)
+ {
+ first = false;
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::insertPicture: find too much small pictures, skip them from now\n"));
+ }
+ return;
+ }
+ if (!openFrame(pos, style)) return;
+
+ librevenge::RVNGPropertyList propList;
+ if (picture.addTo(propList))
+ m_documentInterface->insertBinaryObject(propList);
+
+ closeFrame();
+}
+
+///////////////////
+// frame
+///////////////////
+bool SW602SpreadsheetListener::openGroup(SW602Position const &/*pos*/)
+{
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::openGroup is not implemented\n"));
+ return false;
+}
+
+void SW602SpreadsheetListener::closeGroup()
+{
+}
+
+bool SW602SpreadsheetListener::openFrame(SW602Position const &pos, SW602GraphicStyle const &style)
+{
+ if (!m_ds->m_isSheetOpened || (m_ds->m_isSheetRowOpened && pos.m_anchorTo!=SW602Position::Cell))
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::openFrame insert a frame outside a sheet is not implemented\n"));
+ return false;
+ }
+ if (m_ps->m_isFrameOpened)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::openFrame: called but a frame is already opened\n"));
+ return false;
+ }
+ SW602Position fPos(pos);
+ switch (pos.m_anchorTo)
+ {
+ case SW602Position::Page:
+ break;
+ case SW602Position::Paragraph:
+ if (m_ps->m_isParagraphOpened)
+ _flushText();
+ else
+ _openParagraph();
+ break;
+ case SW602Position::Unknown:
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::openFrame: UNKNOWN position, insert as char position\n"));
+ // fallthrough intended
+ case SW602Position::CharBaseLine:
+ case SW602Position::Char:
+ if (m_ps->m_isSpanOpened)
+ _flushText();
+ else
+ _openSpan();
+ break;
+ case SW602Position::Frame:
+ if (!m_ds->m_subDocuments.size())
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::openFrame: can not determine the frame\n"));
+ return false;
+ }
+ if (m_ps->m_subDocumentType==libsw602::DOC_HEADER_FOOTER)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::openFrame: called with Frame position in header footer, switch to paragraph\n"));
+ if (m_ps->m_isParagraphOpened)
+ _flushText();
+ else
+ _openParagraph();
+ fPos.m_anchorTo=SW602Position::Paragraph;
+ }
+ break;
+ case SW602Position::Cell:
+ if (!m_ps->m_isSheetCellOpened)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::openFrame: called with Cell position not in a sheet cell\n"));
+ return false;
+ }
+ if (pos.m_anchorCellName.empty())
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::openFrame: can not find the cell name\n"));
+ return false;
+ }
+ break;
+ default:
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::openFrame: can not determine the anchor\n"));
+ return false;
+ }
+
+ librevenge::RVNGPropertyList propList;
+ style.addFrameTo(propList);
+ if (!propList["draw:fill"])
+ propList.insert("draw:fill","none");
+ _handleFrameParameters(propList, fPos);
+ m_documentInterface->openFrame(propList);
+
+ m_ps->m_isFrameOpened = true;
+ return true;
+}
+
+void SW602SpreadsheetListener::closeFrame()
+{
+ if (!m_ps->m_isFrameOpened)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::closeFrame: called but no frame is already opened\n"));
+ return;
+ }
+ m_documentInterface->closeFrame();
+ m_ps->m_isFrameOpened = false;
+}
+
+void SW602SpreadsheetListener::_handleFrameParameters
+(librevenge::RVNGPropertyList &propList, SW602Position const &pos)
+{
+ SW602Vec2f origin = pos.origin();
+ librevenge::RVNGUnit unit = pos.unit();
+ float inchFactor=pos.getInvUnitScale(librevenge::RVNG_INCH);
+ float pointFactor = pos.getInvUnitScale(librevenge::RVNG_POINT);
+
+ if (pos.size()[0]>0)
+ propList.insert("svg:width", double(pos.size()[0]), unit);
+ else if (pos.size()[0]<0)
+ propList.insert("fo:min-width", double(-pos.size()[0]), unit);
+ if (pos.size()[1]>0)
+ propList.insert("svg:height", double(pos.size()[1]), unit);
+ else if (pos.size()[1]<0)
+ propList.insert("fo:min-height", double(-pos.size()[1]), unit);
+ if (pos.order() > 0)
+ propList.insert("draw:z-index", pos.order());
+ if (pos.naturalSize().x() > 4*pointFactor && pos.naturalSize().y() > 4*pointFactor)
+ {
+ propList.insert("librevenge:naturalWidth", pos.naturalSize().x(), pos.unit());
+ propList.insert("librevenge:naturalHeight", pos.naturalSize().y(), pos.unit());
+ }
+ SW602Vec2f TLClip = (1.f/pointFactor)*pos.leftTopClipping();
+ SW602Vec2f RBClip = (1.f/pointFactor)*pos.rightBottomClipping();
+ if (TLClip[0] > 0 || TLClip[1] > 0 || RBClip[0] > 0 || RBClip[1] > 0)
+ {
+ // in ODF1.2 we need to separate the value with ,
+ std::stringstream s;
+ s << "rect(" << TLClip[1] << "pt " << RBClip[0] << "pt "
+ << RBClip[1] << "pt " << TLClip[0] << "pt)";
+ propList.insert("fo:clip", s.str().c_str());
+ }
+
+ if (pos.m_wrapping == SW602Position::WDynamic)
+ propList.insert("style:wrap", "dynamic");
+ else if (pos.m_wrapping == SW602Position::WBackground)
+ {
+ propList.insert("style:wrap", "run-through");
+ propList.insert("style:run-through", "background");
+ }
+ else if (pos.m_wrapping == SW602Position::WForeground)
+ {
+ propList.insert("style:wrap", "run-through");
+ propList.insert("style:run-through", "foreground");
+ }
+ else if (pos.m_wrapping == SW602Position::WParallel)
+ {
+ propList.insert("style:wrap", "parallel");
+ propList.insert("style:run-through", "foreground");
+ }
+ else if (pos.m_wrapping == SW602Position::WRunThrough)
+ propList.insert("style:wrap", "run-through");
+ else
+ propList.insert("style:wrap", "none");
+
+ if (pos.m_anchorTo == SW602Position::Paragraph ||
+ pos.m_anchorTo == SW602Position::Frame)
+ {
+ std::string what= pos.m_anchorTo == SW602Position::Paragraph ?
+ "paragraph" : "frame";
+ propList.insert("text:anchor-type", what.c_str());
+ propList.insert("style:vertical-rel", what.c_str());
+ propList.insert("style:horizontal-rel", what.c_str());
+ double w = m_ds->m_pageSpan.getPageWidth() - m_ps->m_paragraph.getMarginsWidth();
+ w *= inchFactor;
+ switch (pos.m_xPos)
+ {
+ case SW602Position::XRight:
+ if (origin[0] < 0.0 || origin[0] > 0.0)
+ {
+ propList.insert("style:horizontal-pos", "from-left");
+ propList.insert("svg:x", double(origin[0] - pos.size()[0] + w), unit);
+ }
+ else
+ propList.insert("style:horizontal-pos", "right");
+ break;
+ case SW602Position::XCenter:
+ if (origin[0] < 0.0 || origin[0] > 0.0)
+ {
+ propList.insert("style:horizontal-pos", "from-left");
+ propList.insert("svg:x", double(origin[0] - pos.size()[0]/2.0 + w/2.0), unit);
+ }
+ else
+ propList.insert("style:horizontal-pos", "center");
+ break;
+ case SW602Position::XLeft:
+ case SW602Position::XFull:
+ default:
+ if (origin[0] < 0.0 || origin[0] > 0.0)
+ {
+ propList.insert("style:horizontal-pos", "from-left");
+ propList.insert("svg:x", double(origin[0]), unit);
+ }
+ else
+ propList.insert("style:horizontal-pos", "left");
+ break;
+ }
+
+ if (origin[1] < 0.0 || origin[1] > 0.0)
+ {
+ propList.insert("style:vertical-pos", "from-top");
+ propList.insert("svg:y", double(origin[1]), unit);
+ }
+ else
+ propList.insert("style:vertical-pos", "top");
+ return;
+ }
+
+ if (pos.m_anchorTo == SW602Position::Page)
+ {
+ // Page position seems to do not use the page margin...
+ propList.insert("text:anchor-type", "page");
+ if (pos.page() > 0) propList.insert("text:anchor-page-number", pos.page());
+ double w = m_ds->m_pageSpan.getFormWidth();
+ double h = m_ds->m_pageSpan.getFormLength();
+ w *= inchFactor;
+ h *= inchFactor;
+
+ propList.insert("style:vertical-rel", "page");
+ propList.insert("style:horizontal-rel", "page");
+ double newPosition;
+ switch (pos.m_yPos)
+ {
+ case SW602Position::YFull:
+ propList.insert("svg:height", double(h), unit);
+ // fallthrough intended
+ case SW602Position::YTop:
+ if (origin[1] < 0.0 || origin[1] > 0.0)
+ {
+ propList.insert("style:vertical-pos", "from-top");
+ newPosition = origin[1];
+ if (newPosition > h -pos.size()[1])
+ newPosition = h - pos.size()[1];
+ propList.insert("svg:y", double(newPosition), unit);
+ }
+ else
+ propList.insert("style:vertical-pos", "top");
+ break;
+ case SW602Position::YCenter:
+ if (origin[1] < 0.0 || origin[1] > 0.0)
+ {
+ propList.insert("style:vertical-pos", "from-top");
+ newPosition = (h - pos.size()[1])/2.0;
+ if (newPosition > h -pos.size()[1]) newPosition = h - pos.size()[1];
+ propList.insert("svg:y", double(newPosition), unit);
+ }
+ else
+ propList.insert("style:vertical-pos", "middle");
+ break;
+ case SW602Position::YBottom:
+ if (origin[1] < 0.0 || origin[1] > 0.0)
+ {
+ propList.insert("style:vertical-pos", "from-top");
+ newPosition = h - pos.size()[1]-origin[1];
+ if (newPosition > h -pos.size()[1]) newPosition = h -pos.size()[1];
+ else if (newPosition < 0) newPosition = 0;
+ propList.insert("svg:y", double(newPosition), unit);
+ }
+ else
+ propList.insert("style:vertical-pos", "bottom");
+ break;
+ default:
+ break;
+ }
+
+ switch (pos.m_xPos)
+ {
+ case SW602Position::XFull:
+ propList.insert("svg:width", double(w), unit);
+ // fallthrough intended
+ case SW602Position::XLeft:
+ if (origin[0] < 0.0 || origin[0] > 0.0)
+ {
+ propList.insert("style:horizontal-pos", "from-left");
+ propList.insert("svg:x", double(origin[0]), unit);
+ }
+ else
+ propList.insert("style:horizontal-pos", "left");
+ break;
+ case SW602Position::XRight:
+ if (origin[0] < 0.0 || origin[0] > 0.0)
+ {
+ propList.insert("style:horizontal-pos", "from-left");
+ propList.insert("svg:x",double(w - pos.size()[0] + origin[0]), unit);
+ }
+ else
+ propList.insert("style:horizontal-pos", "right");
+ break;
+ case SW602Position::XCenter:
+ if (origin[0] < 0.0 || origin[0] > 0.0)
+ {
+ propList.insert("style:horizontal-pos", "from-left");
+ propList.insert("svg:x", double((w - pos.size()[0])/2. + origin[0]), unit);
+ }
+ else
+ propList.insert("style:horizontal-pos", "center");
+ break;
+ default:
+ break;
+ }
+ return;
+ }
+
+ if (pos.m_anchorTo == SW602Position::Cell)
+ {
+ if (!pos.m_anchorCellName.empty())
+ propList.insert("table:end-cell-address", pos.m_anchorCellName);
+ // todo: implement also different m_xPos and m_yPos
+ if (origin[0] < 0.0 || origin[0] > 0.0)
+ propList.insert("svg:x", double(origin[0]), unit);
+ if (origin[1] < 0.0 || origin[1] > 0.0)
+ propList.insert("svg:y", double(origin[1]), unit);
+ return;
+ }
+
+ if (pos.m_anchorTo != SW602Position::Char &&
+ pos.m_anchorTo != SW602Position::CharBaseLine &&
+ pos.m_anchorTo != SW602Position::Unknown) return;
+
+ propList.insert("text:anchor-type", "as-char");
+ if (pos.m_anchorTo == SW602Position::CharBaseLine)
+ propList.insert("style:vertical-rel", "baseline");
+ else
+ propList.insert("style:vertical-rel", "line");
+ switch (pos.m_yPos)
+ {
+ case SW602Position::YFull:
+ case SW602Position::YTop:
+ if (origin[1] < 0.0 || origin[1] > 0.0)
+ {
+ propList.insert("style:vertical-pos", "from-top");
+ propList.insert("svg:y", double(origin[1]), unit);
+ }
+ else
+ propList.insert("style:vertical-pos", "top");
+ break;
+ case SW602Position::YCenter:
+ if (origin[1] < 0.0 || origin[1] > 0.0)
+ {
+ propList.insert("style:vertical-pos", "from-top");
+ propList.insert("svg:y", double(origin[1] - pos.size()[1]/2.0), unit);
+ }
+ else
+ propList.insert("style:vertical-pos", "middle");
+ break;
+ case SW602Position::YBottom:
+ default:
+ if (origin[1] < 0.0 || origin[1] > 0.0)
+ {
+ propList.insert("style:vertical-pos", "from-top");
+ propList.insert("svg:y", double(origin[1] - pos.size()[1]), unit);
+ }
+ else
+ propList.insert("style:vertical-pos", "bottom");
+ break;
+ }
+}
+
+///////////////////
+// subdocument
+///////////////////
+void SW602SpreadsheetListener::handleSubDocument(SW602SubDocumentPtr subDocument, libsw602::SubDocumentType subDocumentType)
+{
+ _pushParsingState();
+ _startSubDocument();
+ m_ps->m_subDocumentType = subDocumentType;
+
+ m_ps->m_isPageSpanOpened = true;
+ m_ps->m_list.reset();
+
+ switch (subDocumentType)
+ {
+ case libsw602::DOC_TEXT_BOX:
+ m_ps->m_isTextboxOpened = true;
+ m_ds->m_pageSpan.setMargins(0.0);
+ break;
+ case libsw602::DOC_HEADER_FOOTER:
+ m_ps->m_isHeaderFooterWithoutParagraph = true;
+ m_ps->m_isHeaderFooterOpened = true;
+ break;
+ case libsw602::DOC_CHART_ZONE:
+ m_ps->m_isTextboxOpened = true;
+ break;
+ case libsw602::DOC_NONE:
+ case libsw602::DOC_CHART:
+ case libsw602::DOC_NOTE:
+ case libsw602::DOC_SHEET:
+ case libsw602::DOC_TABLE:
+ case libsw602::DOC_COMMENT_ANNOTATION:
+ case libsw602::DOC_GRAPHIC_GROUP:
+ default:
+ break;
+ }
+
+ // Check whether the document is calling itself
+ bool sendDoc = true;
+ for (size_t i = 0; i < m_ds->m_subDocuments.size(); i++)
+ {
+ if (!subDocument)
+ break;
+ if (subDocument == m_ds->m_subDocuments[i])
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::handleSubDocument: recursif call, stop...\n"));
+ sendDoc = false;
+ break;
+ }
+ }
+ if (sendDoc)
+ {
+ if (subDocument)
+ {
+ m_ds->m_subDocuments.push_back(subDocument);
+ boost::shared_ptr<SW602Listener> listen(this, SW602_shared_ptr_noop_deleter<SW602SpreadsheetListener>());
+ try
+ {
+ subDocument->parse(listen, subDocumentType);
+ }
+ catch (...)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::handleSubDocument exception catched \n"));
+ }
+ m_ds->m_subDocuments.pop_back();
+ }
+ if (m_ps->m_isHeaderFooterWithoutParagraph)
+ _openSpan();
+ }
+
+ _endSubDocument();
+ _popParsingState();
+}
+
+bool SW602SpreadsheetListener::isSubDocumentOpened(libsw602::SubDocumentType &subdocType) const
+{
+ if (!m_ps->m_inSubDocument)
+ return false;
+ subdocType = m_ps->m_subDocumentType;
+ return true;
+}
+
+void SW602SpreadsheetListener::_startSubDocument()
+{
+ m_ds->m_isDocumentStarted = true;
+ m_ps->m_inSubDocument = true;
+}
+
+void SW602SpreadsheetListener::_endSubDocument()
+{
+ if (m_ps->m_isTableOpened)
+ closeTable();
+ if (m_ps->m_isSpanOpened)
+ _closeSpan();
+ if (m_ps->m_isParagraphOpened)
+ _closeParagraph();
+
+ m_ps->m_paragraph.m_listLevelIndex=0;
+ _changeList(); // flush the list exterior
+}
+
+///////////////////
+// sheet
+///////////////////
+void SW602SpreadsheetListener::openSheet(std::vector<float> const &colWidth, librevenge::RVNGUnit unit,
+ std::vector<int> const &repeatColWidthNumber, std::string const &name)
+{
+ if (m_ds->m_isSheetOpened)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::openSheet: called with m_isSheetOpened=true\n"));
+ return;
+ }
+ if (!m_ps->m_isPageSpanOpened)
+ _openPageSpan();
+ if (m_ps->m_isParagraphOpened)
+ _closeParagraph();
+
+ _pushParsingState();
+ _startSubDocument();
+ m_ps->m_subDocumentType = libsw602::DOC_SHEET;
+ m_ps->m_isPageSpanOpened = true;
+
+ librevenge::RVNGPropertyList propList;
+ librevenge::RVNGPropertyListVector columns;
+ size_t nCols = colWidth.size();
+ bool useRepeated=repeatColWidthNumber.size()==nCols;
+ if (!useRepeated&&!repeatColWidthNumber.empty())
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::openSheet: repeatColWidthNumber seems bad\n"));
+ }
+ for (size_t c = 0; c < nCols; c++)
+ {
+ librevenge::RVNGPropertyList column;
+ column.insert("style:column-width", colWidth[c], unit);
+ if (useRepeated && repeatColWidthNumber[c]>1)
+ column.insert("table:number-columns-repeated", repeatColWidthNumber[c]);
+ columns.append(column);
+ }
+ propList.insert("librevenge:columns", columns);
+ if (!name.empty())
+ propList.insert("librevenge:sheet-name", name.c_str());
+ m_documentInterface->openSheet(propList);
+ m_ds->m_isSheetOpened = true;
+}
+
+void SW602SpreadsheetListener::closeSheet()
+{
+ if (!m_ds->m_isSheetOpened)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::closeSheet: called with m_isSheetOpened=false\n"));
+ return;
+ }
+
+ m_ds->m_isSheetOpened = false;
+ m_documentInterface->closeSheet();
+ _endSubDocument();
+ _popParsingState();
+}
+
+void SW602SpreadsheetListener::openSheetRow(float h, librevenge::RVNGUnit unit, int numRepeated)
+{
+ if (m_ds->m_isSheetRowOpened)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::openSheetRow: called with m_isSheetRowOpened=true\n"));
+ return;
+ }
+ if (!m_ds->m_isSheetOpened)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::openSheetRow: called with m_isSheetOpened=false\n"));
+ return;
+ }
+ librevenge::RVNGPropertyList propList;
+ if (h > 0)
+ propList.insert("style:row-height", h, unit);
+ else if (h < 0)
+ propList.insert("style:min-row-height", -h, unit);
+ if (numRepeated>1)
+ propList.insert("table:number-rows-repeated", numRepeated);
+ m_documentInterface->openSheetRow(propList);
+ m_ds->m_isSheetRowOpened = true;
+}
+
+void SW602SpreadsheetListener::closeSheetRow()
+{
+ if (!m_ds->m_isSheetRowOpened)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::openSheetRow: called with m_isSheetRowOpened=false\n"));
+ return;
+ }
+ m_ds->m_isSheetRowOpened = false;
+ m_documentInterface->closeSheetRow();
+}
+
+void SW602SpreadsheetListener::openSheetCell(SW602Cell const &cell, SW602CellContent const &content, int numRepeated)
+{
+ if (!m_ds->m_isSheetRowOpened)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::openSheetCell: called with m_isSheetRowOpened=false\n"));
+ return;
+ }
+ if (m_ps->m_isSheetCellOpened)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::openSheetCell: called with m_isSheetCellOpened=true\n"));
+ closeSheetCell();
+ }
+
+ librevenge::RVNGPropertyList propList;
+ cell.addTo(propList);
+ if (numRepeated>1)
+ propList.insert("table:number-columns-repeated", numRepeated);
+ SW602Cell::Format const &format=cell.getFormat();
+ if (!format.hasBasicFormat())
+ {
+ int numberingId=-1;
+ std::stringstream name;
+ if (m_ds->m_numberingIdMap.find(format)!=m_ds->m_numberingIdMap.end())
+ {
+ numberingId=m_ds->m_numberingIdMap.find(format)->second;
+ name << "Numbering" << numberingId;
+ }
+ else
+ {
+ numberingId=(int) m_ds->m_numberingIdMap.size();
+ name << "Numbering" << numberingId;
+
+ librevenge::RVNGPropertyList numList;
+ if (format.getNumberingProperties(numList))
+ {
+ numList.insert("librevenge:name", name.str().c_str());
+ m_documentInterface->defineSheetNumberingStyle(numList);
+ m_ds->m_numberingIdMap[format]=numberingId;
+ }
+ else
+ numberingId=-1;
+ }
+ if (numberingId>=0)
+ propList.insert("librevenge:numbering-name", name.str().c_str());
+ }
+ // formula
+ if (content.m_formula.size())
+ {
+ librevenge::RVNGPropertyListVector formulaVect;
+ for (size_t i=0; i < content.m_formula.size(); ++i)
+ formulaVect.append(content.m_formula[i].getPropertyList());
+ propList.insert("librevenge:formula", formulaVect);
+ }
+ bool hasFormula=!content.m_formula.empty();
+ if (content.isValueSet() || hasFormula)
+ {
+ bool hasValue=content.isValueSet();
+ if (hasFormula && (content.m_value >= 0 && content.m_value <= 0))
+ hasValue=false;
+ switch (format.m_format)
+ {
+ case SW602Cell::F_TEXT:
+ if (!hasValue) break;
+ propList.insert("librevenge:value-type", format.getValueType().c_str());
+ propList.insert("librevenge:value", content.m_value, librevenge::RVNG_GENERIC);
+ break;
+ case SW602Cell::F_NUMBER:
+ propList.insert("librevenge:value-type", format.getValueType().c_str());
+ if (!hasValue) break;
+ propList.insert("librevenge:value", content.m_value, librevenge::RVNG_GENERIC);
+ break;
+ case SW602Cell::F_BOOLEAN:
+ propList.insert("librevenge:value-type", "boolean");
+ if (!hasValue) break;
+ propList.insert("librevenge:value", content.m_value, librevenge::RVNG_GENERIC);
+ break;
+ case SW602Cell::F_DATE:
+ {
+ propList.insert("librevenge:value-type", "date");
+ if (!hasValue) break;
+ int Y=0, M=0, D=0;
+ if (!SW602CellContent::double2Date(content.m_value, Y, M, D)) break;
+ propList.insert("librevenge:year", Y);
+ propList.insert("librevenge:month", M);
+ propList.insert("librevenge:day", D);
+ break;
+ }
+ case SW602Cell::F_TIME:
+ {
+ propList.insert("librevenge:value-type", "time");
+ if (!hasValue) break;
+ int H=0, M=0, S=0;
+ if (!SW602CellContent::double2Time(std::fmod(content.m_value,1.),H,M,S))
+ break;
+ propList.insert("librevenge:hours", H);
+ propList.insert("librevenge:minutes", M);
+ propList.insert("librevenge:seconds", S);
+ break;
+ }
+ case SW602Cell::F_UNKNOWN:
+ if (!hasValue) break;
+ propList.insert("librevenge:value-type", format.getValueType().c_str());
+ propList.insert("librevenge:value", content.m_value, librevenge::RVNG_GENERIC);
+ break;
+ default:
+ break;
+ }
+ }
+
+ m_ps->m_isSheetCellOpened = true;
+ m_documentInterface->openSheetCell(propList);
+}
+
+void SW602SpreadsheetListener::closeSheetCell()
+{
+ if (!m_ps->m_isSheetCellOpened)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::closeSheetCell: called with m_isSheetCellOpened=false\n"));
+ return;
+ }
+
+ _closeParagraph();
+
+ m_ps->m_isSheetCellOpened = false;
+ m_documentInterface->closeSheetCell();
+}
+
+void SW602SpreadsheetListener::insertTable
+(SW602Position const &pos, SW602Table &table, SW602GraphicStyle const &style)
+{
+ if (!m_ds->m_isSheetOpened || m_ds->m_isSheetRowOpened)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::insertTable insert a table outside a sheet is not implemented\n"));
+ return;
+ }
+ if (!openFrame(pos, style)) return;
+
+ _pushParsingState();
+ _startSubDocument();
+ m_ps->m_subDocumentType = libsw602::DOC_TABLE;
+
+ boost::shared_ptr<SW602Listener> listen(this, SW602_shared_ptr_noop_deleter<SW602SpreadsheetListener>());
+ try
+ {
+ table.sendTable(listen);
+ }
+ catch (...)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::insertTable exception catched \n"));
+ }
+ _endSubDocument();
+ _popParsingState();
+
+ closeFrame();
+}
+
+
+///////////////////
+// chart
+///////////////////
+void SW602SpreadsheetListener::insertChart
+(SW602Position const &pos, SW602Chart &chart, SW602GraphicStyle const &style)
+{
+ if (!m_ds->m_isSheetOpened || m_ds->m_isSheetRowOpened)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::insertChart outside a chart in a sheet is not implemented\n"));
+ return;
+ }
+ if (!openFrame(pos, style)) return;
+
+ _pushParsingState();
+ _startSubDocument();
+ m_ps->m_subDocumentType = libsw602::DOC_CHART;
+
+ boost::shared_ptr<SW602SpreadsheetListener> listen(this, SW602_shared_ptr_noop_deleter<SW602SpreadsheetListener>());
+ try
+ {
+ chart.sendChart(listen, m_documentInterface);
+ }
+ catch (...)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::insertChart exception catched \n"));
+ }
+ _endSubDocument();
+ _popParsingState();
+
+ closeFrame();
+}
+
+void SW602SpreadsheetListener::openTable(SW602Table const &table)
+{
+ if (m_ps->m_isFrameOpened || m_ps->m_isTableOpened)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::openTable: no frame is already open...\n"));
+ return;
+ }
+
+ if (m_ps->m_isParagraphOpened)
+ _closeParagraph();
+
+ // default value: which can be redefined by table
+ librevenge::RVNGPropertyList propList;
+ propList.insert("table:align", "left");
+ propList.insert("fo:margin-left", *m_ps->m_paragraph.m_margins[1], *m_ps->m_paragraph.m_marginsUnit);
+
+ _pushParsingState();
+ _startSubDocument();
+ m_ps->m_subDocumentType = libsw602::DOC_TABLE;
+
+ table.addTablePropertiesTo(propList);
+ m_documentInterface->openTable(propList);
+ m_ps->m_isTableOpened = true;
+}
+
+void SW602SpreadsheetListener::closeTable()
+{
+ if (!m_ps->m_isTableOpened)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::closeTable: called with m_isTableOpened=false\n"));
+ return;
+ }
+
+ m_ps->m_isTableOpened = false;
+ _endSubDocument();
+ m_documentInterface->closeTable();
+
+ _popParsingState();
+}
+
+void SW602SpreadsheetListener::openTableRow(float h, librevenge::RVNGUnit unit, bool headerRow)
+{
+ if (m_ps->m_isTableRowOpened)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::openTableRow: called with m_isTableRowOpened=true\n"));
+ return;
+ }
+ if (!m_ps->m_isTableOpened)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::openTableRow: called with m_isTableOpened=false\n"));
+ return;
+ }
+ librevenge::RVNGPropertyList propList;
+ propList.insert("librevenge:is-header-row", headerRow);
+
+ if (h > 0)
+ propList.insert("style:row-height", h, unit);
+ else if (h < 0)
+ propList.insert("style:min-row-height", -h, unit);
+ m_documentInterface->openTableRow(propList);
+ m_ps->m_isTableRowOpened = true;
+}
+
+void SW602SpreadsheetListener::closeTableRow()
+{
+ if (!m_ps->m_isTableRowOpened)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::openTableRow: called with m_isTableRowOpened=false\n"));
+ return;
+ }
+ m_ps->m_isTableRowOpened = false;
+ m_documentInterface->closeTableRow();
+}
+
+void SW602SpreadsheetListener::addEmptyTableCell(SW602Vec2i const &pos, SW602Vec2i span)
+{
+ if (!m_ps->m_isTableRowOpened)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::addEmptyTableCell: called with m_isTableRowOpened=false\n"));
+ return;
+ }
+ if (m_ps->m_isTableCellOpened)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::addEmptyTableCell: called with m_isTableCellOpened=true\n"));
+ closeTableCell();
+ }
+ librevenge::RVNGPropertyList propList;
+ propList.insert("librevenge:column", pos[0]);
+ propList.insert("librevenge:row", pos[1]);
+ propList.insert("table:number-columns-spanned", span[0]);
+ propList.insert("table:number-rows-spanned", span[1]);
+ m_documentInterface->openTableCell(propList);
+ m_documentInterface->closeTableCell();
+}
+
+void SW602SpreadsheetListener::openTableCell(SW602Cell const &cell)
+{
+ if (!m_ps->m_isTableRowOpened)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::openTableCell: called with m_isTableRowOpened=false\n"));
+ return;
+ }
+ if (m_ps->m_isTableCellOpened)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::openTableCell: called with m_isTableCellOpened=true\n"));
+ closeTableCell();
+ }
+
+ librevenge::RVNGPropertyList propList;
+ cell.addTo(propList);
+ m_ps->m_isTableCellOpened = true;
+ m_documentInterface->openTableCell(propList);
+}
+
+void SW602SpreadsheetListener::closeTableCell()
+{
+ if (!m_ps->m_isTableCellOpened)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::closeTableCell: called with m_isTableCellOpened=false\n"));
+ return;
+ }
+
+ _closeParagraph();
+ m_ps->m_paragraph.m_listLevelIndex=0;
+ _changeList(); // flush the list exterior
+
+ m_ps->m_isTableCellOpened = false;
+ m_documentInterface->closeTableCell();
+}
+
+///////////////////
+// others
+///////////////////
+
+// ---------- state stack ------------------
+boost::shared_ptr<SW602SpreadsheetListenerInternal::State> SW602SpreadsheetListener::_pushParsingState()
+{
+ boost::shared_ptr<SW602SpreadsheetListenerInternal::State> actual = m_ps;
+ m_psStack.push_back(actual);
+ m_ps.reset(new SW602SpreadsheetListenerInternal::State);
+
+ m_ps->m_isNote = actual->m_isNote;
+
+ return actual;
+}
+
+void SW602SpreadsheetListener::_popParsingState()
+{
+ if (m_psStack.size()==0)
+ {
+ SW602_DEBUG_MSG(("SW602SpreadsheetListener::_popParsingState: psStack is empty()\n"));
+ throw libsw602::ParseException();
+ }
+ m_ps = m_psStack.back();
+ m_psStack.pop_back();
+}
+
+}
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602SpreadsheetListener.h b/src/lib/SW602SpreadsheetListener.h
new file mode 100644
index 0000000..a1b243d
--- /dev/null
+++ b/src/lib/SW602SpreadsheetListener.h
@@ -0,0 +1,289 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/** \file SW602SpreadsheetListener.h
+ * Defines SW602SpreadsheetListener: the libsw602 spreadsheet processor listener
+ *
+ * \note this class is the only class which does the interface with
+ * the librevenge::RVNGSpreadsheetInterface
+ */
+#ifndef INCLUDED_SW602_SPREADSHEET_LISTENER_H
+#define INCLUDED_SW602_SPREADSHEET_LISTENER_H
+
+#include <vector>
+
+#include "SW602Listener.h"
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+class SW602Cell;
+class SW602CellContent;
+class SW602Chart;
+class SW602GraphicStyle;
+class SW602GraphicShape;
+class SW602Table;
+
+namespace SW602SpreadsheetListenerInternal
+{
+struct DocumentState;
+struct State;
+}
+
+/** This class contents the main functions needed to create a spreadsheet processing Document */
+class SW602SpreadsheetListener : public SW602Listener
+{
+public:
+ /** constructor */
+ SW602SpreadsheetListener(SW602ParserState &parserState, std::vector<SW602PageSpan> const &pageList, librevenge::RVNGSpreadsheetInterface *documentInterface);
+ /** simplified constructor (can be used for a embedded spreadsheet with one page).
+
+ \note the box coordinates must be given in point.*/
+ SW602SpreadsheetListener(SW602ParserState &parserState, SW602Box2f const &box, librevenge::RVNGSpreadsheetInterface *documentInterface);
+ /** destructor */
+ virtual ~SW602SpreadsheetListener();
+
+ /** returns the listener type */
+ Type getType() const
+ {
+ return Spreadsheet;
+ }
+
+ /** sets the documents language */
+ void setDocumentLanguage(std::string locale);
+
+ /** starts the document */
+ void startDocument();
+ /** ends the document */
+ void endDocument(bool sendDelayedSubDoc=true);
+ /** returns true if a document is opened */
+ bool isDocumentStarted() const;
+
+ /** function called to add a subdocument */
+ void handleSubDocument(SW602SubDocumentPtr subDocument, libsw602::SubDocumentType subDocumentType);
+ /** returns try if a subdocument is open */
+ bool isSubDocumentOpened(libsw602::SubDocumentType &subdocType) const;
+ /** tries to open a frame */
+ bool openFrame(SW602Position const &pos, SW602GraphicStyle const &style=SW602GraphicStyle::emptyStyle());
+ /** tries to close a frame */
+ void closeFrame();
+ /** open a group (not implemented) */
+ bool openGroup(SW602Position const &pos);
+ /** close a group (not implemented) */
+ void closeGroup();
+
+ /** returns true if we can add text data */
+ bool canWriteText() const;
+
+ // ------ page --------
+ /** returns true if a page is opened */
+ bool isPageSpanOpened() const;
+ /** returns the current page span
+
+ \note this forces the opening of a new page if no page is opened.*/
+ SW602PageSpan const &getPageSpan();
+
+ // ------ header/footer --------
+ /** insert a header */
+ bool insertHeader(SW602SubDocumentPtr subDocument, librevenge::RVNGPropertyList const &extras);
+ /** insert a footer */
+ bool insertFooter(SW602SubDocumentPtr subDocument, librevenge::RVNGPropertyList const &extras);
+ /** returns true if the header/footer is open */
+ bool isHeaderFooterOpened() const;
+
+ // ------- sheet -----------------
+ /** opens a sheet
+
+ \note if defWidth is positive, add 1000 columns with size defWidth
+ */
+ void openSheet(std::vector<float> const &colWidth, librevenge::RVNGUnit unit,
+ std::vector<int> const &repeatColWidthNumber=std::vector<int>(), std::string const &name="");
+ /** closes this sheet */
+ void closeSheet();
+ /** open a row with given height ( if h < 0.0, set min-row-height = -h )*/
+ void openSheetRow(float h, librevenge::RVNGUnit unit, int numRepeated=1);
+ /** closes this row */
+ void closeSheetRow();
+ /** open a cell */
+ void openSheetCell(SW602Cell const &cell, SW602CellContent const &content, int numRepeated=1);
+ /** close a cell */
+ void closeSheetCell();
+
+ // ------- chart -----------------
+ /** adds a chart in given position */
+ void insertChart(SW602Position const &pos, SW602Chart &chart,
+ SW602GraphicStyle const &style=SW602GraphicStyle::emptyStyle());
+
+ // ------ text data -----------
+
+ //! adds a basic character, ..
+ void insertChar(uint8_t character);
+ /** insert a character using the font converter to find the utf8
+ character */
+ void insertCharacter(unsigned char c);
+ /** insert a character using the font converter to find the utf8
+ character and if needed, input to read extra character.
+
+ \return the number of extra character read
+ */
+ int insertCharacter(unsigned char c, librevenge::RVNGInputStream &input, long endPos=-1);
+ /** adds an unicode character.
+ * By convention if \a character=0xfffd(undef), no character is added */
+ void insertUnicode(uint32_t character);
+ //! adds a unicode string
+ void insertUnicodeString(librevenge::RVNGString const &str);
+
+ //! adds a tab
+ void insertTab();
+ //! adds an end of line ( by default an hard one)
+ void insertEOL(bool softBreak=false);
+
+ // ------ text format -----------
+ //! sets the font
+ void setFont(SW602Font const &font);
+ //! returns the actual font
+ SW602Font const &getFont() const;
+
+ // ------ paragraph format -----------
+ //! returns true if a paragraph or a list is opened
+ bool isParagraphOpened() const;
+ //! sets the paragraph
+ void setParagraph(SW602Paragraph const &paragraph);
+ //! returns the actual paragraph
+ SW602Paragraph const &getParagraph() const;
+
+ // ------- fields ----------------
+ //! adds a field type
+ void insertField(SW602Field const &field);
+
+ // ------- link ----------------
+ //! open a link
+ void openLink(SW602Link const &link);
+ //! close a link
+ void closeLink();
+
+ // ------- subdocument -----------------
+ /** insert a note */
+ void insertNote(SW602Note const &note, SW602SubDocumentPtr &subDocument);
+
+ /** adds comment */
+ void insertComment(SW602SubDocumentPtr &subDocument);
+
+ /** adds a picture with potential various representationin given position */
+ void insertPicture(SW602Position const &pos, SW602EmbeddedObject const &picture,
+ SW602GraphicStyle const &style=SW602GraphicStyle::emptyStyle());
+ /** adds a shape picture in given position */
+ void insertShape(SW602Position const &pos, SW602GraphicShape const &shape,
+ SW602GraphicStyle const &style);
+ /** adds a textbox in given position */
+ void insertTextBox(SW602Position const &pos, SW602SubDocumentPtr subDocument,
+ SW602GraphicStyle const &frameStyle=SW602GraphicStyle::emptyStyle());
+
+ // ------- table -----------------
+ /** adds a table in given position */
+ void insertTable(SW602Position const &pos, SW602Table &table, SW602GraphicStyle const &style=SW602GraphicStyle::emptyStyle());
+ /** open a table */
+ void openTable(SW602Table const &table);
+ /** closes this table */
+ void closeTable();
+ /** open a row with given height ( if h < 0.0, set min-row-height = -h )*/
+ void openTableRow(float h, librevenge::RVNGUnit unit, bool headerRow=false);
+ /** closes this row */
+ void closeTableRow();
+ /** open a cell */
+ void openTableCell(SW602Cell const &cell);
+ /** close a cell */
+ void closeTableCell();
+ /** add empty cell */
+ void addEmptyTableCell(SW602Vec2i const &pos, SW602Vec2i span=SW602Vec2i(1,1));
+
+ // ------- section ---------------
+ /** returns true if we can add open a section, add page break, ... */
+ bool canOpenSectionAddBreak() const
+ {
+ return false;
+ }
+ //! returns true if a section is opened
+ bool isSectionOpened() const
+ {
+ return false;
+ }
+ //! returns the actual section
+ SW602Section const &getSection() const;
+ //! open a section if possible
+ bool openSection(SW602Section const &section);
+ //! close a section
+ bool closeSection();
+ //! inserts a break type: ColumBreak, PageBreak, ..
+ void insertBreak(BreakType breakType);
+
+protected:
+ //! does open a new page (low level)
+ void _openPageSpan(bool sendHeaderFooters=true);
+ //! does close a page (low level)
+ void _closePageSpan();
+
+ void _startSubDocument();
+ void _endSubDocument();
+
+ void _handleFrameParameters(librevenge::RVNGPropertyList &propList, SW602Position const &pos);
+
+ void _openParagraph();
+ void _closeParagraph();
+ void _resetParagraphState(const bool isListElement=false);
+
+ /** open a list level */
+ void _openListElement();
+ /** close a list level */
+ void _closeListElement();
+ /** update the list so that it corresponds to the actual level */
+ void _changeList();
+ /** low level: find a list id which corresponds to actual list and a change of level.
+
+ \note called when the list id is not set
+ */
+ int _getListId() const;
+
+ void _openSpan();
+ void _closeSpan();
+
+ void _flushText();
+ void _flushDeferredTabs();
+
+ /** creates a new parsing state (copy of the actual state)
+ *
+ * \return the old one */
+ boost::shared_ptr<SW602SpreadsheetListenerInternal::State> _pushParsingState();
+ //! resets the previous parsing state
+ void _popParsingState();
+
+protected:
+ //! the main parse state
+ boost::shared_ptr<SW602SpreadsheetListenerInternal::DocumentState> m_ds;
+ //! the actual local parse state
+ boost::shared_ptr<SW602SpreadsheetListenerInternal::State> m_ps;
+ //! stack of local state
+ std::vector<boost::shared_ptr<SW602SpreadsheetListenerInternal::State> > m_psStack;
+ //! the parser state
+ SW602ParserState &m_parserState;
+ //! the document interface
+ librevenge::RVNGSpreadsheetInterface *m_documentInterface;
+
+private:
+ //! copy constructor (unimplemented)
+ SW602SpreadsheetListener(const SW602SpreadsheetListener &);
+ //! operator= (unimplemented)
+ SW602SpreadsheetListener &operator=(const SW602SpreadsheetListener &);
+};
+
+}
+#endif
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602SubDocument.cpp b/src/lib/SW602SubDocument.cpp
new file mode 100644
index 0000000..2925538
--- /dev/null
+++ b/src/lib/SW602SubDocument.cpp
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "SW602SubDocument.h"
+
+namespace libsw602
+{
+
+SW602SubDocument::SW602SubDocument(SW602Parser *pars, boost::shared_ptr<librevenge::RVNGInputStream> ip, SW602Entry const &z):
+ m_parser(pars), m_input(ip), m_zone(z)
+{
+}
+
+SW602SubDocument::SW602SubDocument(SW602SubDocument const &doc) : m_parser(0), m_input(), m_zone()
+{
+ *this = doc;
+}
+
+SW602SubDocument::~SW602SubDocument()
+{
+}
+
+SW602SubDocument &SW602SubDocument::operator=(SW602SubDocument const &doc)
+{
+ if (&doc != this)
+ {
+ m_parser = doc.m_parser;
+ m_input = doc.m_input;
+ m_zone = doc.m_zone;
+ }
+ return *this;
+}
+
+bool SW602SubDocument::operator!=(SW602SubDocument const &doc) const
+{
+ if (doc.m_parser != m_parser) return true;
+ if (doc.m_input.get() != m_input.get()) return true;
+ if (doc.m_zone != m_zone) return true;
+ return false;
+}
+
+bool SW602SubDocument::operator!=(boost::shared_ptr<SW602SubDocument> const &doc) const
+{
+ if (!doc) return true;
+ return operator!=(*doc.get());
+}
+
+}
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602SubDocument.h b/src/lib/SW602SubDocument.h
new file mode 100644
index 0000000..44369ea
--- /dev/null
+++ b/src/lib/SW602SubDocument.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SW602_SUB_DOCUMENT_H
+#define INCLUDED_SW602_SUB_DOCUMENT_H
+
+#include <boost/shared_ptr.hpp>
+
+#include <librevenge/librevenge.h>
+
+#include "SW602Entry.h"
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+class SW602Parser;
+
+/** abstract class used to store a subdocument (with a comparison function) */
+class SW602SubDocument
+{
+public:
+ //! constructor from parser, input stream and zone in the input
+ SW602SubDocument(SW602Parser *pars, boost::shared_ptr<librevenge::RVNGInputStream> ip, SW602Entry const &z);
+ //! copy constructor
+ explicit SW602SubDocument(SW602SubDocument const &doc);
+ //! copy operator
+ SW602SubDocument &operator=(SW602SubDocument const &doc);
+ //! virtual destructor
+ virtual ~SW602SubDocument();
+
+ //! comparison operator!=
+ virtual bool operator!=(SW602SubDocument const &doc) const;
+ //! comparison operator==
+ bool operator==(SW602SubDocument const &doc) const
+ {
+ return !operator!=(doc);
+ }
+ //! comparison operator!=
+ bool operator!=(boost::shared_ptr<SW602SubDocument> const &doc) const;
+ //! comparison operator==
+ bool operator==(boost::shared_ptr<SW602SubDocument> const &doc) const
+ {
+ return !operator!=(doc);
+ }
+
+ /** virtual parse function
+ *
+ * this function is called to parse the subdocument */
+ virtual void parse(SW602ListenerPtr &listener, libsw602::SubDocumentType subDocumentType) = 0;
+
+protected:
+ //! the main zone parser
+ SW602Parser *m_parser;
+ //! the input
+ boost::shared_ptr<librevenge::RVNGInputStream> m_input;
+ //! if valid the zone to parse
+ SW602Entry m_zone;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602Table.cpp b/src/lib/SW602Table.cpp
new file mode 100644
index 0000000..44e7d32
--- /dev/null
+++ b/src/lib/SW602Table.cpp
@@ -0,0 +1,531 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/*
+ * Structure to store and construct a table from an unstructured list
+ * of cell
+ *
+ */
+
+#include "SW602Table.h"
+
+#include <iomanip>
+#include <iostream>
+#include <map>
+#include <set>
+#include <sstream>
+
+#include <librevenge/librevenge.h>
+
+#include "SW602Cell.h"
+#include "SW602GraphicShape.h"
+#include "SW602GraphicStyle.h"
+#include "SW602Listener.h"
+#include "SW602Position.h"
+
+namespace libsw602
+{
+
+/** Internal: the structures of a SW602Table */
+namespace SW602TableInternal
+{
+//! a comparaison structure used retrieve the rows and the columns
+struct Compare
+{
+ //! constructor
+ explicit Compare(int dim) : m_coord(dim) {}
+ //! small structure to define a cell point
+ struct Point
+ {
+ Point(int wh, SW602Cell const *cell, int cellId) : m_which(wh), m_cell(cell), m_cellId(cellId) {}
+ float getPos(int coord) const
+ {
+ if (m_which)
+ return m_cell->bdBox().max()[coord];
+ return m_cell->bdBox().min()[coord];
+ }
+ /** returns the cells size */
+ float getSize(int coord) const
+ {
+ return m_cell->bdBox().size()[coord];
+ }
+ /** the position of the point in the cell (0: LT, 1: RB) */
+ int m_which;
+ /** the cell */
+ SW602Cell const *m_cell;
+ //! the cell id ( used by compare)
+ int m_cellId;
+ };
+
+ //! comparaison function
+ bool operator()(Point const &c1, Point const &c2) const
+ {
+ float diffF = c1.getPos(m_coord)-c2.getPos(m_coord);
+ if (diffF < 0) return true;
+ if (diffF > 0) return false;
+ int diff = c2.m_which - c1.m_which;
+ if (diff) return (diff < 0);
+ diffF = c1.m_cell->bdBox().size()[m_coord]
+ - c2.m_cell->bdBox().size()[m_coord];
+ if (diffF < 0) return true;
+ if (diffF > 0) return false;
+ return c1.m_cellId < c2.m_cellId;
+ }
+
+ //! the coord to compare
+ int m_coord;
+};
+}
+
+////////////////////////////////////////////////////////////
+// SW602Table
+////////////////////////////////////////////////////////////
+
+// destructor, ...
+SW602Table::~SW602Table()
+{
+}
+
+boost::shared_ptr<SW602Cell> SW602Table::get(int id)
+{
+ if (id < 0 || id >= int(m_cellsList.size()))
+ {
+ SW602_DEBUG_MSG(("SW602Table::get: cell %d does not exists\n",id));
+ return boost::shared_ptr<SW602Cell>();
+ }
+ return m_cellsList[size_t(id)];
+}
+
+void SW602Table::addTablePropertiesTo(librevenge::RVNGPropertyList &propList) const
+{
+ switch (m_alignment)
+ {
+ case Paragraph:
+ break;
+ case Left:
+ propList.insert("table:align", "left");
+ propList.insert("fo:margin-left", m_leftMargin, librevenge::RVNG_POINT);
+ break;
+ case Center:
+ propList.insert("table:align", "center");
+ break;
+ case Right:
+ propList.insert("table:align", "right");
+ propList.insert("fo:margin-right", m_rightMargin, librevenge::RVNG_POINT);
+ break;
+ default:
+ break;
+ }
+ if (mergeBorders())
+ propList.insert("table:border-model","collapsing");
+
+ size_t nCols = m_colsSize.size();
+ float tableWidth = 0;
+ librevenge::RVNGPropertyListVector columns;
+ for (size_t c = 0; c < nCols; ++c)
+ {
+ librevenge::RVNGPropertyList column;
+ column.insert("style:column-width", m_colsSize[c], librevenge::RVNG_POINT);
+ columns.append(column);
+ tableWidth += m_colsSize[c];
+ }
+ propList.insert("style:width", tableWidth, librevenge::RVNG_POINT);
+ propList.insert("librevenge:table-columns", columns);
+}
+
+////////////////////////////////////////////////////////////
+// send extra line
+void SW602Table::sendExtraLines(SW602ListenerPtr listener) const
+{
+ if (!listener)
+ {
+ SW602_DEBUG_MSG(("SW602Table::sendExtraLines: called without listener\n"));
+ return;
+ }
+ std::vector<float> rowsPos, columnsPos;
+ size_t nRows = m_rowsSize.size();
+ rowsPos.resize(nRows+1);
+ rowsPos[0] = 0;
+ for (size_t r = 0; r < nRows; ++r)
+ rowsPos[r+1] = rowsPos[r]+
+ (float)(m_rowsSize[r]<0?-m_rowsSize[r]:m_rowsSize[r]);
+ size_t nColumns = m_colsSize.size();
+ columnsPos.resize(nColumns+1);
+ columnsPos[0] = 0;
+ for (size_t c = 0; c < nColumns; ++c)
+ columnsPos[c+1] = columnsPos[c]+
+ (float)(m_colsSize[c]<0?-m_colsSize[c]:m_colsSize[c]);
+
+ for (size_t c = 0; c < m_cellsList.size(); ++c)
+ {
+ if (!m_cellsList[c]) continue;
+ SW602Cell const &cell=*(m_cellsList[c]);
+ if (!cell.hasExtraLine())
+ continue;
+ SW602Vec2i const &pos=m_cellsList[c]->position();
+ SW602Vec2i const &span=m_cellsList[c]->numSpannedCells();
+ if (span[0] <= 0 || span[1] <= 0 || pos[0]+span[0] > (int)nColumns ||
+ pos[1]+span[1] > (int) nRows)
+ continue;
+ SW602Box2f box;
+ box.setMin(SW602Vec2f(columnsPos[size_t(pos[0])], rowsPos[size_t(pos[1])]));
+ box.setMax(SW602Vec2f(columnsPos[size_t(pos[0]+span[0])],
+ rowsPos[size_t(pos[1]+span[1])]));
+
+ SW602Border const &border=cell.extraLineType();
+ SW602GraphicStyle pStyle;
+ pStyle.m_lineWidth=(float)border.m_width;
+ pStyle.m_lineColor=border.m_color;
+
+ SW602Position lPos(box[0], box.size(), librevenge::RVNG_POINT);
+ lPos.setRelativePosition(SW602Position::Frame);
+ lPos.m_wrapping=SW602Position::WForeground;
+ lPos.setOrder(-1);
+ if (cell.extraLine()==SW602Cell::E_Cross || cell.extraLine()==SW602Cell::E_Line1)
+ listener->insertShape(lPos, SW602GraphicShape::line(SW602Vec2f(0,0), box.size()), pStyle);
+ if (cell.extraLine()==SW602Cell::E_Cross || cell.extraLine()==SW602Cell::E_Line2)
+ listener->insertShape(lPos, SW602GraphicShape::line(SW602Vec2f(0,box.size()[1]), SW602Vec2f(box.size()[0], 0)), pStyle);
+ }
+}
+
+////////////////////////////////////////////////////////////
+// build the table structure
+bool SW602Table::buildStructures()
+{
+ if (m_setData&CellPositionBit)
+ return true;
+ if ((m_setData&BoxBit)==0)
+ {
+ SW602_DEBUG_MSG(("SW602Table::buildStructures: can not reconstruct cellule position if their boxes are not set\n"));
+ return false;
+ }
+
+ size_t nCells = m_cellsList.size();
+ std::vector<float> listPositions[2];
+ for (int dim = 0; dim < 2; dim++)
+ {
+ SW602TableInternal::Compare compareFunction(dim);
+ std::set<SW602TableInternal::Compare::Point,
+ SW602TableInternal::Compare> set(compareFunction);
+ for (size_t c = 0; c < nCells; ++c)
+ {
+ set.insert(SW602TableInternal::Compare::Point(0, m_cellsList[c].get(), int(c)));
+ set.insert(SW602TableInternal::Compare::Point(1, m_cellsList[c].get(), int(c)));
+ }
+
+ std::vector<float> positions;
+ std::set<SW602TableInternal::Compare::Point,
+ SW602TableInternal::Compare>::iterator it = set.begin();
+ float maxPosiblePos=0;
+ int actCell = -1;
+ for (; it != set.end(); ++it)
+ {
+ float pos = it->getPos(dim);
+ if (actCell < 0 || pos > maxPosiblePos)
+ {
+ actCell++;
+ positions.push_back(pos);
+ maxPosiblePos = float(pos+2.0); // 2 pixel ok
+ }
+ if (it->m_which == 0 && it->getPos(dim)-2.0 < maxPosiblePos)
+ maxPosiblePos = float((it->getPos(dim)+pos)/2.);
+ }
+ listPositions[dim] = positions;
+ }
+ for (size_t c = 0; c < nCells; ++c)
+ {
+ int cellPos[2], spanCell[2];
+ for (int dim = 0; dim < 2; dim++)
+ {
+ float pt[2] = { m_cellsList[c]->bdBox().min()[dim],
+ m_cellsList[c]->bdBox().max()[dim]
+ };
+ std::vector<float> &pos = listPositions[dim];
+ size_t numPos = pos.size();
+ size_t i = 0;
+ while (i+1 < numPos && pos[i+1] < pt[0])
+ i++;
+ if (i+1 < numPos && (pos[i]+pos[i+1])/2 < pt[0])
+ i++;
+ if (i+1 > numPos)
+ {
+ SW602_DEBUG_MSG(("SW602Table::buildStructures: impossible to find cell position !!!\n"));
+ return false;
+ }
+ cellPos[dim] = int(i);
+ while (i+1 < numPos && pos[i+1] < pt[1])
+ i++;
+ if (i+1 < numPos && (pos[i]+pos[i+1])/2 < pt[1])
+ i++;
+ spanCell[dim] = int(i)-cellPos[dim];
+ if (spanCell[dim]==0 &&
+ (m_cellsList[c]->bdBox().size()[dim] < 0 || m_cellsList[c]->bdBox().size()[dim] > 0))
+ {
+ SW602_DEBUG_MSG(("SW602Table::buildStructures: impossible to find span number !!!\n"));
+ return false;
+ }
+ if (spanCell[dim] > 1 &&
+ pos[size_t(cellPos[dim])]+2.0f > pos[size_t(cellPos[dim]+1)])
+ {
+ spanCell[dim]--;
+ cellPos[dim]++;
+ }
+ }
+ m_cellsList[c]->setPosition(SW602Vec2i(cellPos[0], cellPos[1]));
+ m_cellsList[c]->setNumSpannedCells(SW602Vec2i(spanCell[0], spanCell[1]));
+ }
+ m_setData |= CellPositionBit;
+ // finally update the row/col size
+ for (int dim = 0; dim < 2; dim++)
+ {
+ std::vector<float> const &pos = listPositions[dim];
+ size_t numPos = pos.size();
+ if (!numPos) continue;
+ std::vector<float> &res = (dim==0) ? m_colsSize : m_rowsSize;
+ res.resize(numPos-1);
+ for (size_t i = 0; i < numPos-1; i++)
+ res[i] = pos[i+1]-pos[i];
+ }
+ m_setData |= TableDimBit;
+ return true;
+}
+
+
+bool SW602Table::buildPosToCellId()
+{
+ if (m_setData&TablePosToCellBit)
+ return true;
+ if ((m_setData&CellPositionBit)==0)
+ {
+ SW602_DEBUG_MSG(("SW602Table::buildPosToCellId: can not reconstruct cellule position if their boxes are not set\n"));
+ return false;
+ }
+ m_posToCellId.resize(0);
+
+ size_t nCells = m_cellsList.size();
+ m_numRows=(m_setData&TableDimBit) ? m_rowsSize.size() : 0;
+ m_numCols=(m_setData&TableDimBit) ? m_colsSize.size() : 0;
+ if ((m_setData&TableDimBit)==0)
+ {
+ // m_numCols, m_numRows is not updated, we must compute it
+ m_numCols = 0;
+ m_numRows = 0;
+ for (size_t c = 0; c < nCells; ++c)
+ {
+ if (!m_cellsList[c]) continue;
+ SW602Vec2i const &lastPos=m_cellsList[c]->position() +
+ m_cellsList[c]->numSpannedCells();
+ if (lastPos[0]>int(m_numCols)) m_numCols=size_t(lastPos[0]);
+ if (lastPos[1]>int(m_numRows)) m_numRows=size_t(lastPos[1]);
+ }
+ }
+ if (!m_numCols || !m_numRows)
+ return false;
+ m_posToCellId.resize(m_numCols*m_numRows, -1);
+ for (size_t c = 0; c < nCells; ++c)
+ {
+ if (!m_cellsList[c]) continue;
+ if (m_cellsList[c]->hasExtraLine())
+ m_hasExtraLines=true;
+
+ SW602Vec2i const &pos=m_cellsList[c]->position();
+ SW602Vec2i lastPos=pos+m_cellsList[c]->numSpannedCells();
+ for (int x = pos[0]; x < lastPos[0]; x++)
+ {
+ for (int y = pos[1]; y < lastPos[1]; y++)
+ {
+ int tablePos = getCellIdPos(x,y);
+ if (tablePos<0)
+ {
+ SW602_DEBUG_MSG(("SW602Table::buildPosToCellId: the position is bad!!!\n"));
+ return false;
+ }
+ if (m_posToCellId[size_t(tablePos)] != -1)
+ {
+ SW602_DEBUG_MSG(("SW602Table::buildPosToCellId: cells is used!!!\n"));
+ return false;
+ }
+ if (x == pos[0] && y == pos[1])
+ m_posToCellId[size_t(tablePos)] = int(c);
+ else
+ m_posToCellId[size_t(tablePos)] = -2;
+ }
+ }
+ }
+ m_setData |= TablePosToCellBit;
+ return true;
+}
+
+bool SW602Table::buildDims()
+{
+ if (m_setData&TableDimBit)
+ return true;
+ if ((m_setData&CellPositionBit)==0)
+ return false;
+ if ((m_setData&BoxBit)==0 && (m_setData&SizeBit)==0)
+ {
+ SW602_DEBUG_MSG(("SW602Table::buildDims: not enough information to reconstruct dimension\n"));
+ return false;
+ }
+
+ if (m_numRows<=0 || m_numCols<=0)
+ {
+ SW602_DEBUG_MSG(("SW602Table::buildDims: can not compute the number of columns/row\n"));
+ return false;
+ }
+
+ std::vector<float> colLimit(m_numCols+1,0);
+ std::vector<bool> isFixed(m_numCols+1, (m_setData&BoxBit));
+ for (int c = 0; c < int(m_numCols); ++c)
+ {
+ for (int r = 0; r < int(m_numRows); ++r)
+ {
+ int cPos = getCellIdPos(c, r);
+ if (cPos<0 || m_posToCellId[size_t(cPos)]<0) continue;
+ boost::shared_ptr<SW602Cell> cell=m_cellsList[size_t(m_posToCellId[size_t(cPos)])];
+ if (!cell) continue;
+ SW602Vec2i const &pos=cell->position();
+ SW602Vec2i lastPos=pos+cell->numSpannedCells();
+ if (m_setData&BoxBit)
+ {
+ colLimit[size_t(pos[0])] = cell->bdBox()[0][0];
+ colLimit[size_t(lastPos[0])] = cell->bdBox()[1][0];
+ }
+ else if (cell->bdSize()[0]>=0)
+ {
+ colLimit[size_t(lastPos[0])] = colLimit[size_t(pos[0])]+cell->bdSize()[0];
+ isFixed[size_t(lastPos[0])]=true;
+ }
+ else if (!isFixed[size_t(lastPos[0])])
+ colLimit[size_t(lastPos[0])] = colLimit[size_t(pos[0])]-cell->bdSize()[0];
+ }
+ if (colLimit[size_t(c)+1]<=colLimit[size_t(c)])
+ {
+ SW602_DEBUG_MSG(("SW602Table::buildDims: oops can not find the size of col %d\n", c));
+ return false;
+ }
+ }
+ m_colsSize.resize(m_numCols);
+ for (size_t c = 0; c < m_numCols; ++c)
+ {
+ if (isFixed[c+1])
+ m_colsSize[c]=colLimit[c+1]-colLimit[c];
+ else
+ m_colsSize[c]=colLimit[c]-colLimit[c+1];
+ }
+
+ std::vector<float> rowLimit(m_numRows+1,0);
+ isFixed.resize(0);
+ isFixed.resize(m_numRows+1,(m_setData&BoxBit));
+ for (int r = 0; r < int(m_numRows); ++r)
+ {
+ for (int c = 0; c < int(m_numCols); ++c)
+ {
+ int cPos = getCellIdPos(c, r);
+ if (cPos<0 || m_posToCellId[size_t(cPos)]<0) continue;
+ boost::shared_ptr<SW602Cell> cell=m_cellsList[size_t(m_posToCellId[size_t(cPos)])];
+ if (!cell) continue;
+ SW602Vec2i const &pos=cell->position();
+ SW602Vec2i lastPos=pos+cell->numSpannedCells();
+ if (m_setData&BoxBit)
+ {
+ rowLimit[size_t(pos[1])] = cell->bdBox()[0][1];
+ rowLimit[size_t(lastPos[1])] = cell->bdBox()[1][1];
+ }
+ else if (cell->bdSize()[1]>=0)
+ {
+ rowLimit[size_t(lastPos[1])] = rowLimit[size_t(pos[1])]+cell->bdSize()[1];
+ isFixed[size_t(lastPos[1])]=true;
+ }
+ else if (!isFixed[size_t(lastPos[1])])
+ rowLimit[size_t(lastPos[1])] = rowLimit[size_t(pos[1])]-cell->bdSize()[1];
+ }
+ if (rowLimit[size_t(r)+1]<=rowLimit[size_t(r)])
+ {
+ SW602_DEBUG_MSG(("SW602Table::buildDims: oops can not find the size of row %d\n", r));
+ return false;
+ }
+ }
+ m_rowsSize.resize(m_numRows);
+ for (size_t r = 0; r < m_numRows; ++r)
+ {
+ if (isFixed[r+1])
+ m_rowsSize[r]=rowLimit[r+1]-rowLimit[r];
+ else
+ m_rowsSize[r]=rowLimit[r]-rowLimit[r+1];
+ }
+ m_setData |= TableDimBit;
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// try to send the table
+bool SW602Table::updateTable()
+{
+ if ((m_setData&CellPositionBit)==0 && !buildStructures())
+ return false;
+ if ((m_setData&TablePosToCellBit)==0 && !buildPosToCellId())
+ return false;
+ if (!m_numCols || !m_numRows)
+ return false;
+ if ((m_givenData&TableDimBit)==0 && !buildDims())
+ return false;
+ return true;
+}
+
+bool SW602Table::sendTable(SW602ListenerPtr listener, bool inFrame)
+{
+ if (!updateTable())
+ return false;
+ if (!listener)
+ return true;
+ listener->openTable(*this);
+ for (size_t r = 0; r < m_numRows; ++r)
+ {
+ listener->openTableRow(m_rowsSize[r], librevenge::RVNG_POINT);
+ for (size_t c = 0; c < m_numCols; ++c)
+ {
+ int tablePos = getCellIdPos(int(c), int(r));
+ if (tablePos<0)
+ continue;
+ int id = m_posToCellId[size_t(tablePos)];
+ if (id == -1)
+ listener->addEmptyTableCell(SW602Vec2i(int(c), int(r)));
+ if (id < 0) continue;
+ m_cellsList[size_t(id)]->send(listener, *this);
+ }
+ listener->closeTableRow();
+ }
+ listener->closeTable();
+ if (inFrame && m_hasExtraLines)
+ sendExtraLines(listener);
+
+ return true;
+}
+
+
+////////////////////////////////////////////////////////////
+// try to send the table
+bool SW602Table::sendAsText(SW602ListenerPtr listener)
+{
+ if (!listener) return true;
+
+ size_t nCells = m_cellsList.size();
+ for (size_t i = 0; i < nCells; i++)
+ {
+ if (!m_cellsList[i]) continue;
+ m_cellsList[i]->sendContent(listener, *this);
+ listener->insertEOL();
+ }
+ return true;
+}
+
+}
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602Table.h b/src/lib/SW602Table.h
new file mode 100644
index 0000000..29db4a2
--- /dev/null
+++ b/src/lib/SW602Table.h
@@ -0,0 +1,186 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/*
+ * Structure to store and construct a table from an unstructured list
+ * of cell
+ *
+ */
+
+#ifndef INCLUDED_SW602_TABLE_H
+#define INCLUDED_SW602_TABLE_H
+
+#include <iostream>
+#include <vector>
+
+#include "SW602Cell.h"
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+/** a class used to recreate the table structure using cell informations, .... */
+class SW602Table
+{
+public:
+ //! an enum used to indicate what the list of entries which are filled
+ enum DataSet
+ {
+ CellPositionBit=1, BoxBit=2, SizeBit=4, TableDimBit=8, TablePosToCellBit=0x10
+ };
+ /** an enum do define the table alignment.
+
+ \note Paragraph means using the default alignment, left page alignment and use left margin */
+ enum Alignment
+ {
+ Paragraph, Left, Center, Right
+ };
+ //! the constructor
+ explicit SW602Table(uint32_t givenData=BoxBit) :
+ m_givenData(givenData), m_setData(givenData), m_mergeBorders(true), m_cellsList(),
+ m_numRows(0), m_numCols(0), m_rowsSize(), m_colsSize(), m_alignment(Paragraph), m_leftMargin(0), m_rightMargin(0),
+ m_posToCellId(), m_hasExtraLines(false) {}
+
+ //! the destructor
+ virtual ~SW602Table();
+
+ //! add a new cells
+ void add(boost::shared_ptr<SW602Cell> cell)
+ {
+ if (!cell)
+ {
+ SW602_DEBUG_MSG(("SW602Table::add: must be called with a cell\n"));
+ return;
+ }
+ m_cellsList.push_back(cell);
+ }
+ //! returns true if we need to merge borders
+ bool mergeBorders() const
+ {
+ return m_mergeBorders;
+ }
+ //! sets the merge borders' value
+ bool setMergeBorders(bool val)
+ {
+ return m_mergeBorders=val;
+ }
+ /** defines the current alignment
+ \note: leftMargin,rightMargin are given in Points */
+ void setAlignment(Alignment align, float leftMargin=0, float rightMargin=0)
+ {
+ m_alignment = align;
+ m_leftMargin = leftMargin;
+ m_rightMargin = rightMargin;
+ }
+ //! returns the number of cell
+ int numCells() const
+ {
+ return int(m_cellsList.size());
+ }
+ /** returns the row size if defined (in point) */
+ std::vector<float> const &getRowsSize() const
+ {
+ return m_rowsSize;
+ }
+ /** define the row size (in point) */
+ void setRowsSize(std::vector<float> const &rSize)
+ {
+ m_rowsSize=rSize;
+ }
+ /** returns the columns size if defined (in point) */
+ std::vector<float> const &getColsSize() const
+ {
+ return m_colsSize;
+ }
+ /** define the columns size (in point) */
+ void setColsSize(std::vector<float> const &cSize)
+ {
+ m_colsSize=cSize;
+ }
+
+ //! returns the i^th cell
+ boost::shared_ptr<SW602Cell> get(int id);
+
+ /** try to build the table structures */
+ bool updateTable();
+ /** returns true if the table has extralines */
+ bool hasExtraLines()
+ {
+ if (!updateTable()) return false;
+ return m_hasExtraLines;
+ }
+
+ /** try to send the table
+
+ Note: either send the table ( and returns true ) or do nothing.
+ */
+ bool sendTable(SW602ListenerPtr listener, bool inFrame=true);
+
+ /** try to send the table as basic text */
+ bool sendAsText(SW602ListenerPtr listener);
+
+ // interface with the content listener
+
+ //! adds the table properties to propList
+ void addTablePropertiesTo(librevenge::RVNGPropertyList &propList) const;
+
+protected:
+ //! convert a cell position in a posToCellId's position
+ int getCellIdPos(int col, int row) const
+ {
+ if (col<0||col>=int(m_numCols))
+ return -1;
+ if (row<0||row>=int(m_numRows))
+ return -1;
+ return col*int(m_numRows)+row;
+ }
+ //! create the correspondance list, ...
+ bool buildStructures();
+ /** compute the rows and the cells size */
+ bool buildDims();
+ /** a function which fills to posToCellId vector using the cell position */
+ bool buildPosToCellId();
+ //! send extra line
+ void sendExtraLines(SW602ListenerPtr listener) const;
+
+protected:
+ /** a int to indicate what data are given in entries*/
+ uint32_t m_givenData;
+ /** a int to indicate what data are been reconstruct*/
+ uint32_t m_setData;
+ /** do we need to merge cell borders ( default yes) */
+ bool m_mergeBorders;
+ /** the list of cells */
+ std::vector<boost::shared_ptr<SW602Cell> > m_cellsList;
+ /** the number of rows ( set by buildPosToCellId ) */
+ size_t m_numRows;
+ /** the number of cols ( set by buildPosToCellId ) */
+ size_t m_numCols;
+ /** the final row size (in point) */
+ std::vector<float> m_rowsSize;
+ /** the final col size (in point) */
+ std::vector<float> m_colsSize;
+ /** the table alignment */
+ Alignment m_alignment;
+ /** the left margin in point */
+ float m_leftMargin;
+ /** the right margin in point */
+ float m_rightMargin;
+
+ /** a vector used to store an id corresponding to each cell */
+ std::vector<int> m_posToCellId;
+ /** true if we need to send extra lines */
+ bool m_hasExtraLines;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602TextListener.cpp b/src/lib/SW602TextListener.cpp
new file mode 100644
index 0000000..b2eab3a
--- /dev/null
+++ b/src/lib/SW602TextListener.cpp
@@ -0,0 +1,1940 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/** \file SW602TextListener.cxx
+ * Implements SW602TextListener: the libsw602 word processor listener
+ *
+ * \note this class is the only class which does the interface with
+ * the librevenge::RVNGTextInterface
+ */
+
+#include "SW602TextListener.h"
+
+#include <cstring>
+#include <iomanip>
+#include <sstream>
+#include <time.h>
+
+#include <librevenge/librevenge.h>
+
+#include "SW602Cell.h"
+#include "SW602Font.h"
+#include "SW602GraphicEncoder.h"
+#include "SW602GraphicListener.h"
+#include "SW602GraphicShape.h"
+#include "SW602GraphicStyle.h"
+#include "SW602List.h"
+#include "SW602PageSpan.h"
+#include "SW602Paragraph.h"
+#include "SW602Parser.h"
+#include "SW602Position.h"
+#include "SW602Section.h"
+#include "SW602SubDocument.h"
+#include "SW602Table.h"
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+//! Internal and low level namespace to define the states of SW602TextListener
+namespace SW602TextListenerInternal
+{
+//! a enum to define basic break bit
+enum { PageBreakBit=0x1, ColumnBreakBit=0x2 };
+//! a class to store the document state of a SW602TextListener
+struct DocumentState
+{
+ //! constructor
+ explicit DocumentState(std::vector<SW602PageSpan> const &pageList) :
+ m_pageList(pageList), m_pageSpan(), m_metaData(), m_footNoteNumber(0), m_endNoteNumber(0), m_smallPictureNumber(0),
+ m_isDocumentStarted(false), m_isHeaderFooterStarted(false), m_sentListMarkers(), m_subDocuments()
+ {
+ }
+ //! destructor
+ ~DocumentState()
+ {
+ }
+
+ //! the pages definition
+ std::vector<SW602PageSpan> m_pageList;
+ //! the current page span
+ SW602PageSpan m_pageSpan;
+ //! the document meta data
+ librevenge::RVNGPropertyList m_metaData;
+
+ int m_footNoteNumber /** footnote number*/, m_endNoteNumber /** endnote number*/;
+
+ int m_smallPictureNumber /** number of small picture */;
+ bool m_isDocumentStarted /** a flag to know if the document is open */, m_isHeaderFooterStarted /** a flag to know if the header footer is started */;
+ /// the list of marker corresponding to sent list
+ std::vector<int> m_sentListMarkers;
+ std::vector<SW602SubDocumentPtr> m_subDocuments; /** list of document actually open */
+
+private:
+ DocumentState(const DocumentState &);
+ DocumentState &operator=(const DocumentState &);
+};
+
+/** the state of a SW602TextListener */
+struct State
+{
+ //! constructor
+ State();
+ //! destructor
+ ~State() { }
+
+ //! a buffer to stored the text
+ librevenge::RVNGString m_textBuffer;
+ //! the number of tabs to add
+ int m_numDeferredTabs;
+
+ //! the font
+ SW602Font m_font;
+ //! the paragraph
+ SW602Paragraph m_paragraph;
+ //! a sequence of bit used to know if we need page/column break
+ int m_paragraphNeedBreak;
+
+ boost::shared_ptr<SW602List> m_list;
+
+ bool m_isPageSpanOpened;
+ bool m_isSectionOpened;
+ bool m_isFrameOpened;
+ bool m_isPageSpanBreakDeferred;
+ bool m_isHeaderFooterWithoutParagraph;
+ //! a flag to know if openGroup was called
+ bool m_isGroupOpened;
+
+ bool m_isSpanOpened;
+ bool m_isParagraphOpened;
+ bool m_isListElementOpened;
+
+ bool m_firstParagraphInPageSpan;
+
+ bool m_isTableOpened;
+ bool m_isTableRowOpened;
+ bool m_isTableColumnOpened;
+ bool m_isTableCellOpened;
+
+ unsigned m_currentPage;
+ int m_numPagesRemainingInSpan;
+ int m_currentPageNumber;
+
+ bool m_sectionAttributesChanged;
+ //! the section
+ SW602Section m_section;
+
+ std::vector<bool> m_listOrderedLevels; //! a stack used to know what is open
+
+ bool m_inSubDocument;
+
+ bool m_isNote;
+ bool m_inLink;
+ libsw602::SubDocumentType m_subDocumentType;
+
+private:
+ State(const State &);
+ State &operator=(const State &);
+};
+
+State::State() :
+ m_textBuffer(""), m_numDeferredTabs(0),
+
+ m_font(20,12), // default time 12
+
+ m_paragraph(), m_paragraphNeedBreak(0),
+
+ m_list(),
+
+ m_isPageSpanOpened(false), m_isSectionOpened(false), m_isFrameOpened(false),
+ m_isPageSpanBreakDeferred(false),
+ m_isHeaderFooterWithoutParagraph(false),
+ m_isGroupOpened(false),
+
+ m_isSpanOpened(false), m_isParagraphOpened(false), m_isListElementOpened(false),
+
+ m_firstParagraphInPageSpan(true),
+
+ m_isTableOpened(false), m_isTableRowOpened(false), m_isTableColumnOpened(false),
+ m_isTableCellOpened(false),
+
+ m_currentPage(0), m_numPagesRemainingInSpan(0), m_currentPageNumber(1),
+
+ m_sectionAttributesChanged(false),
+ m_section(),
+
+ m_listOrderedLevels(),
+
+ m_inSubDocument(false),
+ m_isNote(false), m_inLink(false),
+ m_subDocumentType(libsw602::DOC_NONE)
+{
+}
+}
+
+SW602TextListener::SW602TextListener(SW602ParserState &parserState, std::vector<SW602PageSpan> const &pageList, librevenge::RVNGTextInterface *documentInterface) : SW602Listener(),
+ m_ds(new SW602TextListenerInternal::DocumentState(pageList)), m_ps(new SW602TextListenerInternal::State), m_psStack(),
+ m_parserState(parserState), m_documentInterface(documentInterface)
+{
+}
+
+SW602TextListener::~SW602TextListener()
+{
+}
+
+///////////////////
+// text data
+///////////////////
+void SW602TextListener::insertChar(uint8_t character)
+{
+ if (character >= 0x80)
+ {
+ SW602TextListener::insertUnicode(character);
+ return;
+ }
+ _flushDeferredTabs();
+ if (!m_ps->m_isSpanOpened) _openSpan();
+ m_ps->m_textBuffer.append((char) character);
+}
+
+void SW602TextListener::insertCharacter(unsigned char c)
+{
+ // TODO: handle
+ // int unicode = m_parserState.m_fontConverter->unicode(m_ps->m_font.id(), c);
+ int unicode = c;
+ if (unicode == -1)
+ {
+ if (c < 0x20)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::insertCharacter: Find odd char %x\n", (unsigned int)c));
+ }
+ else
+ SW602TextListener::insertChar((uint8_t) c);
+ }
+ else
+ SW602TextListener::insertUnicode((uint32_t) unicode);
+}
+
+int SW602TextListener::insertCharacter(unsigned char c, librevenge::RVNGInputStream &input, long endPos)
+{
+ long debPos=input.tell();
+ // int fId = m_ps->m_font.id();
+ // int unicode = endPos==debPos ?
+ // m_parserState.m_fontConverter->unicode(fId, c) :
+ // m_parserState.m_fontConverter->unicode(fId, c, input);
+ int unicode = c;
+
+ long pos=input.tell();
+ if (endPos > 0 && pos > endPos)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::insertCharacter: problem reading a character\n"));
+ pos = debPos;
+ input.seek(pos, librevenge::RVNG_SEEK_SET);
+ // unicode = m_parserState.m_fontConverter->unicode(fId, c);
+ unicode = c;
+ }
+ if (unicode == -1)
+ {
+ if (c < 0x20)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::insertCharacter: Find odd char %x\n", (unsigned int)c));
+ }
+ else
+ SW602TextListener::insertChar((uint8_t) c);
+ }
+ else
+ SW602TextListener::insertUnicode((uint32_t) unicode);
+
+ return int(pos-debPos);
+}
+
+void SW602TextListener::insertUnicode(uint32_t val)
+{
+ // undef character, we skip it
+ if (val == 0xfffd) return;
+
+ _flushDeferredTabs();
+ if (!m_ps->m_isSpanOpened) _openSpan();
+ libsw602::appendUnicode(val, m_ps->m_textBuffer);
+}
+
+void SW602TextListener::insertUnicodeString(librevenge::RVNGString const &str)
+{
+ _flushDeferredTabs();
+ if (!m_ps->m_isSpanOpened) _openSpan();
+ m_ps->m_textBuffer.append(str);
+}
+
+void SW602TextListener::insertEOL(bool soft)
+{
+ if (!m_ps->m_isParagraphOpened && !m_ps->m_isListElementOpened)
+ _openSpan();
+ _flushDeferredTabs();
+
+ if (soft)
+ {
+ if (m_ps->m_isSpanOpened)
+ _flushText();
+ m_documentInterface->insertLineBreak();
+ }
+ else if (m_ps->m_isParagraphOpened)
+ _closeParagraph();
+
+ // sub/superscript must not survive a new line
+ m_ps->m_font.set(SW602Font::Script());
+}
+
+void SW602TextListener::insertTab()
+{
+ if (!m_ps->m_isParagraphOpened)
+ {
+ m_ps->m_numDeferredTabs++;
+ return;
+ }
+ if (m_ps->m_isSpanOpened) _flushText();
+ m_ps->m_numDeferredTabs++;
+ _flushDeferredTabs();
+}
+
+void SW602TextListener::insertBreak(SW602TextListener::BreakType breakType)
+{
+ switch (breakType)
+ {
+ case ColumnBreak:
+ if (!m_ps->m_isPageSpanOpened && !m_ps->m_inSubDocument)
+ _openSpan();
+ if (m_ps->m_isParagraphOpened)
+ _closeParagraph();
+ m_ps->m_paragraphNeedBreak |= SW602TextListenerInternal::ColumnBreakBit;
+ break;
+ case PageBreak:
+ if (!m_ps->m_isPageSpanOpened && !m_ps->m_inSubDocument)
+ _openSpan();
+ if (m_ps->m_isParagraphOpened)
+ _closeParagraph();
+ m_ps->m_paragraphNeedBreak |= SW602TextListenerInternal::PageBreakBit;
+ break;
+ case SoftPageBreak:
+ default:
+ break;
+ }
+
+ if (m_ps->m_inSubDocument)
+ return;
+
+ switch (breakType)
+ {
+ case PageBreak:
+ case SoftPageBreak:
+ if (m_ps->m_numPagesRemainingInSpan > 0)
+ m_ps->m_numPagesRemainingInSpan--;
+ else
+ {
+ if (!m_ps->m_isTableOpened && !m_ps->m_isParagraphOpened && !m_ps->m_isListElementOpened)
+ _closePageSpan();
+ else
+ m_ps->m_isPageSpanBreakDeferred = true;
+ }
+ m_ps->m_currentPageNumber++;
+ break;
+ case ColumnBreak:
+ default:
+ break;
+ }
+}
+
+void SW602TextListener::_insertBreakIfNecessary(librevenge::RVNGPropertyList &propList)
+{
+ if (!m_ps->m_paragraphNeedBreak)
+ return;
+
+ if ((m_ps->m_paragraphNeedBreak&SW602TextListenerInternal::PageBreakBit) ||
+ m_ps->m_section.numColumns() <= 1)
+ {
+ if (m_ps->m_inSubDocument)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::_insertBreakIfNecessary: can not add page break in subdocument\n"));
+ }
+ else
+ propList.insert("fo:break-before", "page");
+ }
+ else if (m_ps->m_paragraphNeedBreak&SW602TextListenerInternal::ColumnBreakBit)
+ propList.insert("fo:break-before", "column");
+ m_ps->m_paragraphNeedBreak=0;
+}
+
+///////////////////
+// font/paragraph function
+///////////////////
+void SW602TextListener::setFont(SW602Font const &font)
+{
+ if (font == m_ps->m_font) return;
+
+ // check if id and size are defined, if not used the previous fields
+ SW602Font finalFont(font);
+ if (font.id() == -1)
+ finalFont.setId(m_ps->m_font.id());
+ if (font.size() <= 0)
+ finalFont.setSize(m_ps->m_font.size());
+ if (finalFont == m_ps->m_font) return;
+
+ _closeSpan();
+ m_ps->m_font = finalFont;
+}
+
+SW602Font const &SW602TextListener::getFont() const
+{
+ return m_ps->m_font;
+}
+
+bool SW602TextListener::isParagraphOpened() const
+{
+ return m_ps->m_isParagraphOpened;
+}
+
+void SW602TextListener::setParagraph(SW602Paragraph const &para)
+{
+ if (para==m_ps->m_paragraph) return;
+
+ m_ps->m_paragraph=para;
+}
+
+SW602Paragraph const &SW602TextListener::getParagraph() const
+{
+ return m_ps->m_paragraph;
+}
+
+///////////////////
+// field/link :
+///////////////////
+void SW602TextListener::insertField(SW602Field const &field)
+{
+ librevenge::RVNGPropertyList propList;
+ if (field.addTo(propList))
+ {
+ _flushDeferredTabs();
+ _flushText();
+ _openSpan();
+ m_documentInterface->insertField(propList);
+ return;
+ }
+ librevenge::RVNGString text=field.getString();
+ if (!text.empty())
+ SW602TextListener::insertUnicodeString(text);
+ else
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::insertField: must not be called with type=%d\n", int(field.m_type)));
+ }
+}
+
+void SW602TextListener::openLink(SW602Link const &link)
+{
+ if (m_ps->m_inLink)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener:openLink: a link is already opened\n"));
+ return;
+ }
+ if (!m_ps->m_isSpanOpened) _openSpan();
+ librevenge::RVNGPropertyList propList;
+ link.addTo(propList);
+ m_documentInterface->openLink(propList);
+ _pushParsingState();
+ m_ps->m_inLink=true;
+// we do not want any close open paragraph in a link
+ m_ps->m_isParagraphOpened=true;
+}
+
+void SW602TextListener::closeLink()
+{
+ if (!m_ps->m_inLink)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener:closeLink: can not close a link\n"));
+ return;
+ }
+ if (m_ps->m_isSpanOpened) _closeSpan();
+ m_documentInterface->closeLink();
+ _popParsingState();
+}
+
+///////////////////
+// document
+///////////////////
+void SW602TextListener::setDocumentLanguage(std::string locale)
+{
+ if (!locale.length()) return;
+ m_ds->m_metaData.insert("librevenge:language", locale.c_str());
+}
+
+bool SW602TextListener::isDocumentStarted() const
+{
+ return m_ds->m_isDocumentStarted;
+}
+
+void SW602TextListener::startDocument()
+{
+ if (m_ds->m_isDocumentStarted)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::startDocument: the document is already started\n"));
+ return;
+ }
+
+ m_documentInterface->startDocument(librevenge::RVNGPropertyList());
+ m_ds->m_isDocumentStarted = true;
+
+ m_documentInterface->setDocumentMetaData(m_ds->m_metaData);
+}
+
+void SW602TextListener::endDocument(bool sendDelayedSubDoc)
+{
+ if (!m_ds->m_isDocumentStarted)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::endDocument: the document is not started\n"));
+ return;
+ }
+
+ if (!m_ps->m_isPageSpanOpened)
+ {
+ // we must call by hand openPageSpan to avoid sending any header/footer documents
+ if (!sendDelayedSubDoc) _openPageSpan(false);
+ _openSpan();
+ }
+
+ if (m_ps->m_isTableOpened)
+ closeTable();
+ if (m_ps->m_isParagraphOpened)
+ _closeParagraph();
+
+ m_ps->m_paragraph.m_listLevelIndex = 0;
+ _changeList(); // flush the list exterior
+
+ // close the document nice and tight
+ _closeSection();
+ _closePageSpan();
+ m_documentInterface->endDocument();
+ m_ds->m_isDocumentStarted = false;
+}
+
+///////////////////
+// page
+///////////////////
+bool SW602TextListener::isPageSpanOpened() const
+{
+ return m_ps->m_isPageSpanOpened;
+}
+
+SW602PageSpan const &SW602TextListener::getPageSpan()
+{
+ if (!m_ps->m_isPageSpanOpened)
+ _openPageSpan();
+ return m_ds->m_pageSpan;
+}
+
+
+void SW602TextListener::_openPageSpan(bool sendHeaderFooters)
+{
+ if (m_ps->m_isPageSpanOpened)
+ return;
+
+ if (!m_ds->m_isDocumentStarted)
+ startDocument();
+
+ if (m_ds->m_pageList.size()==0)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::_openPageSpan: can not find any page\n"));
+ throw libsw602::ParseException();
+ }
+ unsigned actPage = 0;
+ std::vector<SW602PageSpan>::iterator it = m_ds->m_pageList.begin();
+ ++m_ps->m_currentPage;
+ while (true)
+ {
+ actPage+=(unsigned)it->getPageSpan();
+ if (actPage>=m_ps->m_currentPage) break;
+ if (++it == m_ds->m_pageList.end())
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::_openPageSpan: can not find current page, use last page\n"));
+ --it;
+ break;
+ }
+ }
+ SW602PageSpan &currentPage = *it;
+
+ librevenge::RVNGPropertyList propList;
+ currentPage.getPageProperty(propList);
+ propList.insert("librevenge:is-last-page-span", ++it == m_ds->m_pageList.end());
+
+ if (!m_ps->m_isPageSpanOpened)
+ m_documentInterface->openPageSpan(propList);
+
+ m_ps->m_isPageSpanOpened = true;
+ m_ds->m_pageSpan = currentPage;
+
+ // we insert the header footer
+ if (sendHeaderFooters)
+ currentPage.sendHeaderFooters(this);
+
+ // first paragraph in span (necessary for resetting page number)
+ m_ps->m_firstParagraphInPageSpan = true;
+ m_ps->m_numPagesRemainingInSpan = (currentPage.getPageSpan() - 1);
+}
+
+void SW602TextListener::_closePageSpan()
+{
+ if (!m_ps->m_isPageSpanOpened)
+ return;
+
+ if (m_ps->m_isSectionOpened)
+ _closeSection();
+
+ m_documentInterface->closePageSpan();
+ m_ps->m_isPageSpanOpened = m_ps->m_isPageSpanBreakDeferred = false;
+}
+
+///////////////////
+// header/footer
+///////////////////
+bool SW602TextListener::isHeaderFooterOpened() const
+{
+ return m_ds->m_isHeaderFooterStarted;
+}
+
+bool SW602TextListener::insertHeader(SW602SubDocumentPtr subDocument, librevenge::RVNGPropertyList const &extras)
+{
+ if (m_ds->m_isHeaderFooterStarted)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::insertHeader: Oops a header/footer is already opened\n"));
+ return false;
+ }
+ librevenge::RVNGPropertyList propList(extras);
+ m_documentInterface->openHeader(propList);
+ handleSubDocument(subDocument, libsw602::DOC_HEADER_FOOTER);
+ m_documentInterface->closeHeader();
+ return true;
+}
+
+bool SW602TextListener::insertFooter(SW602SubDocumentPtr subDocument, librevenge::RVNGPropertyList const &extras)
+{
+ if (m_ds->m_isHeaderFooterStarted)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::insertFooter: Oops a header/footer is already opened\n"));
+ return false;
+ }
+ librevenge::RVNGPropertyList propList(extras);
+ m_documentInterface->openFooter(propList);
+ handleSubDocument(subDocument, libsw602::DOC_HEADER_FOOTER);
+ m_documentInterface->closeFooter();
+ return true;
+}
+
+///////////////////
+// section
+///////////////////
+bool SW602TextListener::isSectionOpened() const
+{
+ return m_ps->m_isSectionOpened;
+}
+
+SW602Section const &SW602TextListener::getSection() const
+{
+ return m_ps->m_section;
+}
+
+bool SW602TextListener::canOpenSectionAddBreak() const
+{
+ return !m_ps->m_isTableOpened && (!m_ps->m_inSubDocument || m_ps->m_subDocumentType == libsw602::DOC_TEXT_BOX);
+}
+
+bool SW602TextListener::openSection(SW602Section const &section)
+{
+ if (m_ps->m_isSectionOpened)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::openSection: a section is already opened\n"));
+ return false;
+ }
+
+ if (m_ps->m_isTableOpened || (m_ps->m_inSubDocument && m_ps->m_subDocumentType != libsw602::DOC_TEXT_BOX))
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::openSection: impossible to open a section\n"));
+ return false;
+ }
+
+ m_ps->m_section=section;
+ _openSection();
+ return true;
+}
+
+bool SW602TextListener::closeSection()
+{
+ if (!m_ps->m_isSectionOpened)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::closeSection: no section are already opened\n"));
+ return false;
+ }
+
+ if (m_ps->m_isTableOpened || (m_ps->m_inSubDocument && m_ps->m_subDocumentType != libsw602::DOC_TEXT_BOX))
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::closeSection: impossible to close a section\n"));
+ return false;
+ }
+ _closeSection();
+ return true;
+}
+
+void SW602TextListener::_openSection()
+{
+ if (m_ps->m_isSectionOpened)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::_openSection: a section is already opened\n"));
+ return;
+ }
+
+ if (!m_ps->m_isPageSpanOpened)
+ _openPageSpan();
+
+ librevenge::RVNGPropertyList propList;
+ m_ps->m_section.addTo(propList);
+
+ librevenge::RVNGPropertyListVector columns;
+ m_ps->m_section.addColumnsTo(columns);
+ if (columns.count())
+ propList.insert("style:columns", columns);
+ m_documentInterface->openSection(propList);
+
+ m_ps->m_sectionAttributesChanged = false;
+ m_ps->m_isSectionOpened = true;
+}
+
+void SW602TextListener::_closeSection()
+{
+ if (!m_ps->m_isSectionOpened ||m_ps->m_isTableOpened)
+ return;
+
+ if (m_ps->m_isParagraphOpened)
+ _closeParagraph();
+ m_ps->m_paragraph.m_listLevelIndex=0;
+ _changeList();
+
+ m_documentInterface->closeSection();
+
+ m_ps->m_section = SW602Section();
+ m_ps->m_sectionAttributesChanged = false;
+ m_ps->m_isSectionOpened = false;
+}
+
+///////////////////
+// paragraph
+///////////////////
+void SW602TextListener::_openParagraph()
+{
+ if (m_ps->m_isTableOpened && !m_ps->m_isTableCellOpened)
+ return;
+
+ if (m_ps->m_isParagraphOpened || m_ps->m_isListElementOpened)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::_openParagraph: a paragraph (or a list) is already opened"));
+ return;
+ }
+ if (!m_ps->m_isTableOpened && (!m_ps->m_inSubDocument || m_ps->m_subDocumentType == libsw602::DOC_TEXT_BOX))
+ {
+ if (m_ps->m_sectionAttributesChanged)
+ _closeSection();
+
+ if (!m_ps->m_isSectionOpened)
+ _openSection();
+ }
+
+ librevenge::RVNGPropertyList propList;
+ _appendParagraphProperties(propList);
+ if (!m_ps->m_isParagraphOpened)
+ m_documentInterface->openParagraph(propList);
+
+ _resetParagraphState();
+ m_ps->m_firstParagraphInPageSpan = false;
+}
+
+void SW602TextListener::_closeParagraph()
+{
+ // we can not close a paragraph in a link
+ if (m_ps->m_inLink)
+ return;
+ if (m_ps->m_isListElementOpened)
+ {
+ _closeListElement();
+ return;
+ }
+
+ if (m_ps->m_isParagraphOpened)
+ {
+ if (m_ps->m_isSpanOpened)
+ _closeSpan();
+
+ m_documentInterface->closeParagraph();
+ }
+
+ m_ps->m_isParagraphOpened = false;
+ m_ps->m_paragraph.m_listLevelIndex = 0;
+
+ if (!m_ps->m_isTableOpened && m_ps->m_isPageSpanBreakDeferred && !m_ps->m_inSubDocument)
+ _closePageSpan();
+}
+
+void SW602TextListener::_resetParagraphState(const bool isListElement)
+{
+ m_ps->m_paragraphNeedBreak = 0;
+ m_ps->m_isListElementOpened = isListElement;
+ m_ps->m_isParagraphOpened = true;
+ m_ps->m_isHeaderFooterWithoutParagraph = false;
+}
+
+void SW602TextListener::_appendParagraphProperties(librevenge::RVNGPropertyList &propList, const bool /*isListElement*/)
+{
+ m_ps->m_paragraph.addTo(propList,m_ps->m_isTableOpened);
+
+ if (!m_ps->m_inSubDocument && m_ps->m_firstParagraphInPageSpan && m_ds->m_pageSpan.getPageNumber() >= 0)
+ propList.insert("style:page-number", m_ds->m_pageSpan.getPageNumber());
+
+ _insertBreakIfNecessary(propList);
+}
+
+///////////////////
+// list
+///////////////////
+void SW602TextListener::_openListElement()
+{
+ if (m_ps->m_isTableOpened && !m_ps->m_isTableCellOpened)
+ return;
+
+ if (m_ps->m_isParagraphOpened || m_ps->m_isListElementOpened)
+ return;
+
+ if (!m_ps->m_isTableOpened && (!m_ps->m_inSubDocument || m_ps->m_subDocumentType == libsw602::DOC_TEXT_BOX))
+ {
+ if (m_ps->m_sectionAttributesChanged)
+ _closeSection();
+
+ if (!m_ps->m_isSectionOpened)
+ _openSection();
+ }
+
+ librevenge::RVNGPropertyList propList;
+ _appendParagraphProperties(propList, true);
+ // check if we must change the start value
+ int startValue=m_ps->m_paragraph.m_listStartValue.get();
+ if (startValue > 0 && m_ps->m_list && m_ps->m_list->getStartValueForNextElement() != startValue)
+ {
+ propList.insert("text:start-value", startValue);
+ m_ps->m_list->setStartValueForNextElement(startValue);
+ }
+
+ if (m_ps->m_list) m_ps->m_list->openElement();
+ m_documentInterface->openListElement(propList);
+ _resetParagraphState(true);
+}
+
+void SW602TextListener::_closeListElement()
+{
+ if (m_ps->m_isListElementOpened)
+ {
+ if (m_ps->m_isSpanOpened)
+ _closeSpan();
+
+ if (m_ps->m_list) m_ps->m_list->closeElement();
+ m_documentInterface->closeListElement();
+ }
+
+ m_ps->m_isListElementOpened = m_ps->m_isParagraphOpened = false;
+
+ if (!m_ps->m_isTableOpened && m_ps->m_isPageSpanBreakDeferred && !m_ps->m_inSubDocument)
+ _closePageSpan();
+}
+
+int SW602TextListener::_getListId() const
+{
+ size_t newLevel= (size_t) m_ps->m_paragraph.m_listLevelIndex.get();
+ if (newLevel == 0) return -1;
+ int newListId = m_ps->m_paragraph.m_listId.get();
+ if (newListId > 0) return newListId;
+ static bool first = true;
+ if (first)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::_getListId: the list id is not set, try to find a new one\n"));
+ first = false;
+ }
+ boost::shared_ptr<SW602List> list=m_parserState.m_listManager->getNewList
+ (m_ps->m_list, int(newLevel), *m_ps->m_paragraph.m_listLevel);
+ if (!list) return -1;
+ return list->getId();
+}
+
+void SW602TextListener::_changeList()
+{
+ if (m_ps->m_isParagraphOpened)
+ _closeParagraph();
+
+ size_t actualLevel = m_ps->m_listOrderedLevels.size();
+ size_t newLevel= (size_t)(m_ps->m_paragraph.m_listLevelIndex.get() > 0 ? m_ps->m_paragraph.m_listLevelIndex.get() : 0);
+
+ if (!m_ps->m_isSectionOpened && newLevel && !m_ps->m_isTableOpened &&
+ (!m_ps->m_inSubDocument || m_ps->m_subDocumentType == libsw602::DOC_TEXT_BOX))
+ _openSection();
+
+ int newListId = newLevel>0 ? _getListId() : -1;
+ bool changeList = newLevel &&
+ (m_ps->m_list && m_ps->m_list->getId()!=newListId);
+ size_t minLevel = changeList ? 0 : newLevel;
+ while (actualLevel > minLevel)
+ {
+ if (m_ps->m_listOrderedLevels[--actualLevel])
+ m_documentInterface->closeOrderedListLevel();
+ else
+ m_documentInterface->closeUnorderedListLevel();
+ }
+
+ if (newLevel)
+ {
+ boost::shared_ptr<SW602List> theList;
+
+ theList=m_parserState.m_listManager->getList(newListId);
+ if (!theList)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::_changeList: can not find any list\n"));
+ m_ps->m_listOrderedLevels.resize(actualLevel);
+ return;
+ }
+ m_parserState.m_listManager->needToSend(newListId, m_ds->m_sentListMarkers);
+ m_ps->m_list = theList;
+ m_ps->m_list->setLevel((int)newLevel);
+ }
+
+ m_ps->m_listOrderedLevels.resize(newLevel, false);
+ if (actualLevel == newLevel) return;
+
+ for (size_t i=actualLevel+1; i<= newLevel; i++)
+ {
+ bool ordered = m_ps->m_list->isNumeric(int(i));
+ m_ps->m_listOrderedLevels[i-1] = ordered;
+
+ librevenge::RVNGPropertyList level;
+ m_ps->m_list->addTo(int(i), level);
+ if (ordered)
+ m_documentInterface->openOrderedListLevel(level);
+ else
+ m_documentInterface->openUnorderedListLevel(level);
+ }
+}
+
+///////////////////
+// span
+///////////////////
+void SW602TextListener::_openSpan()
+{
+ if (m_ps->m_isSpanOpened)
+ return;
+
+ if (m_ps->m_isTableOpened && !m_ps->m_isTableCellOpened)
+ return;
+
+ if (!m_ps->m_isParagraphOpened && !m_ps->m_isListElementOpened)
+ {
+ _changeList();
+ if (*m_ps->m_paragraph.m_listLevelIndex == 0)
+ _openParagraph();
+ else
+ _openListElement();
+ }
+
+ librevenge::RVNGPropertyList propList;
+ m_ps->m_font.addTo(propList);
+
+ m_documentInterface->openSpan(propList);
+
+ m_ps->m_isSpanOpened = true;
+}
+
+void SW602TextListener::_closeSpan()
+{
+ // better not to close a link...
+ if (!m_ps->m_isSpanOpened)
+ return;
+
+ _flushText();
+ m_documentInterface->closeSpan();
+ m_ps->m_isSpanOpened = false;
+}
+
+///////////////////
+// text (send data)
+///////////////////
+void SW602TextListener::_flushDeferredTabs()
+{
+ if (m_ps->m_numDeferredTabs == 0) return;
+ if (!m_ps->m_font.hasDecorationLines())
+ {
+ if (!m_ps->m_isSpanOpened) _openSpan();
+ for (; m_ps->m_numDeferredTabs > 0; m_ps->m_numDeferredTabs--)
+ m_documentInterface->insertTab();
+ return;
+ }
+
+ SW602Font oldFont(m_ps->m_font);
+ m_ps->m_font.resetDecorationLines();
+ _closeSpan();
+ _openSpan();
+ for (; m_ps->m_numDeferredTabs > 0; m_ps->m_numDeferredTabs--)
+ m_documentInterface->insertTab();
+ setFont(oldFont);
+}
+
+void SW602TextListener::_flushText()
+{
+ if (m_ps->m_textBuffer.len() == 0) return;
+
+ // when some many ' ' follows each other, call insertSpace
+ librevenge::RVNGString tmpText;
+ int numConsecutiveSpaces = 0;
+ librevenge::RVNGString::Iter i(m_ps->m_textBuffer);
+ for (i.rewind(); i.next();)
+ {
+ if (*(i()) == 0x20) // this test is compatible with unicode format
+ numConsecutiveSpaces++;
+ else
+ numConsecutiveSpaces = 0;
+
+ if (numConsecutiveSpaces > 1)
+ {
+ if (tmpText.len() > 0)
+ {
+ m_documentInterface->insertText(tmpText);
+ tmpText.clear();
+ }
+ m_documentInterface->insertSpace();
+ }
+ else
+ tmpText.append(i());
+ }
+ m_documentInterface->insertText(tmpText);
+ m_ps->m_textBuffer.clear();
+}
+
+///////////////////
+// Note/Comment/picture/textbox
+///////////////////
+void SW602TextListener::insertNote(SW602Note const &note, SW602SubDocumentPtr &subDocument)
+{
+ if (m_ps->m_isNote)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::insertNote try to insert a note recursively (ignored)\n"));
+ return;
+ }
+
+ m_ps->m_isNote = true;
+ if (m_ds->m_isHeaderFooterStarted)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::insertNote try to insert a note in a header/footer\n"));
+ /** Must not happen excepted in corrupted document, so we do the minimum.
+ Note that we have no choice, either we begin by closing the paragraph,
+ ... or we reprogram handleSubDocument.
+ */
+ if (m_ps->m_isParagraphOpened)
+ _closeParagraph();
+ int prevListLevel = *m_ps->m_paragraph.m_listLevelIndex;
+ m_ps->m_paragraph.m_listLevelIndex = 0;
+ _changeList(); // flush the list exterior
+ handleSubDocument(subDocument, libsw602::DOC_NOTE);
+ m_ps->m_paragraph.m_listLevelIndex = prevListLevel;
+ }
+ else
+ {
+ if (!m_ps->m_isParagraphOpened)
+ _openParagraph();
+ else
+ {
+ _flushText();
+ _closeSpan();
+ }
+
+ librevenge::RVNGPropertyList propList;
+ if (note.m_label.len())
+ propList.insert("text:label", librevenge::RVNGPropertyFactory::newStringProp(note.m_label));
+ if (note.m_type == SW602Note::FootNote)
+ {
+ if (note.m_number >= 0)
+ m_ds->m_footNoteNumber = note.m_number;
+ else
+ m_ds->m_footNoteNumber++;
+ propList.insert("librevenge:number", m_ds->m_footNoteNumber);
+ m_documentInterface->openFootnote(propList);
+ handleSubDocument(subDocument, libsw602::DOC_NOTE);
+ m_documentInterface->closeFootnote();
+ }
+ else
+ {
+ if (note.m_number >= 0)
+ m_ds->m_endNoteNumber = note.m_number;
+ else
+ m_ds->m_endNoteNumber++;
+ propList.insert("librevenge:number", m_ds->m_endNoteNumber);
+ m_documentInterface->openEndnote(propList);
+ handleSubDocument(subDocument, libsw602::DOC_NOTE);
+ m_documentInterface->closeEndnote();
+ }
+ }
+ m_ps->m_isNote = false;
+}
+
+void SW602TextListener::insertComment(SW602SubDocumentPtr &subDocument)
+{
+ if (m_ps->m_isNote)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::insertComment try to insert a note recursively (ignored)\n"));
+ return;
+ }
+
+ if (!m_ps->m_isParagraphOpened)
+ _openParagraph();
+ else
+ {
+ _flushText();
+ _closeSpan();
+ }
+
+ librevenge::RVNGPropertyList propList;
+ m_documentInterface->openComment(propList);
+
+ m_ps->m_isNote = true;
+ handleSubDocument(subDocument, libsw602::DOC_COMMENT_ANNOTATION);
+
+ m_documentInterface->closeComment();
+ m_ps->m_isNote = false;
+}
+
+void SW602TextListener::insertTextBox
+(SW602Position const &pos, SW602SubDocumentPtr subDocument, SW602GraphicStyle const &frameStyle)
+{
+ if (!openFrame(pos, frameStyle)) return;
+
+ librevenge::RVNGPropertyList propList;
+ if (!frameStyle.m_frameNextName.empty())
+ propList.insert("librevenge:next-frame-name",frameStyle.m_frameNextName.c_str());
+ m_documentInterface->openTextBox(propList);
+ handleSubDocument(subDocument, libsw602::DOC_TEXT_BOX);
+ m_documentInterface->closeTextBox();
+
+ closeFrame();
+}
+
+void SW602TextListener::insertShape
+(SW602Position const &pos, SW602GraphicShape const &shape, SW602GraphicStyle const &style)
+{
+ // sanity check: avoid to send to many small pict
+ float factor=pos.getScaleFactor(pos.unit(), librevenge::RVNG_POINT);
+ if (pos.size()[0]*factor <= 8 && pos.size()[1]*factor <= 8 && m_ds->m_smallPictureNumber++ > 200)
+ {
+ static bool first = true;
+ if (first)
+ {
+ first = false;
+ SW602_DEBUG_MSG(("SW602TextListener::insertShape: find too much small pictures, skip them from now\n"));
+ }
+ return;
+ }
+
+ // now check that the anchor is coherent with the actual state
+ switch (pos.m_anchorTo)
+ {
+ case SW602Position::Page:
+ break;
+ case SW602Position::Paragraph:
+ if (m_ps->m_isParagraphOpened)
+ _flushText();
+ else
+ _openParagraph();
+ break;
+ case SW602Position::Unknown:
+ default:
+ SW602_DEBUG_MSG(("SW602TextListener::insertShape: UNKNOWN position, insert as char position\n"));
+ // fallthrough intended
+ case SW602Position::CharBaseLine:
+ case SW602Position::Char:
+ if (m_ps->m_isSpanOpened)
+ _flushText();
+ else
+ _openSpan();
+ break;
+ case SW602Position::Cell:
+ case SW602Position::Frame:
+ break;
+ }
+
+ librevenge::RVNGPropertyList shapePList;
+ _handleFrameParameters(shapePList, pos);
+ shapePList.remove("svg:x");
+ shapePList.remove("svg:y");
+
+ librevenge::RVNGPropertyList list;
+ style.addTo(list, shape.getType()==SW602GraphicShape::Line);
+
+ SW602Vec2f decal = factor*pos.origin();
+ switch (shape.addTo(decal, style.hasSurface(), shapePList))
+ {
+ case SW602GraphicShape::C_Ellipse:
+ m_documentInterface->defineGraphicStyle(list);
+ m_documentInterface->drawEllipse(shapePList);
+ break;
+ case SW602GraphicShape::C_Path:
+ {
+ // odt seems to have some problem displaying path so
+ // first create the picture, reset origin (if it is bad)
+ SW602Box2f bdbox = shape.getBdBox(style,true);
+ SW602GraphicEncoder graphicEncoder;
+ SW602GraphicListener graphicListener(m_parserState, SW602Box2f(SW602Vec2f(0,0),bdbox.size()), &graphicEncoder);
+ graphicListener.startDocument();
+ SW602Position pathPos(-1.f*bdbox[0],bdbox.size(),librevenge::RVNG_POINT);
+ pathPos.m_anchorTo=SW602Position::Page;
+ graphicListener.insertShape(pathPos, shape, style);
+ graphicListener.endDocument();
+
+ SW602EmbeddedObject picture;
+ if (!graphicEncoder.getBinaryResult(picture) || !openFrame(pos))
+ break;
+ librevenge::RVNGPropertyList propList;
+ if (picture.addTo(propList))
+ m_documentInterface->insertBinaryObject(propList);
+ closeFrame();
+ break;
+ }
+ case SW602GraphicShape::C_Polyline:
+ m_documentInterface->defineGraphicStyle(list);
+ m_documentInterface->drawPolyline(shapePList);
+ break;
+ case SW602GraphicShape::C_Polygon:
+ m_documentInterface->defineGraphicStyle(list);
+ m_documentInterface->drawPolygon(shapePList);
+ break;
+ case SW602GraphicShape::C_Rectangle:
+ m_documentInterface->defineGraphicStyle(list);
+ m_documentInterface->drawRectangle(shapePList);
+ break;
+ case SW602GraphicShape::C_Bad:
+ break;
+ default:
+ SW602_DEBUG_MSG(("SW602TextListener::insertShape: unexpected shape\n"));
+ break;
+ }
+}
+
+void SW602TextListener::insertPicture(SW602Position const &pos, SW602EmbeddedObject const &picture, SW602GraphicStyle const &style)
+{
+ // sanity check: avoid to send to many small pict
+ float factor=pos.getScaleFactor(pos.unit(), librevenge::RVNG_POINT);
+ if (pos.size()[0]*factor <= 8 && pos.size()[1]*factor <= 8 && m_ds->m_smallPictureNumber++ > 200)
+ {
+ static bool first = true;
+ if (first)
+ {
+ first = false;
+ SW602_DEBUG_MSG(("SW602TextListener::insertPicture: find too much small pictures, skip them from now\n"));
+ }
+ return;
+ }
+
+ if (!openFrame(pos, style)) return;
+
+ librevenge::RVNGPropertyList propList;
+ if (picture.addTo(propList))
+ m_documentInterface->insertBinaryObject(propList);
+ closeFrame();
+}
+
+///////////////////
+// frame
+///////////////////
+bool SW602TextListener::openFrame(SW602Position const &pos, SW602GraphicStyle const &style)
+{
+ if (m_ps->m_isTableOpened && !m_ps->m_isTableCellOpened)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::openFrame: called in table but cell is not opened\n"));
+ return false;
+ }
+ if (m_ps->m_isFrameOpened)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::openFrame: called but a frame is already opened\n"));
+ return false;
+ }
+ SW602Position fPos(pos);
+ switch (pos.m_anchorTo)
+ {
+ case SW602Position::Page:
+ break;
+ case SW602Position::Paragraph:
+ if (m_ps->m_isParagraphOpened)
+ _flushText();
+ else
+ _openParagraph();
+ break;
+ case SW602Position::Unknown:
+ SW602_DEBUG_MSG(("SW602TextListener::openFrame: UNKNOWN position, insert as char position\n"));
+ // fallthrough intended
+ case SW602Position::CharBaseLine:
+ case SW602Position::Char:
+ if (m_ps->m_isSpanOpened)
+ _flushText();
+ else
+ _openSpan();
+ break;
+ case SW602Position::Frame:
+ if (!m_ds->m_subDocuments.size())
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::openFrame: can not determine the frame\n"));
+ return false;
+ }
+ if (m_ps->m_subDocumentType==libsw602::DOC_HEADER_FOOTER)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::openFrame: called with Frame position in header footer, switch to paragraph\n"));
+ if (m_ps->m_isParagraphOpened)
+ _flushText();
+ else
+ _openParagraph();
+ fPos.m_anchorTo=SW602Position::Paragraph;
+ }
+ break;
+ case SW602Position::Cell:
+ if (!m_ps->m_isTableCellOpened)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::openFrame: called with Cell position not in a table cell\n"));
+ return false;
+ }
+ if (pos.m_anchorCellName.empty())
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::openFrame: can not find the cell name\n"));
+ return false;
+ }
+ break;
+ default:
+ SW602_DEBUG_MSG(("SW602TextListener::openFrame: can not determine the anchor\n"));
+ return false;
+ }
+
+ librevenge::RVNGPropertyList propList;
+ style.addFrameTo(propList);
+ _handleFrameParameters(propList, fPos);
+ m_documentInterface->openFrame(propList);
+
+ m_ps->m_isFrameOpened = true;
+ return true;
+}
+
+void SW602TextListener::closeFrame()
+{
+ if (!m_ps->m_isFrameOpened)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::closeFrame: called but no frame is already opened\n"));
+ return;
+ }
+ m_documentInterface->closeFrame();
+ m_ps->m_isFrameOpened = false;
+}
+
+bool SW602TextListener::openGroup(SW602Position const &pos)
+{
+ if (!m_ds->m_isDocumentStarted)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::openGroup: the document is not started\n"));
+ return false;
+ }
+ if (m_ps->m_isTableOpened)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::openGroup: called in table or in a text zone\n"));
+ return false;
+ }
+
+ // now check that the anchor is coherent with the actual state
+ switch (pos.m_anchorTo)
+ {
+ case SW602Position::Page:
+ break;
+ case SW602Position::Paragraph:
+ if (m_ps->m_isParagraphOpened)
+ _flushText();
+ else
+ _openParagraph();
+ break;
+ case SW602Position::Unknown:
+ default:
+ SW602_DEBUG_MSG(("SW602TextListener::openGroup: UNKNOWN position, insert as char position\n"));
+ // fallthrough intended
+ case SW602Position::CharBaseLine:
+ case SW602Position::Char:
+ if (m_ps->m_isSpanOpened)
+ _flushText();
+ else
+ _openSpan();
+ break;
+ case SW602Position::Frame:
+ case SW602Position::Cell:
+ break;
+ }
+
+ librevenge::RVNGPropertyList propList;
+ _handleFrameParameters(propList, pos);
+
+ _pushParsingState();
+ _startSubDocument();
+ m_ps->m_isGroupOpened = true;
+
+ m_documentInterface->openGroup(propList);
+
+ return true;
+}
+
+void SW602TextListener::closeGroup()
+{
+ if (!m_ps->m_isGroupOpened)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::closeGroup: called but no group is already opened\n"));
+ return;
+ }
+ _endSubDocument();
+ _popParsingState();
+ m_documentInterface->closeGroup();
+}
+
+void SW602TextListener::_handleFrameParameters
+(librevenge::RVNGPropertyList &propList, SW602Position const &pos)
+{
+ SW602Vec2f origin = pos.origin();
+ librevenge::RVNGUnit unit = pos.unit();
+ float inchFactor=pos.getInvUnitScale(librevenge::RVNG_INCH);
+ float pointFactor = pos.getInvUnitScale(librevenge::RVNG_POINT);
+
+ if (pos.size()[0]>0)
+ propList.insert("svg:width", double(pos.size()[0]), unit);
+ else if (pos.size()[0]<0)
+ propList.insert("fo:min-width", double(-pos.size()[0]), unit);
+ if (pos.size()[1]>0)
+ propList.insert("svg:height", double(pos.size()[1]), unit);
+ else if (pos.size()[1]<0)
+ propList.insert("fo:min-height", double(-pos.size()[1]), unit);
+ if (pos.order() > 0)
+ propList.insert("draw:z-index", pos.order());
+ if (pos.naturalSize().x() > 4*pointFactor && pos.naturalSize().y() > 4*pointFactor)
+ {
+ propList.insert("librevenge:naturalWidth", pos.naturalSize().x(), pos.unit());
+ propList.insert("librevenge:naturalHeight", pos.naturalSize().y(), pos.unit());
+ }
+ SW602Vec2f TLClip = (1.f/pointFactor)*pos.leftTopClipping();
+ SW602Vec2f RBClip = (1.f/pointFactor)*pos.rightBottomClipping();
+ if (TLClip[0] > 0 || TLClip[1] > 0 || RBClip[0] > 0 || RBClip[1] > 0)
+ {
+ // in ODF1.2 we need to separate the value with ,
+ std::stringstream s;
+ s << "rect(" << TLClip[1] << "pt " << RBClip[0] << "pt "
+ << RBClip[1] << "pt " << TLClip[0] << "pt)";
+ propList.insert("fo:clip", s.str().c_str());
+ }
+
+ if (pos.m_wrapping == SW602Position::WDynamic)
+ propList.insert("style:wrap", "dynamic");
+ else if (pos.m_wrapping == SW602Position::WBackground)
+ {
+ propList.insert("style:wrap", "run-through");
+ propList.insert("style:run-through", "background");
+ }
+ else if (pos.m_wrapping == SW602Position::WForeground)
+ {
+ propList.insert("style:wrap", "run-through");
+ propList.insert("style:run-through", "foreground");
+ }
+ else if (pos.m_wrapping == SW602Position::WParallel)
+ {
+ propList.insert("style:wrap", "parallel");
+ propList.insert("style:run-through", "foreground");
+ }
+ else if (pos.m_wrapping == SW602Position::WRunThrough)
+ propList.insert("style:wrap", "run-through");
+ else
+ propList.insert("style:wrap", "none");
+
+ if (pos.m_anchorTo == SW602Position::Paragraph ||
+ pos.m_anchorTo == SW602Position::Frame)
+ {
+ std::string what= pos.m_anchorTo == SW602Position::Paragraph ?
+ "paragraph" : "frame";
+ propList.insert("text:anchor-type", what.c_str());
+ propList.insert("style:vertical-rel", what.c_str());
+ propList.insert("style:horizontal-rel", what.c_str());
+ double w = m_ds->m_pageSpan.getPageWidth() - m_ps->m_paragraph.getMarginsWidth();
+ w *= inchFactor;
+ switch (pos.m_xPos)
+ {
+ case SW602Position::XRight:
+ if (origin[0] < 0.0 || origin[0] > 0.0)
+ {
+ propList.insert("style:horizontal-pos", "from-left");
+ propList.insert("svg:x", double(origin[0] - pos.size()[0] + w), unit);
+ }
+ else
+ propList.insert("style:horizontal-pos", "right");
+ break;
+ case SW602Position::XCenter:
+ if (origin[0] < 0.0 || origin[0] > 0.0)
+ {
+ propList.insert("style:horizontal-pos", "from-left");
+ propList.insert("svg:x", double(origin[0] - pos.size()[0]/2.0 + w/2.0), unit);
+ }
+ else
+ propList.insert("style:horizontal-pos", "center");
+ break;
+ case SW602Position::XLeft:
+ case SW602Position::XFull:
+ default:
+ if (origin[0] < 0.0 || origin[0] > 0.0)
+ {
+ propList.insert("style:horizontal-pos", "from-left");
+ propList.insert("svg:x", double(origin[0]), unit);
+ }
+ else
+ propList.insert("style:horizontal-pos", "left");
+ break;
+ }
+
+ if (origin[1] < 0.0 || origin[1] > 0.0)
+ {
+ propList.insert("style:vertical-pos", "from-top");
+ propList.insert("svg:y", double(origin[1]), unit);
+ }
+ else
+ propList.insert("style:vertical-pos", "top");
+ return;
+ }
+
+ if (pos.m_anchorTo == SW602Position::Page)
+ {
+ // Page position seems to do not use the page margin...
+ propList.insert("text:anchor-type", "page");
+ if (pos.page() > 0) propList.insert("text:anchor-page-number", pos.page());
+ double w = m_ds->m_pageSpan.getFormWidth();
+ double h = m_ds->m_pageSpan.getFormLength();
+ w *= inchFactor;
+ h *= inchFactor;
+
+ propList.insert("style:vertical-rel", "page");
+ propList.insert("style:horizontal-rel", "page");
+ double newPosition;
+ switch (pos.m_yPos)
+ {
+ case SW602Position::YFull:
+ propList.insert("svg:height", double(h), unit);
+ // fallthrough intended
+ case SW602Position::YTop:
+ if (origin[1] < 0.0 || origin[1] > 0.0)
+ {
+ propList.insert("style:vertical-pos", "from-top");
+ newPosition = origin[1];
+ if (newPosition > h -pos.size()[1])
+ newPosition = h - pos.size()[1];
+ propList.insert("svg:y", double(newPosition), unit);
+ }
+ else
+ propList.insert("style:vertical-pos", "top");
+ break;
+ case SW602Position::YCenter:
+ if (origin[1] < 0.0 || origin[1] > 0.0)
+ {
+ propList.insert("style:vertical-pos", "from-top");
+ newPosition = (h - pos.size()[1])/2.0;
+ if (newPosition > h -pos.size()[1]) newPosition = h - pos.size()[1];
+ propList.insert("svg:y", double(newPosition), unit);
+ }
+ else
+ propList.insert("style:vertical-pos", "middle");
+ break;
+ case SW602Position::YBottom:
+ if (origin[1] < 0.0 || origin[1] > 0.0)
+ {
+ propList.insert("style:vertical-pos", "from-top");
+ newPosition = h - pos.size()[1]-origin[1];
+ if (newPosition > h -pos.size()[1]) newPosition = h -pos.size()[1];
+ else if (newPosition < 0) newPosition = 0;
+ propList.insert("svg:y", double(newPosition), unit);
+ }
+ else
+ propList.insert("style:vertical-pos", "bottom");
+ break;
+ default:
+ break;
+ }
+
+ switch (pos.m_xPos)
+ {
+ case SW602Position::XFull:
+ propList.insert("svg:width", double(w), unit);
+ // fallthrough intended
+ case SW602Position::XLeft:
+ if (origin[0] < 0.0 || origin[0] > 0.0)
+ {
+ propList.insert("style:horizontal-pos", "from-left");
+ propList.insert("svg:x", double(origin[0]), unit);
+ }
+ else
+ propList.insert("style:horizontal-pos", "left");
+ break;
+ case SW602Position::XRight:
+ if (origin[0] < 0.0 || origin[0] > 0.0)
+ {
+ propList.insert("style:horizontal-pos", "from-left");
+ propList.insert("svg:x",double(w - pos.size()[0] + origin[0]), unit);
+ }
+ else
+ propList.insert("style:horizontal-pos", "right");
+ break;
+ case SW602Position::XCenter:
+ if (origin[0] < 0.0 || origin[0] > 0.0)
+ {
+ propList.insert("style:horizontal-pos", "from-left");
+ propList.insert("svg:x", double((w - pos.size()[0])/2. + origin[0]), unit);
+ }
+ else
+ propList.insert("style:horizontal-pos", "center");
+ break;
+ default:
+ break;
+ }
+ return;
+ }
+ if (pos.m_anchorTo == SW602Position::Cell)
+ {
+ if (!pos.m_anchorCellName.empty())
+ propList.insert("table:end-cell-address", pos.m_anchorCellName);
+ // todo: implement also different m_xPos and m_yPos
+ if (origin[0] < 0.0 || origin[0] > 0.0)
+ propList.insert("svg:x", double(origin[0]), unit);
+ if (origin[1] < 0.0 || origin[1] > 0.0)
+ propList.insert("svg:y", double(origin[1]), unit);
+ return;
+ }
+ if (pos.m_anchorTo != SW602Position::Char &&
+ pos.m_anchorTo != SW602Position::CharBaseLine &&
+ pos.m_anchorTo != SW602Position::Unknown) return;
+
+ propList.insert("text:anchor-type", "as-char");
+ if (pos.m_anchorTo == SW602Position::CharBaseLine)
+ propList.insert("style:vertical-rel", "baseline");
+ else
+ propList.insert("style:vertical-rel", "line");
+ switch (pos.m_yPos)
+ {
+ case SW602Position::YFull:
+ case SW602Position::YTop:
+ if (origin[1] < 0.0 || origin[1] > 0.0)
+ {
+ propList.insert("style:vertical-pos", "from-top");
+ propList.insert("svg:y", double(origin[1]), unit);
+ }
+ else
+ propList.insert("style:vertical-pos", "top");
+ break;
+ case SW602Position::YCenter:
+ if (origin[1] < 0.0 || origin[1] > 0.0)
+ {
+ propList.insert("style:vertical-pos", "from-top");
+ propList.insert("svg:y", double(origin[1] - pos.size()[1]/2.0), unit);
+ }
+ else
+ propList.insert("style:vertical-pos", "middle");
+ break;
+ case SW602Position::YBottom:
+ default:
+ if (origin[1] < 0.0 || origin[1] > 0.0)
+ {
+ propList.insert("style:vertical-pos", "from-top");
+ propList.insert("svg:y", double(origin[1] - pos.size()[1]), unit);
+ }
+ else
+ propList.insert("style:vertical-pos", "bottom");
+ break;
+ }
+}
+
+///////////////////
+// subdocument
+///////////////////
+void SW602TextListener::handleSubDocument(SW602SubDocumentPtr subDocument, libsw602::SubDocumentType subDocumentType)
+{
+ _pushParsingState();
+ _startSubDocument();
+ m_ps->m_subDocumentType = subDocumentType;
+
+ m_ps->m_isPageSpanOpened = true;
+ m_ps->m_list.reset();
+
+ switch (subDocumentType)
+ {
+ case libsw602::DOC_TEXT_BOX:
+ m_ds->m_pageSpan.setMargins(0.0);
+ m_ps->m_sectionAttributesChanged = true;
+ break;
+ case libsw602::DOC_HEADER_FOOTER:
+ m_ps->m_isHeaderFooterWithoutParagraph = true;
+ m_ds->m_isHeaderFooterStarted = true;
+ break;
+ case libsw602::DOC_NONE:
+ case libsw602::DOC_CHART:
+ case libsw602::DOC_CHART_ZONE:
+ case libsw602::DOC_NOTE:
+ case libsw602::DOC_SHEET:
+ case libsw602::DOC_TABLE:
+ case libsw602::DOC_COMMENT_ANNOTATION:
+ case libsw602::DOC_GRAPHIC_GROUP:
+ default:
+ break;
+ }
+
+ // Check whether the document is calling itself
+ bool sendDoc = true;
+ for (size_t i = 0; i < m_ds->m_subDocuments.size(); i++)
+ {
+ if (!subDocument)
+ break;
+ if (subDocument == m_ds->m_subDocuments[i])
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::handleSubDocument: recursif call, stop...\n"));
+ sendDoc = false;
+ break;
+ }
+ }
+ if (sendDoc)
+ {
+ if (subDocument)
+ {
+ m_ds->m_subDocuments.push_back(subDocument);
+ boost::shared_ptr<SW602Listener> listen(this, SW602_shared_ptr_noop_deleter<SW602TextListener>());
+ try
+ {
+ subDocument->parse(listen, subDocumentType);
+ }
+ catch (...)
+ {
+ SW602_DEBUG_MSG(("Works: SW602TextListener::handleSubDocument exception catched \n"));
+ }
+ m_ds->m_subDocuments.pop_back();
+ }
+ if (m_ps->m_isHeaderFooterWithoutParagraph)
+ _openSpan();
+ }
+
+ switch (m_ps->m_subDocumentType)
+ {
+ case libsw602::DOC_TEXT_BOX:
+ _closeSection();
+ break;
+ case libsw602::DOC_HEADER_FOOTER:
+ m_ds->m_isHeaderFooterStarted = false;
+ case libsw602::DOC_NONE:
+ case libsw602::DOC_CHART:
+ case libsw602::DOC_CHART_ZONE:
+ case libsw602::DOC_NOTE:
+ case libsw602::DOC_SHEET:
+ case libsw602::DOC_TABLE:
+ case libsw602::DOC_COMMENT_ANNOTATION:
+ case libsw602::DOC_GRAPHIC_GROUP:
+ default:
+ break;
+ }
+ _endSubDocument();
+ _popParsingState();
+}
+
+bool SW602TextListener::isSubDocumentOpened(libsw602::SubDocumentType &subdocType) const
+{
+ if (!m_ps->m_inSubDocument)
+ return false;
+ subdocType = m_ps->m_subDocumentType;
+ return true;
+}
+
+void SW602TextListener::_startSubDocument()
+{
+ m_ds->m_isDocumentStarted = true;
+ m_ps->m_inSubDocument = true;
+}
+
+void SW602TextListener::_endSubDocument()
+{
+ if (m_ps->m_isTableOpened)
+ closeTable();
+ if (m_ps->m_isParagraphOpened)
+ _closeParagraph();
+
+ m_ps->m_paragraph.m_listLevelIndex=0;
+ _changeList(); // flush the list exterior
+}
+
+///////////////////
+// table
+///////////////////
+void SW602TextListener::openTable(SW602Table const &table)
+{
+ if (m_ps->m_isTableOpened)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::openTable: called with m_isTableOpened=true\n"));
+ return;
+ }
+
+ if (m_ps->m_isParagraphOpened)
+ _closeParagraph();
+
+ // default value: which can be redefined by table
+ librevenge::RVNGPropertyList propList;
+ propList.insert("table:align", "left");
+ propList.insert("fo:margin-left", *m_ps->m_paragraph.m_margins[1], *m_ps->m_paragraph.m_marginsUnit);
+ _pushParsingState();
+ _startSubDocument();
+ m_ps->m_subDocumentType = libsw602::DOC_TABLE;
+
+ table.addTablePropertiesTo(propList);
+ m_documentInterface->openTable(propList);
+ m_ps->m_isTableOpened = true;
+}
+
+void SW602TextListener::closeTable()
+{
+ if (!m_ps->m_isTableOpened)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::closeTable: called with m_isTableOpened=false\n"));
+ return;
+ }
+
+ m_ps->m_isTableOpened = false;
+ _endSubDocument();
+ m_documentInterface->closeTable();
+
+ _popParsingState();
+}
+
+void SW602TextListener::openTableRow(float h, librevenge::RVNGUnit unit, bool headerRow)
+{
+ if (m_ps->m_isTableRowOpened)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::openTableRow: called with m_isTableRowOpened=true\n"));
+ return;
+ }
+ if (!m_ps->m_isTableOpened)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::openTableRow: called with m_isTableOpened=false\n"));
+ return;
+ }
+ librevenge::RVNGPropertyList propList;
+ propList.insert("librevenge:is-header-row", headerRow);
+
+ if (h > 0)
+ propList.insert("style:row-height", h, unit);
+ else if (h < 0)
+ propList.insert("style:min-row-height", -h, unit);
+ m_documentInterface->openTableRow(propList);
+ m_ps->m_isTableRowOpened = true;
+}
+
+void SW602TextListener::closeTableRow()
+{
+ if (!m_ps->m_isTableRowOpened)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::openTableRow: called with m_isTableRowOpened=false\n"));
+ return;
+ }
+ m_ps->m_isTableRowOpened = false;
+ m_documentInterface->closeTableRow();
+}
+
+void SW602TextListener::addEmptyTableCell(SW602Vec2i const &pos, SW602Vec2i span)
+{
+ if (!m_ps->m_isTableRowOpened)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::addEmptyTableCell: called with m_isTableRowOpened=false\n"));
+ return;
+ }
+ if (m_ps->m_isTableCellOpened)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::addEmptyTableCell: called with m_isTableCellOpened=true\n"));
+ closeTableCell();
+ }
+ librevenge::RVNGPropertyList propList;
+ propList.insert("librevenge:column", pos[0]);
+ propList.insert("librevenge:row", pos[1]);
+ propList.insert("table:number-columns-spanned", span[0]);
+ propList.insert("table:number-rows-spanned", span[1]);
+ m_documentInterface->openTableCell(propList);
+ m_documentInterface->closeTableCell();
+}
+
+void SW602TextListener::openTableCell(SW602Cell const &cell)
+{
+ if (!m_ps->m_isTableRowOpened)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::openTableCell: called with m_isTableRowOpened=false\n"));
+ return;
+ }
+ if (m_ps->m_isTableCellOpened)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::openTableCell: called with m_isTableCellOpened=true\n"));
+ closeTableCell();
+ }
+
+ librevenge::RVNGPropertyList propList;
+ cell.addTo(propList);
+ m_ps->m_isTableCellOpened = true;
+ m_documentInterface->openTableCell(propList);
+}
+
+void SW602TextListener::closeTableCell()
+{
+ if (!m_ps->m_isTableCellOpened)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::closeTableCell: called with m_isTableCellOpened=false\n"));
+ return;
+ }
+
+ _closeParagraph();
+ m_ps->m_paragraph.m_listLevelIndex=0;
+ _changeList(); // flush the list exterior
+
+ m_ps->m_isTableCellOpened = false;
+ m_documentInterface->closeTableCell();
+}
+
+///////////////////
+// others
+///////////////////
+
+// ---------- state stack ------------------
+boost::shared_ptr<SW602TextListenerInternal::State> SW602TextListener::_pushParsingState()
+{
+ boost::shared_ptr<SW602TextListenerInternal::State> actual = m_ps;
+ m_psStack.push_back(actual);
+ m_ps.reset(new SW602TextListenerInternal::State);
+
+ m_ps->m_isNote = actual->m_isNote;
+
+ return actual;
+}
+
+void SW602TextListener::_popParsingState()
+{
+ if (m_psStack.size()==0)
+ {
+ SW602_DEBUG_MSG(("SW602TextListener::_popParsingState: psStack is empty()\n"));
+ throw libsw602::ParseException();
+ }
+ m_ps = m_psStack.back();
+ m_psStack.pop_back();
+}
+
+}
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602TextListener.h b/src/lib/SW602TextListener.h
new file mode 100644
index 0000000..7dd8adc
--- /dev/null
+++ b/src/lib/SW602TextListener.h
@@ -0,0 +1,268 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/** \file SW602TextListener.h
+ * Defines SW602TextListener: the libsw602 word processor listener
+ *
+ * \note this class is the only class which does the interface with
+ * the librevenge::RVNGTextInterface
+ */
+#ifndef INCLUDED_SW602_TEXT_LISTENER_H
+#define INCLUDED_SW602_TEXT_LISTENER_H
+
+#include <vector>
+
+#include "SW602GraphicStyle.h"
+#include "SW602Listener.h"
+#include "SW602Types.h"
+
+namespace libsw602
+{
+
+class SW602Cell;
+class SW602GraphicStyle;
+class SW602GraphicShape;
+class SW602Table;
+
+namespace SW602TextListenerInternal
+{
+struct DocumentState;
+struct State;
+}
+
+/** This class contents the main functions needed to create a Word processing Document */
+class SW602TextListener : public SW602Listener
+{
+public:
+ /** constructor */
+ SW602TextListener(SW602ParserState &parserState, std::vector<SW602PageSpan> const &pageList, librevenge::RVNGTextInterface *documentInterface);
+ /** destructor */
+ virtual ~SW602TextListener();
+
+ /** returns the listener type */
+ Type getType() const
+ {
+ return Text;
+ }
+
+ /** sets the documents language */
+ void setDocumentLanguage(std::string locale);
+
+ /** starts the document */
+ void startDocument();
+ /** ends the document */
+ void endDocument(bool sendDelayedSubDoc=true);
+ /** returns true if a document is opened */
+ bool isDocumentStarted() const;
+
+ /** function called to add a subdocument */
+ void handleSubDocument(SW602SubDocumentPtr subDocument, libsw602::SubDocumentType subDocumentType);
+ /** returns try if a subdocument is open */
+ bool isSubDocumentOpened(libsw602::SubDocumentType &subdocType) const;
+ /** tries to open a frame */
+ bool openFrame(SW602Position const &pos, SW602GraphicStyle const &style=SW602GraphicStyle::emptyStyle());
+ /** tries to close the last open frame */
+ void closeFrame();
+ /** open a group */
+ bool openGroup(SW602Position const &pos);
+ /** close a group */
+ void closeGroup();
+
+ /** returns true if we can add text data */
+ bool canWriteText() const
+ {
+ return SW602TextListener::isDocumentStarted();
+ }
+
+ // ------ page --------
+ /** returns true if a page is opened */
+ bool isPageSpanOpened() const;
+ /** returns the current page span
+
+ \note this forces the opening of a new page if no page is opened.*/
+ SW602PageSpan const &getPageSpan();
+
+ // ------ header/footer --------
+ /** insert a header */
+ bool insertHeader(SW602SubDocumentPtr subDocument, librevenge::RVNGPropertyList const &extras);
+ /** insert a footer */
+ bool insertFooter(SW602SubDocumentPtr subDocument, librevenge::RVNGPropertyList const &extras);
+ /** returns true if the header/footer is open */
+ bool isHeaderFooterOpened() const;
+
+ // ------ text data -----------
+
+ //! adds a basic character, ..
+ void insertChar(uint8_t character);
+ /** insert a character using the font converter to find the utf8
+ character */
+ void insertCharacter(unsigned char c);
+ /** insert a character using the font converter to find the utf8
+ character and if needed, input to read extra character.
+
+ \return the number of extra character read
+ */
+ int insertCharacter(unsigned char c, librevenge::RVNGInputStream &input, long endPos=-1);
+ /** adds an unicode character.
+ * By convention if \a character=0xfffd(undef), no character is added */
+ void insertUnicode(uint32_t character);
+ //! adds a unicode string
+ void insertUnicodeString(librevenge::RVNGString const &str);
+
+ //! adds a tab
+ void insertTab();
+ //! adds an end of line ( by default an hard one)
+ void insertEOL(bool softBreak=false);
+
+ // ------ text format -----------
+ //! sets the font
+ void setFont(SW602Font const &font);
+ //! returns the actual font
+ SW602Font const &getFont() const;
+
+ // ------ paragraph format -----------
+ //! returns true if a paragraph or a list is opened
+ bool isParagraphOpened() const;
+ //! sets the paragraph
+ void setParagraph(SW602Paragraph const &paragraph);
+ //! returns the actual paragraph
+ SW602Paragraph const &getParagraph() const;
+
+ // ------- fields ----------------
+ //! adds a field type
+ void insertField(SW602Field const &field);
+
+ // ------- link ----------------
+ //! open a link
+ void openLink(SW602Link const &link);
+ //! close a link
+ void closeLink();
+
+ // ------- subdocument -----------------
+ /** insert a note */
+ void insertNote(SW602Note const &note, SW602SubDocumentPtr &subDocument);
+
+ /** adds comment */
+ void insertComment(SW602SubDocumentPtr &subDocument);
+
+ /** adds a picture with potential various representationin given position */
+ void insertPicture(SW602Position const &pos, SW602EmbeddedObject const &picture,
+ SW602GraphicStyle const &style=SW602GraphicStyle::emptyStyle());
+ /** adds a shape picture in given position */
+ void insertShape(SW602Position const &pos, SW602GraphicShape const &shape,
+ SW602GraphicStyle const &style);
+ /** adds a textbox in given position */
+ void insertTextBox(SW602Position const &pos, SW602SubDocumentPtr subDocument,
+ SW602GraphicStyle const &frameStyle=SW602GraphicStyle::emptyStyle());
+
+ // ------- table -----------------
+ /** open a table*/
+ void openTable(SW602Table const &table);
+ /** closes this table */
+ void closeTable();
+ /** open a row with given height ( if h < 0.0, set min-row-height = -h )*/
+ void openTableRow(float h, librevenge::RVNGUnit unit, bool headerRow=false);
+ /** closes this row */
+ void closeTableRow();
+ /** open a cell */
+ void openTableCell(SW602Cell const &cell);
+ /** close a cell */
+ void closeTableCell();
+ /** add empty cell */
+ void addEmptyTableCell(SW602Vec2i const &pos, SW602Vec2i span=SW602Vec2i(1,1));
+
+ // ------- section ---------------
+ /** returns true if we can add open a section, add page break, ... */
+ bool canOpenSectionAddBreak() const;
+ //! returns true if a section is opened
+ bool isSectionOpened() const;
+ //! returns the actual section
+ SW602Section const &getSection() const;
+ //! open a section if possible
+ bool openSection(SW602Section const &section);
+ //! close a section
+ bool closeSection();
+ //! inserts a break type: ColumBreak, PageBreak, ..
+ void insertBreak(BreakType breakType);
+
+protected:
+ //! does open a section (low level)
+ void _openSection();
+ //! does close a section (low level)
+ void _closeSection();
+ //! does open a new page (low level)
+ void _openPageSpan(bool sendHeaderFooters=true);
+ //! does close a page (low level)
+ void _closePageSpan();
+
+ void _startSubDocument();
+ void _endSubDocument();
+
+ void _handleFrameParameters(librevenge::RVNGPropertyList &propList, SW602Position const &pos);
+
+ void _openParagraph();
+ void _closeParagraph();
+ void _appendParagraphProperties(librevenge::RVNGPropertyList &propList, const bool isListElement=false);
+ void _resetParagraphState(const bool isListElement=false);
+
+ /** open a list level */
+ void _openListElement();
+ /** close a list level */
+ void _closeListElement();
+ /** update the list so that it corresponds to the actual level */
+ void _changeList();
+ /** low level: find a list id which corresponds to actual list and a change of level.
+
+ \note called when the list id is not set
+ */
+ int _getListId() const;
+
+ /** low level: the function which opens a new span property */
+ void _openSpan();
+ /** low level: the function which closes the last opened span property */
+ void _closeSpan();
+
+ /** low level: flush the deferred text */
+ void _flushText();
+ /** low level: flush the deferred tabs */
+ void _flushDeferredTabs();
+
+ void _insertBreakIfNecessary(librevenge::RVNGPropertyList &propList);
+
+ /** creates a new parsing state (copy of the actual state)
+ *
+ * \return the old one */
+ boost::shared_ptr<SW602TextListenerInternal::State> _pushParsingState();
+ //! resets the previous parsing state
+ void _popParsingState();
+
+protected:
+ //! the main parse state
+ boost::shared_ptr<SW602TextListenerInternal::DocumentState> m_ds;
+ //! the actual local parse state
+ boost::shared_ptr<SW602TextListenerInternal::State> m_ps;
+ //! stack of local state
+ std::vector<boost::shared_ptr<SW602TextListenerInternal::State> > m_psStack;
+ //! the parser state
+ SW602ParserState &m_parserState;
+ //! the document interface
+ librevenge::RVNGTextInterface *m_documentInterface;
+
+private:
+ //! copy constructor (unimplemented)
+ SW602TextListener(const SW602TextListener &);
+ //! operator= (unimplemented)
+ SW602TextListener &operator=(const SW602TextListener &);
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602Types.cpp b/src/lib/SW602Types.cpp
new file mode 100644
index 0000000..2c256a2
--- /dev/null
+++ b/src/lib/SW602Types.cpp
@@ -0,0 +1,559 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "SW602Types.h"
+
+#include <cmath>
+#include <cstdarg>
+#include <cstdio>
+#include <iomanip>
+#include <string>
+#include <sstream>
+#include <time.h>
+
+#include <ctype.h>
+#include <locale.h>
+
+#include <librevenge/librevenge.h>
+
+/** namespace used to regroup all libwpd functions, enumerations which we have redefined for internal usage */
+namespace libsw602
+{
+uint8_t readU8(librevenge::RVNGInputStream *input)
+{
+ unsigned long numBytesRead;
+ uint8_t const *p = input->read(sizeof(uint8_t), numBytesRead);
+
+ if (!p || numBytesRead != sizeof(uint8_t))
+ throw libsw602::FileException();
+
+ return *p;
+}
+
+void appendUnicode(uint32_t val, librevenge::RVNGString &buffer)
+{
+ uint8_t first;
+ int len;
+ if (val < 0x80)
+ {
+ first = 0;
+ len = 1;
+ }
+ else if (val < 0x800)
+ {
+ first = 0xc0;
+ len = 2;
+ }
+ else if (val < 0x10000)
+ {
+ first = 0xe0;
+ len = 3;
+ }
+ else if (val < 0x200000)
+ {
+ first = 0xf0;
+ len = 4;
+ }
+ else if (val < 0x4000000)
+ {
+ first = 0xf8;
+ len = 5;
+ }
+ else
+ {
+ first = 0xfc;
+ len = 6;
+ }
+
+ uint8_t outbuf[6] = { 0, 0, 0, 0, 0, 0 };
+ int i;
+ for (i = len - 1; i > 0; --i)
+ {
+ outbuf[i] = uint8_t((val & 0x3f) | 0x80);
+ val >>= 6;
+ }
+ outbuf[0] = uint8_t(val | first);
+ for (i = 0; i < len; i++) buffer.append((char)outbuf[i]);
+}
+}
+
+namespace libsw602
+{
+std::string numberingTypeToString(NumberingType type)
+{
+ switch (type)
+ {
+ case ARABIC:
+ return "1";
+ case LOWERCASE:
+ return "a";
+ case UPPERCASE:
+ return "A";
+ case LOWERCASE_ROMAN:
+ return "i";
+ case UPPERCASE_ROMAN:
+ return "I";
+ case NONE:
+ case BULLET:
+ default:
+ break;
+ }
+ SW602_DEBUG_MSG(("libsw602::numberingTypeToString: must not be called with type %d\n", int(type)));
+ return "1";
+}
+
+std::string numberingValueToString(NumberingType type, int value)
+{
+ std::stringstream ss;
+ std::string s("");
+ switch (type)
+ {
+ case ARABIC:
+ ss << value;
+ return ss.str();
+ case LOWERCASE:
+ case UPPERCASE:
+ if (value <= 0)
+ {
+ SW602_DEBUG_MSG(("libsw602::numberingValueToString: value can not be negative or null for type %d\n", int(type)));
+ return (type == LOWERCASE) ? "a" : "A";
+ }
+ while (value > 0)
+ {
+ s = char((type == LOWERCASE ? 'a' : 'A')+((value-1)%26))+s;
+ value = (value-1)/26;
+ }
+ return s;
+ case LOWERCASE_ROMAN:
+ case UPPERCASE_ROMAN:
+ {
+ static char const *(romanS[]) = {"M", "CM", "D", "CD", "C", "XC", "L",
+ "XL", "X", "IX", "V", "IV", "I"
+ };
+ static char const *(romans[]) = {"m", "cm", "d", "cd", "c", "xc", "l",
+ "xl", "x", "ix", "v", "iv", "i"
+ };
+ static int const(romanV[]) = {1000, 900, 500, 400, 100, 90, 50,
+ 40, 10, 9, 5, 4, 1
+ };
+ if (value <= 0 || value >= 4000)
+ {
+ SW602_DEBUG_MSG(("libsw602::numberingValueToString: out of range value for type %d\n", int(type)));
+ return (type == LOWERCASE_ROMAN) ? "i" : "I";
+ }
+ for (int p = 0; p < 13; p++)
+ {
+ while (value >= romanV[p])
+ {
+ ss << ((type == LOWERCASE_ROMAN) ? romans[p] : romanS[p]);
+ value -= romanV[p];
+ }
+ }
+ return ss.str();
+ }
+ case NONE:
+ return "";
+ break;
+ case BULLET:
+ default:
+ SW602_DEBUG_MSG(("libsw602::numberingValueToString: must not be called with type %d\n", int(type)));
+ break;
+ }
+ return "";
+}
+}
+
+namespace libsw602
+{
+
+// color function
+SW602Color SW602Color::barycenter(float alpha, SW602Color const &colA,
+ float beta, SW602Color const &colB)
+{
+ uint32_t res = 0;
+ for (int i=0, depl=0; i<4; i++, depl+=8)
+ {
+ float val=alpha*float((colA.m_value>>depl)&0xFF)+beta*float((colB.m_value>>depl)&0xFF);
+ if (val < 0) val=0;
+ if (val > 256) val=256;
+ unsigned char comp= (unsigned char)val;
+ res+=uint32_t(comp<<depl);
+ }
+ return SW602Color(res);
+}
+
+std::ostream &operator<< (std::ostream &o, SW602Color const &c)
+{
+ const std::streamsize width = o.width();
+ const char fill = o.fill();
+ o << "#" << std::hex << std::setfill('0') << std::setw(6)
+ << (c.m_value&0xFFFFFF)
+ // std::ios::width() takes/returns std::streamsize (long), but
+ // std::setw() takes int. Go figure...
+ << std::dec << std::setfill(fill) << std::setw(static_cast<int>(width));
+ return o;
+}
+
+std::string SW602Color::str() const
+{
+ std::stringstream stream;
+ stream << *this;
+ return stream.str();
+}
+
+// field function
+bool SW602Field::addTo(librevenge::RVNGPropertyList &propList) const
+{
+ switch (m_type)
+ {
+ break;
+ case PageCount:
+ propList.insert("librevenge:field-type", "text:page-count");
+ propList.insert("style:num-format", numberingTypeToString(m_numberingType).c_str());
+ break;
+ case PageNumber:
+ propList.insert("librevenge:field-type", "text:page-number");
+ propList.insert("style:num-format", numberingTypeToString(m_numberingType).c_str());
+ break;
+ case Title:
+ propList.insert("librevenge:field-type", "text:title");
+ break;
+ case Database:
+ case Date:
+ case Time:
+ case None:
+ default:
+ return false;
+ }
+ return true;
+}
+
+librevenge::RVNGString SW602Field::getString() const
+{
+ librevenge::RVNGString res;
+ switch (m_type)
+ {
+ case Database:
+ if (m_data.length())
+ res=librevenge::RVNGString(m_data.c_str());
+ else
+ res=librevenge::RVNGString("#DATAFIELD#");
+ break;
+ case Date:
+ case Time:
+ {
+ std::string format(m_DTFormat);
+ if (format.length()==0)
+ {
+ if (m_type==Date)
+ format="%m/%d/%y";
+ else
+ format="%I:%M:%S %p";
+ }
+ time_t now = time(0L);
+ struct tm timeinfo;
+ if (localtime_r(&now, &timeinfo))
+ {
+ char buf[256];
+ strftime(buf, 256, format.c_str(), &timeinfo);
+ res=librevenge::RVNGString(buf);
+ }
+ break;
+ }
+ case PageCount:
+ case PageNumber:
+ case Title:
+ case None:
+ default:
+ break;
+ }
+
+ return res;
+}
+// link function
+bool SW602Link::addTo(librevenge::RVNGPropertyList &propList) const
+{
+ propList.insert("xlink:type","simple");
+ if (!m_HRef.empty())
+ propList.insert("xlink:href",m_HRef.c_str());
+ return true;
+}
+
+// border function
+int SW602Border::compare(SW602Border const &orig) const
+{
+ int diff = int(m_style)-int(orig.m_style);
+ if (diff) return diff;
+ diff = int(m_type)-int(orig.m_type);
+ if (diff) return diff;
+ if (m_width < orig.m_width) return -1;
+ if (m_width > orig.m_width) return 1;
+ if (m_color < orig.m_color) return -1;
+ if (m_color > orig.m_color) return 1;
+ return 0;
+}
+bool SW602Border::addTo(librevenge::RVNGPropertyList &propList, std::string const which) const
+{
+ std::stringstream stream, field;
+ stream << m_width << "pt ";
+ if (m_type==SW602Border::Double || m_type==SW602Border::Triple)
+ {
+ static bool first = true;
+ if (first && m_style!=Simple)
+ {
+ SW602_DEBUG_MSG(("SW602Border::addTo: find double or tripe border with complex style\n"));
+ first = false;
+ }
+ stream << "double";
+ }
+ else
+ {
+ switch (m_style)
+ {
+ case Dot:
+ case LargeDot:
+ stream << "dotted";
+ break;
+ case Dash:
+ stream << "dashed";
+ break;
+ case Simple:
+ stream << "solid";
+ break;
+ case None:
+ default:
+ stream << "none";
+ break;
+ }
+ }
+ stream << " " << m_color;
+ field << "fo:border";
+ if (which.length())
+ field << "-" << which;
+ propList.insert(field.str().c_str(), stream.str().c_str());
+ size_t numRelWidth=m_widthsList.size();
+ if (!numRelWidth)
+ return true;
+ if (m_type!=SW602Border::Double || numRelWidth!=3)
+ {
+ static bool first = true;
+ if (first)
+ {
+ SW602_DEBUG_MSG(("SW602Border::addTo: relative width is only implemented with double style\n"));
+ first = false;
+ }
+ return true;
+ }
+ double totalWidth=0;
+ for (size_t w=0; w < numRelWidth; w++)
+ totalWidth+=m_widthsList[w];
+ if (totalWidth <= 0)
+ {
+ SW602_DEBUG_MSG(("SW602Border::addTo: can not compute total width\n"));
+ return true;
+ }
+ double factor=m_width/totalWidth;
+ stream.str("");
+ for (size_t w=0; w < numRelWidth; w++)
+ {
+ stream << factor *m_widthsList[w]<< "pt";
+ if (w+1!=numRelWidth)
+ stream << " ";
+ }
+ field.str("");
+ field << "style:border-line-width";
+ if (which.length())
+ field << "-" << which;
+ propList.insert(field.str().c_str(), stream.str().c_str());
+ return true;
+}
+
+std::ostream &operator<< (std::ostream &o, SW602Border::Style const &style)
+{
+ switch (style)
+ {
+ case SW602Border::None:
+ o << "none";
+ break;
+ case SW602Border::Simple:
+ break;
+ case SW602Border::Dot:
+ o << "dot";
+ break;
+ case SW602Border::LargeDot:
+ o << "large dot";
+ break;
+ case SW602Border::Dash:
+ o << "dash";
+ break;
+ default:
+ SW602_DEBUG_MSG(("SW602Border::operator<<: find unknown style\n"));
+ o << "#style=" << int(style);
+ break;
+ }
+ return o;
+}
+
+std::ostream &operator<< (std::ostream &o, SW602Border const &border)
+{
+ o << border.m_style << ":";
+ switch (border.m_type)
+ {
+ case SW602Border::Single:
+ break;
+ case SW602Border::Double:
+ o << "double:";
+ break;
+ case SW602Border::Triple:
+ o << "triple:";
+ break;
+ default:
+ SW602_DEBUG_MSG(("SW602Border::operator<<: find unknown type\n"));
+ o << "#type=" << int(border.m_type) << ":";
+ break;
+ }
+ if (border.m_width > 1 || border.m_width < 1) o << "w=" << border.m_width << ":";
+ if (!border.m_color.isBlack())
+ o << "col=" << border.m_color << ":";
+ o << ",";
+ size_t numRelWidth=border.m_widthsList.size();
+ if (numRelWidth)
+ {
+ o << "bordW[rel]=[";
+ for (size_t i=0; i < numRelWidth; i++)
+ o << border.m_widthsList[i] << ",";
+ o << "]:";
+ }
+ o << border.m_extra;
+ return o;
+}
+
+// picture function
+bool SW602EmbeddedObject::addTo(librevenge::RVNGPropertyList &propList) const
+{
+ bool firstSet=false;
+ librevenge::RVNGPropertyListVector auxiliarVector;
+ for (size_t i=0; i<m_dataList.size(); ++i)
+ {
+ if (m_dataList[i].empty()) continue;
+ std::string type=m_typeList.size() ? m_typeList[i] : "image/pict";
+ if (!firstSet)
+ {
+ propList.insert("librevenge:mime-type", type.c_str());
+ propList.insert("office:binary-data", m_dataList[i]);
+ firstSet=true;
+ continue;
+ }
+ librevenge::RVNGPropertyList auxiList;
+ auxiList.insert("librevenge:mime-type", type.c_str());
+ auxiList.insert("office:binary-data", m_dataList[i]);
+ auxiliarVector.append(auxiList);
+ }
+ if (!auxiliarVector.empty())
+ propList.insert("librevenge:replacement-objects", auxiliarVector);
+ if (!firstSet)
+ {
+ SW602_DEBUG_MSG(("SW602EmbeddedObject::addTo: called without picture\n"));
+ return false;
+ }
+ return true;
+}
+
+int SW602EmbeddedObject::cmp(SW602EmbeddedObject const &pict) const
+{
+ if (m_typeList.size()!=pict.m_typeList.size())
+ return m_typeList.size()<pict.m_typeList.size() ? -1 : 1;
+ for (size_t i=0; i<m_typeList.size(); ++i)
+ {
+ if (m_typeList[i]<pict.m_typeList[i]) return -1;
+ if (m_typeList[i]>pict.m_typeList[i]) return 1;
+ }
+ if (m_dataList.size()!=pict.m_dataList.size())
+ return m_dataList.size()<pict.m_dataList.size() ? -1 : 1;
+ for (size_t i=0; i<m_dataList.size(); ++i)
+ {
+ if (m_dataList[i].size() < pict.m_dataList[i].size()) return 1;
+ if (m_dataList[i].size() > pict.m_dataList[i].size()) return -1;
+
+ const unsigned char *ptr=m_dataList[i].getDataBuffer();
+ const unsigned char *aPtr=pict.m_dataList[i].getDataBuffer();
+ if (!ptr || !aPtr) continue; // must only appear if the two buffers are empty
+ for (unsigned long h=0; h < m_dataList[i].size(); ++h, ++ptr, ++aPtr)
+ {
+ if (*ptr < *aPtr) return 1;
+ if (*ptr > *aPtr) return -1;
+ }
+ }
+ return 0;
+}
+
+std::ostream &operator<<(std::ostream &o, SW602EmbeddedObject const &pict)
+{
+ if (pict.isEmpty()) return o;
+ o << "[";
+ for (size_t i=0; i<pict.m_typeList.size(); ++i)
+ {
+ if (pict.m_typeList[i].empty())
+ o << "_,";
+ else
+ o << pict.m_typeList[i] << ",";
+ }
+ o << "],";
+ return o;
+}
+
+}
+
+// a little geometry
+namespace libsw602
+{
+SW602Vec2f rotatePointAroundCenter(SW602Vec2f const &point, SW602Vec2f const &center, float angle)
+{
+ float angl=float(M_PI/180.)*angle;
+ SW602Vec2f pt = point-center;
+ return center + SW602Vec2f(std::cos(angl)*pt[0]-std::sin(angl)*pt[1],
+ std::sin(angl)*pt[0]+std::cos(angl)*pt[1]);
+}
+
+SW602Box2f rotateBoxFromCenter(SW602Box2f const &box, float angle)
+{
+ SW602Vec2f center=box.center();
+ SW602Vec2f minPt, maxPt;
+ for (int p=0; p<4; ++p)
+ {
+ SW602Vec2f pt=rotatePointAroundCenter(SW602Vec2f(box[p<2?0:1][0],box[(p%2)?0:1][1]), center, angle);
+ if (p==0)
+ {
+ minPt=maxPt=pt;
+ continue;
+ }
+ for (int c=0; c<2; ++c)
+ {
+ if (pt[c]<minPt[c])
+ minPt[c]=pt[c];
+ else if (pt[c]>maxPt[c])
+ maxPt[c]=pt[c];
+ }
+ }
+ return SW602Box2f(minPt,maxPt);
+}
+
+// debug message
+#ifdef DEBUG
+void printDebugMsg(const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ std::vfprintf(stderr, format, args);
+ va_end(args);
+}
+#endif
+}
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/SW602Types.h b/src/lib/SW602Types.h
new file mode 100644
index 0000000..5d1eafb
--- /dev/null
+++ b/src/lib/SW602Types.h
@@ -0,0 +1,1184 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * This file is part of the libsw602 project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SW602_TYPES_H
+#define INCLUDED_SW602_TYPES_H
+
+#include <cmath>
+#include <map>
+#include <ostream>
+#include <vector>
+
+#include <boost/shared_ptr.hpp>
+
+#include "libsw602_utils.h"
+
+/* ---------- input ----------------- */
+namespace libsw602
+{
+//! adds an unicode character to a string
+void appendUnicode(uint32_t val, librevenge::RVNGString &buffer);
+}
+
+/* ---------- small enum/class ------------- */
+namespace libsw602
+{
+//! basic position enum
+enum Position { Left = 0, Right = 1, Top = 2, Bottom = 3, HMiddle = 4, VMiddle = 5 };
+//! basic position enum bits
+enum { LeftBit = 0x01, RightBit = 0x02, TopBit=0x4, BottomBit = 0x08, HMiddleBit = 0x10, VMiddleBit = 0x20 };
+
+enum NumberingType { NONE, BULLET, ARABIC, LOWERCASE, UPPERCASE, LOWERCASE_ROMAN, UPPERCASE_ROMAN };
+std::string numberingTypeToString(NumberingType type);
+std::string numberingValueToString(NumberingType type, int value);
+enum SubDocumentType { DOC_NONE, DOC_CHART, DOC_CHART_ZONE, DOC_COMMENT_ANNOTATION, DOC_GRAPHIC_GROUP, DOC_HEADER_FOOTER, DOC_NOTE, DOC_SHEET, DOC_TABLE, DOC_TEXT_BOX };
+}
+
+namespace libsw602
+{
+
+//! the class to store a color
+struct SW602Color
+{
+ //! constructor
+ explicit SW602Color(uint32_t argb=0) : m_value(argb)
+ {
+ }
+ //! constructor from color
+ SW602Color(unsigned char r, unsigned char g, unsigned char b, unsigned char a=255) :
+ m_value(uint32_t((a<<24)+(r<<16)+(g<<8)+b))
+ {
+ }
+ //! operator=
+ SW602Color &operator=(uint32_t argb)
+ {
+ m_value = argb;
+ return *this;
+ }
+ //! return a color from a cmyk color ( basic)
+ static SW602Color colorFromCMYK(unsigned char c, unsigned char m, unsigned char y, unsigned char k)
+ {
+ double w=1.-double(k)/255.;
+ return SW602Color
+ ((unsigned char)(255 * (1-double(c)/255) * w),
+ (unsigned char)(255 * (1-double(m)/255) * w),
+ (unsigned char)(255 * (1-double(y)/255) * w)
+ );
+ }
+ //! return a color from a hsl color (basic)
+ static SW602Color colorFromHSL(unsigned char H, unsigned char S, unsigned char L)
+ {
+ double c=(1-((L>=128) ? (2*double(L)-255) : (255-2*double(L)))/255)*
+ double(S)/255;
+ double tmp=std::fmod((double(H)*6/255),2)-1;
+ double x=c*(1-(tmp>0 ? tmp : -tmp));
+ unsigned char C=(unsigned char)(255*c);
+ unsigned char M=(unsigned char)(double(L)-255*c/2);
+ unsigned char X=(unsigned char)(255*x);
+ if (H<=42) return SW602Color((unsigned char)(M+C),(unsigned char)(M+X),(unsigned char)M);
+ if (H<=85) return SW602Color((unsigned char)(M+X),(unsigned char)(M+C),(unsigned char)M);
+ if (H<=127) return SW602Color((unsigned char)M,(unsigned char)(M+C),(unsigned char)(M+X));
+ if (H<=170) return SW602Color((unsigned char)M,(unsigned char)(M+X),(unsigned char)(M+C));
+ if (H<=212) return SW602Color((unsigned char)(M+X),(unsigned char)M,(unsigned char)(M+C));
+ return SW602Color((unsigned char)(M+C),(unsigned char)(M),(unsigned char)(M+X));
+ }
+ //! return the back color
+ static SW602Color black()
+ {
+ return SW602Color(0,0,0);
+ }
+ //! return the white color
+ static SW602Color white()
+ {
+ return SW602Color(255,255,255);
+ }
+
+ //! return alpha*colA+beta*colB
+ static SW602Color barycenter(float alpha, SW602Color const &colA,
+ float beta, SW602Color const &colB);
+ //! return the rgba value
+ uint32_t value() const
+ {
+ return m_value;
+ }
+ //! returns the alpha value
+ unsigned char getAlpha() const
+ {
+ return (unsigned char)((m_value>>24)&0xFF);
+ }
+ //! returns the green value
+ unsigned char getBlue() const
+ {
+ return (unsigned char)(m_value&0xFF);
+ }
+ //! returns the red value
+ unsigned char getRed() const
+ {
+ return (unsigned char)((m_value>>16)&0xFF);
+ }
+ //! returns the green value
+ unsigned char getGreen() const
+ {
+ return (unsigned char)((m_value>>8)&0xFF);
+ }
+ //! return true if the color is black
+ bool isBlack() const
+ {
+ return (m_value&0xFFFFFF)==0;
+ }
+ //! return true if the color is white
+ bool isWhite() const
+ {
+ return (m_value&0xFFFFFF)==0xFFFFFF;
+ }
+ //! operator==
+ bool operator==(SW602Color const &c) const
+ {
+ return (c.m_value&0xFFFFFF)==(m_value&0xFFFFFF);
+ }
+ //! operator!=
+ bool operator!=(SW602Color const &c) const
+ {
+ return !operator==(c);
+ }
+ //! operator<
+ bool operator<(SW602Color const &c) const
+ {
+ return (c.m_value&0xFFFFFF)<(m_value&0xFFFFFF);
+ }
+ //! operator<=
+ bool operator<=(SW602Color const &c) const
+ {
+ return (c.m_value&0xFFFFFF)<=(m_value&0xFFFFFF);
+ }
+ //! operator>
+ bool operator>(SW602Color const &c) const
+ {
+ return !operator<=(c);
+ }
+ //! operator>=
+ bool operator>=(SW602Color const &c) const
+ {
+ return !operator<(c);
+ }
+ //! operator<< in the form \#rrggbb
+ friend std::ostream &operator<< (std::ostream &o, SW602Color const &c);
+ //! print the color in the form \#rrggbb
+ std::string str() const;
+protected:
+ //! the argb color
+ uint32_t m_value;
+};
+
+//! a border
+struct SW602Border
+{
+ /** the line style */
+ enum Style { None, Simple, Dot, LargeDot, Dash };
+ /** the line repetition */
+ enum Type { Single, Double, Triple };
+
+ //! constructor
+ SW602Border() : m_style(Simple), m_type(Single), m_width(1), m_widthsList(), m_color(SW602Color::black()), m_extra("") { }
+ /** add the border property to proplist (if needed )
+
+ \note if set which must be equal to "left", "top", ... */
+ bool addTo(librevenge::RVNGPropertyList &propList, std::string which="") const;
+ //! returns true if the border is empty
+ bool isEmpty() const
+ {
+ return m_style==None || m_width <= 0;
+ }
+ //! operator==
+ bool operator==(SW602Border const &orig) const
+ {
+ return !operator!=(orig);
+ }
+ //! operator!=
+ bool operator!=(SW602Border const &orig) const
+ {
+ return m_style != orig.m_style || m_type != orig.m_type ||
+ m_width < orig.m_width || m_width > orig.m_width || m_color != orig.m_color;
+ }
+ //! compare two borders
+ int compare(SW602Border const &orig) const;
+
+ //! operator<<
+ friend std::ostream &operator<< (std::ostream &o, SW602Border const &border);
+ //! operator<<: prints data in form "none|dot|..."
+ friend std::ostream &operator<< (std::ostream &o, SW602Border::Style const &style);
+ //! the border style
+ Style m_style;
+
+ // multiple borders
+
+ //! the border repetition
+ Type m_type;
+ //! the border total width in point
+ double m_width;
+ /** the different length used for each line/sep (if defined)
+
+ \note when defined, the size of this list must be equal to 2*Type-1*/
+ std::vector<double> m_widthsList;
+ //! the border color
+ SW602Color m_color;
+ //! extra data ( if needed)
+ std::string m_extra;
+};
+
+//! a field
+struct SW602Field
+{
+ /** Defines some basic type for field */
+ enum Type { None, PageCount, PageNumber, Date, Time, Title, Database };
+
+ /** basic constructor */
+ explicit SW602Field(Type type) : m_type(type), m_DTFormat(""), m_numberingType(libsw602::ARABIC), m_data("")
+ {
+ }
+ /** add the link property to proplist (if possible) */
+ bool addTo(librevenge::RVNGPropertyList &propList) const;
+ //! returns a string corresponding to the field (if possible) */
+ librevenge::RVNGString getString() const;
+ //! the type
+ Type m_type;
+ //! the date/time format using strftime format if defined
+ std::string m_DTFormat;
+ //! the number type ( for number field )
+ libsw602::NumberingType m_numberingType;
+ //! the database/link field ( if defined )
+ std::string m_data;
+};
+
+//! a link
+struct SW602Link
+{
+ /** basic constructor */
+ SW602Link() : m_HRef("")
+ {
+ }
+
+ /** add the link property to proplist (if needed ) */
+ bool addTo(librevenge::RVNGPropertyList &propList) const;
+
+ //! the href field
+ std::string m_HRef;
+};
+
+//! a note
+struct SW602Note
+{
+ //! enum to define note type
+ enum Type { FootNote, EndNote };
+ //! constructor
+ explicit SW602Note(Type type) : m_type(type), m_label(""), m_number(-1)
+ {
+ }
+ //! the note type
+ Type m_type;
+ //! the note label
+ librevenge::RVNGString m_label;
+ //! the note number if defined
+ int m_number;
+};
+
+/** small class use to define a embedded object
+
+ \note mainly used to store picture
+ */
+struct SW602EmbeddedObject
+{
+ //! empty constructor
+ SW602EmbeddedObject() : m_dataList(), m_typeList()
+ {
+ }
+ //! constructor
+ SW602EmbeddedObject(librevenge::RVNGBinaryData const &binaryData,
+ std::string type="image/pict") : m_dataList(), m_typeList()
+ {
+ add(binaryData, type);
+ }
+ //! destructor
+ virtual ~SW602EmbeddedObject()
+ {
+ }
+ //! return true if the picture contains no data
+ bool isEmpty() const
+ {
+ for (size_t i=0; i<m_dataList.size(); ++i)
+ {
+ if (!m_dataList[i].empty())
+ return false;
+ }
+ return true;
+ }
+ //! add a picture
+ void add(librevenge::RVNGBinaryData const &binaryData, std::string type="image/pict")
+ {
+ size_t pos=m_dataList.size();
+ if (pos<m_typeList.size()) pos=m_typeList.size();
+ m_dataList.resize(pos+1);
+ m_dataList[pos]=binaryData;
+ m_typeList.resize(pos+1);
+ m_typeList[pos]=type;
+ }
+ /** add the link property to proplist */
+ bool addTo(librevenge::RVNGPropertyList &propList) const;
+ /** operator<<*/
+ friend std::ostream &operator<<(std::ostream &o, SW602EmbeddedObject const &pict);
+ /** a comparison function */
+ int cmp(SW602EmbeddedObject const &pict) const;
+
+ //! the picture content: one data by representation
+ std::vector<librevenge::RVNGBinaryData> m_dataList;
+ //! the picture type: one type by representation
+ std::vector<std::string> m_typeList;
+};
+
+// declarations of smart pointers
+class SW602GraphicListener;
+class SW602Listener;
+class SW602ListManager;
+class SW602ParserState;
+class SW602SpreadsheetListener;
+class SW602SubDocument;
+class SW602TextListener;
+//! a smart pointer of SW602GraphicListener
+typedef boost::shared_ptr<SW602GraphicListener> SW602GraphicListenerPtr;
+//! a smart pointer of SW602Listener
+typedef boost::shared_ptr<SW602Listener> SW602ListenerPtr;
+//! a smart pointer of SW602ListManager
+typedef boost::shared_ptr<SW602ListManager> SW602ListManagerPtr;
+//! a smart pointer of SW602ParserState
+typedef boost::shared_ptr<SW602ParserState> SW602ParserStatePtr;
+//! a smart pointer of SW602SpreadsheetListener
+typedef boost::shared_ptr<SW602SpreadsheetListener> SW602SpreadsheetListenerPtr;
+//! a smart pointer of SW602SubDocument
+typedef boost::shared_ptr<SW602SubDocument> SW602SubDocumentPtr;
+//! a smart pointer of SW602TextListener
+typedef boost::shared_ptr<SW602TextListener> SW602TextListenerPtr;
+
+/** a generic variable template: value + flag to know if the variable is set
+
+\note the variable is considered set as soon a new value is set or
+when its content is acceded by a function which returns a not-const
+reference... You can use the function setSet to unset it.
+*/
+template <class T> struct SW602Variable
+{
+ //! constructor
+ SW602Variable() : m_data(), m_set(false) {}
+ //! constructor with a default value
+ explicit SW602Variable(T const &def) : m_data(def), m_set(false) {}
+ //! copy constructor
+ SW602Variable(SW602Variable const &orig) : m_data(orig.m_data), m_set(orig.m_set) {}
+ //! copy operator
+ SW602Variable &operator=(SW602Variable const &orig)
+ {
+ if (this != &orig)
+ {
+ m_data = orig.m_data;
+ m_set = orig.m_set;
+ }
+ return *this;
+ }
+ //! set a value
+ SW602Variable &operator=(T const &val)
+ {
+ m_data = val;
+ m_set = true;
+ return *this;
+ }
+ //! update the current value if orig is set
+ void insert(SW602Variable const &orig)
+ {
+ if (orig.m_set)
+ {
+ m_data = orig.m_data;
+ m_set = orig.m_set;
+ }
+ }
+ //! operator*
+ T const *operator->() const
+ {
+ return &m_data;
+ }
+ /** operator* */
+ T *operator->()
+ {
+ m_set = true;
+ return &m_data;
+ }
+ //! operator*
+ T const &operator*() const
+ {
+ return m_data;
+ }
+ //! operator*
+ T &operator*()
+ {
+ m_set = true;
+ return m_data;
+ }
+ //! return the current value
+ T const &get() const
+ {
+ return m_data;
+ }
+ //! return true if the variable is set
+ bool isSet() const
+ {
+ return m_set;
+ }
+ //! define if the variable is set
+ void setSet(bool newVal)
+ {
+ m_set=newVal;
+ }
+protected:
+ //! the value
+ T m_data;
+ //! a flag to know if the variable is set or not
+ bool m_set;
+};
+
+/* ---------- vec2/box2f ------------- */
+/*! \class SW602Vec2
+ * \brief small class which defines a vector with 2 elements
+ */
+template <class T> class SW602Vec2
+{
+public:
+ //! constructor
+ SW602Vec2(T xx=0,T yy=0) : m_x(xx), m_y(yy) { }
+ //! generic copy constructor
+ template <class U> SW602Vec2(SW602Vec2<U> const &p) : m_x(T(p.x())), m_y(T(p.y())) {}
+
+ //! first element
+ T x() const
+ {
+ return m_x;
+ }
+ //! second element
+ T y() const
+ {
+ return m_y;
+ }
+ //! operator[]
+ T operator[](int c) const
+ {
+ if (c<0 || c>1) throw libsw602::GenericException();
+ return (c==0) ? m_x : m_y;
+ }
+ //! operator[]
+ T &operator[](int c)
+ {
+ if (c<0 || c>1) throw libsw602::GenericException();
+ return (c==0) ? m_x : m_y;
+ }
+
+ //! resets the two elements
+ void set(T xx, T yy)
+ {
+ m_x = xx;
+ m_y = yy;
+ }
+ //! resets the first element
+ void setX(T xx)
+ {
+ m_x = xx;
+ }
+ //! resets the second element
+ void setY(T yy)
+ {
+ m_y = yy;
+ }
+
+ //! increases the actuals values by \a dx and \a dy
+ void add(T dx, T dy)
+ {
+ m_x += dx;
+ m_y += dy;
+ }
+
+ //! operator+=
+ SW602Vec2<T> &operator+=(SW602Vec2<T> const &p)
+ {
+ m_x += p.m_x;
+ m_y += p.m_y;
+ return *this;
+ }
+ //! operator-=
+ SW602Vec2<T> &operator-=(SW602Vec2<T> const &p)
+ {
+ m_x -= p.m_x;
+ m_y -= p.m_y;
+ return *this;
+ }
+ //! generic operator*=
+ template <class U>
+ SW602Vec2<T> &operator*=(U scale)
+ {
+ m_x = T(m_x*scale);
+ m_y = T(m_y*scale);
+ return *this;
+ }
+
+ //! operator+
+ friend SW602Vec2<T> operator+(SW602Vec2<T> const &p1, SW602Vec2<T> const &p2)
+ {
+ SW602Vec2<T> p(p1);
+ return p+=p2;
+ }
+ //! operator-
+ friend SW602Vec2<T> operator-(SW602Vec2<T> const &p1, SW602Vec2<T> const &p2)
+ {
+ SW602Vec2<T> p(p1);
+ return p-=p2;
+ }
+ //! generic operator*
+ template <class U>
+ friend SW602Vec2<T> operator*(U scale, SW602Vec2<T> const &p1)
+ {
+ SW602Vec2<T> p(p1);
+ return p *= scale;
+ }
+
+ //! comparison==
+ bool operator==(SW602Vec2<T> const &p) const
+ {
+ return cmpY(p) == 0;
+ }
+ //! comparison!=
+ bool operator!=(SW602Vec2<T> const &p) const
+ {
+ return cmpY(p) != 0;
+ }
+ //! comparison<: sort by y
+ bool operator<(SW602Vec2<T> const &p) const
+ {
+ return cmpY(p) < 0;
+ }
+ //! a comparison function: which first compares x then y
+ int cmp(SW602Vec2<T> const &p) const
+ {
+ if (m_x < p.m_x) return -1;
+ if (m_x > p.m_x) return 1;
+ if (m_y < p.m_y) return -1;
+ if (m_y > p.m_y) return 1;
+ return 0;
+ }
+ //! a comparison function: which first compares y then x
+ int cmpY(SW602Vec2<T> const &p) const
+ {
+ if (m_y < p.m_y) return -1;
+ if (m_y > p.m_y) return 1;
+ if (m_x < p.m_x) return -1;
+ if (m_x > p.m_x) return 1;
+ return 0;
+ }
+
+ //! operator<<: prints data in form "XxY"
+ friend std::ostream &operator<< (std::ostream &o, SW602Vec2<T> const &f)
+ {
+ o << f.m_x << "x" << f.m_y;
+ return o;
+ }
+
+ /*! \struct PosSizeLtX
+ * \brief internal struct used to create sorted map, sorted by X
+ */
+ struct PosSizeLtX
+ {
+ //! comparaison function
+ bool operator()(SW602Vec2<T> const &s1, SW602Vec2<T> const &s2) const
+ {
+ return s1.cmp(s2) < 0;
+ }
+ };
+ /*! \typedef MapX
+ * \brief map of SW602Vec2
+ */
+ typedef std::map<SW602Vec2<T>, T,struct PosSizeLtX> MapX;
+
+ /*! \struct PosSizeLtY
+ * \brief internal struct used to create sorted map, sorted by Y
+ */
+ struct PosSizeLtY
+ {
+ //! comparaison function
+ bool operator()(SW602Vec2<T> const &s1, SW602Vec2<T> const &s2) const
+ {
+ return s1.cmpY(s2) < 0;
+ }
+ };
+ /*! \typedef MapY
+ * \brief map of SW602Vec2
+ */
+ typedef std::map<SW602Vec2<T>, T,struct PosSizeLtY> MapY;
+protected:
+ T m_x/*! \brief first element */, m_y/*! \brief second element */;
+};
+
+/*! \brief SW602Vec2 of bool */
+typedef SW602Vec2<bool> SW602Vec2b;
+/*! \brief SW602Vec2 of int */
+typedef SW602Vec2<int> SW602Vec2i;
+/*! \brief SW602Vec2 of long */
+typedef SW602Vec2<long> SW602Vec2l;
+/*! \brief SW602Vec2 of float */
+typedef SW602Vec2<float> SW602Vec2f;
+
+/*! \class SW602Vec3
+ * \brief small class which defines a vector with 3 elements
+ */
+template <class T> class SW602Vec3
+{
+public:
+ //! constructor
+ SW602Vec3(T xx=0,T yy=0,T zz=0)
+ {
+ m_val[0] = xx;
+ m_val[1] = yy;
+ m_val[2] = zz;
+ }
+ //! generic copy constructor
+ template <class U> SW602Vec3(SW602Vec3<U> const &p)
+ {
+ for (int c = 0; c < 3; c++) m_val[c] = T(p[c]);
+ }
+
+ //! first element
+ T x() const
+ {
+ return m_val[0];
+ }
+ //! second element
+ T y() const
+ {
+ return m_val[1];
+ }
+ //! third element
+ T z() const
+ {
+ return m_val[2];
+ }
+ //! operator[]
+ T operator[](int c) const
+ {
+ if (c<0 || c>2) throw libsw602::GenericException();
+ return m_val[c];
+ }
+ //! operator[]
+ T &operator[](int c)
+ {
+ if (c<0 || c>2) throw libsw602::GenericException();
+ return m_val[c];
+ }
+
+ //! resets the three elements
+ void set(T xx, T yy, T zz)
+ {
+ m_val[0] = xx;
+ m_val[1] = yy;
+ m_val[2] = zz;
+ }
+ //! resets the first element
+ void setX(T xx)
+ {
+ m_val[0] = xx;
+ }
+ //! resets the second element
+ void setY(T yy)
+ {
+ m_val[1] = yy;
+ }
+ //! resets the third element
+ void setZ(T zz)
+ {
+ m_val[2] = zz;
+ }
+
+ //! increases the actuals values by \a dx, \a dy, \a dz
+ void add(T dx, T dy, T dz)
+ {
+ m_val[0] += dx;
+ m_val[1] += dy;
+ m_val[2] += dz;
+ }
+
+ //! operator+=
+ SW602Vec3<T> &operator+=(SW602Vec3<T> const &p)
+ {
+ for (int c = 0; c < 3; c++) m_val[c] = T(m_val[c]+p.m_val[c]);
+ return *this;
+ }
+ //! operator-=
+ SW602Vec3<T> &operator-=(SW602Vec3<T> const &p)
+ {
+ for (int c = 0; c < 3; c++) m_val[c] = T(m_val[c]-p.m_val[c]);
+ return *this;
+ }
+ //! generic operator*=
+ template <class U>
+ SW602Vec3<T> &operator*=(U scale)
+ {
+ for (int c = 0; c < 3; c++) m_val[c] = T(m_val[c]*scale);
+ return *this;
+ }
+
+ //! operator+
+ friend SW602Vec3<T> operator+(SW602Vec3<T> const &p1, SW602Vec3<T> const &p2)
+ {
+ SW602Vec3<T> p(p1);
+ return p+=p2;
+ }
+ //! operator-
+ friend SW602Vec3<T> operator-(SW602Vec3<T> const &p1, SW602Vec3<T> const &p2)
+ {
+ SW602Vec3<T> p(p1);
+ return p-=p2;
+ }
+ //! generic operator*
+ template <class U>
+ friend SW602Vec3<T> operator*(U scale, SW602Vec3<T> const &p1)
+ {
+ SW602Vec3<T> p(p1);
+ return p *= scale;
+ }
+
+ //! comparison==
+ bool operator==(SW602Vec3<T> const &p) const
+ {
+ return cmp(p) == 0;
+ }
+ //! comparison!=
+ bool operator!=(SW602Vec3<T> const &p) const
+ {
+ return cmp(p) != 0;
+ }
+ //! comparison<: which first compares x values, then y values then z values.
+ bool operator<(SW602Vec3<T> const &p) const
+ {
+ return cmp(p) < 0;
+ }
+ //! a comparison function: which first compares x values, then y values then z values.
+ int cmp(SW602Vec3<T> const &p) const
+ {
+ for (int c = 0; c < 3; c++)
+ {
+ T diff = m_val[c]-p.m_val[c];
+ if (diff) return (diff < 0) ? -1 : 1;
+ }
+ return 0;
+ }
+
+ //! operator<<: prints data in form "XxYxZ"
+ friend std::ostream &operator<< (std::ostream &o, SW602Vec3<T> const &f)
+ {
+ o << f.m_val[0] << "x" << f.m_val[1] << "x" << f.m_val[2];
+ return o;
+ }
+
+ /*! \struct PosSizeLt
+ * \brief internal struct used to create sorted map, sorted by X, Y, Z
+ */
+ struct PosSizeLt
+ {
+ //! comparaison function
+ bool operator()(SW602Vec3<T> const &s1, SW602Vec3<T> const &s2) const
+ {
+ return s1.cmp(s2) < 0;
+ }
+ };
+ /*! \typedef Map
+ * \brief map of SW602Vec3
+ */
+ typedef std::map<SW602Vec3<T>, T,struct PosSizeLt> Map;
+
+protected:
+ //! the values
+ T m_val[3];
+};
+
+/*! \brief SW602Vec3 of unsigned char */
+typedef SW602Vec3<unsigned char> SW602Vec3uc;
+/*! \brief SW602Vec3 of int */
+typedef SW602Vec3<int> SW602Vec3i;
+/*! \brief SW602Vec3 of float */
+typedef SW602Vec3<float> SW602Vec3f;
+
+/*! \class SW602Box2
+ * \brief small class which defines a 2D Box
+ */
+template <class T> class SW602Box2
+{
+public:
+ //! constructor
+ SW602Box2(SW602Vec2<T> minPt=SW602Vec2<T>(), SW602Vec2<T> maxPt=SW602Vec2<T>())
+ {
+ m_pt[0] = minPt;
+ m_pt[1] = maxPt;
+ }
+ //! generic constructor
+ template <class U> SW602Box2(SW602Box2<U> const &p)
+ {
+ for (int c=0; c < 2; c++) m_pt[c] = p[c];
+ }
+
+ //! the minimum 2D point (in x and in y)
+ SW602Vec2<T> const &min() const
+ {
+ return m_pt[0];
+ }
+ //! the maximum 2D point (in x and in y)
+ SW602Vec2<T> const &max() const
+ {
+ return m_pt[1];
+ }
+ //! the minimum 2D point (in x and in y)
+ SW602Vec2<T> &min()
+ {
+ return m_pt[0];
+ }
+ //! the maximum 2D point (in x and in y)
+ SW602Vec2<T> &max()
+ {
+ return m_pt[1];
+ }
+ /*! \brief the two extremum points which defined the box
+ * \param c 0 means the minimum and 1 the maximum
+ */
+ SW602Vec2<T> const &operator[](int c) const
+ {
+ if (c<0 || c>1) throw libsw602::GenericException();
+ return m_pt[c];
+ }
+ //! the box size
+ SW602Vec2<T> size() const
+ {
+ return m_pt[1]-m_pt[0];
+ }
+ //! the box center
+ SW602Vec2<T> center() const
+ {
+ return 0.5*(m_pt[0]+m_pt[1]);
+ }
+
+ //! resets the data to minimum \a x and maximum \a y
+ void set(SW602Vec2<T> const &x, SW602Vec2<T> const &y)
+ {
+ m_pt[0] = x;
+ m_pt[1] = y;
+ }
+ //! resets the minimum point
+ void setMin(SW602Vec2<T> const &x)
+ {
+ m_pt[0] = x;
+ }
+ //! resets the maximum point
+ void setMax(SW602Vec2<T> const &y)
+ {
+ m_pt[1] = y;
+ }
+
+ //! resize the box keeping the minimum
+ void resizeFromMin(SW602Vec2<T> const &sz)
+ {
+ m_pt[1] = m_pt[0]+sz;
+ }
+ //! resize the box keeping the maximum
+ void resizeFromMax(SW602Vec2<T> const &sz)
+ {
+ m_pt[0] = m_pt[1]-sz;
+ }
+ //! resize the box keeping the center
+ void resizeFromCenter(SW602Vec2<T> const &sz)
+ {
+ SW602Vec2<T> centerPt = 0.5*(m_pt[0]+m_pt[1]);
+ m_pt[0] = centerPt - 0.5*sz;
+ m_pt[1] = centerPt + (sz - 0.5*sz);
+ }
+
+ //! scales all points of the box by \a factor
+ template <class U> void scale(U factor)
+ {
+ m_pt[0] *= factor;
+ m_pt[1] *= factor;
+ }
+
+ //! extends the bdbox by (\a val, \a val) keeping the center
+ void extend(T val)
+ {
+ m_pt[0] -= SW602Vec2<T>(val/2,val/2);
+ m_pt[1] += SW602Vec2<T>(val-(val/2),val-(val/2));
+ }
+
+ //! returns the union between this and box
+ SW602Box2<T> getUnion(SW602Box2<T> const &box) const
+ {
+ SW602Box2<T> res;
+ res.m_pt[0]=SW602Vec2<T>(m_pt[0][0]<box.m_pt[0][0]?m_pt[0][0] : box.m_pt[0][0],
+ m_pt[0][1]<box.m_pt[0][1]?m_pt[0][1] : box.m_pt[0][1]);
+ res.m_pt[1]=SW602Vec2<T>(m_pt[1][0]>box.m_pt[1][0]?m_pt[1][0] : box.m_pt[1][0],
+ m_pt[1][1]>box.m_pt[1][1]?m_pt[1][1] : box.m_pt[1][1]);
+ return res;
+ }
+ //! returns the intersection between this and box
+ SW602Box2<T> getIntersection(SW602Box2<T> const &box) const
+ {
+ SW602Box2<T> res;
+ res.m_pt[0]=SW602Vec2<T>(m_pt[0][0]>box.m_pt[0][0]?m_pt[0][0] : box.m_pt[0][0],
+ m_pt[0][1]>box.m_pt[0][1]?m_pt[0][1] : box.m_pt[0][1]);
+ res.m_pt[1]=SW602Vec2<T>(m_pt[1][0]<box.m_pt[1][0]?m_pt[1][0] : box.m_pt[1][0],
+ m_pt[1][1]<box.m_pt[1][1]?m_pt[1][1] : box.m_pt[1][1]);
+ return res;
+ }
+ //! comparison operator==
+ bool operator==(SW602Box2<T> const &p) const
+ {
+ return cmp(p) == 0;
+ }
+ //! comparison operator!=
+ bool operator!=(SW602Box2<T> const &p) const
+ {
+ return cmp(p) != 0;
+ }
+ //! comparison operator< : fist sorts min by Y,X values then max extremity
+ bool operator<(SW602Box2<T> const &p) const
+ {
+ return cmp(p) < 0;
+ }
+
+ //! comparison function : fist sorts min by Y,X values then max extremity
+ int cmp(SW602Box2<T> const &p) const
+ {
+ int diff = m_pt[0].cmpY(p.m_pt[0]);
+ if (diff) return diff;
+ diff = m_pt[1].cmpY(p.m_pt[1]);
+ if (diff) return diff;
+ return 0;
+ }
+
+ //! print data in form X0xY0<->X1xY1
+ friend std::ostream &operator<< (std::ostream &o, SW602Box2<T> const &f)
+ {
+ o << "(" << f.m_pt[0] << "<->" << f.m_pt[1] << ")";
+ return o;
+ }
+
+ /*! \struct PosSizeLt
+ * \brief internal struct used to create sorted map, sorted first min then max
+ */
+ struct PosSizeLt
+ {
+ //! comparaison function
+ bool operator()(SW602Box2<T> const &s1, SW602Box2<T> const &s2) const
+ {
+ return s1.cmp(s2) < 0;
+ }
+ };
+ /*! \typedef Map
+ * \brief map of SW602Box2
+ */
+ typedef std::map<SW602Box2<T>, T,struct PosSizeLt> Map;
+
+protected:
+ //! the two extremities
+ SW602Vec2<T> m_pt[2];
+};
+
+/*! \brief SW602Box2 of int */
+typedef SW602Box2<int> SW602Box2i;
+/*! \brief SW602Box2 of float */
+typedef SW602Box2<float> SW602Box2f;
+/*! \brief SW602Box2 of long */
+typedef SW602Box2<long> SW602Box2l;
+
+/*! \class SW602Box3
+ * \brief small class which defines a 3D Box
+ */
+template <class T> class SW602Box3
+{
+public:
+ //! constructor
+ SW602Box3(SW602Vec3<T> minPt=SW602Vec3<T>(), SW602Vec3<T> maxPt=SW602Vec3<T>())
+ {
+ m_pt[0] = minPt;
+ m_pt[1] = maxPt;
+ }
+ //! generic constructor
+ template <class U> SW602Box3(SW602Box3<U> const &p)
+ {
+ for (int c=0; c < 2; c++) m_pt[c] = p[c];
+ }
+
+ //! the minimum 3D point (in x and in y)
+ SW602Vec3<T> const &min() const
+ {
+ return m_pt[0];
+ }
+ //! the maximum 3D point (in x and in y)
+ SW602Vec3<T> const &max() const
+ {
+ return m_pt[1];
+ }
+ //! the minimum 3D point (in x and in y)
+ SW602Vec3<T> &min()
+ {
+ return m_pt[0];
+ }
+ //! the maximum 3D point (in x and in y)
+ SW602Vec3<T> &max()
+ {
+ return m_pt[1];
+ }
+ /*! \brief the two extremum points which defined the box
+ * \param c 0 means the minimum and 1 the maximum
+ */
+ SW602Vec3<T> const &operator[](int c) const
+ {
+ if (c<0 || c>1) throw libsw602::GenericException();
+ return m_pt[c];
+ }
+ //! the box size
+ SW602Vec3<T> size() const
+ {
+ return m_pt[1]-m_pt[0];
+ }
+ //! the box center
+ SW602Vec3<T> center() const
+ {
+ return 0.5*(m_pt[0]+m_pt[1]);
+ }
+
+ //! resets the data to minimum \a x and maximum \a y
+ void set(SW602Vec3<T> const &x, SW602Vec3<T> const &y)
+ {
+ m_pt[0] = x;
+ m_pt[1] = y;
+ }
+ //! resets the minimum point
+ void setMin(SW602Vec3<T> const &x)
+ {
+ m_pt[0] = x;
+ }
+ //! resets the maximum point
+ void setMax(SW602Vec3<T> const &y)
+ {
+ m_pt[1] = y;
+ }
+
+ //! resize the box keeping the minimum
+ void resizeFromMin(SW602Vec3<T> const &sz)
+ {
+ m_pt[1] = m_pt[0]+sz;
+ }
+ //! resize the box keeping the maximum
+ void resizeFromMax(SW602Vec3<T> const &sz)
+ {
+ m_pt[0] = m_pt[1]-sz;
+ }
+ //! resize the box keeping the center
+ void resizeFromCenter(SW602Vec3<T> const &sz)
+ {
+ SW602Vec3<T> centerPt = 0.5*(m_pt[0]+m_pt[1]);
+ m_pt[0] = centerPt - 0.5*sz;
+ m_pt[1] = centerPt + (sz - 0.5*sz);
+ }
+
+ //! scales all points of the box by \a factor
+ template <class U> void scale(U factor)
+ {
+ m_pt[0] *= factor;
+ m_pt[1] *= factor;
+ }
+
+ //! extends the bdbox by (\a val, \a val) keeping the center
+ void extend(T val)
+ {
+ m_pt[0] -= SW602Vec3<T>(val/2,val/2,val/2);
+ m_pt[1] += SW602Vec3<T>(val-(val/2),val-(val/2),val-(val/2));
+ }
+
+ //! comparison operator==
+ bool operator==(SW602Box3<T> const &p) const
+ {
+ return cmp(p) == 0;
+ }
+ //! comparison operator!=
+ bool operator!=(SW602Box3<T> const &p) const
+ {
+ return cmp(p) != 0;
+ }
+ //! comparison operator< : fist sorts min by Y,X values then max extremity
+ bool operator<(SW602Box3<T> const &p) const
+ {
+ return cmp(p) < 0;
+ }
+
+ //! comparison function : fist sorts min by Y,X values then max extremity
+ int cmp(SW602Box3<T> const &p) const
+ {
+ int diff = m_pt[0].cmp(p.m_pt[0]);
+ if (diff) return diff;
+ diff = m_pt[1].cmp(p.m_pt[1]);
+ if (diff) return diff;
+ return 0;
+ }
+
+ //! print data in form X0xY0xZ0<->X1xY1xZ1
+ friend std::ostream &operator<< (std::ostream &o, SW602Box3<T> const &f)
+ {
+ o << "(" << f.m_pt[0] << "<->" << f.m_pt[1] << ")";
+ return o;
+ }
+
+ /*! \struct PosSizeLt
+ * \brief internal struct used to create sorted map, sorted first min then max
+ */
+ struct PosSizeLt
+ {
+ //! comparaison function
+ bool operator()(SW602Box3<T> const &s1, SW602Box3<T> const &s2) const
+ {
+ return s1.cmp(s2) < 0;
+ }
+ };
+ /*! \typedef Map
+ * \brief map of SW602Box3
+ */
+ typedef std::map<SW602Box3<T>, T,struct PosSizeLt> Map;
+
+protected:
+ //! the two extremities
+ SW602Vec3<T> m_pt[2];
+};
+
+/*! \brief SW602Box3 of int */
+typedef SW602Box3<int> SW602Box3i;
+/*! \brief SW602Box3 of float */
+typedef SW602Box3<float> SW602Box3f;
+/*! \brief SW602Box3 of long */
+typedef SW602Box3<long> SW602Box3l;
+
+}
+
+// some geometrical function
+namespace libsw602
+{
+//! rotate a point around center, angle is given in degree
+SW602Vec2f rotatePointAroundCenter(SW602Vec2f const &point, SW602Vec2f const &center, float angle);
+//! rotate a bdox and returns the final bdbox, angle is given in degree
+SW602Box2f rotateBoxFromCenter(SW602Box2f const &box, float angle);
+}
+
+#endif /* INCLUDED_SW602_TYPES_H */
+
+/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/libsw602_utils.h b/src/lib/libsw602_utils.h
index ea1e83b..68d7f97 100644
--- a/src/lib/libsw602_utils.h
+++ b/src/lib/libsw602_utils.h
@@ -97,6 +97,22 @@ class GenericException
{
};
+class VersionException
+{
+};
+
+class FileException
+{
+};
+
+class ParseException
+{
+};
+
+class WrongPasswordException
+{
+};
+
} // namespace libsw602
#endif // INCLUDED_LIBSW602_UTILS_H