55 : globalInfoSection(header)
73 retval = readTiffImpl<psd_byte_order::psdLittleEndian>(io);
83 warnKrita <<
"WARNING: PSD (emb. pattern):" << e.what();
90template<psd_
byte_order
byteOrder>
93 quint64 layerInfoSectionSize = 0;
95 quint32 _layerInfoSectionSize = 0;
97 layerInfoSectionSize = _layerInfoSectionSize;
102 if (layerInfoSectionSize & 0x1) {
103 warnKrita <<
"WARNING: layerInfoSectionSize is NOT even! Fixing...";
104 layerInfoSectionSize++;
109 dbgFile <<
"Layer info block size" << layerInfoSectionSize;
111 if (layerInfoSectionSize > 0) {
113 error = QString(
"Could not read number of layers or no layers in image. %1").arg(
nLayers);
123 for (
int i = 0; i <
nLayers; ++i) {
124 dbgFile <<
"Going to read layer" << i <<
"pos" << io.pos();
125 dbgFile <<
"== Enter PSDLayerRecord";
128 QScopedPointer<PSDLayerRecord> layerRecord(
new PSDLayerRecord(sanitizedHeader));
129 if (!layerRecord->read(io)) {
130 error = QString(
"Could not load layer %1: %2").arg(i).arg(layerRecord->error);
133 dbgFile <<
"== Leave PSDLayerRecord";
134 dbgFile <<
"Finished reading layer" << i << layerRecord->layerName <<
"blending mode" << layerRecord->blendModeKey << io.pos()
135 <<
"Number of channels:" << layerRecord->channelInfoRecords.size();
136 layers << layerRecord.take();
141 for (
int i = 0; i <
nLayers; ++i) {
142 dbgFile <<
"Going to seek channel positions for layer" << i <<
"pos" << io.pos();
144 error = QString(
"Expected layer %1, but only have %2 layers").arg(i).arg(
layers.size());
150 for (
int j = 0; j < layerRecord->
nChannels; ++j) {
152 quint64 channelStartPos = io.pos();
153 dbgFile <<
"\tReading channel image data for channel" << j <<
"from pos" << io.pos();
162 quint16 compressionType;
163 if (!psdread<byteOrder>(io, compressionType)) {
164 error =
"Could not read compression type for channel";
168 dbgFile <<
"\t\tChannel" << j <<
"has compression type" << compressionType;
170 QRect channelRect = layerRecord->
channelRect(channelInfo);
174 for (qint64 row = 0; row < channelRect.height(); ++row) {
180 if (!psdread<byteOrder>(io, _byteCount)) {
181 error = QString(
"Could not read byteCount for rle-encoded channel");
184 byteCount = _byteCount;
186 if (!psdread<byteOrder>(io, byteCount)) {
187 error = QString(
"Could not read byteCount for rle-encoded channel");
201 <<
"pos" << io.pos();
209 dbgFile <<
"\t\tchannel record" << j <<
"for layer" << i <<
"with id" << channelInfo->
channelId <<
"starting position"
221 dbgFile <<
"(PSD) reading layer section. Pos:" << io.pos() <<
"bytes left:" << io.bytesAvailable();
224 boost::optional<quint64> layerMaskBlockSize = 0;
227 quint32 _layerMaskBlockSize = 0;
229 layerMaskBlockSize = _layerMaskBlockSize;
234 qint64 start = io.pos();
236 dbgFile <<
"layer block size" << *layerMaskBlockSize;
238 if (*layerMaskBlockSize == 0) {
239 dbgFile <<
"No layer info, so no PSD layers available";
248 if (
static_cast<qint64
>(*layerMaskBlockSize) > io.bytesAvailable()) {
249 warnKrita <<
"WARNING: invalid layer block size. Got" << *layerMaskBlockSize <<
"Bytes left" << io.bytesAvailable() <<
"Triggering a workaround...";
252 layerMaskBlockSize = boost::none;
259 dbgFile <<
"Leftover before additional blocks:" << io.pos() << io.bytesAvailable();
261 quint32 globalMaskBlockLength;
262 if (!
psdread(io, globalMaskBlockLength)) {
263 error =
"Could not read global mask info block";
267 dbgFile <<
"Global mask size:" << globalMaskBlockLength <<
"(" << io.pos() << io.bytesAvailable() <<
")";
269 if (globalMaskBlockLength > 0) {
271 error =
"Could not read global mask info overlay colorspace";
275 for (
int i = 0; i < 4; ++i) {
277 error = QString(
"Could not read mask info visualization color component %1").arg(i);
283 error =
"Could not read global mask info visualization opacity";
288 error =
"Could not read global mask info visualization type";
294 if (globalMaskBlockLength >= 13) {
295 dbgFile <<
"Padding for global mask block:"
296 << globalMaskBlockLength - 13 <<
"(" << io.pos() <<
")";
297 io.skip(
static_cast<size_t>(globalMaskBlockLength) - 13);
312 std::bind(&PSDLayerMaskSection::readLayerInfoImpl<psd_byte_order::psdBigEndian>,
this, std::placeholders::_1));
314 dbgFile <<
"Position before starting global info section:" << io.pos();
318 if (layerMaskBlockSize) {
320 io.seek(start +
static_cast<qint64
>(*layerMaskBlockSize));
326template<psd_
byte_order
byteOrder>
329 quint32 globalMaskBlockLength;
330 if (!psdread<byteOrder>(io, globalMaskBlockLength)) {
331 error =
"Could not read global mask info block";
335 dbgFile <<
"Global mask size:" << globalMaskBlockLength <<
"(" << io.pos() << io.bytesAvailable() <<
")";
337 if (globalMaskBlockLength > 0) {
339 error =
"Could not read global mask info overlay colorspace";
343 for (
int i = 0; i < 4; ++i) {
345 error = QString(
"Could not read mask info visualization color component %1").arg(i);
351 error =
"Could not read global mask info visualization opacity";
356 error =
"Could not read global mask info visualization type";
360 dbgFile <<
"Global mask info: ";
369 if (globalMaskBlockLength >= 15) {
370 io.skip(qMax(globalMaskBlockLength - 15, 0x0U));
377template<psd_
byte_order
byteOrder>
380 dbgFile <<
"(TIFF) reading layer section. Pos:" << io.pos() <<
"bytes left:" << io.bytesAvailable();
398 dbgFile <<
"Failed to read TIFF Photoshop blocks!";
402 dbgFile <<
"Leftover data after parsing layer/extra blocks:" << io.pos() << io.bytesAvailable() << io.peek(io.bytesAvailable());
443 Q_FOREACH (
KoShape *shape, shapes) {
444 const QString name = shape->
name().isEmpty()?
"shape "+QString::number(nodes.size()): shape->
name();
455 item.
node = newGroup;
462 item.
node = newGroup;
468 parentShapeLayer->
image(),
481 item.
node = newLayer;
493 const bool isLayer = child->inherits(
"KisLayer");
494 const bool isGroupLayer = child->inherits(
"KisGroupLayer");
513 }
else if (shapeLayer) {
514 if (shapeLayer->
shapes().size() > 1) {
551 KisLayer *layer = qobject_cast<KisLayer *>(node.
data());
554 if (masks.size() != 1)
558 return onlyMask->inherits(
"KisTransparencyMask") ? onlyMask : 0;
563 const KisLayer *layer = qobject_cast<KisLayer *>(node.
data());
567 return QDomDocument();
584 if (srcPatternsNode.isNull())
586 if (dstPatternsNode.isNull()) {
594 QDomNode node = srcPatternsNode.firstChild();
595 while (!node.isNull()) {
596 QDomNode importedNode = dst.importNode(node,
true);
599 dstPatternsNode.appendChild(importedNode);
600 node = node.nextSibling();
612 writeTiffImpl<psd_byte_order::psdLittleEndian>(io, rootLayer, compressionType);
631 dbgFile <<
"Writing layer section";
641 if (nodes.isEmpty()) {
647 QDomDocument mergedPatternsXmlDoc;
654 const qint16 layersSize =
static_cast<qint16
>(-nodes.size());
657 dbgFile <<
"Number of layers" << layersSize <<
"at" << io.pos();
665 layers.append(layerRecord);
670 const bool nodeVisible = node->
visible();
672 const quint8 nodeOpacity = node->
opacity();
673 const quint8 nodeClipping = 0;
676 const bool alphaLocked = (paintLayer && paintLayer->
alphaLocked());
680 const bool nodeIsPassThrough = groupLayer && groupLayer->
passThroughMode();
683 QDomDocument fillConfig;
686 QString generatorName = fillLayer->
filter()->name();
687 if (generatorName ==
"color") {
698 }
else if (generatorName ==
"gradient") {
706 }
else if (generatorName ==
"pattern") {
713 QString uuid = w.writePattern(
"", fill.
pattern);
715 mergedPatternsXmlDoc = w.document();
728 QTransform FlaketoPixels = QTransform::fromScale(rootLayer->
image()->
xRes(), rootLayer->
image()->
yRes());
733 QDomDocument strokeData;
734 QDomDocument vogkData;
736 if (shapeLayer && !shapeLayer->
isFakeNode()) {
738 if (shapeLayer->
shapes().size() == 1) {
752 if (!inlineSizeProp.
isAuto) {
763 if (!res && !convert.
errors().isEmpty()) {
764 qWarning() << convert.
errors();
773 if (!textData.
bounds.isEmpty()) {
775 textData.
bounds = FlaketoPixels.mapRect(textData.
bounds);
839 strokeData = strokeDataStruct.
getASLXML();
848 if (mergedPatternsXmlDoc.isNull() && !stylesXmlDoc.isNull()) {
849 mergedPatternsXmlDoc = stylesXmlDoc;
850 }
else if (!mergedPatternsXmlDoc.isNull() && !stylesXmlDoc.isNull()) {
854 bool nodeIrrelevant =
false;
860 nodeIrrelevant =
false;
861 nodeName = node->
name();
870 if (transparency || semiOpacity) {
875 if (shapeSelection) {
886 layerContentDevice = node->
original();
887 onlyTransparencyMask = node;
897 if (shapeSelection) {
911 nodeIrrelevant =
true;
913 layerContentDevice = 0;
923 if (layerContentDevice) {
925 rc = rc.normalized();
928 if (rc.width() > 30000)
930 if (rc.height() > 30000)
936 layerRecord->
top = layerRect.y();
937 layerRecord->
left = layerRect.x();
938 layerRecord->
bottom = layerRect.y() + layerRect.height();
939 layerRecord->
right = layerRect.x() + layerRect.width();
958 layerRecord->
opacity = nodeOpacity;
959 layerRecord->
clipping = nodeClipping;
964 layerRecord->
visible = nodeVisible;
967 layerRecord->
layerName = nodeName.isEmpty() ? i18n(
"Unnamed Layer") : nodeName;
978 layerRecord->
write(io, layerContentDevice, onlyTransparencyMask, maskRect, sectionType, stylesXmlDoc, node->inherits(
"KisGroupLayer"));
981 dbgFile <<
"start writing layer pixel data" << io.pos();
985 layerRecord->writePixelData(io, compressionType);
991 const quint32 globalMaskSize = 0;
1016 if (textCount > 0) {
1023template<psd_
byte_order
byteOrder>
1026 dbgFile <<
"(TIFF) Writing layer section";
1033 if (nodes.isEmpty()) {
1038 QDomDocument mergedPatternsXmlDoc;
1041 KisAslWriterUtils::writeFixedString<byteOrder>(
"8BIM", io);
1042 KisAslWriterUtils::writeFixedString<byteOrder>(
"Layr", io);
1046 const qint16 layersSize = nodes.size();
1049 dbgFile <<
"Number of layers" << layersSize <<
"at" << io.pos();
1056 layers.append(layerRecord);
1058 const bool nodeVisible = node->
visible();
1060 const quint8 nodeOpacity = node->
opacity();
1061 const quint8 nodeClipping = 0;
1064 const bool alphaLocked = (paintLayer && paintLayer->
alphaLocked());
1068 const bool nodeIsPassThrough = groupLayer && groupLayer->
passThroughMode();
1072 if (mergedPatternsXmlDoc.isNull() && !stylesXmlDoc.isNull()) {
1073 mergedPatternsXmlDoc = stylesXmlDoc;
1074 }
else if (!mergedPatternsXmlDoc.isNull() && !stylesXmlDoc.isNull()) {
1078 bool nodeIrrelevant =
false;
1084 nodeIrrelevant =
false;
1085 nodeName = node->
name();
1089 nodeIrrelevant =
true;
1091 layerContentDevice = 0;
1101 if (layerContentDevice) {
1103 rc = rc.normalized();
1107 if (rc.width() > 30000)
1109 if (rc.height() > 30000)
1110 rc.setHeight(30000);
1115 layerRecord->
top = layerRect.y();
1116 layerRecord->
left = layerRect.x();
1117 layerRecord->
bottom = layerRect.y() + layerRect.height();
1118 layerRecord->
right = layerRect.x() + layerRect.width();
1131 info->
channelId =
static_cast<qint16
>(i);
1137 layerRecord->
opacity = nodeOpacity;
1138 layerRecord->
clipping = nodeClipping;
1141 layerRecord->
visible = nodeVisible;
1145 layerRecord->
layerName = nodeName.isEmpty() ? i18n(
"Unnamed Layer") : nodeName;
1147 layerRecord->
write(io, layerContentDevice,
nullptr, QRect(), sectionType, stylesXmlDoc, node->inherits(
"KisGroupLayer"));
1150 dbgFile <<
"start writing layer pixel data" << io.pos();
1154 layerRecord->writePixelData(io, compressionType);
const quint8 OPACITY_TRANSPARENT_U8
const quint8 OPACITY_OPAQUE_U8
QDomDocument formPsdXmlDocument() const
void setStyles(const QVector< KisPSDLayerStyleSP > &styles)
const KoColorSpace * colorSpace() const
virtual KisFilterConfigurationSP filter() const
void setDefaultPixel(const KoColor &defPixel)
QRect exactBounds() const
KoColor defaultPixel() const
static bool checkDeviceHasTransparency(KisPaintDeviceSP dev)
void setVisible(bool visible, bool isLoading=false) override
KoShapeManager * shapeManager() const
static QVariantHash tyShFromTxt2(const QVariantHash Txt2, const QRectF boundsInPx, int textIndex)
tyShFromTxt2 Txt2 is more or less a superset of tySh, so this function allows generating the tySh dat...
static QVariantHash defaultTxt2()
defaultTxt2 Generate a default txt2 varianthash. This includes stuff like Kinsoku sets default fonts ...
A simple solid color shape background.
virtual quint32 colorChannelCount() const =0
A gradient shape background.
const QGradient * gradient() const
Returns the gradient.
The position of a path point within a path shape.
virtual QString pathShapeId() const
QRectF outlineRect() const override
reimplemented
int pointCount() const
Returns the number of points in the path.
A pattern shape background.
QList< KoShape * > shapes() const
void addShape(KoShape *shape)
@ AddWithoutRepaint
Avoids each shapes 'update()' to be called for faster addition when its possible.
void addShape(KoShape *shape, KoShapeManager::Repaint repaint=PaintShapeOnAdd)
void setTransparency(qreal transparency)
virtual KoShapeStrokeModelSP stroke() const
QTransform absoluteTransformation() const
void setTransformation(const QTransform &matrix)
virtual KoShape * cloneShape() const
creates a deep copy of the shape or shape's subtree
virtual QSharedPointer< KoShapeBackground > background() const
bool isVisible(bool recursive=true) const
qreal transparency(bool recursive=false) const
@ InlineSizeId
KoSvgText::AutoValue.
QVariant property(PropertyId id, const QVariant &defaultValue=QVariant()) const
bool convertToSvg(QString *svgText, QString *stylesText)
QRectF boundingRect() const override
Get the bounding box of the shape.
QList< KoShape * > shapesInside
KoSvgText::WritingMode writingMode() const
writingMode There's a number of places we need to check the writing mode to provide proper controls.
QRectF outlineRect() const override
KoSvgTextProperties textProperties() const
bool readGlobalMask(QIODevice &io)
bool readTiffImpl(QIODevice &io)
void writeTiffImpl(QIODevice &io, KisNodeSP rootLayer, psd_compression_type compressionType)
bool readLayerInfoImpl(QIODevice &io)
void writePsdImpl(QIODevice &io, KisNodeSP rootLayer, psd_compression_type compressionType)
bool readPsdImpl(QIODevice &io)
PsdAdditionalLayerInfoBlock globalInfoSection
bool write(QIODevice &io, KisNodeSP rootLayer, psd_compression_type compressionType)
QVector< PSDLayerRecord * > layers
PSDLayerMaskSection(const PSDHeader &header)
GlobalLayerMaskInfo globalLayerMaskInfo
psd_vector_mask vectorMask
psd_layer_type_shape textShape
QDomDocument vectorOriginationData
QVector< ChannelInfo * > channelInfoRecords
QRect channelRect(ChannelInfo *channel) const
QDomDocument vectorStroke
void write(QIODevice &io, KisPaintDeviceSP layerContentDevice, KisNodeSP onlyTransparencyMask, const QRect &maskRect, psd_section_type sectionType, const QDomDocument &stylesXmlDoc, bool useLfxsLayerStyleFormat)
void addPathShapeToPSDPath(psd_path &path, KoPathShape *shape, double shapeWidth, double shapeHeight)
addPathShapeToPSDPath add all KoPathShape subpaths to the given psd_path struct.
bool transparencyProtected
void writeTxt2BlockEx(QIODevice &io, const QVariantHash txt2Hash)
void writePattBlockEx(QIODevice &io, const QDomDocument &patternsXmlDoc)
void setExtraLayerInfoBlockHandler(ExtraLayerInfoBlockHandler handler)
void setUserMaskInfoBlockHandler(UserMaskInfoBlockHandler handler)
The PsdTextDataConverter class.
QStringList errors() const
bool convertToPSDTextEngineData(const QString &svgText, QRectF &boundingBox, const QList< KoShape * > &shapesInside, QVariantHash &txt2, int &textIndex, QString &textTotal, bool &isHorizontal, QTransform scaleToPx=QTransform())
QStringList warnings() const
#define SAFE_READ_EX(byteOrder, device, varname)
#define SAFE_WRITE_EX(byteOrder, device, varname)
#define KIS_ASSERT_RECOVER(cond)
#define KIS_ASSERT_RECOVER_RETURN(cond)
#define PREPEND_METHOD(msg)
static bool isLayer(KisNodeSP node)
#define SETUP_OFFSET_VERIFIER(name, device, expectedOffset, maxPadding)
QDomElement findElementByAttribute(QDomNode parent, const QString &tag, const QString &attribute, const QString &value)
QString composite_op_to_psd_blendmode(const QString &compositeop)
QDomDocument fetchLayerStyleXmlData(KisNodeSP node)
KisNodeSP findOnlyTransparencyMask(KisNodeSP node, FlattenedNode::Type type)
QDomNode findNodeByKey(const QString &key, QDomNode parent)
void flattenShapes(const KisShapeLayer *parentShapeLayer, QList< KoShape * > shapes, QList< FlattenedNode > &nodes)
void flattenNodes(KisNodeSP node, QList< FlattenedNode > &nodes)
void addBackgroundIfNeeded(KisNodeSP root, QList< FlattenedNode > &nodes)
void mergePatternsXMLSection(const QDomDocument &src, QDomDocument &dst)
std::enable_if_t< std::is_arithmetic< T >::value, bool > psdread(QIODevice &io, T &v)
QVector< quint32 > rleRowLengths
psd_compression_type compressionType
quint64 channelDataLength
virtual KisPaintDeviceSP projection() const =0
const QString & compositeOpId() const
void setOpacity(quint8 val)
virtual void setVisible(bool visible, bool loading=false)
virtual KisPaintDeviceSP original() const =0
virtual const KoColorSpace * colorSpace() const =0
int colorLabelIndex() const
virtual KisPaintDeviceSP paintDevice() const =0
virtual bool isFakeNode() const
virtual bool visible(bool recursive=false) const
KoColor defaultProjectionColor() const
QList< KisEffectMaskSP > effectMasks() const
KisPSDLayerStyleSP layerStyle
const KoColorSpace * colorSpace() const override
returns the image's colorSpace or null, if there is no image
KisNodeSP firstChild() const
KisNodeSP nextSibling() const
KisPaintDeviceSP paintDevice
KisSelectionSP internalSelection() const
bool hasNonEmptyShapeSelection() const
KisSelectionComponent * shapeSelection
quint16 colorComponents[4]
quint16 overlayColorSpace
bool loadFromConfig(KisFilterConfigurationSP cfg)
void setFromQGradient(const QGradient *gradient)
bool loadFromConfig(KisFilterConfigurationSP cfg)
void setColor(const KoColor &color)
bool loadFromConfig(KisFilterConfigurationSP cfg)
QRectF originShapeBBox
pre-transform bbox.
int originType
1 = rect, 2 = rounded rect?, 3 = ? 4 = ? 5 = ellipse, 6 = ?, 7 = polygon, 8 = star,...
QPolygonF originBoxCorners
post-transform bbox as a polygon, seen on 1, 7, 8 and 9.
const QMap< int, QString > typeToName
void loadFromShapeStroke(KoShapeStrokeSP stroke)