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 }