File indexing completed on 2025-01-19 13:27:33
0001 /* 0002 * Copyright (c) 2010 Sebastian Sauer <sebsauer@kdab.com> 0003 * Copyright (c) 2010 Carlos Licea <carlos@kdab.com> 0004 * Copyright (c) 2014 Inge Wallin <inge@lysator.liu.se> 0005 * 0006 * This program is free software; you can redistribute it and/or modify 0007 * it under the terms of the GNU Lesser General Public License as published 0008 * by the Free Software Foundation; either version 2 of the License, or 0009 * (at your option) any later version. 0010 * 0011 * This program 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 0014 * GNU Lesser General Public License for more details. 0015 * 0016 * You should have received a copy of the GNU Lesser General Public License 0017 * along with this program; if not, write to the Free Software 0018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 0019 */ 0020 0021 // Own 0022 #include "XlsxChartOdfWriter.h" 0023 0024 // Calligra 0025 #include <KoStore.h> 0026 #include <KoXmlWriter.h> 0027 #include <KoOdfWriteStore.h> 0028 #include <KoStoreDevice.h> 0029 #include <KoGenStyles.h> 0030 #include <KoGenStyle.h> 0031 0032 0033 #include <Charting.h> 0034 #include "NumberFormatParser.h" 0035 #include <MsooXmlTheme.h> 0036 0037 #include <algorithm> // For std:find() 0038 0039 // Print the content of generated content.xml to the console for debugging purpose 0040 //#define CONTENTXML_DEBUG 0041 0042 using namespace KoChart; 0043 0044 XlsxChartOdfWriter::XlsxChartOdfWriter(KoChart::Chart* chart, const MSOOXML::DrawingMLTheme* const theme) 0045 : KoOdfChartWriter(chart) 0046 , m_theme(theme) 0047 { 0048 Q_ASSERT(m_chart); 0049 m_drawLayer = false; 0050 } 0051 0052 XlsxChartOdfWriter::~XlsxChartOdfWriter() 0053 { 0054 } 0055 0056 0057 QColor XlsxChartOdfWriter::calculateColorFromGradientStop(const KoChart::Gradient::GradientStop& grad) 0058 { 0059 QColor color = grad.knownColorValue; 0060 0061 if (!grad.referenceColor.isEmpty()) 0062 color = m_theme->colorScheme.value(grad.referenceColor)->value(); 0063 0064 const int tintedColor = 255 * grad.tintVal / 100.0; 0065 const qreal nonTindedPart = 1.0 - grad.tintVal / 100.0; 0066 color.setRed(tintedColor + nonTindedPart * color.red()); 0067 color.setGreen(tintedColor + nonTindedPart * color.green()); 0068 color.setBlue(tintedColor + nonTindedPart * color.blue()); 0069 0070 return color; 0071 } 0072 0073 QColor XlsxChartOdfWriter::labelFontColor() const 0074 { 0075 bool useTheme = !chart()->m_areaFormat && m_theme; 0076 if (useTheme) { 0077 // The following assumes that we just need to invert the in 0078 // genChartAreaStyle used font-color for the axis. It's not clear yet 0079 // (means any documentation in the specs is missing) if that is really 0080 // the correct thing to do. 0081 const MSOOXML::DrawingMLColorScheme& colorScheme = m_theme->colorScheme; 0082 switch(chart()->m_style) { 0083 case(33): 0084 case(34): 0085 case(35): 0086 case(36): 0087 case(37): 0088 case(38): 0089 case(39): 0090 case(40): { 0091 return colorScheme.value("dk1")->value(); 0092 } break; 0093 0094 case(41): 0095 case(42): 0096 case(43): 0097 case(44): 0098 case(45): 0099 case(46): 0100 case(47): 0101 case(48): { 0102 return colorScheme.value("lt1")->value(); 0103 } break; 0104 0105 default: 0106 break; 0107 } 0108 } 0109 0110 return QColor(); 0111 } 0112 0113 QString XlsxChartOdfWriter::genChartAreaStyle(KoGenStyle& style, KoGenStyles& styles, 0114 KoGenStyles& mainStyles) 0115 { 0116 if (chart()->m_fillGradient) { 0117 style.addProperty("draw:fill", "gradient", KoGenStyle::GraphicType); 0118 style.addProperty("draw:fill-gradient-name", 0119 generateGradientStyle(mainStyles, chart()->m_fillGradient), 0120 KoGenStyle::GraphicType); 0121 } else { 0122 style.addProperty("draw:fill", "solid", KoGenStyle::GraphicType); 0123 bool useTheme = !chart()->m_areaFormat && m_theme; 0124 if (useTheme) { 0125 const MSOOXML::DrawingMLColorScheme& colorScheme = m_theme->colorScheme; 0126 switch(chart()->m_style) { 0127 case(33): 0128 case(34): 0129 case(35): 0130 case(36): 0131 case(37): 0132 case(38): 0133 case(39): 0134 case(40): { 0135 style.addProperty("draw:fill-color", colorScheme.value("lt1")->value().name(), 0136 KoGenStyle::GraphicType); 0137 } break; 0138 case(41): 0139 case(42): 0140 case(43): 0141 case(44): 0142 case(45): 0143 case(46): 0144 case(47): 0145 case(48): { 0146 style.addProperty("draw:fill-color", colorScheme.value("dk1")->value().name(), 0147 KoGenStyle::GraphicType); 0148 } break; 0149 0150 default: { 0151 useTheme = false; 0152 } break; 0153 } 0154 } 0155 0156 if (!useTheme) { 0157 QColor color; 0158 if (chart()->m_areaFormat 0159 && chart()->m_areaFormat->m_fill 0160 && chart()->m_areaFormat->m_foreground.isValid()) 0161 { 0162 color = chart()->m_areaFormat->m_foreground; 0163 } 0164 else 0165 color = QColor("#FFFFFF"); 0166 style.addProperty("draw:fill-color", color.name(), KoGenStyle::GraphicType); 0167 0168 if (color.alpha() < 255) 0169 style.addProperty("draw:opacity", 0170 QString("%1%").arg(chart()->m_areaFormat->m_foreground.alphaF() 0171 * 100.0), 0172 KoGenStyle::GraphicType); 0173 } 0174 } 0175 0176 return styles.insert(style, "ch"); 0177 } 0178 0179 0180 QString XlsxChartOdfWriter::genPlotAreaStyle(KoGenStyle& style, KoGenStyles& styles, 0181 KoGenStyles& mainStyles) 0182 { 0183 KoChart::AreaFormat *areaFormat = ((chart()->m_plotArea 0184 && chart()->m_plotArea->m_areaFormat 0185 && chart()->m_plotArea->m_areaFormat->m_fill) 0186 ? chart()->m_plotArea->m_areaFormat 0187 : chart()->m_areaFormat); 0188 if (chart()->m_plotAreaFillGradient) { 0189 style.addProperty("draw:fill", "gradient", KoGenStyle::GraphicType); 0190 style.addProperty("draw:fill-gradient-name", 0191 generateGradientStyle(mainStyles, chart()->m_plotAreaFillGradient), 0192 KoGenStyle::GraphicType); 0193 } else { 0194 style.addProperty("draw:fill", "solid", KoGenStyle::GraphicType); 0195 bool useTheme = !areaFormat && m_theme; 0196 if (useTheme) { 0197 const MSOOXML::DrawingMLColorScheme& colorScheme = m_theme->colorScheme; 0198 switch(chart()->m_style) { 0199 case(33): 0200 case(34): { 0201 style.addProperty("draw:fill-color", 0202 tintColor(colorScheme.value("dk1")->value(), 0.2).name(), 0203 KoGenStyle::GraphicType); 0204 } break; 0205 case(35): 0206 case(36): 0207 case(37): 0208 case(38): 0209 case(39): 0210 case(40): { 0211 QString prop = QString::fromLatin1("accent%1").arg(chart()->m_style - 34); 0212 style.addProperty("draw:fill-color", 0213 colorScheme.value("dk1")->value().name(), 0214 KoGenStyle::GraphicType); 0215 } break; 0216 case(41): 0217 case(42): 0218 case(43): 0219 case(44): 0220 case(45): 0221 case(46): 0222 case(47): 0223 case(48): { 0224 style.addProperty("draw:fill-color", 0225 tintColor(colorScheme.value("dk1")->value(), 0.95).name(), 0226 KoGenStyle::GraphicType); 0227 } break; 0228 0229 default: { 0230 useTheme = false; 0231 } break; 0232 } 0233 } 0234 0235 if (!useTheme) { 0236 QColor color; 0237 if (areaFormat && areaFormat->m_foreground.isValid()) 0238 color = areaFormat->m_foreground; 0239 else 0240 color = QColor(paletteIsSet ? "#C0C0C0" : "#FFFFFF"); 0241 style.addProperty("draw:fill-color", color.name(), KoGenStyle::GraphicType); 0242 0243 if (color.alpha() < 255) 0244 style.addProperty("draw:opacity", 0245 QString("%1%").arg(areaFormat->m_foreground.alphaF() * 100.0), 0246 KoGenStyle::GraphicType); 0247 } 0248 } 0249 0250 return styles.insert(style, "ch"); 0251 } 0252 0253 0254 // ---------------------------------------------------------------- 0255 // The actual saving code 0256 0257 0258 // We don't have to do anything special here in the case of Xlsx. 0259 0260 0261 // ---------------------------------------------------------------- 0262 // Some helper functions 0263 0264 0265 void XlsxChartOdfWriter::addDataThemeToStyle(KoGenStyle& style, int dataNumber, int maxNumData, 0266 bool strokes) 0267 { 0268 if (!m_theme) return; 0269 0270 const int patternOneIndexes[] = { 1, 9, 17, 25, 33 }; 0271 const int patternTwoIndexes[] = { 42, 34, 26, 18, 10, 2 }; 0272 const int patternFourIndexes[] = { 41 }; 0273 0274 const int fadepatternOne[] = { 3, 11, 19, 27, 35, 43 }; 0275 const int fadepatternTwo[] = { 4, 12, 20, 28, 36, 44 }; 0276 const int fadepatternThree[] = { 5, 13, 21, 29, 37, 45 }; 0277 const int fadepatternFour[] = { 6, 14, 22, 30, 38, 46 }; 0278 const int fadepatternFive[] = { 7, 15, 23, 31, 39, 47 }; 0279 const int fadepatternSix[] = { 8, 16, 24, 32, 40, 48 }; 0280 QVector< const int* > fadePatterns; fadePatterns << fadepatternOne << fadepatternTwo 0281 << fadepatternThree << fadepatternFour 0282 << fadepatternFive << fadepatternSix; 0283 0284 const MSOOXML::DrawingMLColorScheme& colorScheme = m_theme->colorScheme; 0285 const int rounds = dataNumber / 6; 0286 const int maxRounds = maxNumData / 6 + 1; 0287 QColor seriesColor; 0288 if (std::find(patternTwoIndexes, patternTwoIndexes + 6, chart()->m_style) 0289 != patternTwoIndexes + 6) 0290 { 0291 const QString themeColorString = QString::fromLatin1("accent%1").arg((dataNumber % 6) + 1); 0292 const qreal tintFactor = 1.0 - (rounds / maxRounds * 2); 0293 MSOOXML::DrawingMLColorSchemeItemBase *colorSchemeItem = colorScheme.value(themeColorString); 0294 if (colorSchemeItem) { 0295 seriesColor = colorSchemeItem->value(); 0296 if (rounds > 1) 0297 seriesColor = tintColor(seriesColor, tintFactor); 0298 } 0299 } 0300 else if (std::find(patternOneIndexes, patternOneIndexes + 5, chart()->m_style) 0301 != patternOneIndexes + 5) 0302 { 0303 const QString themeColorString = QString::fromLatin1("dk1"); 0304 MSOOXML::DrawingMLColorSchemeItemBase *colorSchemeItem = colorScheme.value(themeColorString); 0305 if (colorSchemeItem) { 0306 seriesColor = colorSchemeItem->value(); 0307 const qreal tintVals[] = { 0.885, 0.55, 0.78, 0.925, 0.7, 0.3 }; 0308 seriesColor = tintColor(seriesColor, tintVals[ dataNumber % 6 ]); 0309 const qreal tintFactor = 1.0 - (rounds / maxRounds * 2); 0310 if (rounds > 1) 0311 seriesColor = tintColor(seriesColor, tintFactor); 0312 } 0313 } 0314 else if (std::find(patternFourIndexes, patternFourIndexes + 1, chart()->m_style) 0315 != patternFourIndexes + 1) 0316 { 0317 const QString themeColorString = QString::fromLatin1("dk1"); 0318 MSOOXML::DrawingMLColorSchemeItemBase *colorSchemeItem = colorScheme.value(themeColorString); 0319 if (colorSchemeItem) { 0320 seriesColor = colorSchemeItem->value(); 0321 const qreal tintVals[] = { 0.885, 0.55, 0.78, 0.925, 0.7, 0.3 }; 0322 seriesColor = tintColor(seriesColor, tintVals[ dataNumber % 6 ]); 0323 const qreal tintFactor = 1.0 - (rounds / maxRounds * 2); 0324 if (rounds > 1) 0325 seriesColor = tintColor(seriesColor, tintFactor); 0326 } 0327 } 0328 else { 0329 for (int i = 0; i < fadePatterns.count(); ++i) { 0330 if (std::find(fadePatterns[ i ], fadePatterns[ i ] + 6, chart()->m_style) 0331 != fadePatterns[ i ] + 6) 0332 { 0333 const QString themeColorString = QString::fromLatin1("accent%1").arg(i + 1); 0334 MSOOXML::DrawingMLColorSchemeItemBase *colorSchemeItem = colorScheme.value(themeColorString); 0335 if (colorSchemeItem) { 0336 seriesColor = colorSchemeItem->value(); 0337 qreal fadeValue = calculateFade(dataNumber, maxNumData) / 100.0; 0338 if (fadeValue > 0.0) 0339 seriesColor = tintColor(seriesColor, 1 - fadeValue); 0340 else 0341 seriesColor = shadeColor(seriesColor, 1 + fadeValue); 0342 } 0343 } 0344 } 0345 } 0346 0347 if (seriesColor.isValid()) { 0348 style.addProperty("draw:fill", "solid", KoGenStyle::GraphicType); 0349 style.addProperty("draw:fill-color", seriesColor.name(), KoGenStyle::GraphicType); 0350 0351 if (strokes) { 0352 style.addProperty("draw:stroke", "solid", KoGenStyle::GraphicType); 0353 style.addProperty("svg:stroke-color", seriesColor.name(), KoGenStyle::GraphicType); 0354 } else { 0355 style.addProperty("draw:stroke", "none", KoGenStyle::GraphicType); 0356 } 0357 } 0358 }