include/vcl/pdfwriter.hxx | 6 + sw/inc/EnhancedPDFExportHelper.hxx | 14 ++- sw/source/core/text/EnhancedPDFExportHelper.cxx | 107 +++++++++++++++++++++++- sw/source/core/text/itrpaint.cxx | 4 sw/source/core/text/pormulti.cxx | 32 ++++++- vcl/source/gdi/pdfwriter_impl.cxx | 38 ++++++++ 6 files changed, 192 insertions(+), 9 deletions(-)
New commits: commit 949f0d9cf2fe7953691160103139a16473dd3171 Author: Michael Stahl <michael.st...@allotropia.de> AuthorDate: Tue Oct 24 20:05:37 2023 +0200 Commit: Michael Stahl <michael.st...@allotropia.de> CommitDate: Wed Oct 25 11:31:01 2023 +0200 vcl,sw: PDF/UA export: produce Ruby and Warichu SEs These need to generate multiple elements in SwTextPainter::PaintMultiPortion() and it's not altogether obvious. Change-Id: Ib5fd36c3ea8e15dff93a87bb231c3cc4f78b0089 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/158398 Tested-by: Jenkins Reviewed-by: Michael Stahl <michael.st...@allotropia.de> diff --git a/include/vcl/pdfwriter.hxx b/include/vcl/pdfwriter.hxx index acc491efdfa9..cd8be2a50581 100644 --- a/include/vcl/pdfwriter.hxx +++ b/include/vcl/pdfwriter.hxx @@ -132,6 +132,7 @@ public: // inline level elements Span, Quote, Note, Reference, BibEntry, Code, Link, Annot, + Ruby, RB, RT, RP, Warichu, WT, WP, // illustration elements Figure, Formula, Form @@ -146,6 +147,7 @@ public: TextIndent, TextAlign, Width, Height, BlockAlign, InlineAlign, LineHeight, BaselineShift, TextDecorationType, ListNumbering, RowSpan, ColSpan, Scope, Role, + RubyAlign, RubyPosition, // link destination is an artificial attribute that sets // the link annotation ID of a Link element @@ -186,6 +188,10 @@ public: Row, Column, Both, // Role Rb, Cb, Pb, Tv, + // RubyAlign + RStart, RCenter, REnd, RJustify, RDistribute, + // RubyPosition + RBefore, RAfter, RWarichu, RInline, // ListNumbering Disc, Circle, Square, Decimal, UpperRoman, LowerRoman, UpperAlpha, LowerAlpha }; diff --git a/sw/inc/EnhancedPDFExportHelper.hxx b/sw/inc/EnhancedPDFExportHelper.hxx index 542138157d2f..be3c45383061 100644 --- a/sw/inc/EnhancedPDFExportHelper.hxx +++ b/sw/inc/EnhancedPDFExportHelper.hxx @@ -113,10 +113,15 @@ struct Por_Info { const SwLinePortion& mrPor; const SwTextPainter& mrTextPainter; - bool const m_isNumberingLabel; - - Por_Info(const SwLinePortion& rPor, const SwTextPainter& rTextPainer, bool const isNumberingLabel) - : mrPor(rPor), mrTextPainter(rTextPainer), m_isNumberingLabel(isNumberingLabel) {}; + /** this can be used to generate multiple different SE for the same portion: + FootnoteNum: 0-> Link 1-> Lbl + Double: 0-> Warichu 1-> WP 2-> WT + Ruby: 0-> Ruby 1-> RT 2-> RB + */ + int const m_Mode; + + Por_Info(const SwLinePortion& rPor, const SwTextPainter& rTextPainer, int const nMode) + : mrPor(rPor), mrTextPainter(rTextPainer), m_Mode(nMode) {}; }; struct lt_TableColumn @@ -160,6 +165,7 @@ class SwTaggedPDFHelper void BeginInlineStructureElements(); void EndStructureElements(); + void EndCurrentAll(); void EndCurrentSpan(); void CreateCurrentSpan(SwTextPaintInfo const& rInf, OUString const& rStyleName); bool CheckContinueSpan(SwTextPaintInfo const& rInf, std::u16string_view rStyleName, SwTextAttr const* pInetFormatAttr); diff --git a/sw/source/core/text/EnhancedPDFExportHelper.cxx b/sw/source/core/text/EnhancedPDFExportHelper.cxx index 999e599b4478..f9702e28b19a 100644 --- a/sw/source/core/text/EnhancedPDFExportHelper.cxx +++ b/sw/source/core/text/EnhancedPDFExportHelper.cxx @@ -71,6 +71,7 @@ #include <flyfrm.hxx> #include <notxtfrm.hxx> #include "porfld.hxx" +#include "pormulti.hxx" #include <SwStyleNameMapper.hxx> #include "itrpaint.hxx" #include <i18nlangtag/languagetag.hxx> @@ -935,6 +936,51 @@ void SwTaggedPDFHelper::SetAttributes( vcl::PDFWriter::StructElement eType ) bLanguage = true; break; + case vcl::PDFWriter::RT: + { + SwRubyPortion const*const pRuby(static_cast<SwRubyPortion const*>(pPor)); + vcl::PDFWriter::StructAttributeValue nAlign = {}; + switch (pRuby->GetAdjustment()) + { + case text::RubyAdjust_LEFT: + nAlign = vcl::PDFWriter::RStart; + break; + case text::RubyAdjust_CENTER: + nAlign = vcl::PDFWriter::RCenter; + break; + case text::RubyAdjust_RIGHT: + nAlign = vcl::PDFWriter::REnd; + break; + case text::RubyAdjust_BLOCK: + nAlign = vcl::PDFWriter::RJustify; + break; + case text::RubyAdjust_INDENT_BLOCK: + nAlign = vcl::PDFWriter::RDistribute; + break; + default: + assert(false); + break; + } + ::std::optional<vcl::PDFWriter::StructAttributeValue> oPos; + switch (pRuby->GetRubyPosition()) + { + case RubyPosition::ABOVE: + oPos = vcl::PDFWriter::RBefore; + break; + case RubyPosition::BELOW: + oPos = vcl::PDFWriter::RAfter; + break; + case RubyPosition::RIGHT: + break; // no such thing??? + } + mpPDFExtOutDevData->SetStructureAttribute(vcl::PDFWriter::RubyAlign, nAlign); + if (oPos) + { + mpPDFExtOutDevData->SetStructureAttribute(vcl::PDFWriter::RubyPosition, *oPos); + } + } + break; + default: break; } @@ -1621,6 +1667,18 @@ void SwTaggedPDFHelper::EndStructureElements() CheckRestoreTag(); } +void SwTaggedPDFHelper::EndCurrentAll() +{ + if (mpPDFExtOutDevData->GetSwPDFState()->m_oCurrentSpan) + { + mpPDFExtOutDevData->GetSwPDFState()->m_oCurrentSpan.reset(); + } + if (mpPDFExtOutDevData->GetSwPDFState()->m_oCurrentLink) + { + mpPDFExtOutDevData->GetSwPDFState()->m_oCurrentLink.reset(); + } +} + void SwTaggedPDFHelper::EndCurrentSpan() { mpPDFExtOutDevData->GetSwPDFState()->m_oCurrentSpan.reset(); @@ -1828,10 +1886,55 @@ void SwTaggedPDFHelper::BeginInlineStructureElements() } break; + case PortionType::Multi: + { + SwMultiPortion const*const pMulti(static_cast<SwMultiPortion const*>(pPor)); + if (pMulti->IsRuby()) + { + EndCurrentAll(); + switch (mpPorInfo->m_Mode) + { + case 0: + nPDFType = vcl::PDFWriter::Ruby; + aPDFType = "Ruby"; + break; + case 1: + nPDFType = vcl::PDFWriter::RT; + aPDFType = "RT"; + break; + case 2: + nPDFType = vcl::PDFWriter::RB; + aPDFType = "RB"; + break; + } + } + else if (pMulti->IsDouble()) + { + EndCurrentAll(); + switch (mpPorInfo->m_Mode) + { + case 0: + nPDFType = vcl::PDFWriter::Warichu; + aPDFType = "Warichu"; + break; + case 1: + nPDFType = vcl::PDFWriter::WP; + aPDFType = "WP"; + break; + case 2: + nPDFType = vcl::PDFWriter::WT; + aPDFType = "WT"; + break; + } + } + } + break; + + // for FootnoteNum, is called twice: outer generates Lbl, inner Link case PortionType::FootnoteNum: assert(!isContinueSpan); // is at start - if (!mpPorInfo->m_isNumberingLabel) + if (mpPorInfo->m_Mode == 0) { // tdf#152218 link both directions nPDFType = vcl::PDFWriter::Link; aPDFType = aLinkString; @@ -1842,7 +1945,7 @@ void SwTaggedPDFHelper::BeginInlineStructureElements() case PortionType::Bullet: case PortionType::GrfNum: assert(!isContinueSpan); // is at start - if (mpPorInfo->m_isNumberingLabel) + if (mpPorInfo->m_Mode == 1) { // only works for multiple lines via wrapper from PaintSwFrame nPDFType = vcl::PDFWriter::LILabel; aPDFType = aListLabelString; diff --git a/sw/source/core/text/itrpaint.cxx b/sw/source/core/text/itrpaint.cxx index 6d07ef7dc468..31bd418e94e2 100644 --- a/sw/source/core/text/itrpaint.cxx +++ b/sw/source/core/text/itrpaint.cxx @@ -415,13 +415,13 @@ void SwTextPainter::DrawTextLine( const SwRect &rPaint, SwSaveClip &rClip, && !roTaggedLabel) // note: CalcPaintOfst may skip some portions { assert(isPDFTaggingEnabled); - Por_Info aPorInfo(*pPor, *this, true); // open Lbl + Por_Info aPorInfo(*pPor, *this, 1); // open Lbl roTaggedLabel.emplace(nullptr, nullptr, &aPorInfo, *pOut); } { // #i16816# tagged pdf support - Por_Info aPorInfo(*pPor, *this, false); + Por_Info aPorInfo(*pPor, *this, 0); SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, &aPorInfo, *pOut ); if( pPor->IsMultiPortion() ) diff --git a/sw/source/core/text/pormulti.cxx b/sw/source/core/text/pormulti.cxx index 7a2e8c44c653..07453a4b1fcb 100644 --- a/sw/source/core/text/pormulti.cxx +++ b/sw/source/core/text/pormulti.cxx @@ -31,6 +31,7 @@ #include <charfmt.hxx> #include <layfrm.hxx> #include <SwPortionHandler.hxx> +#include <EnhancedPDFExportHelper.hxx> #include "pormulti.hxx" #include "inftxt.hxx" #include "itrpaint.hxx" @@ -1601,6 +1602,10 @@ void SwTextPainter::PaintMultiPortion( const SwRect &rPaint, if( rMulti.HasBrackets() ) { + // WP is mandatory + Por_Info const por(rMulti, *this, 1); + SwTaggedPDFHelper const tag(nullptr, nullptr, &por, *GetInfo().GetOut()); + TextFrameIndex const nTmpOldIdx = GetInfo().GetIdx(); GetInfo().SetIdx(static_cast<SwDoubleLinePortion&>(rMulti).GetBrackets()->nStart); SeekAndChg( GetInfo() ); @@ -1659,8 +1664,18 @@ void SwTextPainter::PaintMultiPortion( const SwRect &rPaint, OSL_ENSURE( nullptr == GetInfo().GetUnderFnt() || rMulti.IsBidi(), " Only BiDi portions are allowed to use the common underlining font" ); - if ( rMulti.IsRuby() ) + ::std::optional<SwTaggedPDFHelper> oTag; + if (rMulti.IsDouble()) + { + Por_Info const por(rMulti, *this, 2); + oTag.emplace(nullptr, nullptr, &por, *GetInfo().GetOut()); + } + else if (rMulti.IsRuby()) + { + Por_Info const por(rMulti, *this, bRubyTop ? 1 : 2); + oTag.emplace(nullptr, nullptr, &por, *GetInfo().GetOut()); GetInfo().SetRuby( rMulti.OnTop() ); + } do { @@ -1822,9 +1837,20 @@ void SwTextPainter::PaintMultiPortion( const SwRect &rPaint, // We switch to the baseline of the next inner line nOfst += rMulti.GetRoot().Height(); } + if (rMulti.IsRuby()) + { + oTag.reset(); + Por_Info const por(rMulti, *this, bRubyTop ? 2 : 1); + oTag.emplace(nullptr, nullptr, &por, *GetInfo().GetOut()); + } } } while( pPor ); + if (rMulti.IsDouble()) + { + oTag.reset(); + } + if ( bRubyInGrid ) GetInfo().SetSnapToGrid( bOldGridModeAllowed ); @@ -1840,6 +1866,10 @@ void SwTextPainter::PaintMultiPortion( const SwRect &rPaint, if( rMulti.HasBrackets() ) { + // WP is mandatory + Por_Info const por(rMulti, *this, 1); + SwTaggedPDFHelper const tag(nullptr, nullptr, &por, *GetInfo().GetOut()); + TextFrameIndex const nTmpOldIdx = GetInfo().GetIdx(); GetInfo().SetIdx(static_cast<SwDoubleLinePortion&>(rMulti).GetBrackets()->nStart); SeekAndChg( GetInfo() ); diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx index 677212e15d1a..751c931ff945 100644 --- a/vcl/source/gdi/pdfwriter_impl.cxx +++ b/vcl/source/gdi/pdfwriter_impl.cxx @@ -1880,6 +1880,8 @@ const char* PDFWriterImpl::getAttributeTag( PDFWriter::StructAttribute eAttr ) { PDFWriter::ColSpan, "ColSpan" }, { PDFWriter::Scope, "Scope" }, { PDFWriter::Role, "Role" }, + { PDFWriter::RubyAlign, "RubyAlign" }, + { PDFWriter::RubyPosition, "RubyPosition" }, { PDFWriter::Type, "Type" }, { PDFWriter::Subtype, "Subtype" }, { PDFWriter::LinkAnnotation, "LinkAnnotation" } @@ -1928,6 +1930,15 @@ const char* PDFWriterImpl::getAttributeValueTag( PDFWriter::StructAttributeValue { PDFWriter::Cb, "cb" }, { PDFWriter::Pb, "pb" }, { PDFWriter::Tv, "tv" }, + { PDFWriter::RStart, "Start" }, + { PDFWriter::RCenter, "Center" }, + { PDFWriter::REnd, "End" }, + { PDFWriter::RJustify, "Justify" }, + { PDFWriter::RDistribute,"Distribute" }, + { PDFWriter::RBefore, "Before" }, + { PDFWriter::RAfter, "After" }, + { PDFWriter::RWarichu, "Warichu" }, + { PDFWriter::RInline, "Inline" }, { PDFWriter::Disc, "Disc" }, { PDFWriter::Circle, "Circle" }, { PDFWriter::Square, "Square" }, @@ -10687,6 +10698,13 @@ const char* PDFWriterImpl::getStructureTag( PDFWriter::StructElement eType ) { PDFWriter::Code, "Code" }, { PDFWriter::Link, "Link" }, { PDFWriter::Annot, "Annot" }, + { PDFWriter::Ruby, "Ruby" }, + { PDFWriter::RB, "RB" }, + { PDFWriter::RT, "RT" }, + { PDFWriter::RP, "RP" }, + { PDFWriter::Warichu, "Warichu" }, + { PDFWriter::WT, "WT" }, + { PDFWriter::WP, "WP" }, { PDFWriter::Figure, "Figure" }, { PDFWriter::Formula, "Formula"}, { PDFWriter::Form, "Form" } @@ -11370,6 +11388,26 @@ bool PDFWriterImpl::setStructureAttribute( enum PDFWriter::StructAttribute eAttr } } break; + case PDFWriter::RubyAlign: + if (eVal == PDFWriter::RStart || eVal == PDFWriter::RCenter || eVal == PDFWriter::REnd || eVal == PDFWriter::RJustify || eVal == PDFWriter::RDistribute) + { + if (eType == PDFWriter::RT + && PDFWriter::PDFVersion::PDF_1_5 <= m_aContext.Version) + { + bInsert = true; + } + } + break; + case PDFWriter::RubyPosition: + if (eVal == PDFWriter::RBefore || eVal == PDFWriter::RAfter || eVal == PDFWriter::RWarichu || eVal == PDFWriter::RInline) + { + if (eType == PDFWriter::RT + && PDFWriter::PDFVersion::PDF_1_5 <= m_aContext.Version) + { + bInsert = true; + } + } + break; case PDFWriter::ListNumbering: if( eVal == PDFWriter::NONE || eVal == PDFWriter::Disc ||