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.
992 lines
33 KiB
992 lines
33 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 "UBCFFSubsetAdaptor.h" |
|
#include "UBMetadataDcSubsetAdaptor.h" |
|
#include "UBThumbnailAdaptor.h" |
|
#include "UBSvgSubsetAdaptor.h" |
|
|
|
//enum of xmlparse status |
|
|
|
//tag names definition |
|
//use them everiwhere! |
|
static QString tElement = "element"; |
|
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 tRect = "rect"; |
|
static QString tSvg = "svg"; |
|
static QString tText = "text"; |
|
static QString tTextarea = "textarea"; |
|
static QString tTspan = "tspan"; |
|
static QString tBreak = "tbreak"; |
|
|
|
//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"; |
|
|
|
|
|
UBCFFSubsetAdaptor::UBCFFSubsetAdaptor() |
|
{ |
|
} |
|
|
|
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; |
|
} |
|
|
|
QByteArray data = file.readAll(); |
|
if (data.length() == 0) |
|
{ |
|
qWarning() << "Either content file " << cffSourceFile << " is empty or failed to read from file"; |
|
file.close(); |
|
return false; |
|
} |
|
|
|
UBCFFSubsetReader cffReader(pDocument, data); |
|
bool result = cffReader.parse(); |
|
file.close(); |
|
|
|
return result; |
|
} |
|
|
|
UBCFFSubsetAdaptor::UBCFFSubsetReader::UBCFFSubsetReader(UBDocumentProxy *proxy, QByteArray &content): |
|
mReader(content), mProxy(proxy), currentState(NONE) |
|
{ |
|
} |
|
|
|
bool UBCFFSubsetAdaptor::UBCFFSubsetReader::parse() |
|
{ |
|
UBMetadataDcSubsetAdaptor::persist(mProxy); |
|
|
|
mIndent = ""; |
|
if (!getTempFileName()) |
|
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::parseDoc() |
|
{ |
|
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; |
|
} |
|
} |
|
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 == 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()); |
|
} |
|
|
|
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(); |
|
|
|
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) |
|
{ |
|
//check pattern for strings like 'rotate(10)' |
|
QRegExp regexp("rotate\\( *([-+]{0,1}[0-9]*\\.{0,1}[0-9]*) *\\)"); |
|
if (regexp.exactMatch(trString)) |
|
{ |
|
if (regexp.capturedTexts().count() == 2 && regexp.capturedTexts().at(0).length() == trString.length()) |
|
{ |
|
qreal angle = regexp.capturedTexts().at(1).toDouble(); |
|
return QTransform().rotate(angle); |
|
} |
|
} |
|
|
|
//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)) |
|
{ |
|
if (regexp.capturedTexts().count() == 4 && regexp.capturedTexts().at(0).length() == trString.length()) |
|
{ |
|
qreal angle = regexp.capturedTexts().at(1).toDouble(); |
|
qreal dx = regexp.capturedTexts().at(2).toDouble(); |
|
qreal dy = regexp.capturedTexts().at(3).toDouble(); |
|
return QTransform().translate(dx, dy).rotate(angle); |
|
} |
|
} |
|
|
|
return QTransform(); |
|
} |
|
|
|
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()); |
|
} |
|
|
|
|