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 }