saicore/src/adaptors/UBSvgSubsetAdaptor.cpp

3521 lines
118 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (C) 2025-2026 ООО «КАРУМА»
*
* This file is part of SaiCore.
*
* SaiCore is proprietary software: you can NOT redistribute it and/or modify
* it without the express written permission of ООО «КАРУМА».
*
* SaiCore is distributed under a proprietary EULA license.
* See the LICENSE file for details, or contact support@saicore.ru
*/
#include "UBSvgSubsetAdaptor.h"
#include <QObject>
#include <QtCore>
#include <QtXml>
#include <QGraphicsTextItem>
#include <QDomElement>
#include <QGraphicsVideoItem>
#include <QElapsedTimer>
#include "domain/UBGraphicsSvgItem.h"
#include "domain/UBGraphicsPixmapItem.h"
#include "domain/UBGraphicsPolygonItem.h"
#include "domain/UBGraphicsMediaItem.h"
#include "domain/UBGraphicsWidgetItem.h"
#include "domain/UBGraphicsPDFItem.h"
#include "domain/UBGraphicsTextItem.h"
#include "domain/UBGraphicsTextItemDelegate.h"
#include "domain/UBGraphicsStroke.h"
#include "domain/UBGraphicsStrokesGroup.h"
#include "domain/UBGraphicsGroupContainerItem.h"
#include "domain/UBGraphicsGroupContainerItemDelegate.h"
#include "domain/UBItem.h"
#include "tools/UBGraphicsRuler.h"
#include "tools/UBGraphicsAxes.h"
#include "tools/UBGraphicsCompass.h"
#include "tools/UBGraphicsProtractor.h"
#include "tools/UBGraphicsCurtainItem.h"
#include "tools/UBGraphicsTriangle.h"
#include "tools/UBGraphicsCache.h"
#include "document/UBDocumentProxy.h"
#include "board/UBBoardView.h"
#include "board/UBBoardController.h"
#include "board/UBDrawingController.h"
#include "board/UBBoardPaletteManager.h"
#include "frameworks/UBFileSystemUtils.h"
#include "frameworks/UBStringUtils.h"
#include "frameworks/UBFileSystemUtils.h"
#include "core/UBSettings.h"
#include "core/UBSetting.h"
#include "core/UBPersistenceManager.h"
#include "core/UBApplication.h"
#include "core/UBDisplayManager.h"
#include "core/UBTextTools.h"
#include "pdf/PDFRenderer.h"
#include "core/memcheck.h"
const QString UBSvgSubsetAdaptor::nsSvg = "http://www.w3.org/2000/svg";
const QString UBSvgSubsetAdaptor::nsXHtml = "http://www.w3.org/1999/xhtml";
const QString UBSvgSubsetAdaptor::nsXLink = "http://www.w3.org/1999/xlink";
const QString UBSvgSubsetAdaptor::xmlTrue = "true";
const QString UBSvgSubsetAdaptor::xmlFalse = "false";
const QString UBSvgSubsetAdaptor::sFontSizePrefix = "font-size:";
const QString UBSvgSubsetAdaptor::sPixelUnit = "px";
const QString UBSvgSubsetAdaptor::sFontWeightPrefix = "font-weight:";
const QString UBSvgSubsetAdaptor::sFontStylePrefix = "font-style:";
const QString UBSvgSubsetAdaptor::sFormerUniboardDocumentNamespaceUri = "http://www.mnemis.com/uniboard";
const QString tElement = "element";
const QString tGroup = "group";
const QString tStrokeGroup = "strokeGroup";
const QString tGroups = "groups";
const QString aId = "id";
QString UBSvgSubsetAdaptor::toSvgTransform(const QTransform& matrix)
{
return QString("matrix(%1, %2, %3, %4, %5, %6)")
.arg(matrix.m11(), 0 , 'g')
.arg(matrix.m12(), 0 , 'g')
.arg(matrix.m21(), 0 , 'g')
.arg(matrix.m22(), 0 , 'g')
.arg(matrix.dx(), 0 , 'g')
.arg(matrix.dy(), 0 , 'g');
}
QTransform UBSvgSubsetAdaptor::fromSvgTransform(const QString& transform)
{
QTransform matrix;
QString ts = transform;
ts.replace("matrix(", "");
ts.replace(")", "");
QStringList sl = ts.split(",");
if (sl.size() >= 6)
{
matrix.setMatrix(
sl.at(0).toFloat(),
sl.at(1).toFloat(),
0,
sl.at(2).toFloat(),
sl.at(3).toFloat(),
0,
sl.at(4).toFloat(),
sl.at(5).toFloat(),
1);
}
return matrix;
}
static bool itemZIndexComp(const QGraphicsItem* item1,
const QGraphicsItem* item2)
{
return item1->data(UBGraphicsItemData::ItemOwnZValue).toReal() < item2->data(UBGraphicsItemData::ItemOwnZValue).toReal();
}
void UBSvgSubsetAdaptor::upgradeScene(std::shared_ptr<UBDocumentProxy> proxy, const int pageIndex)
{
//4.2
QDomDocument doc = loadSceneDocument(proxy, pageIndex);
QDomElement elSvg = doc.documentElement(); // SVG tag
QString ubVersion = elSvg.attributeNS(UBSettings::uniboardDocumentNamespaceUri, "version", "4.1"); // default to 4.1
if (ubVersion.startsWith("4.1") || ubVersion.startsWith("4.2") || ubVersion.startsWith("4.3"))
{
// migrate to 4.2.1 (or latter)
std::shared_ptr<UBGraphicsScene> scene = loadScene(proxy, pageIndex);
scene->setModified(true);
persistScene(proxy, scene, pageIndex);
}
}
QDomDocument UBSvgSubsetAdaptor::loadSceneDocument(std::shared_ptr<UBDocumentProxy> proxy, const int pPageIndex)
{
QString fileName = proxy->persistencePath() + UBFileSystemUtils::digitFileFormat("/page%1.svg",pPageIndex);
QFile file(fileName);
QDomDocument doc("page");
if (file.exists())
{
if (!file.open(QIODevice::ReadOnly))
{
qWarning() << "Cannot open file " << fileName << " for reading ...";
return doc;
}
doc.setContent(&file, true);
file.close();
}
return doc;
}
void UBSvgSubsetAdaptor::setSceneUuid(std::shared_ptr<UBDocumentProxy> proxy, const int pageIndex, QUuid pUuid)
{
QString fileName = proxy->persistencePath() + UBFileSystemUtils::digitFileFormat("/page%1.svg",pageIndex);
QFile file(fileName);
if (!file.exists() || !file.open(QIODevice::ReadOnly))
return;
QTextStream textReadStream(&file);
QString xmlContent = textReadStream.readAll();
int uuidIndex = xmlContent.indexOf("uuid");
if (-1 == uuidIndex)
{
qWarning() << "Cannot read UUID from file" << fileName << "to set new UUID";
file.close();
return;
}
int quoteStartIndex = xmlContent.indexOf('"', uuidIndex);
if (-1 == quoteStartIndex)
{
qWarning() << "Cannot read UUID from file" << fileName << "to set new UUID";
file.close();
return;
}
int quoteEndIndex = xmlContent.indexOf('"', quoteStartIndex + 1);
if (-1 == quoteEndIndex)
{
qWarning() << "Cannot read UUID from file" << fileName << "to set new UUID";
file.close();
return;
}
file.close();
QString newXmlContent = xmlContent.left(quoteStartIndex + 1);
newXmlContent.append(UBStringUtils::toCanonicalUuid(pUuid));
newXmlContent.append(xmlContent.right(xmlContent.length() - quoteEndIndex));
if (file.open(QIODevice::WriteOnly | QIODevice::Truncate))
{
QTextStream textWriteStream(&file);
textWriteStream << newXmlContent;
file.close();
}
else
{
qWarning() << "Cannot open file" << fileName << "to write UUID";
}
}
QString UBSvgSubsetAdaptor::uniboardDocumentNamespaceUriFromVersion(int mFileVersion)
{
return mFileVersion >= 40200 ? UBSettings::uniboardDocumentNamespaceUri : sFormerUniboardDocumentNamespaceUri;
}
std::shared_ptr<UBGraphicsScene> UBSvgSubsetAdaptor::loadScene(std::shared_ptr<UBDocumentProxy> proxy, const int pageIndex)
{
UBApplication::showMessage(QObject::tr("Loading scene (%1/%2)").arg(pageIndex+1).arg(proxy->pageCount()));
QString fileName = proxy->persistencePath() + UBFileSystemUtils::digitFileFormat("/page%1.svg", pageIndex);
qInfo() << "loading scene. Filename is : " << fileName;
QFile file(fileName);
if (file.exists())
{
if (!file.open(QIODevice::ReadOnly))
{
qWarning() << "Cannot open file " << fileName << " for reading ...";
return 0;
}
std::shared_ptr<UBGraphicsScene> scene = loadScene(proxy, file.readAll());
file.close();
return scene;
}
return 0;
}
QByteArray UBSvgSubsetAdaptor::loadSceneAsText(std::shared_ptr<UBDocumentProxy> proxy, const int pageIndex)
{
QString fileName = proxy->persistencePath() + UBFileSystemUtils::digitFileFormat("/page%1.svg", pageIndex);
qDebug() << fileName;
QFile file(fileName);
if (file.exists())
{
if (!file.open(QIODevice::ReadOnly))
{
qWarning() << "Cannot open file " << fileName << " for reading ...";
return "";
}
return file.readAll();
file.close();
}
return "";
}
QUuid UBSvgSubsetAdaptor::sceneUuid(std::shared_ptr<UBDocumentProxy> proxy, const int pageIndex)
{
QString fileName = proxy->persistencePath() + UBFileSystemUtils::digitFileFormat("/page%1.svg", pageIndex);
QFile file(fileName);
QUuid uuid;
if (file.exists())
{
if (!file.open(QIODevice::ReadOnly))
{
qWarning() << "Cannot open file " << fileName << " for reading ...";
return QUuid();
}
QXmlStreamReader xml(file.readAll());
bool foundSvg = false;
while (!xml.atEnd() && !foundSvg)
{
xml.readNext();
if (xml.isStartElement())
{
if (xml.name().toString() == "svg")
{
auto svgSceneUuid = xml.attributes().value(UBSettings::uniboardDocumentNamespaceUri, "uuid");
if (svgSceneUuid.isNull())
svgSceneUuid = xml.attributes().value("http://www.mnemis.com/uniboard", "uuid");
if (!svgSceneUuid.isNull())
uuid = QUuid(svgSceneUuid.toString());
foundSvg = true;
}
}
}
file.close();
}
return uuid;
}
std::shared_ptr<UBGraphicsScene> UBSvgSubsetAdaptor::loadScene(std::shared_ptr<UBDocumentProxy> proxy, const QByteArray& pArray)
{
UBSvgSubsetReader reader(proxy, UBTextTools::cleanHtmlCData(QString(pArray)).toUtf8());
return reader.loadScene(proxy);
}
std::shared_ptr<UBSvgSubsetAdaptor::UBSvgReaderContext> UBSvgSubsetAdaptor::prepareLoadingScene(std::shared_ptr<UBDocumentProxy> proxy, const int pageIndex)
{
const auto fileContent = loadSceneAsText(proxy, pageIndex);
auto context = std::make_shared<UBSvgReaderContext>(proxy, fileContent);
return context;
}
UBSvgSubsetAdaptor::UBSvgSubsetReader::UBSvgSubsetReader(std::shared_ptr<UBDocumentProxy> pProxy, const QByteArray& pXmlData)
: mXmlReader(pXmlData)
, mProxy(pProxy)
, mDocumentPath(pProxy->persistencePath())
, mGroupHasInfo(false)
{
// NOOP
}
std::shared_ptr<UBGraphicsScene> UBSvgSubsetAdaptor::UBSvgSubsetReader::loadScene(std::shared_ptr<UBDocumentProxy> proxy)
{
QElapsedTimer time;
time.start();
start();
while (!mXmlReader.atEnd())
{
processElement();
}
qDebug() << "spent milliseconds: " << time.elapsed();
return scene();
}
void UBSvgSubsetAdaptor::UBSvgSubsetReader::start()
{
qDebug() << "loadScene() : starting reading...";
mScene = nullptr;
UBGraphicsWidgetItem *currentWidget = 0;
//bool pageDpiSpecified = true;
saveSceneAfterLoading = false;
mFileVersion = 40100; // default to 4.1.0
}
bool UBSvgSubsetAdaptor::UBSvgSubsetReader::isFinished()
{
return mXmlReader.atEnd();
}
void UBSvgSubsetAdaptor::UBSvgSubsetReader::processElement()
{
mXmlReader.readNext();
if (mXmlReader.isStartElement())
{
QString name = mXmlReader.name().toString();
if (name == "svg")
{
if (!mScene)
{
mScene = std::make_shared<UBGraphicsScene>(mProxy, false);
mScene->setModified(false);
}
// introduced in UB 4.2
auto svgUbVersion = mXmlReader.attributes().value(UBSettings::uniboardDocumentNamespaceUri, "version");
if (!svgUbVersion.isNull())
{
QString ubVersion = svgUbVersion.toString();
//may look like : 4 or 4.1 or 4.2 or 4.2.1, etc
QStringList parts = ubVersion.split(".");
if (parts.length() > 0)
{
mFileVersion = parts.at(0).toInt() * 10000;
}
if (parts.length() > 1)
{
mFileVersion += parts.at(1).toInt() * 100;
}
if (parts.length() > 2)
{
mFileVersion += parts.at(2).toInt();
}
}
mNamespaceUri = uniboardDocumentNamespaceUriFromVersion(mFileVersion);
auto svgSceneUuid = mXmlReader.attributes().value(mNamespaceUri, "uuid");
if (!svgSceneUuid.isNull())
mScene->setUuid(QUuid(svgSceneUuid.toString()));
// introduced in UB 4.0
auto svgViewBox = mXmlReader.attributes().value("viewBox");
if (!svgViewBox.isNull())
{
QStringList ts = svgViewBox.toString().split(QLatin1Char(' '), UB::SplitBehavior::SkipEmptyParts);
QRectF sceneRect;
if (ts.size() >= 4)
{
sceneRect.setX(ts.at(0).toFloat());
sceneRect.setY(ts.at(1).toFloat());
sceneRect.setWidth(ts.at(2).toFloat());
sceneRect.setHeight(ts.at(3).toFloat());
mScene->setSceneRect(sceneRect);
}
else
{
qWarning() << "cannot make sense of 'viewBox' value " << svgViewBox.toString();
}
}
auto pageDpi = mXmlReader.attributes().value("pageDpi");
if (!pageDpi.isNull())
mProxy->setPageDpi(pageDpi.toInt());
else if (mProxy->pageDpi() == 0) {
mProxy->setPageDpi(UBApplication::displayManager->logicalDpi(ScreenRole::Control));
//pageDpiSpecified = false;
}
bool darkBackground = false;
bool crossedBackground = false;
bool ruledBackground = false;
auto ubDarkBackground = mXmlReader.attributes().value(mNamespaceUri, "dark-background");
if (!ubDarkBackground.isNull())
darkBackground = (ubDarkBackground.toString() == xmlTrue);
auto ubCrossedBackground = mXmlReader.attributes().value(mNamespaceUri, "crossed-background");
if (!ubCrossedBackground.isNull())
crossedBackground = (ubCrossedBackground.toString() == xmlTrue);
auto ubGridSize = mXmlReader.attributes().value(mNamespaceUri, "grid-size");
if (!ubGridSize.isNull()) {
int gridSize = ubGridSize.toInt();
mScene->setBackgroundGridSize(gridSize);
}
if (crossedBackground) {
auto ubIntermediateLines = mXmlReader.attributes().value(mNamespaceUri, "intermediate-lines");
if (!ubIntermediateLines.isNull()) {
bool intermediateLines = ubIntermediateLines.toInt();
mScene->setIntermediateLines(intermediateLines);
}
}
auto ubRuledBackground = mXmlReader.attributes().value(mNamespaceUri, "ruled-background");
if (!ubRuledBackground.isNull())
ruledBackground = (ubRuledBackground.toString() == xmlTrue);
if (ruledBackground && !crossedBackground) { // if for some reason both are true, the background will be a grid
auto ubIntermediateLines = mXmlReader.attributes().value(mNamespaceUri, "intermediate-lines");
if (!ubIntermediateLines.isNull()) {
bool intermediateLines = ubIntermediateLines.toInt();
mScene->setIntermediateLines(intermediateLines);
}
}
UBPageBackground bg;
if (crossedBackground)
bg = UBPageBackground::crossed;
else if (ruledBackground)
bg = UBPageBackground::ruled;
else
bg = UBPageBackground::plain;
mScene->setBackground(darkBackground, bg);
auto pageNominalSize = mXmlReader.attributes().value(mNamespaceUri, "nominal-size");
if (!pageNominalSize.isNull())
{
QStringList ts = pageNominalSize.toString().split(QLatin1Char('x'), UB::SplitBehavior::SkipEmptyParts);
QSize sceneSize;
if (ts.size() >= 2)
{
sceneSize.setWidth(ts.at(0).toInt());
sceneSize.setHeight(ts.at(1).toInt());
mScene->setNominalSize(sceneSize);
}
else
{
qWarning() << "cannot make sense of 'nominal-size' value " << pageNominalSize.toString();
}
}
}
else if (name == "g")
{
strokesGroup = new UBGraphicsStrokesGroup();
graphicsItemFromSvg(strokesGroup);
bool hasZValue;
qreal zValue = normalizedZValue(&hasZValue);
if (hasZValue)
{
mGroupZIndex = zValue;
mGroupHasInfo = true;
}
auto ubFillOnDarkBackground = mXmlReader.attributes().value(mNamespaceUri, "fill-on-dark-background");
if (!ubFillOnDarkBackground.isNull())
{
#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0))
mGroupDarkBackgroundColor = QColor::fromString(ubFillOnDarkBackground.toString());
#else
mGroupDarkBackgroundColor.setNamedColor(ubFillOnDarkBackground.toString());
#endif
}
auto ubFillOnLightBackground = mXmlReader.attributes().value(mNamespaceUri, "fill-on-light-background");
if (!ubFillOnLightBackground.isNull())
{
#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0))
mGroupLightBackgroundColor = QColor::fromString(ubFillOnLightBackground.toString());
#else
mGroupLightBackgroundColor.setNamedColor(ubFillOnLightBackground.toString());
#endif
}
auto ubUuid = mXmlReader.attributes().value(mNamespaceUri, "uuid");
if (!ubUuid.isNull())
strokesGroup->setUuid(QUuid(ubUuid.toString()));
else
strokesGroup->setUuid(QUuid::createUuid());
QString uuid_stripped = strokesGroup->uuid().toString().replace("}","").replace("{","");
if (!mStrokesList.contains(uuid_stripped))
mStrokesList.insert(uuid_stripped, strokesGroup);
}
else if (name == "polygon" || name == "line")
{
UBGraphicsPolygonItem* polygonItem = 0;
QString parentId = mXmlReader.attributes().value(mNamespaceUri, "parent").toString();
if (name == "polygon")
polygonItem = polygonItemFromPolygonSvg(mScene->isDarkBackground() ? Qt::white : Qt::black);
else if (name == "line")
polygonItem = polygonItemFromLineSvg(mScene->isDarkBackground() ? Qt::white : Qt::black);
if(parentId.isEmpty() && strokesGroup)
parentId = strokesGroup->uuid().toString();
if(parentId.isEmpty())
parentId = QUuid::createUuid().toString();
if (polygonItem)
{
polygonItem->setData(UBGraphicsItemData::ItemLayerType, QVariant(UBItemLayerType::Graphic));
UBGraphicsStrokesGroup* group;
if(!mStrokesList.contains(parentId)){
group = new UBGraphicsStrokesGroup();
mStrokesList.insert(parentId,group);
group->setTransform(polygonItem->transform());
UBGraphicsItem::assignZValue(group, polygonItem->zValue());
}
else
group = mStrokesList.value(parentId);
if (!currentStroke)
currentStroke = new UBGraphicsStroke();
if(polygonItem->transform().isIdentity())
polygonItem->setTransform(group->transform());
group->addToGroup(polygonItem);
polygonItem->setStrokesGroup(group);
polygonItem->setStroke(currentStroke);
polygonItem->show();
group->addToGroup(polygonItem);
}
}
else if (name == "polyline")
{
QList<UBGraphicsPolygonItem*> polygonItems = polygonItemsFromPolylineSvg(mScene->isDarkBackground() ? Qt::white : Qt::black);
QString parentId = mXmlReader.attributes().value(mNamespaceUri, "parent").toString();
if(parentId.isEmpty() && strokesGroup)
parentId = strokesGroup->uuid().toString();
if(parentId.isEmpty())
parentId = QUuid::createUuid().toString();
foreach(UBGraphicsPolygonItem* polygonItem, polygonItems)
{
polygonItem->setData(UBGraphicsItemData::ItemLayerType, QVariant(UBItemLayerType::Graphic));
UBGraphicsStrokesGroup* group;
if(!mStrokesList.contains(parentId)){
group = new UBGraphicsStrokesGroup();
mStrokesList.insert(parentId,group);
group->setTransform(polygonItem->transform());
UBGraphicsItem::assignZValue(group, polygonItem->zValue());
}
else
group = mStrokesList.value(parentId);
if (!currentStroke)
currentStroke = new UBGraphicsStroke();
if(polygonItem->transform().isIdentity())
polygonItem->setTransform(group->transform());
group->addToGroup(polygonItem);
polygonItem->setStrokesGroup(group);
polygonItem->setStroke(currentStroke);
polygonItem->show();
group->addToGroup(polygonItem);
}
}
else if (name == "image")
{
auto imageHref = mXmlReader.attributes().value(nsXLink, "href");
if (!imageHref.isNull())
{
QString href = imageHref.toString();
auto ubBackground = mXmlReader.attributes().value(mNamespaceUri, "background");
bool isBackground = (!ubBackground.isNull() && ubBackground.toString() == xmlTrue);
if (href.endsWith(".svg"))
{
UBGraphicsSvgItem* svgItem = svgItemFromSvg();
if (svgItem)
{
svgItem->setSourceUrl(QUrl::fromLocalFile(href));
svgItem->setFlag(QGraphicsItem::ItemIsMovable, true);
svgItem->setFlag(QGraphicsItem::ItemIsSelectable, true);
mScene->addItem(svgItem);
if (isBackground)
mScene->setAsBackgroundObject(svgItem);
svgItem->show();
}
}
else
{
UBGraphicsPixmapItem* pixmapItem = pixmapItemFromSvg();
if (pixmapItem)
{
pixmapItem->setSourceUrl(QUrl::fromLocalFile(href));
pixmapItem->setFlag(QGraphicsItem::ItemIsMovable, true);
pixmapItem->setFlag(QGraphicsItem::ItemIsSelectable, true);
mScene->addItem(pixmapItem);
if (isBackground)
mScene->setAsBackgroundObject(pixmapItem);
pixmapItem->show();
}
}
}
}
else if (name == "audio")
{
UBGraphicsMediaItem* audioItem = audioItemFromSvg();
if (audioItem)
{
audioItem->setFlag(QGraphicsItem::ItemIsMovable, true);
audioItem->setFlag(QGraphicsItem::ItemIsSelectable, true);
mScene->addItem(audioItem);
audioItem->show();
audioItem->play();
audioItem->pause();
}
}
else if (name == "video")
{
UBGraphicsMediaItem* videoItem = videoItemFromSvg();
if (videoItem)
{
videoItem->setFlag(QGraphicsItem::ItemIsMovable, true);
videoItem->setFlag(QGraphicsItem::ItemIsSelectable, true);
mScene->addItem(videoItem);
videoItem->show();
}
}
else if (name == "text")//This is for backward compatibility with proto text field prior to version 4.3
{
UBGraphicsTextItem* textItem = textItemFromSvg();
if (textItem)
{
textItem->setFlag(QGraphicsItem::ItemIsMovable, true);
textItem->setFlag(QGraphicsItem::ItemIsSelectable, true);
mScene->addItem(textItem);
textItem->show();
}
}
else if (name == "curtain")
{
UBGraphicsCurtainItem* mask = curtainItemFromSvg();
if (mask)
{
mScene->addItem(mask);
mScene->registerTool(mask);
}
}
else if (name == "ruler")
{
UBGraphicsRuler *ruler = rulerFromSvg();
if (ruler)
{
mScene->addItem(ruler);
mScene->registerTool(ruler);
}
}
else if (name == "axes")
{
UBGraphicsAxes *axes = axesFromSvg();
if (axes)
{
mScene->addItem(axes);
}
}
else if (name == "compass")
{
UBGraphicsCompass *compass = compassFromSvg();
if (compass)
{
mScene->addItem(compass);
mScene->registerTool(compass);
}
}
else if (name == "protractor")
{
UBGraphicsProtractor *protractor = protractorFromSvg();
if (protractor)
{
mScene->addItem(protractor);
mScene->registerTool(protractor);
}
}
else if (name == "triangle")
{
UBGraphicsTriangle *triangle = triangleFromSvg();
if (triangle)
{
mScene->addItem(triangle);
mScene->registerTool(triangle);
}
}
else if (name == "cache")
{
UBGraphicsCache* cache = cacheFromSvg();
if(cache)
{
mScene->addItem(cache);
mScene->registerTool(cache);
UBApplication::boardController->notifyCache(true);
}
}
else if (name == "foreignObject")
{
QString href = mXmlReader.attributes().value(nsXLink, "href").toString();
QString src = mXmlReader.attributes().value(mNamespaceUri, "src").toString();
QString type = mXmlReader.attributes().value(mNamespaceUri, "type").toString();
bool isBackground = mXmlReader.attributes().value(mNamespaceUri, "background").toString() == xmlTrue;
qreal foreignObjectWidth = mXmlReader.attributes().value("width").toString().toFloat();
qreal foreignObjectHeight = mXmlReader.attributes().value("height").toString().toFloat();
if (href.contains(".pdf"))
{
UBGraphicsPDFItem* pdfItem = pdfItemFromPDF();
if (pdfItem)
{
qreal currentDpi = UBApplication::displayManager->logicalDpi(ScreenRole::Control);
// qDebug() << "currentDpi = " << currentDpi;
qreal pdfScale = qreal(mProxy->pageDpi())/currentDpi;
// qDebug() << "pdfScale " << pdfScale;
// If the PDF is in the background, it occupies the whole page; so we can simply
// use that information to calculate its scale.
if (isBackground) {
qreal pageWidth = mScene->nominalSize().width();
qreal pageHeight = mScene->nominalSize().height();
qreal scaleX = pageWidth / pdfItem->sceneBoundingRect().width();
qreal scaleY = pageHeight / pdfItem->sceneBoundingRect().height();
pdfScale = (scaleX+scaleY)/2.;
}
pdfItem->setScale(pdfScale);
pdfItem->setFlag(QGraphicsItem::ItemIsMovable, true);
pdfItem->setFlag(QGraphicsItem::ItemIsSelectable, true);
mScene->addItem(pdfItem);
if (isBackground)
mScene->setAsBackgroundObject(pdfItem);
pdfItem->show();
currentWidget = 0;
}
}
else if (src.contains(".wdgt")) // NOTE @letsfindaway obsolete
{
UBGraphicsAppleWidgetItem* appleWidgetItem = graphicsAppleWidgetFromSvg();
if (appleWidgetItem)
{
appleWidgetItem->setFlag(QGraphicsItem::ItemIsSelectable, true);
appleWidgetItem->resize(foreignObjectWidth, foreignObjectHeight);
mScene->addItem(appleWidgetItem);
appleWidgetItem->show();
currentWidget = appleWidgetItem;
}
}
else if (src.contains(".wgt"))
{
UBGraphicsW3CWidgetItem* w3cWidgetItem = graphicsW3CWidgetFromSvg();
if (w3cWidgetItem)
{
// check compatibility
QUuid uuid(mXmlReader.attributes().value(mNamespaceUri, "uuid").toString());
if (!mProxy->isWidgetCompatible(uuid))
{
// show a substitute image and freeze the widget
QPixmap pixmap(w3cWidgetItem->size().toSize());
pixmap.fill(Qt::lightGray);
QPainter painter(&pixmap);
QFont font = painter.font();
font.setPixelSize(16);
painter.setFont(font);
const QRectF rectangle(QPointF(0, 0), w3cWidgetItem->size());
std::string incompatibleWidgetMessage = QString("Incompatible widget (%1).").arg(w3cWidgetItem->metadatas().name).toStdString();
painter.drawText(rectangle, Qt::AlignCenter, QObject::tr(incompatibleWidgetMessage.c_str()));
w3cWidgetItem->setSnapshot(pixmap, true);
// disable user interactions
w3cWidgetItem->setFreezable(false);
w3cWidgetItem->setCanBeTool(false);
}
w3cWidgetItem->setFlag(QGraphicsItem::ItemIsSelectable, true);
w3cWidgetItem->resize(foreignObjectWidth, foreignObjectHeight);
mScene->addItem(w3cWidgetItem);
w3cWidgetItem->show();
currentWidget = w3cWidgetItem;
}
}
else if (type == "text")
{
UBGraphicsTextItem* textItem = textItemFromSvg();
if (textItem)
{
textItem->setFlag(QGraphicsItem::ItemIsMovable, true);
textItem->setFlag(QGraphicsItem::ItemIsSelectable, true);
textItem->activateTextEditor(false);
mScene->addItem(textItem);
textItem->show();
}
}
else
{
qWarning() << "Ignoring unknown foreignObject:" << href;
}
}
else if (currentWidget && (name == "preference"))
{
QString key = mXmlReader.attributes().value("key").toString();
QString value = mXmlReader.attributes().value("value").toString();
currentWidget->setPreference(key, value);
}
else if (currentWidget && (name == "datastoreEntry"))
{
QString key = mXmlReader.attributes().value("key").toString();
QString value = mXmlReader.attributes().value("value").toString();
currentWidget->setDatastoreEntry(key, value);
} else if (name == tGroups) {
//considering groups section at the end of the document
readGroupRoot();
}
else
{
// NOOP
}
}
else if (mXmlReader.isEndElement())
{
if (mXmlReader.name().toString() == "g")
{
mGroupHasInfo = false;
mGroupDarkBackgroundColor = QColor();
mGroupLightBackgroundColor = QColor();
strokesGroup = NULL;
currentStroke = NULL;
}
}
if (mXmlReader.atEnd())
{
mMustFinalize = true;
}
}
std::shared_ptr<UBGraphicsScene> UBSvgSubsetAdaptor::UBSvgSubsetReader::scene()
{
if (mMustFinalize)
{
if (mXmlReader.hasError())
{
qWarning() << "error parsing file " << mXmlReader.errorString();
}
qDebug() << "Number of detected strokes: " << mStrokesList.count();
if (mScene) {
QHashIterator<QString, UBGraphicsStrokesGroup*> iterator(mStrokesList);
while (iterator.hasNext()) {
iterator.next();
mScene->addItem(iterator.value());
}
mScene->setModified(saveSceneAfterLoading);
mScene->enableUndoRedoStack();
qDebug() << "loadScene() : created scene and read file";
}
mMustFinalize = false;
}
return mScene;
}
UBGraphicsGroupContainerItem* UBSvgSubsetAdaptor::UBSvgSubsetReader::readGroup()
{
UBGraphicsGroupContainerItem *group = new UBGraphicsGroupContainerItem();
QList<QGraphicsItem *> groupContainer;
QString id = mXmlReader.attributes().value(aId).toString();
id = id.mid(1,id.length()-2);
bool shouldSkipSubElements = false;
if(mStrokesList.contains(id))
shouldSkipSubElements = true;
QString ubLocked = mXmlReader.attributes().value(mNamespaceUri, "locked").toString();
if (!ubLocked.isEmpty())
{
bool isLocked = ubLocked.contains(xmlTrue);
group->setData(UBGraphicsItemData::ItemLocked, QVariant(isLocked));
}
QString ubHiddenOnDisplay = mXmlReader.attributes().value(mNamespaceUri, "hidden-on-display").toString();
if (!ubHiddenOnDisplay.isEmpty())
{
bool isHiddenOnDisplay = ubHiddenOnDisplay.contains(xmlTrue);
group->setData(UBGraphicsItemData::ItemIsHiddenOnDisplay, QVariant(isHiddenOnDisplay));
}
auto ubLayer = mXmlReader.attributes().value(mNamespaceUri, "layer");
if (!ubLayer.isNull())
{
bool ok;
int layerAsInt = ubLayer.toString().toInt(&ok);
if (ok)
group->setData(UBGraphicsItemData::ItemLayerType, QVariant(layerAsInt));
}
auto ubZValue = mXmlReader.attributes().value(mNamespaceUri, "z-value");
if (!ubZValue.isNull())
{
bool ok;
qreal zValue = ubZValue.toString().toFloat(&ok);
if (ok)
{
group->setZValue(zValue);
group->setData(UBGraphicsItemData::ItemOwnZValue, QVariant(zValue));
}
}
mXmlReader.readNext();
while (!mXmlReader.atEnd())
{
if (mXmlReader.isEndElement()) {
mXmlReader.readNext();
break;
}
else if (mXmlReader.isStartElement()) {
if (mXmlReader.name() == tGroup) {
UBGraphicsGroupContainerItem *curGroup = readGroup();
if (curGroup)
groupContainer.append(curGroup);
}
else if (mXmlReader.name() == tElement && !shouldSkipSubElements) {
QString id = mXmlReader.attributes().value(aId).toString();
QGraphicsItem *curItem = readElementFromGroup();
// Explanation: the second condition discriminate the old storage version that should
// not be interpreted anymore
if(curItem && id.count("{") < 2)
groupContainer.append(curItem);
}
else {
mXmlReader.skipCurrentElement();
}
}
else {
mXmlReader.readNext();
}
}
foreach(QGraphicsItem* item, groupContainer)
group->addToGroup(item);
if (group->childItems().count())
{
// mScene->addItem(group);
if (1 == group->childItems().count())
{
group->destroy(false);
}
}
return group;
}
void UBSvgSubsetAdaptor::UBSvgSubsetReader::readGroupRoot()
{
mXmlReader.readNext();
while (!mXmlReader.atEnd()) {
if (mXmlReader.isEndElement()) {
mXmlReader.readNext();
break;
}
else if (mXmlReader.isStartElement()) {
if (mXmlReader.name() == tGroup) {
UBGraphicsGroupContainerItem *curGroup = readGroup();
if (curGroup) {
mScene->addGroup(curGroup);
}
}
else {
mXmlReader.skipCurrentElement();
}
}
else {
mXmlReader.readNext();
}
}
}
QGraphicsItem *UBSvgSubsetAdaptor::UBSvgSubsetReader::readElementFromGroup()
{
QGraphicsItem *result = 0;
QString id = mXmlReader.attributes().value(aId).toString();
QString uuid = id.right(QUuid().toString().size());
result = mScene->itemForUuid(QUuid(uuid));
if(!result)
result = mStrokesList.take(uuid.replace("}","").replace("{",""));
mXmlReader.skipCurrentElement();
mXmlReader.readNext();
return result;
}
void UBSvgSubsetAdaptor::persistScene(std::shared_ptr<UBDocumentProxy> proxy, std::shared_ptr<UBGraphicsScene> pScene, const int pageIndex)
{
UBSvgSubsetWriter writer(proxy, pScene, pageIndex);
writer.persistScene(proxy, pageIndex);
}
UBSvgSubsetAdaptor::UBSvgSubsetWriter::UBSvgSubsetWriter(std::shared_ptr<UBDocumentProxy> proxy, std::shared_ptr<UBGraphicsScene> pScene, const int pageIndex)
: mScene(pScene)
, mDocumentPath(proxy->persistencePath())
, mPageIndex(pageIndex)
{
// NOOP
}
void UBSvgSubsetAdaptor::UBSvgSubsetWriter::writeSvgElement(std::shared_ptr<UBDocumentProxy> proxy)
{
mXmlWriter.writeStartElement("svg");
mXmlWriter.writeAttribute("version", "1.1");
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "version", UBSettings::currentFileVersion);
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "uuid", UBStringUtils::toCanonicalUuid(mScene->uuid()));
int margin = UBSettings::settings()->svgViewBoxMargin->get().toInt();
QRect normalized = mScene->normalizedSceneRect().toRect();
normalized.translate(margin * -1, margin * -1);
normalized.setWidth(normalized.width() + (margin * 2));
normalized.setHeight(normalized.height() + (margin * 2));
mXmlWriter.writeAttribute("viewBox", QString("%1 %2 %3 %4").arg(normalized.x()).arg(normalized.y()).arg(normalized.width()).arg(normalized.height()));
QSize pageNominalSize = mScene->nominalSize();
if (pageNominalSize.isValid())
{
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "nominal-size", QString("%1x%2").arg(pageNominalSize.width()).arg(pageNominalSize.height()));
}
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "dark-background", mScene->isDarkBackground() ? xmlTrue : xmlFalse);
bool crossedBackground = mScene->pageBackground() == UBPageBackground::crossed;
bool ruledBackground = mScene->pageBackground() == UBPageBackground::ruled;
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "crossed-background", crossedBackground ? xmlTrue : xmlFalse);
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "ruled-background", ruledBackground ? xmlTrue : xmlFalse);
int gridSize = mScene->backgroundGridSize();
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "grid-size", QString::number(gridSize));
if (crossedBackground || ruledBackground) {
bool intermediateLines = mScene->intermediateLines();
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "intermediate-lines", QString::number(intermediateLines));
}
if (proxy->pageDpi() == 0)
proxy->setPageDpi(UBApplication::displayManager->logicalDpi(ScreenRole::Control));
mXmlWriter.writeAttribute("pageDpi", QString::number(proxy->pageDpi()));
mXmlWriter.writeStartElement("rect");
mXmlWriter.writeAttribute("fill", mScene->isDarkBackground() ? "black" : "white");
mXmlWriter.writeAttribute("x", QString::number(normalized.x()));
mXmlWriter.writeAttribute("y", QString::number(normalized.y()));
mXmlWriter.writeAttribute("width", QString::number(normalized.width()));
mXmlWriter.writeAttribute("height", QString::number(normalized.height()));
mXmlWriter.writeEndElement();
}
bool UBSvgSubsetAdaptor::UBSvgSubsetWriter::persistScene(std::shared_ptr<UBDocumentProxy> proxy, int pageIndex)
{
Q_UNUSED(pageIndex);
//Creating dom structure to store information
QDomDocument groupDomDocument;
QDomElement groupRoot = groupDomDocument.createElement(tGroups);
groupDomDocument.appendChild(groupRoot);
QBuffer buffer;
buffer.open(QBuffer::WriteOnly);
mXmlWriter.setDevice(&buffer);
mXmlWriter.setAutoFormatting(true);
mXmlWriter.writeStartDocument();
mXmlWriter.writeDefaultNamespace(nsSvg);
mXmlWriter.writeNamespace(nsXLink, "xlink");
mXmlWriter.writeNamespace(UBSettings::uniboardDocumentNamespaceUri, "ub");
mXmlWriter.writeNamespace(nsXHtml, "xhtml");
writeSvgElement(proxy);
// Get the items from the scene
QList<QGraphicsItem*> items = mScene->items();
std::sort(items.begin(), items.end(), itemZIndexComp);
UBGraphicsStroke *openStroke = 0;
bool groupHoldsInfo = false;
while (!items.empty())
{
QGraphicsItem *item = items.takeFirst();
// Is the item a polygon?
UBGraphicsPolygonItem *polygonItem = qgraphicsitem_cast<UBGraphicsPolygonItem*> (item);
if (polygonItem && polygonItem->isVisible())
{
UBGraphicsStroke* currentStroke = polygonItem->stroke();
if (openStroke && (currentStroke != openStroke))
{
mXmlWriter.writeEndElement(); //g
openStroke = 0;
groupHoldsInfo = false;
}
bool firstPolygonInStroke = currentStroke && !openStroke;
if (firstPolygonInStroke)
{
mXmlWriter.writeStartElement("g");
openStroke = currentStroke;
UBGraphicsStroke* stroke = dynamic_cast<UBGraphicsStroke* >(currentStroke);
if (stroke)
{
QColor colorOnDarkBackground = polygonItem->colorOnDarkBackground();
QColor colorOnLightBackground = polygonItem->colorOnLightBackground();
UBGraphicsStrokesGroup * sg = polygonItem->strokesGroup();
if (colorOnDarkBackground.isValid() && colorOnLightBackground.isValid() && sg)
{
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "z-value"
, QString("%1").arg(polygonItem->strokesGroup()->zValue()));
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri
, "fill-on-dark-background", colorOnDarkBackground.name());
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri
, "fill-on-light-background", colorOnLightBackground.name());
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "uuid", UBStringUtils::toCanonicalUuid(sg->uuid()));
QVariant locked = sg->data(UBGraphicsItemData::ItemLocked);
if (!locked.isNull() && locked.toBool())
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "locked", xmlTrue);
QVariant hiddenOnDisplay = sg->data(UBGraphicsItemData::ItemIsHiddenOnDisplay);
if (!hiddenOnDisplay.isNull() && hiddenOnDisplay.toBool())
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "hidden-on-display", xmlTrue);
else
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "hidden-on-display", xmlFalse);
QVariant layer = sg->data(UBGraphicsItemData::ItemLayerType);
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "layer", QString("%1").arg(layer.toInt()));
QTransform matrix = sg->sceneTransform();
if (!matrix.isIdentity())
mXmlWriter.writeAttribute("transform", toSvgTransform(matrix));
qDebug() << "Attributes written";
groupHoldsInfo = true;
}
}
if (stroke && !stroke->hasPressure())
{
strokeToSvgPolyline(stroke, groupHoldsInfo);
//we can dequeue all polygons belonging to that stroke
foreach(UBGraphicsPolygonItem* gi, stroke->polygons())
{
items.removeOne(gi);
}
continue;
}
}
UBGraphicsStroke* stroke = dynamic_cast<UBGraphicsStroke* >(currentStroke);
if (stroke && stroke->hasPressure())
polygonItemToSvgPolygon(polygonItem, groupHoldsInfo);
else if (polygonItem->isNominalLine())
polygonItemToSvgLine(polygonItem, groupHoldsInfo);
continue;
}
if (openStroke)
{
mXmlWriter.writeEndElement(); //g
groupHoldsInfo = false;
openStroke = 0;
}
// Is the item a picture?
UBGraphicsPixmapItem *pixmapItem = qgraphicsitem_cast<UBGraphicsPixmapItem*> (item);
if (pixmapItem && pixmapItem->isVisible())
{
pixmapItemToLinkedImage(pixmapItem);
continue;
}
// Is the item a shape?
UBGraphicsSvgItem *svgItem = qgraphicsitem_cast<UBGraphicsSvgItem*> (item);
if (svgItem && svgItem->isVisible())
{
svgItemToLinkedSvg(svgItem);
continue;
}
UBGraphicsVideoItem * videoItem = qgraphicsitem_cast<UBGraphicsVideoItem*> (item);
if (videoItem && videoItem->isVisible()) {
videoItemToLinkedVideo(videoItem);
continue;
}
UBGraphicsAudioItem * audioItem = qgraphicsitem_cast<UBGraphicsAudioItem*> (item);
if (audioItem && audioItem->isVisible()) {
audioItemToLinkedAudio(audioItem);
continue;
}
// Is the item an app? // NOTE @letsfindaway obsolete
UBGraphicsAppleWidgetItem *appleWidgetItem = qgraphicsitem_cast<UBGraphicsAppleWidgetItem*> (item);
if (appleWidgetItem && appleWidgetItem->isVisible())
{
graphicsAppleWidgetToSvg(appleWidgetItem);
continue;
}
// Is the item a W3C?
UBGraphicsW3CWidgetItem *w3cWidgetItem = qgraphicsitem_cast<UBGraphicsW3CWidgetItem*> (item);
if (w3cWidgetItem && w3cWidgetItem->isVisible())
{
graphicsW3CWidgetToSvg(w3cWidgetItem);
continue;
}
// Is the item a PDF?
UBGraphicsPDFItem *pdfItem = qgraphicsitem_cast<UBGraphicsPDFItem*> (item);
if (pdfItem && pdfItem->isVisible())
{
pdfItemToLinkedPDF(pdfItem);
continue;
}
// Is the item a text?
UBGraphicsTextItem *textItem = qgraphicsitem_cast<UBGraphicsTextItem*> (item);
if (textItem && textItem->isVisible())
{
textItemToSvg(textItem);
continue;
}
// Is the item a curtain?
UBGraphicsCurtainItem *curtainItem = qgraphicsitem_cast<UBGraphicsCurtainItem*> (item);
if (curtainItem && curtainItem->isVisible())
{
curtainItemToSvg(curtainItem);
continue;
}
// Is the item a ruler?
UBGraphicsRuler *ruler = qgraphicsitem_cast<UBGraphicsRuler*> (item);
if (ruler && ruler->isVisible())
{
rulerToSvg(ruler);
continue;
}
// Is the item a axes?
UBGraphicsAxes *axes = qgraphicsitem_cast<UBGraphicsAxes*> (item);
if (axes && axes->isVisible())
{
axesToSvg(axes);
continue;
}
// Is the item a cache?
UBGraphicsCache* cache = qgraphicsitem_cast<UBGraphicsCache*>(item);
if(cache && cache->isVisible())
{
cacheToSvg(cache);
continue;
}
// Is the item a compass
UBGraphicsCompass *compass = qgraphicsitem_cast<UBGraphicsCompass*> (item);
if (compass && compass->isVisible())
{
compassToSvg(compass);
continue;
}
// Is the item a protractor?
UBGraphicsProtractor *protractor = qgraphicsitem_cast<UBGraphicsProtractor*> (item);
if (protractor && protractor->isVisible())
{
protractorToSvg(protractor);
continue;
}
// Is the item a triangle?
UBGraphicsTriangle *triangle = qgraphicsitem_cast<UBGraphicsTriangle*> (item);
if (triangle && triangle->isVisible())
{
triangleToSvg(triangle);
continue;
}
// Is the item a group?
UBGraphicsGroupContainerItem *groupItem = qgraphicsitem_cast<UBGraphicsGroupContainerItem*>(item);
if (groupItem && groupItem->isVisible())
{
persistGroupToDom(groupItem, &groupRoot, &groupDomDocument);
continue;
}
}
if (openStroke)
{
mXmlWriter.writeEndElement();
openStroke = 0;
}
//writing group data
if (groupRoot.hasChildNodes()) {
mXmlWriter.writeStartElement(tGroups);
QDomElement curElement = groupRoot.firstChildElement();
while (!curElement.isNull()) {
if (curElement.hasAttribute(aId)) {
mXmlWriter.writeStartElement(curElement.tagName());
mXmlWriter.writeAttribute(aId, curElement.attribute(aId));
if(curElement.hasAttribute("locked")){
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri,"locked",curElement.attribute("locked"));
}
if(curElement.hasAttribute("hidden-on-display")){
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri,"hidden-on-display", curElement.attribute("hidden-on-display"));
}
if(curElement.hasAttribute("layer")){
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri,"layer",curElement.attribute("layer"));
}
if(curElement.hasAttribute("z-value")){
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri,"z-value",curElement.attribute("z-value"));
}
QDomElement curSubElement = curElement.firstChildElement();
while (!curSubElement.isNull()) {
if (curSubElement.hasAttribute(aId)) {
mXmlWriter.writeStartElement(curSubElement.tagName());
mXmlWriter.writeAttribute(aId, curSubElement.attribute(aId));
mXmlWriter.writeEndElement();
curSubElement = curSubElement.nextSiblingElement();
}
}
mXmlWriter.writeEndElement();
}
curElement = curElement.nextSiblingElement();
}
mXmlWriter.writeEndElement();
}
mXmlWriter.writeEndDocument();
QString fileName = mDocumentPath + UBFileSystemUtils::digitFileFormat("/page%1.svg", mPageIndex);
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
{
qCritical() << "cannot open " << fileName << " for writing. Error : " << file.errorString();
return false;
}
file.write(buffer.data());
file.flush();
file.close();
return true;
}
void UBSvgSubsetAdaptor::UBSvgSubsetWriter::persistGroupToDom(QGraphicsItem *groupItem, QDomElement *curParent, QDomDocument *groupDomDocument)
{
QUuid uuid = UBGraphicsScene::getPersonalUuid(groupItem);
if (!uuid.isNull()) {
QDomElement curGroupElement = groupDomDocument->createElement(tGroup);
curGroupElement.setAttribute(aId, uuid.toString());
UBGraphicsGroupContainerItem* group = dynamic_cast<UBGraphicsGroupContainerItem*>(groupItem);
if(group && group->Delegate()){
if(group->Delegate()->isLocked())
curGroupElement.setAttribute("locked", xmlTrue);
else
curGroupElement.setAttribute("locked", xmlFalse);
if (group->data(UBGraphicsItemData::ItemIsHiddenOnDisplay).toBool())
curGroupElement.setAttribute("hidden-on-display", xmlTrue);
else
curGroupElement.setAttribute("hidden-on-display", xmlFalse);
curGroupElement.setAttribute("layer", group->data(UBGraphicsItemData::ItemLayerType).toString());
QString zs;
zs.setNum(group->zValue(), 'f'); // 'f' keeps precision
curGroupElement.setAttribute("z-value", zs);
}
curParent->appendChild(curGroupElement);
foreach (QGraphicsItem *item, groupItem->childItems()) {
QUuid tmpUuid = UBGraphicsScene::getPersonalUuid(item);
if (!tmpUuid.isNull()) {
if (item->type() == UBGraphicsGroupContainerItem::Type && item->childItems().count())
persistGroupToDom(item, curParent, groupDomDocument);
else {
QDomElement curSubElement = groupDomDocument->createElement(tElement);
curSubElement.setAttribute(aId, tmpUuid.toString());
curGroupElement.appendChild(curSubElement);
}
}
}
}
}
void UBSvgSubsetAdaptor::UBSvgSubsetWriter::polygonItemToSvgLine(UBGraphicsPolygonItem* polygonItem, bool groupHoldsInfo)
{
mXmlWriter.writeStartElement("line");
QLineF line = polygonItem->originalLine();
mXmlWriter.writeAttribute("x1", QString::number(line.p1().x(), 'f', 2));
mXmlWriter.writeAttribute("y1", QString::number(line.p1().y(), 'f', 2));
// SVG renderers (Chrome) do not like line where (x1, y1) == (x2, y2)
qreal x2 = line.p2().x();
if (line.p1() == line.p2())
x2 += 0.01;
mXmlWriter.writeAttribute("x2", QString::number(x2, 'f', 2));
mXmlWriter.writeAttribute("y2", QString::number(line.p2().y(), 'f', 2));
mXmlWriter.writeAttribute("stroke-width", QString::number(polygonItem->originalWidth(), 'f', -1));
mXmlWriter.writeAttribute("stroke", polygonItem->brush().color().name());
qreal alpha = polygonItem->brush().color().alphaF();
if (alpha < 1.0)
mXmlWriter.writeAttribute("stroke-opacity", QString::number(alpha, 'f', 2));
mXmlWriter.writeAttribute("stroke-linecap", "round");
if (!groupHoldsInfo)
{
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "z-value", QString("%1").arg(polygonItem->zValue()));
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "fill-on-dark-background", polygonItem->colorOnDarkBackground().name());
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "fill-on-light-background", polygonItem->colorOnLightBackground().name());
}
mXmlWriter.writeEndElement();
}
void UBSvgSubsetAdaptor::UBSvgSubsetWriter::strokeToSvgPolyline(UBGraphicsStroke* stroke, bool groupHoldsInfo)
{
QList<UBGraphicsPolygonItem*> pols = stroke->polygons();
if (pols.length() > 0)
{
mXmlWriter.writeStartElement("polyline");
QVector<QPointF> points;
foreach(UBGraphicsPolygonItem* polygon, pols)
{
points << polygon->originalLine().p1();
}
points << pols.last()->originalLine().p2();
// SVG renderers (Chrome) do not like line withe where x1/y1 == x2/y2
if (points.size() == 2 && (points.at(0) == points.at(1)))
{
points[1] = QPointF(points[1].x() + 0.01, points[1].y());
}
QString svgPoints = pointsToSvgPointsAttribute(points);
mXmlWriter.writeAttribute("points", svgPoints);
UBGraphicsPolygonItem* firstPolygonItem = pols.at(0);
mXmlWriter.writeAttribute("fill", "none");
mXmlWriter.writeAttribute("stroke-width", QString::number(firstPolygonItem->originalWidth(), 'f', 2));
mXmlWriter.writeAttribute("stroke", firstPolygonItem->brush().color().name());
mXmlWriter.writeAttribute("stroke-opacity", QString("%1").arg(firstPolygonItem->brush().color().alphaF()));
mXmlWriter.writeAttribute("stroke-linecap", "round");
if (!groupHoldsInfo)
{
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "z-value", QString("%1").arg(firstPolygonItem->zValue()));
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri
, "fill-on-dark-background", firstPolygonItem->colorOnDarkBackground().name());
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri
, "fill-on-light-background", firstPolygonItem->colorOnLightBackground().name());
}
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "uuid", UBStringUtils::toCanonicalUuid(firstPolygonItem->uuid()));
if (firstPolygonItem->parentItem()) {
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "parent", UBStringUtils::toCanonicalUuid(UBGraphicsItem::getOwnUuid(firstPolygonItem->strokesGroup())));
}
mXmlWriter.writeEndElement();
}
}
void UBSvgSubsetAdaptor::UBSvgSubsetWriter::strokeToSvgPolygon(UBGraphicsStroke* stroke, bool groupHoldsInfo)
{
QList<UBGraphicsPolygonItem*> pis = stroke->polygons();
if (pis.length() > 0)
{
QPolygonF united;
foreach(UBGraphicsPolygonItem* pi, pis)
{
united = united.united(pi->polygon());
}
QScopedPointer<UBGraphicsPolygonItem> clone(static_cast<UBGraphicsPolygonItem*>(pis.at(0)->deepCopy()));
clone->setPolygon(united);
polygonItemToSvgPolygon(clone.get(), groupHoldsInfo);
}
}
void UBSvgSubsetAdaptor::UBSvgSubsetWriter::polygonItemToSvgPolygon(UBGraphicsPolygonItem* polygonItem, bool groupHoldsInfo)
{
QPolygonF polygon = polygonItem->polygon();
int pointsCount = polygon.size();
if (pointsCount > 0)
{
mXmlWriter.writeStartElement("polygon");
QString points = pointsToSvgPointsAttribute(polygon);
mXmlWriter.writeAttribute("points", points);
mXmlWriter.writeAttribute("transform",toSvgTransform(polygonItem->transform()));
mXmlWriter.writeAttribute("fill", polygonItem->brush().color().name());
qreal alpha = polygonItem->brush().color().alphaF();
mXmlWriter.writeAttribute("fill-opacity", QString::number(alpha, 'f', 2));
// we trick SVG antialiasing, to avoid seeing light gaps between polygons
if (alpha < 1.0)
{
qreal trickedAlpha = trickAlpha(alpha);
mXmlWriter.writeAttribute("stroke", polygonItem->brush().color().name());
mXmlWriter.writeAttribute("stroke-width", "1");
mXmlWriter.writeAttribute("stroke-opacity", QString::number(trickedAlpha, 'f', 2));
}
// svg default fill rule is nonzero, but Qt is evenodd
//
//http://www.w3.org/TR/SVG11/painting.html
//http://doc.trolltech.com/4.5/qgraphicspolygonitem.html#fillRule
//
if (polygonItem->fillRule() == Qt::OddEvenFill)
mXmlWriter.writeAttribute("fill-rule", "evenodd");
else
mXmlWriter.writeAttribute("fill-rule", "winding");
if (!groupHoldsInfo)
{
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "z-value", QString("%1").arg(polygonItem->zValue()));
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri
, "fill-on-dark-background", polygonItem->colorOnDarkBackground().name());
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri
, "fill-on-light-background", polygonItem->colorOnLightBackground().name());
}
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "uuid", UBStringUtils::toCanonicalUuid(polygonItem->uuid()));
UBGraphicsStrokesGroup* sg = polygonItem->strokesGroup();
if (sg)
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "parent", UBStringUtils::toCanonicalUuid(sg->uuid()));
mXmlWriter.writeEndElement();
}
}
UBGraphicsPolygonItem* UBSvgSubsetAdaptor::UBSvgSubsetReader::polygonItemFromPolygonSvg(const QColor& pDefaultColor)
{
UBGraphicsPolygonItem* polygonItem = new UBGraphicsPolygonItem();
graphicsItemFromSvg(polygonItem);
auto svgPoints = mXmlReader.attributes().value("points");
QPolygonF polygon;
if (!svgPoints.isNull())
{
QStringList ts = svgPoints.toString().split(QLatin1Char(' '), UB::SplitBehavior::SkipEmptyParts);
foreach(const QString sPoint, ts)
{
QStringList sCoord = sPoint.split(QLatin1Char(','), UB::SplitBehavior::SkipEmptyParts);
if (sCoord.size() == 2)
{
QPointF point;
point.setX(sCoord.at(0).toFloat());
point.setY(sCoord.at(1).toFloat());
polygon << point;
}
else if (sCoord.size() == 4){
//This is the case on system were the "," is used to seperate decimal
QPointF point;
QString x = sCoord.at(0) + "." + sCoord.at(1);
QString y = sCoord.at(2) + "." + sCoord.at(3);
point.setX(x.toFloat());
point.setY(y.toFloat());
polygon << point;
}
else
{
qWarning() << "cannot make sense of a 'point' value" << sCoord;
}
}
}
else
{
qWarning() << "cannot make sense of 'points' value " << svgPoints.toString();
}
polygonItem->setPolygon(polygon);
auto svgFill = mXmlReader.attributes().value("fill");
QColor brushColor = pDefaultColor;
if (!svgFill.isNull())
#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0))
brushColor = QColor::fromString(svgFill.toString());
#else
brushColor.setNamedColor(svgFill.toString());
#endif
auto svgFillOpacity = mXmlReader.attributes().value("fill-opacity");
qreal opacity = 1.0;
if (!svgFillOpacity.isNull())
{
opacity = svgFillOpacity.toString().toFloat();
brushColor.setAlphaF(opacity);
}
polygonItem->setColor(brushColor);
auto ubFillOnDarkBackground = mXmlReader.attributes().value(mNamespaceUri, "fill-on-dark-background");
if (!ubFillOnDarkBackground.isNull())
{
#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0))
QColor color = QColor::fromString(ubFillOnDarkBackground.toString());
#else
QColor color;
color.setNamedColor(ubFillOnDarkBackground.toString());
#endif
if (!color.isValid())
color = Qt::white;
color.setAlphaF(opacity);
polygonItem->setColorOnDarkBackground(color);
}
else
{
QColor color = mGroupDarkBackgroundColor;
color.setAlphaF(opacity);
polygonItem->setColorOnDarkBackground(color);
}
auto ubFillOnLightBackground = mXmlReader.attributes().value(mNamespaceUri, "fill-on-light-background");
if (!ubFillOnLightBackground.isNull())
{
#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0))
QColor color = QColor::fromString(ubFillOnLightBackground.toString());
#else
QColor color;
color.setNamedColor(ubFillOnLightBackground.toString());
#endif
if (!color.isValid())
color = Qt::black;
color.setAlphaF(opacity);
polygonItem->setColorOnLightBackground(color);
}
else
{
QColor color = mGroupLightBackgroundColor;
color.setAlphaF(opacity);
polygonItem->setColorOnLightBackground(color);
}
// Before SaiCore v1.4, fill rule was only saved if it was "Even-odd". Therefore if no fill rule
// is specified, we assume that it should be Winding fill.
auto fillRule = mXmlReader.attributes().value("fill-rule");
if (!fillRule.isNull() && fillRule.toString() == "evenodd")
polygonItem->setFillRule(Qt::OddEvenFill);
else
polygonItem->setFillRule(Qt::WindingFill);
return polygonItem;
}
UBGraphicsPolygonItem* UBSvgSubsetAdaptor::UBSvgSubsetReader::polygonItemFromLineSvg(const QColor& pDefaultColor)
{
auto svgX1 = mXmlReader.attributes().value("x1");
auto svgY1 = mXmlReader.attributes().value("y1");
auto svgX2 = mXmlReader.attributes().value("x2");
auto svgY2 = mXmlReader.attributes().value("y2");
QLineF line;
if (!svgX1.isNull() && !svgY1.isNull() && !svgX2.isNull() && !svgY2.isNull())
{
qreal x1 = svgX1.toString().toFloat();
qreal y1 = svgY1.toString().toFloat();
qreal x2 = svgX2.toString().toFloat();
qreal y2 = svgY2.toString().toFloat();
line.setLine(x1, y1, x2, y2);
}
else
{
qWarning() << "cannot make sense of 'line' value";
return 0;
}
auto strokeWidth = mXmlReader.attributes().value("stroke-width");
qreal lineWidth = 1.;
if (!strokeWidth.isNull())
{
lineWidth = strokeWidth.toString().toFloat();
}
UBGraphicsPolygonItem* polygonItem = new UBGraphicsPolygonItem(line, lineWidth);
graphicsItemFromSvg(polygonItem);
auto svgStroke = mXmlReader.attributes().value("stroke");
QColor brushColor = pDefaultColor;
if (!svgStroke.isNull())
{
#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0))
brushColor = QColor::fromString(svgStroke.toString());
#else
brushColor.setNamedColor(svgStroke.toString());
#endif
}
auto svgStrokeOpacity = mXmlReader.attributes().value("stroke-opacity");
qreal opacity = 1.0;
if (!svgStrokeOpacity.isNull())
{
opacity = svgStrokeOpacity.toString().toFloat();
brushColor.setAlphaF(opacity);
}
polygonItem->setColor(brushColor);
auto ubFillOnDarkBackground = mXmlReader.attributes().value(mNamespaceUri, "fill-on-dark-background");
if (!ubFillOnDarkBackground.isNull())
{
#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0))
QColor color = QColor::fromString(ubFillOnDarkBackground.toString());
#else
QColor color;
color.setNamedColor(ubFillOnDarkBackground.toString());
#endif
if (!color.isValid())
color = Qt::white;
color.setAlphaF(opacity);
polygonItem->setColorOnDarkBackground(color);
}
else
{
QColor color = mGroupDarkBackgroundColor;
color.setAlphaF(opacity);
polygonItem->setColorOnDarkBackground(color);
}
auto ubFillOnLightBackground = mXmlReader.attributes().value(mNamespaceUri, "fill-on-light-background");
if (!ubFillOnLightBackground.isNull())
{
#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0))
QColor color = QColor::fromString(ubFillOnLightBackground.toString());
#else
QColor color;
color.setNamedColor(ubFillOnLightBackground.toString());
#endif
if (!color.isValid())
color = Qt::black;
color.setAlphaF(opacity);
polygonItem->setColorOnLightBackground(color);
}
else
{
QColor color = mGroupLightBackgroundColor;
color.setAlphaF(opacity);
polygonItem->setColorOnLightBackground(color);
}
return polygonItem;
}
QList<UBGraphicsPolygonItem*> UBSvgSubsetAdaptor::UBSvgSubsetReader::polygonItemsFromPolylineSvg(const QColor& pDefaultColor)
{
auto strokeWidth = mXmlReader.attributes().value("stroke-width");
qreal lineWidth = 1.;
if (!strokeWidth.isNull())
{
lineWidth = strokeWidth.toString().toFloat();
}
QColor brushColor = pDefaultColor;
auto svgStroke = mXmlReader.attributes().value("stroke");
if (!svgStroke.isNull())
{
#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0))
brushColor = QColor::fromString(svgStroke.toString());
#else
brushColor.setNamedColor(svgStroke.toString());
#endif
}
qreal opacity = 1.0;
auto svgStrokeOpacity = mXmlReader.attributes().value("stroke-opacity");
if (!svgStrokeOpacity.isNull())
{
opacity = svgStrokeOpacity.toString().toFloat();
brushColor.setAlphaF(opacity);
}
bool hasZValue;
qreal zValue = normalizedZValue(&hasZValue);
if (!hasZValue)
{
zValue = mGroupZIndex;
}
QColor colorOnDarkBackground = mGroupDarkBackgroundColor;
auto ubFillOnDarkBackground = mXmlReader.attributes().value(mNamespaceUri, "fill-on-dark-background");
if (!ubFillOnDarkBackground.isNull())
{
#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0))
colorOnDarkBackground = QColor::fromString(ubFillOnDarkBackground.toString());
#else
colorOnDarkBackground.setNamedColor(ubFillOnDarkBackground.toString());
#endif
}
if (!colorOnDarkBackground.isValid())
colorOnDarkBackground = Qt::white;
colorOnDarkBackground.setAlphaF(opacity);
QColor colorOnLightBackground = mGroupLightBackgroundColor;
auto ubFillOnLightBackground = mXmlReader.attributes().value(mNamespaceUri, "fill-on-light-background");
if (!ubFillOnLightBackground.isNull())
{
#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0))
colorOnLightBackground = QColor::fromString(ubFillOnLightBackground.toString());
#else
colorOnLightBackground.setNamedColor(ubFillOnLightBackground.toString());
#endif
}
if (!colorOnLightBackground.isValid())
colorOnLightBackground = Qt::black;
colorOnLightBackground.setAlphaF(opacity);
auto svgPoints = mXmlReader.attributes().value("points");
QList<UBGraphicsPolygonItem*> polygonItems;
if (!svgPoints.isNull())
{
QStringList ts = svgPoints.toString().split(QLatin1Char(' '),
UB::SplitBehavior::SkipEmptyParts);
QList<QPointF> points;
foreach(const QString sPoint, ts)
{
QStringList sCoord = sPoint.split(QLatin1Char(','), UB::SplitBehavior::SkipEmptyParts);
if (sCoord.size() == 2)
{
QPointF point;
point.setX(sCoord.at(0).toFloat());
point.setY(sCoord.at(1).toFloat());
points << point;
}
else if (sCoord.size() == 4){
//This is the case on system were the "," is used to seperate decimal
QPointF point;
QString x = sCoord.at(0) + "." + sCoord.at(1);
QString y = sCoord.at(2) + "." + sCoord.at(3);
point.setX(x.toFloat());
point.setY(y.toFloat());
points << point;
}
else
{
qWarning() << "cannot make sense of a 'point' value" << sCoord;
}
}
for (int i = 0; i < points.size() - 1; i++)
{
UBGraphicsPolygonItem* polygonItem = new UBGraphicsPolygonItem(QLineF(points.at(i), points.at(i + 1)), lineWidth);
polygonItem->setColor(brushColor);
UBGraphicsItem::assignZValue(polygonItem, zValue);
polygonItem->setColorOnDarkBackground(colorOnDarkBackground);
polygonItem->setColorOnLightBackground(colorOnLightBackground);
polygonItems <<polygonItem;
}
}
else
{
qWarning() << "cannot make sense of 'points' value " << svgPoints.toString();
}
return polygonItems;
}
void UBSvgSubsetAdaptor::UBSvgSubsetWriter::pixmapItemToLinkedImage(UBGraphicsPixmapItem* pixmapItem)
{
// find image file
QDir imageDir = mDocumentPath + "/" + UBPersistenceManager::imageDirectory;
QStringList imageFiles = imageDir.entryList({pixmapItem->uuid().toString() + ".*"});
if (imageFiles.size() >= 1)
{
mXmlWriter.writeStartElement("image");
QString fileName = UBPersistenceManager::imageDirectory + "/" + imageFiles.last();
mXmlWriter.writeAttribute(nsXLink, "href", fileName);
graphicsItemToSvg(pixmapItem);
mXmlWriter.writeEndElement();
}
}
UBGraphicsPixmapItem* UBSvgSubsetAdaptor::UBSvgSubsetReader::pixmapItemFromSvg()
{
UBGraphicsPixmapItem* pixmapItem = nullptr;
auto imageHref = mXmlReader.attributes().value(nsXLink, "href");
if (!imageHref.isNull())
{
pixmapItem = new UBGraphicsPixmapItem();
QString href = imageHref.toString();
QImageReader rdr(mDocumentPath + "/" + UBFileSystemUtils::normalizeFilePath(href));
rdr.setAutoTransform(true);
QImage img = rdr.read();
QPixmap pix = QPixmap::fromImage(img);
pixmapItem->setPixmap(pix);
graphicsItemFromSvg(pixmapItem);
}
else
{
qWarning() << "cannot make sense of image href value";
}
return pixmapItem;
}
void UBSvgSubsetAdaptor::UBSvgSubsetWriter::svgItemToLinkedSvg(UBGraphicsSvgItem* svgItem)
{
mXmlWriter.writeStartElement("image");
QString fileName = UBPersistenceManager::imageDirectory + "/" + svgItem->uuid().toString() + ".svg";
mXmlWriter.writeAttribute(nsXLink, "href", fileName);
graphicsItemToSvg(svgItem);
mXmlWriter.writeEndElement();
}
UBGraphicsSvgItem* UBSvgSubsetAdaptor::UBSvgSubsetReader::svgItemFromSvg()
{
UBGraphicsSvgItem* svgItem = 0;
auto imageHref = mXmlReader.attributes().value(nsXLink, "href");
if (!imageHref.isNull())
{
QString href = imageHref.toString();
svgItem = new UBGraphicsSvgItem(mDocumentPath + "/" + UBFileSystemUtils::normalizeFilePath(href));
}
else
{
qWarning() << "cannot make sens of image href value";
return 0;
}
graphicsItemFromSvg(svgItem);
return svgItem;
}
void UBSvgSubsetAdaptor::UBSvgSubsetWriter::pdfItemToLinkedPDF(UBGraphicsPDFItem* pdfItem)
{
mXmlWriter.writeStartElement("foreignObject");
mXmlWriter.writeAttribute("requiredExtensions", "http://ns.adobe.com/pdf/1.3/");
QString fileName = UBPersistenceManager::objectDirectory + "/" + pdfItem->fileUuid().toString() + ".pdf";
QString path = mDocumentPath + "/" + fileName;
if (!QFile::exists(path))
{
QDir dir;
dir.mkdir(mDocumentPath + "/" + UBPersistenceManager::objectDirectory);
QFile file(path);
if (!file.open(QIODevice::WriteOnly))
{
qWarning() << "cannot open file for writing embeded pdf content " << path;
return;
}
file.write(pdfItem->fileData());
file.close();
}
mXmlWriter.writeAttribute(nsXLink, "href", fileName + "#page=" + QString::number(pdfItem->pageNumber()));
graphicsItemToSvg(pdfItem);
mXmlWriter.writeEndElement();
}
UBGraphicsPDFItem* UBSvgSubsetAdaptor::UBSvgSubsetReader::pdfItemFromPDF()
{
UBGraphicsPDFItem* pdfItem = 0;
QString href = mXmlReader.attributes().value(nsXLink, "href").toString();
QStringList parts = href.split("#page=");
if (parts.count() != 2)
{
qWarning() << "invalid pdf href value" << href;
return 0;
}
QString pdfPath = parts[0];
QUuid uuid(QFileInfo(pdfPath).baseName());
int pageNumber = parts[1].toInt();
pdfItem = new UBGraphicsPDFItem(PDFRenderer::rendererForUuid(uuid, mDocumentPath + "/" + UBFileSystemUtils::normalizeFilePath(pdfPath)), pageNumber);
graphicsItemFromSvg(pdfItem);
return pdfItem;
}
void UBSvgSubsetAdaptor::UBSvgSubsetWriter::audioItemToLinkedAudio(UBGraphicsAudioItem *audioItem)
{
mXmlWriter.writeStartElement("audio");
graphicsItemToSvg(audioItem);
if (audioItem->playerState() == QMediaPlayer::PausedState &&
(audioItem->mediaDuration() - audioItem->mediaPosition()) > 0)
{
qint64 pos = audioItem->mediaPosition();
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "position", QString("%1").arg(pos));
}
QString audioFileHref = "audios/" + audioItem->mediaFileUrl().fileName();
mXmlWriter.writeAttribute(nsXLink, "href", audioFileHref);
mXmlWriter.writeEndElement();
}
void UBSvgSubsetAdaptor::UBSvgSubsetWriter::videoItemToLinkedVideo(UBGraphicsVideoItem* videoItem)
{
/* w3c sample
*
* <video xlink:href="noonoo.avi" volume=".8" type="video/x-msvideo"
* width="320" height="240" x="50" y="50" repeatCount="indefinite"/>
*
*/
mXmlWriter.writeStartElement("video");
graphicsItemToSvg(videoItem);
if (videoItem->playerState() == QMediaPlayer::PausedState &&
(videoItem->mediaDuration() - videoItem->mediaPosition()) > 0)
{
qint64 pos = videoItem->mediaPosition();
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "position", QString("%1").arg(pos));
}
QString videoFileHref = "videos/" + videoItem->mediaFileUrl().fileName();
mXmlWriter.writeAttribute(nsXLink, "href", videoFileHref);
mXmlWriter.writeEndElement();
}
UBGraphicsMediaItem* UBSvgSubsetAdaptor::UBSvgSubsetReader::audioItemFromSvg()
{
auto audioHref = mXmlReader.attributes().value(nsXLink, "href");
if (audioHref.isNull())
{
qWarning() << "cannot make sens of video href value";
return 0;
}
QString href = mDocumentPath + "/" + audioHref.toString();
//Claudio this is necessary to fix the absolute path added on Sankore 3.1 1.00.00
//The absoult path doesn't work when you want to share Sankore documents.
if(!audioHref.toString().startsWith("audios/")){
int indexOfAudioDirectory = href.lastIndexOf("audios");
href = mDocumentPath + "/" + href.right(href.length() - indexOfAudioDirectory);
}
UBGraphicsMediaItem* audioItem = UBGraphicsMediaItem::createMediaItem(QUrl::fromLocalFile(href));
if(audioItem)
{
audioItem->connect(UBApplication::boardController, SIGNAL(activeSceneChanged()), audioItem, SLOT(activeSceneChanged()));
graphicsItemFromSvg(audioItem);
auto ubPos = mXmlReader.attributes().value(mNamespaceUri, "position");
qint64 p = 0;
if (!ubPos.isNull())
p = ubPos.toString().toLongLong();
audioItem->setInitialPos(p);
}
return audioItem;
}
UBGraphicsMediaItem* UBSvgSubsetAdaptor::UBSvgSubsetReader::videoItemFromSvg()
{
auto videoHref = mXmlReader.attributes().value(nsXLink, "href");
if (videoHref.isNull())
{
qWarning() << "cannot make sens of video href value";
return 0;
}
QString href = mDocumentPath + "/" + videoHref.toString();
//Claudio this is necessary to fix the absolute path added on Sankore 3.1 1.00.00
//The absoult path doesn't work when you want to share Sankore documents.
if(!videoHref.toString().startsWith("videos/")){
int indexOfVideoDirectory = href.lastIndexOf("videos");
href = mDocumentPath + "/" + href.right(href.length() - indexOfVideoDirectory);
}
UBGraphicsMediaItem* videoItem = UBGraphicsMediaItem::createMediaItem(QUrl::fromLocalFile(href));
if(videoItem){
videoItem->connect(UBApplication::boardController, SIGNAL(activeSceneChanged()), videoItem, SLOT(activeSceneChanged()));
graphicsItemFromSvg(videoItem);
auto ubPos = mXmlReader.attributes().value(mNamespaceUri, "position");
qint64 p = 0;
if (!ubPos.isNull())
{
p = ubPos.toString().toLongLong();
}
videoItem->setInitialPos(p);
}
return videoItem;
}
void UBSvgSubsetAdaptor::UBSvgSubsetReader::graphicsItemFromSvg(QGraphicsItem* gItem)
{
auto svgTransform = mXmlReader.attributes().value("transform");
QTransform itemMatrix;
if (!svgTransform.isNull())
{
itemMatrix = fromSvgTransform(svgTransform.toString());
gItem->setTransform(itemMatrix);
}
auto svgX = mXmlReader.attributes().value("x");
auto svgY = mXmlReader.attributes().value("y");
if (mFileVersion >= 40202)
{
gItem->setPos(0,0);
}
else if (mFileVersion >= 40201)
{
if (!svgX.isNull() && !svgY.isNull())
{
gItem->setPos(svgX.toString().toFloat() * itemMatrix.m11(), svgY.toString().toFloat() * itemMatrix.m22());
}
}
else
{
if (!svgX.isNull() && !svgY.isNull())
{
#ifdef Q_OS_WIN
gItem->setPos(svgX.toString().toFloat(), svgY.toString().toFloat());
#endif
}
}
UBResizableGraphicsItem *rgi = dynamic_cast<UBResizableGraphicsItem*>(gItem);
if (rgi)
{
auto svgWidth = mXmlReader.attributes().value("width");
auto svgHeight = mXmlReader.attributes().value("height");
if (!svgWidth.isNull() && !svgHeight.isNull())
{
rgi->resize(svgWidth.toString().toFloat(), svgHeight.toString().toFloat());
}
}
bool hasZValue;
qreal zValue = normalizedZValue(&hasZValue);
if (hasZValue)
{
UBGraphicsItem::assignZValue(gItem, zValue);
}
UBItem* ubItem = dynamic_cast<UBItem*>(gItem);
if (ubItem)
{
auto ubUuid = mXmlReader.attributes().value(mNamespaceUri, "uuid");
if (!ubUuid.isNull())
ubItem->setUuid(QUuid(ubUuid.toString()));
else
ubItem->setUuid(QUuid::createUuid());
auto ubSource = mXmlReader.attributes().value(mNamespaceUri, "source");
if (!ubSource.isNull())
ubItem->setSourceUrl(QUrl(ubSource.toString()));
}
auto ubLocked = mXmlReader.attributes().value(mNamespaceUri, "locked");
if (!ubLocked.isNull())
{
bool isLocked = (ubLocked.toString() == xmlTrue || ubLocked.toString() == "1");
gItem->setData(UBGraphicsItemData::ItemLocked, QVariant(isLocked));
}
auto ubHiddenOnDisplay = mXmlReader.attributes().value(mNamespaceUri, "hidden-on-display");
if (!ubHiddenOnDisplay.isNull())
{
bool isHiddenOnDisplay = (ubHiddenOnDisplay.toString() == xmlTrue || ubHiddenOnDisplay.toString() == "1");
gItem->setData(UBGraphicsItemData::ItemIsHiddenOnDisplay, QVariant(isHiddenOnDisplay));
}
auto ubEditable = mXmlReader.attributes().value(mNamespaceUri, "editable");
if (!ubEditable.isNull())
{
bool isEditable = (ubEditable.toString() == xmlTrue || ubEditable.toString() == "1");
gItem->setData(UBGraphicsItemData::ItemEditable, QVariant(isEditable));
}
//deprecated as of 4.4.a.12
auto ubLayer = mXmlReader.attributes().value(mNamespaceUri, "layer");
if (!ubLayer.isNull())
{
bool ok;
int layerAsInt = ubLayer.toString().toInt(&ok);
if (ok)
gItem->setData(UBGraphicsItemData::ItemLayerType, QVariant(layerAsInt));
}
}
qreal UBSvgSubsetAdaptor::UBSvgSubsetReader::normalizedZValue(bool* hasValue)
{
auto ubZValue = mXmlReader.attributes().value(mNamespaceUri, "z-value");
qreal zValue = 0;
if (!ubZValue.isNull())
{
zValue = ubZValue.toFloat(hasValue);
// FIX: In the first zvalue implemenations values outside of the boudaries have been used.
// No boundaries specified on documentation but too small values are not correctly handled.
// Note: zValue could even be -inf, so we're better off with some arbitrary value instead
// of doing any calculations.
if (zValue < -999999)
{
zValue = -999999;
}
}
else
{
*hasValue = false;
}
return zValue;
}
void UBSvgSubsetAdaptor::UBSvgSubsetWriter::graphicsItemToSvg(QGraphicsItem* item)
{
mXmlWriter.writeAttribute("x", "0");
mXmlWriter.writeAttribute("y", "0");
QRectF rect = item->boundingRect();
QAbstractGraphicsShapeItem* shapeItem = dynamic_cast<QAbstractGraphicsShapeItem*>(item);
if (shapeItem && shapeItem->pen().style() != Qt::NoPen)
{
qreal margin = shapeItem->pen().widthF() / 2.f;
rect -= QMarginsF(margin, margin, margin, margin);
}
mXmlWriter.writeAttribute("width", QString("%1").arg(rect.width()));
mXmlWriter.writeAttribute("height", QString("%1").arg(rect.height()));
mXmlWriter.writeAttribute("transform", toSvgTransform(item->sceneTransform()));
QString zs;
zs.setNum(item->zValue(), 'f'); // 'f' keeps precision
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "z-value", zs);
bool isBackground = mScene->isBackgroundObject(item);
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "background", isBackground ? xmlTrue : xmlFalse);
UBItem* ubItem = dynamic_cast<UBItem*>(item);
if (ubItem)
{
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "uuid", UBStringUtils::toCanonicalUuid(ubItem->uuid()));
QUrl sourceUrl = ubItem->sourceUrl();
if (!sourceUrl.isEmpty())
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "source", sourceUrl.path());
}
QVariant layer = item->data(UBGraphicsItemData::ItemLayerType);
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "layer", QString("%1").arg(layer.toInt()));
QVariant locked = item->data(UBGraphicsItemData::ItemLocked);
if (!locked.isNull() && locked.toBool())
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "locked", xmlTrue);
QVariant hiddenOnDisplay = item->data(UBGraphicsItemData::ItemIsHiddenOnDisplay);
if (!hiddenOnDisplay.isNull() && hiddenOnDisplay.toBool())
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "hidden-on-display", xmlTrue);
else
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "hidden-on-display", xmlFalse);
QVariant editable = item->data(UBGraphicsItemData::ItemEditable);
if (!editable.isNull()) {
if (editable.toBool())
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "editable", xmlTrue);
else
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "editable", xmlFalse);
}
}
// NOTE @letsfindaway obsolete
void UBSvgSubsetAdaptor::UBSvgSubsetWriter::graphicsAppleWidgetToSvg(UBGraphicsAppleWidgetItem* item)
{
graphicsWidgetToSvg(item);
}
void UBSvgSubsetAdaptor::UBSvgSubsetWriter::graphicsW3CWidgetToSvg(UBGraphicsW3CWidgetItem* item)
{
graphicsWidgetToSvg(item);
}
void UBSvgSubsetAdaptor::UBSvgSubsetWriter::graphicsWidgetToSvg(UBGraphicsWidgetItem* item)
{
QUrl widgetRootUrl = item->widgetUrl();
QString widgetDirectoryPath = UBPersistenceManager::widgetDirectory;
if (widgetRootUrl.toString().startsWith("file://"))
{
QString widgetRootDir = widgetRootUrl.toLocalFile();
QFileInfo fi(widgetRootDir);
QString extension = fi.suffix();
QString widgetTargetDir = widgetDirectoryPath + "/" + item->uuid().toString() + "." + extension;
QString path = mDocumentPath + "/" + widgetTargetDir;
QDir dir(path);
if (!dir.exists(path))
{
QDir dir;
dir.mkpath(path);
UBFileSystemUtils::copyDir(widgetRootDir, path);
}
// save snapshot of widget
item->saveSnapshot();
widgetRootUrl = widgetTargetDir;
}
QString widgetPath = "widgets/" + widgetRootUrl.fileName();
mXmlWriter.writeStartElement("foreignObject");
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "src", widgetPath);
graphicsItemToSvg(item);
if (item->isFrozen())
{
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "frozen", xmlTrue);
}
mXmlWriter.writeStartElement(nsXHtml, "iframe");
mXmlWriter.writeAttribute("style", "border: none");
QRectF rect = item->boundingRect() - QMarginsF(0.5, 0.5, 0.5, 0.5);
mXmlWriter.writeAttribute("width", QString("%1").arg(rect.width()));
mXmlWriter.writeAttribute("height", QString("%1").arg(rect.height()));
QString startFileUrl;
if (item->mainHtmlFileName().startsWith("http://"))
startFileUrl = item->mainHtmlFileName();
else
startFileUrl = widgetRootUrl.toString() + "/" + item->mainHtmlFileName();
startFileUrl = QUrl::fromPercentEncoding(startFileUrl.toUtf8());
mXmlWriter.writeAttribute("src", startFileUrl);
mXmlWriter.writeEndElement(); //iFrame
//persists widget state
QMap<QString, QString> preferences = item->preferences();
foreach(QString key, preferences.keys())
{
QString value = preferences.value(key);
mXmlWriter.writeStartElement(UBSettings::uniboardDocumentNamespaceUri, "preference");
mXmlWriter.writeAttribute("key", key);
mXmlWriter.writeAttribute("value", value);
mXmlWriter.writeEndElement(); //ub::preference
}
//persists datastore state
QMap<QString, QString> datastore = item->datastoreEntries();
foreach(QString key, datastore.keys())
{
QString value = datastore.value(key);
mXmlWriter.writeStartElement(UBSettings::uniboardDocumentNamespaceUri, "datastoreEntry");
mXmlWriter.writeAttribute("key", key);
mXmlWriter.writeAttribute("value", value);
mXmlWriter.writeEndElement(); //ub::datastoreEntry
}
mXmlWriter.writeEndElement();
}
// NOTE @letsfindaway obsolete
UBGraphicsAppleWidgetItem* UBSvgSubsetAdaptor::UBSvgSubsetReader::graphicsAppleWidgetFromSvg()
{
auto widgetUrl = mXmlReader.attributes().value(mNamespaceUri, "src");
if (widgetUrl.isNull())
{
qWarning() << "cannot make sens of widget src value";
return 0;
}
QString href = widgetUrl.toString();
QUrl url(href);
if (url.isRelative())
{
href = mDocumentPath + "/" + UBFileSystemUtils::normalizeFilePath(widgetUrl.toString());
}
UBGraphicsAppleWidgetItem* widgetItem = new UBGraphicsAppleWidgetItem(QUrl::fromLocalFile(href));
graphicsItemFromSvg(widgetItem);
return widgetItem;
}
UBGraphicsW3CWidgetItem* UBSvgSubsetAdaptor::UBSvgSubsetReader::graphicsW3CWidgetFromSvg()
{
auto widgetUrl = mXmlReader.attributes().value(mNamespaceUri, "src");
if (widgetUrl.isNull())
{
qWarning() << "cannot make sense of widget src value";
return 0;
}
QString href = widgetUrl.toString();
QUrl url(href);
if (url.isRelative())
{
href = mDocumentPath + "/" + UBFileSystemUtils::normalizeFilePath(widgetUrl.toString());
}
UBGraphicsW3CWidgetItem* widgetItem = new UBGraphicsW3CWidgetItem(QUrl::fromLocalFile(href));
widgetItem->setWebActive(false);
auto uuid = mXmlReader.attributes().value(mNamespaceUri, "uuid");
QString pixPath = mDocumentPath + "/" + UBPersistenceManager::widgetDirectory + "/" + uuid.toString() + ".png";
widgetItem->setSnapshotPath(QUrl::fromLocalFile(pixPath));
QPixmap snapshot(pixPath);
auto frozen = mXmlReader.attributes().value(mNamespaceUri, "frozen");
if (!snapshot.isNull())
{
widgetItem->setSnapshot(snapshot, !frozen.isNull() && frozen.toString() == xmlTrue);
}
graphicsItemFromSvg(widgetItem);
return widgetItem;
}
void UBSvgSubsetAdaptor::UBSvgSubsetWriter::textItemToSvg(UBGraphicsTextItem* item)
{
/**
* sample
*
<foreignObject x="0" y="0" width="489.297" height="76.3303" transform="matrix(0.508456, -0.861088, 0.861088, 0.508456, -181.721, 243.124)" ub:z-value="-9999998.000000" ub:background="false" ub:fill-on-dark-background="#81ff5c" ub:fill-on-light-background="#008000">
<xhtml:body>
<xhtml:div>
<xhtml:font face="Arial" style="font-size:48px;" color="#81ff5c">this a text<br/>on 2 lines</xhtml:font>
</xhtml:div>
</xhtml:body>
</foreignObject>
*/
mXmlWriter.writeStartElement("foreignObject");
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "type", "text");
graphicsItemToSvg(item);
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "width", QString("%1").arg(item->textWidth()));
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "height", QString("%1").arg(item->textHeight()));
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "pixels-per-point", QString("%1").arg(item->pixelsPerPoint()));
QColor colorDarkBg = item->colorOnDarkBackground();
QColor colorLightBg = item->colorOnLightBackground();
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri
, "fill-on-dark-background", colorDarkBg.name());
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri
, "fill-on-light-background", colorLightBg.name());
//for new documents from version 4.5.0
mXmlWriter.writeStartElement("itemTextContent");
// Note: don't use mXmlWriter.writeCDATA(htmlString); because it doesn't escape characters sequences correctly.
// Texts copied from other programs like Open-Office can truncate the svg file.
//mXmlWriter.writeCharacters(item->toHtml());
mXmlWriter.writeCharacters(UBTextTools::cleanHtmlCData(item->toHtml()));
mXmlWriter.writeEndElement(); //itemTextContent
mXmlWriter.writeEndElement(); //foreignObject
}
UBGraphicsTextItem* UBSvgSubsetAdaptor::UBSvgSubsetReader::textItemFromSvg()
{
qreal width = mXmlReader.attributes().value("width").toString().toFloat();
qreal height = mXmlReader.attributes().value("height").toString().toFloat();
qreal originalPixelsPerPoint = mXmlReader.attributes().value(mNamespaceUri, "pixels-per-point").toString().toDouble();
UBGraphicsTextItem* textItem = new UBGraphicsTextItem();
graphicsItemFromSvg(textItem);
auto ubFillOnDarkBackground = mXmlReader.attributes().value(mNamespaceUri, "fill-on-dark-background");
auto ubFillOnLightBackground = mXmlReader.attributes().value(mNamespaceUri, "fill-on-light-background");
if (!ubFillOnDarkBackground.isNull()) {
#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0))
QColor color = QColor::fromString(ubFillOnDarkBackground.toString());
#else
QColor color;
color.setNamedColor(ubFillOnDarkBackground.toString());
#endif
if (!color.isValid())
color = Qt::white;
textItem->setColorOnDarkBackground(color);
}
if (!ubFillOnLightBackground.isNull()) {
#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0))
QColor color = QColor::fromString(ubFillOnLightBackground.toString());
#else
QColor color;
color.setNamedColor(ubFillOnLightBackground.toString());
#endif
if (!color.isValid())
color = Qt::black;
textItem->setColorOnLightBackground(color);
}
QString text;
QString name;
while (!(mXmlReader.isEndElement() && ((name = mXmlReader.name().toString()) == "font" || name == "foreignObject")))
{
if (mXmlReader.hasError())
{
delete textItem;
textItem = 0;
return 0;
}
mXmlReader.readNext();
if (mXmlReader.isStartElement())
{
//for new documents from version 4.5.0
if (mFileVersion >= 40500) {
if (mXmlReader.name().toString() == "itemTextContent") {
text = mXmlReader.readElementText();
textItem->setHtml(text);
// Fonts sizes are not displayed the same across platforms: e.g a text item with the same
// font size (in Pts) is displayed smaller on Linux than Windows. This messes up layouts
// when importing documents created on another computer, so if a font is being displayed
// at a different size (relative to the rest of the document) than it was when created,
// we adjust its size.
if (originalPixelsPerPoint != 0) {
qreal pixelsPerPoint = textItem->pixelsPerPoint();
qDebug() << "Pixels per point: original/current" << originalPixelsPerPoint
<< "/" << pixelsPerPoint;
qreal ratio = originalPixelsPerPoint/pixelsPerPoint;
if (ratio != 1) {
qDebug() << "Scaling text by " << ratio;
UBGraphicsTextItemDelegate* textDelegate = dynamic_cast<UBGraphicsTextItemDelegate*>(textItem->Delegate());
if (textDelegate)
textDelegate->scaleTextSize(ratio);
}
}
else
// mark scene as modified so the text item will be saved with a pixelsPerPoint value
saveSceneAfterLoading = true;
textItem->resize(width, height);
if (textItem->toPlainText().isEmpty()) {
delete textItem;
textItem = 0;
}
return textItem;
}
//tracking for backward capability with older versions
} else if (mXmlReader.name().toString() == "font") {
QFont font = textItem->font();
auto fontFamily = mXmlReader.attributes().value("face");
if (!fontFamily.isNull()) {
font.setFamily(fontFamily.toString());
}
auto fontStyle = mXmlReader.attributes().value("style");
if (!fontStyle.isNull()) {
foreach (QString styleToken, fontStyle.toString().split(";")) {
styleToken = styleToken.trimmed();
if (styleToken.startsWith(sFontSizePrefix) && styleToken.endsWith(sPixelUnit)) {
int fontSize = styleToken.mid(
sFontSizePrefix.length(),
styleToken.length() - sFontSizePrefix.length() - sPixelUnit.length()).toInt();
font.setPixelSize(fontSize);
} else if (styleToken.startsWith(sFontWeightPrefix)) {
QString fontWeight = styleToken.mid(
sFontWeightPrefix.length(),
styleToken.length() - sFontWeightPrefix.length());
font.setBold(fontWeight.contains("bold"));
} else if (styleToken.startsWith(sFontStylePrefix)) {
QString fontStyle = styleToken.mid(
sFontStylePrefix.length(),
styleToken.length() - sFontStylePrefix.length());
font.setItalic(fontStyle.contains("italic"));
}
}
}
QTextCursor curCursor = textItem->textCursor();
QTextCharFormat format;
format.setFont(font);
curCursor.mergeCharFormat(format);
textItem->setTextCursor(curCursor);
textItem->setFont(font);
auto fill = mXmlReader.attributes().value("color");
if (!fill.isNull()) {
#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0))
QColor textColor = QColor::fromString(fill.toString());
#else
QColor textColor;
textColor.setNamedColor(fill.toString());
#endif
textItem->setDefaultTextColor(textColor);
}
while (!(mXmlReader.isEndElement() && mXmlReader.name().toString() == "font")) {
if (mXmlReader.hasError()) {
break;
}
QXmlStreamReader::TokenType tt = mXmlReader.readNext();
if (tt == QXmlStreamReader::Characters) {
text += mXmlReader.text().toString();
}
if (mXmlReader.isStartElement() && mXmlReader.name().toString() == "br") {
text += "\n";
}
}
}
}
}
if (text.isEmpty()) {
delete textItem;
textItem = 0;
} else {
textItem->setPlainText(text);
textItem->resize(width, height);
}
return textItem;
}
void UBSvgSubsetAdaptor::UBSvgSubsetWriter::curtainItemToSvg(UBGraphicsCurtainItem* curtainItem)
{
/**
*
* sample
*
<ub:curtain x="250" y="150" width="122" height="67"...>
</ub:curtain>
*/
mXmlWriter.writeStartElement(UBSettings::uniboardDocumentNamespaceUri, "curtain");
QRectF rect = curtainItem->boundingRect() - QMarginsF(0.5, 0.5, 0.5, 0.5);
mXmlWriter.writeAttribute("x", QString("%1").arg(rect.center().x()));
mXmlWriter.writeAttribute("y", QString("%1").arg(rect.center().y()));
mXmlWriter.writeAttribute("width", QString("%1").arg(rect.width()));
mXmlWriter.writeAttribute("height", QString("%1").arg(rect.height()));
mXmlWriter.writeAttribute("transform", toSvgTransform(curtainItem->sceneTransform()));
//graphicsItemToSvg(curtainItem);
QString zs;
zs.setNum(curtainItem->zValue(), 'f'); // 'f' keeps precision
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "z-value", zs);
UBItem* ubItem = dynamic_cast<UBItem*>(curtainItem);
if (ubItem)
{
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "uuid", UBStringUtils::toCanonicalUuid(ubItem->uuid()));
}
mXmlWriter.writeEndElement();
}
UBGraphicsCurtainItem* UBSvgSubsetAdaptor::UBSvgSubsetReader::curtainItemFromSvg()
{
UBGraphicsCurtainItem* curtainItem = new UBGraphicsCurtainItem();
graphicsItemFromSvg(curtainItem);
auto svgX = mXmlReader.attributes().value("x");
auto svgY = mXmlReader.attributes().value("y");
auto svgWidth = mXmlReader.attributes().value("width");
auto svgHeight = mXmlReader.attributes().value("height");
QRect rect;
rect.setX(svgX.toString().toFloat()-svgWidth.toString().toFloat()/2);
rect.setY(svgY.toString().toFloat()-svgHeight.toString().toFloat()/2);
rect.setWidth(svgWidth.toString().toFloat());
rect.setHeight(svgHeight.toString().toFloat());
curtainItem->setRect(rect);
curtainItem->setVisible(true);
return curtainItem;
}
void UBSvgSubsetAdaptor::UBSvgSubsetWriter::rulerToSvg(UBGraphicsRuler* item)
{
/**
*
* sample
*
<ub:ruler x="250" y="150" width="122" height="67"...>
</ub:ruler>
*/
mXmlWriter.writeStartElement(UBSettings::uniboardDocumentNamespaceUri, "ruler");
QRectF rect = item->boundingRect() - QMarginsF(0.5, 0.5, 0.5, 0.5);
mXmlWriter.writeAttribute("x", QString("%1").arg(rect.x()));
mXmlWriter.writeAttribute("y", QString("%1").arg(rect.y()));
mXmlWriter.writeAttribute("width", QString("%1").arg(rect.width()));
mXmlWriter.writeAttribute("height", QString("%1").arg(rect.height()));
mXmlWriter.writeAttribute("transform", toSvgTransform(item->sceneTransform()));
QString zs;
zs.setNum(item->zValue(), 'f'); // 'f' keeps precision
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "z-value", zs);
UBItem* ubItem = dynamic_cast<UBItem*>(item);
if (ubItem)
{
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "uuid", UBStringUtils::toCanonicalUuid(ubItem->uuid()));
}
mXmlWriter.writeEndElement();
}
void UBSvgSubsetAdaptor::UBSvgSubsetWriter::axesToSvg(UBGraphicsAxes *item)
{
/**
*
* sample
*
<ub:axes x="250" y="150" left="65" top="28" width="122" height="67" numbers="1"...>
</ub:ruler>
*/
mXmlWriter.writeStartElement(UBSettings::uniboardDocumentNamespaceUri, "axes");
mXmlWriter.writeAttribute("x", QString("%1").arg(item->pos().x()));
mXmlWriter.writeAttribute("y", QString("%1").arg(item->pos().y()));
mXmlWriter.writeAttribute("left", QString("%1").arg(item->bounds().left()));
mXmlWriter.writeAttribute("top", QString("%1").arg(item->bounds().top()));
mXmlWriter.writeAttribute("width", QString("%1").arg(item->bounds().width()));
mXmlWriter.writeAttribute("height", QString("%1").arg(item->bounds().height()));
mXmlWriter.writeAttribute("numbers", QString("%1").arg(item->showNumbes()));
QString zs;
zs.setNum(item->zValue(), 'f'); // 'f' keeps precision
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "z-value", zs);
UBItem* ubItem = dynamic_cast<UBItem*>(item);
if (ubItem)
{
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "uuid", UBStringUtils::toCanonicalUuid(ubItem->uuid()));
}
mXmlWriter.writeEndElement();
}
UBGraphicsRuler* UBSvgSubsetAdaptor::UBSvgSubsetReader::rulerFromSvg()
{
UBGraphicsRuler* ruler = new UBGraphicsRuler();
graphicsItemFromSvg(ruler);
ruler->setData(UBGraphicsItemData::ItemLayerType, QVariant(UBItemLayerType::Tool));
auto svgWidth = mXmlReader.attributes().value("width");
auto svgHeight = mXmlReader.attributes().value("height");
auto svgX = mXmlReader.attributes().value("x");
auto svgY = mXmlReader.attributes().value("y");
if (!svgWidth.isNull() && !svgHeight.isNull() && !svgX.isNull() && !svgY.isNull())
{
ruler->setRect(svgX.toString().toFloat(), svgY.toString().toFloat(), svgWidth.toString().toFloat(), svgHeight.toString().toFloat());
}
ruler->setVisible(true);
return ruler;
}
UBGraphicsAxes *UBSvgSubsetAdaptor::UBSvgSubsetReader::axesFromSvg()
{
UBGraphicsAxes* axes = new UBGraphicsAxes();
graphicsItemFromSvg(axes);
axes->setData(UBGraphicsItemData::ItemLayerType, QVariant(UBItemLayerType::Tool));
auto svgX = mXmlReader.attributes().value("x");
auto svgY = mXmlReader.attributes().value("y");
auto svgLeft = mXmlReader.attributes().value("left");
auto svgTop = mXmlReader.attributes().value("top");
auto svgWidth = mXmlReader.attributes().value("width");
auto svgHeight = mXmlReader.attributes().value("height");
auto svgNumbers = mXmlReader.attributes().value("numbers");
if (!svgX.isNull() && !svgY.isNull())
{
axes->setPos(svgX.toString().toFloat(), svgY.toString().toFloat());
}
if (!svgWidth.isNull() && !svgHeight.isNull() && !svgLeft.isNull() && !svgTop.isNull())
{
axes->setRect(svgLeft.toString().toFloat(), svgTop.toString().toFloat(),
svgWidth.toString().toFloat(), svgHeight.toString().toFloat());
}
if (!svgNumbers.isNull())
{
axes->setShowNumbers(svgNumbers.toInt());
}
axes->setVisible(true);
return axes;
}
void UBSvgSubsetAdaptor::UBSvgSubsetWriter::compassToSvg(UBGraphicsCompass* item)
{
/**
*
* sample
*
<ub:compass x="250" y="150" width="122" height="67"...>
</ub:compass>
*/
mXmlWriter.writeStartElement(UBSettings::uniboardDocumentNamespaceUri, "compass");
QRectF rect = item->boundingRect() - QMarginsF(0.5, 0.5, 0.5, 0.5);
mXmlWriter.writeAttribute("x", QString("%1").arg(rect.x()));
mXmlWriter.writeAttribute("y", QString("%1").arg(rect.y()));
mXmlWriter.writeAttribute("width", QString("%1").arg(rect.width()));
mXmlWriter.writeAttribute("height", QString("%1").arg(rect.height()));
mXmlWriter.writeAttribute("transform", toSvgTransform(item->sceneTransform()));
QString zs;
zs.setNum(item->zValue(), 'f'); // 'f' keeps precision
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "z-value", zs);
UBItem* ubItem = dynamic_cast<UBItem*>(item);
if (ubItem)
{
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "uuid", UBStringUtils::toCanonicalUuid(ubItem->uuid()));
}
mXmlWriter.writeEndElement();
}
UBGraphicsCompass* UBSvgSubsetAdaptor::UBSvgSubsetReader::compassFromSvg()
{
UBGraphicsCompass* compass = new UBGraphicsCompass();
graphicsItemFromSvg(compass);
compass->setData(UBGraphicsItemData::ItemLayerType, QVariant(UBItemLayerType::Tool));
auto svgX = mXmlReader.attributes().value("x");
auto svgY = mXmlReader.attributes().value("y");
auto svgWidth = mXmlReader.attributes().value("width");
auto svgHeight = mXmlReader.attributes().value("height");
if (!svgX.isNull() && !svgY.isNull() && !svgWidth.isNull() && !svgHeight.isNull())
{
compass->setRect(svgX.toString().toFloat(), svgY.toString().toFloat()
, svgWidth.toString().toFloat(), svgHeight.toString().toFloat());
}
compass->setVisible(true);
return compass;
}
void UBSvgSubsetAdaptor::UBSvgSubsetWriter::protractorToSvg(UBGraphicsProtractor* item)
{
/**
*
* sample
*
<ub:protractor x="250" y="150" width="122" height="67"...>
</ub:protractor>
*/
mXmlWriter.writeStartElement(UBSettings::uniboardDocumentNamespaceUri, "protractor");
mXmlWriter.writeAttribute("x", QString("%1").arg(item->rect().x()));
mXmlWriter.writeAttribute("y", QString("%1").arg(item->rect().y()));
mXmlWriter.writeAttribute("width", QString("%1").arg(item->rect().width()));
mXmlWriter.writeAttribute("height", QString("%1").arg(item->rect().height()));
mXmlWriter.writeAttribute("transform", toSvgTransform(item->sceneTransform()));
QString angle;
angle.setNum(item->angle(), 'f'); // 'f' keeps precision
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "angle", angle);
angle.setNum(item->markerAngle(), 'f'); // 'f' keeps precision
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "marker-angle", angle);
QString zs;
zs.setNum(item->zValue(), 'f'); // 'f' keeps precision
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "z-value", zs);
UBItem* ubItem = dynamic_cast<UBItem*>(item);
if (ubItem)
{
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "uuid", UBStringUtils::toCanonicalUuid(ubItem->uuid()));
}
mXmlWriter.writeEndElement();
}
UBGraphicsProtractor* UBSvgSubsetAdaptor::UBSvgSubsetReader::protractorFromSvg()
{
UBGraphicsProtractor* protractor = new UBGraphicsProtractor();
protractor->setData(UBGraphicsItemData::ItemLayerType, QVariant(UBItemLayerType::Tool));
graphicsItemFromSvg(protractor);
auto angle = mXmlReader.attributes().value(mNamespaceUri, "angle");
if (!angle.isNull())
{
protractor->setAngle(angle.toString().toFloat());
}
auto markerAngle = mXmlReader.attributes().value(mNamespaceUri, "marker-angle");
if (!markerAngle.isNull())
{
protractor->setMarkerAngle(markerAngle.toString().toFloat());
}
auto svgX = mXmlReader.attributes().value("x");
auto svgY = mXmlReader.attributes().value("y");
auto svgWidth = mXmlReader.attributes().value("width");
auto svgHeight = mXmlReader.attributes().value("height");
if (!svgX.isNull() && !svgY.isNull() && !svgWidth.isNull() && !svgHeight.isNull())
{
protractor->setRect(svgX.toString().toFloat(), svgY.toString().toFloat()
, svgWidth.toString().toFloat(), svgHeight.toString().toFloat());
}
protractor->setVisible(true);
return protractor;
}
void UBSvgSubsetAdaptor::UBSvgSubsetWriter::triangleToSvg(UBGraphicsTriangle *item)
{
/**
*
* sample
*
<ub:triangle x="250" y="150" width="122" height="67"...>
</ub:triangle>
*/
mXmlWriter.writeStartElement(UBSettings::uniboardDocumentNamespaceUri, "triangle");
QRectF rect = item->boundingRect() - QMarginsF(0.5, 0.5, 0.5, 0.5);
mXmlWriter.writeAttribute("x", QString("%1").arg(rect.x()));
mXmlWriter.writeAttribute("y", QString("%1").arg(rect.y()));
mXmlWriter.writeAttribute("width", QString("%1").arg(rect.width()));
mXmlWriter.writeAttribute("height", QString("%1").arg(rect.height()));
mXmlWriter.writeAttribute("transform", toSvgTransform(item->sceneTransform()));
mXmlWriter.writeAttribute("orientation", UBGraphicsTriangle::orientationToStr(item->getOrientation()));
QString zs;
zs.setNum(item->zValue(), 'f'); // 'f' keeps precision
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "z-value", zs);
UBItem* ubItem = dynamic_cast<UBItem*>(item);
if (ubItem)
{
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "uuid", UBStringUtils::toCanonicalUuid(ubItem->uuid()));
}
mXmlWriter.writeEndElement();
}
UBGraphicsTriangle* UBSvgSubsetAdaptor::UBSvgSubsetReader::triangleFromSvg()
{
UBGraphicsTriangle* triangle = new UBGraphicsTriangle();
triangle->setData(UBGraphicsItemData::ItemLayerType, QVariant(UBItemLayerType::Tool));
graphicsItemFromSvg(triangle);
auto svgX = mXmlReader.attributes().value("x");
auto svgY = mXmlReader.attributes().value("y");
auto svgWidth = mXmlReader.attributes().value("width");
auto svgHeight = mXmlReader.attributes().value("height");
auto orientationStringRef = mXmlReader.attributes().value("orientation");
UBGraphicsTriangle::UBGraphicsTriangleOrientation orientation = UBGraphicsTriangle::orientationFromStr(orientationStringRef.toString());
triangle->setOrientation(orientation);
if (!svgX.isNull() && !svgY.isNull() && !svgWidth.isNull() && !svgHeight.isNull())
{
triangle->setRect(svgX.toString().toFloat(), svgY.toString().toFloat(), svgWidth.toString().toFloat(), svgHeight.toString().toFloat(), orientation);
}
triangle->setVisible(true);
return triangle;
}
UBGraphicsCache* UBSvgSubsetAdaptor::UBSvgSubsetReader::cacheFromSvg()
{
UBGraphicsCache* pCache = mScene->graphicsCache();
pCache->setData(UBGraphicsItemData::ItemLayerType, QVariant(UBItemLayerType::Tool));
graphicsItemFromSvg(pCache);
auto colorR = mXmlReader.attributes().value("colorR");
auto colorG = mXmlReader.attributes().value("colorG");
auto colorB = mXmlReader.attributes().value("colorB");
auto colorA = mXmlReader.attributes().value("colorA");
auto shape = mXmlReader.attributes().value("shape");
auto shapeSize = mXmlReader.attributes().value("shapeSize");
QColor color(colorR.toString().toInt(), colorG.toString().toInt(), colorB.toString().toInt(), colorA.toString().toInt());
pCache->setMaskColor(color);
pCache->setShapeWidth(shapeSize.toString().toInt());
pCache->setMaskShape(static_cast<eMaskShape>(shape.toString().toInt()));
pCache->setVisible(true);
return pCache;
}
void UBSvgSubsetAdaptor::UBSvgSubsetWriter::cacheToSvg(UBGraphicsCache* item)
{
mXmlWriter.writeStartElement(UBSettings::uniboardDocumentNamespaceUri, "cache");
mXmlWriter.writeAttribute("x", QString("%1").arg(item->rect().x()));
mXmlWriter.writeAttribute("y", QString("%1").arg(item->rect().y()));
mXmlWriter.writeAttribute("width", QString("%1").arg(item->rect().width()));
mXmlWriter.writeAttribute("height", QString("%1").arg(item->rect().height()));
mXmlWriter.writeAttribute("colorR", QString("%1").arg(item->maskColor().red()));
mXmlWriter.writeAttribute("colorG", QString("%1").arg(item->maskColor().green()));
mXmlWriter.writeAttribute("colorB", QString("%1").arg(item->maskColor().blue()));
mXmlWriter.writeAttribute("colorA", QString("%1").arg(item->maskColor().alpha()));
mXmlWriter.writeAttribute("shape", QString("%1").arg(item->maskshape()));
mXmlWriter.writeAttribute("shapeSize", QString("%1").arg(item->shapeWidth()));
QString zs;
zs.setNum(item->zValue(), 'f'); // 'f' keeps precision
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "z-value", zs);
UBItem* ubItem = dynamic_cast<UBItem*>(item);
if (ubItem)
{
mXmlWriter.writeAttribute(UBSettings::uniboardDocumentNamespaceUri, "uuid", UBStringUtils::toCanonicalUuid(ubItem->uuid()));
}
mXmlWriter.writeEndElement();
}
void UBSvgSubsetAdaptor::convertPDFObjectsToImages(std::shared_ptr<UBDocumentProxy> proxy)
{
for (int i = 0; i < proxy->pageCount(); i++)
{
std::shared_ptr<UBGraphicsScene> scene = loadScene(proxy, i);
if (scene)
{
bool foundPDFItem = false;
foreach(QGraphicsItem* item, scene->items())
{
UBGraphicsPDFItem *pdfItem = dynamic_cast<UBGraphicsPDFItem*>(item);
if (pdfItem)
{
foundPDFItem = true;
UBGraphicsPixmapItem* pixmapItem = pdfItem->toPixmapItem();
scene->removeItem(pdfItem);
scene->addItem(pixmapItem);
}
}
if (foundPDFItem)
{
scene->setModified(true);
persistScene(proxy, scene, i);
}
}
}
}
void UBSvgSubsetAdaptor::convertSvgImagesToImages(std::shared_ptr<UBDocumentProxy> proxy)
{
for (int i = 0; i < proxy->pageCount(); i++)
{
std::shared_ptr<UBGraphicsScene> scene = loadScene(proxy, i);
if (scene)
{
bool foundSvgItem = false;
foreach(QGraphicsItem* item, scene->items())
{
UBGraphicsSvgItem *svgItem = dynamic_cast<UBGraphicsSvgItem*>(item);
if (svgItem)
{
foundSvgItem = true;
UBGraphicsPixmapItem* pixmapItem = svgItem->toPixmapItem();
scene->removeItem(svgItem);
scene->addItem(pixmapItem);
}
}
if (foundSvgItem)
{
scene->setModified(true);
persistScene(proxy, scene, i);
}
}
}
}
UBSvgSubsetAdaptor::UBSvgReaderContext::UBSvgReaderContext(std::shared_ptr<UBDocumentProxy> proxy, const QByteArray& pXmlData)
{
reader = new UBSvgSubsetReader(proxy, pXmlData);
reader->start();
}
UBSvgSubsetAdaptor::UBSvgReaderContext::~UBSvgReaderContext()
{
delete reader;
}
bool UBSvgSubsetAdaptor::UBSvgReaderContext::isFinished() const
{
return reader->isFinished();
}
void UBSvgSubsetAdaptor::UBSvgReaderContext::step()
{
reader->processElement();
}
std::shared_ptr<UBGraphicsScene> UBSvgSubsetAdaptor::UBSvgReaderContext::scene() const
{
return reader->scene();
}