File indexing completed on 2024-05-12 16:33:36

0001 /* This file is part of the KDE project
0002 
0003    Copyright 2007 Johannes Simon <johannes.simon@gmail.com>
0004    Copyright 2009 Inge Wallin    <ingwa@lysator.liu.se>
0005 
0006    This library is free software; you can redistribute it and/or
0007    modify it under the terms of the GNU Library General Public
0008    License as published by the Free Software Foundation; either
0009    version 2 of the License, or (at your option) any later version.
0010 
0011    This library is distributed in the hope that it will be useful,
0012    but WITHOUT ANY WARRANTY; without even the implied warranty of
0013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014    Library General Public License for more details.
0015 
0016    You should have received a copy of the GNU Library General Public License
0017    along with this library; see the file COPYING.LIB.  If not, write to
0018    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0019    Boston, MA 02110-1301, USA.
0020 */
0021 
0022 // Own
0023 #include "Surface.h"
0024 
0025 // Qt
0026 #include <QPointF>
0027 #include <QBrush>
0028 #include <QPen>
0029 
0030 // Calligra
0031 #include <KoXmlReader.h>
0032 #include <KoXmlWriter.h>
0033 #include <KoXmlNS.h>
0034 #include <KoOdfStylesReader.h>
0035 #include <KoShapeLoadingContext.h>
0036 #include <KoShapeSavingContext.h>
0037 #include <KoOdfLoadingContext.h>
0038 #include <KoStyleStack.h>
0039 #include <KoOdfGraphicStyles.h>
0040 #include <KoGenStyles.h>
0041 #include <KoOdfWorkaround.h>
0042 
0043 #include <KoImageData.h>
0044 #include <KoUnit.h>
0045 
0046 // KChart
0047 #include <KChartCartesianCoordinatePlane>
0048 #include <KChartBackgroundAttributes>
0049 #include <KChartFrameAttributes>
0050 
0051 // KoChart
0052 #include "PlotArea.h"
0053 #include "ChartDebug.h"
0054 
0055 
0056 using namespace KoChart;
0057 
0058 class Surface::Private
0059 {
0060 public:
0061     Private(PlotArea *parent);
0062     ~Private();
0063 
0064     PlotArea *plotArea;
0065     
0066     QPointF  position;
0067     int      width;
0068 
0069     QBrush   brush;
0070     QPen     framePen;
0071 
0072     KChart::CartesianCoordinatePlane *kdPlane;
0073 };
0074 
0075 Surface::Private::Private(PlotArea *parent)
0076     : plotArea(parent)
0077 {
0078 }
0079 
0080 Surface::Private::~Private()
0081 {
0082 }
0083 
0084 
0085 // ================================================================
0086 
0087 
0088 Surface::Surface(PlotArea *parent)
0089     : d(new Private(parent))
0090 {
0091     Q_ASSERT(parent);
0092 
0093     // FIXME: Make this class capable of storing floor-specific
0094     // attributes as well. Right now, it's really only used
0095     // and designed to load and save the chart's wall.
0096     d->kdPlane = d->plotArea->kdCartesianPlane();
0097     Q_ASSERT(d->kdPlane);
0098 }
0099 
0100 Surface::~Surface()
0101 {
0102     delete d;
0103 }
0104 
0105 
0106 QPointF Surface::position() const
0107 {
0108     return d->position;
0109 }
0110 
0111 void Surface::setPosition(const QPointF &position)
0112 {
0113     d->position = position;
0114 }
0115 
0116 int Surface::width() const
0117 {
0118     return d->width;
0119 }
0120 
0121 void Surface::setWidth(int width)
0122 {
0123     d->width = width;
0124 }
0125 
0126 QBrush Surface::brush() const
0127 {
0128     return d->brush;
0129 }
0130 
0131 void Surface::setBrush(const QBrush &brush)
0132 {
0133     d->brush = brush;
0134 }
0135 
0136 QPen Surface::framePen() const
0137 {
0138     return d->framePen;
0139 }
0140 
0141 void Surface::setFramePen(const QPen &pen)
0142 {
0143     d->framePen = pen;
0144 }
0145 
0146 bool Surface::loadOdf(const KoXmlElement &surfaceElement,
0147                        KoShapeLoadingContext &context)
0148 {
0149     // Get the current style stack and save it's state.
0150     KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
0151 
0152     bool brushLoaded = false;
0153     
0154     if (surfaceElement.hasAttributeNS(KoXmlNS::chart, "style-name")) {
0155         KChart::BackgroundAttributes backgroundAttributes = d->kdPlane->backgroundAttributes();
0156         KChart::FrameAttributes frameAttributes = d->kdPlane->frameAttributes();
0157         
0158         // Add the chart style to the style stack.
0159         styleStack.clear();
0160         context.odfLoadingContext().fillStyleStack(surfaceElement, KoXmlNS::chart, "style-name", "chart");
0161         
0162         styleStack.setTypeProperties("graphic");
0163         
0164         // If there is a "stroke" property, then get the stroke style
0165         // and set the pen accordingly.
0166         if (styleStack.hasProperty(KoXmlNS::draw, "stroke")) {
0167             frameAttributes.setVisible(true);
0168 
0169             QString  stroke = styleStack.property(KoXmlNS::draw, "stroke");
0170             QPen pen(Qt::NoPen);
0171             if (stroke == "solid" || stroke == "dash")
0172                 pen = KoOdfGraphicStyles::loadOdfStrokeStyle(styleStack, stroke,
0173                                                              context.odfLoadingContext().stylesReader());
0174 
0175             frameAttributes.setPen(pen);
0176         }
0177         
0178         // If there is a "fill" property, then get the fill style, and
0179         // set the brush for the surface accordingly.
0180         if (styleStack.hasProperty(KoXmlNS::draw, "fill")) {
0181             backgroundAttributes.setVisible(true);
0182 
0183             QBrush   brush;
0184             QString  fill = styleStack.property(KoXmlNS::draw, "fill");
0185             if (fill == "solid" || fill == "hatch") {
0186                 brushLoaded = true;
0187                 brush = KoOdfGraphicStyles::loadOdfFillStyle(styleStack, fill,
0188                                                              context.odfLoadingContext().stylesReader());
0189             }
0190             else if (fill == "gradient") {
0191                 brushLoaded = true;
0192                 brush = KoOdfGraphicStyles::loadOdfGradientStyle(styleStack, context.odfLoadingContext().stylesReader(), QSizeF(5.0, 60.0));
0193             }
0194             else if (fill == "bitmap") {
0195                 brushLoaded = true;
0196                 brush = loadOdfPatternStyle(styleStack, context.odfLoadingContext(), QSizeF(5.0, 60.0));
0197             }
0198 
0199             backgroundAttributes.setBrush(brush);
0200         }
0201         
0202         // Finally actually set the attributes.
0203         d->kdPlane->setBackgroundAttributes(backgroundAttributes);
0204         d->kdPlane->setFrameAttributes(frameAttributes);
0205     }
0206 
0207 #ifndef NWORKAROUND_ODF_BUGS
0208     if (!brushLoaded) {
0209         KChart::BackgroundAttributes backgroundAttributes = d->kdPlane->backgroundAttributes();
0210         QColor fillColor = KoOdfWorkaround::fixMissingFillColor(surfaceElement, context);
0211         if (fillColor.isValid()) {
0212             backgroundAttributes.setVisible(true);
0213             backgroundAttributes.setBrush(fillColor);
0214             d->kdPlane->setBackgroundAttributes(backgroundAttributes);
0215         }
0216     }
0217 #endif
0218     
0219     return true;
0220 }
0221 
0222 void Surface::saveOdf(KoShapeSavingContext &context, const char *elementName)
0223 {
0224     KoXmlWriter  &bodyWriter = context.xmlWriter();
0225     KoGenStyles  &mainStyles = context.mainStyles();
0226     KoGenStyle    style      = KoGenStyle(KoGenStyle::GraphicAutoStyle, "chart");
0227 
0228     // elementName is chart:floor or chart:wall
0229     bodyWriter.startElement(elementName);
0230 
0231     QBrush backgroundBrush;
0232     if (d->kdPlane->backgroundAttributes().isVisible())
0233         backgroundBrush = d->kdPlane->backgroundAttributes().brush();
0234     QPen framePen(Qt::NoPen);
0235     if (d->kdPlane->frameAttributes().isVisible())
0236         framePen = d->kdPlane->frameAttributes().pen();
0237 
0238     KoOdfGraphicStyles::saveOdfFillStyle(style, mainStyles, backgroundBrush);
0239     KoOdfGraphicStyles::saveOdfStrokeStyle(style, mainStyles, framePen);
0240 
0241     bodyWriter.addAttribute("chart:style-name", mainStyles.insert(style, "ch"));
0242 
0243     bodyWriter.endElement(); // chart:floor or chart:wall
0244 }
0245 
0246 QBrush Surface::loadOdfPatternStyle(const KoStyleStack &styleStack, 
0247                                     KoOdfLoadingContext &context, const QSizeF &size)
0248 {
0249     QString styleName = styleStack.property(KoXmlNS::draw, "fill-image-name");
0250 
0251     KoXmlElement* e = context.stylesReader().drawStyles("fill-image")[styleName];
0252     if (! e)
0253         return QBrush();
0254 
0255     const QString href = e->attributeNS(KoXmlNS::xlink, "href", QString());
0256 
0257     if (href.isEmpty())
0258         return QBrush();
0259 
0260     QString strExtension;
0261     const int result = href.lastIndexOf('.');
0262     if (result >= 0) {
0263         strExtension = href.mid(result + 1); // As we are using KoPicture, the extension should be without the dot.
0264     }
0265     QString filename(href);
0266 
0267     KoImageData data;
0268     data.setImage(href, context.store());
0269     if (data.errorCode() != KoImageData::Success)
0270         return QBrush();
0271 
0272     // read the pattern repeat style
0273     QString style = styleStack.property(KoXmlNS::style, "repeat");
0274     debugChart << "pattern style =" << style;
0275 
0276     QSize imageSize = data.image().size();
0277 
0278     if (style == "stretch") {
0279         imageSize = size.toSize();
0280     } else {
0281         // optional attributes which can override original image size
0282         if (styleStack.hasProperty(KoXmlNS::draw, "fill-image-height") && styleStack.hasProperty(KoXmlNS::draw, "fill-image-width")) {
0283             QString height = styleStack.property(KoXmlNS::draw, "fill-image-height");
0284             qreal newHeight = 0.0;
0285             if (height.endsWith('%'))
0286                 newHeight = 0.01 * height.remove('%').toDouble() * imageSize.height();
0287             else
0288                 newHeight = KoUnit::parseValue(height);
0289             QString width = styleStack.property(KoXmlNS::draw, "fill-image-width");
0290             qreal newWidth = 0.0;
0291             if (width.endsWith('%'))
0292                 newWidth = 0.01 * width.remove('%').toDouble() * imageSize.width();
0293             else
0294                 newWidth = KoUnit::parseValue(width);
0295             if (newHeight > 0.0)
0296                 imageSize.setHeight(static_cast<int>(newHeight));
0297             if (newWidth > 0.0)
0298                 imageSize.setWidth(static_cast<int>(newWidth));
0299         }
0300     }
0301 
0302     debugChart << "shape size =" << size;
0303     debugChart << "original image size =" << data.image().size();
0304     debugChart << "resulting image size =" << imageSize;
0305 
0306     QBrush resultBrush(QPixmap::fromImage(data.image()).scaled(imageSize));
0307 
0308     if (style == "repeat") {
0309         QTransform matrix;
0310         if (styleStack.hasProperty(KoXmlNS::draw, "fill-image-ref-point")) {
0311             // align pattern to the given size
0312             QString align = styleStack.property(KoXmlNS::draw, "fill-image-ref-point");
0313             debugChart << "pattern align =" << align;
0314             if (align == "top-left")
0315                 matrix.translate(0, 0);
0316             else if (align == "top")
0317                 matrix.translate(0.5*size.width(), 0);
0318             else if (align == "top-right")
0319                 matrix.translate(size.width(), 0);
0320             else if (align == "left")
0321                 matrix.translate(0, 0.5*size.height());
0322             else if (align == "center")
0323                 matrix.translate(0.5*size.width(), 0.5*size.height());
0324             else if (align == "right")
0325                 matrix.translate(size.width(), 0.5*size.height());
0326             else if (align == "bottom-left")
0327                 matrix.translate(0, size.height());
0328             else if (align == "bottom")
0329                 matrix.translate(0.5*size.width(), size.height());
0330             else if (align == "bottom-right")
0331                 matrix.translate(size.width(), size.height());
0332         }
0333         if (styleStack.hasProperty(KoXmlNS::draw, "fill-image-ref-point-x")) {
0334             QString pointX = styleStack.property(KoXmlNS::draw, "fill-image-ref-point-x");
0335             matrix.translate(0.01 * pointX.remove('%').toDouble() * imageSize.width(), 0);
0336         }
0337         if (styleStack.hasProperty(KoXmlNS::draw, "fill-image-ref-point-y")) {
0338             QString pointY = styleStack.property(KoXmlNS::draw, "fill-image-ref-point-y");
0339             matrix.translate(0, 0.01 * pointY.remove('%').toDouble() * imageSize.height());
0340         }
0341         resultBrush.setTransform(matrix);
0342     }
0343 
0344     return resultBrush;
0345 }