File indexing completed on 2024-05-26 16:14:23

0001 /* This file is part of the KDE project
0002  * Copyright (C) 2007, 2009-2010 Thomas Zander <zander@kde.org>
0003  * Copyright (C) 2010 Ko Gmbh <cbo@kogmbh.com>
0004  * Copyright (C) 2011 Matus Hanzes <matus.hanzes@ixonos.com>
0005  * Copyright (C) 2013 C. Boemann <cbo@boemann.dk>
0006  *
0007  * This library is free software; you can redistribute it and/or
0008  * modify it under the terms of the GNU Library General Public
0009  * License as published by the Free Software Foundation; either
0010  * version 2 of the License, or (at your option) any later version.
0011  *
0012  * This library is distributed in the hope that it will be useful,
0013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015  * Library General Public License for more details.
0016  *
0017  * You should have received a copy of the GNU Library General Public License
0018  * along with this library; see the file COPYING.LIB.  If not, write to
0019  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0020  * Boston, MA 02110-1301, USA.
0021  */
0022 
0023 #include "KoShapeAnchor.h"
0024 #include "KoStyleStack.h"
0025 #include "KoOdfLoadingContext.h"
0026 
0027 #include <KoShapeContainer.h>
0028 #include <KoXmlWriter.h>
0029 #include <KoXmlReader.h>
0030 #include <KoXmlNS.h>
0031 #include <KoShapeSavingContext.h>
0032 #include <KoShapeLoadingContext.h>
0033 
0034 #include <QRectF>
0035 #include <QTransform>
0036 #include <FlakeDebug.h>
0037 
0038 #include <KoGenChanges.h>
0039 
0040 class Q_DECL_HIDDEN KoShapeAnchor::Private
0041 {
0042 public:
0043     Private(KoShape *s)
0044             : shape(s)
0045             , verticalPos(KoShapeAnchor::VTop)
0046             , verticalRel(KoShapeAnchor::VLine)
0047             , horizontalPos(KoShapeAnchor::HLeft)
0048             , horizontalRel(KoShapeAnchor::HChar)
0049             , flowWithText(true)
0050             , anchorType(KoShapeAnchor::AnchorToCharacter)
0051             , placementStrategy(0)
0052             , pageNumber(-1)
0053             , textLocation(0)
0054     {
0055     }
0056 
0057 
0058     QDebug printDebug(QDebug dbg) const
0059     {
0060 #ifndef NDEBUG
0061         dbg.space() << "KoShapeAnchor" << this;
0062         dbg.space() << "offset:" << offset;
0063         dbg.space() << "shape:" << shape->name();
0064 #endif
0065         return dbg.space();
0066     }
0067 
0068     KoShape * const shape;
0069     QPointF offset;
0070     KoShapeAnchor::VerticalPos verticalPos;
0071     KoShapeAnchor::VerticalRel verticalRel;
0072     KoShapeAnchor::HorizontalPos horizontalPos;
0073     KoShapeAnchor::HorizontalRel horizontalRel;
0074     QString wrapInfluenceOnPosition;
0075     bool flowWithText;
0076     KoShapeAnchor::AnchorType anchorType;
0077     KoShapeAnchor::PlacementStrategy *placementStrategy;
0078     int pageNumber;
0079     KoShapeAnchor::TextLocation *textLocation;
0080 };
0081 
0082 KoShapeAnchor::KoShapeAnchor(KoShape *shape)
0083     : d(new Private(shape))
0084 {
0085 }
0086 
0087 KoShapeAnchor::~KoShapeAnchor()
0088 {
0089     if (d->placementStrategy != 0) {
0090         delete d->placementStrategy;
0091     }
0092 }
0093 
0094 KoShape *KoShapeAnchor::shape() const
0095 {
0096     return d->shape;
0097 }
0098 
0099 KoShapeAnchor::AnchorType KoShapeAnchor::anchorType() const
0100 {
0101     return d->anchorType;
0102 }
0103 
0104 void KoShapeAnchor::setHorizontalPos(HorizontalPos hp)
0105 {
0106     d->horizontalPos = hp;
0107 }
0108 
0109 KoShapeAnchor::HorizontalPos KoShapeAnchor::horizontalPos() const
0110 {
0111     return d->horizontalPos;
0112 }
0113 
0114 void KoShapeAnchor::setHorizontalRel(HorizontalRel hr)
0115 {
0116     d->horizontalRel = hr;
0117 }
0118 
0119 KoShapeAnchor::HorizontalRel KoShapeAnchor::horizontalRel() const
0120 {
0121     return d->horizontalRel;
0122 }
0123 
0124 void KoShapeAnchor::setVerticalPos(VerticalPos vp)
0125 {
0126     d->verticalPos = vp;
0127 }
0128 
0129 KoShapeAnchor::VerticalPos KoShapeAnchor::verticalPos() const
0130 {
0131     return d->verticalPos;
0132 }
0133 
0134 void KoShapeAnchor::setVerticalRel(VerticalRel vr)
0135 {
0136     d->verticalRel = vr;
0137 }
0138 
0139 KoShapeAnchor::VerticalRel KoShapeAnchor::verticalRel() const
0140 {
0141     return d->verticalRel;
0142 }
0143 
0144 QString KoShapeAnchor::wrapInfluenceOnPosition() const
0145 {
0146     return d->wrapInfluenceOnPosition;
0147 }
0148 
0149 bool KoShapeAnchor::flowWithText() const
0150 {
0151     return d->flowWithText;
0152 }
0153 
0154 int KoShapeAnchor::pageNumber() const
0155 {
0156     return d->pageNumber;
0157 }
0158 
0159 const QPointF &KoShapeAnchor::offset() const
0160 {
0161     return d->offset;
0162 }
0163 
0164 void KoShapeAnchor::setOffset(const QPointF &offset)
0165 {
0166     d->offset = offset;
0167 }
0168 
0169 void KoShapeAnchor::saveOdf(KoShapeSavingContext &context) const
0170 {
0171     // anchor-type
0172     switch (d->anchorType) {
0173     case AnchorToCharacter:
0174         shape()->setAdditionalAttribute("text:anchor-type", "char");
0175         break;
0176     case AnchorAsCharacter:
0177         shape()->setAdditionalAttribute("text:anchor-type", "as-char");
0178         break;
0179     case AnchorParagraph:
0180         shape()->setAdditionalAttribute("text:anchor-type", "paragraph");
0181         break;
0182     case AnchorPage:
0183         shape()->setAdditionalAttribute("text:anchor-type", "page");
0184         break;
0185     default:
0186         break;
0187     }
0188 
0189     // vertical-pos
0190     switch (d->verticalPos) {
0191     case VBelow:
0192         shape()->setAdditionalStyleAttribute("style:vertical-pos", "below");
0193         break;
0194     case VBottom:
0195         shape()->setAdditionalStyleAttribute("style:vertical-pos", "bottom");
0196         break;
0197     case VFromTop:
0198         shape()->setAdditionalStyleAttribute("style:vertical-pos", "from-top");
0199         break;
0200     case VMiddle:
0201         shape()->setAdditionalStyleAttribute("style:vertical-pos", "middle");
0202         break;
0203     case VTop:
0204         shape()->setAdditionalStyleAttribute("style:vertical-pos", "top");
0205         break;
0206     default:
0207         break;
0208     }
0209 
0210     // vertical-rel
0211     switch (d->verticalRel) {
0212     case VBaseline:
0213         shape()->setAdditionalStyleAttribute("style:vertical-rel", "baseline");
0214         break;
0215     case VChar:
0216         shape()->setAdditionalStyleAttribute("style:vertical-rel", "char");
0217         break;
0218     case VFrame:
0219         shape()->setAdditionalStyleAttribute("style:vertical-rel", "frame");
0220         break;
0221     case VFrameContent:
0222         shape()->setAdditionalStyleAttribute("style:vertical-rel", "frame-content");
0223         break;
0224     case VLine:
0225         shape()->setAdditionalStyleAttribute("style:vertical-rel", "line");
0226         break;
0227     case VPage:
0228         shape()->setAdditionalStyleAttribute("style:vertical-rel", "page");
0229         break;
0230     case VPageContent:
0231         shape()->setAdditionalStyleAttribute("style:vertical-rel", "page-content");
0232         break;
0233     case VParagraph:
0234         shape()->setAdditionalStyleAttribute("style:vertical-rel", "paragraph");
0235         break;
0236     case VParagraphContent:
0237         shape()->setAdditionalStyleAttribute("style:vertical-rel", "paragraph-content");
0238         break;
0239     case VText:
0240         shape()->setAdditionalStyleAttribute("style:vertical-rel", "text");
0241         break;
0242     default:
0243         break;
0244     }
0245 
0246     // horizontal-pos
0247     switch (d->horizontalPos) {
0248     case HCenter:
0249         shape()->setAdditionalStyleAttribute("style:horizontal-pos", "center");
0250         break;
0251     case HFromInside:
0252         shape()->setAdditionalStyleAttribute("style:horizontal-pos", "from-inside");
0253         break;
0254     case HFromLeft:
0255         shape()->setAdditionalStyleAttribute("style:horizontal-pos", "from-left");
0256         break;
0257     case HInside:
0258         shape()->setAdditionalStyleAttribute("style:horizontal-posl", "inside");
0259         break;
0260     case HLeft:
0261         shape()->setAdditionalStyleAttribute("style:horizontal-pos", "left");
0262         break;
0263     case HOutside:
0264         shape()->setAdditionalStyleAttribute("style:horizontal-pos", "outside");
0265         break;
0266     case HRight:
0267         shape()->setAdditionalStyleAttribute("style:horizontal-pos", "right");
0268         break;
0269     default:
0270         break;
0271     }
0272 
0273     // horizontal-rel
0274     switch (d->horizontalRel) {
0275     case HChar:
0276         shape()->setAdditionalStyleAttribute("style:horizontal-rel", "char");
0277         break;
0278     case HPage:
0279         shape()->setAdditionalStyleAttribute("style:horizontal-rel", "page");
0280         break;
0281     case HPageContent:
0282         shape()->setAdditionalStyleAttribute("style:horizontal-rel", "page-content");
0283         break;
0284     case HPageStartMargin:
0285         shape()->setAdditionalStyleAttribute("style:horizontal-rel", "page-start-margin");
0286         break;
0287     case HPageEndMargin:
0288         shape()->setAdditionalStyleAttribute("style:horizontal-rel", "page-end-margin");
0289         break;
0290     case HFrame:
0291         shape()->setAdditionalStyleAttribute("style:horizontal-rel", "frame");
0292         break;
0293     case HFrameContent:
0294         shape()->setAdditionalStyleAttribute("style:horizontal-rel", "frame-content");
0295         break;
0296     case HFrameEndMargin:
0297         shape()->setAdditionalStyleAttribute("style:horizontal-rel", "frame-end-margin");
0298         break;
0299     case HFrameStartMargin:
0300         shape()->setAdditionalStyleAttribute("style:horizontal-rel", "frame-start-margin");
0301         break;
0302     case HParagraph:
0303         shape()->setAdditionalStyleAttribute("style:horizontal-rel", "paragraph");
0304         break;
0305     case HParagraphContent:
0306         shape()->setAdditionalStyleAttribute("style:horizontal-rel", "paragraph-content");
0307         break;
0308     case HParagraphEndMargin:
0309         shape()->setAdditionalStyleAttribute("style:horizontal-rel", "paragraph-end-margin");
0310         break;
0311     case HParagraphStartMargin:
0312         shape()->setAdditionalStyleAttribute("style:horizontal-rel", "paragraph-start-margin");
0313         break;
0314     default:
0315         break;
0316     }
0317 
0318     if (!d->wrapInfluenceOnPosition.isEmpty()) {
0319         shape()->setAdditionalStyleAttribute("draw:wrap-influence-on-position", d->wrapInfluenceOnPosition);
0320     }
0321 
0322     if (d->flowWithText) {
0323         shape()->setAdditionalStyleAttribute("style:flow-with-text", "true");
0324     } else {
0325         shape()->setAdditionalStyleAttribute("style:flow-with-text", "false");
0326     }
0327 
0328     if (shape()->parent()) {// an anchor may not yet have been layout-ed
0329         QTransform parentMatrix = shape()->parent()->absoluteTransformation(0).inverted();
0330         QTransform shapeMatrix = shape()->absoluteTransformation(0);
0331 
0332         qreal dx = d->offset.x() - shapeMatrix.dx()*parentMatrix.m11()
0333                                    - shapeMatrix.dy()*parentMatrix.m21();
0334         qreal dy = d->offset.y() - shapeMatrix.dx()*parentMatrix.m12()
0335                                    - shapeMatrix.dy()*parentMatrix.m22();
0336         context.addShapeOffset(shape(), QTransform(parentMatrix.m11(),parentMatrix.m12(),
0337                                                 parentMatrix.m21(),parentMatrix.m22(),
0338                                                 dx,dy));
0339     }
0340 
0341     shape()->saveOdf(context);
0342 
0343     context.removeShapeOffset(shape());
0344 }
0345 
0346 bool KoShapeAnchor::loadOdf(const KoXmlElement &element, KoShapeLoadingContext &context)
0347 {
0348     d->offset = shape()->position();
0349 
0350     QString anchorType = shape()->additionalAttribute("text:anchor-type");
0351 
0352     if (anchorType == "char") {
0353         d->anchorType = AnchorToCharacter;
0354     } else if (anchorType == "as-char") {
0355         d->anchorType = AnchorAsCharacter;
0356         d->horizontalRel = HChar;
0357         d->horizontalPos = HLeft;
0358     } else if (anchorType == "paragraph") {
0359         d->anchorType = AnchorParagraph;
0360     } else {
0361         d->anchorType = AnchorPage;
0362         // it has different defaults at least LO thinks so - ODF doesn't define defaults for this
0363         d->horizontalPos = HFromLeft;
0364         d->verticalPos = VFromTop;
0365         d->horizontalRel = HPage;
0366         d->verticalRel = VPage;
0367     }
0368 
0369     if (anchorType == "page" && shape()->hasAdditionalAttribute("text:anchor-page-number")) {
0370         d->pageNumber = shape()->additionalAttribute("text:anchor-page-number").toInt();
0371         if (d->pageNumber <= 0) {
0372             // invalid if the page-number is invalid (OO.org does the same)
0373             // see http://bugs.kde.org/show_bug.cgi?id=281869
0374             d->pageNumber = -1;
0375         }
0376     } else {
0377         d->pageNumber = -1;
0378     }
0379     // always make it invisible or it will create empty rects on the first page
0380     // during initial layout. This is because only when we layout it's final page is
0381     // the shape moved away from page 1
0382     // in KWRootAreaProvider of textlayout it's set back to visible
0383     //
0384     // FIXME: asfaics svg shapes can be invisible if display=none
0385     // see: https://www.w3.org/TR/SVG11/painting.html#VisibilityControl
0386     shape()->setVisible(false);
0387 
0388     // load settings from graphic style
0389     KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
0390     styleStack.save();
0391     if (element.hasAttributeNS(KoXmlNS::draw, "style-name")) {
0392         context.odfLoadingContext().fillStyleStack(element, KoXmlNS::draw, "style-name", "graphic");
0393         styleStack.setTypeProperties("graphic");
0394     }
0395     QString verticalPos = styleStack.property(KoXmlNS::style, "vertical-pos");
0396     QString verticalRel = styleStack.property(KoXmlNS::style, "vertical-rel");
0397     QString horizontalPos = styleStack.property(KoXmlNS::style, "horizontal-pos");
0398     QString horizontalRel = styleStack.property(KoXmlNS::style, "horizontal-rel");
0399     d->wrapInfluenceOnPosition = styleStack.property(KoXmlNS::draw, "wrap-influence-on-position");
0400     QString flowWithText = styleStack.property(KoXmlNS::style, "flow-with-text");
0401     d->flowWithText = flowWithText.isEmpty() ? false : flowWithText == "true";
0402     styleStack.restore();
0403 
0404     // vertical-pos
0405     if (verticalPos == "below") {//svg:y attribute is ignored
0406         d->verticalPos = VBelow;
0407         d->offset.setY(0);
0408     } else if (verticalPos == "bottom") {//svg:y attribute is ignored
0409         d->verticalPos = VBottom;
0410         d->offset.setY(-shape()->size().height());
0411     } else if (verticalPos == "from-top") {
0412         d->verticalPos = VFromTop;
0413     } else if (verticalPos == "middle") {//svg:y attribute is ignored
0414         d->verticalPos = VMiddle;
0415         d->offset.setY(-(shape()->size().height()/2));
0416     } else if (verticalPos == "top") {//svg:y attribute is ignored
0417         d->verticalPos = VTop;
0418         d->offset.setY(0);
0419     }
0420 
0421     // vertical-rel
0422     if (verticalRel == "baseline")
0423         d->verticalRel = VBaseline;
0424     else if (verticalRel == "char")
0425         d->verticalRel = VChar;
0426     else if (verticalRel == "frame")
0427         d->verticalRel = VFrame;
0428     else if (verticalRel == "frame-content")
0429         d->verticalRel = VFrameContent;
0430     else if (verticalRel == "line")
0431         d->verticalRel = VLine;
0432     else if (verticalRel == "page")
0433         d->verticalRel = VPage;
0434     else if (verticalRel == "page-content")
0435         d->verticalRel = VPageContent;
0436     else if (verticalRel == "paragraph")
0437         d->verticalRel = VParagraph;
0438     else if (verticalRel == "paragraph-content")
0439         d->verticalRel = VParagraphContent;
0440     else if (verticalRel == "text")
0441         d->verticalRel = VText;
0442 
0443     // horizontal-pos
0444     if (horizontalPos == "center") {//svg:x attribute is ignored
0445         d->horizontalPos = HCenter;
0446         d->offset.setX(-(shape()->size().width()/2));
0447     } else if (horizontalPos == "from-inside") {
0448         d->horizontalPos = HFromInside;
0449     } else if (horizontalPos == "from-left") {
0450         d->horizontalPos = HFromLeft;
0451     } else if (horizontalPos == "inside") {//svg:x attribute is ignored
0452         d->horizontalPos = HInside;
0453         d->offset.setX(0);
0454     } else if (horizontalPos == "left") {//svg:x attribute is ignored
0455         d->horizontalPos = HLeft;
0456         d->offset.setX(0);
0457     }else if (horizontalPos == "outside") {//svg:x attribute is ignored
0458         d->horizontalPos = HOutside;
0459         d->offset.setX(-shape()->size().width());
0460     }else if (horizontalPos == "right") {//svg:x attribute is ignored
0461         d->horizontalPos = HRight;
0462         d->offset.setX(-shape()->size().width());
0463     }
0464 
0465     // horizontal-rel
0466     if (horizontalRel == "char")
0467         d->horizontalRel = HChar;
0468     else if (horizontalRel == "page")
0469         d->horizontalRel = HPage;
0470     else if (horizontalRel == "page-content")
0471         d->horizontalRel = HPageContent;
0472     else if (horizontalRel == "page-start-margin")
0473         d->horizontalRel = HPageStartMargin;
0474     else if (horizontalRel == "page-end-margin")
0475         d->horizontalRel = HPageEndMargin;
0476     else if (horizontalRel == "frame")
0477         d->horizontalRel = HFrame;
0478     else if (horizontalRel == "frame-content")
0479         d->horizontalRel = HFrameContent;
0480     else if (horizontalRel == "frame-end-margin")
0481         d->horizontalRel = HFrameEndMargin;
0482     else if (horizontalRel == "frame-start-margin")
0483         d->horizontalRel = HFrameStartMargin;
0484     else if (horizontalRel == "paragraph")
0485         d->horizontalRel = HParagraph;
0486     else if (horizontalRel == "paragraph-content")
0487         d->horizontalRel = HParagraphContent;
0488     else if (horizontalRel == "paragraph-end-margin")
0489         d->horizontalRel = HParagraphEndMargin;
0490     else if (horizontalRel == "paragraph-start-margin")
0491         d->horizontalRel = HParagraphStartMargin;
0492 
0493     // if svg:x or svg:y should be ignored set new position
0494     shape()->setPosition(d->offset);
0495 
0496     return true;
0497 }
0498 
0499 void KoShapeAnchor::setAnchorType(KoShapeAnchor::AnchorType type)
0500 {
0501     d->anchorType = type;
0502     if (type == AnchorAsCharacter) {
0503         d->horizontalRel = HChar;
0504         d->horizontalPos = HLeft;
0505     }
0506 }
0507 
0508 KoShapeAnchor::TextLocation *KoShapeAnchor::textLocation() const
0509 {
0510     return d->textLocation;
0511 }
0512 
0513 void KoShapeAnchor::setTextLocation(TextLocation *textLocation)
0514 {
0515     d->textLocation = textLocation;
0516 }
0517 
0518 KoShapeAnchor::PlacementStrategy *KoShapeAnchor::placementStrategy() const
0519 {
0520     return d->placementStrategy;
0521 }
0522 
0523 void KoShapeAnchor::setPlacementStrategy(PlacementStrategy *placementStrategy)
0524 {
0525     if (placementStrategy != d->placementStrategy) {
0526         delete d->placementStrategy;
0527 
0528         d->placementStrategy = placementStrategy;
0529     }
0530 }