PSD has a weird "optimization": if a group layer has only one child layer, it omits it's 'psd_bounding_divider' section. So fi you ever see an unbalanced layers group in PSD, most probably, it is just a single layered group.
In some files saved by PS CS6 the group layer sections seem to be unbalanced. I don't know why it happens because the reporter didn't provide us an example file. So here we just check if the new layer was created, and if not, skip the initialization of masks.
75{
76
77
79
81 if (!header.
read(io)) {
84 }
85
87 dbgFile <<
"Read header. pos:" << io.pos();
88
90 if (!colorModeBlock.read(io)) {
91 dbgFile <<
"failed reading colormode block: " << colorModeBlock.error;
93 }
94
95 dbgFile <<
"Read color mode block. pos:" << io.pos();
96
98 if (!resourceSection.
read(io)) {
99 dbgFile <<
"failed image reading resource section: " << resourceSection.
error;
101 }
102 dbgFile <<
"Read image resource section. pos:" << io.pos();
103
105 if (!layerSection.read(io)) {
106 dbgFile <<
"failed reading layer/mask section: " << layerSection.error;
108 }
109 dbgFile <<
"Read layer/mask section. " << layerSection.nLayers <<
"layers. pos:" << io.pos();
110
111
112
113
114
116 header.channelDepth);
117 if (colorSpaceId.first.isNull()) {
118 dbgFile <<
"Unsupported colorspace" << header.colormode << header.channelDepth;
120 }
121
122
126 if (iccProfileData ) {
128 colorSpaceId.second,
129 iccProfileData->
icc);
130 dbgFile <<
"Loaded ICC profile" << profile->
name();
132 }
133 }
134
135
137 if (!cs) {
139 }
140
141
142 QFile *
file =
dynamic_cast<QFile *
>(&io);
146
147 KisImageBarrierLock lock(
m_image);
148
149
152 if (resInfo) {
153
154 if (resInfo->
hRes * resInfo->
vRes > 0)
156
158 }
159 }
160
163 if (gridGuidesInfo) {
167 }
170 }
173 }
174 }
175
176
179 }
180
181
182 if (header.colormode ==
DuoTone) {
184 i18n("Duotone Colormode Block"),
185 colorModeBlock.data);
187 }
188
189
190
192 layerSection.globalInfoSection.embeddedPatterns;
193
195
197
199 if (!embeddedPatterns.isEmpty()) {
200 Q_FOREACH (const QDomDocument &doc, embeddedPatterns) {
202 }
204 if (pattern && pattern->valid()) {
205 resourceProxy.addResource(pattern);
206 dbgFile <<
"Loaded embedded pattern: " << pattern->name();
207 }
208 else {
209 qWarning() << "Invalid or empty pattern" << pattern;
210 }
211 }
212 }
213
214
215
216
217
218 if (layerSection.nLayers == 0) {
219 dbgFile <<
"Position" << io.pos() <<
"Going to read the projection into the first layer, which Photoshop calls 'Background'";
220
222
225
227
228
230 }
231
232
233
236
244
245 typedef QPair<QDomDocument, KisLayerSP> LayerStyleMapping;
247 using namespace std::placeholders;
248
249 bool convertTextToShape = true;
250 for (int i = 0; i < layerSection.nLayers; i++) {
251 if (!layerSection.layers.at(i)->infoBlocks.textData.isNull()) {
254 QMessageBox::StandardButton btn = QMessageBox::question(parent,
255 i18nc("@title:window PSD import question about text.", "Found Text Layers"),
256 i18nc("PSD import question about text",
257 "Found text objects, do you wish to load them as editable text shapes? "
258 "If not, they will be loaded as pixel data, which will be visually"
259 " more accurate to the original file."));
260 return (btn == QMessageBox::Yes);
261 });
264 break;
265 }
266 }
267
268
269 for(int i = 0; i < layerSection.nLayers; ++i) {
270
272 dbgFile <<
"Going to read channels for layer" << i << layerRecord->
layerName;
276
280 groupStack.push(groupLayer);
281 newLayer = groupLayer;
282 }
285 (groupStack.size() > 1 || (lastAddedLayer && !groupStack.isEmpty()))) {
287
288 if (groupStack.size() <= 1) {
292 } else {
293 groupLayer = groupStack.pop();
294 }
295
297
298 if (!styleXml.isNull()) {
299 allStylesXml << LayerStyleMapping(styleXml, groupLayer);
300 }
301
305
308 }
309
311
312
313
314
318 }
319
321
322 newLayer = groupLayer;
323 } else {
334 warnKrita <<
"WARNING: Provided PSD has unbalanced group "
335 << "layer markers. Some masks and/or layers can "
336 << "be lost while loading this file. Please "
337 << "report a bug to Krita developers and attach "
338 << "this file to the bugreport\n"
341 <<
" " <<
ppVar(groupStack.size());
342 continue;
343 }
344 } else {
348 QDomDocument fillConfig;
350
359 }
367 } else {
369 double angle;
372 QTransform scaleToPt = QTransform::fromScale(resMultiplier, resMultiplier).inverted();
373 size = QSizeF(
size.width()/resMultiplier,
size.height()/resMultiplier);
374
377 if (shapeName == "RectangleShape") {
382 } else if (shapeName == "StarShape") {
385
388 double totalHeight = a + 100.0;
389 double l =
size.height() / totalHeight * 100.0;
390
392
395 }
399 }
400 KoShape *shape =
f->createShape(&props, &manager);
401 if (!shape)
402 continue;
404 QTransform t;
405 t.rotate(360.0-angle);
406
409
410 vectorMask = shape;
411 }
412 }
415
423 if (vectorMask) {
425 }
426
429
432
436 if (vectorMask) {
438 }
439
440 } else {
442
448
450 if (vectorMask) {
452 }
453 }
454 if (vectorMask) {
456
457
470
473 }
477 stroke->setColor(c);
479 stroke->setLineBrush(grad.
getBrush());
480 }
481 } else {
482 stroke->setColor(Qt::transparent);
483 }
485
487 }
488
490 layer = shapeLayer;
491 } else {
492 cfg->fromXML(fillConfig.firstChildElement());
493 cfg->createLocalResourcesSnapshot();
496 layer = genlayer;
497 }
498
499
510
511 QString svg;
512 QString styles;
513
514 bool offsetByAscent = false;
515 QPointF offset1;
516
517
518
521 layerSection.globalInfoSection.txt2Data,
524 &svg, &styles,
525 offset1, offsetByAscent,
527 if (!res || !converter.
errors().isEmpty()) {
528 qWarning() << converter.
errors();
529 }
532 if (offsetByAscent) {
533 QPointF offset2 = QPointF() - shape->
outlineRect().topLeft();
535 offset2.setX(offset1.x());
536 } else {
537 offset2.setY(offset1.y());
538 }
539 shape->
setTransformation(QTransform::fromTranslate(offset2.x(), offset2.y()) * scaleToPt.inverted()
541 } else {
543 }
545 layer = textLayer;
546 } else {
551 }
552 }
554
556
558
559 if (!styleXml.isNull()) {
560 allStylesXml << LayerStyleMapping(styleXml, layer);
561 }
562
563 if (!groupStack.isEmpty()) {
565 }
566 else {
568 }
571 newLayer = layer;
572
573 }
574
578 const KisShapeLayer *shapeLayer = qobject_cast<KisShapeLayer *>(newLayer.
data());
585 }
587 if (fillLayer) {
589 dbgFile <<
"failed reading masks for generator layer: " << layerRecord->
layerName << layerRecord->
error;
590 }
591 if (hasVectorMask) {
594 shapeSelection->
addShape(vectorMask);
596 }
597 } else {
598 if (!(shapeLayer && hasVectorMask)) {
603 }
604 if (hasVectorMask) {
607 shapeSelection->
addShape(vectorMask);
609 }
611 }
612 }
613 }
614 }
615
616 lastAddedLayer = newLayer;
617 }
618
619 if (!allStylesXml.isEmpty()) {
620 Q_FOREACH (const LayerStyleMapping &mapping, allStylesXml) {
621
623
624 if (serializer.
styles().size() == 1) {
627
629 if (gradient && gradient->valid()) {
630 resourceProxy.addResource(gradient);
631 }
632 else {
633 qWarning() << "Invalid or empty gradient" << gradient;
634 }
635 }
636
638 if (pattern && pattern->valid()) {
639 resourceProxy.addResource(pattern);
640 } else {
641 qWarning() << "Invalid or empty pattern" << pattern;
642 }
643 }
644
645 layerStyle->setName(layer->
name());
646 layerStyle->setResourcesInterface(resourceProxy.detachedResourcesInterface());
647 if (!layerStyle->uuid().isNull()) {
648 layerStyle->setUuid(QUuid::createUuid());
649 }
650 layerStyle->setValid(true);
651
652 resourceProxy.addResource(layerStyle);
653
654 layer->
setLayerStyle(layerStyle->cloneWithResourcesSnapshot(layerStyle->resourcesInterface(), 0));
655 } else {
657 }
658
659 }
660 }
661
663}
const quint8 OPACITY_OPAQUE_U8
const QString COMPOSITE_OVER
const QString COMPOSITE_PASS_THROUGH
constexpr qreal POINT_TO_INCH(qreal px)
A data extension mechanism for Krita.
QVector< KoAbstractGradientSP > gradients() const
QVector< KisPSDLayerStyleSP > styles() const
void readFromPSDXML(const QDomDocument &doc)
void registerPSDPattern(const QDomDocument &doc)
QHash< QString, KoPatternSP > patterns() const
void parseXML(const QDomDocument &doc, KisAslObjectCatcher &catcher)
KisUndoStore * createUndoStore()
KisShapeController * shapeController
QString embeddedResourcesStorageId() const
void setGuidesConfig(const KisGuidesConfig &data)
KisGuidesConfig guidesConfig
static KisGeneratorRegistry * instance()
void setShowGuides(bool value)
void addGuideLine(Qt::Orientation orientation, qreal position)
Add a guide line to the canvas.
void addAnnotation(KisAnnotationSP annotation)
KisGroupLayerSP rootLayer() const
const KoColorSpace * colorSpace() const
KisSelectionSP globalSelection() const
QRect bounds() const override
void setResolution(double xres, double yres)
virtual Result askUser(AskCallback callback)=0
ask the user a question about the loading process
const T value(const QString &id) const
The position of a path point within a path shape.
int pointCount() const
Returns the number of points in the path.
void setProperty(const QString &name, const QVariant &value)
void addShape(KoShape *shape)
static KoShapeRegistry * instance()
virtual void setStroke(KoShapeStrokeModelSP stroke)
void setTransformation(const QTransform &matrix)
virtual void setBackground(QSharedPointer< KoShapeBackground > background)
void setUserData(KoShapeUserData *userData)
void setAbsolutePosition(const QPointF &newPosition, KoFlake::AnchorPosition anchor=KoFlake::Center)
virtual void setSize(const QSizeF &size)
Resize the shape.
QRectF outlineRect() const override
QMap< PSDResourceID, PSDResourceBlock * > resources
PsdAdditionalLayerInfoBlock infoBlocks
bool readPixelData(QIODevice &io, KisPaintDeviceSP device)
QVector< ChannelInfo * > channelInfoRecords
KoPathShape * constructPathShape(psd_path path, double shapeWidth, double shapeHeight)
constructPathShape create a KoPathshape based on a psd_path struct, used in vector masks and path res...
bool readMask(QIODevice &io, KisPaintDeviceSP dev, ChannelInfo *channel)
psd_section_type sectionDividerType
QDomDocument vectorStroke
QDomDocument vectorOriginationData
QString sectionDividerBlendMode
QDomDocument layerStyleXml
psd_vector_mask vectorMask
The PsdTextDataConverter class.
bool convertPSDTextEngineDataToSVG(const QVariantHash tySh, const QVariantHash txt2, const KoColorSpace *imageCs, const int textIndex, QString *svgText, QString *svgStyles, QPointF &offset, bool &offsetByAscent, bool &isHorizontal, QTransform scaleToPt=QTransform())
QStringList errors() const
QStringList warnings() const
T kisDegreesToRadians(T degrees)
KisSharedPtr< KisNode > KisNodeSP
@ FormatColorSpaceUnsupported
const char * name(StandardAction id)
int size(const Forest< T > &forest)
KisImportExportErrorCode workaroundUnsuitableImageColorSpace(KisImageSP image, KisImportUserFeedbackInterface *feedbackInterface, KisImageBarrierLock &lock)
QPair< QString, QString > psd_colormode_to_colormodelid(psd_color_mode colormode, quint16 channelDepth)
QString psd_blendmode_to_composite_op(const QString &blendmode)
QList< quint32 > verticalGuides
QList< quint32 > horizontalGuides
virtual void setUserLocked(bool l)
virtual void setVisible(bool visible, bool loading=false)
void setColorLabelIndex(int index)
void setName(const QString &name)
void setCollapsed(bool collapsed)
virtual KisPaintDeviceSP paintDevice() const =0
void setCompositeOpId(const QString &compositeOpId)
virtual KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
void setFilter(KisFilterConfigurationSP filterConfig, bool checkCompareConfig=true) override
void setPassThroughMode(bool value)
void setLayerStyle(KisPSDLayerStyleSP layerStyle)
void initSelection(KisSelectionSP copyFrom, KisLayerSP parentLayer)
initSelection initializes the selection for the mask from the given selection's projection.
KisPaintDeviceSP paintDevice() const override
bool addNode(KisNodeSP node, KisNodeSP parent=KisNodeSP(), KisNodeAdditionFlags flags=KisNodeAdditionFlag::None)
bool moveNode(KisNodeSP node, KisNodeSP parent, KisNodeSP aboveThis)
KisPaintDeviceSP paintDevice
KisPaintDeviceSP paintDevice
KisSelectionSP internalSelection() const
void updateProjection(const QRect &rect)
void convertToVectorSelectionNoUndo(KisSelectionComponent *shapeSelection)
const KoColorSpace * colorSpace(const QString &colorModelId, const QString &colorDepthId, const KoColorProfile *profile)
static KoColorSpaceRegistry * instance()
const KoColorProfile * createColorProfile(const QString &colorModelId, const QString &colorDepthId, const QByteArray &rawData)
static void setupCatcher(const QString path, KisAslCallbackObjectCatcher &catcher, psd_layer_gradient_fill *data)
QDomDocument getFillLayerConfig()
QSharedPointer< KoShapeBackground > getBackground()
QSharedPointer< KoShapeBackground > getBackground(KisEmbeddedResourceStorageProxy &embeddedProxy)
static void setupCatcher(const QString path, KisAslCallbackObjectCatcher &catcher, psd_layer_pattern_fill *data)
QDomDocument getFillLayerConfig() const
QSharedPointer< KoShapeBackground > getBackground()
static void setupCatcher(const QString path, KisAslCallbackObjectCatcher &catcher, psd_layer_solid_color *data)
QDomDocument getFillLayerConfig()
static void setupCatcher(const QString path, KisAslCallbackObjectCatcher &catcher, psd_layer_type_shape *data)
QList< psd_path_sub_path > subPaths
QRectF originShapeBBox
pre-transform bbox.
static void setupCatcher(const QString path, KisAslCallbackObjectCatcher &catcher, psd_vector_origination_data *data)
double originResolution
Resolution of the coordinates.
int originPolySides
Only for 7 and 8.
void OriginalSizeAndAngle(QSizeF &size, double &angle)
bool canMakeParametricShape()
double originPolyStarRatio
Only for 8, percentage of small radius to big radius.
static void setupCatcher(const QString path, KisAslCallbackObjectCatcher &catcher, psd_vector_stroke_data *data)
void setupShapeStroke(KoShapeStrokeSP stroke)