valentina/src/app/puzzle/xml/vplayoutfilewriter.cpp
2023-02-10 17:51:21 +02:00

442 lines
18 KiB
C++

/************************************************************************
**
** @file vplayoutfilewriter.cpp
** @author Ronan Le Tiec
** @date 18 4, 2020
**
** @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) 2020 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 "vplayoutfilewriter.h"
#include "../layout/vplayout.h"
#include "../layout/vpsheet.h"
#include "../layout/vppiece.h"
#include "vplayoutliterals.h"
#include "../ifc/xml/vlayoutconverter.h"
#include "../vmisc/projectversion.h"
#include "../vlayout/vlayoutpiecepath.h"
#include "../vlayout/vtextmanager.h"
#include "../vgeometry/vlayoutplacelabel.h"
namespace
{
//---------------------------------------------------------------------------------------------------------------------
template <class T>
auto NumberToString(T number) -> QString
{
const QLocale locale = QLocale::c();
return locale.toString(number, 'g', 12).remove(LocaleGroupSeparator(locale));
}
//---------------------------------------------------------------------------------------------------------------------
auto TransformToString(const QTransform &m) -> QString
{
QStringList matrix
{
NumberToString(m.m11()),
NumberToString(m.m12()),
NumberToString(m.m13()),
NumberToString(m.m21()),
NumberToString(m.m22()),
NumberToString(m.m23()),
NumberToString(m.m31()),
NumberToString(m.m32()),
NumberToString(m.m33())
};
return matrix.join(ML::groupSep);
}
//---------------------------------------------------------------------------------------------------------------------
auto PointToString(const QPointF &p) -> QString
{
return NumberToString(p.x()) + ML::coordintatesSep + NumberToString(p.y());
}
//---------------------------------------------------------------------------------------------------------------------
auto PathToString(const QVector<QPointF> &pathPoints) -> QString
{
QStringList path;
path.reserve(pathPoints.size());
for (auto point : pathPoints)
{
path.append(PointToString(point));
}
return path.join(ML::pointsSep);
}
//---------------------------------------------------------------------------------------------------------------------
auto RectToString(const QRectF &r) -> QString
{
return NumberToString(r.x()) + ML::groupSep +
NumberToString(r.y()) + ML::groupSep +
NumberToString(r.width()) + ML::groupSep +
NumberToString(r.height());
}
//---------------------------------------------------------------------------------------------------------------------
auto LineToString(const QLineF &line) -> QString
{
return PointToString(line.p1()) + ML::groupSep + PointToString(line.p2());
}
//---------------------------------------------------------------------------------------------------------------------
auto LinesToString(const QVector<QLineF> &lines) -> QString
{
QStringList l;
l.reserve(lines.size());
for (auto line : lines)
{
l.append(LineToString(line));
}
return l.join(ML::itemsSep);
}
//---------------------------------------------------------------------------------------------------------------------
auto GrainlineArrowDirrectionToString(GrainlineArrowDirection type) -> QString
{
switch(type)
{
case GrainlineArrowDirection::atFront:
return ML::atFrontStr;
case GrainlineArrowDirection::atRear:
return ML::atRearStr;
case GrainlineArrowDirection::atBoth:
default:
return ML::atBothStr;
}
}
} // namespace
//---------------------------------------------------------------------------------------------------------------------
void VPLayoutFileWriter::WriteFile(const VPLayoutPtr &layout, QIODevice *file)
{
setDevice(file);
setAutoFormatting(true);
writeStartDocument();
writeComment(QStringLiteral("Layout created with Valentina v%1 (https://smart-pattern.com.ua/).")
.arg(APP_VERSION_STR));
WriteLayout(layout);
writeEndDocument();
}
//---------------------------------------------------------------------------------------------------------------------
void VPLayoutFileWriter::WriteLayout(const VPLayoutPtr &layout)
{
writeStartElement(ML::TagLayout);
SetAttribute(AttrLayoutVersion, VLayoutConverter::LayoutMaxVerStr);
WriteLayoutProperties(layout);
WritePieceList(layout->GetUnplacedPieces(), ML::TagUnplacedPieces);
WriteSheets(layout);
writeEndElement(); //layout
}
//---------------------------------------------------------------------------------------------------------------------
void VPLayoutFileWriter::WriteLayoutProperties(const VPLayoutPtr &layout)
{
writeStartElement(ML::TagProperties);
writeTextElement(ML::TagUnit, UnitsToStr(layout->LayoutSettings().GetUnit()));
writeTextElement(ML::TagTitle, layout->LayoutSettings().GetTitle());
writeTextElement(ML::TagDescription, layout->LayoutSettings().GetDescription());
writeStartElement(ML::TagControl);
SetAttribute(ML::AttrWarningSuperposition, layout->LayoutSettings().GetWarningSuperpositionOfPieces());
SetAttribute(ML::AttrWarningOutOfBound, layout->LayoutSettings().GetWarningPiecesOutOfBound());
SetAttribute(ML::AttrStickyEdges, layout->LayoutSettings().GetStickyEdges());
SetAttribute(ML::AttrPiecesGap, layout->LayoutSettings().GetPiecesGap());
SetAttribute(ML::AttrFollowGrainline, layout->LayoutSettings().GetFollowGrainline());
writeEndElement(); // control
WriteTiles(layout);
writeStartElement(ML::TagScale);
SetAttribute(ML::AttrXScale, layout->LayoutSettings().HorizontalScale());
SetAttribute(ML::AttrYScale, layout->LayoutSettings().VerticalScale());
writeEndElement(); // scale
writeStartElement(ML::TagWatermark);
SetAttributeOrRemoveIf<bool>(ML::AttrShowPreview, layout->LayoutSettings().GetShowWatermark(),
[](bool show) noexcept {return not show;});
writeCharacters(layout->LayoutSettings().WatermarkPath());
writeEndElement(); // watermark
writeEndElement(); // properties
}
//---------------------------------------------------------------------------------------------------------------------
void VPLayoutFileWriter::WriteSheets(const VPLayoutPtr &layout)
{
writeStartElement(ML::TagSheets);
QList<VPSheetPtr> sheets = layout->GetSheets();
for (const auto &sheet : sheets)
{
if (not sheet.isNull())
{
WriteSheet(sheet);
}
}
writeEndElement(); // sheets
}
//---------------------------------------------------------------------------------------------------------------------
void VPLayoutFileWriter::WriteSheet(const VPSheetPtr &sheet)
{
writeStartElement(ML::TagSheet);
SetAttributeOrRemoveIf<QString>(ML::AttrGrainlineType, GrainlineTypeToStr(sheet->GetGrainlineType()),
[](const QString &type) noexcept
{return type == GrainlineTypeToStr(GrainlineType::NotFixed);});
writeTextElement(ML::TagName, sheet->GetName());
WriteSize(sheet->GetSheetSize());
WriteMargins(sheet->GetSheetMargins(), sheet->IgnoreMargins());
WritePieceList(sheet->GetPieces(), ML::TagPieces);
writeEndElement(); // sheet
}
//---------------------------------------------------------------------------------------------------------------------
void VPLayoutFileWriter::WriteTiles(const VPLayoutPtr &layout)
{
writeStartElement(ML::TagTiles);
SetAttribute(ML::AttrVisible, layout->LayoutSettings().GetShowTiles());
SetAttribute(ML::AttrMatchingMarks, "standard"); // TODO / Fixme get the right value
SetAttributeOrRemoveIf<bool>(ML::AttrPrintScheme, layout->LayoutSettings().GetPrintTilesScheme(),
[](bool print) noexcept {return not print;});
SetAttributeOrRemoveIf<bool>(ML::AttrTileNumber, layout->LayoutSettings().GetShowTileNumber(),
[](bool show) noexcept {return not show;});
WriteSize(layout->LayoutSettings().GetTilesSize());
WriteMargins(layout->LayoutSettings().GetTilesMargins(), layout->LayoutSettings().IgnoreTilesMargins());
writeEndElement(); // tiles
}
//---------------------------------------------------------------------------------------------------------------------
void VPLayoutFileWriter::WritePieceList(const QList<VPPiecePtr> &list, const QString &tagName)
{
writeStartElement(tagName); // piece list
for (const auto &piece : list)
{
WritePiece(piece);
}
writeEndElement(); // piece list
}
//---------------------------------------------------------------------------------------------------------------------
void VPLayoutFileWriter::WritePiece(const VPPiecePtr &piece)
{
writeStartElement(ML::TagPiece);
SetAttribute(ML::AttrUID, piece->GetUUID().toString());
SetAttribute(ML::AttrName, piece->GetName());
SetAttributeOrRemoveIf<bool>(ML::AttrMirrored, piece->IsMirror(),
[](bool mirrored) noexcept {return not mirrored;});
SetAttributeOrRemoveIf<bool>(ML::AttrForbidFlipping, piece->IsForbidFlipping(),
[](bool forbid) noexcept {return not forbid;});
SetAttributeOrRemoveIf<bool>(ML::AttrForceFlipping, piece->IsForceFlipping(),
[](bool force) noexcept {return not force;});
SetAttributeOrRemoveIf<bool>(ML::AttrSewLineOnDrawing, piece->IsSewLineOnDrawing(),
[](bool value) noexcept {return not value;});
SetAttribute(ML::AttrTransform, TransformToString(piece->GetMatrix()));
SetAttributeOrRemoveIf<QString>(ML::AttrGradationLabel, piece->GetGradationId(),
[](const QString &label) noexcept {return label.isEmpty();});
SetAttribute(ML::AttrCopyNumber, piece->CopyNumber());
SetAttributeOrRemoveIf<bool>(ML::AttrShowSeamline, not piece->IsHideMainPath(),
[](bool show) noexcept {return show;});
SetAttributeOrRemoveIf<qreal>(ML::AttrXScale, piece->GetXScale(),
[](qreal xs) noexcept {return VFuzzyComparePossibleNulls(xs, 1.0);});
SetAttributeOrRemoveIf<qreal>(ML::AttrYScale, piece->GetYScale(),
[](qreal ys) noexcept {return VFuzzyComparePossibleNulls(ys, 1.0);});
SetAttributeOrRemoveIf<qreal>(ML::AttrZValue, piece->ZValue(),
[](qreal z) noexcept {return VFuzzyComparePossibleNulls(z, 1.0);});
writeStartElement(ML::TagSeamLine);
QVector<VLayoutPoint> contourPoints = piece->GetContourPoints();
for (auto &point : contourPoints)
{
WriteLayoutPoint(point);
}
writeEndElement();
writeStartElement(ML::TagSeamAllowance);
SetAttributeOrRemoveIf<bool>(ML::AttrEnabled, piece->IsSeamAllowance(),
[](bool enabled) noexcept {return not enabled;});
SetAttributeOrRemoveIf<bool>(ML::AttrBuiltIn, piece->IsSeamAllowanceBuiltIn(),
[](bool builtin) noexcept {return not builtin;});
if (piece->IsSeamAllowance() && not piece->IsSeamAllowanceBuiltIn())
{
QVector<VLayoutPoint> seamAllowancePoints = piece->GetSeamAllowancePoints();
for (auto &point : seamAllowancePoints)
{
WriteLayoutPoint(point);
}
}
writeEndElement();
writeStartElement(ML::TagGrainline);
SetAttributeOrRemoveIf<bool>(ML::AttrEnabled, piece->IsGrainlineEnabled(),
[](bool enabled) noexcept {return not enabled;});
if (piece->IsGrainlineEnabled())
{
SetAttribute(ML::AttrAngle, piece->GrainlineAngle());
SetAttribute(ML::AttrArrowDirection, GrainlineArrowDirrectionToString(piece->GrainlineArrowType()));
writeCharacters(PathToString(piece->GetGrainline()));
}
writeEndElement();
writeStartElement(ML::TagNotches);
QVector<VLayoutPassmark> passmarks = piece->GetPassmarks();
for (const auto& passmark : passmarks)
{
writeStartElement(ML::TagNotch);
SetAttribute(ML::AttrBuiltIn, passmark.isBuiltIn);
SetAttribute(ML::AttrType, static_cast<int>(passmark.type));
SetAttribute(ML::AttrBaseLine, LineToString(passmark.baseLine));
SetAttribute(ML::AttrPath, LinesToString(passmark.lines));
writeEndElement();
}
writeEndElement();
writeStartElement(ML::TagInternalPaths);
QVector<VLayoutPiecePath> internalPaths = piece->GetInternalPaths();
for (const auto& path : internalPaths)
{
writeStartElement(ML::TagInternalPath);
SetAttribute(ML::AttrCut, path.IsCutPath());
SetAttribute(ML::AttrPenStyle, PenStyleToLineStyle(path.PenStyle()));
QVector<VLayoutPoint> points = path.Points();
for (auto &point : points)
{
WriteLayoutPoint(point);
}
writeEndElement();
}
writeEndElement();
writeStartElement(ML::TagMarkers);
QVector<VLayoutPlaceLabel> placelabels = piece->GetPlaceLabels();
for (const auto& label : placelabels)
{
writeStartElement(ML::TagMarker);
SetAttribute(ML::AttrTransform, TransformToString(label.RotationMatrix()));
SetAttribute(ML::AttrType, static_cast<int>(label.Type()));
SetAttribute(ML::AttrCenter, PointToString(label.Center()));
SetAttribute(ML::AttrBox, RectToString(label.Box()));
writeEndElement();
}
writeEndElement();
writeStartElement(ML::TagLabels);
WriteLabel(piece->GetPieceLabelRect(), piece->GetPieceLabelData(), ML::TagPieceLabel);
WriteLabel(piece->GetPatternLabelRect(), piece->GetPatternLabelData(), ML::TagPatternLabel);
writeEndElement();
writeEndElement();
}
//---------------------------------------------------------------------------------------------------------------------
void VPLayoutFileWriter::WriteLabel(const QVector<QPointF> &labelShape, const VTextManager &tm, const QString &tagName)
{
if (labelShape.size() > 2 && tm.GetSourceLinesCount() > 0)
{
writeStartElement(tagName);
SetAttribute(ML::AttrShape, PathToString(labelShape));
WriteLabelLines(tm);
writeEndElement();
}
}
//---------------------------------------------------------------------------------------------------------------------
void VPLayoutFileWriter::WriteLabelLines(const VTextManager &tm)
{
writeStartElement(ML::TagLines);
SetAttribute(ML::AttrFont, tm.GetFont().toString());
for (int i = 0; i < tm.GetSourceLinesCount(); ++i)
{
writeStartElement(ML::TagLine);
const TextLine& tl = tm.GetSourceLine(i);
SetAttribute(ML::AttrFontSize, tl.m_iFontSize);
SetAttribute(ML::AttrBold, tl.m_bold);
SetAttribute(ML::AttrItalic, tl.m_italic);
SetAttribute(ML::AttrAlignment, static_cast<int>(tl.m_eAlign));
writeCharacters(tl.m_qsText);
writeEndElement();
}
writeEndElement();
}
//---------------------------------------------------------------------------------------------------------------------
void VPLayoutFileWriter::WriteMargins(const QMarginsF &margins, bool ignore)
{
writeStartElement(ML::TagMargin);
SetAttributeOrRemoveIf<qreal>(ML::AttrLeft, margins.left(), [](qreal margin) noexcept {return margin <= 0;});
SetAttributeOrRemoveIf<qreal>(ML::AttrTop, margins.top(), [](qreal margin) noexcept {return margin <= 0;});
SetAttributeOrRemoveIf<qreal>(ML::AttrRight, margins.right(), [](qreal margin) noexcept {return margin <= 0;});
SetAttributeOrRemoveIf<qreal>(ML::AttrBottom, margins.bottom(), [](qreal margin) noexcept {return margin <= 0;});
SetAttributeOrRemoveIf<bool>(ML::AttrIgnoreMargins, ignore, [](bool ignore) noexcept {return not ignore;});
writeEndElement(); // margin
}
//---------------------------------------------------------------------------------------------------------------------
void VPLayoutFileWriter::WriteSize(QSizeF size)
{
// maybe not necessary to test this, the writer should "stupidly write", the application should take care of these tests
qreal width = size.width();
if(width < 0)
{
width = 0;
}
qreal length = size.height();
if(length < 0)
{
length = 0;
}
writeStartElement(ML::TagSize);
SetAttribute(ML::AttrWidth, width);
SetAttribute(ML::AttrLength, length);
writeEndElement(); // size
}
//---------------------------------------------------------------------------------------------------------------------
auto VPLayoutFileWriter::WriteLayoutPoint(const VLayoutPoint &point) -> void
{
writeStartElement(ML::TagPoint);
SetAttribute(ML::AttrX, point.x());
SetAttribute(ML::AttrY, point.y());
SetAttributeOrRemoveIf<bool>(ML::AttrTurnPoint, point.TurnPoint(), [](bool val) noexcept {return val;});
SetAttributeOrRemoveIf<bool>(ML::AttrCurvePoint, point.CurvePoint(), [](bool val) noexcept {return val;});
writeEndElement();
}