/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
* This file is part of the libabw 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 <limits>
#include <vector>
#include <boost/algorithm/string.hpp>
#include <librevenge/librevenge.h>
#include "ABWStylesCollector.h"
#include "libabw_internal.h"
#define ABW_EPSILON 1.0E-06
namespace libabw
{
namespace
{
enum ABWListType
{
NUMBERED_LIST = 0,
LOWERCASE_LIST = 1,
UPPERCASE_LIST = 2,
LOWERROMAN_LIST = 3,
UPPERROMAN_LIST = 4,
BULLETED_LIST = 5,
DASHED_LIST = 6,
SQUARE_LIST = 7,
TRIANGLE_LIST = 8,
DIAMOND_LIST = 9,
STAR_LIST = 10,
IMPLIES_LIST = 11,
TICK_LIST = 12,
BOX_LIST = 13,
HAND_LIST = 14,
HEART_LIST = 15,
ARROWHEAD_LIST = 16,
LAST_BULLETED_LIST = 17,
OTHER_NUMBERED_LISTS = 0x7f,
ARABICNUMBERED_LIST = 0x80,
HEBREW_LIST = 0x81,
NOT_A_LIST = 0xff
};
static int abw_unichar_to_utf8(unsigned c, char *outbuf)
{
unsigned char len = 1;
unsigned char first = 0;
if (c < 0x80)
{
first = 0;
len = 1;
}
else if (c < 0x800)
{
first = 0xc0;
len = 2;
}
else if (c < 0x10000)
{
first = 0xe0;
len = 3;
}
else if (c < 0x200000)
{
first = 0xf0;
len = 4;
}
else if (c < 0x4000000)
{
first = 0xf8;
len = 5;
}
else
{
first = 0xfc;
len = 6;
}
if (outbuf)
{
for (auto i = (unsigned char)(len - 1); i > 0; --i)
{
outbuf[i] = (char)((c & 0x3f) | 0x80);
c >>= 6;
}
outbuf[0] = (char)(c | first);
}
return len;
}
static void appendUCS4(librevenge::RVNGString &str, unsigned ucs4)
{
int charLength = abw_unichar_to_utf8(ucs4, nullptr);
std::vector<char> utf8(charLength+1);
utf8[charLength] = '\0';
abw_unichar_to_utf8(ucs4, utf8.data());
str.append(utf8.data());
}
} // anonymous namespace
} // namespace libabw
libabw::ABWStylesTableState::ABWStylesTableState() :
m_currentCellProperties(),
m_currentTableWidth(0),
m_currentTableRow(-1),
m_currentTableId(-1) {}
libabw::ABWStylesTableState::ABWStylesTableState(const ABWStylesTableState &ts) :
m_currentCellProperties(ts.m_currentCellProperties),
m_currentTableWidth(ts.m_currentTableWidth),
m_currentTableRow(ts.m_currentTableRow),
m_currentTableId(ts.m_currentTableId) {}
libabw::ABWStylesTableState::~ABWStylesTableState() {}
libabw::ABWStylesParsingState::ABWStylesParsingState() :
m_tableStates() {}
libabw::ABWStylesParsingState::ABWStylesParsingState(const ABWStylesParsingState &ps) :
m_tableStates(ps.m_tableStates) {}
libabw::ABWStylesParsingState::~ABWStylesParsingState() {}
libabw::ABWStylesCollector::ABWStylesCollector(std::map<int, int> &tableSizes,
std::map<std::string, ABWData> &data,
std::map<int, std::shared_ptr<ABWListElement>> &listElements) :
m_ps(new ABWStylesParsingState),
m_tableSizes(tableSizes),
m_data(data),
m_tableCounter(0),
m_listElements(listElements) {}
libabw::ABWStylesCollector::~ABWStylesCollector()
{
}
void libabw::ABWStylesCollector::openTable(const char *)
{
m_ps->m_tableStates.push(ABWStylesTableState());
m_ps->m_tableStates.top().m_currentTableId = m_tableCounter++;
m_ps->m_tableStates.top().m_currentTableRow = -1;
m_ps->m_tableStates.top().m_currentTableWidth = 0;
}
void libabw::ABWStylesCollector::closeTable()
{
if (!m_ps->m_tableStates.empty())
{
auto curWidth = m_ps->m_tableStates.top().m_currentTableWidth;
m_tableSizes[m_ps->m_tableStates.top().m_currentTableId] = curWidth > 0 ? curWidth : 0;
m_ps->m_tableStates.pop();
}
}
void libabw::ABWStylesCollector::openCell(const char *props)
{
if (!m_ps->m_tableStates.empty())
{
if (props)
parsePropString(props, m_ps->m_tableStates.top().m_currentCellProperties);
int currentRow(0);
if (!findInt(_findCellProperty("top-attach"), currentRow))
{
currentRow = m_ps->m_tableStates.top().m_currentTableRow;
if (currentRow < std::numeric_limits<int>::max())
++currentRow;
}
if (m_ps->m_tableStates.top().m_currentTableRow < currentRow)
m_ps->m_tableStates.top().m_currentTableRow = currentRow;
if (0 == m_ps->m_tableStates.top().m_currentTableRow)
{
int leftAttach(0);
int rightAttach(0);
if (findInt(_findCellProperty("left-attach"), leftAttach)
&& findInt(_findCellProperty("right-attach"), rightAttach)
&& leftAttach >= 0
&& rightAttach > leftAttach
&& rightAttach - leftAttach < std::numeric_limits<int>::max() - m_ps->m_tableStates.top().m_currentTableWidth
)
m_ps->m_tableStates.top().m_currentTableWidth += rightAttach - leftAttach;
else
m_ps->m_tableStates.top().m_currentTableWidth++;
}
}
}
void libabw::ABWStylesCollector::closeCell()
{
if (!m_ps->m_tableStates.empty())
m_ps->m_tableStates.top().m_currentCellProperties.clear();
}
std::string libabw::ABWStylesCollector::_findCellProperty(const char *name)
{
ABWPropertyMap::const_iterator iter = m_ps->m_tableStates.top().m_currentCellProperties.find(name);
if (iter != m_ps->m_tableStates.top().m_currentCellProperties.end())
return iter->second;
return std::string();
}
void libabw::ABWStylesCollector::collectData(const char *name, const char *mimeType, const librevenge::RVNGBinaryData &data)
{
if (!name)
return;
m_data[name] = ABWData(mimeType ? mimeType : "", data);
}
void libabw::ABWStylesCollector::_processList(int id, const char *listDelim, int parentid, int startValue, int type)
{
using namespace boost;
using namespace boost::algorithm;
if ((type >= BULLETED_LIST && type < LAST_BULLETED_LIST) || type == NOT_A_LIST)
{
auto tmpElement = std::make_shared<ABWUnorderedListElement>();
switch (type)
{
case BULLETED_LIST:
appendUCS4(tmpElement->m_bulletChar, 0x2022);
break;
case DASHED_LIST:
appendUCS4(tmpElement->m_bulletChar, 0x002D);
break;
case SQUARE_LIST:
appendUCS4(tmpElement->m_bulletChar, 0x25A0);
break;
case TRIANGLE_LIST:
appendUCS4(tmpElement->m_bulletChar, 0x25B2);
break;
case DIAMOND_LIST:
appendUCS4(tmpElement->m_bulletChar, 0x2666);
break;
case STAR_LIST:
appendUCS4(tmpElement->m_bulletChar, 0x2733);
break;
case IMPLIES_LIST:
appendUCS4(tmpElement->m_bulletChar, 0x21D2);
break;
case TICK_LIST:
appendUCS4(tmpElement->m_bulletChar, 0x2713);
break;
case BOX_LIST:
appendUCS4(tmpElement->m_bulletChar, 0x2752);
break;
case HAND_LIST:
appendUCS4(tmpElement->m_bulletChar, 0x261E);
break;
case HEART_LIST:
appendUCS4(tmpElement->m_bulletChar, 0x2665);
break;
case ARROWHEAD_LIST:
appendUCS4(tmpElement->m_bulletChar, 0x27A3);
break;
default:
tmpElement->m_bulletChar = ""; // for the while
break;
}
m_listElements[id] = tmpElement;
}
else
{
auto tmpElement = std::make_shared<ABWOrderedListElement>();
switch (type)
{
case NUMBERED_LIST:
tmpElement->m_numFormat = "1";
break;
case LOWERCASE_LIST:
tmpElement->m_numFormat = "a";
break;
case UPPERCASE_LIST:
tmpElement->m_numFormat = "A";
break;
case LOWERROMAN_LIST:
tmpElement->m_numFormat = "i";
break;
case UPPERROMAN_LIST:
tmpElement->m_numFormat = "I";
break;
default:
tmpElement->m_numFormat = "1";
break;
}
tmpElement->m_startValue = startValue;
// get prefix and suffix by splitting the listDelim
if (listDelim)
{
std::string delim(listDelim);
std::vector<librevenge::RVNGString> strVec;
for (split_iterator<std::string::iterator> It =
make_split_iterator(delim, first_finder("%L", is_iequal()));
It != split_iterator<std::string::iterator>(); ++It)
{
strVec.push_back(copy_range<std::string>(*It).c_str());
}
if (2 <= strVec.size())
{
tmpElement->m_numPrefix = strVec[0];
tmpElement->m_numSuffix = strVec[1];
}
}
m_listElements[id] = tmpElement;
}
if (parentid)
m_listElements[id]->m_parentId = parentid;
}
void libabw::ABWStylesCollector::collectList(const char *id, const char *, const char *listDelim,
const char *parentid, const char *startValue, const char *type)
{
int intId(0);
if (!id || !findInt(id, intId) || intId < 0)
intId = 0;
if (!intId)
return;
if (m_listElements[intId])
m_listElements[intId].reset();
int intType(0);
if (!type || !findInt(type, intType) || intType < 0)
intType = 5;
int intParentId(0);
if (!parentid || !findInt(parentid, intParentId) || intParentId < 0)
intParentId = 0;
int intStartValue(0);
if (!startValue || !findInt(startValue, intStartValue) || intStartValue < 0)
intStartValue = 0;
_processList(intId, listDelim, intParentId, intStartValue, intType);
}
void libabw::ABWStylesCollector::collectParagraphProperties(const char *level, const char *listid, const char *parentid, const char * /* style */, const char *props)
{
ABWPropertyMap properties;
if (props)
parsePropString(props, properties);
int intParentId(0);
if (!parentid || !findInt(parentid, intParentId) || intParentId < 0)
intParentId = 0;
int intListId(0);
if (!listid || !findInt(listid, intListId) || intListId < 0)
intListId = 0;
auto iter = m_listElements.find(intListId);
if (iter == m_listElements.end() || !iter->second)
{
ABWPropertyMap::const_iterator i = properties.find("list-style");
int listStyle(NOT_A_LIST);
if (i != properties.end())
{
if (i->second == "Numbered List")
listStyle = NUMBERED_LIST;
else if (i->second == "Lower Case List")
listStyle = LOWERCASE_LIST;
else if (i->second == "Upper Case List")
listStyle = UPPERCASE_LIST;
else if (i->second == "Lower Roman List")
listStyle = LOWERROMAN_LIST;
else if (i->second == "Upper Roman List")
listStyle = UPPERROMAN_LIST;
else if (i->second == "Hebrew List")
listStyle = HEBREW_LIST;
else if (i->second == "Arabic List")
listStyle = ARABICNUMBERED_LIST;
else if (i->second == "Bullet List")
listStyle = BULLETED_LIST;
else if (i->second == "Dashed List")
listStyle = DASHED_LIST;
else if (i->second == "Square List")
listStyle = SQUARE_LIST;
else if (i->second == "Triangle List")
listStyle = TRIANGLE_LIST;
else if (i->second == "Diamond List")
listStyle = DIAMOND_LIST;
else if (i->second == "Star List")
listStyle = STAR_LIST;
else if (i->second == "Implies List")
listStyle = IMPLIES_LIST;
else if (i->second == "Tick List")
listStyle = TICK_LIST;
else if (i->second == "Box List")
listStyle = BOX_LIST;
else if (i->second == "Hand List")
listStyle = HAND_LIST;
else if (i->second == "Heart List")
listStyle = HEART_LIST;
else if (i->second == "Arrowhead List")
listStyle = ARROWHEAD_LIST;
else
listStyle = NOT_A_LIST;
}
i = properties.find("start-value");
std::string startValue;
if (i != properties.end())
startValue = i->second;
int intStartValue(0);
if (startValue.empty() || findInt(startValue, intStartValue) || intStartValue < 0)
intStartValue = 0;
_processList(intListId, "%L", intParentId, intStartValue, listStyle);
iter = m_listElements.find(intListId);
}
if (iter != m_listElements.end() && iter->second)
{
const auto &listElement = iter->second;
if (!level || !findInt(level, listElement->m_listLevel) || listElement->m_listLevel < 0)
listElement->m_listLevel = 0;
ABWPropertyMap::const_iterator i = properties.find("margin-left");
ABWUnit unit(ABW_NONE);
double marginLeft(0.0);
if (i == properties.end() || !findDouble(i->second, marginLeft, unit) || unit != ABW_IN)
marginLeft = 0.0;
i = properties.find("text-indent");
double textIndent(0.0);
if (i == properties.end() || !findDouble(i->second, textIndent, unit) || unit != ABW_IN)
textIndent = 0.0;
listElement->m_minLabelWidth = -textIndent;
listElement->m_spaceBefore = marginLeft + textIndent;
}
}
/* vim:set shiftwidth=2 softtabstop=2 expandtab: */