Warning, file /office/calligra/filters/libodf2/chart/KoOdfChartWriter.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 * SPDX-FileCopyrightText: 2010 Sebastian Sauer <sebsauer@kdab.com> 0003 * SPDX-FileCopyrightText: 2010 Carlos Licea <carlos@kdab.com> 0004 * SPDX-FileCopyrightText: 2014 Inge Wallin <inge@lysator.liu.se> 0005 * 0006 * SPDX-License-Identifier: LGPL-2.1-or-later 0007 */ 0008 0009 // Own 0010 #include "KoOdfChartWriter.h" 0011 0012 // libstdc++ 0013 #include <algorithm> // For std:find() 0014 0015 // Calligra 0016 #include <KoStore.h> 0017 #include <KoXmlWriter.h> 0018 #include <KoOdfWriteStore.h> 0019 #include <KoStoreDevice.h> 0020 #include <KoGenStyles.h> 0021 #include <KoGenStyle.h> 0022 0023 #include "Odf2Debug.h" 0024 #include <Charting.h> 0025 #include "NumberFormatParser.h" 0026 0027 0028 // Print the content of generated content.xml to the console for debugging purpose 0029 //#define CONTENTXML_DEBUG 0030 0031 using namespace KoChart; 0032 0033 KoOdfChartWriter::KoOdfChartWriter(KoChart::Chart* chart) 0034 : m_x(0) 0035 , m_y(0) 0036 , m_width(0) 0037 , m_height(0) 0038 , m_end_x(0) 0039 , m_end_y(0) 0040 , m_chart(chart) 0041 , sheetReplacement(true) 0042 , paletteIsSet(false) 0043 { 0044 Q_ASSERT(m_chart); 0045 m_drawLayer = false; 0046 } 0047 0048 KoOdfChartWriter::~KoOdfChartWriter() 0049 { 0050 } 0051 0052 0053 // Takes a Excel cellrange and translates it into a ODF cellrange 0054 QString KoOdfChartWriter::normalizeCellRange(QString range) 0055 { 0056 if (range.startsWith('[') && range.endsWith(']')) { 0057 range.remove(0, 1).chop(1); 0058 } 0059 range.remove('$'); 0060 0061 const bool isPoint = !range.contains( ':' ); 0062 QRegExp regEx(isPoint ? "(|.*\\.|.*\\!)([A-Z0-9]+)" 0063 : "(|.*\\.|.*\\!)([A-Z]+[0-9]+)\\:(|.*\\.|.*\\!)([A-Z0-9]+)"); 0064 if (regEx.indexIn(range) >= 0) { 0065 range.clear(); 0066 QString sheetName = regEx.cap(1); 0067 if (sheetName.endsWith(QLatin1Char('.')) || sheetName.endsWith(QLatin1Char('!'))) 0068 sheetName.chop(1); 0069 if (!sheetName.isEmpty()) 0070 range = sheetName + '.'; 0071 range += regEx.cap(2); 0072 if (!isPoint) 0073 range += ':' + regEx.cap(4); 0074 } 0075 0076 return range; 0077 } 0078 0079 QColor KoOdfChartWriter::tintColor(const QColor & color, qreal tintfactor) 0080 { 0081 QColor retColor; 0082 const qreal nonTindedPart = 1.0 - tintfactor; 0083 qreal luminance = 0.0; 0084 qreal sat = 0.0; 0085 qreal hue = 0.0; 0086 color.getHslF(&hue, &sat, &luminance); 0087 luminance = luminance * tintfactor + nonTindedPart; 0088 retColor.setHslF(hue, sat, luminance); 0089 // const int tintedColor = 255 * nonTindedPart; 0090 // retColor.setRed(tintedColor + tintfactor * color.red()); 0091 // retColor.setGreen(tintedColor + tintfactor * color.green()); 0092 // retColor.setBlue(tintedColor + tintfactor * color.blue()); 0093 0094 return retColor; 0095 } 0096 0097 QColor KoOdfChartWriter::calculateColorFromGradientStop(const KoChart::Gradient::GradientStop& grad) 0098 { 0099 QColor color = grad.knownColorValue; 0100 0101 const int tintedColor = 255 * grad.tintVal / 100.0; 0102 const qreal nonTindedPart = 1.0 - grad.tintVal / 100.0; 0103 color.setRed(tintedColor + nonTindedPart * color.red()); 0104 color.setGreen(tintedColor + nonTindedPart * color.green()); 0105 color.setBlue(tintedColor + nonTindedPart * color.blue()); 0106 0107 return color; 0108 } 0109 0110 QString KoOdfChartWriter::generateGradientStyle(KoGenStyles& mainStyles, 0111 const KoChart::Gradient* grad) 0112 { 0113 KoGenStyle gradStyle(KoGenStyle::GradientStyle); 0114 gradStyle.addAttribute("draw:style", "linear"); 0115 0116 QColor startColor = calculateColorFromGradientStop(grad->gradientStops.first()); 0117 QColor endColor = calculateColorFromGradientStop(grad->gradientStops.last()); 0118 0119 gradStyle.addAttribute("draw:start-color", startColor.name()); 0120 gradStyle.addAttribute("draw:end-color", endColor.name()); 0121 gradStyle.addAttribute("draw:angle", QString::number(grad->angle)); 0122 0123 return mainStyles.insert(gradStyle, "ms_chart_gradient"); 0124 } 0125 0126 QColor KoOdfChartWriter::labelFontColor() const 0127 { 0128 return QColor(); 0129 } 0130 0131 QString KoOdfChartWriter::genChartAreaStyle(KoGenStyle& style, KoGenStyles& styles, 0132 KoGenStyles& mainStyles) 0133 { 0134 if (chart()->m_fillGradient) { 0135 style.addProperty("draw:fill", "gradient", KoGenStyle::GraphicType); 0136 style.addProperty("draw:fill-gradient-name", 0137 generateGradientStyle(mainStyles, chart()->m_fillGradient), 0138 KoGenStyle::GraphicType); 0139 } else { 0140 style.addProperty("draw:fill", "solid", KoGenStyle::GraphicType); 0141 0142 QColor color; 0143 if (chart()->m_areaFormat 0144 && chart()->m_areaFormat->m_fill 0145 && chart()->m_areaFormat->m_foreground.isValid()) 0146 { 0147 color = chart()->m_areaFormat->m_foreground; 0148 } 0149 else 0150 color = QColor(255, 255, 255); 0151 style.addProperty("draw:fill-color", color.name(), KoGenStyle::GraphicType); 0152 0153 if (color.alpha() < 255) 0154 style.addProperty("draw:opacity", 0155 QString("%1%").arg(chart()->m_areaFormat->m_foreground.alphaF() 0156 * 100.0), 0157 KoGenStyle::GraphicType); 0158 } 0159 0160 return styles.insert(style, "ch"); 0161 } 0162 0163 0164 QString KoOdfChartWriter::genChartAreaStyle(KoGenStyles& styles, KoGenStyles& mainStyles) 0165 { 0166 KoGenStyle style(KoGenStyle::GraphicAutoStyle, "chart"); 0167 0168 return genChartAreaStyle(style, styles, mainStyles); 0169 } 0170 0171 0172 QString KoOdfChartWriter::genPlotAreaStyle(KoGenStyle& style, KoGenStyles& styles, 0173 KoGenStyles& mainStyles) 0174 { 0175 KoChart::AreaFormat *areaFormat = ((chart()->m_plotArea 0176 && chart()->m_plotArea->m_areaFormat 0177 && chart()->m_plotArea->m_areaFormat->m_fill) 0178 ? chart()->m_plotArea->m_areaFormat 0179 : chart()->m_areaFormat); 0180 if (chart()->m_plotAreaFillGradient) { 0181 style.addProperty("draw:fill", "gradient", KoGenStyle::GraphicType); 0182 style.addProperty("draw:fill-gradient-name", 0183 generateGradientStyle(mainStyles, chart()->m_plotAreaFillGradient), 0184 KoGenStyle::GraphicType); 0185 } else { 0186 style.addProperty("draw:fill", "solid", KoGenStyle::GraphicType); 0187 0188 QColor color; 0189 if (areaFormat && areaFormat->m_foreground.isValid()) 0190 color = areaFormat->m_foreground; 0191 else 0192 color = QColor(paletteIsSet ? "#C0C0C0" : "#FFFFFF"); 0193 style.addProperty("draw:fill-color", color.name(), KoGenStyle::GraphicType); 0194 0195 if (color.alpha() < 255) 0196 style.addProperty("draw:opacity", 0197 QString("%1%").arg(areaFormat->m_foreground.alphaF() * 100.0), 0198 KoGenStyle::GraphicType); 0199 } 0200 0201 return styles.insert(style, "ch"); 0202 } 0203 0204 0205 void KoOdfChartWriter::addShapePropertyStyle(/*const*/ KoChart::Series* series, KoGenStyle& style, 0206 KoGenStyles& /*mainStyles*/) 0207 { 0208 Q_ASSERT(series); 0209 bool marker = false; 0210 KoChart::ScatterImpl* impl = dynamic_cast< KoChart::ScatterImpl* >(m_chart->m_impl); 0211 0212 if (impl) 0213 marker = (impl->style == KoChart::ScatterImpl::Marker 0214 || impl->style == KoChart::ScatterImpl::LineMarker); 0215 0216 if (series->spPr->lineFill.valid) { 0217 if (series->spPr->lineFill.type == KoChart::Fill::Solid) { 0218 style.addProperty("draw:stroke", "solid", KoGenStyle::GraphicType); 0219 style.addProperty("svg:stroke-color", series->spPr->lineFill.solidColor.name(), 0220 KoGenStyle::GraphicType); 0221 } 0222 else if (series->spPr->lineFill.type == KoChart::Fill::None) { 0223 style.addProperty("draw:stroke", "none", KoGenStyle::GraphicType); 0224 } 0225 } 0226 else if ( (paletteIsSet && m_chart->m_impl->name() != "scatter") 0227 || m_chart->m_showLines) 0228 { 0229 const int curSerNum = m_chart->m_series.indexOf(series); 0230 style.addProperty("draw:stroke", "solid", KoGenStyle::GraphicType); 0231 style.addProperty("svg:stroke-color", m_palette.at(24 + curSerNum).name(), 0232 KoGenStyle::GraphicType); 0233 } 0234 else if (paletteIsSet && m_chart->m_impl->name() == "scatter") 0235 style.addProperty("draw:stroke", "none", KoGenStyle::GraphicType); 0236 if (series->spPr->areaFill.valid) { 0237 if (series->spPr->areaFill.type == KoChart::Fill::Solid) { 0238 style.addProperty("draw:fill", "solid", KoGenStyle::GraphicType); 0239 style.addProperty("draw:fill-color", series->spPr->areaFill.solidColor.name(), 0240 KoGenStyle::GraphicType); 0241 } 0242 else if (series->spPr->areaFill.type == KoChart::Fill::None) 0243 style.addProperty("draw:fill", "none", KoGenStyle::GraphicType); 0244 } 0245 else if (paletteIsSet 0246 && !(m_chart->m_markerType != KoChart::NoMarker || marker) 0247 && series->m_markerType == KoChart::NoMarker) 0248 { 0249 const int curSerNum = m_chart->m_series.indexOf(series) % 8; 0250 style.addProperty("draw:fill", "solid", KoGenStyle::GraphicType); 0251 style.addProperty("draw:fill-color", m_palette.at(16 + curSerNum).name(), 0252 KoGenStyle::GraphicType); 0253 } 0254 } 0255 0256 QString KoOdfChartWriter::genPlotAreaStyle(KoGenStyles& styles, KoGenStyles& mainStyles) 0257 { 0258 KoGenStyle style(KoGenStyle::ChartAutoStyle/*, "chart"*/); 0259 return genPlotAreaStyle(style, styles, mainStyles); 0260 } 0261 0262 QString KoOdfChartWriter::replaceSheet(const QString &originalString, 0263 const QString &replacementSheet) 0264 { 0265 QStringList split = originalString.split(QLatin1Char('!')); 0266 split[0] = replacementSheet; 0267 return split.join(QString::fromLatin1("!")); 0268 } 0269 0270 void KoOdfChartWriter::set2003ColorPalette(QList < QColor > palette) 0271 { 0272 m_palette = palette; 0273 paletteIsSet = true; 0274 } 0275 0276 QString KoOdfChartWriter::markerType(KoChart::MarkerType type, int currentSeriesNumber) 0277 { 0278 QString markerName; 0279 switch(type) { 0280 case NoMarker: 0281 break; 0282 case AutoMarker: { // auto marker type 0283 const int resNum = currentSeriesNumber % 3; 0284 if (resNum == 0) 0285 markerName = "square"; 0286 else if (resNum == 1) 0287 markerName = "diamond"; 0288 else if (resNum == 2) 0289 markerName = "circle"; 0290 } break; 0291 case SquareMarker: 0292 markerName = "square"; 0293 break; 0294 case DiamondMarker: 0295 markerName = "diamond"; 0296 break; 0297 case StarMarker: 0298 markerName = "star"; 0299 break; 0300 case TriangleMarker: 0301 markerName = "arrow-up"; 0302 break; 0303 case DotMarker: 0304 markerName = "dot"; 0305 break; 0306 case PlusMarker: 0307 markerName = "plus"; 0308 break; 0309 case SymbolXMarker: 0310 markerName = "x"; 0311 break; 0312 case CircleMarker: 0313 markerName = "circle"; 0314 break; 0315 case DashMarker: 0316 markerName = "horizontal-bar"; 0317 break; 0318 } 0319 0320 return markerName; 0321 } 0322 0323 0324 // ---------------------------------------------------------------- 0325 // The actual saving code 0326 0327 0328 bool KoOdfChartWriter::saveIndex(KoXmlWriter* xmlWriter) 0329 { 0330 if (!chart() || m_href.isEmpty()) 0331 return false; 0332 0333 // This because for presesentations the frame is done in read_graphicFrame 0334 if (!m_drawLayer) { 0335 xmlWriter->startElement("draw:frame"); 0336 // used in opendocumentpresentation for layers 0337 //if (m_drawLayer) 0338 // xmlWriter->addAttribute("draw:layer", "layout"); 0339 0340 // used in opendocumentspreadsheet to reference cells 0341 if (!m_endCellAddress.isEmpty()) { 0342 xmlWriter->addAttribute("table:end-cell-address", m_endCellAddress); 0343 xmlWriter->addAttributePt("table:end-x", m_end_x); 0344 xmlWriter->addAttributePt("table:end-y", m_end_y); 0345 } 0346 0347 xmlWriter->addAttributePt("svg:x", m_x); 0348 xmlWriter->addAttributePt("svg:y", m_y); 0349 if (m_width > 0) 0350 xmlWriter->addAttributePt("svg:width", m_width); 0351 if (m_height > 0) 0352 xmlWriter->addAttributePt("svg:height", m_height); 0353 } 0354 //xmlWriter->addAttribute("draw:z-index", "0"); 0355 xmlWriter->startElement("draw:object"); 0356 //TODO don't show on e.g. presenter 0357 if (!m_notifyOnUpdateOfRanges.isEmpty()) 0358 xmlWriter->addAttribute("draw:notify-on-update-of-ranges", m_notifyOnUpdateOfRanges); 0359 0360 xmlWriter->addAttribute("xlink:href", "./" + m_href); 0361 xmlWriter->addAttribute("xlink:type", "simple"); 0362 xmlWriter->addAttribute("xlink:show", "embed"); 0363 xmlWriter->addAttribute("xlink:actuate", "onLoad"); 0364 0365 xmlWriter->endElement(); // draw:object 0366 if (!m_drawLayer) { 0367 xmlWriter->endElement(); // draw:frame 0368 } 0369 return true; 0370 } 0371 0372 bool KoOdfChartWriter::saveContent(KoStore* store, KoXmlWriter* manifestWriter) 0373 { 0374 if (!chart() || !chart()->m_impl || m_href.isEmpty()) 0375 return false; 0376 0377 KoGenStyles styles; 0378 KoGenStyles mainStyles; 0379 0380 store->pushDirectory(); 0381 store->enterDirectory(m_href); 0382 0383 KoOdfWriteStore s(store); 0384 KoXmlWriter* bodyWriter = s.bodyWriter(); 0385 KoXmlWriter* contentWriter = s.contentWriter(); 0386 Q_ASSERT(bodyWriter && contentWriter); 0387 0388 bodyWriter->startElement("office:body"); 0389 bodyWriter->startElement("office:chart"); 0390 0391 //<chart:chart chart:class="chart:circle" 0392 // svg:width="8cm" svg:height="7cm" 0393 // chart:style-name="ch1"> 0394 bodyWriter->startElement("chart:chart"); 0395 0396 if (!chart()->m_impl->name().isEmpty()) { 0397 bodyWriter->addAttribute("chart:class", "chart:" + chart()->m_impl->name()); 0398 } 0399 0400 if (m_width > 0) { 0401 bodyWriter->addAttributePt("svg:width", m_width); 0402 } 0403 if (m_height > 0) { 0404 bodyWriter->addAttributePt("svg:height", m_height); 0405 } 0406 0407 bodyWriter->addAttribute("chart:style-name", genChartAreaStyle(styles, mainStyles)); 0408 0409 // <chart:title svg:x="5.618cm" svg:y="0.14cm" chart:style-name="ch2"> 0410 // <text:p>PIE CHART</text:p> 0411 // </chart:title> 0412 if (!chart()->m_title.isEmpty()) { 0413 bodyWriter->startElement("chart:title"); 0414 0415 /* TODO we can't determine this because by default we need to center the title, 0416 in order to center it we need to know the textbox size, and to do that we need 0417 the used font metrics. 0418 0419 Also, for now, the default implementation of KChart centers 0420 the title, so we get close to the expected behavior. We ignore any offset though. 0421 0422 Nonetheless, the formula should be something like this: 0423 const int widht = m_width/2 - textWidth/2 + sprcToPt(t->m_x1, vertical); 0424 const int height = m_height/2 - textHeight/2 + sprcToPt(t->m_y1, horizontal); 0425 bodyWriter->addAttributePt("svg:x", width); 0426 bodyWriter->addAttributePt("svg:y", height); 0427 */ 0428 0429 // NOTE: Don't load width or height, the record MUST be ignored and 0430 // determined by the application 0431 // see [MS-XLS] p. 362 0432 0433 bodyWriter->startElement("text:p"); 0434 bodyWriter->addTextNode(chart()->m_title); 0435 bodyWriter->endElement(); // text:p 0436 bodyWriter->endElement(); // chart:title 0437 } 0438 0439 // Legend 0440 if (chart()->m_legend) { 0441 bodyWriter->startElement("chart:legend"); 0442 bodyWriter->addAttribute("chart:legend-position", "end"); 0443 0444 KoGenStyle legendstyle(KoGenStyle::ChartAutoStyle, "chart"); 0445 0446 QColor labelColor = labelFontColor(); 0447 if (labelColor.isValid()) 0448 legendstyle.addProperty("fo:font-color", labelColor.name(), KoGenStyle::TextType); 0449 0450 bodyWriter->addAttribute("chart:style-name", styles.insert(legendstyle, "lg")); 0451 0452 bodyWriter->endElement(); // chart:legend 0453 } 0454 0455 // <chart:plot-area chart:style-name="ch3" 0456 // table:cell-range-address="Sheet1.C2:Sheet1.E2" 0457 // svg:x="0.16cm" svg:y="0.14cm"> 0458 bodyWriter->startElement("chart:plot-area"); 0459 0460 if (chart()->m_is3d) { 0461 //bodyWriter->addAttribute("dr3d:transform", "matrix (0.893670830886674 0.102940425033731 -0.436755898547686 -0.437131441492021 0.419523087196176 -0.795560483036015 0.101333848646097 0.901888933407692 0.419914042293545 0cm 0cm 0cm)"); 0462 //bodyWriter->addAttribute("dr3d:vrp", "(12684.722548717 7388.35827488833 17691.2795565958)"); 0463 //bodyWriter->addAttribute("dr3d:vpn", "(0.416199821709347 0.173649045905254 0.892537795986984)"); 0464 //bodyWriter->addAttribute("dr3d:vup", "(-0.0733876362771618 0.984807599917971 -0.157379306090273)"); 0465 //bodyWriter->addAttribute("dr3d:projection", "parallel"); 0466 //bodyWriter->addAttribute("dr3d:distance", "4.2cm"); 0467 //bodyWriter->addAttribute("dr3d:focal-length", "8cm"); 0468 //bodyWriter->addAttribute("dr3d:shadow-slant", "0"); 0469 //bodyWriter->addAttribute("dr3d:shade-mode", "flat"); 0470 //bodyWriter->addAttribute("dr3d:ambient-color", "#b3b3b3"); 0471 //bodyWriter->addAttribute("dr3d:lighting-mode", "true"); 0472 } 0473 0474 KoGenStyle chartstyle(KoGenStyle::ChartAutoStyle, "chart"); 0475 //chartstyle.addProperty("chart:connect-bars", "false"); 0476 //chartstyle.addProperty("chart:include-hidden-cells", "false"); 0477 chartstyle.addProperty("chart:auto-position", "true"); 0478 chartstyle.addProperty("chart:auto-size", "true"); 0479 chartstyle.addProperty("chart:angle-offset", chart()->m_angleOffset); 0480 0481 //chartstyle.addProperty("chart:series-source", "rows"); 0482 //chartstyle.addProperty("chart:sort-by-x-values", "false"); 0483 //chartstyle.addProperty("chart:right-angled-axes", "true"); 0484 if (chart()->m_is3d) { 0485 chartstyle.addProperty("chart:three-dimensional", "true"); 0486 } 0487 //chartstyle.addProperty("chart:angle-offset", "90"); 0488 //chartstyle.addProperty("chart:series-source", "rows"); 0489 //chartstyle.addProperty("chart:right-angled-axes", "false"); 0490 if (chart()->m_transpose) { 0491 chartstyle.addProperty("chart:vertical", "true"); 0492 } 0493 if (chart()->m_stacked) { 0494 chartstyle.addProperty("chart:stacked", "true"); 0495 } 0496 if (chart()->m_f100) { 0497 chartstyle.addProperty("chart:percentage", "true"); 0498 } 0499 bodyWriter->addAttribute("chart:style-name", genPlotAreaStyle(chartstyle, styles, mainStyles)); 0500 0501 QString verticalCellRangeAddress = chart()->m_verticalCellRangeAddress; 0502 // FIXME microsoft treats the regions from this area in a different order, so don't use it or x and y values will be switched 0503 // if (!chart()->m_cellRangeAddress.isEmpty()) { 0504 // if (sheetReplacement) 0505 // bodyWriter->addAttribute("table:cell-range-address", replaceSheet(normalizeCellRange(m_cellRangeAddress), QString::fromLatin1("local"))); //"Sheet1.C2:Sheet1.E5"); 0506 // else 0507 // bodyWriter->addAttribute("table:cell-range-address", normalizeCellRange(m_cellRangeAddress)); //"Sheet1.C2:Sheet1.E5"); 0508 // } 0509 0510 /*FIXME 0511 if (verticalCellRangeAddress.isEmpty()) { 0512 // only add the chart:data-source-has-labels if no chart:categories with a table:cell-range-address was defined within an axis. 0513 bodyWriter->addAttribute("chart:data-source-has-labels", "both"); 0514 } 0515 */ 0516 0517 //bodyWriter->addAttribute("svg:x", "0.16cm"); //FIXME 0518 //bodyWriter->addAttribute("svg:y", "0.14cm"); //FIXME 0519 //bodyWriter->addAttribute("svg:width", "6.712cm"); //FIXME 0520 //bodyWriter->addAttribute("svg:height", "6.58cm"); //FIXME 0521 0522 const bool definesCategories = chart()->m_impl->name() != "scatter"; // scatter charts are using domains 0523 int countXAxis = 0; 0524 int countYAxis = 0; 0525 foreach (KoChart::Axis* axis, chart()->m_axes) { 0526 //TODO handle series-axis 0527 if (axis->m_type == KoChart::Axis::SeriesAxis) continue; 0528 0529 bodyWriter->startElement("chart:axis"); 0530 0531 KoGenStyle axisstyle(KoGenStyle::ChartAutoStyle, "chart"); 0532 0533 if (axis->m_reversed) 0534 axisstyle.addProperty("chart:reverse-direction", "true", KoGenStyle::ChartType); 0535 0536 //FIXME this hits an infinite-looping bug in kdchart it seems... maybe fixed with a newer version 0537 // if (axis->m_logarithmic) 0538 // axisstyle.addProperty("chart:logarithmic", "true", KoGenStyle::ChartType); 0539 0540 if (!axis->m_autoMinimum) 0541 axisstyle.addProperty("chart:minimum", QString::number(axis->m_minimum, 'f'), 0542 KoGenStyle::ChartType); 0543 if (!axis->m_autoMaximum) 0544 axisstyle.addProperty("chart:maximum", QString::number(axis->m_maximum, 'f'), 0545 KoGenStyle::ChartType); 0546 0547 axisstyle.addProperty("fo:font-size", QString("%0pt").arg(chart()->m_textSize), 0548 KoGenStyle::TextType); 0549 0550 QColor labelColor = labelFontColor(); 0551 if (labelColor.isValid()) 0552 axisstyle.addProperty("fo:font-color", labelColor.name(), KoGenStyle::TextType); 0553 0554 if (!axis->m_numberFormat.isEmpty()) { 0555 const KoGenStyle style = NumberFormatParser::parse(axis->m_numberFormat, &styles); 0556 axisstyle.addAttribute("style:data-style-name", styles.insert(style, "ds")); 0557 } 0558 0559 bodyWriter->addAttribute("chart:style-name", styles.insert(axisstyle, "ch")); 0560 0561 switch(axis->m_type) { 0562 case KoChart::Axis::VerticalValueAxis: 0563 bodyWriter->addAttribute("chart:dimension", "y"); 0564 bodyWriter->addAttribute("chart:name", QString("y%1").arg(++countYAxis)); 0565 break; 0566 case KoChart::Axis::HorizontalValueAxis: 0567 bodyWriter->addAttribute("chart:dimension", "x"); 0568 bodyWriter->addAttribute("chart:name", QString("x%1").arg(++countXAxis)); 0569 if (countXAxis == 1 && definesCategories && !verticalCellRangeAddress.isEmpty()) { 0570 bodyWriter->startElement("chart:categories"); 0571 if (sheetReplacement) 0572 verticalCellRangeAddress 0573 = normalizeCellRange(replaceSheet(verticalCellRangeAddress, 0574 QString::fromLatin1("local"))); 0575 else 0576 verticalCellRangeAddress = normalizeCellRange(verticalCellRangeAddress); 0577 bodyWriter->addAttribute("table:cell-range-address", verticalCellRangeAddress); //"Sheet1.C2:Sheet1.E2"); 0578 bodyWriter->endElement(); 0579 } 0580 break; 0581 default: break; 0582 } 0583 0584 if (axis->m_majorGridlines.m_format.m_style != KoChart::LineFormat::None) { 0585 bodyWriter->startElement("chart:grid"); 0586 bodyWriter->addAttribute("chart:class", "major"); 0587 bodyWriter->endElement(); // chart:grid 0588 } 0589 0590 if (axis->m_minorGridlines.m_format.m_style != KoChart::LineFormat::None) { 0591 bodyWriter->startElement("chart:grid"); 0592 bodyWriter->addAttribute("chart:class", "minor"); 0593 bodyWriter->endElement(); // chart:grid 0594 } 0595 bodyWriter->endElement(); // chart:axis 0596 } 0597 0598 // Add at least one x-axis. 0599 if (countXAxis == 0) { 0600 bodyWriter->startElement("chart:axis"); 0601 bodyWriter->addAttribute("chart:dimension", "x"); 0602 bodyWriter->addAttribute("chart:name", "primary-x"); 0603 0604 if (definesCategories && !verticalCellRangeAddress.isEmpty()) { 0605 bodyWriter->startElement("chart:categories"); 0606 if (sheetReplacement) 0607 verticalCellRangeAddress 0608 = normalizeCellRange(replaceSheet(verticalCellRangeAddress, 0609 QString::fromLatin1("local"))); 0610 else 0611 verticalCellRangeAddress = normalizeCellRange(verticalCellRangeAddress); 0612 0613 bodyWriter->addAttribute("table:cell-range-address", verticalCellRangeAddress); 0614 bodyWriter->endElement(); 0615 } 0616 0617 bodyWriter->endElement(); // chart:axis 0618 } 0619 0620 // Add at least one y-axis. 0621 if (countYAxis == 0) { 0622 bodyWriter->startElement("chart:axis"); 0623 bodyWriter->addAttribute("chart:dimension", "y"); 0624 bodyWriter->addAttribute("chart:name", "primary-y"); 0625 bodyWriter->endElement(); // chart:axis 0626 } 0627 0628 //<chart:axis chart:dimension="x" chart:name="primary-x" chart:style-name="ch4"/> 0629 //<chart:axis chart:dimension="y" chart:name="primary-y" chart:style-name="ch5"><chart:grid chart:style-name="ch6" chart:class="major"/></chart:axis> 0630 0631 // NOTE: The XLS format specifies that if an explodeFactor that is > 100 0632 // is found, we should find the biggest and make it 100, then scale 0633 // all the other factors accordingly. 0634 // see 2.4.195 PieFormat 0635 int maxExplode = 100; 0636 foreach (KoChart::Series* series, chart()->m_series) { 0637 foreach (KoChart::Format* f, series->m_datasetFormat) { 0638 if (KoChart::PieFormat* pieformat = dynamic_cast<KoChart::PieFormat*>(f)) { 0639 if (pieformat->m_pcExplode > 0) { 0640 maxExplode = qMax(maxExplode, pieformat->m_pcExplode); 0641 } 0642 } 0643 } 0644 } 0645 0646 // Area diagrams are special in that Excel displays the areas in another 0647 // order than OpenOffice.org and Calligra Sheets. To make sure the same areas are 0648 // visible we do the same as OpenOffice.org does and reverse the order. 0649 if (chart()->m_impl->name() == "area") { 0650 for (int i = chart()->m_series.count() - 1; i >= 0; --i) { 0651 chart()->m_series.append(chart()->m_series.takeAt(i)); 0652 } 0653 } 0654 0655 // Save the series. 0656 if (!saveSeries(styles, mainStyles, bodyWriter, maxExplode)) 0657 return false; 0658 0659 bodyWriter->startElement("chart:wall"); 0660 bodyWriter->endElement(); // chart:wall 0661 0662 bodyWriter->startElement("chart:floor"); 0663 bodyWriter->endElement(); // chart:floor 0664 0665 bodyWriter->endElement(); // chart:plot-area 0666 0667 writeInternalTable(bodyWriter); 0668 0669 bodyWriter->endElement(); // chart:chart 0670 bodyWriter->endElement(); // office:chart 0671 bodyWriter->endElement(); // office:body 0672 0673 #ifdef CONTENTXML_DEBUG 0674 debugOdf2 << bodyWriter->toString(); 0675 #endif 0676 0677 styles.saveOdfStyles(KoGenStyles::DocumentAutomaticStyles, contentWriter); 0678 s.closeContentWriter(); 0679 0680 if (store->open("styles.xml")) { 0681 KoStoreDevice dev(store); 0682 KoXmlWriter* stylesWriter = new KoXmlWriter(&dev); 0683 0684 stylesWriter->startDocument("office:document-styles"); 0685 stylesWriter->startElement("office:document-styles"); 0686 stylesWriter->addAttribute("xmlns:office", "urn:oasis:names:tc:opendocument:xmlns:office:1.0"); 0687 stylesWriter->addAttribute("xmlns:style", "urn:oasis:names:tc:opendocument:xmlns:style:1.0"); 0688 stylesWriter->addAttribute("xmlns:text", "urn:oasis:names:tc:opendocument:xmlns:text:1.0"); 0689 stylesWriter->addAttribute("xmlns:table", "urn:oasis:names:tc:opendocument:xmlns:table:1.0"); 0690 stylesWriter->addAttribute("xmlns:draw", "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0"); 0691 stylesWriter->addAttribute("xmlns:fo", "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"); 0692 stylesWriter->addAttribute("xmlns:svg", "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"); 0693 stylesWriter->addAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink"); 0694 stylesWriter->addAttribute("xmlns:chart", "urn:oasis:names:tc:opendocument:xmlns:chart:1.0"); 0695 stylesWriter->addAttribute("xmlns:dc", "http://purl.org/dc/elements/1.1/"); 0696 stylesWriter->addAttribute("xmlns:meta", "urn:oasis:names:tc:opendocument:xmlns:meta:1.0"); 0697 stylesWriter->addAttribute("xmlns:number", "urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0"); 0698 stylesWriter->addAttribute("xmlns:dr3d", "urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0"); 0699 stylesWriter->addAttribute("xmlns:math", "http://www.w3.org/1998/Math/MathML"); 0700 stylesWriter->addAttribute("xmlns:of", "urn:oasis:names:tc:opendocument:xmlns:of:1.2"); 0701 stylesWriter->addAttribute("office:version", "1.2"); 0702 mainStyles.saveOdfStyles(KoGenStyles::MasterStyles, stylesWriter); 0703 mainStyles.saveOdfStyles(KoGenStyles::DocumentStyles, stylesWriter); // office:style 0704 mainStyles.saveOdfStyles(KoGenStyles::DocumentAutomaticStyles, stylesWriter); // office:automatic-styles 0705 stylesWriter->endElement(); // office:document-styles 0706 stylesWriter->endDocument(); 0707 0708 delete stylesWriter; 0709 store->close(); 0710 } 0711 0712 manifestWriter->addManifestEntry(m_href + QLatin1Char('/'), "application/vnd.oasis.opendocument.chart"); 0713 manifestWriter->addManifestEntry(QString("%1/styles.xml").arg(m_href), "text/xml"); 0714 manifestWriter->addManifestEntry(QString("%1/content.xml").arg(m_href), "text/xml"); 0715 0716 store->popDirectory(); 0717 return true; 0718 } 0719 0720 // FIXME: We should probably create a KoOdfChartWriterContext out of these 0721 // parameters when we add more similar saving functions later. 0722 bool KoOdfChartWriter::saveSeries(KoGenStyles &styles, KoGenStyles &mainStyles, 0723 KoXmlWriter* bodyWriter, int maxExplode) 0724 { 0725 int curSerNum = 0; 0726 bool lines = true; 0727 bool marker = false; 0728 Q_FOREACH (KoChart::Series* series, chart()->m_series) { 0729 lines = true; 0730 if (chart()->m_impl->name() == "scatter" && !paletteIsSet) { 0731 KoChart::ScatterImpl* impl = static_cast< KoChart::ScatterImpl* >(chart()->m_impl); 0732 lines = (impl->style == KoChart::ScatterImpl::Line 0733 || impl->style == KoChart::ScatterImpl::LineMarker); 0734 marker = (impl->style == KoChart::ScatterImpl::Marker 0735 || impl->style == KoChart::ScatterImpl::LineMarker); 0736 } 0737 const bool noLineFill = ((series->spPr != 0) 0738 && series->spPr->lineFill.type == KoChart::Fill::None); 0739 lines = lines && !noLineFill; 0740 lines = lines || m_chart->m_showLines; 0741 0742 // <chart:series chart:style-name="ch7" 0743 // chart:values-cell-range-address="Sheet1.C2:Sheet1.E2" 0744 // chart:class="chart:circle"> 0745 bodyWriter->startElement("chart:series"); 0746 KoGenStyle seriesstyle(KoGenStyle::GraphicAutoStyle, "chart"); 0747 if (series->spPr) 0748 addShapePropertyStyle(series, seriesstyle, mainStyles); 0749 else if (lines && paletteIsSet) { 0750 lines = false; 0751 seriesstyle.addProperty("draw:stroke", "solid", KoGenStyle::GraphicType); 0752 seriesstyle.addProperty("svg:stroke-color", m_palette.at(24 + curSerNum).name(), 0753 KoGenStyle::GraphicType); 0754 } 0755 0756 if (paletteIsSet 0757 && m_chart->m_impl->name() != "ring" 0758 && m_chart->m_impl->name() != "circle") 0759 { 0760 if (series->m_markerType == KoChart::NoMarker 0761 && m_chart->m_markerType == KoChart::NoMarker 0762 && !marker) 0763 { 0764 seriesstyle.addProperty("draw:fill", "solid", KoGenStyle::GraphicType); 0765 seriesstyle.addProperty("draw:fill-color", m_palette.at(16 + curSerNum).name(), 0766 KoGenStyle::GraphicType); 0767 } 0768 } 0769 0770 if (series->m_markerType != KoChart::NoMarker) { 0771 QString markerName = markerType(series->m_markerType, curSerNum); 0772 if (!markerName.isEmpty()) { 0773 seriesstyle.addProperty("chart:symbol-type", "named-symbol", KoGenStyle::ChartType); 0774 seriesstyle.addProperty("chart:symbol-name", markerName, KoGenStyle::ChartType); 0775 } 0776 } 0777 else if (m_chart->m_markerType != KoChart::NoMarker || marker) { 0778 QString markerName = markerType(m_chart->m_markerType == KoChart::NoMarker 0779 ? KoChart::AutoMarker 0780 : m_chart->m_markerType, curSerNum); 0781 if (!markerName.isEmpty()) { 0782 seriesstyle.addProperty("chart:symbol-type", "named-symbol", KoGenStyle::ChartType); 0783 seriesstyle.addProperty("chart:symbol-name", markerName, KoGenStyle::ChartType); 0784 } 0785 } 0786 0787 if (chart()->m_impl->name() != "circle" && chart()->m_impl->name() != "ring") 0788 addDataThemeToStyle(seriesstyle, curSerNum, chart()->m_series.count(), lines); 0789 //seriesstyle.addProperty("draw:stroke", "solid"); 0790 //seriesstyle.addProperty("draw:fill-color", "#ff0000"); 0791 0792 foreach (KoChart::Format* f, series->m_datasetFormat) { 0793 if (KoChart::PieFormat* pieformat = dynamic_cast<KoChart::PieFormat*>(f)) { 0794 if (pieformat->m_pcExplode > 0) { 0795 // Note that 100.0/maxExplode will yield 1.0 most of the 0796 // time, that's why do that division first 0797 const int pcExplode = (int)((float)pieformat->m_pcExplode * (100.0 / (float)maxExplode)); 0798 seriesstyle.addProperty("chart:pie-offset", pcExplode, KoGenStyle::ChartType); 0799 } 0800 } 0801 } 0802 0803 if (series->m_showDataLabelValues && series->m_showDataLabelPercent) { 0804 seriesstyle.addProperty("chart:data-label-number", "value-and-percentage", 0805 KoGenStyle::ChartType); 0806 } else if (series->m_showDataLabelValues) { 0807 seriesstyle.addProperty("chart:data-label-number", "value", KoGenStyle::ChartType); 0808 } else if (series->m_showDataLabelPercent) { 0809 seriesstyle.addProperty("chart:data-label-number", "percentage", KoGenStyle::ChartType); 0810 } 0811 0812 if (series->m_showDataLabelCategory) { 0813 seriesstyle.addProperty("chart:data-label-text", "true", KoGenStyle::ChartType); 0814 } 0815 //seriesstyle.addProperty("chart:data-label-symbol", "true", KoGenStyle::ChartType); 0816 0817 if (!series->m_numberFormat.isEmpty()) { 0818 const KoGenStyle style = NumberFormatParser::parse(series->m_numberFormat, &styles); 0819 seriesstyle.addAttribute("style:data-style-name", styles.insert(style, "ds")); 0820 } 0821 0822 bodyWriter->addAttribute("chart:style-name", styles.insert(seriesstyle, "ch")); 0823 0824 // ODF does not support custom labels so we depend on the 0825 // SeriesLegendOrTrendlineName being defined and to point to a valid 0826 // cell to be able to display custom labels. 0827 if (series->m_datasetValue.contains(KoChart::Value::SeriesLegendOrTrendlineName)) { 0828 KoChart::Value* v = series->m_datasetValue[KoChart::Value::SeriesLegendOrTrendlineName]; 0829 if (!v->m_formula.isEmpty()) { 0830 bodyWriter->addAttribute("chart:label-cell-address", 0831 (v->m_type == KoChart::Value::CellRange 0832 ? normalizeCellRange(v->m_formula) 0833 : v->m_formula)); 0834 } 0835 } 0836 0837 if (!series->m_labelCell.isEmpty()) { 0838 QString labelAddress = series->m_labelCell; 0839 if (sheetReplacement) 0840 labelAddress = normalizeCellRange(replaceSheet(labelAddress, 0841 QString::fromLatin1("local"))); 0842 else 0843 labelAddress = normalizeCellRange(labelAddress); 0844 bodyWriter->addAttribute("chart:label-cell-address", labelAddress); 0845 } 0846 0847 QString valuesCellRangeAddress; 0848 if (sheetReplacement) 0849 valuesCellRangeAddress 0850 = normalizeCellRange(replaceSheet(series->m_valuesCellRangeAddress, 0851 QString::fromLatin1("local"))); 0852 else 0853 valuesCellRangeAddress = normalizeCellRange(series->m_valuesCellRangeAddress); 0854 0855 if (!valuesCellRangeAddress.isEmpty()) { 0856 // "Sheet1.C2:Sheet1.E2"; 0857 bodyWriter->addAttribute("chart:values-cell-range-address", valuesCellRangeAddress); 0858 } 0859 else if (!series->m_domainValuesCellRangeAddress.isEmpty()) { 0860 // "Sheet1.C2:Sheet1.E2"; 0861 bodyWriter->addAttribute("chart:values-cell-range-address", 0862 series->m_domainValuesCellRangeAddress.last()); 0863 } 0864 0865 bodyWriter->addAttribute("chart:class", "chart:" + chart()->m_impl->name()); 0866 0867 // if (chart()->m_impl->name() == "scatter") { 0868 // bodyWriter->startElement("chart:domain"); 0869 // bodyWriter->addAttribute("table:cell-range-address", verticalCellRangeAddress); //"Sheet1.C2:Sheet1.E5"); 0870 // bodyWriter->endElement(); 0871 // } else if (chart()->m_impl->name() == "bubble") { 0872 0873 QString domainRange; 0874 Q_FOREACH (const QString& curRange, series->m_domainValuesCellRangeAddress) { 0875 bodyWriter->startElement("chart:domain"); 0876 if (sheetReplacement) 0877 domainRange = normalizeCellRange(replaceSheet(curRange, QString::fromLatin1("local"))); 0878 else 0879 domainRange = normalizeCellRange(curRange); 0880 if (!domainRange.isEmpty()) 0881 bodyWriter->addAttribute("table:cell-range-address", domainRange); 0882 bodyWriter->endElement(); 0883 } 0884 // if (series->m_domainValuesCellRangeAddress.count() == 1){ 0885 // bodyWriter->startElement("chart:domain"); 0886 // bodyWriter->addAttribute("table:cell-range-address", series->m_domainValuesCellRangeAddress.last()); //"Sheet1.C2:Sheet1.E5"); 0887 // bodyWriter->endElement(); 0888 // } 0889 // if (series->m_domainValuesCellRangeAddress.isEmpty()){ 0890 // bodyWriter->startElement("chart:domain"); 0891 // bodyWriter->addAttribute("table:cell-range-address", series->m_valuesCellRangeAddress); //"Sheet1.C2:Sheet1.E5"); 0892 // bodyWriter->endElement(); 0893 // bodyWriter->startElement("chart:domain"); 0894 // bodyWriter->addAttribute("table:cell-range-address", series->m_valuesCellRangeAddress); //"Sheet1.C2:Sheet1.E5"); 0895 // bodyWriter->endElement(); 0896 // } 0897 // } 0898 0899 for (int j = 0; j < series->m_countYValues; ++j) { 0900 bodyWriter->startElement("chart:data-point"); 0901 KoGenStyle gs(KoGenStyle::GraphicAutoStyle, "chart"); 0902 0903 if (chart()->m_impl->name() == "circle" || chart()->m_impl->name() == "ring") { 0904 QColor fillColor; 0905 if (j < series->m_dataPoints.count()) { 0906 KoChart::DataPoint *dataPoint = series->m_dataPoints[j]; 0907 if (dataPoint->m_areaFormat) { 0908 fillColor = dataPoint->m_areaFormat->m_foreground; 0909 } 0910 } 0911 0912 if (fillColor.isValid()) { 0913 gs.addProperty("draw:fill", "solid", KoGenStyle::GraphicType); 0914 gs.addProperty("draw:fill-color", fillColor.name(), KoGenStyle::GraphicType); 0915 } 0916 else if (series->m_markerType == KoChart::NoMarker 0917 && m_chart->m_markerType == KoChart::NoMarker 0918 && !marker) 0919 { 0920 if (paletteIsSet) { 0921 gs.addProperty("draw:fill", "solid", KoGenStyle::GraphicType); 0922 gs.addProperty("draw:fill-color", m_palette.at(16 + j).name(), 0923 KoGenStyle::GraphicType); 0924 } 0925 else { 0926 addDataThemeToStyle(gs, j, series->m_countYValues, lines); 0927 } 0928 } 0929 }/* 0930 else 0931 { 0932 addSeriesThemeToStyle(gs, curSerNum, chart()->m_series.count()); 0933 }*/ 0934 0935 //gs.addProperty("chart:solid-type", "cuboid", KoGenStyle::ChartType); 0936 //gs.addProperty("draw:fill-color",j==0?"#004586":j==1?"#ff420e":"#ffd320", 0937 // KoGenStyle::GraphicType); 0938 bodyWriter->addAttribute("chart:style-name", styles.insert(gs, "ch")); 0939 0940 Q_FOREACH (KoChart::Text* t, series->m_texts) { 0941 bodyWriter->startElement("chart:data-label"); 0942 bodyWriter->startElement("text:p"); 0943 bodyWriter->addTextNode(t->m_text); 0944 bodyWriter->endElement(); 0945 bodyWriter->endElement(); 0946 } 0947 0948 bodyWriter->endElement(); 0949 } 0950 0951 ++curSerNum; 0952 bodyWriter->endElement(); // chart:series 0953 } 0954 0955 return true; 0956 } 0957 0958 0959 // ---------------------------------------------------------------- 0960 // Some helper functions 0961 0962 0963 // Calculate fade factor as suggested in msoo xml reference page 4161 0964 qreal KoOdfChartWriter::calculateFade(int index, int maxIndex) 0965 { 0966 return -70.0 + 140.0 * ((double) index / ((double) maxIndex + 1.0)); 0967 } 0968 0969 QColor KoOdfChartWriter::shadeColor(const QColor& col, qreal factor) 0970 { 0971 QColor result = col; 0972 qreal luminance = 0.0; 0973 qreal hue = 0.0; 0974 qreal sat = 0.0; 0975 result.getHslF(&hue, &sat, &luminance); 0976 luminance *= factor; 0977 result.setHslF(hue, sat, luminance); 0978 return result; 0979 } 0980 0981 void KoOdfChartWriter::addDataThemeToStyle(KoGenStyle& style, int dataNumber, int maxNumData, 0982 bool strokes) 0983 { 0984 // FIXME: This is only relevant to themes, so remove this function after 0985 // we are done with saveContent(). 0986 Q_UNUSED(style); 0987 Q_UNUSED(dataNumber); 0988 Q_UNUSED(maxNumData); 0989 Q_UNUSED(strokes); 0990 } 0991 0992 0993 float KoOdfChartWriter::sprcToPt(int sprc, Orientation orientation ) 0994 { 0995 if (orientation & vertical) 0996 return (float)sprc * ( (float)m_width / 4000.0); 0997 0998 return (float)sprc * ( (float)m_height / 4000.0); 0999 } 1000 1001 void KoOdfChartWriter::writeInternalTable(KoXmlWriter* bodyWriter) 1002 { 1003 Q_ASSERT( bodyWriter ); 1004 bodyWriter->startElement("table:table"); 1005 bodyWriter->addAttribute( "table:name", "local" ); 1006 1007 bodyWriter->startElement( "table:table-header-columns" ); 1008 bodyWriter->startElement( "table:table-column" ); 1009 bodyWriter->endElement(); 1010 bodyWriter->endElement(); 1011 1012 bodyWriter->startElement( "table:table-columns" ); 1013 bodyWriter->startElement( "table:table-column" ); 1014 bodyWriter->endElement(); 1015 bodyWriter->endElement(); 1016 1017 bodyWriter->startElement( "table:table-rows" ); 1018 1019 const int rowCount = chart()->m_internalTable.maxRow(); 1020 for (int r = 1; r <= rowCount; ++r) { 1021 bodyWriter->startElement("table:table-row"); 1022 const int columnCount = chart()->m_internalTable.maxCellsInRow(r); 1023 for (int c = 1; c <= columnCount; ++c) { 1024 bodyWriter->startElement("table:table-cell"); 1025 if (Cell* cell = chart()->m_internalTable.cell(c, r, false)) { 1026 //debugOdf2 << "cell->m_value " << cell->m_value; 1027 if (!cell->m_value.isEmpty()) { 1028 if (!cell->m_valueType.isEmpty()) { 1029 bodyWriter->addAttribute("office:value-type", cell->m_valueType); 1030 if (cell->m_valueType == "string") { 1031 bodyWriter->addAttribute("office:string-value", cell->m_value); 1032 } else if (cell->m_valueType == "boolean") { 1033 bodyWriter->addAttribute("office:boolean-value", cell->m_value); 1034 } else if (cell->m_valueType == "date") { 1035 bodyWriter->addAttribute("office:date-value", cell->m_value); 1036 } else if (cell->m_valueType == "time") { 1037 bodyWriter->addAttribute("office:time-value", cell->m_value); 1038 } else { // float, percentage and currency including fraction and scientific 1039 bodyWriter->addAttribute("office:value", cell->m_value); 1040 } 1041 } 1042 1043 bodyWriter->startElement("text:p"); 1044 bodyWriter->addTextNode( cell->m_value ); 1045 bodyWriter->endElement(); // text:p 1046 } 1047 } 1048 bodyWriter->endElement(); // table:table-cell 1049 } 1050 bodyWriter->endElement(); // table:table-row 1051 } 1052 bodyWriter->endElement(); // table:table-rows 1053 bodyWriter->endElement(); // table:table 1054 } 1055 1056 void KoOdfChartWriter::setSheetReplacement( bool val ) 1057 { 1058 sheetReplacement = val; 1059 }