Redesign of pattern image.

Preparations for support of background image. #43
This commit is contained in:
Roman Telezhynskyi 2022-01-11 17:24:16 +02:00
parent 2448ee4361
commit 141b33884d
17 changed files with 1662 additions and 113 deletions

View File

@ -36,6 +36,11 @@
#include <QMessageBox> #include <QMessageBox>
#include <QRadioButton> #include <QRadioButton>
#include <QCompleter> #include <QCompleter>
#include <QSet>
#include <QImageReader>
#include <QMimeType>
#include <QDesktopServices>
#include <QUrl>
#include "../xml/vpattern.h" #include "../xml/vpattern.h"
#include "../vpatterndb/vcontainer.h" #include "../vpatterndb/vcontainer.h"
@ -44,6 +49,7 @@
#include "dialogknownmaterials.h" #include "dialogknownmaterials.h"
#include "../vmisc/vvalentinasettings.h" #include "../vmisc/vvalentinasettings.h"
#include "../qmuparser/qmudef.h" #include "../qmuparser/qmudef.h"
#include "../ifc/xml/vpatternimage.h"
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
DialogPatternProperties::DialogPatternProperties(VPattern *doc, VContainer *pattern, QWidget *parent) DialogPatternProperties::DialogPatternProperties(VPattern *doc, VContainer *pattern, QWidget *parent)
@ -250,21 +256,6 @@ void DialogPatternProperties::SaveReadOnlyState()
} }
} }
//---------------------------------------------------------------------------------------------------------------------
QImage DialogPatternProperties::GetImage()
{
// we set an image from file.val
QImage image;
QByteArray byteArray;
byteArray.append(doc->GetImage().toUtf8());
QByteArray ba = QByteArray::fromBase64(byteArray);
QBuffer buffer(&ba);
buffer.open(QIODevice::ReadOnly);
QString extension = doc->GetImageExtension();
image.load(&buffer, extension.toLatin1().data()); // writes image into ba in 'extension' format
return image;
}
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
void DialogPatternProperties::ValidatePassmarkLength() const void DialogPatternProperties::ValidatePassmarkLength() const
{ {
@ -318,19 +309,12 @@ void DialogPatternProperties::InitImage()
connect(changeImageAction, &QAction::triggered, this, &DialogPatternProperties::ChangeImage); connect(changeImageAction, &QAction::triggered, this, &DialogPatternProperties::ChangeImage);
connect(saveImageAction, &QAction::triggered, this, &DialogPatternProperties::SaveImage); connect(saveImageAction, &QAction::triggered, this, &DialogPatternProperties::SaveImage);
connect(showImageAction, &QAction::triggered, this, [this]() connect(showImageAction, &QAction::triggered, this, &DialogPatternProperties::ShowImage);
{
QLabel *label = new QLabel(this, Qt::Window);
const QImage image = GetImage();
label->setPixmap(QPixmap::fromImage(image));
label->setGeometry(QRect(QCursor::pos(), image.size()));
label->show();
});
const QImage image = GetImage(); const VPatternImage image = doc->GetImage();
if (not image.isNull()) if (image.IsValid())
{ {
ui->imageLabel->setPixmap(QPixmap::fromImage(image)); ui->imageLabel->setPixmap(image.GetPixmap(ui->imageLabel->width(), ui->imageLabel->height()));
} }
else else
{ {
@ -343,35 +327,46 @@ void DialogPatternProperties::InitImage()
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
void DialogPatternProperties::ChangeImage() void DialogPatternProperties::ChangeImage()
{ {
const QString filter = tr("Images") + QLatin1String(" (*.png *.jpg *.jpeg *.bmp)"); auto PrepareFilter = []()
const QString fileName = QFileDialog::getOpenFileName(this, tr("Image for pattern"), QString(), filter, nullptr, {
VAbstractApplication::VApp()->NativeFileDialog()); const QList<QByteArray> supportedFormats = QImageReader::supportedImageFormats();
const QSet<QString> filterFormats{"bmp", "jpeg", "jpg", "png", "svg", "svgz", "tif", "tiff", "webp"};
QStringList sufixes;
for (const auto& format : supportedFormats)
{
if (filterFormats.contains(format))
{
sufixes.append(QStringLiteral("*.%1").arg(QString(format)));
}
}
QStringList filters;
if (not sufixes.isEmpty())
{
filters.append(tr("Images") + QStringLiteral(" (%1)").arg(sufixes.join(' ')));
}
filters.append(tr("All files") + QStringLiteral(" (*.*)"));
return filters.join(QStringLiteral(";;"));
};
const QString fileName = QFileDialog::getOpenFileName(this, tr("Image for pattern"), QString(), PrepareFilter(),
nullptr, VAbstractApplication::VApp()->NativeFileDialog());
if (not fileName.isEmpty()) if (not fileName.isEmpty())
{ {
QImage image; VPatternImage image = VPatternImage::FromFile(fileName);
if (not image.load(fileName))
if (not image.IsValid())
{ {
qCritical() << tr("Invalid image. Error: %1").arg(image.ErrorString());
return; return;
} }
ui->imageLabel->setPixmap(QPixmap::fromImage(image));
QFileInfo f(fileName);
QString extension = f.suffix().toUpper();
if (extension == QLatin1String("JPEG")) doc->SetImage(image);
{ ui->imageLabel->setPixmap(image.GetPixmap(ui->imageLabel->width(), ui->imageLabel->height()));
extension = "JPG";
}
if (extension == QLatin1String("PNG") || extension == QLatin1String("JPG") || extension == QLatin1String("BMP"))
{
QByteArray byteArray;
QBuffer buffer(&byteArray);
buffer.open(QIODevice::WriteOnly);
image.save(&buffer, extension.toLatin1().data()); //writes the image in 'extension' format inside the buffer
QString iconBase64 = QString::fromLatin1(byteArray.toBase64().data());
// save our image to file.val
doc->SetImage(iconBase64, extension);
}
deleteAction->setEnabled(true); deleteAction->setEnabled(true);
saveImageAction->setEnabled(true); saveImageAction->setEnabled(true);
showImageAction->setEnabled(true); showImageAction->setEnabled(true);
@ -381,24 +376,70 @@ void DialogPatternProperties::ChangeImage()
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
void DialogPatternProperties::SaveImage() void DialogPatternProperties::SaveImage()
{ {
QByteArray byteArray; const VPatternImage image = doc->GetImage();
byteArray.append(doc->GetImage().toUtf8());
QByteArray ba = QByteArray::fromBase64(byteArray); if (not image.IsValid())
const QString extension = doc->GetImageExtension().prepend(QChar('.')); {
QString filter = tr("Images") + QStringLiteral(" (*") + extension + QChar(')'); qCritical() << tr("Unable to save image. Error: %1").arg(image.ErrorString());
QString filename = QFileDialog::getSaveFileName(this, tr("Save File"), tr("untitled"), filter, &filter, return;
}
QMimeType mime = image.MimeTypeFromData();
QString path = QDir::homePath() + QDir::separator() + tr("untitled");
QStringList suffixes = mime.suffixes();
if (not suffixes.isEmpty())
{
path += '.' + suffixes.at(0);
}
QString filter = mime.filterString();
QString filename = QFileDialog::getSaveFileName(this, tr("Save Image"), path, filter, nullptr,
VAbstractApplication::VApp()->NativeFileDialog()); VAbstractApplication::VApp()->NativeFileDialog());
if (not filename.isEmpty()) if (not filename.isEmpty())
{ {
if (not filename.endsWith(extension.toUpper()))
{
filename.append(extension);
}
QFile file(filename); QFile file(filename);
if (file.open(QIODevice::WriteOnly)) if (file.open(QIODevice::WriteOnly))
{ {
file.write(ba); file.write(QByteArray::fromBase64(image.ContentData()));
file.close(); }
else
{
qCritical() << tr("Unable to save image. Error: %1").arg(file.errorString());
} }
} }
} }
//---------------------------------------------------------------------------------------------------------------------
void DialogPatternProperties::ShowImage()
{
const VPatternImage image = doc->GetImage();
if (not image.IsValid())
{
qCritical() << tr("Unable to show image. Error: %1").arg(image.ErrorString());
return;
}
QMimeType mime = image.MimeTypeFromData();
QString name = QDir::tempPath() + QDir::separator() + QStringLiteral("image.XXXXXX");
QStringList suffixes = mime.suffixes();
if (not suffixes.isEmpty())
{
name += '.' + suffixes.at(0);
}
delete m_tmpImage;
m_tmpImage = new QTemporaryFile(name, this);
if (m_tmpImage->open())
{
m_tmpImage->write(QByteArray::fromBase64(image.ContentData()));
m_tmpImage->flush();
QDesktopServices::openUrl(QUrl::fromLocalFile(m_tmpImage->fileName()));
}
else
{
qCritical() << tr("Unable to open temp file");
}
}

View File

@ -31,6 +31,7 @@
#include <QDialog> #include <QDialog>
#include <QMap> #include <QMap>
#include <QPointer>
#include "../vmisc/def.h" #include "../vmisc/def.h"
#include "../ifc/ifcdef.h" #include "../ifc/ifcdef.h"
@ -39,6 +40,7 @@ class VPattern;
class VContainer; class VContainer;
class QCheckBox; class QCheckBox;
class QCompleter; class QCompleter;
class QTemporaryFile;
namespace Ui namespace Ui
{ {
@ -61,6 +63,7 @@ private slots:
void DescEdited(); void DescEdited();
void ChangeImage(); void ChangeImage();
void SaveImage(); void SaveImage();
void ShowImage();
private: private:
Q_DISABLE_COPY(DialogPatternProperties) Q_DISABLE_COPY(DialogPatternProperties)
Ui::DialogPatternProperties *ui; Ui::DialogPatternProperties *ui;
@ -77,12 +80,12 @@ private:
QCompleter *m_completer{nullptr}; QCompleter *m_completer{nullptr};
QStringList m_variables{}; QStringList m_variables{};
QString m_oldPassmarkLength{}; QString m_oldPassmarkLength{};
QPointer<QTemporaryFile> m_tmpImage{};
void SaveDescription(); void SaveDescription();
void SaveReadOnlyState(); void SaveReadOnlyState();
void InitImage(); void InitImage();
QImage GetImage();
void ValidatePassmarkLength() const; void ValidatePassmarkLength() const;
}; };

View File

@ -8,7 +8,7 @@
include(../../../common.pri) include(../../../common.pri)
# Library work with xml. # Library work with xml.
QT += xml xmlpatterns printsupport concurrent QT += xml xmlpatterns printsupport concurrent svg
# We don't need gui library. # We don't need gui library.
QT -= gui QT -= gui

View File

@ -65,6 +65,7 @@
<file>schema/pattern/v0.8.11.xsd</file> <file>schema/pattern/v0.8.11.xsd</file>
<file>schema/pattern/v0.8.12.xsd</file> <file>schema/pattern/v0.8.12.xsd</file>
<file>schema/pattern/v0.8.13.xsd</file> <file>schema/pattern/v0.8.13.xsd</file>
<file>schema/pattern/v0.9.0.xsd</file>
<file>schema/multisize_measurements/v0.3.0.xsd</file> <file>schema/multisize_measurements/v0.3.0.xsd</file>
<file>schema/multisize_measurements/v0.4.0.xsd</file> <file>schema/multisize_measurements/v0.4.0.xsd</file>
<file>schema/multisize_measurements/v0.4.1.xsd</file> <file>schema/multisize_measurements/v0.4.1.xsd</file>

File diff suppressed because it is too large Load Diff

View File

@ -41,6 +41,7 @@
#include <QtConcurrentMap> #include <QtConcurrentMap>
#include <QFuture> #include <QFuture>
#include <QtConcurrentRun> #include <QtConcurrentRun>
#include <QMimeDatabase>
#include "../exception/vexceptionemptyparameter.h" #include "../exception/vexceptionemptyparameter.h"
#include "../exception/vexceptionobjecterror.h" #include "../exception/vexceptionobjecterror.h"
@ -57,6 +58,7 @@
#include "../vmisc/vabstractvalapplication.h" #include "../vmisc/vabstractvalapplication.h"
#include "../vmisc/compatibility.h" #include "../vmisc/compatibility.h"
#include "../vlayout/vtextmanager.h" #include "../vlayout/vtextmanager.h"
#include "vpatternimage.h"
class QDomElement; class QDomElement;
@ -137,7 +139,7 @@ const QString VAbstractPattern::AttrPassmarkLength = QStringLiteral("passmark
const QString VAbstractPattern::AttrOpacity = QStringLiteral("opacity"); const QString VAbstractPattern::AttrOpacity = QStringLiteral("opacity");
const QString VAbstractPattern::AttrTags = QStringLiteral("tags"); const QString VAbstractPattern::AttrTags = QStringLiteral("tags");
const QString VAbstractPattern::AttrExtension = QStringLiteral("extension"); const QString VAbstractPattern::AttrContentType = QStringLiteral("contentType");
const QString VAbstractPattern::AttrFormula = QStringLiteral("formula"); const QString VAbstractPattern::AttrFormula = QStringLiteral("formula");
const QString VAbstractPattern::AttrDescription = QStringLiteral("description"); const QString VAbstractPattern::AttrDescription = QStringLiteral("description");
@ -1228,44 +1230,32 @@ void VAbstractPattern::SetPassmarkLengthVariable(const QString &name)
} }
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
QString VAbstractPattern::GetImage() const auto VAbstractPattern::GetImage() const -> VPatternImage
{ {
return UniqueTagText(TagImage); VPatternImage image;
}
//--------------------------------------------------------------------------------------------------------------------- const QDomNodeList list = elementsByTagName(TagImage);
QString VAbstractPattern::GetImageExtension() const if (not list.isEmpty())
{
const QString defExt = QStringLiteral("PNG");
const QDomNodeList nodeList = this->elementsByTagName(TagImage);
if (nodeList.isEmpty())
{ {
return defExt; QDomElement imgTag = list.at(0).toElement();
} if (not imgTag.isNull())
else
{
const QDomNode domNode = nodeList.at(0);
if (domNode.isNull() == false && domNode.isElement())
{ {
const QDomElement domElement = domNode.toElement(); image.SetContentData(imgTag.text().toLatin1(), imgTag.attribute(AttrContentType));
if (domElement.isNull() == false)
{
const QString ext = domElement.attribute(AttrExtension, defExt);
return ext;
}
} }
} }
return defExt;
return image;
} }
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
void VAbstractPattern::SetImage(const QString &text, const QString &extension) auto VAbstractPattern::SetImage(const VPatternImage &image) -> bool
{ {
QDomElement imageElement = CheckTagExists(TagImage); QDomElement imageElement = CheckTagExists(TagImage);
setTagText(imageElement, text); setTagText(imageElement, image.ContentData());
CheckTagExists(TagImage).setAttribute(AttrExtension, extension); imageElement.setAttribute(AttrContentType, image.ContentType());
modified = true; modified = true;
emit patternChanged(false); emit patternChanged(false);
return true;
} }
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------

View File

@ -48,6 +48,7 @@
class QDomElement; class QDomElement;
class VPiecePath; class VPiecePath;
class VPieceNode; class VPieceNode;
class VPatternImage;
enum class Document : qint8 { FullLiteParse, LiteParse, LitePPParse, FullParse }; enum class Document : qint8 { FullLiteParse, LiteParse, LitePPParse, FullParse };
enum class LabelType : qint8 {NewPatternPiece, NewLabel}; enum class LabelType : qint8 {NewPatternPiece, NewLabel};
@ -200,9 +201,8 @@ public:
QString GetPassmarkLengthVariable() const; QString GetPassmarkLengthVariable() const;
void SetPassmarkLengthVariable(const QString &name); void SetPassmarkLengthVariable(const QString &name);
QString GetImage() const; VPatternImage GetImage() const;
QString GetImageExtension() const; bool SetImage(const VPatternImage &image);
void SetImage(const QString &text, const QString &extension);
void DeleteImage(); void DeleteImage();
QString GetVersion() const; QString GetVersion() const;
@ -319,7 +319,7 @@ public:
static const QString AttrOpacity; static const QString AttrOpacity;
static const QString AttrTags; static const QString AttrTags;
static const QString AttrExtension; static const QString AttrContentType;
static const QString AttrFormula; static const QString AttrFormula;
static const QString AttrDescription; static const QString AttrDescription;

View File

@ -891,7 +891,7 @@ auto VDomDocument::GetFormatVersion(const QString &version) -> unsigned
} }
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
bool VDomDocument::setTagText(const QString &tag, const QString &text) auto VDomDocument::setTagText(const QString &tag, const QString &text) -> bool
{ {
const QDomNodeList nodeList = this->elementsByTagName(tag); const QDomNodeList nodeList = this->elementsByTagName(tag);
if (nodeList.isEmpty()) if (nodeList.isEmpty())
@ -900,10 +900,10 @@ bool VDomDocument::setTagText(const QString &tag, const QString &text)
} }
else else
{ {
const QDomNode domNode = nodeList.at(0); QDomNode domNode = nodeList.at(0);
if (domNode.isNull() == false && domNode.isElement()) if (not domNode.isNull() && domNode.isElement())
{ {
const QDomElement domElement = domNode.toElement(); QDomElement domElement = domNode.toElement();
return setTagText(domElement, text); return setTagText(domElement, text);
} }
} }
@ -911,17 +911,30 @@ bool VDomDocument::setTagText(const QString &tag, const QString &text)
} }
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
bool VDomDocument::setTagText(const QDomElement &domElement, const QString &text) auto VDomDocument::setTagText(QDomElement &domElement, const QString &text) -> bool
{ {
if (domElement.isNull() == false) if (not domElement.isNull())
{ {
QDomElement parent = domElement.parentNode().toElement(); QDomNode oldText = domElement.firstChild();
QDomElement newTag = createElement(domElement.tagName()); const QDomText newText = createTextNode(text);
const QDomText newTagText = createTextNode(text); if (oldText.isNull())
newTag.appendChild(newTagText); {
domElement.appendChild(newText);
}
else
{
if (oldText.nodeType() == QDomNode::TextNode)
{
domElement.replaceChild(newText, oldText);
}
else
{
RemoveAllChildren(domElement);
domElement.appendChild(newText);
}
}
parent.replaceChild(newTag, domElement);
return true; return true;
} }
return false; return false;

View File

@ -149,11 +149,11 @@ public:
protected: protected:
bool setTagText(const QString &tag, const QString &text); bool setTagText(const QString &tag, const QString &text);
bool setTagText(const QDomElement &domElement, const QString &text); bool setTagText(QDomElement &domElement, const QString &text);
QString UniqueTagText(const QString &tagName, const QString &defVal = QString()) const; QString UniqueTagText(const QString &tagName, const QString &defVal = QString()) const;
void CollectId(const QDomElement &node, QVector<quint32> &vector)const; void CollectId(const QDomElement &node, QVector<quint32> &vector)const;
static void ValidateVersion(const QString &version); static void ValidateVersion(const QString &version);
private slots: private slots:
void CacheRefreshed(); void CacheRefreshed();

View File

@ -60,8 +60,8 @@ class QDomElement;
*/ */
const QString VPatternConverter::PatternMinVerStr = QStringLiteral("0.1.4"); const QString VPatternConverter::PatternMinVerStr = QStringLiteral("0.1.4");
const QString VPatternConverter::PatternMaxVerStr = QStringLiteral("0.8.13"); const QString VPatternConverter::PatternMaxVerStr = QStringLiteral("0.9.0");
const QString VPatternConverter::CurrentSchema = QStringLiteral("://schema/pattern/v0.8.13.xsd"); const QString VPatternConverter::CurrentSchema = QStringLiteral("://schema/pattern/v0.9.0.xsd");
//VPatternConverter::PatternMinVer; // <== DON'T FORGET TO UPDATE TOO!!!! //VPatternConverter::PatternMinVer; // <== DON'T FORGET TO UPDATE TOO!!!!
//VPatternConverter::PatternMaxVer; // <== DON'T FORGET TO UPDATE TOO!!!! //VPatternConverter::PatternMaxVer; // <== DON'T FORGET TO UPDATE TOO!!!!
@ -167,6 +167,8 @@ Q_GLOBAL_STATIC_WITH_ARGS(const QString, strUserDefined, (QLatin1String("userDef
Q_GLOBAL_STATIC_WITH_ARGS(const QString, strPlacement, (QLatin1String("placement"))) Q_GLOBAL_STATIC_WITH_ARGS(const QString, strPlacement, (QLatin1String("placement")))
Q_GLOBAL_STATIC_WITH_ARGS(const QString, strCutNumber, (QLatin1String("cutNumber"))) Q_GLOBAL_STATIC_WITH_ARGS(const QString, strCutNumber, (QLatin1String("cutNumber")))
Q_GLOBAL_STATIC_WITH_ARGS(const QString, strQuantity, (QLatin1String("quantity"))) Q_GLOBAL_STATIC_WITH_ARGS(const QString, strQuantity, (QLatin1String("quantity")))
Q_GLOBAL_STATIC_WITH_ARGS(const QString, strExtension, (QLatin1String("extension")))
Q_GLOBAL_STATIC_WITH_ARGS(const QString, strContentType, (QLatin1String("contentType")))
} // anonymous namespace } // anonymous namespace
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
@ -246,7 +248,8 @@ auto VPatternConverter::XSDSchema(unsigned ver) const -> QString
std::make_pair(FormatVersion(0, 8, 10), QStringLiteral("://schema/pattern/v0.8.10.xsd")), std::make_pair(FormatVersion(0, 8, 10), QStringLiteral("://schema/pattern/v0.8.10.xsd")),
std::make_pair(FormatVersion(0, 8, 11), QStringLiteral("://schema/pattern/v0.8.11.xsd")), std::make_pair(FormatVersion(0, 8, 11), QStringLiteral("://schema/pattern/v0.8.11.xsd")),
std::make_pair(FormatVersion(0, 8, 12), QStringLiteral("://schema/pattern/v0.8.12.xsd")), std::make_pair(FormatVersion(0, 8, 12), QStringLiteral("://schema/pattern/v0.8.12.xsd")),
std::make_pair(FormatVersion(0, 8, 13), CurrentSchema) std::make_pair(FormatVersion(0, 8, 13), QStringLiteral("://schema/pattern/v0.8.13.xsd")),
std::make_pair(FormatVersion(0, 9, 0), CurrentSchema)
}; };
if (schemas.contains(ver)) if (schemas.contains(ver))
@ -519,6 +522,10 @@ void VPatternConverter::ApplyPatches()
ValidateXML(XSDSchema(FormatVersion(0, 8, 13))); ValidateXML(XSDSchema(FormatVersion(0, 8, 13)));
Q_FALLTHROUGH(); Q_FALLTHROUGH();
case (FormatVersion(0, 8, 13)): case (FormatVersion(0, 8, 13)):
ToV0_9_0();
ValidateXML(XSDSchema(FormatVersion(0, 9, 0)));
Q_FALLTHROUGH();
case (FormatVersion(0, 9, 0)):
break; break;
default: default:
InvalidVersion(m_ver); InvalidVersion(m_ver);
@ -536,7 +543,7 @@ void VPatternConverter::DowngradeToCurrentMaxVersion()
bool VPatternConverter::IsReadOnly() const bool VPatternConverter::IsReadOnly() const
{ {
// Check if attribute readOnly was not changed in file format // Check if attribute readOnly was not changed in file format
Q_STATIC_ASSERT_X(VPatternConverter::PatternMaxVer == FormatVersion(0, 8, 13), Q_STATIC_ASSERT_X(VPatternConverter::PatternMaxVer == FormatVersion(0, 9, 0),
"Check attribute readOnly."); "Check attribute readOnly.");
// Possibly in future attribute readOnly will change position etc. // Possibly in future attribute readOnly will change position etc.
@ -1238,6 +1245,19 @@ void VPatternConverter::ToV0_8_13()
Save(); Save();
} }
//---------------------------------------------------------------------------------------------------------------------
void VPatternConverter::ToV0_9_0()
{
// TODO. Delete if minimal supported version is 0.9.0
Q_STATIC_ASSERT_X(VPatternConverter::PatternMinVer < FormatVersion(0, 9, 0),
"Time to refactor the code.");
ConvertImageToV0_9_0();
SetVersion(QStringLiteral("0.9.0"));
Save();
}
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
void VPatternConverter::TagUnitToV0_2_0() void VPatternConverter::TagUnitToV0_2_0()
{ {
@ -2790,6 +2810,61 @@ void VPatternConverter::AddPieceUUIDV0_8_8()
} }
} }
//---------------------------------------------------------------------------------------------------------------------
void VPatternConverter::ConvertImageToV0_9_0()
{
// TODO. Delete if minimal supported version is 0.9.0
Q_STATIC_ASSERT_X(VPatternConverter::PatternMinVer < FormatVersion(0, 9, 0),
"Time to refactor the code.");
const QDomNodeList list = elementsByTagName(*strImage);
if (not list.isEmpty())
{
QDomElement img = list.at(0).toElement();
if (not img.isNull())
{
QString extension = img.attribute(*strExtension);
img.removeAttribute(*strExtension);
if (not extension.isEmpty())
{
QMap<QString, QString> mimeTypes{
{"BMP", "image/bmp"},
{"JPG", "image/jpeg"},
{"PNG", "image/png"}
};
if (mimeTypes.contains(extension))
{
img.setAttribute(*strContentType, mimeTypes.value(extension));
}
}
const QString content = img.text();
if (not content.isEmpty())
{
auto SplitString = [content]()
{
const int n = 80;
QStringList list;
QString tmp(content);
while (not tmp.isEmpty())
{
list.append(tmp.left(n));
tmp.remove(0, n);
}
return list;
};
QStringList data = SplitString();
setTagText(img, data.join("\n"));
}
}
}
}
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
void VPatternConverter::TagUnionDetailsToV0_4_0() void VPatternConverter::TagUnionDetailsToV0_4_0()
{ {

View File

@ -53,7 +53,7 @@ public:
static const QString PatternMaxVerStr; static const QString PatternMaxVerStr;
static const QString CurrentSchema; static const QString CurrentSchema;
static Q_DECL_CONSTEXPR const unsigned PatternMinVer = FormatVersion(0, 1, 4); static Q_DECL_CONSTEXPR const unsigned PatternMinVer = FormatVersion(0, 1, 4);
static Q_DECL_CONSTEXPR const unsigned PatternMaxVer = FormatVersion(0, 8, 13); static Q_DECL_CONSTEXPR const unsigned PatternMaxVer = FormatVersion(0, 9, 0);
protected: protected:
virtual unsigned MinVer() const override; virtual unsigned MinVer() const override;
@ -136,6 +136,7 @@ private:
void ToV0_8_11(); void ToV0_8_11();
void ToV0_8_12(); void ToV0_8_12();
void ToV0_8_13(); void ToV0_8_13();
void ToV0_9_0();
void TagUnitToV0_2_0(); void TagUnitToV0_2_0();
void TagIncrementToV0_2_0(); void TagIncrementToV0_2_0();
@ -191,6 +192,8 @@ private:
void RemoveGradationV0_8_8(); void RemoveGradationV0_8_8();
void AddPieceUUIDV0_8_8(); void AddPieceUUIDV0_8_8();
void ConvertImageToV0_9_0();
}; };
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------

View File

@ -0,0 +1,221 @@
/************************************************************************
**
** @file vpatternimage.cpp
** @author Roman Telezhynskyi <dismine(at)gmail.com>
** @date 11 1, 2022
**
** @brief
** @copyright
** This source code is part of the Valentina project, a pattern making
** program, whose allow create and modeling patterns of clothing.
** Copyright (C) 2022 Valentina project
** <https://gitlab.com/smart-pattern/valentina> All Rights Reserved.
**
** Valentina is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** Valentina is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with Valentina. If not, see <http://www.gnu.org/licenses/>.
**
*************************************************************************/
#include "vpatternimage.h"
#include <QImage>
#include <QImageReader>
#include <QMimeDatabase>
#include <QPainter>
#include <QPixmap>
#include <QRegularExpressionMatch>
#include <QSvgRenderer>
#include <QDebug>
#include <QBuffer>
#include <QSize>
#include <QFile>
namespace
{
//---------------------------------------------------------------------------------------------------------------------
auto IsMimeTypeImage(const QMimeType &mime) -> bool
{
QStringList aliases = mime.aliases();
aliases.prepend(mime.name());
QRegularExpression rx(QStringLiteral("^image\\/[-\\w]+(\\.[-\\w]+)*([+][-\\w]+)?$"));
return std::any_of(aliases.begin(), aliases.end(), [rx](const QString &name) { return rx.match(name).hasMatch(); });
}
//---------------------------------------------------------------------------------------------------------------------
auto SplitString(QString str) -> QStringList
{
QStringList list;
const int n = 80;
while (not str.isEmpty())
{
list.append(str.left(n));
str.remove(0, n);
}
return list;
}
} // namespace
//---------------------------------------------------------------------------------------------------------------------
auto VPatternImage::FromFile(const QString &fileName) -> VPatternImage
{
VPatternImage image;
QMimeType mime = QMimeDatabase().mimeTypeForFile(fileName);
if (not IsMimeTypeImage(mime))
{
qCritical() << tr("Unexpected mime type: %1").arg(mime.name());
return {};
}
QFile file(fileName);
if (not file.open(QIODevice::ReadOnly))
{
qCritical() << tr("Couldn't read the image. Error: %1").arg(file.errorString());
return {};
}
QString base64 = SplitString(QString::fromLatin1(file.readAll().toBase64().data())).join('\n');
image.SetContentData(base64.toLatin1(), mime.name());
return image;
}
//---------------------------------------------------------------------------------------------------------------------
auto VPatternImage::ContentType() const -> const QString &
{
return m_contentType;
}
//---------------------------------------------------------------------------------------------------------------------
auto VPatternImage::ContentData() const -> const QByteArray &
{
return m_contentData;
}
//---------------------------------------------------------------------------------------------------------------------
void VPatternImage::SetContentData(const QByteArray &newContentData, const QString &newContentType)
{
m_contentData = newContentData;
m_contentType = not newContentType.isEmpty() ? newContentType : MimeTypeFromData().name();
}
//---------------------------------------------------------------------------------------------------------------------
auto VPatternImage::IsNull() const -> bool
{
return m_contentData.isEmpty();
}
//---------------------------------------------------------------------------------------------------------------------
auto VPatternImage::IsValid() const -> bool
{
m_errorString.clear();
if (IsNull())
{
m_errorString = tr("No data.");
return false;
}
if (m_contentType.isEmpty())
{
m_errorString = tr("Content type is empty.");
return false;
}
QMimeType mime = MimeTypeFromData();
QSet<QString> aliases = mime.aliases().toSet();
aliases.insert(mime.name());
QSet<QString> gzipMime {"application/gzip", "application/x-gzip"};
if (gzipMime.contains(aliases))
{
QSvgRenderer render(QByteArray::fromBase64(m_contentData));
if (render.isValid())
{
mime = QMimeDatabase().mimeTypeForName(QStringLiteral("image/svg+xml-compressed"));
aliases = mime.aliases().toSet();
aliases.insert(mime.name());
}
}
if (not aliases.contains(m_contentType))
{
m_errorString = tr("Content type mistmatch.");
return false;
}
if (not IsMimeTypeImage(mime))
{
m_errorString = tr("Not image.");
return false;
}
return true;
}
//---------------------------------------------------------------------------------------------------------------------
auto VPatternImage::GetPixmap(int width, int height) const -> QPixmap
{
if (not IsValid())
{
return {};
}
QByteArray array = QByteArray::fromBase64(m_contentData);
QBuffer buffer(&array);
buffer.open(QIODevice::ReadOnly);
QImageReader imageReader(&buffer);
imageReader.setScaledSize(QSize(width, height));
QImage image = imageReader.read();
if (image.isNull())
{
qCritical()<< tr("Couldn't read the image. Error: %1").arg(imageReader.errorString());
return {};
}
return QPixmap::fromImage(image);
}
//---------------------------------------------------------------------------------------------------------------------
auto VPatternImage::ErrorString() const -> const QString &
{
return m_errorString;
}
//---------------------------------------------------------------------------------------------------------------------
auto VPatternImage::MimeTypeFromData() const -> QMimeType
{
return QMimeDatabase().mimeTypeForData(QByteArray::fromBase64(m_contentData));
}
//---------------------------------------------------------------------------------------------------------------------
auto VPatternImage::Size() const -> QSize
{
if (not IsValid())
{
return {};
}
QByteArray array = QByteArray::fromBase64(m_contentData);
QBuffer buffer(&array);
buffer.open(QIODevice::ReadOnly);
return QImageReader(&buffer).size();
}

View File

@ -0,0 +1,68 @@
/************************************************************************
**
** @file vpatternimage.h
** @author Roman Telezhynskyi <dismine(at)gmail.com>
** @date 11 1, 2022
**
** @brief
** @copyright
** This source code is part of the Valentina project, a pattern making
** program, whose allow create and modeling patterns of clothing.
** Copyright (C) 2022 Valentina project
** <https://gitlab.com/smart-pattern/valentina> All Rights Reserved.
**
** Valentina is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** Valentina is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with Valentina. If not, see <http://www.gnu.org/licenses/>.
**
*************************************************************************/
#ifndef VPATTERNIMAGE_H
#define VPATTERNIMAGE_H
#include <QString>
#include <QCoreApplication>
class QPixmap;
class QMimeType;
class VPatternImage
{
Q_DECLARE_TR_FUNCTIONS(VPatternImage)
public:
VPatternImage() = default;
static auto FromFile(const QString &fileName) -> VPatternImage;
auto ContentType() const -> const QString &;
auto ContentData() const -> const QByteArray &;
void SetContentData(const QByteArray &newContentData, const QString & newContentType);
auto IsNull() const -> bool;
auto IsValid() const -> bool;
auto GetPixmap(int width, int height) const -> QPixmap;
auto ErrorString() const -> const QString &;
auto MimeTypeFromData() const -> QMimeType;
auto Size() const -> QSize;
private:
QString m_contentType{};
QByteArray m_contentData{};
mutable QString m_errorString{};
};
#endif // VPATTERNIMAGE_H

View File

@ -6,6 +6,7 @@ HEADERS += \
$$PWD/vdomdocument.h \ $$PWD/vdomdocument.h \
$$PWD/vlayoutconverter.h \ $$PWD/vlayoutconverter.h \
$$PWD/vpatternconverter.h \ $$PWD/vpatternconverter.h \
$$PWD/vpatternimage.h \
$$PWD/vtoolrecord.h \ $$PWD/vtoolrecord.h \
$$PWD/vabstractpattern.h \ $$PWD/vabstractpattern.h \
$$PWD/vvstconverter.h \ $$PWD/vvstconverter.h \
@ -19,6 +20,7 @@ SOURCES += \
$$PWD/vdomdocument.cpp \ $$PWD/vdomdocument.cpp \
$$PWD/vlayoutconverter.cpp \ $$PWD/vlayoutconverter.cpp \
$$PWD/vpatternconverter.cpp \ $$PWD/vpatternconverter.cpp \
$$PWD/vpatternimage.cpp \
$$PWD/vtoolrecord.cpp \ $$PWD/vtoolrecord.cpp \
$$PWD/vabstractpattern.cpp \ $$PWD/vabstractpattern.cpp \
$$PWD/vvstconverter.cpp \ $$PWD/vvstconverter.cpp \

View File

@ -4,7 +4,7 @@
# #
#------------------------------------------------- #-------------------------------------------------
QT += testlib widgets printsupport concurrent xml xmlpatterns QT += testlib widgets printsupport concurrent xml xmlpatterns svg
QT -= gui QT -= gui

View File

@ -4,7 +4,7 @@
# #
#------------------------------------------------- #-------------------------------------------------
QT += testlib widgets xml printsupport concurrent xmlpatterns QT += testlib widgets xml printsupport concurrent xmlpatterns svg
QT -= gui QT -= gui

View File

@ -4,7 +4,7 @@
# #
#------------------------------------------------- #-------------------------------------------------
QT += core testlib gui printsupport xml xmlpatterns concurrent QT += core testlib gui printsupport xml xmlpatterns concurrent svg
TARGET = ValentinaTests TARGET = ValentinaTests