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 }