181void QTextOdfWriter::writeFrame(QXmlStreamWriter &writer,
const QTextFrame *frame)
184 const QTextTable *table = qobject_cast<
const QTextTable*> (frame);
187 writer.writeStartElement(tableNS, u"table");
188 writer.writeAttribute(tableNS, u"style-name",
189 QString::fromLatin1(
"Table%1").arg(table->formatIndex()));
191 if (m_tableFormatsWithColWidthConstraints.contains(table->formatIndex())) {
192 for (
int colit = 0; colit < table->columns(); ++colit) {
193 writer.writeStartElement(tableNS, u"table-column");
194 writer.writeAttribute(tableNS, u"style-name",
195 QString::fromLatin1(
"Table%1.%2").arg(table->formatIndex()).arg(colit));
196 writer.writeEndElement();
199 writer.writeEmptyElement(tableNS, u"table-column");
200 writer.writeAttribute(tableNS, u"number-columns-repeated",
201 QString::number(table->columns()));
203 }
else if (frame->document() && frame->document()->rootFrame() != frame) {
204 writer.writeStartElement(textNS, u"section");
207 QTextFrame::iterator iterator = frame->begin();
208 QTextFrame *child =
nullptr;
211 while (! iterator.atEnd()) {
212 if (iterator.currentFrame() && child != iterator.currentFrame())
213 writeFrame(writer, iterator.currentFrame());
215 QTextBlock block = iterator.currentBlock();
217 QTextTableCell cell = table->cellAt(block.position());
218 if (tableRow < cell.row()) {
220 writer.writeEndElement();
221 tableRow = cell.row();
222 writer.writeStartElement(tableNS, u"table-row");
224 writer.writeStartElement(tableNS, u"table-cell");
225 if (cell.columnSpan() > 1) {
226 writer.writeAttribute(tableNS, u"number-columns-spanned",
227 QString::number(cell.columnSpan()));
229 if (cell.rowSpan() > 1) {
230 writer.writeAttribute(tableNS, u"number-rows-spanned",
231 QString::number(cell.rowSpan()));
233 if (cell.format().isTableCellFormat()) {
234 writer.writeAttribute(tableNS, u"style-name",
235 QString::fromLatin1(
"T%1").arg(cell.tableCellFormatIndex()));
238 writeBlock(writer, block);
240 writer.writeEndElement();
242 child = iterator.currentFrame();
246 writer.writeEndElement();
248 if (table || (frame->document() && frame->document()->rootFrame() != frame))
249 writer.writeEndElement();
252void QTextOdfWriter::writeBlock(QXmlStreamWriter &writer,
const QTextBlock &block)
254 if (block.textList()) {
255 const int listLevel = block.textList()->format().indent();
256 if (m_listStack.isEmpty() || m_listStack.top() != block.textList()) {
258 while (m_listStack.size() >= listLevel && !m_listStack.isEmpty() && m_listStack.top() != block.textList() ) {
260 writer.writeEndElement();
261 if (m_listStack.size())
262 writer.writeEndElement();
264 while (m_listStack.size() < listLevel) {
265 if (m_listStack.size())
266 writer.writeStartElement(textNS, u"list-item");
267 writer.writeStartElement(textNS, u"list");
268 if (m_listStack.size() == listLevel - 1) {
269 m_listStack.push(block.textList());
270 writer.writeAttribute(
271 textNS, u"style-name",
272 QString::fromLatin1(
"L%1").arg(block.textList()->formatIndex()));
275 m_listStack.push(
nullptr);
279 writer.writeStartElement(textNS, u"list-item");
282 while (! m_listStack.isEmpty()) {
284 writer.writeEndElement();
285 if (m_listStack.size())
286 writer.writeEndElement();
290 if (block.length() == 1) {
291 writer.writeEmptyElement(textNS, u"p");
292 writer.writeAttribute(textNS, u"style-name",
293 QString::fromLatin1(
"p%1").arg(block.blockFormatIndex()));
294 if (block.textList())
295 writer.writeEndElement();
298 writer.writeStartElement(textNS, u"p");
299 writer.writeAttribute(textNS, u"style-name",
300 QString::fromLatin1(
"p%1").arg(block.blockFormatIndex()));
301 for (QTextBlock::Iterator frag = block.begin(); !frag.atEnd(); ++frag) {
302 bool isHyperlink = frag.fragment().charFormat().hasProperty(QTextFormat::AnchorHref);
304 QString value = frag.fragment().charFormat().property(QTextFormat::AnchorHref).toString();
305 writer.writeStartElement(textNS, u"a");
306 writer.writeAttribute(xlinkNS, u"href", value);
308 writer.writeCharacters(QString());
309 writer.writeStartElement(textNS, u"span");
311 QString fragmentText = frag.fragment().text();
312 if (fragmentText.size() == 1 && fragmentText[0] == u'\xFFFC') {
313 writeInlineCharacter(writer, frag.fragment());
314 writer.writeEndElement();
318 writer.writeAttribute(textNS, u"style-name",
319 QString::fromLatin1(
"c%1").arg(frag.fragment().charFormatIndex()));
320 bool escapeNextSpace =
true;
321 int precedingSpaces = 0;
322 int exportedIndex = 0;
323 for (
int i=0; i <= fragmentText.size(); ++i) {
324 QChar character = (i == fragmentText.size() ? QChar() : fragmentText.at(i));
325 bool isSpace = character.unicode() ==
' ';
328 if (!isSpace && escapeNextSpace && precedingSpaces > 1) {
329 const bool startParag = exportedIndex == 0 && i == precedingSpaces;
331 writer.writeCharacters(fragmentText.mid(exportedIndex, i - precedingSpaces + 1 - exportedIndex));
332 writer.writeEmptyElement(textNS, u"s");
333 const int count = precedingSpaces - (startParag?0:1);
335 writer.writeAttribute(textNS, u"c", QString::number(count));
340 if (i < fragmentText.size()) {
341 if (character.unicode() == 0x2028) {
343 writer.writeCharacters(fragmentText.mid(exportedIndex, i - exportedIndex));
346 writer.writeEmptyElement(textNS, u"tab");
347 writer.writeEmptyElement(textNS, u"line-break");
350 }
else if (character.unicode() ==
'\t') {
352 writer.writeCharacters(fragmentText.mid(exportedIndex, i - exportedIndex));
353 writer.writeEmptyElement(textNS, u"tab");
356 }
else if (isSpace) {
358 escapeNextSpace =
true;
359 }
else if (!isSpace) {
365 writer.writeCharacters(fragmentText.mid(exportedIndex));
366 writer.writeEndElement();
367 writer.writeCharacters(QString());
369 writer.writeEndElement();
371 writer.writeCharacters(QString());
372 writer.writeEndElement();
373 if (block.textList())
374 writer.writeEndElement();
400void QTextOdfWriter::writeInlineCharacter(QXmlStreamWriter &writer,
const QTextFragment &fragment)
const
402 writer.writeStartElement(drawNS, u"frame");
403 if (m_strategy ==
nullptr) {
406 else if (fragment.charFormat().isImageFormat()) {
407 QTextImageFormat imageFormat = fragment.charFormat().toImageFormat();
408 writer.writeAttribute(drawNS, u"name", imageFormat.name());
416 QString name = imageFormat.name();
417 if (name.startsWith(
":/"_L1))
418 name.prepend(
"qrc"_L1);
419 QUrl url = QUrl(name);
420 const QVariant variant = m_document->resource(QTextDocument::ImageResource, url);
421 if (variant.userType() == QMetaType::QPixmap || variant.userType() == QMetaType::QImage) {
422 image = qvariant_cast<QImage>(variant);
423 }
else if (variant.userType() == QMetaType::QByteArray) {
424 data = variant.toByteArray();
426 QBuffer buffer(&data);
427 buffer.open(QIODevice::ReadOnly);
428 probeImageData(&buffer, &image, &mimeType, &width, &height);
431 QFile file(imageFormat.name());
432 if (file.open(QIODevice::ReadOnly) && !probeImageData(&file, &image, &mimeType, &width, &height)) {
434 data = file.readAll();
438 if (! image.isNull()) {
441 int imgQuality = imageFormat.quality();
442 if (imgQuality >= 100 || imgQuality <= 0 || image.hasAlphaChannel()) {
443 QImageWriter imageWriter(&imageBytes,
"png");
444 imageWriter.write(image);
446 data = imageBytes.data();
447 mimeType = u"image/png"_s;
450 QImageWriter imageWriter(&imageBytes,
"jpg");
451 imageWriter.setQuality(imgQuality);
452 imageWriter.write(image);
454 data = imageBytes.data();
455 mimeType = u"image/jpg"_s;
458 width = image.width();
459 height = image.height();
462 if (!data.isEmpty()) {
463 if (imageFormat.hasProperty(QTextFormat::ImageWidth)) {
464 width = imageFormat.width();
466 if (imageFormat.hasProperty(QTextFormat::ImageHeight)) {
467 height = imageFormat.height();
470 QString filename = m_strategy->createUniqueImageName();
472 m_strategy->addFile(filename, mimeType, data);
474 writer.writeAttribute(svgNS, u"width", pixelToPoint(width));
475 writer.writeAttribute(svgNS, u"height", pixelToPoint(height));
476 writer.writeAttribute(textNS, u"anchor-type", u"as-char");
477 writer.writeStartElement(drawNS, u"image");
478 writer.writeAttribute(xlinkNS, u"href", filename);
479 writer.writeEndElement();
482 writer.writeEndElement();
485void QTextOdfWriter::writeFormats(QXmlStreamWriter &writer,
const QSet<
int> &formats)
const
487 writer.writeStartElement(officeNS, u"automatic-styles");
488 QList<QTextFormat> allStyles = m_document->allFormats();
489 for (
int formatIndex : formats) {
490 QTextFormat textFormat = allStyles.at(formatIndex);
491 switch (textFormat.type()) {
492 case QTextFormat::CharFormat:
493 if (textFormat.isTableCellFormat())
494 writeTableCellFormat(writer, textFormat.toTableCellFormat(), formatIndex, allStyles);
496 writeCharacterFormat(writer, textFormat.toCharFormat(), formatIndex);
498 case QTextFormat::BlockFormat:
499 writeBlockFormat(writer, textFormat.toBlockFormat(), formatIndex);
501 case QTextFormat::ListFormat:
502 writeListFormat(writer, textFormat.toListFormat(), formatIndex);
504 case QTextFormat::FrameFormat:
505 if (textFormat.isTableFormat())
506 writeTableFormat(writer, textFormat.toTableFormat(), formatIndex);
508 writeFrameFormat(writer, textFormat.toFrameFormat(), formatIndex);
513 writer.writeEndElement();
516void QTextOdfWriter::writeBlockFormat(QXmlStreamWriter &writer,
const QTextBlockFormat &format,
int formatIndex)
const
518 writer.writeStartElement(styleNS, u"style");
519 writer.writeAttribute(styleNS, u"name", QString::fromLatin1(
"p%1").arg(formatIndex));
520 writer.writeAttribute(styleNS, u"family", u"paragraph");
521 writer.writeStartElement(styleNS, u"paragraph-properties");
523 if (format.hasProperty(QTextBlockFormat::LineHeightType)) {
524 const int blockLineHeightType = format.lineHeightType();
525 const qreal blockLineHeight = format.lineHeight();
528 switch (blockLineHeightType) {
529 case QTextBlockFormat::SingleHeight:
530 type = u"line-height";
533 case QTextBlockFormat::ProportionalHeight:
534 type = u"line-height";
535 value = QString::number(blockLineHeight) + u"%";
537 case QTextBlockFormat::FixedHeight:
538 type = u"line-height";
539 value = pixelToPoint(qMax(qreal(0.), blockLineHeight));
541 case QTextBlockFormat::MinimumHeight:
542 type = u"line-height-at-least";
543 value = pixelToPoint(qMax(qreal(0.), blockLineHeight));
545 case QTextBlockFormat::LineDistanceHeight:
546 type = u"line-spacing";
547 value = pixelToPoint(qMax(qreal(0.), blockLineHeight));
551 writer.writeAttribute(styleNS, type, value);
554 if (format.hasProperty(QTextFormat::BlockAlignment)) {
555 const Qt::Alignment alignment = format.alignment() & Qt::AlignHorizontal_Mask;
558 case Qt::AlignLeading:
561 case Qt::AlignTrailing:
564 case Qt::AlignLeft | Qt::AlignAbsolute:
567 case Qt::AlignRight | Qt::AlignAbsolute:
570 case Qt::AlignHCenter:
573 case Qt::AlignJustify:
577 qWarning() <<
"QTextOdfWriter: unsupported paragraph alignment; " << format.alignment();
581 writer.writeAttribute(foNS, u"text-align", value);
584 if (format.hasProperty(QTextFormat::BlockTopMargin))
585 writer.writeAttribute(foNS, u"margin-top", pixelToPoint(qMax(qreal(0.), format.topMargin())) );
586 if (format.hasProperty(QTextFormat::BlockBottomMargin))
587 writer.writeAttribute(foNS, u"margin-bottom", pixelToPoint(qMax(qreal(0.), format.bottomMargin())) );
588 if (format.hasProperty(QTextFormat::BlockLeftMargin) || format.hasProperty(QTextFormat::BlockIndent))
589 writer.writeAttribute(foNS, u"margin-left", pixelToPoint(qMax(qreal(0.),
590 format.leftMargin() + format.indent())));
591 if (format.hasProperty(QTextFormat::BlockRightMargin))
592 writer.writeAttribute(foNS, u"margin-right", pixelToPoint(qMax(qreal(0.), format.rightMargin())) );
593 if (format.hasProperty(QTextFormat::TextIndent))
594 writer.writeAttribute(foNS, u"text-indent", pixelToPoint(format.textIndent()));
595 if (format.hasProperty(QTextFormat::PageBreakPolicy)) {
596 if (format.pageBreakPolicy() & QTextFormat::PageBreak_AlwaysBefore)
597 writer.writeAttribute(foNS, u"break-before", u"page");
598 if (format.pageBreakPolicy() & QTextFormat::PageBreak_AlwaysAfter)
599 writer.writeAttribute(foNS, u"break-after", u"page");
601 if (format.hasProperty(QTextFormat::BackgroundBrush)) {
602 QBrush brush = format.background();
603 writer.writeAttribute(foNS, u"background-color", brush.color().name());
605 if (format.hasProperty(QTextFormat::BlockNonBreakableLines))
606 writer.writeAttribute(foNS, u"keep-together",
607 format.nonBreakableLines() ? u"true" : u"false");
608 if (format.hasProperty(QTextFormat::TabPositions)) {
609 const QList<QTextOption::Tab> tabs = format.tabPositions();
610 writer.writeStartElement(styleNS, u"tab-stops");
611 for (
const auto &tab : tabs) {
612 writer.writeEmptyElement(styleNS, u"tab-stop");
613 writer.writeAttribute(styleNS, u"position", pixelToPoint(tab.position) );
616 case QTextOption::DelimiterTab: type = u"char";
break;
617 case QTextOption::LeftTab: type = u"left";
break;
618 case QTextOption::RightTab: type = u"right";
break;
619 case QTextOption::CenterTab: type = u"center";
break;
622 writer.writeAttribute(styleNS, u"type", type);
623 if (!tab.delimiter.isNull())
624 writer.writeAttribute(styleNS, u"char", tab.delimiter);
627 writer.writeEndElement();
630 writer.writeEndElement();
631 writer.writeEndElement();
634void QTextOdfWriter::writeCharacterFormat(QXmlStreamWriter &writer,
const QTextCharFormat &format,
int formatIndex)
const
636 writer.writeStartElement(styleNS, u"style");
637 writer.writeAttribute(styleNS, u"name", QString::fromLatin1(
"c%1").arg(formatIndex));
638 writer.writeAttribute(styleNS, u"family", u"text");
639 writer.writeEmptyElement(styleNS, u"text-properties");
641 const QFont defaultFont = m_document->defaultFont();
642 const uint defaultFontResolveMask = defaultFont.resolveMask();
644 if (format.hasProperty(QTextFormat::FontItalic)
645 || (defaultFontResolveMask & QFont::StyleResolved)) {
646 const bool italic = format.hasProperty(QTextFormat::FontItalic) ? format.fontItalic() : defaultFont.italic();
648 writer.writeAttribute(foNS, u"font-style", u"italic");
651 if (format.hasProperty(QTextFormat::FontWeight)
652 || (defaultFontResolveMask & QFont::WeightResolved)) {
653 int weight = format.hasProperty(QTextFormat::FontWeight)
654 ? format.fontWeight()
655 : defaultFont.weight();
657 if (weight != QFont::Normal) {
658 const QString value = weight == QFont::Bold ? u"bold"_s : QString::number(weight);
659 writer.writeAttribute(foNS, u"font-weight", value);
663 if (format.hasProperty(QTextFormat::OldFontFamily)
664 || format.hasProperty(QTextFormat::FontFamilies)
665 || (defaultFontResolveMask & QFont::FamiliesResolved)) {
666 const QString fontFamily = (format.hasProperty(QTextFormat::OldFontFamily)
667 || format.hasProperty(QTextFormat::FontFamilies))
668 ? format.fontFamilies().toStringList().value(0, QString())
669 : defaultFont.family();
670 writer.writeAttribute(foNS, u"font-family", fontFamily);
672 writer.writeAttribute(foNS, u"font-family", u"Sans");
675 if (format.hasProperty(QTextFormat::FontPointSize)
676 || (defaultFontResolveMask & QFont::SizeResolved)) {
677 const qreal pointSize = format.hasProperty(QTextFormat::FontPointSize)
678 ? format.fontPointSize()
679 : defaultFont.pointSizeF();
680 writer.writeAttribute(foNS, u"font-size", QString::fromLatin1(
"%1pt").arg(pointSize));
683 if (format.hasProperty(QTextFormat::FontCapitalization)
684 || (defaultFontResolveMask & QFont::CapitalizationResolved)) {
685 QFont::Capitalization capitalization = format.hasProperty(QTextFormat::FontCapitalization)
686 ? format.fontCapitalization()
687 : defaultFont.capitalization();
688 switch(capitalization) {
689 case QFont::MixedCase:
690 writer.writeAttribute(foNS, u"text-transform", u"none");
break;
691 case QFont::AllUppercase:
692 writer.writeAttribute(foNS, u"text-transform", u"uppercase");
break;
693 case QFont::AllLowercase:
694 writer.writeAttribute(foNS, u"text-transform", u"lowercase");
break;
695 case QFont::Capitalize:
696 writer.writeAttribute(foNS, u"text-transform", u"capitalize");
break;
697 case QFont::SmallCaps:
698 writer.writeAttribute(foNS, u"font-variant", u"small-caps");
break;
702 if (format.hasProperty(QTextFormat::FontLetterSpacing) ||
703 (defaultFontResolveMask & QFont::LetterSpacingResolved)) {
704 const qreal letterSpacing = format.hasProperty(QTextFormat::FontLetterSpacing)
705 ? format.fontLetterSpacing()
706 : defaultFont.letterSpacing();
707 writer.writeAttribute(foNS, u"letter-spacing", pixelToPoint(letterSpacing));
710 if (format.hasProperty(QTextFormat::FontWordSpacing)
711 || (defaultFontResolveMask & QFont::WordSpacingResolved)) {
712 const qreal wordSpacing = format.hasProperty(QTextFormat::FontWordSpacing)
713 ? format.fontWordSpacing()
714 : defaultFont.wordSpacing();
715 if (wordSpacing != 0)
716 writer.writeAttribute(foNS, u"word-spacing", pixelToPoint(wordSpacing));
719 if (format.hasProperty(QTextFormat::FontUnderline)
720 || ((defaultFontResolveMask & QFont::UnderlineResolved)
721 && !format.hasProperty(QTextFormat::TextUnderlineStyle))) {
722 const bool underline = format.hasProperty(QTextFormat::FontUnderline)
723 ? format.fontUnderline()
724 : defaultFont.underline();
725 writer.writeAttribute(styleNS, u"text-underline-type", underline ? u"single" : u"none");
728 if (format.hasProperty(QTextFormat::FontOverline)) {
732 if (format.hasProperty(QTextFormat::FontStrikeOut)
733 || (defaultFontResolveMask & QFont::StrikeOutResolved)) {
734 const bool strikeOut = format.hasProperty(QTextFormat::FontStrikeOut)
735 ? format.fontStrikeOut()
736 : defaultFont.strikeOut();
737 writer.writeAttribute(styleNS, u"text-line-through-type", strikeOut ? u"single" : u"none");
740 if (format.hasProperty(QTextFormat::TextUnderlineColor))
741 writer.writeAttribute(styleNS, u"text-underline-color", format.underlineColor().name());
742 if (format.hasProperty(QTextFormat::FontFixedPitch)) {
745 if (format.hasProperty(QTextFormat::TextUnderlineStyle)) {
747 switch (format.underlineStyle()) {
748 case QTextCharFormat::NoUnderline: value = u"none";
break;
749 case QTextCharFormat::SingleUnderline: value = u"solid";
break;
750 case QTextCharFormat::DashUnderline: value = u"dash";
break;
751 case QTextCharFormat::DotLine: value = u"dotted";
break;
752 case QTextCharFormat::DashDotLine: value = u"dash-dot";
break;
753 case QTextCharFormat::DashDotDotLine: value = u"dot-dot-dash";
break;
754 case QTextCharFormat::WaveUnderline: value = u"wave";
break;
755 case QTextCharFormat::SpellCheckUnderline: value = u"none";
break;
757 if (!value.isEmpty())
758 writer.writeAttribute(styleNS, u"text-underline-style", value);
760 if (format.hasProperty(QTextFormat::TextVerticalAlignment)) {
762 switch (format.verticalAlignment()) {
763 case QTextCharFormat::AlignMiddle:
764 case QTextCharFormat::AlignNormal: value = u"0%";
break;
765 case QTextCharFormat::AlignSuperScript: value = u"super";
break;
766 case QTextCharFormat::AlignSubScript: value = u"sub";
break;
767 case QTextCharFormat::AlignTop: value = u"100%";
break;
768 case QTextCharFormat::AlignBottom : value = u"-100%";
break;
769 case QTextCharFormat::AlignBaseline:
break;
771 if (!value.isEmpty())
772 writer.writeAttribute(styleNS, u"text-position", value);
774 if (format.hasProperty(QTextFormat::TextOutline))
775 writer.writeAttribute(styleNS, u"text-outline", u"true");
776 if (format.hasProperty(QTextFormat::TextToolTip)) {
779 if (format.hasProperty(QTextFormat::IsAnchor)) {
782 if (format.hasProperty(QTextFormat::AnchorHref)) {
785 if (format.hasProperty(QTextFormat::AnchorName)) {
788 if (format.hasProperty(QTextFormat::ForegroundBrush)) {
789 QBrush brush = format.foreground();
790 writer.writeAttribute(foNS, u"color", brush.color().name());
792 if (format.hasProperty(QTextFormat::BackgroundBrush)) {
793 QBrush brush = format.background();
794 writer.writeAttribute(foNS, u"background-color", brush.color().name());
797 writer.writeEndElement();
800void QTextOdfWriter::writeListFormat(QXmlStreamWriter &writer,
const QTextListFormat &format,
int formatIndex)
const
802 writer.writeStartElement(textNS, u"list-style");
803 writer.writeAttribute(styleNS, u"name", QString::fromLatin1(
"L%1").arg(formatIndex));
805 QTextListFormat::Style style = format.style();
806 if (style == QTextListFormat::ListDecimal || style == QTextListFormat::ListLowerAlpha
807 || style == QTextListFormat::ListUpperAlpha
808 || style == QTextListFormat::ListLowerRoman
809 || style == QTextListFormat::ListUpperRoman) {
810 writer.writeStartElement(textNS, u"list-level-style-number");
811 writer.writeAttribute(styleNS, u"num-format", bulletChar(style));
813 if (format.hasProperty(QTextFormat::ListNumberSuffix))
814 writer.writeAttribute(styleNS, u"num-suffix", format.numberSuffix());
816 writer.writeAttribute(styleNS, u"num-suffix", u".");
818 if (format.hasProperty(QTextFormat::ListNumberPrefix))
819 writer.writeAttribute(styleNS, u"num-prefix", format.numberPrefix());
822 writer.writeStartElement(textNS, u"list-level-style-bullet");
823 writer.writeAttribute(textNS, u"bullet-char", bulletChar(style));
826 writer.writeAttribute(textNS, u"level", QString::number(format.indent()));
827 writer.writeEmptyElement(styleNS, u"list-level-properties");
828 writer.writeAttribute(foNS, u"text-align", u"start");
829 QString spacing = QString::fromLatin1(
"%1mm").arg(format.indent() * 8);
830 writer.writeAttribute(textNS, u"space-before", spacing);
833 writer.writeEndElement();
834 writer.writeEndElement();
837void QTextOdfWriter::writeFrameFormat(QXmlStreamWriter &writer,
const QTextFrameFormat &format,
int formatIndex)
const
839 writer.writeStartElement(styleNS, u"style");
840 writer.writeAttribute(styleNS, u"name", QString::fromLatin1(
"s%1").arg(formatIndex));
841 writer.writeAttribute(styleNS, u"family", u"section");
842 writer.writeEmptyElement(styleNS, u"section-properties");
843 if (format.hasProperty(QTextFormat::FrameTopMargin))
844 writer.writeAttribute(foNS, u"margin-top", pixelToPoint(qMax(qreal(0.), format.topMargin())) );
845 if (format.hasProperty(QTextFormat::FrameBottomMargin))
846 writer.writeAttribute(foNS, u"margin-bottom", pixelToPoint(qMax(qreal(0.), format.bottomMargin())) );
847 if (format.hasProperty(QTextFormat::FrameLeftMargin))
848 writer.writeAttribute(foNS, u"margin-left", pixelToPoint(qMax(qreal(0.), format.leftMargin())) );
849 if (format.hasProperty(QTextFormat::FrameRightMargin))
850 writer.writeAttribute(foNS, u"margin-right", pixelToPoint(qMax(qreal(0.), format.rightMargin())) );
852 writer.writeEndElement();
865void QTextOdfWriter::writeTableFormat(QXmlStreamWriter &writer,
const QTextTableFormat &format,
int formatIndex)
const
868 writer.writeStartElement(styleNS, u"style");
869 writer.writeAttribute(styleNS, u"name", QString::fromLatin1(
"Table%1").arg(formatIndex));
870 writer.writeAttribute(styleNS, u"family", u"table");
871 writer.writeEmptyElement(styleNS, u"table-properties");
873 if (m_tableFormatsWithBorders.contains(formatIndex))
874 writer.writeAttribute(tableNS, u"border-model",
875 format.borderCollapse() ? u"collapsing" : u"separating");
878 switch (format.alignment()) {
885 case Qt::AlignHCenter:
888 case Qt::AlignJustify:
893 writer.writeAttribute(tableNS, u"align", align);
894 if (format.width().rawValue()) {
895 writer.writeAttribute(styleNS, u"width",
896 QString::number(format.width().rawValue()) +
"pt"_L1);
898 writer.writeEndElement();
900 const auto &columnWidthConstraints = format.columnWidthConstraints();
901 if (!columnWidthConstraints.empty()) {
903 m_tableFormatsWithColWidthConstraints.insert(formatIndex);
904 for (qsizetype colit = 0; colit < columnWidthConstraints.size(); ++colit) {
905 const auto &constraint = columnWidthConstraints.at(colit);
906 writer.writeStartElement(styleNS, u"style");
907 writer.writeAttribute(styleNS, u"name",
908 QString::fromLatin1(
"Table%1.%2").arg(formatIndex).arg(colit));
909 writer.writeAttribute(styleNS, u"family", u"table-column");
910 writer.writeEmptyElement(styleNS, u"table-column-properties");
911 QByteArray columnWidth;
912 if (constraint.type() == QTextLength::PercentageLength) {
913 columnWidth = QByteArray::number(constraint.rawValue()) +
"%";
914 }
else if (constraint.type() == QTextLength::FixedLength) {
915 columnWidth = QByteArray::number(constraint.rawValue()) +
"pt";
918 columnWidth = QByteArray::number(100 / columnWidthConstraints.size()) +
"%";
920 writer.writeAttribute(styleNS, u"column-width", columnWidth);
921 writer.writeEndElement();
926QTextTableCellFormat QTextOdfWriter::mergeFormats(
const QTextTableCellFormat &cell,
927 const QTextTableFormat &table)
929 QTextTableCellFormat ret(cell);
930 const auto tryCopyProperties =
931 [&](
const QVariant &value,
932 const std::initializer_list<QTextFormat::Property> &properties) {
935 for (
const auto prop : properties) {
936 if (ret.property(prop).isNull())
937 ret.setProperty(prop, value);
940 const auto &paddingVal = table.property(QTextFormat::TableCellPadding);
941 if (!paddingVal.isNull()) {
943 ret.setLeftPadding(ret.leftPadding() + table.cellPadding());
944 ret.setRightPadding(ret.rightPadding() + table.cellPadding());
945 ret.setTopPadding(ret.topPadding() + table.cellPadding());
946 ret.setBottomPadding(ret.bottomPadding() + table.cellPadding());
949 tryCopyProperties(table.property(QTextFormat::FrameBorder),
950 { QTextFormat::TableCellTopBorder, QTextFormat::TableCellBottomBorder,
951 QTextFormat::TableCellLeftBorder, QTextFormat::TableCellRightBorder });
953 tryCopyProperties(table.property(QTextFormat::FrameBorderBrush),
954 { QTextFormat::TableCellTopBorderBrush, QTextFormat::TableCellBottomBorderBrush,
955 QTextFormat::TableCellLeftBorderBrush, QTextFormat::TableCellRightBorderBrush });
957 tryCopyProperties(table.property(QTextFormat::FrameBorderStyle),
958 { QTextFormat::TableCellTopBorderStyle, QTextFormat::TableCellBottomBorderStyle,
959 QTextFormat::TableCellLeftBorderStyle, QTextFormat::TableCellRightBorderStyle });
988void QTextOdfWriter::tableCellStyleElement(QXmlStreamWriter &writer,
int formatIndex,
989 const QTextTableCellFormat &format,
bool hasBorder)
const
991 writer.writeStartElement(styleNS, u"style");
992 writer.writeAttribute(styleNS, u"name", QString::fromLatin1(
"T%1").arg(formatIndex));
993 writer.writeAttribute(styleNS, u"family", u"table-cell");
994 writer.writeEmptyElement(styleNS, u"table-cell-properties");
996 auto writeBorder = [&](QAnyStringView borderName, qreal size, QTextFrameFormat::BorderStyle style,
const QBrush &brush)
998 writer.writeAttribute(foNS, borderName, pixelToPoint(size) +
" "_L1
999 + borderStyleName(style) +
" "_L1 + brush.color().name(QColor::HexRgb));
1001 const auto topBorder = format.topBorder();
1002 const auto topBorderStyle = format.topBorderStyle();
1003 const auto topBorderBrush = format.topBorderBrush();
1004 if (topBorder == format.rightBorder() &&
1005 topBorder == format.bottomBorder() &&
1006 topBorder == format.leftBorder() &&
1007 topBorderStyle == format.rightBorderStyle() &&
1008 topBorderStyle == format.bottomBorderStyle() &&
1009 topBorderStyle == format.leftBorderStyle() &&
1010 topBorderBrush == format.rightBorderBrush() &&
1011 topBorderBrush == format.bottomBorderBrush() &&
1012 topBorderBrush == format.leftBorderBrush()
1014 writeBorder(u"border", topBorder, topBorderStyle, topBorderBrush);
1016 writeBorder(u"border-top", topBorder, topBorderStyle, topBorderBrush);
1017 writeBorder(u"border-right", format.rightBorder(), format.rightBorderStyle(), format.rightBorderBrush());
1018 writeBorder(u"border-bottom", format.bottomBorder(), format.bottomBorderStyle(), format.bottomBorderBrush());
1019 writeBorder(u"border-left", format.leftBorder(), format.leftBorderStyle(), format.leftBorderBrush());
1022 const qreal topPadding = format.topPadding();
1023 if (topPadding != 0 &&
1024 topPadding == format.bottomPadding() &&
1025 topPadding == format.leftPadding() &&
1026 topPadding == format.rightPadding()) {
1027 writer.writeAttribute(foNS, u"padding", pixelToPoint(topPadding));
1029 const auto writeBorder = [&](qreal padding, QAnyStringView attr) {
1031 writer.writeAttribute(foNS, attr, pixelToPoint(padding));
1033 writeBorder(topPadding, u"padding-top");
1034 writeBorder(format.bottomPadding(), u"padding-bottom");
1035 writeBorder(format.leftPadding(), u"padding-left");
1036 writeBorder(format.rightPadding(), u"padding-right");
1039 if (format.hasProperty(QTextFormat::TextVerticalAlignment)) {
1041 switch (format.verticalAlignment()) {
1042 case QTextCharFormat::AlignMiddle:
1043 pos = u"middle";
break;
1044 case QTextCharFormat::AlignTop:
1045 pos = u"top";
break;
1046 case QTextCharFormat::AlignBottom:
1047 pos = u"bottom";
break;
1049 pos = u"automatic";
break;
1051 writer.writeAttribute(styleNS, u"vertical-align", pos);
1058 writer.writeEndElement();
1079bool QTextOdfWriter::writeAll()
1081 const auto reset = qScopeGuard([
this] {
1083 m_strategy =
nullptr;
1086 if (m_createArchive)
1087 m_strategy =
new QZipStreamStrategy(m_device);
1089 m_strategy =
new QXmlStreamStrategy(m_device);
1091 if (!m_device->isWritable() && ! m_device->open(QIODevice::WriteOnly)) {
1092 qWarning(
"QTextOdfWriter::writeAll: the device cannot be opened for writing");
1095 QXmlStreamWriter writer(m_strategy->contentStream);
1097 writer.setAutoFormatting(
true);
1098 writer.setAutoFormattingIndent(2);
1100 writer.writeNamespace(officeNS, u"office");
1101 writer.writeNamespace(textNS, u"text");
1102 writer.writeNamespace(styleNS, u"style");
1103 writer.writeNamespace(foNS, u"fo");
1104 writer.writeNamespace(tableNS, u"table");
1105 writer.writeNamespace(drawNS, u"draw");
1106 writer.writeNamespace(xlinkNS, u"xlink");
1107 writer.writeNamespace(svgNS, u"svg");
1108 writer.writeStartDocument();
1109 writer.writeStartElement(officeNS, u"document-content");
1110 writer.writeAttribute(officeNS, u"version", u"1.2");
1114 const QTextDocumentPrivate::FragmentMap &fragments = QTextDocumentPrivate::get(m_document)->fragmentMap();
1115 for (
const auto *fragment : fragments)
1116 formats << fragment->format;
1119 const QTextDocumentPrivate::BlockMap &blocks = QTextDocumentPrivate::get(m_document)->blockMap();
1120 for (
const auto *block : blocks)
1121 formats << block->format;
1124 const QList<QTextFormat> allFormats = m_document->allFormats();
1125 const QSet<
int> copy = formats;
1126 for (
auto index : copy) {
1127 QTextObject *object = m_document->objectForFormat(allFormats[index]);
1129 formats << object->formatIndex();
1130 if (
auto *tableobject = qobject_cast<QTextTable *>(object)) {
1131 if (tableobject->format().borderStyle()) {
1132 int tableID = tableobject->formatIndex();
1133 m_tableFormatsWithBorders.insert(tableID);
1136 for (
int rowindex = 0; rowindex < tableobject->rows(); ++rowindex) {
1137 for (
int colindex = 0; colindex < tableobject->columns(); ++colindex) {
1138 const int cellFormatID = tableobject->cellAt(rowindex, colindex).tableCellFormatIndex();
1139 auto it = m_cellFormatsInTablesWithBorders.find(cellFormatID);
1140 if (it == m_cellFormatsInTablesWithBorders.end()) {
1141 m_cellFormatsInTablesWithBorders.insert(cellFormatID, { tableID });
1144 if (!val.contains(tableID))
1145 val.append(tableID);
1154 writeFormats(writer, formats);
1156 writer.writeStartElement(officeNS, u"body");
1157 writer.writeStartElement(officeNS, u"text");
1158 QTextFrame *rootFrame = m_document->rootFrame();
1159 writeFrame(writer, rootFrame);
1160 writer.writeEndElement();
1161 writer.writeEndElement();
1162 writer.writeEndElement();
1163 writer.writeEndDocument();