diff --git a/src/gui/text/qtextodfwriter.cpp b/src/gui/text/qtextodfwriter.cpp index 30f5bc1051..ce65f9def1 100644 --- a/src/gui/text/qtextodfwriter.cpp +++ b/src/gui/text/qtextodfwriter.cpp @@ -183,6 +183,35 @@ static QString bulletChar(QTextListFormat::Style style) } } +static QString borderStyleName(QTextFrameFormat::BorderStyle style) +{ + switch (style) { + case QTextFrameFormat::BorderStyle_None: + return QString::fromLatin1("none"); + case QTextFrameFormat::BorderStyle_Dotted: + return QString::fromLatin1("dotted"); + case QTextFrameFormat::BorderStyle_Dashed: + return QString::fromLatin1("dashed"); + case QTextFrameFormat::BorderStyle_Solid: + return QString::fromLatin1("solid"); + case QTextFrameFormat::BorderStyle_Double: + return QString::fromLatin1("double"); + case QTextFrameFormat::BorderStyle_DotDash: + return QString::fromLatin1("dashed"); + case QTextFrameFormat::BorderStyle_DotDotDash: + return QString::fromLatin1("dotted"); + case QTextFrameFormat::BorderStyle_Groove: + return QString::fromLatin1("groove"); + case QTextFrameFormat::BorderStyle_Ridge: + return QString::fromLatin1("ridge"); + case QTextFrameFormat::BorderStyle_Inset: + return QString::fromLatin1("inset"); + case QTextFrameFormat::BorderStyle_Outset: + return QString::fromLatin1("outset"); + } + return QString::fromLatin1(""); +} + void QTextOdfWriter::writeFrame(QXmlStreamWriter &writer, const QTextFrame *frame) { Q_ASSERT(frame); @@ -190,8 +219,21 @@ void QTextOdfWriter::writeFrame(QXmlStreamWriter &writer, const QTextFrame *fram if (table) { // Start a table. writer.writeStartElement(tableNS, QString::fromLatin1("table")); - writer.writeEmptyElement(tableNS, QString::fromLatin1("table-column")); - writer.writeAttribute(tableNS, QString::fromLatin1("number-columns-repeated"), QString::number(table->columns())); + writer.writeAttribute(tableNS, QString::fromLatin1("style-name"), + QString::fromLatin1("Table%1").arg(table->formatIndex())); + // check if column widths are set, if so add TableNS line above for all columns and link to style + if (m_tableFormatsWithColWidthConstraints.contains(table->formatIndex())) { + for (int colit = 0; colit < table->columns(); ++colit) { + writer.writeStartElement(tableNS, QString::fromLatin1("table-column")); + writer.writeAttribute(tableNS, QString::fromLatin1("style-name"), + QString::fromLatin1("Table%1.%2").arg(table->formatIndex()).arg(colit)); + writer.writeEndElement(); + } + } else { + writer.writeEmptyElement(tableNS, QString::fromLatin1("table-column")); + writer.writeAttribute(tableNS, QString::fromLatin1("number-columns-repeated"), + QString::number(table->columns())); + } } else if (frame->document() && frame->document()->rootFrame() != frame) { // start a section writer.writeStartElement(textNS, QString::fromLatin1("section")); } @@ -219,7 +261,15 @@ void QTextOdfWriter::writeFrame(QXmlStreamWriter &writer, const QTextFrame *fram if (cell.rowSpan() > 1) writer.writeAttribute(tableNS, QString::fromLatin1("number-rows-spanned"), QString::number(cell.rowSpan())); if (cell.format().isTableCellFormat()) { - writer.writeAttribute(tableNS, QString::fromLatin1("style-name"), QString::fromLatin1("T%1").arg(cell.tableCellFormatIndex())); + if (m_cellFormatsInTablesWithBorders.contains(cell.tableCellFormatIndex()) ) { + // writing table:style-name tag in element + writer.writeAttribute(tableNS, QString::fromLatin1("style-name"), + QString::fromLatin1("TB%1.%2").arg(table->formatIndex()) + .arg(cell.tableCellFormatIndex())); + } else { + writer.writeAttribute(tableNS, QString::fromLatin1("style-name"), + QString::fromLatin1("T%1").arg(cell.tableCellFormatIndex())); + } } } writeBlock(writer, block); @@ -326,7 +376,10 @@ void QTextOdfWriter::writeBlock(QXmlStreamWriter &writer, const QTextBlock &bloc if (i < fragmentText.count()) { if (character.unicode() == 0x2028) { // soft-return //if (exportedIndex < i) - writer.writeCharacters(fragmentText.mid(exportedIndex, i - exportedIndex)); + writer.writeCharacters(fragmentText.mid(exportedIndex, i - exportedIndex)); + // adding tab before line-break, so last line in justified paragraph + // will not stretch to the end + writer.writeEmptyElement(textNS, QString::fromLatin1("tab")); writer.writeEmptyElement(textNS, QString::fromLatin1("line-break")); exportedIndex = i+1; continue; @@ -387,7 +440,6 @@ void QTextOdfWriter::writeInlineCharacter(QXmlStreamWriter &writer, const QTextF } } - // ^^^ Copy pasted mostly from Qt ================= if (! image.isNull()) { QBuffer imageBytes; QImageWriter imageWriter(&imageBytes, "png"); @@ -396,17 +448,17 @@ void QTextOdfWriter::writeInlineCharacter(QXmlStreamWriter &writer, const QTextF m_strategy->addFile(filename, QString::fromLatin1("image/png"), imageBytes.data()); // get the width/height from the format. - qreal width = (imageFormat.hasProperty(QTextFormat::ImageWidth)) ? imageFormat.width() : image.width(); + qreal width = imageFormat.hasProperty(QTextFormat::ImageWidth) + ? imageFormat.width() : image.width(); writer.writeAttribute(svgNS, QString::fromLatin1("width"), pixelToPoint(width)); - qreal height = (imageFormat.hasProperty(QTextFormat::ImageHeight)) ? imageFormat.height() : image.height(); + qreal height = imageFormat.hasProperty(QTextFormat::ImageHeight) + ? imageFormat.height() : image.height(); writer.writeAttribute(svgNS, QString::fromLatin1("height"), pixelToPoint(height)); - writer.writeStartElement(drawNS, QString::fromLatin1("image")); writer.writeAttribute(xlinkNS, QString::fromLatin1("href"), filename); writer.writeEndElement(); // image } } - writer.writeEndElement(); // frame } @@ -421,7 +473,7 @@ void QTextOdfWriter::writeFormats(QXmlStreamWriter &writer, const QSet &for switch (textFormat.type()) { case QTextFormat::CharFormat: if (textFormat.isTableCellFormat()) - writeTableCellFormat(writer, textFormat.toTableCellFormat(), formatIndex); + writeTableCellFormat(writer, textFormat.toTableCellFormat(), formatIndex, allStyles); else writeCharacterFormat(writer, textFormat.toCharFormat(), formatIndex); break; @@ -432,10 +484,15 @@ void QTextOdfWriter::writeFormats(QXmlStreamWriter &writer, const QSet &for writeListFormat(writer, textFormat.toListFormat(), formatIndex); break; case QTextFormat::FrameFormat: - writeFrameFormat(writer, textFormat.toFrameFormat(), formatIndex); + if (textFormat.isTableFormat()) + writeTableFormat(writer, textFormat.toTableFormat(), formatIndex); + else + writeFrameFormat(writer, textFormat.toFrameFormat(), formatIndex); break; case QTextFormat::TableFormat: - ;break; + // this case never happens, because TableFormat is a FrameFormat + Q_UNREACHABLE(); + break; } } @@ -449,6 +506,36 @@ void QTextOdfWriter::writeBlockFormat(QXmlStreamWriter &writer, QTextBlockFormat writer.writeAttribute(styleNS, QString::fromLatin1("family"), QString::fromLatin1("paragraph")); writer.writeStartElement(styleNS, QString::fromLatin1("paragraph-properties")); + if (format.hasProperty(QTextBlockFormat::LineHeightType)) { + const int blockLineHeightType = format.lineHeightType(); + const qreal blockLineHeight = format.lineHeight(); + QString type, value; + switch (blockLineHeightType) { + case QTextBlockFormat::SingleHeight: + type = QString::fromLatin1("line-height"); + value = QString::fromLatin1("100%"); + break; + case QTextBlockFormat::ProportionalHeight: + type = QString::fromLatin1("line-height"); + value = QString::number(blockLineHeight) + QString::fromLatin1("%"); + break; + case QTextBlockFormat::FixedHeight: + type = QString::fromLatin1("line-height"); + value = pixelToPoint(qMax(qreal(0.), blockLineHeight)); + break; + case QTextBlockFormat::MinimumHeight: + type = QString::fromLatin1("line-height-at-least"); + value = pixelToPoint(qMax(qreal(0.), blockLineHeight)); + break; + case QTextBlockFormat::LineDistanceHeight: + type = QString::fromLatin1("line-spacing"); + value = pixelToPoint(qMax(qreal(0.), blockLineHeight)); + } + + if (!type.isNull()) + writer.writeAttribute(styleNS, type, value); + } + if (format.hasProperty(QTextFormat::BlockAlignment)) { const Qt::Alignment alignment = format.alignment() & Qt::AlignHorizontal_Mask; QString value; @@ -693,14 +780,108 @@ void QTextOdfWriter::writeFrameFormat(QXmlStreamWriter &writer, QTextFrameFormat // PageBreakFlags pageBreakPolicy () const } -void QTextOdfWriter::writeTableCellFormat(QXmlStreamWriter &writer, QTextTableCellFormat format, int formatIndex) const +void QTextOdfWriter::writeTableFormat(QXmlStreamWriter &writer, QTextTableFormat format, int formatIndex) const { + // start writing table style element writer.writeStartElement(styleNS, QString::fromLatin1("style")); - writer.writeAttribute(styleNS, QString::fromLatin1("name"), QString::fromLatin1("T%1").arg(formatIndex)); + writer.writeAttribute(styleNS, QString::fromLatin1("name"), + QString::fromLatin1("Table%1").arg(formatIndex)); writer.writeAttribute(styleNS, QString::fromLatin1("family"), QString::fromLatin1("table")); writer.writeEmptyElement(styleNS, QString::fromLatin1("table-properties")); + if (m_tableFormatsWithBorders.contains(formatIndex)) { + // write border format collapsing to table style + writer.writeAttribute(tableNS, QString::fromLatin1("border-model"), + QString::fromLatin1("collapsing")); + } + const char* align = nullptr; + switch (format.alignment()) { + case Qt::AlignLeft: + align = "left"; + break; + case Qt::AlignRight: + align = "right"; + break; + case Qt::AlignHCenter: + align = "center"; + break; + case Qt::AlignJustify: + align = "margins"; + break; + } + if (align) + writer.writeAttribute(tableNS, QString::fromLatin1("align"), QString::fromLatin1(align)); + if (format.width().rawValue()) { + writer.writeAttribute(styleNS, QString::fromLatin1("width"), + QString::number(format.width().rawValue()) + QLatin1String("pt")); + } + writer.writeEndElement(); + // start writing table-column style element + if (format.columnWidthConstraints().size()) { + // write table-column-properties for columns with constraints + m_tableFormatsWithColWidthConstraints.insert(formatIndex); // needed for linking of columns to styles + for (int colit = 0; colit < format.columnWidthConstraints().size(); ++colit) { + writer.writeStartElement(styleNS, QString::fromLatin1("style")); + writer.writeAttribute(styleNS, QString::fromLatin1("name"), + QString::fromLatin1("Table%1.%2").arg(formatIndex).arg(colit)); + writer.writeAttribute(styleNS, QString::fromLatin1("family"), QString::fromLatin1("table-column")); + writer.writeEmptyElement(styleNS, QString::fromLatin1("table-column-properties")); + QString columnWidth; + if (format.columnWidthConstraints().at(colit).type() == QTextLength::PercentageLength) { + columnWidth = QString::number(format.columnWidthConstraints().at(colit).rawValue()) + + QLatin1String("%"); + } else if (format.columnWidthConstraints().at(colit).type() == QTextLength::FixedLength) { + columnWidth = QString::number(format.columnWidthConstraints().at(colit).rawValue()) + + QLatin1String("pt"); + } else { + //!! HARD-CODING variableWidth Constraints to 100% / nr constraints + columnWidth = QString::number(100 / format.columnWidthConstraints().size()) + + QLatin1String("%"); + } + writer.writeAttribute(styleNS, QString::fromLatin1("column-width"), columnWidth); + writer.writeEndElement(); + } + } +} +void QTextOdfWriter::writeTableCellFormat(QXmlStreamWriter &writer, QTextTableCellFormat format, + int formatIndex, QVector &styles) const +{ + // check for all table cells here if they are in a table with border + if (m_cellFormatsInTablesWithBorders.contains(formatIndex)) { + const QVector tableIdVector = m_cellFormatsInTablesWithBorders.value(formatIndex); + for (const auto &tableId : tableIdVector) { + const auto &tmpStyle = styles.at(tableId); + if (tmpStyle.isTableFormat()) { + QTextTableFormat tableFormatTmp = tmpStyle.toTableFormat(); + tableCellStyleElement(writer, formatIndex, format, true, tableId, tableFormatTmp); + } else { + qDebug("QTextOdfWriter::writeTableCellFormat: ERROR writing table border format"); + } + } + } + tableCellStyleElement(writer, formatIndex, format, false); +} + +void QTextOdfWriter::tableCellStyleElement(QXmlStreamWriter &writer, const int &formatIndex, + const QTextTableCellFormat &format, + bool hasBorder, int tableId, + const QTextTableFormat tableFormatTmp) const { + writer.writeStartElement(styleNS, QString::fromLatin1("style")); + if (hasBorder) { + writer.writeAttribute(styleNS, QString::fromLatin1("name"), + QString::fromLatin1("TB%1.%2").arg(tableId).arg(formatIndex)); + } else { + writer.writeAttribute(styleNS, QString::fromLatin1("name"), QString::fromLatin1("T%1").arg(formatIndex)); + } + writer.writeAttribute(styleNS, QString::fromLatin1("family"), QString::fromLatin1("table-cell")); + writer.writeEmptyElement(styleNS, QString::fromLatin1("table-cell-properties")); + if (hasBorder) { + writer.writeAttribute(foNS, QString::fromLatin1("border"), + pixelToPoint(tableFormatTmp.border()) + QLatin1String(" ") + + borderStyleName(tableFormatTmp.borderStyle()) + + QLatin1String(" #000000")); //!! HARD-CODING color black + } qreal padding = format.topPadding(); if (padding > 0 && padding == format.bottomPadding() && padding == format.leftPadding() && padding == format.rightPadding()) { @@ -710,16 +891,19 @@ void QTextOdfWriter::writeTableCellFormat(QXmlStreamWriter &writer, QTextTableCe if (padding > 0) writer.writeAttribute(foNS, QString::fromLatin1("padding-top"), pixelToPoint(padding)); if (format.bottomPadding() > 0) - writer.writeAttribute(foNS, QString::fromLatin1("padding-bottom"), pixelToPoint(format.bottomPadding())); + writer.writeAttribute(foNS, QString::fromLatin1("padding-bottom"), + pixelToPoint(format.bottomPadding())); if (format.leftPadding() > 0) - writer.writeAttribute(foNS, QString::fromLatin1("padding-left"), pixelToPoint(format.leftPadding())); + writer.writeAttribute(foNS, QString::fromLatin1("padding-left"), + pixelToPoint(format.leftPadding())); if (format.rightPadding() > 0) - writer.writeAttribute(foNS, QString::fromLatin1("padding-right"), pixelToPoint(format.rightPadding())); + writer.writeAttribute(foNS, QString::fromLatin1("padding-right"), + pixelToPoint(format.rightPadding())); } if (format.hasProperty(QTextFormat::TextVerticalAlignment)) { QString pos; - switch (format.verticalAlignment()) { + switch (format.verticalAlignment()) { // TODO - review: doesn't handle all cases case QTextCharFormat::AlignMiddle: pos = QString::fromLatin1("middle"); break; case QTextCharFormat::AlignTop: @@ -736,8 +920,6 @@ void QTextOdfWriter::writeTableCellFormat(QXmlStreamWriter &writer, QTextTableCe // ODF just search for style-table-cell-properties-attlist) // QTextFormat::BackgroundImageUrl // format.background - // QTextFormat::FrameBorder - writer.writeEndElement(); // style } @@ -815,8 +997,28 @@ bool QTextOdfWriter::writeAll() const QList copy = formats.toList(); for (auto index : copy) { QTextObject *object = m_document->objectForFormat(allFormats[index]); - if (object) + if (object) { formats << object->formatIndex(); + if (auto *tableobject = qobject_cast(object)) { + if (tableobject->format().borderStyle()) { + int tableID = tableobject->formatIndex(); + m_tableFormatsWithBorders.insert(tableID); + // loop through all rows and cols of table and store cell IDs, + // create Hash with cell ID as Key and table IDs as Vector + for (int rowindex = 0; rowindex < tableobject->rows(); ++rowindex) { + for (int colindex = 0; colindex < tableobject->columns(); ++colindex) { + const int cellFormatID = tableobject->cellAt(rowindex, colindex).tableCellFormatIndex(); + QVector tableIdsTmp; + if (m_cellFormatsInTablesWithBorders.contains(cellFormatID)) + tableIdsTmp = m_cellFormatsInTablesWithBorders.value(cellFormatID); + if (!tableIdsTmp.contains(tableID)) + tableIdsTmp.append(tableID); + m_cellFormatsInTablesWithBorders.insert(cellFormatID, tableIdsTmp); + } + } + } + } + } } writeFormats(writer, formats); diff --git a/src/gui/text/qtextodfwriter_p.h b/src/gui/text/qtextodfwriter_p.h index d0dd7d2b5c..98a6fdfa96 100644 --- a/src/gui/text/qtextodfwriter_p.h +++ b/src/gui/text/qtextodfwriter_p.h @@ -56,8 +56,10 @@ // #include +#include #include #include +#include #include "qtextdocument_p.h" #include "qtextdocumentwriter.h" @@ -94,11 +96,21 @@ public: void writeCharacterFormat(QXmlStreamWriter &writer, QTextCharFormat format, int formatIndex) const; void writeListFormat(QXmlStreamWriter &writer, QTextListFormat format, int formatIndex) const; void writeFrameFormat(QXmlStreamWriter &writer, QTextFrameFormat format, int formatIndex) const; - void writeTableCellFormat(QXmlStreamWriter &writer, QTextTableCellFormat format, int formatIndex) const; + void writeTableFormat(QXmlStreamWriter &writer, QTextTableFormat format, int formatIndex) const; + void writeTableCellFormat(QXmlStreamWriter &writer, QTextTableCellFormat format, int formatIndex, + QVector &styles) const; void writeFrame(QXmlStreamWriter &writer, const QTextFrame *frame); void writeInlineCharacter(QXmlStreamWriter &writer, const QTextFragment &fragment) const; const QString officeNS, textNS, styleNS, foNS, tableNS, drawNS, xlinkNS, svgNS; + const int defaultImageResolution = 11811; // 11811 dots per meter = (about) 300 dpi + +protected: + void tableCellStyleElement(QXmlStreamWriter &writer, const int &formatIndex, + const QTextTableCellFormat &format, + bool hasBorder, int tableId = 0, + const QTextTableFormat tableFormatTmp = QTextTableFormat()) const; + private: const QTextDocument *m_document; QIODevice *m_device; @@ -108,6 +120,10 @@ private: bool m_createArchive; QStack m_listStack; + + QHash> m_cellFormatsInTablesWithBorders; + QSet m_tableFormatsWithBorders; + mutable QSet m_tableFormatsWithColWidthConstraints; }; QT_END_NAMESPACE diff --git a/tests/auto/gui/text/qtextodfwriter/tst_qtextodfwriter.cpp b/tests/auto/gui/text/qtextodfwriter/tst_qtextodfwriter.cpp index fb92b5a20f..bbb90ec82a 100644 --- a/tests/auto/gui/text/qtextodfwriter/tst_qtextodfwriter.cpp +++ b/tests/auto/gui/text/qtextodfwriter/tst_qtextodfwriter.cpp @@ -131,7 +131,7 @@ void tst_QTextOdfWriter::testWriteParagraph_data() QTest::newRow("misc2") << "\t \tFoo" << " Foo"; QTest::newRow("linefeed") << (QStringLiteral("line1") + QChar(0x2028) + QStringLiteral("line2")) << - "line1line2"; + "line1line2"; QTest::newRow("spaces") << "The quick brown fox jumped over the lazy dog" << "The quick brown fox jumped over the lazy dog"; } @@ -378,7 +378,7 @@ void tst_QTextOdfWriter::testWriteTable() odfWriter->writeFrame(*xmlWriter, document->rootFrame()); QString xml = QString::fromLatin1( "" - "" + "" "" "" ""