новые иконки в OpenBoard
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
OpenBoard/src/adaptors/UBCFFSubsetAdaptor.cpp

1943 lines
70 KiB

/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QRegExp>
#include <QSvgGenerator>
#include <QSvgRenderer>
#include "core/UBPersistenceManager.h"
#include "document/UBDocumentProxy.h"
#include "domain/UBItem.h"
#include "domain/UBGraphicsPolygonItem.h"
#include "domain/UBGraphicsStroke.h"
#include "domain/UBGraphicsTextItem.h"
#include "domain/UBGraphicsSvgItem.h"
#include "domain/UBGraphicsPixmapItem.h"
#include "UBCFFSubsetAdaptor.h"
#include "UBMetadataDcSubsetAdaptor.h"
#include "UBThumbnailAdaptor.h"
#include "UBSvgSubsetAdaptor.h"
#include "core/UBApplication.h"
#include "QFile"
#include "QDomDocument"
//enum of xmlparse status
//tag names definition
//use them everiwhere!
static QString tElement = "element";
static QString tGroup = "group";
static QString tEllipse = "ellipse";
static QString tIwb = "iwb";
static QString tMeta = "meta";
static QString tPage = "page";
static QString tPageset = "pageset";
static QString tPolygon = "polygon";
static QString tPolyline = "polyline";
static QString tRect = "rect";
static QString tSvg = "svg";
static QString tText = "text";
static QString tTextarea = "textarea";
static QString tTspan = "tspan";
static QString tBreak = "tbreak";
static QString tImage = "image";
//attribute names definition
static QString aFill = "fill";
static QString aFillopacity = "fill-opacity";
static QString aX = "x";
static QString aY = "y";
static QString aWidth = "width";
static QString aHeight = "height";
static QString aStroke = "stroke";
static QString aStrokewidth = "stroke-width";
static QString aCx = "cx";
static QString aCy = "cy";
static QString aRx = "rx";
static QString aRy = "ry";
static QString aTransform = "transform";
static QString aViewbox = "viewbox";
static QString aFontSize = "font-size";
static QString aFontfamily = "font-family";
static QString aFontstretch = "font-stretch";
static QString aFontstyle = "font-style";
static QString aFontweight = "font-weight";
static QString aTextalign = "text-align";
static QString aPoints = "points";
static QString svgNS = "http://www.w3.org/2000/svg";
static QString iwbNS = "http://www.becta.org.uk/iwb";
static QString aId = "id";
static QString aRef = "ref";
static QString aHref = "href";
//attributes part names
static QString apRotate = "rotate";
static QString apTranslate = "translate";
UBCFFSubsetAdaptor::UBCFFSubsetAdaptor()
{
}
void UBCFFSubsetAdaptor::UBCFFSubsetReader::hashSiblingIwbElements(QDomElement *parent, QDomElement *topGroup) {
QDomElement curExt = parent->firstChildElement(tElement);
while (!curExt.isNull()) {
if (curExt.namespaceURI() != iwbNS)
continue;
QHash<QString, IwbExt>::iterator iSvgElement = iwbExtProperties.find(curExt.attribute(aRef));
if (iSvgElement != iwbExtProperties.end()) {
IwbExt &svgElement = *iSvgElement;
svgElement.extAttr.push_back(curExt);
if (topGroup)
svgElement.group = *topGroup;
}
curExt = curExt.nextSiblingElement(tElement);
}
}
void UBCFFSubsetAdaptor::UBCFFSubsetReader::addExtentionsToHash(QDomElement *parent, QDomElement *topGroup)
{
//add top level elements
if(*parent == mDOMdoc.documentElement()) {
hashSiblingIwbElements(parent);
} else
hashSiblingIwbElements(parent, topGroup);
//add iwb groups if needed
QDomElement curGroup = parent->firstChildElement(tGroup);
while (!curGroup.isNull()) {
if (curGroup.namespaceURI() != iwbNS)
continue;
if(*parent == mDOMdoc.documentElement()) {
topGroup = &curGroup;
}
if (curGroup.hasChildNodes()) {
addExtentionsToHash(&curGroup, topGroup);
}
curGroup = curGroup.nextSiblingElement(tGroup);
}
}
void UBCFFSubsetAdaptor::UBCFFSubsetReader::hashSvg(QDomNode *parent, QString prefix)
{
QDomNode n = parent->firstChild();
while (!n.isNull()) {
QDomElement e = n.toElement();
QString id = e.attribute(aId);
if(!id.isNull()) {
iwbExtProperties.insert(id, IwbExt(e));
qDebug() << prefix + e.prefix() + ":" + e.tagName();
}
if (n.hasChildNodes()) {
hashSvg(&n, QString("| %1").arg(prefix));
}
n = n.nextSibling();
}
}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::hashElements()
{
bool result = true;
QDomElement svgSection = mDOMdoc.elementsByTagNameNS(svgNS, tSvg).at(0).toElement();
if (svgSection.isNull()) {
qDebug("\"svg:svg\" section not found maybe invalid document");
result = false;
}
hashSvg(&svgSection);
QDomElement parElement = mDOMdoc.documentElement();
if (parElement.isNull()) {
qDebug("invalid pass paramentr maybe invalid document");
result = false;
}
// Adding iwb extentions to hash table crossing elements and groups using recursive descent
addExtentionsToHash(&parElement, 0);
// int i = 0;
// foreach (IwbExt cur, iwbExtProperties) {
// QString elem = cur.element.toElement().attribute(aId);
// QString tagName = cur.element.toElement().tagName();
// QString gr = !cur.group.isNull()
// ? i++, " is group\n-------------\n" + QString::number(i)
// + cur.group.toElement().tagName()
// + (cur.group.toElement().hasChildNodes() ? "true" : "false")
// : "";
//// QString attr = !cur.extAttr.isEmpty() ? cur.extAttr.first().toElement().attribute(aRef) : "";
//// if (cur.group) {
//// *(cur.group);
//// }
// qDebug() << "element" + elem + "tag" + tagName + gr;
// if (!gr.isNull()) {
// mDOMdoc.documentElement().removeChild(cur.group);
// }
// }
// QDomNode n = mDOMdoc.documentElement().firstChild();
// while (!n.isNull()) {
// qDebug() << "new dom tags"<< n.toElement().tagName();
// n = n.nextSibling();
// }
return result;
}
bool UBCFFSubsetAdaptor::ConvertCFFFileToUbz(QString &cffSourceFile, UBDocumentProxy* pDocument)
{
//TODO
// fill document proxy metadata
// create persistance manager to save data using proxy
// create UBCFFSubsetReader and make it parse cffSourceFolder
QFile file(cffSourceFile);
if (!file.open(QIODevice::ReadOnly))
{
qWarning() << "Cannot open file " << cffSourceFile << " for reading ...";
return false;
}
UBCFFSubsetReader cffReader(pDocument, &file);
bool result = cffReader.parse();
file.close();
return result;
}
UBCFFSubsetAdaptor::UBCFFSubsetReader::UBCFFSubsetReader(UBDocumentProxy *proxy, QFile *content):
mReader(content), mProxy(proxy), currentState(NONE)
{
int errorLine, errorColumn;
QString errorStr;
if(!mDOMdoc.setContent(content, true, &errorStr, &errorLine, &errorColumn)){
qWarning() << "Error:Parseerroratline" << errorLine << ","
<< "column" << errorColumn << ":" << errorStr;
} else {
qDebug() << "well parsed to DOM";
pwdContent = QFileInfo(content->fileName()).dir().absolutePath();
}
// QFile tfile("/home/ilia/Documents/tmp/2/out.xml");
// tfile.open(QIODevice::ReadWr ite | QIODevice::Text);
// QTextStream out(&tfile);
// out << content;
// tfile.close();
qDebug() << "tmp path is" << pwdContent;
}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::parse()
{
UBMetadataDcSubsetAdaptor::persist(mProxy);
mIndent = "";
if (!getTempFileName())
return false;
if (mDOMdoc.isNull())
return false;
bool result = parseDoc();
if (result)
result = mProxy->pageCount() != 0;
if (QFile::exists(mTempFilePath))
QFile::remove(mTempFilePath);
return result;
}
int UBCFFSubsetAdaptor::UBCFFSubsetReader::PopState()
{
if (stateStack.count() == 0)
currentState = NONE;
else
currentState = stateStack.pop();
return currentState;
}
void UBCFFSubsetAdaptor::UBCFFSubsetReader::PushState(int state)
{
if (currentState != NONE)
stateStack.push(currentState);
currentState = state;
}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::parseSvgRect(const QDomElement &element)
{
qreal x1 = element.attribute(aX).toDouble();
qreal y1 = element.attribute(aY).toDouble();
//rect dimensions
qreal width = element.attribute(aWidth).toDouble();
qreal height = element.attribute(aHeight).toDouble();
QString textFillColor = element.attribute(aFill);
QString textStrokeColor = element.attribute(aStroke);
QString textStrokeWidth = element.attribute(aStrokewidth);
QColor fillColor = !textFillColor.isNull() ? colorFromString(textFillColor) : QColor();
QColor strokeColor = !textStrokeColor.isNull() ? colorFromString(textStrokeColor) : QColor();
int strokeWidth = !textStrokeWidth.isNull() ? textStrokeWidth.toInt() : 0;
//init svg generator with temp file
QSvgGenerator *generator = createSvgGenerator(width + 10, height + 10);
//init painter to paint to svg
QPainter painter;
painter.begin(generator);
//fill rect
if (fillColor.isValid()) {
painter.setBrush(QBrush(fillColor));
painter.fillRect(5, 5, width, height, fillColor);
}
QPen pen;
if (strokeColor.isValid()) {
pen.setColor(strokeColor);
}
if (strokeWidth)
pen.setWidth(strokeWidth);
painter.setPen(pen);
painter.drawRect(5, 5, width, height);
painter.end();
UBGraphicsSvgItem *svgItem = mCurrentScene->addSvg(QUrl::fromLocalFile(generator->fileName()));
QTransform transform;
QString textTransform = element.attribute(aTransform);
bool hastransform = false;
if (!textTransform.isNull()) {
transform = transformFromString(textTransform);
hastransform = true;
}
repositionSvgItem(svgItem, width + 10, height + 10, x1 - 5, y1 - 5, hastransform, transform);
delete generator;
return true;
}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::parseSvgEllipse(const QDomElement &element)
{
//ellipse horisontal and vertical radius
qreal rx = element.attribute(aRx).toDouble();
qreal ry = element.attribute(aRy).toDouble();
QSvgGenerator *generator = createSvgGenerator(rx * 2 + 10, ry * 2 + 10);
//fill and stroke color
QColor fillColor = colorFromString(element.attribute(aFill));
QColor strokeColor = colorFromString(element.attribute(aStroke));
int strokeWidth = element.attribute(aStrokewidth).toInt();
//ellipse center coordinates
qreal cx = element.attribute(aCx).toDouble();
qreal cy = element.attribute(aCy).toDouble();
//init painter to paint to svg
QPainter painter;
painter.begin(generator);
QPen pen(strokeColor);
pen.setWidth(strokeWidth);
painter.setPen(pen);
painter.setBrush(QBrush(fillColor));
painter.drawEllipse(5, 5, rx * 2, ry * 2);
painter.end();
UBGraphicsSvgItem *svgItem = mCurrentScene->addSvg(QUrl::fromLocalFile(generator->fileName()));
QTransform transform;
QString textTransform = element.attribute(aTransform);
bool hastransform = false;
if (!textTransform.isNull()) {
transform = transformFromString(textTransform);
hastransform = true;
}
repositionSvgItem(svgItem, rx * 2 + 10, ry * 2 + 10, cx - rx - 5, cy - ry -5, hastransform, transform);
delete generator;
return true;
}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::parseSvgPolygon(const QDomElement &element)
{
QString svgPoints = element.attribute(aPoints);
QPolygonF polygon;
if (!svgPoints.isNull()) {
QStringList ts = svgPoints.split(QLatin1Char(' '), QString::SkipEmptyParts);
foreach(const QString sPoint, ts) {
QStringList sCoord = sPoint.split(QLatin1Char(','), QString::SkipEmptyParts);
if (sCoord.size() == 2) {
QPointF point;
point.setX(sCoord.at(0).toFloat());
point.setY(sCoord.at(1).toFloat());
polygon << point;
}
else {
qWarning() << "cannot make sense of a 'point' value" << sCoord;
}
}
}
//bounding rect lef top corner coordinates
qreal x1 = polygon.boundingRect().topLeft().x();
qreal y1 = polygon.boundingRect().topLeft().y();
//bounding rect dimensions
qreal width = polygon.boundingRect().width();
qreal height = polygon.boundingRect().height();
QString strokeColorText = element.attribute(aStroke);
QString fillColorText = element.attribute(aFill);
QString strokeWidthText = element.attribute(aStrokewidth);
QColor strokeColor = !strokeColorText.isEmpty() ? colorFromString(strokeColorText) : QColor();
QColor fillColor = !fillColorText.isEmpty() ? colorFromString(fillColorText) : QColor();
int strokeWidth = strokeWidthText.toInt() > 0 ? strokeWidthText.toInt() : 0;
QPen pen;
pen.setColor(strokeColor);
pen.setWidth(strokeWidth);
QBrush brush;
brush.setColor(fillColor);
brush.setStyle(Qt::SolidPattern);
QSvgGenerator *generator = createSvgGenerator(width + pen.width(), height + pen.width());
QPainter painter;
painter.begin(generator); //drawing to svg tmp file
painter.translate(pen.widthF() / 2 - x1, pen.widthF() / 2 - y1);
painter.setBrush(brush);
painter.setPen(pen);
painter.drawPolygon(polygon);
painter.end();
//add resulting svg file to scene
UBGraphicsSvgItem *svgItem = mCurrentScene->addSvg(QUrl::fromLocalFile(generator->fileName()));
QTransform transform;
QString textTransform = element.attribute(aTransform);
bool hastransform = false;
if (!textTransform.isNull()) {
transform = transformFromString(textTransform);
hastransform = true;
}
repositionSvgItem(svgItem, width + 10, height + 10, x1 - 5, y1 - 5, hastransform, transform);
delete generator;
return true;
}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::parseSvgPolyline(const QDomElement &element)
{
QString svgPoints = element.attribute(aPoints);
QPolygonF polygon;
if (!svgPoints.isNull()) {
QStringList ts = svgPoints.split(QLatin1Char(' '),
QString::SkipEmptyParts);
foreach(const QString sPoint, ts) {
QStringList sCoord = sPoint.split(QLatin1Char(','), QString::SkipEmptyParts);
if (sCoord.size() == 2) {
QPointF point;
point.setX(sCoord.at(0).toFloat());
point.setY(sCoord.at(1).toFloat());
polygon << point;
}
else {
qWarning() << "cannot make sense of a 'point' value" << sCoord;
}
}
}
//bounding rect lef top corner coordinates
qreal x1 = polygon.boundingRect().topLeft().x();
qreal y1 = polygon.boundingRect().topLeft().y();
//bounding rect dimensions
qreal width = polygon.boundingRect().width();
qreal height = polygon.boundingRect().height();
QString strokeColorText = element.attribute(aStroke);
QString strokeWidthText = element.attribute(aStrokewidth);
QColor strokeColor = !strokeColorText.isEmpty() ? colorFromString(strokeColorText) : QColor();
int strokeWidth = strokeWidthText.toInt() > 0 ? strokeWidthText.toInt() : 0;
QPen pen;
pen.setColor(strokeColor);
pen.setWidth(strokeWidth);
QSvgGenerator *generator = createSvgGenerator(width + pen.width(), height + pen.width());
QPainter painter;
painter.begin(generator); //drawing to svg tmp file
painter.translate(pen.widthF() / 2 - x1, pen.widthF() / 2 - y1);
painter.setPen(pen);
painter.drawPolyline(polygon);
painter.end();
//add resulting svg file to scene
UBGraphicsSvgItem *svgItem = mCurrentScene->addSvg(QUrl::fromLocalFile(generator->fileName()));
QTransform transform;
QString textTransform = element.attribute(aTransform);
bool hastransform = false;
if (!textTransform.isNull()) {
transform = transformFromString(textTransform);
hastransform = true;
}
repositionSvgItem(svgItem, width + 10, height + 10, x1 - 5, y1 - 5, hastransform, transform);
delete generator;
return true;
}
void UBCFFSubsetAdaptor::UBCFFSubsetReader::parseTextAttributes(const QDomElement &element,
qreal &fontSize, QColor &fontColor, QString &fontFamily,
QString &fontStretch, bool &italic, int &fontWeight,
int &textAlign, QTransform &fontTransform)
{
//consider inch has 72 liens
//since svg font size is given in pixels, divide it by pixels per line
QString fontSz = element.attribute(aFontSize);
if (!fontSz.isNull()) fontSize = fontSz.toDouble() * 72 / QApplication::desktop()->physicalDpiY();
QString fontColorText = element.attribute(aFill);
if (!fontColorText.isNull()) fontColor = colorFromString(fontColorText);
QString fontFamilyText = element.attribute(aFontfamily);
if (!fontFamilyText.isNull()) fontFamily = fontFamilyText;
QString fontStretchText = element.attribute(aFontstretch);
if (!fontStretchText.isNull()) fontStretch = fontStretchText;
if (!element.attribute(aFontstyle).isNull())
italic = (element.attribute(aFontstyle) == "italic");
QString weight = element.attribute(aFontweight);
if (!weight.isNull()) {
if (weight == "normal") fontWeight = QFont::Normal;
else if (weight == "light") fontWeight = QFont::Light;
else if (weight == "demibold") fontWeight = QFont::DemiBold;
else if (weight == "bold") fontWeight = QFont::Bold;
else if (weight == "black") fontWeight = QFont::Black;
}
QString align = element.attribute(aTextalign);
if (!align.isNull()) {
if (align == "middle" || align == "center") textAlign = Qt::AlignHCenter;
else if (align == "start") textAlign = Qt::AlignLeft;
else if (align == "end") textAlign = Qt::AlignRight;
}
if (!element.attribute(aTransform).isNull())
fontTransform = transformFromString(element.attribute(aTransform));
}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::parseSvgText(const QDomElement &element)
{
// qreal x = element.attribute(aX).toDouble();
// qreal y = element.attribute(aY).toDouble();;
// qreal width = 0;
// qreal height = 0;
// QList<QRectF> textRects;
// QList<QFont> textFonts;
// QList<QString> textLines;
// QList<int> textAligns;
// QList<QColor> textColors;
// qWarning() << QString().sprintf("Text coordinates : %f,%f. Text size %f,%f", x, y, width, height);
// qreal fontSize = 12.0;
// QFont textFont;
// QColor fontColor;
// QString fontFamily = "Arial";
// QString fontStretch = "normal";
// bool italic = false;
// int fontWeight = QFont::Normal;
// int textAlign = Qt::AlignLeft;
// QTransform fontTransform;
// parseTextAttributes(element, fontSize, fontColor, fontFamily, fontStretch, italic, fontWeight, textAlign, fontTransform);
// textFont = QFont(fontFamily, fontSize, fontWeight, italic);
// QFontMetricsF metrics = QFontMetricsF(textFont);
// qreal curHeight = metrics.height();
// qreal curY = 0.0;
// qreal curX = 0.0;
// qreal linespacing = QFontMetrics(textFont).leading();
// //remember if text area has transform
// QTransform transform;
//// bool hasTransform = getCurElementTransorm(transform);
// QRectF lastDrawnTextBoundingRect;
// QStack<QFont> fontStack;
// QStack<QColor> colorStack;
// QStack<int> alignStack;
// // first extimate desired text area size
// // to do that, parse text area tags
// while(true)
// {
// mReader.readNext();
// QStringRef elementName = mReader.name();
// if (mReader.isEndDocument())
// break;
// if (mReader.isEndElement())
// {
// if (elementName == tBreak)
// {
// //when tbreak appers, move down by the drawn rect height
// //TODO: line spacing is not calculated yet, probably additional code is required
// curY += lastDrawnTextBoundingRect.height() + linespacing;
// curX = 0.0;
// height += lastDrawnTextBoundingRect.height();
// lastDrawnTextBoundingRect = QRectF(0,0,0,0);
// continue;
// }
// if (elementName == tTspan)
// {
// textFont = fontStack.pop();
// fontColor = colorStack.pop();
// textAlign = alignStack.pop();
// continue;
// }
// }
// if (mReader.isEndElement() && elementName == tText)
// break;
// if (mReader.isStartElement() && elementName == tTspan)
// {
// fontStack.push(textFont);
// colorStack.push(fontColor);
// alignStack.push(textAlign);
// parseTextAttributes(fontSize, fontColor, fontFamily, fontStretch, italic, fontWeight, textAlign, fontTransform);
// textFont = QFont(fontFamily, fontSize, fontWeight, italic);
// metrics = QFontMetricsF(textFont);
// curHeight = metrics.height();
// linespacing = QFontMetricsF(textFont).leading();
// continue;
// }
// if (mReader.isCharacters() || mReader.isCDATA())
// {
// QString text = mReader.text().toString();
// //skip empty text
// if (text.trimmed().length() == 0)
// continue;
// //get bounding rect to obtain desired text height
// lastDrawnTextBoundingRect = metrics.boundingRect(QRectF(), textAlign, text);
// QString log = QString().sprintf(" at rect %f, %f, %f, %f. Bounding rect is %f, %f, %f, %f", 0.0, curY, width, height - curY, lastDrawnTextBoundingRect.x(), lastDrawnTextBoundingRect.y(), lastDrawnTextBoundingRect.width(), lastDrawnTextBoundingRect.height());
// qWarning() << "Text " << text << log;
// textFonts.append(textFont);
// textRects.append(QRectF(curX, curY, lastDrawnTextBoundingRect.width(), lastDrawnTextBoundingRect.height()));
// textLines.append(text);
// textAligns.append(textAlign);
// textColors.append(fontColor);
// curX += lastDrawnTextBoundingRect.width();
// if (width < curX)
// width = curX;
// if (height == 0)
// height = curHeight;
// continue;
// }
// }
// QSvgGenerator *generator = createSvgGenerator(width, height);
// QPainter painter;
// painter.begin(generator);
// if (textRects.count() != 0)
// {
// QListIterator<QRectF> textRectsIter(textRects);
// QListIterator<QFont> textFontsIter(textFonts);
// QListIterator<QString> textLinesIter(textLines);
// QListIterator<int> textAlignsIter(textAligns);
// QListIterator<QColor> textColorsIter(textColors);
// while (textRectsIter.hasNext())
// {
// QRectF rt = textRectsIter.next();
// QFont font = textFontsIter.next();
// QString line = textLinesIter.next();
// int align = textAlignsIter.next();
// QColor color = textColorsIter.next();
// painter.setFont(font);
// painter.setPen(color);
// painter.drawText(rt.x(), rt.y(), rt.width(), rt.height(), align, line);
// }
// }
// painter.end();
// //add resulting svg file to scene
// UBGraphicsSvgItem *svgItem = mCurrentScene->addSvg(QUrl::fromLocalFile(generator->fileName()));
// repositionSvgItem(svgItem, width, height, x, y, hasTransform, transform);
// delete generator;
return true;
}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::parseSvgTextarea(const QDomElement &element)
{
//TODO textarea node
qreal x = element.attribute(aX).toDouble();
qreal y = element.attribute(aY).toDouble();
qreal width = element.attribute(aWidth).toDouble();
qreal height = element.attribute(aHeight).toDouble();
qreal fontSize = 12;
QColor fontColor;
QString fontFamily = "Arial";
QString fontStretch = "normal";
bool italic = false;
int fontWeight = QFont::Normal;
int textAlign = Qt::AlignLeft;
QTransform fontTransform;
parseTextAttributes(fontSize, fontColor, fontFamily, fontStretch, italic, fontWeight, textAlign, fontTransform);
QSvgGenerator *generator = createSvgGenerator(width, height);
QPainter painter;
painter.begin(generator);
painter.setFont(QFont(fontFamily, fontSize, fontWeight, italic));
qreal curY = 0.0;
qreal curX = 0.0;
qreal linespacing = QFontMetricsF(painter.font()).leading();
// remember if text area has transform
// QString transformString;
QTransform transform = fontTransform;
bool hasTransform = !fontTransform.isIdentity();
QRectF lastDrawnTextBoundingRect;
//parse text area tags
QDomElement curTextElement = element.firstChildElement();
while (!curTextElement.isNull()) {
QString tagName = curTextElement.tagName();
if (tagName == tTspan) {
parseTextAttributes(curTextElement, fontSize, fontColor, fontFamily, fontStretch, italic, fontWeight, textAlign, fontTransform);
painter.setFont(QFont(fontFamily, fontSize, fontWeight, italic));
painter.setPen(fontColor);
linespacing = QFontMetricsF(painter.font()).leading();
QDomNode tspanNode = curTextElement.firstChild();
while (!tspanNode.isNull()) {
if (tspanNode.nodeType() == QDomNode::CharacterDataNode
|| tspanNode.nodeType() == QDomNode::CDATASectionNode) {
QDomCharacterData textData = tspanNode.toCharacterData();
QString text = textData.data().trimmed();
//get bounding rect to obtain desired text height
lastDrawnTextBoundingRect = painter.boundingRect(QRectF(curX, curY, width, height - curY), textAlign|Qt::TextWordWrap, text);
painter.drawText(curX, curY, width, lastDrawnTextBoundingRect.height(), textAlign|Qt::TextWordWrap, text);
curX += lastDrawnTextBoundingRect.x() + lastDrawnTextBoundingRect.width();
} else if (tspanNode.nodeType() == QDomNode::ElementNode) {
//when tbreak appers, move down by the drawn rect height
//TODO: line spacing is not calculated yet, additional code is required
curY += lastDrawnTextBoundingRect.height() + linespacing;
curX = 0.0;
lastDrawnTextBoundingRect = QRectF(0,0,0,0);
}
tspanNode = tspanNode.nextSibling();
}
} else if (tagName == tBreak) {
}
curTextElement = curTextElement.nextSiblingElement();
}
painter.end();
//add resulting svg file to scene
UBGraphicsSvgItem *svgItem = mCurrentScene->addSvg(QUrl::fromLocalFile(generator->fileName()));
repositionSvgItem(svgItem, width, height, x, y, hasTransform, transform);
delete generator;
return true;
}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::parseSvgImage(const QDomElement &element)
{
qreal x = element.attribute(aX).toDouble();
qreal y = element.attribute(aY).toDouble();
qreal width = element.attribute(aWidth).toDouble();
qreal height = element.attribute(aHeight).toDouble();
QString itemRefPath = element.attribute(aHref);
QPixmap pix;
if (!itemRefPath.isNull()) {
QString imagePath = pwdContent + "/" + itemRefPath;
if (!QFile::exists(imagePath)) {
qDebug() << "can't load file" << pwdContent + "/" + itemRefPath << "maybe file corrupted";
return false;
} else {
qDebug() << "size of file" << itemRefPath << QFileInfo(itemRefPath).size();
}
pix.load(imagePath);
if (pix.isNull()) {
qDebug() << "can't create pixmap for file" << pwdContent + "/" + itemRefPath << "maybe format does not supported";
}
}
UBGraphicsPixmapItem *pixItem = mCurrentScene->addPixmap(pix);
QTransform transform;
QString textTransform = element.attribute(aTransform);
bool hastransform = false;
if (!textTransform.isNull()) {
transform = transformFromString(textTransform);
hastransform = true;
}
// repositionSvgItem(svgItem, rx * 2 + 10, ry * 2 + 10, cx - rx - 5, cy - ry -5, hastransform, transform);
repositionPixmapItem(pixItem, width, height, x, y, hastransform, transform);
// hashSceneItem(element, pixItem->);
return true;
}
void UBCFFSubsetAdaptor::UBCFFSubsetReader::parseSvgSectionAttr(const QDomElement &svgSection)
{
getViewBoxDimenstions(svgSection.attribute(aViewbox));
mSize = QSize(svgSection.attribute(aWidth).toInt(),
svgSection.attribute(aHeight).toInt());
}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::parseIwbGroup(QDomNode *group)
{
QDomElement curGroupPtr = group->firstChildElement();
while (!curGroupPtr.isNull()) {
if (curGroupPtr.namespaceURI() != iwbNS)
continue;
if (curGroupPtr.hasChildNodes() && curGroupPtr.toElement().tagName() == tGroup) {
parseIwbGroup(&curGroupPtr);
} else if (curGroupPtr.toElement().tagName() == tElement) {
QHash<QString, IwbExt>::iterator iSvgElementExt = iwbExtProperties.find(curGroupPtr.attribute(aRef));
if (iSvgElementExt != iwbExtProperties.end()) {
IwbExt &svgElementExt = *iSvgElementExt;
QDomNode &svgElement = svgElementExt.element;
svgElement.parentNode().removeChild(svgElement);
}
}
curGroupPtr = curGroupPtr.nextSiblingElement(tElement);
}
return true;
}
//void UBCFFSubsetAdaptor::UBCFFSubsetReader::hashSceneItem(QDomNode &element, UBGraphicsItemDelegate *item)
//{
//// adding element pointer to hash to refer if needed
// QString key = element.attribute(aId);
// if (!key.isNull())
// persistedItems.insert(key, item);
//}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::parseSvgElement(const QDomElement &parent)
{
QString tagName = parent.tagName();
if (parent.namespaceURI() != svgNS) {
qDebug() << "Incorrect namespace, error at content file, line number" << parent.lineNumber();
return false;
}
if (tagName == tRect && !parseSvgRect(parent)) return false;
else if (tagName == tEllipse && !parseSvgEllipse(parent)) return false;
else if (tagName == tPolygon && !parseSvgPolygon(parent)) return false;
else if (tagName == tPolyline && !parseSvgPolyline(parent)) return false;
else if (tagName == tText && !parseSvgText(parent)) return false;
else if (tagName == tTextarea && !parseSvgTextarea(parent)) return false;
else if (tagName == tImage && !parseSvgImage(parent)) return false;
return true;
}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::parseSvgPage(const QDomElement &parent)
{
createNewScene();
QDomElement currentSvgElement = parent.firstChildElement();
while (!currentSvgElement.isNull()) {
if (!parseSvgElement(currentSvgElement))
return false;
currentSvgElement = currentSvgElement.nextSiblingElement();
}
persistCurrentScene();
return true;
}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::parseSvgPageset(const QDomElement &parent)
{
QDomElement currentPage = parent.firstChildElement(tPage);
while (!currentPage.isNull()) {
if (!parseSvgPage(currentPage))
return false;
currentPage = currentPage.nextSiblingElement(tPage);
}
return true;
}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::parseDoc()
{
if (!hashElements()) return false; //hashing all elements having id attribute
QDomElement svgSection = mDOMdoc.elementsByTagNameNS(svgNS, tSvg).at(0).toElement();
parseSvgSectionAttr(svgSection);
QDomElement currentSvg = svgSection.firstChildElement();
if (currentSvg.tagName() != tPageset) {
parseSvgPage(svgSection);
} else if (currentSvg.tagName() == tPageset){
parseSvgPageset(currentSvg);
}
// while (!mReader.atEnd())
// {
// mReader.readNext();
// if (mReader.isStartElement())
// {
// if (!parseCurrentElementStart())
// return false;
// }
// else
// if (mReader.isCharacters())
// {
// if (!parseCurrentElementCharacters())
// return false;
// }
// else
// if (mReader.isEndElement())
// {
// if (!parseCurrentElementEnd())
// return false;
// }
// }
// if (!mReader.error() == QXmlStreamReader::NoError)
// UBApplication::showMessage(mReader.errorString());
return true;
}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::parseCurrentElementStart()
{
QStringRef elName = mReader.name();
// QString log = QString("%1<%2>").arg(mIndent).arg(elName.toString());
// qDebug() << log;
mIndent += " ";
if ( elName == tIwb)
{
if (!parseIwb())
return false;
PushState(IWB);
}
else
if ( elName == tMeta)
{
if (!parseIwbMeta())
return false;
}
else
if ( elName == tSvg)
{
if (!parseSvg())
return false;
PushState(SVG);
}
else
if ( elName == tRect)
{
if (!parseRect())
return false;
}
else
if ( elName == tEllipse)
{
if (!parseEllipse())
return false;
}
else
if ( elName == tText)
{
if (!parseText())
return false;
}
else
if ( elName == tTextarea)
{
if (!parseTextArea())
return false;
}
else
if ( elName == tPolygon)
{
if (!parsePolygon())
return false;
}
else
if ( elName == tPolyline)
{
if (!parsePolyline())
return false;
}
else
if ( elName == tPage)
{
if (!parsePage())
return false;
PushState(PAGE);
}
else
if ( elName == tPageset)
{
if (!parsePageSet())
return false;
PushState(PAGESET);
}
else
if ( elName == tElement)
{
if (!parseIwbElementRef())
return false;
}
return true;
}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::parseCurrentElementCharacters()
{
return true;
}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::parseCurrentElementEnd()
{
QStringRef elName = mReader.name();
mIndent.remove(0,1);
QString log = QString("%1</%2>").arg(mIndent).arg(elName.toString());
qDebug() << log;
if ( elName == tIwb)
PopState();
else
if ( elName == tMeta)
{
}
else
if ( elName == tSvg)
{
persistCurrentScene();
PopState();
}
else
if ( elName == tPage)
{
persistCurrentScene();
PopState();
}
else
if ( elName == tPageset)
{
PopState();
}
return true;
}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::parseIwb()
{
if (!stateStack.empty() || currentState != NONE)
{
qWarning() << "iwb content parse error, unexpected iwb tag at line" << mReader.lineNumber();
return false;
}
return true;
}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::parseIwbMeta()
{
if (currentState != IWB)
{
qWarning() << "iwb content parse error, unexpected meta tag at line" << mReader.lineNumber();
return false;
}
return true;
}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::parseSvg()
{
if (currentState != IWB)
{
qWarning() << "iwb content parse error, unexpected svg tag at line" << mReader.lineNumber();
return false;
}
getViewBoxDimenstions(mReader.attributes().value(aViewbox).toString());
mSize = QSize(mReader.attributes().value(aWidth).toString().toInt(),
mReader.attributes().value(aHeight).toString().toInt());
return true;
}
void UBCFFSubsetAdaptor::UBCFFSubsetReader::repositionSvgItem(UBGraphicsSvgItem *item, qreal width, qreal height,
qreal x, qreal y,
bool useTransform, QTransform &transform)
{
QTransform curTrans = item->transform();
// qWarning() << QString().sprintf("Item current transform = %f 0 0 %f %f %f, position %f, %f", curTrans.m11(), curTrans.m22(), curTrans.dx(), curTrans.dy(), item->x(), item->y());
//check if rect is rotated
//rotate svg item itself
QRectF itemBounds = item->boundingRect();
//first, svg is mapped to svg item bound
//second, svg item is mapped to scene
//so, get svg to svg item scale and multiple by scene scale
qreal hScale = itemBounds.width() / width * curTrans.m11();
qreal vScale = itemBounds.height() / height * curTrans.m22();
if (useTransform)
{
QPointF oldVector((x - transform.dx()), (y - transform.dy()));
QTransform rTransform(transform.m11(), transform.m12(), transform.m21(), transform.m22(), 0, 0);
QPointF newVector = rTransform.map(oldVector);
rTransform.scale(curTrans.m11(), curTrans.m22());
item->setTransform(QTransform(rTransform.m11(), rTransform.m12(), rTransform.m21(), rTransform.m22(), 0, 0));
item->setPos((x - mViewBoxCenter.x() + (newVector - oldVector).x()) * hScale, (y - mViewBoxCenter.y() + (newVector - oldVector).y()) * vScale );
}
else
{
item->setPos((x - mViewBoxCenter.x()) * hScale, (y - mViewBoxCenter.y()) * vScale);
}
QTransform newTrans = item->transform();
// qWarning() << QString("Item new transform = %3 0 0 %4 %1 %2, position %5, %6").arg(newTrans.dx()).arg(newTrans.dy()).arg(newTrans.m11()).arg(newTrans.m22()).arg(item->x()).arg(item->y());
}
void UBCFFSubsetAdaptor::UBCFFSubsetReader::repositionPixmapItem(UBGraphicsPixmapItem *item, qreal width, qreal height,
qreal x, qreal y,
bool useTransform, QTransform &transform)
{
//if element is to transform
// if (!transform.isIdentity()) {
// QTransform curTransform = item->transform();
// qreal hScale = item->boundingRect().width() / width * curTransform.m11();
// qreal vScale = item->boundingRect().height() / height * curTransform.m22();
// curTransform = curTransform.translate(x - mViewBoxCenter.x(), y - mViewBoxCenter.y()).scale(hScale, vScale);
// curTransform = curTransform * transform;
// item->setTransform(curTransform);
QTransform curTrans = item->transform();
// qWarning() << QString().sprintf("Item current transform = %f 0 0 %f %f %f, position %f, %f", curTrans.m11(), curTrans.m22(), curTrans.dx(), curTrans.dy(), item->x(), item->y());
//check if rect is rotated
//rotate svg item itself
QRectF itemBounds = item->boundingRect();
//first, svg is mapped to svg item bound
//second, svg item is mapped to scene
//so, get svg to svg item scale and multiple by scene scale
qreal hScale = itemBounds.width() / width * curTrans.m11();
qreal vScale = itemBounds.height() / height * curTrans.m22();
if (useTransform)
{
QPointF oldVector((x - transform.dx()), (y - transform.dy()));
QTransform rTransform(transform.m11(), transform.m12(), transform.m21(), transform.m22(), 0, 0);
QPointF newVector = rTransform.map(oldVector);
rTransform.scale(curTrans.m11(), curTrans.m22());
item->setTransform(QTransform(rTransform.m11(), rTransform.m12(), rTransform.m21(), rTransform.m22(), 0, 0));
item->setPos((x - mViewBoxCenter.x() + (newVector - oldVector).x()) * hScale, (y - mViewBoxCenter.y() + (newVector - oldVector).y()) * vScale );
}
else
{
item->setPos((x - mViewBoxCenter.x()) * hScale, (y - mViewBoxCenter.y()) * vScale);
}
}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::parseRect()
{
if (currentState != SVG && currentState != PAGE)
{
qWarning() << "iwb content parse error, unexpected rect tag at line" << mReader.lineNumber();
return false;
}
//create new scene if it's not created yet (for one page document case)
if (currentState == SVG && mCurrentScene == NULL)
createNewScene();
//rect lef top corner coordinates
qreal x1 = mReader.attributes().value(aX).toString().toDouble();
qreal y1 = mReader.attributes().value(aY).toString().toDouble();
//rect dimensions
qreal width = mReader.attributes().value(aWidth).toString().toDouble();
qreal height = mReader.attributes().value(aHeight).toString().toDouble();
//init svg generator with temp file
QSvgGenerator *generator = createSvgGenerator(width + 10, height + 10);
//init painter to paint to svg
QPainter painter;
painter.begin(generator);
//fill rect
if (mReader.attributes().hasAttribute(aFill))
{
QColor fillColor = colorFromString(mReader.attributes().value(aFill).toString());
painter.setBrush(QBrush(fillColor));
painter.fillRect(5, 5, width, height, fillColor);
}
bool hasStrokeColor = mReader.attributes().hasAttribute(aStroke);
bool hasStrokeWidth = mReader.attributes().hasAttribute(aStrokewidth);
if (hasStrokeColor || hasStrokeWidth)
{
QPen pen;
if (hasStrokeColor)
pen.setColor(colorFromString(mReader.attributes().value(aStroke).toString()));
if (hasStrokeWidth)
pen.setWidth(mReader.attributes().value(aStrokewidth).toString().toInt());
painter.setPen(pen);
painter.drawRect(5, 5, width, height);
}
painter.end();
//add resulting svg file to scene
UBGraphicsSvgItem *svgItem = mCurrentScene->addSvg(QUrl::fromLocalFile(generator->fileName()));
QTransform transform;
bool hastransform = getCurElementTransorm(transform);
repositionSvgItem(svgItem, width + 10, height + 10, x1 - 5, y1 - 5, hastransform, transform);
delete generator;
return true;
}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::parseEllipse()
{
if (currentState != SVG && currentState != PAGE)
{
qWarning() << "iwb content parse error, unexpected ellipse tag at line" << mReader.lineNumber();
return false;
}
//create new scene if it's not created yet (for one page document case)
if (currentState == SVG && mCurrentScene == NULL)
createNewScene();
//ellipse horisontal and vertical radius
qreal rx = mReader.attributes().value(aRx).toString().toDouble();
qreal ry = mReader.attributes().value(aRy).toString().toDouble();
QSvgGenerator *generator = createSvgGenerator(rx * 2 + 10, ry * 2 + 10);
//fill and stroke color
QColor fillColor = colorFromString(mReader.attributes().value(aFill).toString());
QColor strokeColor = colorFromString(mReader.attributes().value(aStroke).toString());
int strokeWidth = mReader.attributes().value(aStrokewidth).toString().toInt();
//ellipse center coordinates
qreal cx = mReader.attributes().value(aCx).toString().toDouble();
qreal cy = mReader.attributes().value(aCy).toString().toDouble();
//init painter to paint to svg
QPainter painter;
painter.begin(generator);
QPen pen(strokeColor);
pen.setWidth(strokeWidth);
painter.setPen(pen);
painter.setBrush(QBrush(fillColor));
painter.drawEllipse(5, 5, rx * 2, ry * 2);
painter.end();
//add resulting svg file to scene
UBGraphicsSvgItem *svgItem = mCurrentScene->addSvg(QUrl::fromLocalFile(generator->fileName()));
QTransform transform;
bool hasTransform = getCurElementTransorm(transform);
repositionSvgItem(svgItem, rx * 2 + 10, ry * 2 + 10, cx - rx - 5, cy - ry -5, hasTransform, transform);
delete generator;
return true;
}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::parseTextArea()
{
if (currentState != SVG && currentState != PAGE)
{
qWarning() << "iwb content parse error, unexpected textarea tag at line" << mReader.lineNumber();
return false;
}
//create new scene if it's not created yet (for one page document case)
if (currentState == SVG && mCurrentScene == NULL)
createNewScene();
//TODO textarea node
qreal x = mReader.attributes().value(aX).toString().toDouble();
qreal y = mReader.attributes().value(aY).toString().toDouble();
qreal width = mReader.attributes().value(aWidth).toString().toDouble();
qreal height = mReader.attributes().value(aHeight).toString().toDouble();
qWarning() << QString().sprintf("Text coordinates : %f,%f. Text size %f,%f", x, y, width, height);
qreal fontSize = 12.0;
QColor fontColor;
QString fontFamily = "Arial";
QString fontStretch = "normal";
bool italic = false;
int fontWeight = QFont::Normal;
int textAlign = Qt::AlignLeft;
QTransform fontTransform;
parseTextAttributes(fontSize, fontColor, fontFamily, fontStretch, italic, fontWeight, textAlign, fontTransform);
QSvgGenerator *generator = createSvgGenerator(width, height);
QPainter painter;
painter.begin(generator);
painter.setFont(QFont(fontFamily, fontSize, fontWeight, italic));
qreal curY = 0.0;
qreal curX = 0.0;
qreal linespacing = QFontMetricsF(painter.font()).leading();
//remember if text area has transform
QString transformString;
QTransform transform;
bool hasTransform = getCurElementTransorm(transform);
QRectF lastDrawnTextBoundingRect;
//parse text area tags
while(true)
{
mReader.readNext();
QStringRef elementName = mReader.name();
if (mReader.isEndDocument())
break;
if (mReader.isEndElement() && elementName == tBreak)
{
//when tbreak appers, move down by the drawn rect height
//TODO: line spacing is not calculated yet, additional code is required
curY += lastDrawnTextBoundingRect.height() + linespacing;
curX = 0.0;
lastDrawnTextBoundingRect = QRectF(0,0,0,0);
continue;
}
if (mReader.isEndElement() && elementName == tTextarea)
break;
if (mReader.isStartElement() && elementName == tTspan)
{
parseTextAttributes(fontSize, fontColor, fontFamily, fontStretch, italic, fontWeight, textAlign, fontTransform);
painter.setFont(QFont(fontFamily, fontSize, fontWeight, italic));
painter.setPen(fontColor);
linespacing = QFontMetricsF(painter.font()).leading();
continue;
}
if (mReader.isCharacters() || mReader.isCDATA())
{
QString text = mReader.text().toString().trimmed();
//skip empty text
if (text.length() == 0)
continue;
//get bounding rect to obtain desired text height
lastDrawnTextBoundingRect = painter.boundingRect(QRectF(curX, curY, width, height - curY), textAlign|Qt::TextWordWrap, text);
QString log = QString().sprintf(" at rect %f, %f, %f, %f. Bounding rect is %f, %f, %f, %f", 0.0, curY, width, height - curY, lastDrawnTextBoundingRect.x(), lastDrawnTextBoundingRect.y(), lastDrawnTextBoundingRect.width(), lastDrawnTextBoundingRect.height());
qWarning() << "Text " << text << log;
painter.drawText(curX, curY, width, lastDrawnTextBoundingRect.height(), textAlign|Qt::TextWordWrap, text);
curX += lastDrawnTextBoundingRect.x() + lastDrawnTextBoundingRect.width();
continue;
}
}
painter.end();
//add resulting svg file to scene
UBGraphicsSvgItem *svgItem = mCurrentScene->addSvg(QUrl::fromLocalFile(generator->fileName()));
repositionSvgItem(svgItem, width, height, x, y, hasTransform, transform);
delete generator;
return true;
}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::parseText()
{
if (currentState != SVG && currentState != PAGE)
{
qWarning() << "iwb content parse error, unexpected textarea tag at line" << mReader.lineNumber();
return false;
}
//create new scene if it's not created yet (for one page document case)
if (currentState == SVG && mCurrentScene == NULL)
createNewScene();
qreal x = mReader.attributes().value(aX).toString().toDouble();
qreal y = mReader.attributes().value(aY).toString().toDouble();
qreal width = 0;
qreal height = 0;
QList<QRectF> textRects;
QList<QFont> textFonts;
QList<QString> textLines;
QList<int> textAligns;
QList<QColor> textColors;
qWarning() << QString().sprintf("Text coordinates : %f,%f. Text size %f,%f", x, y, width, height);
qreal fontSize = 12.0;
QFont textFont;
QColor fontColor;
QString fontFamily = "Arial";
QString fontStretch = "normal";
bool italic = false;
int fontWeight = QFont::Normal;
int textAlign = Qt::AlignLeft;
QTransform fontTransform;
parseTextAttributes(fontSize, fontColor, fontFamily, fontStretch, italic, fontWeight, textAlign, fontTransform);
textFont = QFont(fontFamily, fontSize, fontWeight, italic);
QFontMetricsF metrics = QFontMetricsF(textFont);
qreal curHeight = metrics.height();
qreal curY = 0.0;
qreal curX = 0.0;
qreal linespacing = QFontMetrics(textFont).leading();
//remember if text area has transform
QTransform transform;
bool hasTransform = getCurElementTransorm(transform);
QRectF lastDrawnTextBoundingRect;
QStack<QFont> fontStack;
QStack<QColor> colorStack;
QStack<int> alignStack;
// first extimate desired text area size
// to do that, parse text area tags
while(true)
{
mReader.readNext();
QStringRef elementName = mReader.name();
if (mReader.isEndDocument())
break;
if (mReader.isEndElement())
{
if (elementName == tBreak)
{
//when tbreak appers, move down by the drawn rect height
//TODO: line spacing is not calculated yet, probably additional code is required
curY += lastDrawnTextBoundingRect.height() + linespacing;
curX = 0.0;
height += lastDrawnTextBoundingRect.height();
lastDrawnTextBoundingRect = QRectF(0,0,0,0);
continue;
}
if (elementName == tTspan)
{
textFont = fontStack.pop();
fontColor = colorStack.pop();
textAlign = alignStack.pop();
continue;
}
}
if (mReader.isEndElement() && elementName == tText)
break;
if (mReader.isStartElement() && elementName == tTspan)
{
fontStack.push(textFont);
colorStack.push(fontColor);
alignStack.push(textAlign);
parseTextAttributes(fontSize, fontColor, fontFamily, fontStretch, italic, fontWeight, textAlign, fontTransform);
textFont = QFont(fontFamily, fontSize, fontWeight, italic);
metrics = QFontMetricsF(textFont);
curHeight = metrics.height();
linespacing = QFontMetricsF(textFont).leading();
continue;
}
if (mReader.isCharacters() || mReader.isCDATA())
{
QString text = mReader.text().toString();
//skip empty text
if (text.trimmed().length() == 0)
continue;
//get bounding rect to obtain desired text height
lastDrawnTextBoundingRect = metrics.boundingRect(QRectF(), textAlign, text);
QString log = QString().sprintf(" at rect %f, %f, %f, %f. Bounding rect is %f, %f, %f, %f", 0.0, curY, width, height - curY, lastDrawnTextBoundingRect.x(), lastDrawnTextBoundingRect.y(), lastDrawnTextBoundingRect.width(), lastDrawnTextBoundingRect.height());
qWarning() << "Text " << text << log;
textFonts.append(textFont);
textRects.append(QRectF(curX, curY, lastDrawnTextBoundingRect.width(), lastDrawnTextBoundingRect.height()));
textLines.append(text);
textAligns.append(textAlign);
textColors.append(fontColor);
curX += lastDrawnTextBoundingRect.width();
if (width < curX)
width = curX;
if (height == 0)
height = curHeight;
continue;
}
}
QSvgGenerator *generator = createSvgGenerator(width, height);
QPainter painter;
painter.begin(generator);
if (textRects.count() != 0)
{
QListIterator<QRectF> textRectsIter(textRects);
QListIterator<QFont> textFontsIter(textFonts);
QListIterator<QString> textLinesIter(textLines);
QListIterator<int> textAlignsIter(textAligns);
QListIterator<QColor> textColorsIter(textColors);
while (textRectsIter.hasNext())
{
QRectF rt = textRectsIter.next();
QFont font = textFontsIter.next();
QString line = textLinesIter.next();
int align = textAlignsIter.next();
QColor color = textColorsIter.next();
painter.setFont(font);
painter.setPen(color);
painter.drawText(rt.x(), rt.y(), rt.width(), rt.height(), align, line);
}
}
painter.end();
//add resulting svg file to scene
UBGraphicsSvgItem *svgItem = mCurrentScene->addSvg(QUrl::fromLocalFile(generator->fileName()));
repositionSvgItem(svgItem, width, height, x, y, hasTransform, transform);
delete generator;
return true;
}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::parsePolygon()
{
if (currentState != SVG && currentState != PAGE)
{
qWarning() << "iwb content parse error, unexpected polygon tag at line" << mReader.lineNumber();
return false;
}
//create new scene if it's not created yet (for one page document case)
if (currentState == SVG && mCurrentScene == NULL)
createNewScene();
QStringRef svgPoints = mReader.attributes().value("points");
QPolygonF polygon;
if (!svgPoints.isNull()) {
QStringList ts = svgPoints.toString().split(QLatin1Char(' '),
QString::SkipEmptyParts);
foreach(const QString sPoint, ts) {
QStringList sCoord = sPoint.split(QLatin1Char(','), QString::SkipEmptyParts);
if (sCoord.size() == 2) {
QPointF point;
point.setX(sCoord.at(0).toFloat());
point.setY(sCoord.at(1).toFloat());
polygon << point;
}
else {
qWarning() << "cannot make sense of a 'point' value" << sCoord;
}
}
}
//bounding rect lef top corner coordinates
qreal x1 = polygon.boundingRect().topLeft().x();
qreal y1 = polygon.boundingRect().topLeft().y();
//bounding rect dimensions
qreal width = polygon.boundingRect().width();
qreal height = polygon.boundingRect().height();
QPen pen;
if (mReader.attributes().hasAttribute(aStroke))
pen.setColor(colorFromString(mReader.attributes().value(aStroke).toString()));
if (mReader.attributes().hasAttribute(aStrokewidth))
pen.setWidth(mReader.attributes().value(aStrokewidth).toString().toInt());
QBrush brush;
if (mReader.attributes().hasAttribute(aFill)) {
brush.setColor(colorFromString(mReader.attributes().value(aFill).toString()));
brush.setStyle(Qt::SolidPattern);
}
QSvgGenerator *generator = createSvgGenerator(width + pen.width(), height + pen.width());
QPainter painter;
painter.begin(generator); //drawing to svg tmp file
painter.translate(pen.widthF() / 2 - x1, pen.widthF() / 2 - y1);
painter.setBrush(brush);
painter.setPen(pen);
painter.drawPolygon(polygon);
painter.end();
//add resulting svg file to scene
UBGraphicsSvgItem *svgItem = mCurrentScene->addSvg(QUrl::fromLocalFile(generator->fileName()));
QTransform transform;
bool hastransform = getCurElementTransorm(transform);
repositionSvgItem(svgItem, width + 10, height + 10, x1 - 5, y1 - 5, hastransform, transform);
delete generator;
return true;
}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::parsePolyline()
{
if (currentState != SVG && currentState != PAGE)
{
qWarning() << "iwb content parse error, unexpected polyline tag at line" << mReader.lineNumber();
return false;
}
//create new scene if it's not created yet (for one page document case)
if (currentState == SVG && mCurrentScene == NULL)
createNewScene();
QStringRef svgPoints = mReader.attributes().value("points");
QPolygonF polygon;
if (!svgPoints.isNull()) {
QStringList ts = svgPoints.toString().split(QLatin1Char(' '),
QString::SkipEmptyParts);
foreach(const QString sPoint, ts) {
QStringList sCoord = sPoint.split(QLatin1Char(','), QString::SkipEmptyParts);
if (sCoord.size() == 2) {
QPointF point;
point.setX(sCoord.at(0).toFloat());
point.setY(sCoord.at(1).toFloat());
polygon << point;
}
else {
qWarning() << "cannot make sense of a 'point' value" << sCoord;
}
}
polygon.translate(-polygon.boundingRect().topLeft());
}
//bounding rect lef top corner coordinates
qreal x1 = polygon.boundingRect().topLeft().x();
qreal y1 = polygon.boundingRect().topLeft().y();
//bounding rect dimensions
qreal width = polygon.boundingRect().width();
qreal height = polygon.boundingRect().height();
QPen pen;
if (mReader.attributes().hasAttribute(aStroke))
pen.setColor(colorFromString(mReader.attributes().value(aStroke).toString()));
if (mReader.attributes().hasAttribute(aStrokewidth))
pen.setWidth(mReader.attributes().value(aStrokewidth).toString().toInt());
QSvgGenerator *generator = createSvgGenerator(width + pen.width(), height + pen.width());
QPainter painter;
painter.begin(generator); //drawing to svg tmp file
painter.translate(pen.widthF() / 2, pen.widthF() / 2);
painter.setPen(pen);
painter.drawPolyline(polygon);
painter.end();
//add resulting svg file to scene
UBGraphicsSvgItem *svgItem = mCurrentScene->addSvg(QUrl::fromLocalFile(generator->fileName()));
QTransform transform;
bool hastransform = getCurElementTransorm(transform);
repositionSvgItem(svgItem, width + 10, height + 10, x1 - 5, y1 - 5, hastransform, transform);
delete generator;
return true;
}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::parsePage()
{
if (currentState != PAGESET && currentState != SVG)
{
qWarning() << "iwb content parse error, unexpected page tag at line" << mReader.lineNumber();
return false;
}
createNewScene();
qWarning() << "Added page number" << mProxy->pageCount();
return true;
}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::parsePageSet()
{
if (currentState != SVG)
{
qWarning() << "iwb content parse error, unexpected page tag at line" << mReader.lineNumber();
return false;
}
return true;
}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::parseIwbElementRef()
{
if (currentState != IWB)
{
qWarning() << "iwb content parse error, unexpected element tag at line" << mReader.lineNumber();
return false;
}
return true;
}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::createNewScene()
{
mCurrentScene = UBPersistenceManager::persistenceManager()->createDocumentSceneAt(mProxy, mProxy->pageCount());
mCurrentSceneRect = mCurrentScene->normalizedSceneRect();
return true;
}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::persistCurrentScene()
{
if (mCurrentScene != 0 && mCurrentScene->isModified())
{
UBThumbnailAdaptor::persistScene(mProxy->persistencePath(), mCurrentScene, mProxy->pageCount() - 1);
UBSvgSubsetAdaptor::persistScene(mProxy, mCurrentScene, mProxy->pageCount() - 1);
mCurrentScene->setModified(false);
mCurrentScene = 0;
}
return true;
}
QColor UBCFFSubsetAdaptor::UBCFFSubsetReader::colorFromString(const QString& clrString)
{
//init regexp with pattern
//pattern corresponds to strings like 'rgb(1,2,3) or rgb(10%,20%,30%)'
QRegExp regexp("rgb\\(([0-9]+%{0,1}),([0-9]+%{0,1}),([0-9]+%{0,1})\\)");
if (regexp.exactMatch(clrString))
{
if (regexp.capturedTexts().count() == 4 && regexp.capturedTexts().at(0).length() == clrString.length())
{
int r = regexp.capturedTexts().at(1).toInt();
if (regexp.capturedTexts().at(1).indexOf("%") != -1)
r = r * 255 / 100;
int g = regexp.capturedTexts().at(2).toInt();
if (regexp.capturedTexts().at(2).indexOf("%") != -1)
g = g * 255 / 100;
int b = regexp.capturedTexts().at(3).toInt();
if (regexp.capturedTexts().at(3).indexOf("%") != -1)
b = b * 255 / 100;
return QColor(r, g, b);
}
else
return QColor();
}
else
return QColor(clrString);
}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::getCurElementTransorm(QTransform &transform)
{
if (mReader.attributes().hasAttribute(aTransform))
{
transform = transformFromString(mReader.attributes().value(aTransform).toString());
return true;
}
else
return false;
}
QTransform UBCFFSubsetAdaptor::UBCFFSubsetReader::transformFromString(const QString trString)
{
qreal dx = 0.0;
qreal dy = 0.0;
qreal angle = 0.0;
//check pattern for strings like 'rotate(10)'
QRegExp regexp("rotate\\( *([-+]{0,1}[0-9]*\\.{0,1}[0-9]*) *\\)");
if (regexp.exactMatch(trString)) {
angle = regexp.capturedTexts().at(1).toDouble();
} else {
//check pattern for strings like 'rotate(10,20,20)' or 'rotate(10.1,10.2,34.2)'
regexp.setPattern("rotate\\( *([-+]{0,1}[0-9]*\\.{0,1}[0-9]*) *, *([-+]{0,1}[0-9]*\\.{0,1}[0-9]*) *, *([-+]{0,1}[0-9]*\\.{0,1}[0-9]*) *\\)");
if (regexp.exactMatch(trString)) {
angle = regexp.capturedTexts().at(1).toDouble();
dx = regexp.capturedTexts().at(2).toDouble();
dy = regexp.capturedTexts().at(3).toDouble();
}
}
//check pattern for strings like 'translate(11.0, 12.34)'
regexp.setPattern("translate\\( *([-+]{0,1}[0-9]*\\.{0,1}[0-9]*) *,*([-+]{0,1}[0-9]*\\.{0,1}[0-9]*)*\\)");
if (regexp.exactMatch(trString)) {
dx = regexp.capturedTexts().at(1).toDouble();
dy = regexp.capturedTexts().at(2).toDouble();
}
return QTransform().translate(dx, dy).rotate(angle);
}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::getViewBoxDimenstions(const QString& viewBox)
{
//check pattern for strings like 'rotate(10)'
QRegExp regexp("([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)");
if (regexp.exactMatch(viewBox))
{
int capturesCount = regexp.capturedTexts().count();
if (capturesCount == 5 && regexp.capturedTexts().at(0).length() == viewBox.length())
{
mViewBox = QRectF(0, 0, regexp.capturedTexts().at(3).toDouble(), regexp.capturedTexts().at(4).toDouble());
mViewBoxCenter.setX(mViewBox.width() / 2);
mViewBoxCenter.setY(mViewBox.height() / 2);
return true;
}
}
mViewBox = QRectF(0, 0, 1000, 1000);
mViewBoxCenter = QPointF(500, 500);
return false;
}
QSvgGenerator* UBCFFSubsetAdaptor::UBCFFSubsetReader::createSvgGenerator(qreal width, qreal height)
{
QSvgGenerator* generator = new QSvgGenerator();
qWarning() << QString("Making generator with file %1, size (%2, %3) and viewbox (%4 %5 %6 %7)").arg(mTempFilePath)
.arg(width).arg(height).arg(0.0).arg(0.0).arg(width).arg(width);
generator->setResolution(QApplication::desktop()->physicalDpiY());
generator->setFileName(mTempFilePath);
generator->setSize(QSize(width, height));
generator->setViewBox(QRectF(0, 0, width, height));
return generator;
}
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::getTempFileName()
{
int tmpNumber = 0;
QDir rootDir;
while (true)
{
mTempFilePath = QString("%1/sanksvg%2.%3")
.arg(rootDir.tempPath())
.arg(QDateTime::currentDateTime().toString("dd_MM_yyyy_HH-mm"))
.arg(tmpNumber);
if (!QFile::exists(mTempFilePath))
return true;
tmpNumber++;
if (tmpNumber == 100000)
{
qWarning() << "Import failed. Failed to create temporary file for svg objects";
return false;
}
}
}
void UBCFFSubsetAdaptor::UBCFFSubsetReader::parseTextAttributes(qreal &fontSize, QColor &fontColor,
QString &fontFamily, QString &fontStretch, bool &italic,
int &fontWeight, int &textAlign, QTransform &fontTransform)
{
if (mReader.attributes().hasAttribute(aFontSize))
{
//consider inch has 72 liens
//since svg font size is given in pixels, divide it by pixels per line
fontSize = mReader.attributes().value(aFontSize).toString().toDouble() * 72 / QApplication::desktop()->physicalDpiY();
}
if (mReader.attributes().hasAttribute(aFill))
fontColor = colorFromString(mReader.attributes().value(aFill).toString());
if (mReader.attributes().hasAttribute(aFontfamily))
fontFamily = mReader.attributes().value(aFontfamily).toString();
if (mReader.attributes().hasAttribute(aFontstretch))
fontStretch = mReader.attributes().value(aFontstretch).toString();
if (mReader.attributes().hasAttribute(aFontstyle))
{
QStringRef fontStyle = mReader.attributes().value(aFontstyle);
italic = fontStyle == "italic";
}
if (mReader.attributes().hasAttribute(aFontweight))
{
QStringRef weight = mReader.attributes().value(aFontweight);
if (weight == "normal")
fontWeight = QFont::Normal;
else if (weight == "light")
fontWeight = QFont::Light;
else if (weight == "demibold")
fontWeight = QFont::DemiBold;
else if (weight == "bold")
fontWeight = QFont::Bold;
if (weight == "black")
fontWeight = QFont::Black;
}
if (mReader.attributes().hasAttribute(aTextalign))
{
QString align = mReader.attributes().value(aTextalign).toString();
if (align == "middle" || align == "center")
textAlign = Qt::AlignHCenter;
else
if (align == "start")
textAlign = Qt::AlignLeft;
else
if (align == "end")
textAlign = Qt::AlignRight;
}
if (mReader.attributes().hasAttribute(aTransform))
fontTransform = transformFromString(mReader.attributes().value(aTransform).toString());
}