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