Warning, file /office/calligra/libs/flake/KoOdfWorkaround.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /* This file is part of the KDE project
0002    Copyright (C) 2009 Thorsten Zachmann <zachmann@kde.org>
0003    Copyright (C) 2009 Johannes Simon <johannes.simon@gmail.com>
0004    Copyright (C) 2010,2011 Jan Hambrecht <jaham@gmx.net>
0005    Copyright 2012 Friedrich W. H. Kossebau <kossebau@kde.org>
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 "KoOdfWorkaround.h"
0024 
0025 #include "KoShapeLoadingContext.h"
0026 #include "KoShape.h"
0027 #include "KoPathShape.h"
0028 #include "KoColorBackground.h"
0029 #include <KoOdfLoadingContext.h>
0030 #include <KoXmlReader.h>
0031 #include <KoXmlNS.h>
0032 #include <KoStyleStack.h>
0033 #include <KoUnit.h>
0034 
0035 #include <QPen>
0036 #include <QColor>
0037 
0038 #include <FlakeDebug.h>
0039 
0040 static bool s_workaroundPresentationPlaceholderBug = false;
0041 
0042 void KoOdfWorkaround::fixSkew(QStringList &params, KoShapeLoadingContext &context)
0043 {
0044     if (context.odfLoadingContext().generatorType() == KoOdfLoadingContext::OpenOffice) {
0045         debugFlake << "Work around OO bug: skewa clockwize and radians as default unit";
0046         QString angle = params[0].trimmed();
0047         if (angle.startsWith('-')) {
0048             angle = angle.remove(0, 1);
0049         } else {
0050             angle = angle.prepend('-');
0051         }
0052         const QChar c = angle.at(angle.length() -1);
0053         if (c.isDigit()) {
0054             angle.append("rad");
0055         }
0056         params[0] = angle;
0057     }
0058 }
0059 
0060 void KoOdfWorkaround::fixRotate(QStringList &params, KoShapeLoadingContext &context)
0061 {
0062     if (context.odfLoadingContext().generatorType() == KoOdfLoadingContext::OpenOffice) {
0063         debugFlake << "Work around OO bug: rotates clockwize and radians as default unit";
0064         QString angle = params[0].trimmed();
0065         if (angle.startsWith('-')) {
0066             angle = angle.remove(0, 1);
0067         } else {
0068             angle = angle.prepend('-');
0069         }
0070         const QChar c = angle.at(angle.length() -1);
0071         if (c.isDigit()) {
0072             angle.append("rad");
0073         }
0074         params[0] = angle;
0075     }
0076 }
0077 
0078 void KoOdfWorkaround::fixPenWidth(QPen & pen, KoShapeLoadingContext &context)
0079 {
0080     if (context.odfLoadingContext().generatorType() == KoOdfLoadingContext::OpenOffice && pen.widthF() == 0.0) {
0081         pen.setWidthF(0.5);
0082         debugFlake << "Work around OO bug with pen width 0";
0083     }
0084 }
0085 
0086 void KoOdfWorkaround::fixEnhancedPath(QString & path, const KoXmlElement &element, KoShapeLoadingContext &context)
0087 {
0088     if (context.odfLoadingContext().generatorType() == KoOdfLoadingContext::OpenOffice) {
0089         if (path.isEmpty() && element.attributeNS(KoXmlNS::draw, "type", "") == "ellipse") {
0090             path = "U 10800 10800 10800 10800 0 360 Z N";
0091         }
0092     }
0093 }
0094 
0095 void KoOdfWorkaround::fixEnhancedPathPolarHandlePosition(QString &position, const KoXmlElement &element, KoShapeLoadingContext &context)
0096 {
0097     if (context.odfLoadingContext().generatorType() == KoOdfLoadingContext::OpenOffice) {
0098         if (element.hasAttributeNS(KoXmlNS::draw, "handle-polar")) {
0099             QStringList tokens = position.simplified().split(' ');
0100             if (tokens.count() == 2) {
0101                 position = tokens[1] + ' ' + tokens[0];
0102             }
0103         }
0104     }
0105 }
0106 
0107 QColor KoOdfWorkaround::fixMissingFillColor(const KoXmlElement &element, KoShapeLoadingContext &context)
0108 {
0109     // Default to an invalid color
0110     QColor color;
0111 
0112     if (element.prefix() == "chart") {
0113         KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
0114         styleStack.save();
0115 
0116         bool hasStyle = element.hasAttributeNS(KoXmlNS::chart, "style-name");
0117         if (hasStyle) {
0118             context.odfLoadingContext().fillStyleStack(element, KoXmlNS::chart, "style-name", "chart");
0119             styleStack.setTypeProperties("graphic");
0120         }
0121 
0122         if (context.odfLoadingContext().generatorType() == KoOdfLoadingContext::OpenOffice) {
0123             if (hasStyle && !styleStack.hasProperty(KoXmlNS::draw, "fill") &&
0124                              styleStack.hasProperty(KoXmlNS::draw, "fill-color")) {
0125                 color = QColor(styleStack.property(KoXmlNS::draw, "fill-color"));
0126             } else if (!hasStyle || (!styleStack.hasProperty(KoXmlNS::draw, "fill")
0127                                     && !styleStack.hasProperty(KoXmlNS::draw, "fill-color"))) {
0128                 KoXmlElement plotAreaElement = element.parentNode().toElement();
0129                 KoXmlElement chartElement = plotAreaElement.parentNode().toElement();
0130 
0131                 if (element.tagName() == "wall") {
0132                     if (chartElement.hasAttributeNS(KoXmlNS::chart, "class")) {
0133                         QString chartType = chartElement.attributeNS(KoXmlNS::chart, "class");
0134                         // TODO: Check what default backgrounds for surface, stock and gantt charts are
0135                         if (chartType == "chart:line" ||
0136                              chartType == "chart:area" ||
0137                              chartType == "chart:bar" ||
0138                              chartType == "chart:scatter")
0139                         color = QColor(0xe0e0e0);
0140                     }
0141                 } else if (element.tagName() == "series") {
0142                     if (chartElement.hasAttributeNS(KoXmlNS::chart, "class")) {
0143                         QString chartType = chartElement.attributeNS(KoXmlNS::chart, "class");
0144                         // TODO: Check what default backgrounds for surface, stock and gantt charts are
0145                         if (chartType == "chart:area" ||
0146                              chartType == "chart:bar")
0147                             color = QColor(0x99ccff);
0148                     }
0149                 }
0150                 else if (element.tagName() == "chart")
0151                     color = QColor(0xffffff);
0152             }
0153         }
0154 
0155         styleStack.restore();
0156     }
0157 
0158     return color;
0159 }
0160 
0161 bool KoOdfWorkaround::fixMissingStroke(QPen &pen, const KoXmlElement &element, KoShapeLoadingContext &context, const KoShape *shape)
0162 {
0163     bool fixed = false;
0164 
0165     if (context.odfLoadingContext().generatorType() == KoOdfLoadingContext::OpenOffice) {
0166         KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
0167         if (element.prefix() == "chart") {
0168             styleStack.save();
0169 
0170             bool hasStyle = element.hasAttributeNS(KoXmlNS::chart, "style-name");
0171             if (hasStyle) {
0172                 context.odfLoadingContext().fillStyleStack(element, KoXmlNS::chart, "style-name", "chart");
0173                 styleStack.setTypeProperties("graphic");
0174             }
0175 
0176             if (hasStyle && styleStack.hasProperty(KoXmlNS::draw, "stroke") &&
0177                             !styleStack.hasProperty(KoXmlNS::svg, "stroke-color")) {
0178                 fixed = true;
0179                 pen.setColor(Qt::black);
0180             } else if (!hasStyle) {
0181                 KoXmlElement plotAreaElement = element.parentNode().toElement();
0182                 KoXmlElement chartElement = plotAreaElement.parentNode().toElement();
0183 
0184                 if (element.tagName() == "series") {
0185                     QString chartType = chartElement.attributeNS(KoXmlNS::chart, "class");
0186                     if (!chartType.isEmpty()) {
0187                         // TODO: Check what default backgrounds for surface, stock and gantt charts are
0188                         if (chartType == "chart:line" ||
0189                              chartType == "chart:scatter") {
0190                             fixed = true;
0191                             pen = QPen(0x99ccff);
0192                         }
0193                     }
0194                 } else if (element.tagName() == "legend") {
0195                     fixed = true;
0196                     pen = QPen(Qt::black);
0197                 }
0198             }
0199             styleStack.restore();
0200         }
0201         else {
0202             const KoPathShape *pathShape = dynamic_cast<const KoPathShape*>(shape);
0203             if (pathShape) {
0204                 const QString strokeColor(styleStack.property(KoXmlNS::svg, "stroke-color"));
0205                 if (strokeColor.isEmpty()) {
0206                     pen.setColor(Qt::black);
0207                 } else {
0208                     pen.setColor(strokeColor);
0209                 }
0210                 fixed = true;
0211             }
0212         }
0213     }
0214 
0215     return fixed;
0216 }
0217 
0218 bool KoOdfWorkaround::fixMissingStyle_DisplayLabel(const KoXmlElement &element, KoShapeLoadingContext &context)
0219 {
0220     Q_UNUSED(element);
0221     // If no axis style is specified, OpenOffice.org hides the axis' data labels
0222     if (context.odfLoadingContext().generatorType() == KoOdfLoadingContext::OpenOffice)
0223         return false;
0224 
0225     // In all other cases, they're visible
0226     return true;
0227 }
0228 
0229 void KoOdfWorkaround::setFixPresentationPlaceholder(bool fix, KoShapeLoadingContext &context)
0230 {
0231     KoOdfLoadingContext::GeneratorType type(context.odfLoadingContext().generatorType());
0232     if (type == KoOdfLoadingContext::OpenOffice || type == KoOdfLoadingContext::MicrosoftOffice) {
0233         s_workaroundPresentationPlaceholderBug = fix;
0234     }
0235 }
0236 
0237 bool KoOdfWorkaround::fixPresentationPlaceholder()
0238 {
0239     return s_workaroundPresentationPlaceholderBug;
0240 }
0241 
0242 void KoOdfWorkaround::fixPresentationPlaceholder(KoShape *shape)
0243 {
0244     if (s_workaroundPresentationPlaceholderBug && !shape->hasAdditionalAttribute("presentation:placeholder")) {
0245         shape->setAdditionalAttribute("presentation:placeholder", "true");
0246     }
0247 }
0248 
0249 QSharedPointer<KoColorBackground> KoOdfWorkaround::fixBackgroundColor(const KoShape *shape, KoShapeLoadingContext &context)
0250 {
0251     QSharedPointer<KoColorBackground> colorBackground;
0252     KoOdfLoadingContext &odfContext = context.odfLoadingContext();
0253     if (odfContext.generatorType() == KoOdfLoadingContext::OpenOffice) {
0254         const KoPathShape *pathShape = dynamic_cast<const KoPathShape*>(shape);
0255         //check shape type
0256         if (pathShape) {
0257             KoStyleStack &styleStack = odfContext.styleStack();
0258             const QString color(styleStack.property(KoXmlNS::draw, "fill-color"));
0259             if (color.isEmpty()) {
0260                 colorBackground = QSharedPointer<KoColorBackground>(new KoColorBackground(QColor(153, 204, 255)));
0261             } else {
0262                 colorBackground = QSharedPointer<KoColorBackground>(new KoColorBackground(color));
0263             }
0264         }
0265     }
0266     return colorBackground;
0267 }
0268 
0269 void KoOdfWorkaround::fixGluePointPosition(QString &positionString, KoShapeLoadingContext &context)
0270 {
0271     KoOdfLoadingContext::GeneratorType type(context.odfLoadingContext().generatorType());
0272     if (type == KoOdfLoadingContext::OpenOffice && !positionString.endsWith('%')) {
0273         const qreal pos = KoUnit::parseValue(positionString);
0274         positionString = QString("%1%%").arg(KoUnit::toMillimeter(pos));
0275     }
0276 }
0277 
0278 void KoOdfWorkaround::fixMissingFillRule(Qt::FillRule& fillRule, KoShapeLoadingContext& context)
0279 {
0280     if ((context.odfLoadingContext().generatorType() == KoOdfLoadingContext::OpenOffice)) {
0281         fillRule = Qt::OddEvenFill;
0282     }
0283 }
0284 
0285 bool KoOdfWorkaround::fixAutoGrow(KoTextShapeDataBase::ResizeMethod method, KoShapeLoadingContext &context)
0286 {
0287     bool fix = false;
0288     if (context.odfLoadingContext().generatorType() == KoOdfLoadingContext::OpenOffice) {
0289         if (method == KoTextShapeDataBase::AutoGrowWidth || method == KoTextShapeDataBase::AutoGrowHeight || method == KoTextShapeDataBase::AutoGrowWidthAndHeight) {
0290             fix = true;
0291         }
0292     }
0293     return fix;
0294 }
0295 
0296 bool KoOdfWorkaround::fixEllipse(const QString &kind, KoShapeLoadingContext &context)
0297 {
0298     bool radiusGiven = false;
0299     if (context.odfLoadingContext().generatorType() == KoOdfLoadingContext::OpenOffice) {
0300         if (kind == "section" || kind == "arc") {
0301             radiusGiven = true;
0302         }
0303     }
0304     return radiusGiven;
0305 }
0306 
0307 void KoOdfWorkaround::fixBadFormulaHiddenForStyleCellProtect(QString& value)
0308 {
0309     if (value.endsWith(QLatin1String("Formula.hidden"))) {
0310         const int length = value.length();
0311         value[length-14] = QLatin1Char('f');
0312         value[length-7] = QLatin1Char('-');
0313     }
0314 }
0315 
0316 void KoOdfWorkaround::fixBadDateForTextTime(QString &value)
0317 {
0318     if (value.startsWith(QLatin1String("0-00-00T"))) {
0319         value.remove(0, 8);
0320     }
0321 }
0322 
0323 void KoOdfWorkaround::fixClipRectOffsetValuesString(QString &offsetValuesString)
0324 {
0325     if (! offsetValuesString.contains(QLatin1Char(','))) {
0326         // assumes no spaces existing between values and units
0327         offsetValuesString = offsetValuesString.simplified().replace(QLatin1Char(' '), QLatin1Char(','));
0328     }
0329 }
0330 
0331 QString KoOdfWorkaround::fixTableTemplateName(const KoXmlElement &e)
0332 {
0333     return e.attributeNS(KoXmlNS::text, "style-name", QString());
0334 }
0335 
0336 QString KoOdfWorkaround::fixTableTemplateCellStyleName(const KoXmlElement &e)
0337 {
0338     return e.attributeNS(KoXmlNS::text, "style-name", QString());
0339 }
0340 
0341 static const struct {
0342     const char* oldPath;
0343     const char* newPath;
0344 } markerPathMapping[] = {
0345     // Arrow
0346     {"m10 0-10 30h20z",
0347      "M10 0l-10 30h20z"},
0348     // Square
0349     {"m0 0h10v10h-10",
0350      "M0 0h10v10h-10z"},
0351     // Small Arrow
0352     {"m1321 3493h-1321l702-3493z",
0353      "M1321 3493h-1321l702-3493z"},
0354      // Dimension Lines
0355     {"M0 0h278 278 280v36 36 38h-278-278-280v-36-36z",
0356      "m0 0h278 278 280v36 36 38h-278-278-280v-36-36z"},
0357     // Double Arrow
0358     {"m737 1131h394l-564-1131-567 1131h398l-398 787h1131z",
0359      "M737 1131h394l-564-1131-567 1131h398l-398 787h1131z"},
0360     // Rounded short Arrow
0361     {"m1009 1050-449-1008-22-30-29-12-34 12-21 26-449 1012-5 13v8l5 21 12 21 17 13 21 4h903l21-4 21-13 9-21 4-21v-8z",
0362      "M1009 1050l-449-1008-22-30-29-12-34 12-21 26-449 1012-5 13v8l5 21 12 21 17 13 21 4h903l21-4 21-13 9-21 4-21v-8z"},
0363     // Symmetric Arrow
0364     {"m564 0-564 902h1131z",
0365      "M564 0l-564 902h1131z"},
0366     // Line Arrow
0367     {"m0 2108v17 17l12 42 30 34 38 21 43 4 29-8 30-21 25-26 13-34 343-1532 339 1520 13 42 29 34 39 21 42 4 42-12 34-30 21-42v-39-12l-4 4-440-1998-9-42-25-39-38-25-43-8-42 8-38 25-26 39-8 42z",
0368      "M0 2108v17 17l12 42 30 34 38 21 43 4 29-8 30-21 25-26 13-34 343-1532 339 1520 13 42 29 34 39 21 42 4 42-12 34-30 21-42v-39-12l-4 4-440-1998-9-42-25-39-38-25-43-8-42 8-38 25-26 39-8 42z"},
0369     // Rounded large Arrow
0370     {"m1127 2120-449-2006-9-42-25-39-38-25-38-8-43 8-38 25-25 39-9 42-449 2006v13l-4 9 9 42 25 38 38 25 42 9h903l42-9 38-25 26-38 8-42v-9z",
0371      "M1127 2120l-449-2006-9-42-25-39-38-25-38-8-43 8-38 25-25 39-9 42-449 2006v13l-4 9 9 42 25 38 38 25 42 9h903l42-9 38-25 26-38 8-42v-9z"},
0372     // Circle
0373     {"m462 1118-102-29-102-51-93-72-72-93-51-102-29-102-13-105 13-102 29-106 51-102 72-89 93-72 102-50 102-34 106-9 101 9 106 34 98 50 93 72 72 89 51 102 29 106 13 102-13 105-29 102-51 102-72 93-93 72-98 51-106 29-101 13z",
0374      "M462 1118l-102-29-102-51-93-72-72-93-51-102-29-102-13-105 13-102 29-106 51-102 72-89 93-72 102-50 102-34 106-9 101 9 106 34 98 50 93 72 72 89 51 102 29 106 13 102-13 105-29 102-51 102-72 93-93 72-98 51-106 29-101 13z"},
0375     // Square 45
0376     {"m0 564 564 567 567-567-567-564z",
0377      "M0 564l564 567 567-567-567-564z"},
0378     // Arrow concave
0379     {"m1013 1491 118 89-567-1580-564 1580 114-85 136-68 148-46 161-17 161 13 153 46z",
0380      "M1013 1491l118 89-567-1580-564 1580 114-85 136-68 148-46 161-17 161 13 153 46z"},
0381     // Short line Arrow
0382     {"m1500 0 1500 2789v211h-114l-1286-2392v2392h-200v-2392l-1286 2392h-114v-211z",
0383      "M1500 0l1500 2789v211h-114l-1286-2392v2392h-200v-2392l-1286 2392h-114v-211z"},
0384     // Triangle unfilled
0385     {"m1500 0 1500 3000h-3000zm1500-2553-1176 2353h2353z",
0386      "M1500 0l1500 3000h-3000zM1500 447l-1176 2353h2353z"},
0387     // Diamond unfilled
0388     {"m1500 0 1500 3000-1500 3000-1500-3000zm1500-2553-1276 2553 1276 2553 1276-2553z",
0389      "M1500 0l1500 3000-1500 3000-1500-3000zM1500 447l-1276 2553 1276 2553 1276-2553z"},
0390     // Diamond
0391     {"m1500 0 1500 3000-1500 3000-1500-3000z",
0392      "M1500 0l1500 3000-1500 3000-1500-3000z"},
0393     // Circle unfilled
0394     {"m1500 3000c-276 0-511-63-750-201s-411-310-549-549-201-474-201-750 63-511 201-750 310-411 549-549 474-201 750-201 511 63 750 201 411 310 549 549 201 474 201 750-63 511-201 750-310 411-549 549-474 201-750 201zm0-200c-239 0-443-55-650-174s-356-269-476-476-174-411-174-650 55-443 174-650 269-356 476-476c207-119 411-174 650-174s443 55 650 174c207 120 356 269 476 476s174 411 174 650-55 443-174 650-269 356-476 476c-207 119-411 174-650 174z",
0395      "M1500 3000c-276 0-511-63-750-201s-411-310-549-549-201-474-201-750 63-511 201-750 310-411 549-549 474-201 750-201 511 63 750 201 411 310 549 549 201 474 201 750-63 511-201 750-310 411-549 549-474 201-750 201zM1500 2800c-239 0-443-55-650-174s-356-269-476-476-174-411-174-650 55-443 174-650 269-356 476-476c207-119 411-174 650-174s443 55 650 174c207 120 356 269 476 476s174 411 174 650-55 443-174 650-269 356-476 476c-207 119-411 174-650 174z"},
0396     // Square 45 unfilled
0397     {"m1500 3000-1500-1500 1500-1500 1500 1500zm-1500 1215-1215-1215 1215-1215 1215 1215z",
0398      "M1500 3000l-1500-1500 1500-1500 1500 1500zM1500 2715l-1215-1215 1215-1215 1215 1215z"},
0399     // Square unfilled
0400     {"m0 0h300v300h-300zm20-280h260v260h-260z",
0401      "M0 0h300v300h-300zM20 20h260v260h-260z"},
0402     // Half Circle unfilled
0403     {"m14971 0c21 229 29 423 29 653 0 690-79 1328-244 1943-165 614-416 1206-761 1804-345 597-733 1110-1183 1560-451 450-964 837-1562 1182-598 345-1190 596-1806 760-600 161-1223 240-1894 244v600h-100v-600c-671-4-1294-83-1894-244-616-164-1208-415-1806-760-598-345-1111-732-1562-1182-450-450-838-963-1183-1560-345-598-596-1190-761-1804-165-615-244-1253-244-1943 0-230 8-424 29-653l298 26 299 26c-18 211-26 390-26 601 0 635 72 1222 224 1787 151 566 383 1110 700 1659 318 550 674 1022 1088 1437 415 414 888 769 1438 1087 550 317 1095 548 1661 700 566 151 1154 223 1789 223s1223-72 1789-223c566-152 1111-383 1661-700 550-318 1023-673 1438-1087 414-415 770-887 1088-1437 317-549 549-1093 700-1659 152-565 224-1152 224-1787 0-211-8-390-26-601l299-26z",
0404      "M14971 0c21 229 29 423 29 653 0 690-79 1328-244 1943-165 614-416 1206-761 1804-345 597-733 1110-1183 1560-451 450-964 837-1562 1182s-1190 596-1806 760c-600 161-1223 240-1894 244v600h-100v-600c-671-4-1294-83-1894-244-616-164-1208-415-1806-760s-1111-732-1562-1182c-450-450-838-963-1183-1560-345-598-596-1190-761-1804-165-615-244-1253-244-1943 0-230 8-424 29-653l298 26 299 26c-18 211-26 390-26 601 0 635 72 1222 224 1787 151 566 383 1110 700 1659 318 550 674 1022 1088 1437 415 414 888 769 1438 1087 550 317 1095 548 1661 700 566 151 1154 223 1789 223s1223-72 1789-223c566-152 1111-383 1661-700 550-318 1023-673 1438-1087 414-415 770-887 1088-1437 317-549 549-1093 700-1659 152-565 224-1152 224-1787 0-211-8-390-26-601l299-26z"}
0405 };
0406 static const int markerPathMappingSize = sizeof(markerPathMapping)/sizeof(markerPathMapping[0]);
0407 
0408 void KoOdfWorkaround::fixMarkerPath(QString& path)
0409 {
0410     for (int i = 0; i < markerPathMappingSize; ++i) {
0411         if (path == QLatin1String(markerPathMapping[i].oldPath)) {
0412             path = QLatin1String(markerPathMapping[i].newPath);
0413             break;
0414         }
0415     }
0416 }