diff options
author | David Tardon <dtardon@redhat.com> | 2016-02-24 07:53:13 +0100 |
---|---|---|
committer | David Tardon <dtardon@redhat.com> | 2016-02-24 14:21:50 +0100 |
commit | 458e616d7c67af5377bbdeddd710b7124268eb67 (patch) | |
tree | 50a1f48eb25e46e81484d4fe8fe24a0c4f9419e4 | |
parent | 2cf59007fd920d8b477a2e19d43ffb5a9262a788 (diff) |
copy parser toolkit from libmwaw
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(> 0), condensed(< 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 ¶) +{ + 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 ¤tPage = *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 ¶graph); + //! 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 ¬e, 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 §ion); + //! 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 ¢er) 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 ¢er) 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 ¶graph)=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 §ion)=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 ¬e, 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 ¶) 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 ¶) +{ + 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 ¶); + //! 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 ¶) +{ + 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 ¤tPage = *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 ¬e, 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 ¶graph); + //! 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 ¬e, 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 §ion); + //! 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 ¶) +{ + 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 ¤tPage = *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 §ion) +{ + 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 ¬e, 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 ¶graph); + //! 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 ¬e, 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 §ion); + //! 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 ¢er, 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 ¢er, 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 |