1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <EnhancedPDFExportHelper.hxx>
22 #include <com/sun/star/embed/XEmbeddedObject.hpp>
23 #include <com/sun/star/i18n/ScriptType.hpp>
24 #include <com/sun/star/drawing/XShape.hpp>
25 #include <com/sun/star/beans/XPropertySet.hpp>
26 #include <hintids.hxx>
28 #include <sot/exchange.hxx>
29 #include <vcl/outdev.hxx>
30 #include <vcl/pdfextoutdevdata.hxx>
31 #include <vcl/pdf/PDFNote.hxx>
32 #include <tools/multisel.hxx>
33 #include <editeng/adjustitem.hxx>
34 #include <editeng/lrspitem.hxx>
35 #include <editeng/langitem.hxx>
36 #include <tools/urlobj.hxx>
37 #include <svl/languageoptions.hxx>
38 #include <svl/numformat.hxx>
39 #include <svl/zforlist.hxx>
40 #include <swatrset.hxx>
45 #include <section.hxx>
48 #include <txtinet.hxx>
49 #include <fmtinfmt.hxx>
50 #include <fchrfmt.hxx>
51 #include <charfmt.hxx>
52 #include <fmtanchr.hxx>
59 #include <IDocumentOutlineNodes.hxx>
61 #include <docufld.hxx>
64 #include <rootfrm.hxx>
65 #include <pagefrm.hxx>
69 #include <cellfrm.hxx>
70 #include <sectfrm.hxx>
73 #include <notxtfrm.hxx>
75 #include "pormulti.hxx"
76 #include <SwStyleNameMapper.hxx>
77 #include "itrpaint.hxx"
78 #include <i18nlangtag/languagetag.hxx>
80 #include <printdata.hxx>
82 #include <SwNodeNum.hxx>
84 #include <frmtool.hxx>
85 #include <strings.hrc>
86 #include <frameformats.hxx>
87 #include <tblafmt.hxx>
88 #include <authfld.hxx>
89 #include <dcontact.hxx>
90 #include <PostItMgr.hxx>
91 #include <AnnotationWin.hxx>
93 #include <tools/globname.hxx>
94 #include <svx/svdobj.hxx>
101 using namespace ::com::sun::star
;
103 #if OSL_DEBUG_LEVEL > 1
105 static std::vector
< sal_uInt16
> aStructStack
;
107 void lcl_DBGCheckStack()
109 /* NonStructElement = 0 Document = 1 Part = 2
110 * Article = 3 Section = 4 Division = 5
111 * BlockQuote = 6 Caption = 7 TOC = 8
112 * TOCI = 9 Index = 10 Paragraph = 11
113 * Heading = 12 H1-6 = 13 - 18 List = 19
114 * ListItem = 20 LILabel = 21 LIBody = 22
115 * Table = 23 TableRow = 24 TableHeader = 25
116 * TableData = 26 Span = 27 Quote = 28
117 * Note = 29 Reference = 30 BibEntry = 31
118 * Code = 32 Link = 33 Figure = 34
119 * Formula = 35 Form = 36 Continued frame = 99
123 for ( const auto& rItem
: aStructStack
)
132 typedef std::set
< tools::Long
, lt_TableColumn
> TableColumnsMapEntry
;
133 typedef std::pair
< SwRect
, sal_Int32
> IdMapEntry
;
134 typedef std::vector
< IdMapEntry
> LinkIdMap
;
135 typedef std::vector
< IdMapEntry
> NoteIdMap
;
136 typedef std::map
< const SwTable
*, TableColumnsMapEntry
> TableColumnsMap
;
137 typedef std::map
< const SwNumberTreeNode
*, sal_Int32
> NumListIdMap
;
138 typedef std::map
< const SwNumberTreeNode
*, sal_Int32
> NumListBodyIdMap
;
139 typedef std::set
<const void*> FrameTagSet
;
141 struct SwEnhancedPDFState
143 TableColumnsMap m_TableColumnsMap
;
144 LinkIdMap m_LinkIdMap
;
145 NoteIdMap m_NoteIdMap
;
146 NumListIdMap m_NumListIdMap
;
147 NumListBodyIdMap m_NumListBodyIdMap
;
148 FrameTagSet m_FrameTagSet
;
150 LanguageType m_eLanguageDefault
;
154 FontLineStyle eUnderline
;
155 FontLineStyle eOverline
;
156 FontStrikeout eStrikeout
;
157 FontEmphasisMark eFontEmphasis
;
159 SwFontScript nScript
;
164 ::std::optional
<Span
> m_oCurrentSpan
;
165 ::std::optional
<SwTextAttr
const*> m_oCurrentLink
;
167 SwEnhancedPDFState(LanguageType
const eLanguageDefault
)
168 : m_eLanguageDefault(eLanguageDefault
)
176 constexpr OUString aTableHeadingName
= u
"Table Heading"_ustr
;
177 constexpr OUString aQuotations
= u
"Quotations"_ustr
;
178 constexpr OUString aCaption
= u
"Caption"_ustr
;
179 constexpr OUString aHeading
= u
"Heading"_ustr
;
180 constexpr OUString aQuotation
= u
"Quotation"_ustr
;
181 constexpr OUString aSourceText
= u
"Source Text"_ustr
;
184 constexpr OUStringLiteral aDocumentString
= u
"Document";
185 constexpr OUString aDivString
= u
"Div"_ustr
;
186 constexpr OUStringLiteral aSectString
= u
"Sect";
187 constexpr OUStringLiteral aHString
= u
"H";
188 constexpr OUStringLiteral aH1String
= u
"H1";
189 constexpr OUStringLiteral aH2String
= u
"H2";
190 constexpr OUStringLiteral aH3String
= u
"H3";
191 constexpr OUStringLiteral aH4String
= u
"H4";
192 constexpr OUStringLiteral aH5String
= u
"H5";
193 constexpr OUStringLiteral aH6String
= u
"H6";
194 constexpr OUStringLiteral aH7String
= u
"H7";
195 constexpr OUStringLiteral aH8String
= u
"H8";
196 constexpr OUStringLiteral aH9String
= u
"H9";
197 constexpr OUStringLiteral aH10String
= u
"H10";
198 constexpr OUStringLiteral aListString
= u
"L";
199 constexpr OUStringLiteral aListItemString
= u
"LI";
200 constexpr OUStringLiteral aListLabelString
= u
"Lbl";
201 constexpr OUString aListBodyString
= u
"LBody"_ustr
;
202 constexpr OUStringLiteral aBlockQuoteString
= u
"BlockQuote";
203 constexpr OUString aCaptionString
= u
"Caption"_ustr
;
204 constexpr OUStringLiteral aIndexString
= u
"Index";
205 constexpr OUStringLiteral aTOCString
= u
"TOC";
206 constexpr OUStringLiteral aTOCIString
= u
"TOCI";
207 constexpr OUStringLiteral aTableString
= u
"Table";
208 constexpr OUStringLiteral aTRString
= u
"TR";
209 constexpr OUStringLiteral aTDString
= u
"TD";
210 constexpr OUStringLiteral aTHString
= u
"TH";
211 constexpr OUStringLiteral aBibEntryString
= u
"BibEntry";
212 constexpr OUStringLiteral aQuoteString
= u
"Quote";
213 constexpr OUString aSpanString
= u
"Span"_ustr
;
214 constexpr OUStringLiteral aCodeString
= u
"Code";
215 constexpr OUStringLiteral aFigureString
= u
"Figure";
216 constexpr OUStringLiteral aFormulaString
= u
"Formula";
217 constexpr OUString aLinkString
= u
"Link"_ustr
;
218 constexpr OUStringLiteral aNoteString
= u
"Note";
219 constexpr OUStringLiteral aAnnotString
= u
"Annot";
221 // returns true if first paragraph in cell frame has 'table heading' style
222 bool lcl_IsHeadlineCell( const SwCellFrame
& rCellFrame
)
226 const SwContentFrame
*pCnt
= rCellFrame
.ContainsContent();
227 if ( pCnt
&& pCnt
->IsTextFrame() )
229 SwTextNode
const*const pTextNode
= static_cast<const SwTextFrame
*>(pCnt
)->GetTextNodeForParaProps();
230 const SwFormat
* pTextFormat
= pTextNode
->GetFormatColl();
233 SwStyleNameMapper::FillProgName( pTextFormat
->GetName(), sStyleName
, SwGetPoolIdFromName::TxtColl
);
234 bRet
= sStyleName
== aTableHeadingName
;
237 // tdf#153935 wild guessing for 1st row based on table autoformat
238 if (!bRet
&& !rCellFrame
.GetUpper()->GetPrev())
240 SwTable
const*const pTable(rCellFrame
.FindTabFrame()->GetTable());
242 OUString
const& rStyleName(pTable
->GetTableStyleName());
243 if (!rStyleName
.isEmpty())
245 if (SwTableAutoFormat
const*const pTableAF
=
246 pTable
->GetFrameFormat()->GetDoc()->GetTableStyles().FindAutoFormat(rStyleName
))
248 bRet
|= pTableAF
->HasHeaderRow();
256 // List all frames for which the NonStructElement tag is set:
257 bool lcl_IsInNonStructEnv( const SwFrame
& rFrame
)
261 if ( nullptr != rFrame
.FindFooterOrHeader() &&
262 !rFrame
.IsHeaderFrame() && !rFrame
.IsFooterFrame() )
266 else if ( rFrame
.IsInTab() && !rFrame
.IsTabFrame() )
268 const SwTabFrame
* pTabFrame
= rFrame
.FindTabFrame();
269 if ( rFrame
.GetUpper() != pTabFrame
&&
270 pTabFrame
->IsFollow() && pTabFrame
->IsInHeadline( rFrame
) )
277 // Generate key from frame for reopening tags:
278 void const* lcl_GetKeyFromFrame( const SwFrame
& rFrame
)
280 void const* pKey
= nullptr;
282 if ( rFrame
.IsPageFrame() )
283 pKey
= static_cast<void const *>(&(static_cast<const SwPageFrame
&>(rFrame
).GetFormat()->getIDocumentSettingAccess()));
284 else if ( rFrame
.IsTextFrame() )
285 pKey
= static_cast<void const *>(static_cast<const SwTextFrame
&>(rFrame
).GetTextNodeFirst());
286 else if ( rFrame
.IsSctFrame() )
287 pKey
= static_cast<void const *>(static_cast<const SwSectionFrame
&>(rFrame
).GetSection());
288 else if ( rFrame
.IsTabFrame() )
289 pKey
= static_cast<void const *>(static_cast<const SwTabFrame
&>(rFrame
).GetTable());
290 else if ( rFrame
.IsRowFrame() )
291 pKey
= static_cast<void const *>(static_cast<const SwRowFrame
&>(rFrame
).GetTabLine());
292 else if ( rFrame
.IsCellFrame() )
294 const SwTabFrame
* pTabFrame
= rFrame
.FindTabFrame();
295 const SwTable
* pTable
= pTabFrame
->GetTable();
296 pKey
= static_cast<void const *>(& static_cast<const SwCellFrame
&>(rFrame
).GetTabBox()->FindStartOfRowSpan(*pTable
));
298 else if (rFrame
.IsFootnoteFrame())
300 pKey
= static_cast<void const*>(static_cast<SwFootnoteFrame
const&>(rFrame
).GetAttr());
306 bool lcl_HasPreviousParaSameNumRule(SwTextFrame
const& rTextFrame
, const SwTextNode
& rNode
)
309 SwNodeIndex
aIdx( rNode
);
310 const SwDoc
& rDoc
= rNode
.GetDoc();
311 const SwNodes
& rNodes
= rDoc
.GetNodes();
312 const SwNode
* pNode
= &rNode
;
313 const SwNumRule
* pNumRule
= rNode
.GetNumRule();
315 while (pNode
!= rNodes
.DocumentSectionStartNode(const_cast<SwNode
*>(static_cast<SwNode
const *>(&rNode
))) )
317 sw::GotoPrevLayoutTextFrame(aIdx
, rTextFrame
.getRootFrame());
319 if (aIdx
.GetNode().IsTextNode())
321 const SwTextNode
*const pPrevTextNd
= sw::GetParaPropsNode(
322 *rTextFrame
.getRootFrame(), *aIdx
.GetNode().GetTextNode());
323 const SwNumRule
* pPrevNumRule
= pPrevTextNd
->GetNumRule();
325 // We find the previous text node. Now check, if the previous text node
326 // has the same numrule like rNode:
327 if ( (pPrevNumRule
== pNumRule
) &&
328 (!pPrevTextNd
->IsOutline() == !rNode
.IsOutline()))
334 pNode
= &aIdx
.GetNode();
339 bool lcl_TryMoveToNonHiddenField(SwEditShell
& rShell
, const SwTextNode
& rNd
, const SwFormatField
& rField
)
341 // 1. Check if the whole paragraph is hidden
342 // 2. Move to the field
343 // 3. Check for hidden text attribute
346 if(!rShell
.GotoFormatField(rField
) || rShell
.IsInHiddenRange(/*bSelect=*/false))
348 rShell
.SwCursorShell::ClearMark();
354 // tdf#157816: try to check if the rectangle contains actual text
355 ::std::vector
<SwRect
> GetCursorRectsContainingText(SwCursorShell
const& rShell
)
357 ::std::vector
<SwRect
> ret
;
359 rShell
.GetLayout()->CalcFrameRects(*rShell
.GetCursor_(), rects
, SwRootFrame::RectsMode::NoAnchoredFlys
);
360 for (SwRect
const& rRect
: rects
)
362 Point
center(rRect
.Center());
363 SwSpecialPos special
;
364 SwCursorMoveState
cms(CursorMoveState::NONE
);
365 cms
.m_pSpecialPos
= &special
;
366 cms
.m_bFieldInfo
= true;
367 SwPosition
pos(rShell
.GetDoc()->GetNodes());
368 auto const [pStart
, pEnd
] = rShell
.GetCursor_()->StartEnd();
369 if (rShell
.GetLayout()->GetModelPositionForViewPoint(&pos
, center
, &cms
)
370 && *pStart
<= pos
&& pos
<= *pEnd
)
373 std::pair
<Point
, bool> const tmp(center
, false);
374 SwContentFrame
const*const pFrame(
375 pos
.nNode
.GetNode().GetTextNode()->getLayoutFrame(rShell
.GetLayout(), &pos
, &tmp
));
376 if (pFrame
->GetCharRect(charRect
, pos
, &cms
, false)
377 && rRect
.Overlaps(charRect
))
379 ret
.push_back(rRect
);
382 // reset stupid static var that may have gotten set now
383 SwTextCursor::SetRightMargin(false); // WTF is this crap
390 SwTaggedPDFHelper::SwTaggedPDFHelper( const Num_Info
* pNumInfo
,
391 const Frame_Info
* pFrameInfo
,
392 const Por_Info
* pPorInfo
,
393 OutputDevice
const & rOut
)
394 : m_nEndStructureElement( 0 ),
395 m_nRestoreCurrentTag( -1 ),
396 mpNumInfo( pNumInfo
),
397 mpFrameInfo( pFrameInfo
),
398 mpPorInfo( pPorInfo
)
401 dynamic_cast< vcl::PDFExtOutDevData
*>( rOut
.GetExtOutDevData() );
403 if ( !(mpPDFExtOutDevData
&& mpPDFExtOutDevData
->GetIsExportTaggedPDF()) )
406 #if OSL_DEBUG_LEVEL > 1
407 sal_Int32 nCurrentStruct
= mpPDFExtOutDevData
->GetCurrentStructureElement();
411 BeginNumberedListStructureElements();
412 else if ( mpFrameInfo
)
413 BeginBlockStructureElements();
414 else if ( mpPorInfo
)
415 BeginInlineStructureElements();
417 BeginTag( vcl::PDFWriter::NonStructElement
, OUString() );
419 #if OSL_DEBUG_LEVEL > 1
420 nCurrentStruct
= mpPDFExtOutDevData
->GetCurrentStructureElement();
422 (void)nCurrentStruct
;
426 SwTaggedPDFHelper::~SwTaggedPDFHelper()
428 if ( !(mpPDFExtOutDevData
&& mpPDFExtOutDevData
->GetIsExportTaggedPDF()) )
431 #if OSL_DEBUG_LEVEL > 1
432 sal_Int32 nCurrentStruct
= mpPDFExtOutDevData
->GetCurrentStructureElement();
435 EndStructureElements();
437 #if OSL_DEBUG_LEVEL > 1
438 nCurrentStruct
= mpPDFExtOutDevData
->GetCurrentStructureElement();
440 (void)nCurrentStruct
;
444 void const* SwDrawContact::GetPDFAnchorStructureElementKey(SdrObject
const& rObj
)
446 SwFrame
const*const pAnchorFrame(GetAnchoredObj(&rObj
)->GetAnchorFrame());
447 return pAnchorFrame
? lcl_GetKeyFromFrame(*pAnchorFrame
) : nullptr;
450 bool SwTaggedPDFHelper::CheckReopenTag()
453 void const* pReopenKey(nullptr);
454 bool bContinue
= false; // in some cases we just have to reopen a tag without early returning
458 const SwFrame
& rFrame
= mpFrameInfo
->mrFrame
;
459 const SwFrame
* pKeyFrame
= nullptr;
461 // Reopen an existing structure element if
462 // - rFrame is not the first page frame (reopen Document tag)
463 // - rFrame is a follow frame (reopen Master tag)
464 // - rFrame is a fly frame anchored at content (reopen Anchor paragraph tag)
465 // - rFrame is a fly frame anchored at page (reopen Document tag)
466 // - rFrame is a follow flow row (reopen TableRow tag)
467 // - rFrame is a cell frame in a follow flow row (reopen TableData tag)
468 if ( ( rFrame
.IsPageFrame() && static_cast<const SwPageFrame
&>(rFrame
).GetPrev() ) ||
469 ( rFrame
.IsFlowFrame() && SwFlowFrame::CastFlowFrame(&rFrame
)->IsFollow() ) ||
470 (rFrame
.IsFootnoteFrame() && static_cast<SwFootnoteFrame
const&>(rFrame
).GetMaster()) ||
471 ( rFrame
.IsRowFrame() && rFrame
.IsInFollowFlowRow() ) ||
472 ( rFrame
.IsCellFrame() && const_cast<SwFrame
&>(rFrame
).GetPrevCellLeaf() ) )
476 else if (rFrame
.IsFlyFrame() && !mpFrameInfo
->m_isLink
)
478 const SwFormatAnchor
& rAnchor
=
479 static_cast<const SwFlyFrame
*>(&rFrame
)->GetFormat()->GetAnchor();
480 if ((RndStdIds::FLY_AT_PARA
== rAnchor
.GetAnchorId()) ||
481 (RndStdIds::FLY_AT_CHAR
== rAnchor
.GetAnchorId()) ||
482 (RndStdIds::FLY_AT_PAGE
== rAnchor
.GetAnchorId()))
484 pKeyFrame
= static_cast<const SwFlyFrame
&>(rFrame
).GetAnchorFrame();
491 void const*const pKey
= lcl_GetKeyFromFrame(*pKeyFrame
);
492 FrameTagSet
& rFrameTagSet(mpPDFExtOutDevData
->GetSwPDFState()->m_FrameTagSet
);
493 if (rFrameTagSet
.find(pKey
) != rFrameTagSet
.end()
494 || rFrame
.IsFlyFrame()) // for hell layer flys
503 // note: it would be possible to get rid of the SetCurrentStructureElement()
504 // - which is quite ugly - for most cases by recreating the parents until the
505 // current ancestor, but there are special cases cell frame rowspan > 1 follow
506 // and footnote frame follow where the parent of the follow is different from
507 // the parent of the first one, and so in PDFExtOutDevData the wrong parent
508 // would be restored and used for next elements.
509 m_nRestoreCurrentTag
= mpPDFExtOutDevData
->GetCurrentStructureElement();
510 sal_Int32
const id
= mpPDFExtOutDevData
->EnsureStructureElement(pReopenKey
);
511 mpPDFExtOutDevData
->SetCurrentStructureElement(id
);
516 return bRet
&& !bContinue
;
519 void SwTaggedPDFHelper::CheckRestoreTag() const
521 if ( m_nRestoreCurrentTag
!= -1 )
523 mpPDFExtOutDevData
->SetCurrentStructureElement( m_nRestoreCurrentTag
);
525 #if OSL_DEBUG_LEVEL > 1
526 aStructStack
.pop_back();
531 void SwTaggedPDFHelper::OpenTagImpl(void const*const pKey
)
533 sal_Int32
const id
= mpPDFExtOutDevData
->EnsureStructureElement(pKey
);
534 mpPDFExtOutDevData
->BeginStructureElement(id
);
535 ++m_nEndStructureElement
;
537 #if OSL_DEBUG_LEVEL > 1
538 aStructStack
.push_back( 99 );
542 sal_Int32
SwTaggedPDFHelper::BeginTagImpl(void const*const pKey
,
543 vcl::PDFWriter::StructElement
const eType
, const OUString
& rString
)
546 const sal_Int32 nId
= mpPDFExtOutDevData
->EnsureStructureElement(pKey
);
547 mpPDFExtOutDevData
->InitStructureElement(nId
, eType
, rString
);
548 mpPDFExtOutDevData
->BeginStructureElement(nId
);
549 ++m_nEndStructureElement
;
551 #if OSL_DEBUG_LEVEL > 1
552 aStructStack
.push_back( o3tl::narrowing
<sal_uInt16
>(eType
) );
558 void SwTaggedPDFHelper::BeginTag( vcl::PDFWriter::StructElement eType
, const OUString
& rString
)
560 void const* pKey(nullptr);
564 const SwFrame
& rFrame
= mpFrameInfo
->mrFrame
;
566 if ( ( rFrame
.IsPageFrame() && !static_cast<const SwPageFrame
&>(rFrame
).GetPrev() ) ||
567 ( rFrame
.IsFlowFrame() && !SwFlowFrame::CastFlowFrame(&rFrame
)->IsFollow() && SwFlowFrame::CastFlowFrame(&rFrame
)->HasFollow() ) ||
568 rFrame
.IsSctFrame() || // all of them, so that opening parent sections works
569 ( rFrame
.IsTextFrame() && rFrame
.GetDrawObjs() ) ||
570 (rFrame
.IsFootnoteFrame() && static_cast<SwFootnoteFrame
const&>(rFrame
).GetFollow()) ||
571 ( rFrame
.IsRowFrame() && rFrame
.IsInSplitTableRow() ) ||
572 ( rFrame
.IsCellFrame() && const_cast<SwFrame
&>(rFrame
).GetNextCellLeaf() ) )
574 pKey
= lcl_GetKeyFromFrame(rFrame
);
578 FrameTagSet
& rFrameTagSet(mpPDFExtOutDevData
->GetSwPDFState()->m_FrameTagSet
);
579 assert(rFrameTagSet
.find(pKey
) == rFrameTagSet
.end());
580 rFrameTagSet
.emplace(pKey
);
585 sal_Int32
const nId
= BeginTagImpl(pKey
, eType
, rString
);
587 // Store the id of the current structure element if
588 // - it is a list structure element
589 // - it is a list body element with children
590 // - rFrame is the first page frame
591 // - rFrame is a master frame
592 // - rFrame has objects anchored to it
593 // - rFrame is a row frame or cell frame in a split table row
597 const SwTextFrame
& rTextFrame
= mpNumInfo
->mrFrame
;
598 SwTextNode
const*const pTextNd
= rTextFrame
.GetTextNodeForParaProps();
599 const SwNodeNum
* pNodeNum
= pTextNd
->GetNum(rTextFrame
.getRootFrame());
601 if ( vcl::PDFWriter::List
== eType
)
603 NumListIdMap
& rNumListIdMap(mpPDFExtOutDevData
->GetSwPDFState()->m_NumListIdMap
);
604 rNumListIdMap
[ pNodeNum
] = nId
;
606 else if ( vcl::PDFWriter::LIBody
== eType
)
608 NumListBodyIdMap
& rNumListBodyIdMap(mpPDFExtOutDevData
->GetSwPDFState()->m_NumListBodyIdMap
);
609 rNumListBodyIdMap
[ pNodeNum
] = nId
;
613 SetAttributes( eType
);
616 void SwTaggedPDFHelper::EndTag()
618 mpPDFExtOutDevData
->EndStructureElement();
620 #if OSL_DEBUG_LEVEL > 1
621 aStructStack
.pop_back();
627 // link the link annotation to the link structured element
628 void LinkLinkLink(vcl::PDFExtOutDevData
& rPDFExtOutDevData
, SwRect
const& rRect
)
630 const LinkIdMap
& rLinkIdMap(rPDFExtOutDevData
.GetSwPDFState()->m_LinkIdMap
);
631 const Point aCenter
= rRect
.Center();
632 auto aIter
= std::find_if(rLinkIdMap
.begin(), rLinkIdMap
.end(),
633 [&aCenter
](const IdMapEntry
& rEntry
) { return rEntry
.first
.Contains(aCenter
); });
634 if (aIter
!= rLinkIdMap
.end())
636 sal_Int32 nLinkId
= (*aIter
).second
;
637 rPDFExtOutDevData
.SetStructureAttributeNumerical(vcl::PDFWriter::LinkAnnotation
, nLinkId
);
642 // Sets the attributes according to the structure type.
643 void SwTaggedPDFHelper::SetAttributes( vcl::PDFWriter::StructElement eType
)
648 * ATTRIBUTES FOR BLSE
652 vcl::PDFWriter::StructAttributeValue eVal
;
653 const SwFrame
* pFrame
= &mpFrameInfo
->mrFrame
;
654 SwRectFnSet
aRectFnSet(pFrame
);
656 bool bPlacement
= false;
657 bool bWritingMode
= false;
658 bool bSpaceBefore
= false;
659 bool bSpaceAfter
= false;
660 bool bStartIndent
= false;
661 bool bEndIndent
= false;
662 bool bTextIndent
= false;
663 bool bTextAlign
= false;
665 bool bHeight
= false;
667 bool bRowSpan
= false;
668 bool bAltText
= false;
670 // Check which attributes to set:
674 case vcl::PDFWriter::Document
:
678 case vcl::PDFWriter::Note
:
682 case vcl::PDFWriter::Table
:
694 case vcl::PDFWriter::TableRow
:
699 case vcl::PDFWriter::TableHeader
:
700 mpPDFExtOutDevData
->SetStructureAttribute(vcl::PDFWriter::Scope
, vcl::PDFWriter::Column
);
702 case vcl::PDFWriter::TableData
:
710 case vcl::PDFWriter::Caption
:
711 if (pFrame
->IsSctFrame())
716 case vcl::PDFWriter::H1
:
717 case vcl::PDFWriter::H2
:
718 case vcl::PDFWriter::H3
:
719 case vcl::PDFWriter::H4
:
720 case vcl::PDFWriter::H5
:
721 case vcl::PDFWriter::H6
:
722 case vcl::PDFWriter::Paragraph
:
723 case vcl::PDFWriter::Heading
:
724 case vcl::PDFWriter::BlockQuote
:
736 case vcl::PDFWriter::Formula
:
737 case vcl::PDFWriter::Figure
:
745 case vcl::PDFWriter::Division
:
746 if (pFrame
->IsFlyFrame()) // this can be something else too
753 case vcl::PDFWriter::NonStructElement
:
754 if (pFrame
->IsHeaderFrame() || pFrame
->IsFooterFrame())
756 // ISO 14289-1:2014, Clause: 7.8
757 mpPDFExtOutDevData
->SetStructureAttribute(vcl::PDFWriter::Type
, vcl::PDFWriter::Pagination
);
758 mpPDFExtOutDevData
->SetStructureAttribute(vcl::PDFWriter::Subtype
,
759 pFrame
->IsHeaderFrame()
760 ? vcl::PDFWriter::Header
761 : vcl::PDFWriter::Footer
);
769 // Set the attributes:
773 bool bIsFigureInline
= false;
774 if (vcl::PDFWriter::Figure
== eType
)
776 const SwFrame
* pKeyFrame
= static_cast<const SwFlyFrame
&>(*pFrame
).GetAnchorFrame();
777 if (const SwLayoutFrame
* pUpperFrame
= pKeyFrame
->GetUpper())
778 if (pUpperFrame
->GetType() == SwFrameType::Body
)
779 bIsFigureInline
= true;
782 eVal
= vcl::PDFWriter::TableHeader
== eType
|| vcl::PDFWriter::TableData
== eType
784 ? vcl::PDFWriter::Inline
785 : vcl::PDFWriter::Block
;
787 mpPDFExtOutDevData
->SetStructureAttribute( vcl::PDFWriter::Placement
, eVal
);
792 eVal
= pFrame
->IsVertical() ?
793 vcl::PDFWriter::TbRl
:
794 pFrame
->IsRightToLeft() ?
795 vcl::PDFWriter::RlTb
:
796 vcl::PDFWriter::LrTb
;
798 if ( vcl::PDFWriter::LrTb
!= eVal
)
799 mpPDFExtOutDevData
->SetStructureAttribute( vcl::PDFWriter::WritingMode
, eVal
);
804 nVal
= aRectFnSet
.GetTopMargin(*pFrame
);
806 mpPDFExtOutDevData
->SetStructureAttributeNumerical( vcl::PDFWriter::SpaceBefore
, nVal
);
811 nVal
= aRectFnSet
.GetBottomMargin(*pFrame
);
813 mpPDFExtOutDevData
->SetStructureAttributeNumerical( vcl::PDFWriter::SpaceAfter
, nVal
);
818 nVal
= aRectFnSet
.GetLeftMargin(*pFrame
);
820 mpPDFExtOutDevData
->SetStructureAttributeNumerical( vcl::PDFWriter::StartIndent
, nVal
);
825 nVal
= aRectFnSet
.GetRightMargin(*pFrame
);
827 mpPDFExtOutDevData
->SetStructureAttributeNumerical( vcl::PDFWriter::EndIndent
, nVal
);
832 OSL_ENSURE( pFrame
->IsTextFrame(), "Frame type <-> tag attribute mismatch" );
833 const SvxFirstLineIndentItem
& rFirstLine(
834 static_cast<const SwTextFrame
*>(pFrame
)->GetTextNodeForParaProps()->GetSwAttrSet().GetFirstLineIndent());
835 nVal
= rFirstLine
.ResolveTextFirstLineOffset({});
837 mpPDFExtOutDevData
->SetStructureAttributeNumerical( vcl::PDFWriter::TextIndent
, nVal
);
842 OSL_ENSURE( pFrame
->IsTextFrame(), "Frame type <-> tag attribute mismatch" );
843 const SwAttrSet
& aSet
= static_cast<const SwTextFrame
*>(pFrame
)->GetTextNodeForParaProps()->GetSwAttrSet();
844 const SvxAdjust nAdjust
= aSet
.GetAdjust().GetAdjust();
845 if ( SvxAdjust::Block
== nAdjust
|| SvxAdjust::Center
== nAdjust
||
846 ( (pFrame
->IsRightToLeft() && SvxAdjust::Left
== nAdjust
) ||
847 (!pFrame
->IsRightToLeft() && SvxAdjust::Right
== nAdjust
) ) )
849 eVal
= SvxAdjust::Block
== nAdjust
?
850 vcl::PDFWriter::Justify
:
851 SvxAdjust::Center
== nAdjust
?
852 vcl::PDFWriter::Center
:
855 mpPDFExtOutDevData
->SetStructureAttribute( vcl::PDFWriter::TextAlign
, eVal
);
859 // ISO 14289-1:2014, Clause: 7.3
860 // ISO 14289-1:2014, Clause: 7.7
861 // For images (but not embedded objects), an ObjectInfoPrimitive2D is
862 // created, but it's not evaluated by VclMetafileProcessor2D any more;
863 // that would require producing StructureTagPrimitive2D here but that
864 // looks impossible so instead duplicate the code that sets the Alt
868 SwFlyFrameFormat
const& rFly(*static_cast<SwFlyFrame
const*>(pFrame
)->GetFormat());
870 (rFly
.GetObjTitle().isEmpty() || rFly
.GetObjDescription().isEmpty())
871 ? OUString() : u
" - "_ustr
);
872 OUString
const altText(rFly
.GetObjTitle() + sep
+ rFly
.GetObjDescription());
873 if (!altText
.isEmpty())
875 mpPDFExtOutDevData
->SetAlternateText(altText
);
881 nVal
= aRectFnSet
.GetWidth(pFrame
->getFrameArea());
882 mpPDFExtOutDevData
->SetStructureAttributeNumerical( vcl::PDFWriter::Width
, nVal
);
887 nVal
= aRectFnSet
.GetHeight(pFrame
->getFrameArea());
888 mpPDFExtOutDevData
->SetStructureAttributeNumerical( vcl::PDFWriter::Height
, nVal
);
893 // BBox only for non-split tables:
894 if ( vcl::PDFWriter::Table
!= eType
||
895 ( pFrame
->IsTabFrame() &&
896 !static_cast<const SwTabFrame
*>(pFrame
)->IsFollow() &&
897 !static_cast<const SwTabFrame
*>(pFrame
)->HasFollow() ) )
899 mpPDFExtOutDevData
->SetStructureBoundingBox(pFrame
->getFrameArea().SVRect());
905 if ( pFrame
->IsCellFrame() )
907 const SwCellFrame
* pThisCell
= static_cast<const SwCellFrame
*>(pFrame
);
908 nVal
= pThisCell
->GetTabBox()->getRowSpan();
910 mpPDFExtOutDevData
->SetStructureAttributeNumerical( vcl::PDFWriter::RowSpan
, nVal
);
912 // calculate colspan:
913 const SwTabFrame
* pTabFrame
= pThisCell
->FindTabFrame();
914 const SwTable
* pTable
= pTabFrame
->GetTable();
916 SwRectFnSet
fnRectX(pTabFrame
);
918 const TableColumnsMapEntry
& rCols(mpPDFExtOutDevData
->GetSwPDFState()->m_TableColumnsMap
[pTable
]);
920 const tools::Long nLeft
= fnRectX
.GetLeft(pThisCell
->getFrameArea());
921 const tools::Long nRight
= fnRectX
.GetRight(pThisCell
->getFrameArea());
922 const TableColumnsMapEntry::const_iterator aLeftIter
= rCols
.find( nLeft
);
923 const TableColumnsMapEntry::const_iterator aRightIter
= rCols
.find( nRight
);
925 OSL_ENSURE( aLeftIter
!= rCols
.end() && aRightIter
!= rCols
.end(), "Colspan trouble" );
926 if ( aLeftIter
!= rCols
.end() && aRightIter
!= rCols
.end() )
928 nVal
= std::distance( aLeftIter
, aRightIter
);
930 mpPDFExtOutDevData
->SetStructureAttributeNumerical( vcl::PDFWriter::ColSpan
, nVal
);
935 if (mpFrameInfo
->m_isLink
)
937 SwRect
const aRect(mpFrameInfo
->mrFrame
.getFrameArea());
938 LinkLinkLink(*mpPDFExtOutDevData
, aRect
);
943 * ATTRIBUTES FOR ILSE
945 else if ( mpPorInfo
)
947 const SwLinePortion
* pPor
= &mpPorInfo
->mrPor
;
948 const SwTextPaintInfo
& rInf
= mpPorInfo
->mrTextPainter
.GetInfo();
950 bool bActualText
= false;
951 bool bBaselineShift
= false;
952 bool bTextDecorationType
= false;
953 bool bLinkAttribute
= false;
954 bool bAnnotAttribute
= false;
955 bool bLanguage
= false;
957 // Check which attributes to set:
961 case vcl::PDFWriter::Span
:
962 case vcl::PDFWriter::Quote
:
963 case vcl::PDFWriter::Code
:
964 if( PortionType::HyphenStr
== pPor
->GetWhichPor() || PortionType::SoftHyphenStr
== pPor
->GetWhichPor() ||
965 PortionType::Hyphen
== pPor
->GetWhichPor() || PortionType::SoftHyphen
== pPor
->GetWhichPor() )
970 bTextDecorationType
=
975 case vcl::PDFWriter::Link
:
976 case vcl::PDFWriter::BibEntry
:
977 bTextDecorationType
=
983 case vcl::PDFWriter::RT
:
985 SwRubyPortion
const*const pRuby(static_cast<SwRubyPortion
const*>(pPor
));
986 vcl::PDFWriter::StructAttributeValue nAlign
= {};
987 switch (pRuby
->GetAdjustment())
989 case text::RubyAdjust_LEFT
:
990 nAlign
= vcl::PDFWriter::RStart
;
992 case text::RubyAdjust_CENTER
:
993 nAlign
= vcl::PDFWriter::RCenter
;
995 case text::RubyAdjust_RIGHT
:
996 nAlign
= vcl::PDFWriter::REnd
;
998 case text::RubyAdjust_BLOCK
:
999 nAlign
= vcl::PDFWriter::RJustify
;
1001 case text::RubyAdjust_INDENT_BLOCK
:
1002 nAlign
= vcl::PDFWriter::RDistribute
;
1008 ::std::optional
<vcl::PDFWriter::StructAttributeValue
> oPos
;
1009 switch (pRuby
->GetRubyPosition())
1011 case RubyPosition::ABOVE
:
1012 oPos
= vcl::PDFWriter::RBefore
;
1014 case RubyPosition::BELOW
:
1015 oPos
= vcl::PDFWriter::RAfter
;
1017 case RubyPosition::RIGHT
:
1018 break; // no such thing???
1020 mpPDFExtOutDevData
->SetStructureAttribute(vcl::PDFWriter::RubyAlign
, nAlign
);
1023 mpPDFExtOutDevData
->SetStructureAttribute(vcl::PDFWriter::RubyPosition
, *oPos
);
1028 case vcl::PDFWriter::Annot
:
1039 OUString aActualText
;
1040 if (pPor
->GetWhichPor() == PortionType::SoftHyphen
|| pPor
->GetWhichPor() == PortionType::Hyphen
)
1041 aActualText
= OUString(u
'\x00ad'); // soft hyphen
1043 aActualText
= rInf
.GetText().copy(sal_Int32(rInf
.GetIdx()), sal_Int32(pPor
->GetLen()));
1044 mpPDFExtOutDevData
->SetActualText( aActualText
);
1047 if ( bBaselineShift
)
1049 // TODO: Calculate correct values!
1050 nVal
= rInf
.GetFont()->GetEscapement();
1051 if ( nVal
> 0 ) nVal
= 33;
1052 else if ( nVal
< 0 ) nVal
= -33;
1056 nVal
= nVal
* pPor
->Height() / 100;
1057 mpPDFExtOutDevData
->SetStructureAttributeNumerical( vcl::PDFWriter::BaselineShift
, nVal
);
1061 if ( bTextDecorationType
)
1063 if ( LINESTYLE_NONE
!= rInf
.GetFont()->GetUnderline() )
1064 mpPDFExtOutDevData
->SetStructureAttribute( vcl::PDFWriter::TextDecorationType
, vcl::PDFWriter::Underline
);
1065 if ( LINESTYLE_NONE
!= rInf
.GetFont()->GetOverline() )
1066 mpPDFExtOutDevData
->SetStructureAttribute( vcl::PDFWriter::TextDecorationType
, vcl::PDFWriter::Overline
);
1067 if ( STRIKEOUT_NONE
!= rInf
.GetFont()->GetStrikeout() )
1068 mpPDFExtOutDevData
->SetStructureAttribute( vcl::PDFWriter::TextDecorationType
, vcl::PDFWriter::LineThrough
);
1069 if ( FontEmphasisMark::NONE
!= rInf
.GetFont()->GetEmphasisMark() )
1070 mpPDFExtOutDevData
->SetStructureAttribute( vcl::PDFWriter::TextDecorationType
, vcl::PDFWriter::Overline
);
1076 const LanguageType nCurrentLanguage
= rInf
.GetFont()->GetLanguage();
1077 const LanguageType
nDefaultLang(mpPDFExtOutDevData
->GetSwPDFState()->m_eLanguageDefault
);
1079 if ( nDefaultLang
!= nCurrentLanguage
)
1080 mpPDFExtOutDevData
->SetStructureAttributeNumerical( vcl::PDFWriter::Language
, static_cast<sal_uInt16
>(nCurrentLanguage
) );
1083 if ( bLinkAttribute
)
1086 rInf
.CalcRect( *pPor
, &aPorRect
);
1087 LinkLinkLink(*mpPDFExtOutDevData
, aPorRect
);
1090 if (bAnnotAttribute
)
1093 rInf
.CalcRect(*pPor
, &aPorRect
);
1094 const NoteIdMap
& rNoteIdMap(mpPDFExtOutDevData
->GetSwPDFState()->m_NoteIdMap
);
1095 const Point aCenter
= aPorRect
.Center();
1096 auto aIter
= std::find_if(rNoteIdMap
.begin(), rNoteIdMap
.end(),
1097 [&aCenter
](const IdMapEntry
& rEntry
)
1098 { return rEntry
.first
.Contains(aCenter
); });
1099 if (aIter
!= rNoteIdMap
.end())
1101 sal_Int32 nNoteId
= (*aIter
).second
;
1102 mpPDFExtOutDevData
->SetStructureAttributeNumerical(vcl::PDFWriter::NoteAnnotation
,
1107 else if (mpNumInfo
&& eType
== vcl::PDFWriter::List
)
1109 SwTextFrame
const& rFrame(mpNumInfo
->mrFrame
);
1110 SwTextNode
const& rNode(*rFrame
.GetTextNodeForParaProps());
1111 SwNumRule
const*const pNumRule
= rNode
.GetNumRule();
1112 assert(pNumRule
); // was required for List
1114 auto ToPDFListNumbering
= [](SvxNumberFormat
const& rFormat
) {
1115 switch (rFormat
.GetNumberingType())
1117 case css::style::NumberingType::CHARS_UPPER_LETTER
:
1118 return vcl::PDFWriter::UpperAlpha
;
1119 case css::style::NumberingType::CHARS_LOWER_LETTER
:
1120 return vcl::PDFWriter::LowerAlpha
;
1121 case css::style::NumberingType::ROMAN_UPPER
:
1122 return vcl::PDFWriter::UpperRoman
;
1123 case css::style::NumberingType::ROMAN_LOWER
:
1124 return vcl::PDFWriter::LowerRoman
;
1125 case css::style::NumberingType::ARABIC
:
1126 return vcl::PDFWriter::Decimal
;
1127 case css::style::NumberingType::CHAR_SPECIAL
:
1128 switch (rFormat
.GetBulletChar())
1130 case u
'\u2022': case u
'\uE12C': case u
'\uE01E': case u
'\uE437':
1131 return vcl::PDFWriter::Disc
;
1132 case u
'\u2218': case u
'\u25CB': case u
'\u25E6':
1133 return vcl::PDFWriter::Circle
;
1134 case u
'\u25A0': case u
'\u25AA': case u
'\uE00A':
1135 return vcl::PDFWriter::Square
;
1137 return vcl::PDFWriter::NONE
;
1139 default: // the other 50 types
1140 return vcl::PDFWriter::NONE
;
1144 // Note: for every level, BeginNumberedListStructureElements() produces
1145 // a separate List element, so even though in PDF this is limited to
1146 // the whole List we can just export the current level here.
1147 vcl::PDFWriter::StructAttributeValue
const value(
1148 ToPDFListNumbering(pNumRule
->Get(rNode
.GetActualListLevel())));
1149 // ISO 14289-1:2014, Clause: 7.6
1150 mpPDFExtOutDevData
->SetStructureAttribute(vcl::PDFWriter::ListNumbering
, value
);
1154 void SwTaggedPDFHelper::BeginNumberedListStructureElements()
1156 OSL_ENSURE( mpNumInfo
, "List without mpNumInfo?" );
1160 const SwFrame
& rFrame
= mpNumInfo
->mrFrame
;
1161 assert(rFrame
.IsTextFrame());
1162 const SwTextFrame
& rTextFrame
= static_cast<const SwTextFrame
&>(rFrame
);
1164 // Lowers of NonStructureElements should not be considered:
1165 if (lcl_IsInNonStructEnv(rTextFrame
))
1168 // do it for the first one in the follow chain that has content
1169 for (SwFlowFrame
const* pPrecede
= rTextFrame
.GetPrecede(); pPrecede
; pPrecede
= pPrecede
->GetPrecede())
1171 SwTextFrame
const*const pText(static_cast<SwTextFrame
const*>(pPrecede
));
1172 if (!pText
->HasPara() || pText
->GetPara()->HasContentPortions())
1178 const SwTextNode
*const pTextNd
= rTextFrame
.GetTextNodeForParaProps();
1179 const SwNumRule
* pNumRule
= pTextNd
->GetNumRule();
1180 const SwNodeNum
* pNodeNum
= pTextNd
->GetNum(rTextFrame
.getRootFrame());
1182 const bool bNumbered
= !pTextNd
->IsOutline() && pNodeNum
&& pNodeNum
->GetParent() && pNumRule
;
1184 // Check, if we have to reopen a list or a list body:
1186 // Paragraph is numbered/bulleted
1190 const SwNumberTreeNode
* pParent
= pNodeNum
->GetParent();
1191 const bool bSameNumbering
= lcl_HasPreviousParaSameNumRule(rTextFrame
, *pTextNd
);
1193 // Second condition: current numbering is not 'interrupted'
1194 if ( bSameNumbering
)
1196 sal_Int32 nReopenTag
= -1;
1199 // 1. We have to reopen an existing list body tag:
1200 // - If the current node is either the first child of its parent
1201 // and its level > 1 or
1202 // - Numbering should restart at the current node and its level > 1
1203 // - The current item has no label
1204 const bool bNewSubListStart
= pParent
->GetParent() && (pParent
->IsFirst( pNodeNum
) || pTextNd
->IsListRestart() );
1205 const bool bNoLabel
= !pTextNd
->IsCountedInList() && !pTextNd
->IsListRestart();
1206 if ( bNewSubListStart
|| bNoLabel
)
1208 // Fine, we try to reopen the appropriate list body
1209 NumListBodyIdMap
& rNumListBodyIdMap(mpPDFExtOutDevData
->GetSwPDFState()->m_NumListBodyIdMap
);
1211 if ( bNewSubListStart
)
1213 // The list body tag associated with the parent has to be reopened
1214 // to start a new list inside the list body
1215 NumListBodyIdMap::const_iterator aIter
;
1218 aIter
= rNumListBodyIdMap
.find( pParent
);
1219 while ( aIter
== rNumListBodyIdMap
.end() && nullptr != ( pParent
= pParent
->GetParent() ) );
1221 if ( aIter
!= rNumListBodyIdMap
.end() )
1222 nReopenTag
= (*aIter
).second
;
1224 else // if(bNoLabel)
1226 // The list body tag of a 'counted' predecessor has to be reopened
1227 const SwNumberTreeNode
* pPrevious
= pNodeNum
->GetPred(true);
1230 if ( pPrevious
->IsCounted())
1232 // get id of list body tag
1233 const NumListBodyIdMap::const_iterator aIter
= rNumListBodyIdMap
.find( pPrevious
);
1234 if ( aIter
!= rNumListBodyIdMap
.end() )
1236 nReopenTag
= (*aIter
).second
;
1240 pPrevious
= pPrevious
->GetPred(true);
1244 // 2. We have to reopen an existing list tag:
1245 else if ( !pParent
->IsFirst( pNodeNum
) && !pTextNd
->IsListRestart() )
1247 // any other than the first node in a list level has to reopen the current
1248 // list. The current list is associated in a map with the first child of the list:
1249 NumListIdMap
& rNumListIdMap(mpPDFExtOutDevData
->GetSwPDFState()->m_NumListIdMap
);
1251 // Search backwards and check if any of the previous nodes has a list associated with it:
1252 const SwNumberTreeNode
* pPrevious
= pNodeNum
->GetPred(true);
1255 // get id of list tag
1256 const NumListIdMap::const_iterator aIter
= rNumListIdMap
.find( pPrevious
);
1257 if ( aIter
!= rNumListIdMap
.end() )
1259 nReopenTag
= (*aIter
).second
;
1263 pPrevious
= pPrevious
->GetPred(true);
1267 if ( -1 != nReopenTag
)
1269 m_nRestoreCurrentTag
= mpPDFExtOutDevData
->GetCurrentStructureElement();
1270 mpPDFExtOutDevData
->SetCurrentStructureElement( nReopenTag
);
1272 #if OSL_DEBUG_LEVEL > 1
1273 aStructStack
.push_back( 99 );
1279 // clear list maps in case a list has been interrupted
1280 NumListIdMap
& rNumListIdMap(mpPDFExtOutDevData
->GetSwPDFState()->m_NumListIdMap
);
1281 rNumListIdMap
.clear();
1282 NumListBodyIdMap
& rNumListBodyIdMap(mpPDFExtOutDevData
->GetSwPDFState()->m_NumListBodyIdMap
);
1283 rNumListBodyIdMap
.clear();
1287 const bool bNewListTag
= (pNodeNum
->GetParent()->IsFirst( pNodeNum
) || pTextNd
->IsListRestart() || !bSameNumbering
);
1288 const bool bNewItemTag
= bNewListTag
|| pTextNd
->IsCountedInList(); // If the text node is not counted, we do not start a new list item:
1291 BeginTag( vcl::PDFWriter::List
, aListString
);
1295 BeginTag( vcl::PDFWriter::ListItem
, aListItemString
);
1296 assert(rTextFrame
.GetPara());
1297 // check whether to open LBody now or delay until after Lbl
1298 if (!rTextFrame
.GetPara()->HasNumberingPortion(SwParaPortion::OnlyNumbering
))
1300 BeginTag(vcl::PDFWriter::LIBody
, aListBodyString
);
1305 void SwTaggedPDFHelper::BeginBlockStructureElements()
1307 const SwFrame
* pFrame
= &mpFrameInfo
->mrFrame
;
1309 // Lowers of NonStructureElements should not be considered:
1311 if (lcl_IsInNonStructEnv(*pFrame
) && !pFrame
->IsFlyFrame())
1314 // Check if we have to reopen an existing structure element.
1315 // This has to be done e.g., if pFrame is a follow frame.
1316 if ( CheckReopenTag() )
1319 sal_uInt16 nPDFType
= USHRT_MAX
;
1322 switch ( pFrame
->GetType() )
1328 case SwFrameType::Page
:
1330 // Document: Document
1332 nPDFType
= vcl::PDFWriter::Document
;
1333 aPDFType
= aDocumentString
;
1336 case SwFrameType::Header
:
1337 case SwFrameType::Footer
:
1339 // Header, Footer: NonStructElement
1341 nPDFType
= vcl::PDFWriter::NonStructElement
;
1344 case SwFrameType::FtnCont
:
1346 // Footnote container: Division
1348 nPDFType
= vcl::PDFWriter::Division
;
1349 aPDFType
= aDivString
;
1352 case SwFrameType::Ftn
:
1354 // Footnote frame: Note
1356 // Note: vcl::PDFWriter::Note is actually a ILSE. Nevertheless
1357 // we treat it like a grouping element!
1358 nPDFType
= vcl::PDFWriter::Note
;
1359 aPDFType
= aNoteString
;
1362 case SwFrameType::Section
:
1364 // Section: TOX, Index, or Sect
1367 const SwSection
* pSection
=
1368 static_cast<const SwSectionFrame
*>(pFrame
)->GetSection();
1370 // open all parent sections, so that the SEs of sections
1371 // are nested in the same way as their SwSectionNodes
1372 std::vector
<SwSection
const*> parents
;
1373 for (SwSection
const* pParent
= pSection
->GetParent();
1374 pParent
!= nullptr; pParent
= pParent
->GetParent())
1376 parents
.push_back(pParent
);
1378 for (auto it
= parents
.rbegin(); it
!= parents
.rend(); ++it
)
1380 // key is the SwSection - see lcl_GetKeyFromFrame()
1384 FrameTagSet
& rFrameTagSet(mpPDFExtOutDevData
->GetSwPDFState()->m_FrameTagSet
);
1385 if (rFrameTagSet
.find(pSection
) != rFrameTagSet
.end())
1387 // special case: section may have *multiple* master frames,
1388 // when it is interrupted by nested section - reopen!
1389 OpenTagImpl(pSection
);
1392 else if (SectionType::ToxHeader
== pSection
->GetType())
1394 nPDFType
= vcl::PDFWriter::Caption
;
1395 aPDFType
= aCaptionString
;
1397 else if (SectionType::ToxContent
== pSection
->GetType())
1399 const SwTOXBase
* pTOXBase
= pSection
->GetTOXBase();
1402 if ( TOX_INDEX
== pTOXBase
->GetType() )
1404 nPDFType
= vcl::PDFWriter::Index
;
1405 aPDFType
= aIndexString
;
1409 nPDFType
= vcl::PDFWriter::TOC
;
1410 aPDFType
= aTOCString
;
1414 else if ( SectionType::Content
== pSection
->GetType() )
1416 nPDFType
= vcl::PDFWriter::Section
;
1417 aPDFType
= aSectString
;
1423 * BLOCK-LEVEL STRUCTURE ELEMENTS
1426 case SwFrameType::Txt
:
1428 SwTextFrame
const& rTextFrame(*static_cast<const SwTextFrame
*>(pFrame
));
1429 const SwTextNode
*const pTextNd(rTextFrame
.GetTextNodeForParaProps());
1431 // lazy open LBody after Lbl
1432 if (!pTextNd
->IsOutline()
1433 && rTextFrame
.GetPara()->HasNumberingPortion(SwParaPortion::OnlyNumbering
))
1435 sal_Int32
const nId
= BeginTagImpl(nullptr, vcl::PDFWriter::LIBody
, aListBodyString
);
1436 SwNodeNum
const*const pNodeNum(pTextNd
->GetNum(rTextFrame
.getRootFrame()));
1437 NumListBodyIdMap
& rNumListBodyIdMap(mpPDFExtOutDevData
->GetSwPDFState()->m_NumListBodyIdMap
);
1438 rNumListBodyIdMap
[ pNodeNum
] = nId
;
1441 const SwFormat
* pTextFormat
= pTextNd
->GetFormatColl();
1442 const SwFormat
* pParentTextFormat
= pTextFormat
? pTextFormat
->DerivedFrom() : nullptr;
1444 OUString sStyleName
;
1445 OUString sParentStyleName
;
1448 SwStyleNameMapper::FillProgName( pTextFormat
->GetName(), sStyleName
, SwGetPoolIdFromName::TxtColl
);
1449 if ( pParentTextFormat
)
1450 SwStyleNameMapper::FillProgName( pParentTextFormat
->GetName(), sParentStyleName
, SwGetPoolIdFromName::TxtColl
);
1452 // This is the default. If the paragraph could not be mapped to
1453 // any of the standard pdf tags, we write a user defined tag
1454 // <stylename> with role = P
1455 nPDFType
= vcl::PDFWriter::Paragraph
;
1456 aPDFType
= sStyleName
;
1458 // Quotations: BlockQuote
1460 if (sStyleName
== aQuotations
)
1462 nPDFType
= vcl::PDFWriter::BlockQuote
;
1463 aPDFType
= aBlockQuoteString
;
1468 else if (sStyleName
== aCaption
)
1470 nPDFType
= vcl::PDFWriter::Caption
;
1471 aPDFType
= aCaptionString
;
1476 else if (sParentStyleName
== aCaption
)
1478 nPDFType
= vcl::PDFWriter::Caption
;
1479 aPDFType
= sStyleName
+ aCaptionString
;
1484 else if (sStyleName
== aHeading
)
1486 nPDFType
= vcl::PDFWriter::Heading
;
1487 aPDFType
= aHString
;
1492 if (int nRealLevel
= pTextNd
->GetAttrOutlineLevel() - 1;
1494 && !pTextNd
->IsInRedlines()
1495 && sw::IsParaPropsNode(*pFrame
->getRootFrame(), *pTextNd
))
1500 aPDFType
= aH1String
;
1503 aPDFType
= aH2String
;
1506 aPDFType
= aH3String
;
1509 aPDFType
= aH4String
;
1512 aPDFType
= aH5String
;
1515 aPDFType
= aH6String
;
1518 aPDFType
= aH7String
;
1521 aPDFType
= aH8String
;
1524 aPDFType
= aH9String
;
1527 aPDFType
= aH10String
;
1534 // PDF/UA allows unlimited headings, but PDF only up to H6
1535 // ... and apparently the extra H7.. must be declared in
1536 // RoleMap, or veraPDF complains.
1537 nRealLevel
= std::min(nRealLevel
, 5);
1538 nPDFType
= o3tl::narrowing
<sal_uInt16
>(vcl::PDFWriter::H1
+ nRealLevel
);
1543 else if ( pFrame
->IsInSct() )
1545 const SwSectionFrame
* pSctFrame
= pFrame
->FindSctFrame();
1546 const SwSection
* pSection
= pSctFrame
->GetSection();
1548 if ( SectionType::ToxContent
== pSection
->GetType() )
1550 const SwTOXBase
* pTOXBase
= pSection
->GetTOXBase();
1551 if ( pTOXBase
&& TOX_INDEX
!= pTOXBase
->GetType() )
1553 // Special case: Open additional TOCI tag:
1554 BeginTagImpl(nullptr, vcl::PDFWriter::TOCI
, aTOCIString
);
1561 case SwFrameType::Tab
:
1565 nPDFType
= vcl::PDFWriter::Table
;
1566 aPDFType
= aTableString
;
1569 // set up table column data:
1570 const SwTabFrame
* pTabFrame
= static_cast<const SwTabFrame
*>(pFrame
);
1571 const SwTable
* pTable
= pTabFrame
->GetTable();
1573 TableColumnsMap
& rTableColumnsMap(mpPDFExtOutDevData
->GetSwPDFState()->m_TableColumnsMap
);
1574 const TableColumnsMap::const_iterator aIter
= rTableColumnsMap
.find( pTable
);
1576 if ( aIter
== rTableColumnsMap
.end() )
1578 SwRectFnSet
aRectFnSet(pTabFrame
);
1579 TableColumnsMapEntry
& rCols
= rTableColumnsMap
[ pTable
];
1581 const SwTabFrame
* pMasterFrame
= pTabFrame
->IsFollow() ? pTabFrame
->FindMaster( true ) : pTabFrame
;
1583 while ( pMasterFrame
)
1585 const SwRowFrame
* pRowFrame
= static_cast<const SwRowFrame
*>(pMasterFrame
->GetLower());
1589 const SwFrame
* pCellFrame
= pRowFrame
->GetLower();
1591 const tools::Long nLeft
= aRectFnSet
.GetLeft(pCellFrame
->getFrameArea());
1592 rCols
.insert( nLeft
);
1594 while ( pCellFrame
)
1596 const tools::Long nRight
= aRectFnSet
.GetRight(pCellFrame
->getFrameArea());
1597 rCols
.insert( nRight
);
1598 pCellFrame
= pCellFrame
->GetNext();
1600 pRowFrame
= static_cast<const SwRowFrame
*>(pRowFrame
->GetNext());
1602 pMasterFrame
= pMasterFrame
->GetFollow();
1613 case SwFrameType::Row
:
1617 if ( !static_cast<const SwRowFrame
*>(pFrame
)->IsRepeatedHeadline() )
1619 nPDFType
= vcl::PDFWriter::TableRow
;
1620 aPDFType
= aTRString
;
1624 nPDFType
= vcl::PDFWriter::NonStructElement
;
1628 case SwFrameType::Cell
:
1630 // CellFrame: TH, TD
1633 const SwTabFrame
* pTable
= static_cast<const SwCellFrame
*>(pFrame
)->FindTabFrame();
1634 if ( pTable
->IsInHeadline( *pFrame
) || lcl_IsHeadlineCell( *static_cast<const SwCellFrame
*>(pFrame
) ) )
1636 nPDFType
= vcl::PDFWriter::TableHeader
;
1637 aPDFType
= aTHString
;
1641 nPDFType
= vcl::PDFWriter::TableData
;
1642 aPDFType
= aTDString
;
1651 case SwFrameType::Fly
:
1653 // FlyFrame: Figure, Formula, Control
1654 // fly in content or fly at page
1655 if (mpFrameInfo
->m_isLink
)
1656 { // tdf#154939 additional inner link element for flys
1657 nPDFType
= vcl::PDFWriter::Link
;
1658 aPDFType
= aLinkString
;
1662 const SwFlyFrame
* pFly
= static_cast<const SwFlyFrame
*>(pFrame
);
1663 if (pFly
->GetAnchorFrame()->FindFooterOrHeader() != nullptr
1664 || pFly
->GetFrameFormat()->GetAttrSet().Get(RES_DECORATIVE
).GetValue())
1666 nPDFType
= vcl::PDFWriter::NonStructElement
;
1668 else if (pFly
->Lower() && pFly
->Lower()->IsNoTextFrame())
1670 bool bFormula
= false;
1672 const SwNoTextFrame
* pNoTextFrame
= static_cast<const SwNoTextFrame
*>(pFly
->Lower());
1673 SwOLENode
* pOLENd
= const_cast<SwOLENode
*>(pNoTextFrame
->GetNode()->GetOLENode());
1676 SwOLEObj
& aOLEObj
= pOLENd
->GetOLEObj();
1677 uno::Reference
< embed::XEmbeddedObject
> aRef
= aOLEObj
.GetOleRef();
1680 bFormula
= 0 != SotExchange::IsMath( SvGlobalName( aRef
->getClassID() ) );
1685 nPDFType
= vcl::PDFWriter::Formula
;
1686 aPDFType
= aFormulaString
;
1690 nPDFType
= vcl::PDFWriter::Figure
;
1691 aPDFType
= aFigureString
;
1696 nPDFType
= vcl::PDFWriter::Division
;
1697 aPDFType
= aDivString
;
1705 if ( USHRT_MAX
!= nPDFType
)
1707 BeginTag( static_cast<vcl::PDFWriter::StructElement
>(nPDFType
), aPDFType
);
1711 void SwTaggedPDFHelper::EndStructureElements()
1713 if (mpFrameInfo
!= nullptr)
1715 if (mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentSpan
)
1716 { // close span at end of paragraph
1717 mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentSpan
.reset();
1718 ++m_nEndStructureElement
;
1720 if (mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentLink
)
1721 { // close link at end of paragraph
1722 mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentLink
.reset();
1723 ++m_nEndStructureElement
;
1727 while ( m_nEndStructureElement
> 0 )
1730 --m_nEndStructureElement
;
1736 void SwTaggedPDFHelper::EndCurrentLink(OutputDevice
const& rOut
)
1738 vcl::PDFExtOutDevData
*const pPDFExtOutDevData(
1739 dynamic_cast<vcl::PDFExtOutDevData
*>(rOut
.GetExtOutDevData()));
1740 if (pPDFExtOutDevData
&& pPDFExtOutDevData
->GetSwPDFState()->m_oCurrentLink
)
1742 pPDFExtOutDevData
->GetSwPDFState()->m_oCurrentLink
.reset();
1743 pPDFExtOutDevData
->EndStructureElement();
1744 #if OSL_DEBUG_LEVEL > 1
1745 aStructStack
.pop_back();
1750 void SwTaggedPDFHelper::EndCurrentAll()
1752 if (mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentSpan
)
1754 mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentSpan
.reset();
1756 if (mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentLink
)
1758 mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentLink
.reset();
1762 void SwTaggedPDFHelper::EndCurrentSpan()
1764 mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentSpan
.reset();
1765 EndTag(); // close span
1768 void SwTaggedPDFHelper::CreateCurrentSpan(
1769 SwTextPaintInfo
const& rInf
, OUString
const& rStyleName
)
1771 assert(!mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentSpan
);
1772 mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentSpan
.emplace(
1773 SwEnhancedPDFState::Span
{
1774 rInf
.GetFont()->GetUnderline(),
1775 rInf
.GetFont()->GetOverline(),
1776 rInf
.GetFont()->GetStrikeout(),
1777 rInf
.GetFont()->GetEmphasisMark(),
1778 rInf
.GetFont()->GetEscapement(),
1779 rInf
.GetFont()->GetActual(),
1780 rInf
.GetFont()->GetLanguage(),
1782 // leave it open to let next portion decide to merge or close
1783 --m_nEndStructureElement
;
1786 bool SwTaggedPDFHelper::CheckContinueSpan(
1787 SwTextPaintInfo
const& rInf
, std::u16string_view
const rStyleName
,
1788 SwTextAttr
const*const pInetFormatAttr
)
1790 // for now, don't create span inside of link - this should be very rare
1791 // situation and it looks complicated to implement.
1792 assert(!mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentSpan
1793 || !mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentLink
);
1794 if (mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentLink
)
1796 if (pInetFormatAttr
&& pInetFormatAttr
== *mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentLink
)
1802 mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentLink
.reset();
1807 if (mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentSpan
&& pInetFormatAttr
)
1813 if (!mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentSpan
)
1816 SwEnhancedPDFState::Span
const& rCurrent(*mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentSpan
);
1818 bool const ret(rCurrent
.eUnderline
== rInf
.GetFont()->GetUnderline()
1819 && rCurrent
.eOverline
== rInf
.GetFont()->GetOverline()
1820 && rCurrent
.eStrikeout
== rInf
.GetFont()->GetStrikeout()
1821 && rCurrent
.eFontEmphasis
== rInf
.GetFont()->GetEmphasisMark()
1822 && rCurrent
.nEscapement
== rInf
.GetFont()->GetEscapement()
1823 && rCurrent
.nScript
== rInf
.GetFont()->GetActual()
1824 && rCurrent
.nLang
== rInf
.GetFont()->GetLanguage()
1825 && rCurrent
.StyleName
== rStyleName
);
1833 void SwTaggedPDFHelper::BeginInlineStructureElements()
1835 const SwLinePortion
* pPor
= &mpPorInfo
->mrPor
;
1836 const SwTextPaintInfo
& rInf
= mpPorInfo
->mrTextPainter
.GetInfo();
1837 const SwTextFrame
* pFrame
= rInf
.GetTextFrame();
1839 // Lowers of NonStructureElements should not be considered:
1841 if ( lcl_IsInNonStructEnv( *pFrame
) )
1844 std::pair
<SwTextNode
const*, sal_Int32
> const pos(
1845 pFrame
->MapViewToModel(rInf
.GetIdx()));
1846 SwTextAttr
const*const pInetFormatAttr
=
1847 pos
.first
->GetTextAttrAt(pos
.second
, RES_TXTATR_INETFMT
);
1849 OUString sStyleName
;
1850 if (!pInetFormatAttr
)
1852 std::vector
<SwTextAttr
*> const charAttrs(
1853 pos
.first
->GetTextAttrsAt(pos
.second
, RES_TXTATR_CHARFMT
));
1854 // TODO: handle more than 1 char style?
1855 const SwCharFormat
* pCharFormat
= (charAttrs
.size())
1856 ? (*charAttrs
.begin())->GetCharFormat().GetCharFormat() : nullptr;
1858 SwStyleNameMapper::FillProgName( pCharFormat
->GetName(), sStyleName
, SwGetPoolIdFromName::TxtColl
);
1861 // note: ILSE may be nested, so only end the span if needed to start new one
1862 bool const isContinueSpan(CheckContinueSpan(rInf
, sStyleName
, pInetFormatAttr
));
1864 sal_uInt16 nPDFType
= USHRT_MAX
;
1867 switch ( pPor
->GetWhichPor() )
1869 case PortionType::PostIts
:
1870 if (!mpPDFExtOutDevData
->GetSwPDFState()->m_NoteIdMap
.empty())
1872 nPDFType
= vcl::PDFWriter::Annot
;
1873 aPDFType
= aAnnotString
;
1877 case PortionType::Hyphen
:
1878 case PortionType::SoftHyphen
:
1879 // Check for alternative spelling:
1880 case PortionType::HyphenStr
:
1881 case PortionType::SoftHyphenStr
:
1882 nPDFType
= vcl::PDFWriter::Span
;
1883 aPDFType
= aSpanString
;
1886 case PortionType::Fly
:
1887 // if a link is split by a fly overlap, then there will be multiple
1888 // annotations for the link, and hence there must be multiple SEs,
1889 // so every annotation has its own SE.
1890 if (mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentLink
)
1892 mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentLink
.reset();
1897 case PortionType::Lay
:
1898 case PortionType::Text
:
1899 case PortionType::Para
:
1902 if( pInetFormatAttr
)
1904 if (!isContinueSpan
)
1906 nPDFType
= vcl::PDFWriter::Link
;
1907 aPDFType
= aLinkString
;
1908 assert(!mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentLink
);
1909 mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentLink
.emplace(pInetFormatAttr
);
1910 // leave it open to let next portion decide to merge or close
1911 --m_nEndStructureElement
;
1914 // Check for Quote/Code character style:
1915 else if (sStyleName
== aQuotation
)
1917 if (!isContinueSpan
)
1919 nPDFType
= vcl::PDFWriter::Quote
;
1920 aPDFType
= aQuoteString
;
1921 CreateCurrentSpan(rInf
, sStyleName
);
1924 else if (sStyleName
== aSourceText
)
1926 if (!isContinueSpan
)
1928 nPDFType
= vcl::PDFWriter::Code
;
1929 aPDFType
= aCodeString
;
1930 CreateCurrentSpan(rInf
, sStyleName
);
1933 else if (!isContinueSpan
)
1935 const LanguageType nCurrentLanguage
= rInf
.GetFont()->GetLanguage();
1936 const SwFontScript nFont
= rInf
.GetFont()->GetActual();
1937 const LanguageType
nDefaultLang(mpPDFExtOutDevData
->GetSwPDFState()->m_eLanguageDefault
);
1939 if ( LINESTYLE_NONE
!= rInf
.GetFont()->GetUnderline() ||
1940 LINESTYLE_NONE
!= rInf
.GetFont()->GetOverline() ||
1941 STRIKEOUT_NONE
!= rInf
.GetFont()->GetStrikeout() ||
1942 FontEmphasisMark::NONE
!= rInf
.GetFont()->GetEmphasisMark() ||
1943 0 != rInf
.GetFont()->GetEscapement() ||
1944 SwFontScript::Latin
!= nFont
||
1945 nCurrentLanguage
!= nDefaultLang
||
1946 !sStyleName
.isEmpty())
1948 nPDFType
= vcl::PDFWriter::Span
;
1949 if (!sStyleName
.isEmpty())
1950 aPDFType
= sStyleName
;
1952 aPDFType
= aSpanString
;
1953 CreateCurrentSpan(rInf
, sStyleName
);
1959 case PortionType::Footnote
:
1960 nPDFType
= vcl::PDFWriter::Link
;
1961 aPDFType
= aLinkString
;
1964 case PortionType::Field
:
1966 // check field type:
1967 TextFrameIndex
const nIdx
= static_cast<const SwFieldPortion
*>(pPor
)->IsFollow()
1968 ? rInf
.GetIdx() - TextFrameIndex(1)
1970 const SwTextAttr
* pHint
= mpPorInfo
->mrTextPainter
.GetAttr( nIdx
);
1971 if ( pHint
&& RES_TXTATR_FIELD
== pHint
->Which() )
1973 const SwField
* pField
= pHint
->GetFormatField().GetField();
1974 if ( SwFieldIds::GetRef
== pField
->Which() )
1976 nPDFType
= vcl::PDFWriter::Link
;
1977 aPDFType
= aLinkString
;
1979 else if ( SwFieldIds::TableOfAuthorities
== pField
->Which() )
1981 nPDFType
= vcl::PDFWriter::BibEntry
;
1982 aPDFType
= aBibEntryString
;
1988 case PortionType::Multi
:
1990 SwMultiPortion
const*const pMulti(static_cast<SwMultiPortion
const*>(pPor
));
1991 if (pMulti
->IsRuby())
1994 switch (mpPorInfo
->m_Mode
)
1997 nPDFType
= vcl::PDFWriter::Ruby
;
2001 nPDFType
= vcl::PDFWriter::RT
;
2005 nPDFType
= vcl::PDFWriter::RB
;
2010 else if (pMulti
->IsDouble())
2013 switch (mpPorInfo
->m_Mode
)
2016 nPDFType
= vcl::PDFWriter::Warichu
;
2017 aPDFType
= "Warichu";
2020 nPDFType
= vcl::PDFWriter::WP
;
2024 nPDFType
= vcl::PDFWriter::WT
;
2033 // for FootnoteNum, is called twice: outer generates Lbl, inner Link
2034 case PortionType::FootnoteNum
:
2035 assert(!isContinueSpan
); // is at start
2036 if (mpPorInfo
->m_Mode
== 0)
2037 { // tdf#152218 link both directions
2038 nPDFType
= vcl::PDFWriter::Link
;
2039 aPDFType
= aLinkString
;
2043 case PortionType::Number
:
2044 case PortionType::Bullet
:
2045 case PortionType::GrfNum
:
2046 assert(!isContinueSpan
); // is at start
2047 if (mpPorInfo
->m_Mode
== 1)
2048 { // only works for multiple lines via wrapper from PaintSwFrame
2049 nPDFType
= vcl::PDFWriter::LILabel
;
2050 aPDFType
= aListLabelString
;
2054 case PortionType::Tab
:
2055 case PortionType::TabRight
:
2056 case PortionType::TabCenter
:
2057 case PortionType::TabDecimal
:
2058 nPDFType
= vcl::PDFWriter::NonStructElement
;
2063 if ( USHRT_MAX
!= nPDFType
)
2065 BeginTag( static_cast<vcl::PDFWriter::StructElement
>(nPDFType
), aPDFType
);
2069 bool SwTaggedPDFHelper::IsExportTaggedPDF( const OutputDevice
& rOut
)
2071 vcl::PDFExtOutDevData
* pPDFExtOutDevData
= dynamic_cast< vcl::PDFExtOutDevData
*>( rOut
.GetExtOutDevData() );
2072 return pPDFExtOutDevData
&& pPDFExtOutDevData
->GetIsExportTaggedPDF();
2075 SwEnhancedPDFExportHelper::SwEnhancedPDFExportHelper( SwEditShell
& rSh
,
2077 const OUString
& rPageRange
,
2078 bool bSkipEmptyPages
,
2079 bool bEditEngineOnly
,
2080 const SwPrintData
& rPrintData
)
2083 mbSkipEmptyPages( bSkipEmptyPages
),
2084 mbEditEngineOnly( bEditEngineOnly
),
2085 mrPrintData( rPrintData
)
2087 if ( !rPageRange
.isEmpty() )
2088 mpRangeEnum
.reset( new StringRangeEnumerator( rPageRange
, 0, mrSh
.GetPageCount()-1 ) );
2090 if ( mbSkipEmptyPages
)
2092 maPageNumberMap
.resize( mrSh
.GetPageCount() );
2093 const SwPageFrame
* pCurrPage
=
2094 static_cast<const SwPageFrame
*>( mrSh
.GetLayout()->Lower() );
2095 sal_Int32 nPageNumber
= 0;
2096 for ( size_t i
= 0, n
= maPageNumberMap
.size(); i
< n
&& pCurrPage
; ++i
)
2098 if ( pCurrPage
->IsEmptyPage() )
2099 maPageNumberMap
[i
] = -1;
2101 maPageNumberMap
[i
] = nPageNumber
++;
2103 pCurrPage
= static_cast<const SwPageFrame
*>( pCurrPage
->GetNext() );
2107 #if OSL_DEBUG_LEVEL > 1
2108 aStructStack
.clear();
2111 const sal_Int16 nScript
= SvtLanguageOptions::GetI18NScriptTypeOfLanguage( GetAppLanguage() );
2112 TypedWhichId
<SvxLanguageItem
> nLangRes
= RES_CHRATR_LANGUAGE
;
2114 if ( i18n::ScriptType::ASIAN
== nScript
)
2115 nLangRes
= RES_CHRATR_CJK_LANGUAGE
;
2116 else if ( i18n::ScriptType::COMPLEX
== nScript
)
2117 nLangRes
= RES_CHRATR_CTL_LANGUAGE
;
2119 const SvxLanguageItem
& rLangItem
= mrSh
.GetDoc()->GetDefault( nLangRes
);
2120 auto const eLanguageDefault
= rLangItem
.GetLanguage();
2122 EnhancedPDFExport(eLanguageDefault
);
2125 SwEnhancedPDFExportHelper::~SwEnhancedPDFExportHelper()
2129 tools::Rectangle
SwEnhancedPDFExportHelper::SwRectToPDFRect(const SwPageFrame
* pCurrPage
,
2130 const tools::Rectangle
& rRectangle
) const
2132 if (!::sw::IsShrinkPageForPostIts(mrSh
, mrPrintData
)) // tdf#148729
2136 return MapSwRectToPDFRect(pCurrPage
, rRectangle
);
2139 double SwEnhancedPDFExportHelper::GetSwRectToPDFRectScale()
2144 tools::Rectangle
SwEnhancedPDFExportHelper::MapSwRectToPDFRect(const SwPageFrame
* pCurrPage
,
2145 const tools::Rectangle
& rRectangle
)
2147 //the page has been scaled by 75% and vertically centered, so adjust these
2148 //rectangles equivalently
2149 tools::Rectangle
aRect(rRectangle
);
2150 Size
aRectSize(aRect
.GetSize());
2151 double fScale
= GetSwRectToPDFRectScale();
2152 aRectSize
.setWidth( aRectSize
.Width() * fScale
);
2153 aRectSize
.setHeight( aRectSize
.Height() * fScale
);
2154 tools::Long nOrigHeight
= pCurrPage
->getFrameArea().Height();
2155 tools::Long nNewHeight
= nOrigHeight
*fScale
;
2156 tools::Long nShiftY
= (nOrigHeight
-nNewHeight
)/2;
2157 aRect
.SetLeft( aRect
.Left() * fScale
);
2158 aRect
.SetTop( aRect
.Top() * fScale
);
2159 aRect
.Move(0, nShiftY
);
2160 aRect
.SetSize(aRectSize
);
2164 void SwEnhancedPDFExportHelper::EnhancedPDFExport(LanguageType
const eLanguageDefault
)
2166 vcl::PDFExtOutDevData
* pPDFExtOutDevData
=
2167 dynamic_cast< vcl::PDFExtOutDevData
*>( mrOut
.GetExtOutDevData() );
2169 if ( !pPDFExtOutDevData
)
2172 // set the document locale
2174 lang::Locale
const aDocLocale( LanguageTag(eLanguageDefault
).getLocale() );
2175 pPDFExtOutDevData
->SetDocumentLocale( aDocLocale
);
2177 // Prepare the output device:
2179 mrOut
.Push( vcl::PushFlags::MAPMODE
);
2180 MapMode
aMapMode( mrOut
.GetMapMode() );
2181 aMapMode
.SetMapUnit( MapUnit::MapTwip
);
2182 mrOut
.SetMapMode( aMapMode
);
2184 // Create new cursor and lock the view:
2186 SwDoc
* pDoc
= mrSh
.GetDoc();
2187 mrSh
.SwCursorShell::Push();
2188 mrSh
.SwCursorShell::ClearMark();
2189 const bool bOldLockView
= mrSh
.IsViewLocked();
2190 mrSh
.LockView( true );
2192 if ( !mbEditEngineOnly
)
2194 assert(pPDFExtOutDevData
->GetSwPDFState() == nullptr);
2195 pPDFExtOutDevData
->SetSwPDFState(new SwEnhancedPDFState(eLanguageDefault
));
2199 if ( pPDFExtOutDevData
->GetIsExportNotes() )
2201 std::vector
<SwFormatField
*> vpFields
;
2202 mrSh
.GetFieldType(SwFieldIds::Postit
, OUString())->GatherFields(vpFields
);
2203 for(auto pFormatField
: vpFields
)
2205 const SwTextNode
* pTNd
= pFormatField
->GetTextField()->GetpTextNode();
2206 OSL_ENSURE(nullptr != pTNd
, "Enhanced pdf export - text node is missing");
2207 if(!lcl_TryMoveToNonHiddenField(mrSh
, *pTNd
, *pFormatField
))
2210 const SwRect
& rNoteRect
= mrSh
.GetCharRect();
2211 const SwPageFrame
* pCurrPage
= static_cast<const SwPageFrame
*>(mrSh
.GetLayout()->Lower());
2214 std::vector
<sal_Int32
> aNotePageNums
= CalcOutputPageNums(rNoteRect
);
2215 for (sal_Int32 aNotePageNum
: aNotePageNums
)
2218 // Use the NumberFormatter to get the date string:
2219 const SwPostItField
* pField
= static_cast<SwPostItField
*>(pFormatField
->GetField());
2220 SvNumberFormatter
* pNumFormatter
= pDoc
->GetNumberFormatter();
2221 const Date
aDateDiff(pField
->GetDate() - pNumFormatter
->GetNullDate());
2222 const sal_uLong nFormat
= pNumFormatter
->GetStandardFormat(SvNumFormatType::DATE
, pField
->GetLanguage());
2224 const Color
* pColor
;
2225 pNumFormatter
->GetOutputString(aDateDiff
.GetDate(), nFormat
, sDate
, &pColor
);
2227 vcl::pdf::PDFNote aNote
;
2228 // The title should consist of the author and the date:
2229 aNote
.maTitle
= pField
->GetPar1() + ", " + sDate
+ ", " + (pField
->GetResolved() ? SwResId(STR_RESOLVED
) : u
""_ustr
);
2230 // Guess what the contents contains...
2231 aNote
.maContents
= pField
->GetText();
2233 tools::Rectangle
aPopupRect(0, 0);
2234 SwPostItMgr
* pPostItMgr
= pDoc
->GetEditShell()->GetPostItMgr();
2235 for (auto it
= pPostItMgr
->begin(); it
!= pPostItMgr
->end(); ++it
)
2237 sw::annotation::SwAnnotationWin
* pWin
= it
->get()->mpPostIt
;
2240 const SwRect
& aAnnotRect
= pWin
->GetAnchorRect();
2241 if (aAnnotRect
.Contains(rNoteRect
))
2243 Point
aPt(pDoc
->GetEditShell()->GetWin()->PixelToLogic(pWin
->GetPosPixel()));
2244 Size
aSize(pDoc
->GetEditShell()->GetWin()->PixelToLogic(pWin
->GetSizePixel()));
2245 aPopupRect
= tools::Rectangle(aPt
, aSize
);
2251 tools::Rectangle
aRect(SwRectToPDFRect(pCurrPage
, rNoteRect
.SVRect()));
2252 sal_Int32 nNoteId
= pPDFExtOutDevData
->CreateNote(aRect
, aNote
, aPopupRect
, aNotePageNum
);
2254 if (mrPrintData
.GetPrintPostIts() != SwPostItMode::InMargins
)
2256 const IdMapEntry
aNoteEntry(aRect
, nNoteId
);
2257 pPDFExtOutDevData
->GetSwPDFState()->m_NoteIdMap
.push_back(aNoteEntry
);
2260 mrSh
.SwCursorShell::ClearMark();
2266 SwGetINetAttrs aArr
;
2267 mrSh
.GetINetAttrs( aArr
);
2268 for( auto &rAttr
: aArr
)
2270 SwGetINetAttr
* p
= &rAttr
;
2271 OSL_ENSURE( nullptr != p
, "Enhanced pdf export - SwGetINetAttr is missing" );
2273 const SwTextNode
* pTNd
= p
->rINetAttr
.GetpTextNode();
2274 OSL_ENSURE( nullptr != pTNd
, "Enhanced pdf export - text node is missing" );
2276 // 1. Check if the whole paragraph is hidden
2277 // 2. Move to the hyperlink
2278 // 3. Check for hidden text attribute
2279 if ( !pTNd
->IsHidden() &&
2280 mrSh
.GotoINetAttr( p
->rINetAttr
) &&
2281 !mrSh
.IsInHiddenRange(/*bSelect=*/false) )
2283 // Select the hyperlink:
2284 mrSh
.SwCursorShell::Right( 1, SwCursorSkipMode::Chars
);
2285 if ( mrSh
.SwCursorShell::SelectTextAttr( RES_TXTATR_INETFMT
, true ) )
2287 // First, we create the destination, because there may be more
2288 // than one link to this destination:
2289 OUString
aURL( INetURLObject::decode(
2290 p
->rINetAttr
.GetINetFormat().GetValue(),
2291 INetURLObject::DecodeMechanism::Unambiguous
) );
2293 // We have to distinguish between internal and real URLs
2294 const bool bInternal
= '#' == aURL
[0];
2296 // GetCursor_() is a SwShellCursor, which is derived from
2297 // SwSelPaintRects, therefore the rectangles of the current
2298 // selection can be easily obtained:
2299 // Note: We make a copy of the rectangles, because they may
2300 // be deleted again in JumpToSwMark.
2301 SwRects
const aTmp(GetCursorRectsContainingText(mrSh
));
2302 OSL_ENSURE( !aTmp
.empty(), "Enhanced pdf export - rectangles are missing" );
2303 OUString
altText(p
->rINetAttr
.GetINetFormat().GetName());
2304 if (altText
.isEmpty())
2305 altText
= mrSh
.GetSelText();
2307 const SwPageFrame
* pSelectionPage
=
2308 static_cast<const SwPageFrame
*>( mrSh
.GetLayout()->Lower() );
2310 // Create the destination for internal links:
2311 sal_Int32 nDestId
= -1;
2314 aURL
= aURL
.copy( 1 );
2315 mrSh
.SwCursorShell::ClearMark();
2316 if (! JumpToSwMark( &mrSh
, aURL
))
2318 continue; // target deleted
2321 // Destination Rectangle
2322 const SwRect
& rDestRect
= mrSh
.GetCharRect();
2324 const SwPageFrame
* pCurrPage
=
2325 static_cast<const SwPageFrame
*>( mrSh
.GetLayout()->Lower() );
2327 // Destination PageNum
2328 const sal_Int32 nDestPageNum
= CalcOutputPageNum( rDestRect
);
2330 // Destination Export
2331 if ( -1 != nDestPageNum
)
2333 tools::Rectangle
aRect(SwRectToPDFRect(pCurrPage
, rDestRect
.SVRect()));
2334 nDestId
= pPDFExtOutDevData
->CreateDest(aRect
, nDestPageNum
);
2338 if ( !bInternal
|| -1 != nDestId
)
2340 // #i44368# Links in Header/Footer
2341 const bool bHeaderFooter
= pDoc
->IsInHeaderFooter( *pTNd
);
2343 // Create links for all selected rectangles:
2344 const size_t nNumOfRects
= aTmp
.size();
2345 for ( size_t i
= 0; i
< nNumOfRects
; ++i
)
2348 const SwRect
& rLinkRect( aTmp
[ i
] );
2351 std::vector
<sal_Int32
> aLinkPageNums
= CalcOutputPageNums( rLinkRect
);
2353 for (sal_Int32 aLinkPageNum
: aLinkPageNums
)
2356 tools::Rectangle
aRect(SwRectToPDFRect(pSelectionPage
, rLinkRect
.SVRect()));
2357 const sal_Int32 nLinkId
=
2358 pPDFExtOutDevData
->CreateLink(aRect
, altText
, aLinkPageNum
);
2360 // Store link info for tagged pdf output:
2361 const IdMapEntry
aLinkEntry( rLinkRect
, nLinkId
);
2362 pPDFExtOutDevData
->GetSwPDFState()->m_LinkIdMap
.push_back(aLinkEntry
);
2364 // Connect Link and Destination:
2366 pPDFExtOutDevData
->SetLinkDest( nLinkId
, nDestId
);
2368 pPDFExtOutDevData
->SetLinkURL( nLinkId
, aURL
);
2370 // #i44368# Links in Header/Footer
2371 if ( bHeaderFooter
)
2372 MakeHeaderFooterLinks(*pPDFExtOutDevData
, *pTNd
, rLinkRect
, nDestId
, aURL
, bInternal
, altText
);
2378 mrSh
.SwCursorShell::ClearMark();
2381 // HYPERLINKS (Graphics, Frames, OLEs )
2383 for(sw::SpzFrameFormat
* pFrameFormat
: *pDoc
->GetSpzFrameFormats())
2385 const SwFormatURL
* pItem
;
2386 if ( RES_DRAWFRMFMT
!= pFrameFormat
->Which() &&
2387 GetFrameOfModify(mrSh
.GetLayout(), *pFrameFormat
, SwFrameType::Fly
) &&
2388 (pItem
= pFrameFormat
->GetAttrSet().GetItemIfSet( RES_URL
)) )
2390 const SwPageFrame
* pCurrPage
=
2391 static_cast<const SwPageFrame
*>( mrSh
.GetLayout()->Lower() );
2393 OUString
aURL( pItem
->GetURL() );
2396 const bool bInternal
= '#' == aURL
[0];
2398 // Create the destination for internal links:
2399 sal_Int32 nDestId
= -1;
2402 aURL
= aURL
.copy( 1 );
2403 mrSh
.SwCursorShell::ClearMark();
2404 if (! JumpToSwMark( &mrSh
, aURL
))
2406 continue; // target deleted
2409 // Destination Rectangle
2410 const SwRect
& rDestRect
= mrSh
.GetCharRect();
2412 pCurrPage
= static_cast<const SwPageFrame
*>( mrSh
.GetLayout()->Lower() );
2414 // Destination PageNum
2415 const sal_Int32 nDestPageNum
= CalcOutputPageNum( rDestRect
);
2417 // Destination Export
2418 if ( -1 != nDestPageNum
)
2420 tools::Rectangle
aRect(SwRectToPDFRect(pCurrPage
, rDestRect
.SVRect()));
2421 nDestId
= pPDFExtOutDevData
->CreateDest(aRect
, nDestPageNum
);
2425 if ( !bInternal
|| -1 != nDestId
)
2428 const SwRect aLinkRect
= pFrameFormat
->FindLayoutRect( false, &aNullPt
);
2429 OUString
const formatName(pFrameFormat
->GetName());
2431 std::vector
<sal_Int32
> aLinkPageNums
= CalcOutputPageNums( aLinkRect
);
2434 for (sal_Int32 aLinkPageNum
: aLinkPageNums
)
2436 tools::Rectangle
aRect(SwRectToPDFRect(pCurrPage
, aLinkRect
.SVRect()));
2437 const sal_Int32 nLinkId
=
2438 pPDFExtOutDevData
->CreateLink(aRect
, formatName
, aLinkPageNum
);
2440 // Store link info for tagged pdf output:
2441 const IdMapEntry
aLinkEntry(aLinkRect
, nLinkId
);
2442 pPDFExtOutDevData
->GetSwPDFState()->m_LinkIdMap
.push_back(aLinkEntry
);
2444 // Connect Link and Destination:
2446 pPDFExtOutDevData
->SetLinkDest( nLinkId
, nDestId
);
2448 pPDFExtOutDevData
->SetLinkURL( nLinkId
, aURL
);
2450 // #i44368# Links in Header/Footer
2451 const SwFormatAnchor
&rAnch
= pFrameFormat
->GetAnchor();
2452 if (RndStdIds::FLY_AT_PAGE
!= rAnch
.GetAnchorId())
2454 const SwNode
* pAnchorNode
= rAnch
.GetAnchorNode();
2455 if ( pAnchorNode
&& pDoc
->IsInHeaderFooter( *pAnchorNode
) )
2457 const SwTextNode
* pTNd
= pAnchorNode
->GetTextNode();
2459 MakeHeaderFooterLinks(*pPDFExtOutDevData
, *pTNd
, aLinkRect
, nDestId
, aURL
, bInternal
, formatName
);
2465 else if (pFrameFormat
->Which() == RES_DRAWFRMFMT
)
2467 // Turn media shapes into Screen annotations.
2468 if (SdrObject
* pObject
= pFrameFormat
->FindRealSdrObject())
2470 SwRect
aSnapRect(pObject
->GetSnapRect());
2471 std::vector
<sal_Int32
> aScreenPageNums
= CalcOutputPageNums(aSnapRect
);
2472 if (aScreenPageNums
.empty())
2475 uno::Reference
<drawing::XShape
> xShape(pObject
->getUnoShape(), uno::UNO_QUERY
);
2476 if (xShape
->getShapeType() == "com.sun.star.drawing.MediaShape")
2478 uno::Reference
<beans::XPropertySet
> xShapePropSet(xShape
, uno::UNO_QUERY
);
2480 xShapePropSet
->getPropertyValue(u
"Title"_ustr
) >>= title
;
2481 OUString description
;
2482 xShapePropSet
->getPropertyValue(u
"Description"_ustr
) >>= description
;
2483 OUString
const altText(title
.isEmpty()
2485 : description
.isEmpty()
2487 : OUString::Concat(title
) + OUString::Concat("\n") + OUString::Concat(description
));
2490 xShapePropSet
->getPropertyValue(u
"MediaURL"_ustr
) >>= aMediaURL
;
2491 if (!aMediaURL
.isEmpty())
2493 OUString
const mimeType(xShapePropSet
->getPropertyValue(u
"MediaMimeType"_ustr
).get
<OUString
>());
2494 const SwPageFrame
* pCurrPage
= mrSh
.GetLayout()->GetPageAtPos(aSnapRect
.Center());
2495 tools::Rectangle
aPDFRect(SwRectToPDFRect(pCurrPage
, aSnapRect
.SVRect()));
2496 for (sal_Int32 nScreenPageNum
: aScreenPageNums
)
2498 sal_Int32 nScreenId
= pPDFExtOutDevData
->CreateScreen(aPDFRect
, altText
, mimeType
, nScreenPageNum
, pObject
);
2499 if (aMediaURL
.startsWith("vnd.sun.star.Package:"))
2502 OUString aTempFileURL
;
2503 xShapePropSet
->getPropertyValue(u
"PrivateTempFileURL"_ustr
) >>= aTempFileURL
;
2504 pPDFExtOutDevData
->SetScreenStream(nScreenId
, aTempFileURL
);
2508 pPDFExtOutDevData
->SetScreenURL(nScreenId
, aMediaURL
);
2514 mrSh
.SwCursorShell::ClearMark();
2519 std::vector
<SwFormatField
*> vpFields
;
2520 mrSh
.GetFieldType( SwFieldIds::GetRef
, OUString() )->GatherFields(vpFields
);
2521 for(auto pFormatField
: vpFields
)
2523 if( pFormatField
->GetTextField() && pFormatField
->IsFieldInDoc() )
2525 const SwTextNode
* pTNd
= pFormatField
->GetTextField()->GetpTextNode();
2526 OSL_ENSURE( nullptr != pTNd
, "Enhanced pdf export - text node is missing" );
2527 if(!lcl_TryMoveToNonHiddenField(mrSh
, *pTNd
, *pFormatField
))
2529 // Select the field:
2530 mrSh
.SwCursorShell::SetMark();
2531 mrSh
.SwCursorShell::Right( 1, SwCursorSkipMode::Chars
);
2534 SwRects
const aTmp(GetCursorRectsContainingText(mrSh
));
2535 OSL_ENSURE( !aTmp
.empty(), "Enhanced pdf export - rectangles are missing" );
2537 mrSh
.SwCursorShell::ClearMark();
2539 // Destination Rectangle
2540 const SwGetRefField
* pField
= static_cast<SwGetRefField
*>(pFormatField
->GetField());
2541 const OUString
& rRefName
= pField
->GetSetRefName();
2542 mrSh
.GotoRefMark( rRefName
, pField
->GetSubType(), pField
->GetSeqNo(), pField
->GetFlags() );
2543 const SwRect
& rDestRect
= mrSh
.GetCharRect();
2545 const SwPageFrame
* pCurrPage
= static_cast<const SwPageFrame
*>( mrSh
.GetLayout()->Lower() );
2547 // Destination PageNum
2548 const sal_Int32 nDestPageNum
= CalcOutputPageNum( rDestRect
);
2550 if ( -1 != nDestPageNum
)
2552 // Destination Export
2553 tools::Rectangle
aRect(SwRectToPDFRect(pCurrPage
, rDestRect
.SVRect()));
2554 const sal_Int32 nDestId
= pPDFExtOutDevData
->CreateDest(aRect
, nDestPageNum
);
2556 // #i44368# Links in Header/Footer
2557 const bool bHeaderFooter
= pDoc
->IsInHeaderFooter( *pTNd
);
2558 OUString
const content(pField
->ExpandField(true, mrSh
.GetLayout()));
2560 // Create links for all selected rectangles:
2561 const size_t nNumOfRects
= aTmp
.size();
2562 for ( size_t i
= 0; i
< nNumOfRects
; ++i
)
2565 const SwRect
& rLinkRect( aTmp
[ i
] );
2568 std::vector
<sal_Int32
> aLinkPageNums
= CalcOutputPageNums( rLinkRect
);
2570 for (sal_Int32 aLinkPageNum
: aLinkPageNums
)
2573 aRect
= SwRectToPDFRect(pCurrPage
, rLinkRect
.SVRect());
2574 const sal_Int32 nLinkId
=
2575 pPDFExtOutDevData
->CreateLink(aRect
, content
, aLinkPageNum
);
2577 // Store link info for tagged pdf output:
2578 const IdMapEntry
aLinkEntry( rLinkRect
, nLinkId
);
2579 pPDFExtOutDevData
->GetSwPDFState()->m_LinkIdMap
.push_back(aLinkEntry
);
2581 // Connect Link and Destination:
2582 pPDFExtOutDevData
->SetLinkDest( nLinkId
, nDestId
);
2584 // #i44368# Links in Header/Footer
2585 if ( bHeaderFooter
)
2587 MakeHeaderFooterLinks(*pPDFExtOutDevData
, *pTNd
, rLinkRect
, nDestId
, u
""_ustr
, true, content
);
2593 mrSh
.SwCursorShell::ClearMark();
2596 ExportAuthorityEntryLinks();
2600 const size_t nFootnoteCount
= pDoc
->GetFootnoteIdxs().size();
2601 for ( size_t nIdx
= 0; nIdx
< nFootnoteCount
; ++nIdx
)
2603 // Set cursor to text node that contains the footnote:
2604 const SwTextFootnote
* pTextFootnote
= pDoc
->GetFootnoteIdxs()[ nIdx
];
2605 SwTextNode
& rTNd
= const_cast<SwTextNode
&>(pTextFootnote
->GetTextNode());
2607 mrSh
.GetCursor_()->GetPoint()->Assign(rTNd
, pTextFootnote
->GetStart());
2609 // 1. Check if the whole paragraph is hidden
2610 // 2. Check for hidden text attribute
2611 if (rTNd
.GetTextNode()->IsHidden() || mrSh
.IsInHiddenRange(/*bSelect=*/false)
2612 || (mrSh
.GetLayout()->IsHideRedlines()
2613 && sw::IsFootnoteDeleted(pDoc
->getIDocumentRedlineAccess(), *pTextFootnote
)))
2618 SwCursorSaveState
aSaveState( *mrSh
.GetCursor_() );
2620 // Select the footnote:
2621 mrSh
.SwCursorShell::SetMark();
2622 mrSh
.SwCursorShell::Right( 1, SwCursorSkipMode::Chars
);
2626 aTmp
.insert( aTmp
.begin(), mrSh
.SwCursorShell::GetCursor_()->begin(), mrSh
.SwCursorShell::GetCursor_()->end() );
2627 OSL_ENSURE( !aTmp
.empty(), "Enhanced pdf export - rectangles are missing" );
2629 mrSh
.GetCursor_()->RestoreSavePos();
2630 mrSh
.SwCursorShell::ClearMark();
2635 const SwRect
aLinkRect( aTmp
[ 0 ] );
2637 // Goto footnote text:
2638 if ( mrSh
.GotoFootnoteText() )
2640 // Destination Rectangle
2641 const SwRect
& rDestRect
= mrSh
.GetCharRect();
2642 const sal_Int32 nDestPageNum
= CalcOutputPageNum( rDestRect
);
2643 if ( -1 != nDestPageNum
)
2645 const SwPageFrame
* pCurrPage
= static_cast<const SwPageFrame
*>( mrSh
.GetLayout()->Lower() );
2646 // Destination PageNum
2647 tools::Rectangle aRect
= SwRectToPDFRect(pCurrPage
, rDestRect
.SVRect());
2648 // Back link rectangle calculation
2649 const SwPageFrame
* fnBodyPage
= pCurrPage
->getRootFrame()->GetPageByPageNum(nDestPageNum
+1);
2650 SwRect fnSymbolRect
;
2651 if (fnBodyPage
->IsVertical()){
2652 tools::Long fnSymbolTop
= fnBodyPage
->GetTopMargin() + fnBodyPage
->getFrameArea().Top();
2653 tools::Long symbolHeight
= rDestRect
.Top() - fnSymbolTop
;
2654 fnSymbolRect
= SwRect(rDestRect
.Pos().X(),fnSymbolTop
,rDestRect
.Width(),symbolHeight
);
2656 if (fnBodyPage
->IsRightToLeft()){
2657 tools::Long fnSymbolRight
= fnBodyPage
->getFrameArea().Right() - fnBodyPage
->GetRightMargin();
2658 tools::Long symbolWidth
= fnSymbolRight
- rDestRect
.Right();
2659 fnSymbolRect
= SwRect(rDestRect
.Pos().X(),rDestRect
.Pos().Y(),symbolWidth
,rDestRect
.Height());
2661 tools::Long fnSymbolLeft
= fnBodyPage
->GetLeftMargin() + fnBodyPage
->getFrameArea().Left();
2662 tools::Long symbolWidth
= rDestRect
.Left() - fnSymbolLeft
;
2663 fnSymbolRect
= SwRect(fnSymbolLeft
,rDestRect
.Pos().Y(),symbolWidth
,rDestRect
.Height());
2666 tools::Rectangle aFootnoteSymbolRect
= SwRectToPDFRect(pCurrPage
, fnSymbolRect
.SVRect());
2668 OUString
const numStrSymbol(pTextFootnote
->GetFootnote().GetViewNumStr(*pDoc
, mrSh
.GetLayout(), true));
2669 OUString
const numStrRef(pTextFootnote
->GetFootnote().GetViewNumStr(*pDoc
, mrSh
.GetLayout(), false));
2672 const sal_Int32 nBackLinkId
= pPDFExtOutDevData
->CreateLink(aFootnoteSymbolRect
, numStrSymbol
, nDestPageNum
);
2673 // Destination Export
2674 const sal_Int32 nDestId
= pPDFExtOutDevData
->CreateDest(aRect
, nDestPageNum
);
2675 mrSh
.GotoFootnoteAnchor();
2677 sal_Int32 aLinkPageNum
= CalcOutputPageNum( aLinkRect
);
2678 pCurrPage
= static_cast<const SwPageFrame
*>( mrSh
.GetLayout()->Lower() );
2680 aRect
= SwRectToPDFRect(pCurrPage
, aLinkRect
.SVRect());
2681 const sal_Int32 nLinkId
= pPDFExtOutDevData
->CreateLink(aRect
, numStrRef
, aLinkPageNum
);
2682 // Back link destination Export
2683 const sal_Int32 nBackDestId
= pPDFExtOutDevData
->CreateDest(aRect
, aLinkPageNum
);
2684 // Store link info for tagged pdf output:
2685 const IdMapEntry
aLinkEntry( aLinkRect
, nLinkId
);
2686 pPDFExtOutDevData
->GetSwPDFState()->m_LinkIdMap
.push_back(aLinkEntry
);
2688 // Store backlink info for tagged pdf output:
2689 const IdMapEntry
aBackLinkEntry( aFootnoteSymbolRect
, nBackLinkId
);
2690 pPDFExtOutDevData
->GetSwPDFState()->m_LinkIdMap
.push_back(aBackLinkEntry
);
2691 // Connect Links and Destinations:
2692 pPDFExtOutDevData
->SetLinkDest( nLinkId
, nDestId
);
2693 pPDFExtOutDevData
->SetLinkDest( nBackLinkId
, nBackDestId
);
2700 if( pPDFExtOutDevData
->GetIsExportBookmarks() )
2702 typedef std::pair
< sal_Int8
, sal_Int32
> StackEntry
;
2703 std::stack
< StackEntry
> aOutlineStack
;
2704 aOutlineStack
.push( StackEntry( -1, -1 ) ); // push default value
2706 // outlines inside flys (text frames) collected before the normal
2707 // outlines by GetOutLineNds(), so store them with page/position data
2708 // to insert later on the right page and position:
2709 // tuple< nDestPageNum, rDestRect, nLevel, rEntry, nDestId >
2710 typedef std::tuple
< sal_Int32
, SwRect
, sal_Int32
, const OUString
, sal_Int32
> FlyEntry
;
2711 std::vector
< FlyEntry
> aFlyVector
;
2712 sal_Int32 nStartFly
= 0; // first not processed item in aFlyVector
2714 const SwOutlineNodes::size_type nOutlineCount
=
2715 mrSh
.getIDocumentOutlineNodesAccess()->getOutlineNodesCount();
2716 for ( SwOutlineNodes::size_type i
= 0; i
< nOutlineCount
; ++i
)
2718 // Check if outline is hidden
2719 const SwTextNode
* pTNd
= mrSh
.GetNodes().GetOutLineNds()[ i
]->GetTextNode();
2720 assert(pTNd
&& "Enhanced pdf export - text node is missing");
2722 if ( pTNd
->IsHidden() ||
2723 !sw::IsParaPropsNode(*mrSh
.GetLayout(), *pTNd
) ||
2724 // #i40292# Skip empty outlines:
2725 pTNd
->GetText().isEmpty())
2728 // Check if outline is inside a text frame
2729 bool bFlyOutline
= pTNd
->GetFlyFormat();
2731 // save outline stack to use for postponed fly outlines
2732 std::stack
< StackEntry
> aSavedOutlineStack
;
2733 if ( !aFlyVector
.empty() && !bFlyOutline
)
2734 aSavedOutlineStack
= aOutlineStack
;
2736 // Get parent id from stack:
2737 const sal_Int8 nLevel
= static_cast<sal_Int8
>(mrSh
.getIDocumentOutlineNodesAccess()->getOutlineLevel( i
));
2738 sal_Int8 nLevelOnTopOfStack
= aOutlineStack
.top().first
;
2739 while ( nLevelOnTopOfStack
>= nLevel
&&
2740 nLevelOnTopOfStack
!= -1 )
2742 aOutlineStack
.pop();
2743 nLevelOnTopOfStack
= aOutlineStack
.top().first
;
2745 const sal_Int32 nParent
= aOutlineStack
.top().second
;
2747 // Destination rectangle
2748 mrSh
.GotoOutline(i
);
2749 const SwRect
& rDestRect
= mrSh
.GetCharRect();
2751 const SwPageFrame
* pCurrPage
=
2752 static_cast<const SwPageFrame
*>( mrSh
.GetLayout()->Lower() );
2754 // Destination PageNum
2755 const sal_Int32 nDestPageNum
= CalcOutputPageNum( rDestRect
);
2757 if ( -1 != nDestPageNum
)
2759 // Destination Export
2760 tools::Rectangle
aRect(SwRectToPDFRect(pCurrPage
, rDestRect
.SVRect()));
2761 const sal_Int32 nDestId
=
2762 pPDFExtOutDevData
->CreateDest(aRect
, nDestPageNum
);
2764 // Outline entry text
2765 const OUString aEntry
= mrSh
.getIDocumentOutlineNodesAccess()->getOutlineText(
2766 i
, mrSh
.GetLayout(), true, false, false );
2768 // postpone fly outlines
2771 aFlyVector
.push_back(
2772 FlyEntry(nDestPageNum
, rDestRect
, nLevel
, aEntry
, nDestId
) );
2776 // create new outline items from postponed fly outlines, if they are before
2777 // the recent not fly outline (and after the already created fly outlines)
2778 for (size_t j
= nStartFly
; j
< aFlyVector
.size(); ++j
)
2780 if ( std::get
<0>(aFlyVector
[j
]) < nDestPageNum
||
2781 ( std::get
<0>(aFlyVector
[j
]) == nDestPageNum
&&
2782 std::get
<1>(aFlyVector
[j
]).Pos().Y() < rDestRect
.Pos().Y() ) )
2784 sal_Int32 nFlyLevel
= std::get
<2>(aFlyVector
[j
]);
2785 sal_Int8 nLevelOnTopOfSavedStack
= aSavedOutlineStack
.top().first
;
2786 while ( nLevelOnTopOfSavedStack
>= nFlyLevel
&&
2787 nLevelOnTopOfSavedStack
!= -1 )
2789 aSavedOutlineStack
.pop();
2790 nLevelOnTopOfSavedStack
= aSavedOutlineStack
.top().first
;
2792 const sal_Int32 nFlyParent
= aSavedOutlineStack
.top().second
;
2793 const sal_Int32 nId
= pPDFExtOutDevData
->CreateOutlineItem( nFlyParent
,
2794 std::get
<3>(aFlyVector
[j
]),
2795 std::get
<4>(aFlyVector
[j
]) );
2796 // Push current level and outline id on saved stack:
2797 aSavedOutlineStack
.push( StackEntry( nFlyLevel
, nId
) );
2804 // Create a new outline item:
2805 const sal_Int32 nOutlineId
=
2806 pPDFExtOutDevData
->CreateOutlineItem( nParent
, aEntry
, nDestId
);
2808 // Push current level and nOutlineId on stack:
2809 aOutlineStack
.push( StackEntry( nLevel
, nOutlineId
) );
2813 // create remaining fly outlines
2814 for (size_t j
= nStartFly
; j
< aFlyVector
.size(); ++j
)
2816 sal_Int32 nLevel
= std::get
<2>(aFlyVector
[j
]);
2817 sal_Int8 nLevelOnTopOfStack
= aOutlineStack
.top().first
;
2818 while ( nLevelOnTopOfStack
>= nLevel
&&
2819 nLevelOnTopOfStack
!= -1 )
2821 aOutlineStack
.pop();
2822 nLevelOnTopOfStack
= aOutlineStack
.top().first
;
2824 const sal_Int32 nParent
= aOutlineStack
.top().second
;
2826 const sal_Int32 nOutlineId
= pPDFExtOutDevData
->CreateOutlineItem( nParent
,
2827 std::get
<3>(aFlyVector
[j
]),
2828 std::get
<4>(aFlyVector
[j
]) );
2829 aOutlineStack
.push( StackEntry( std::get
<2>(aFlyVector
[j
]), nOutlineId
) );
2833 if( pPDFExtOutDevData
->GetIsExportNamedDestinations() )
2835 // #i56629# the iteration to convert the OOo bookmark (#bookmark)
2836 // into PDF named destination, see section 8.2.1 in PDF 1.4 spec
2838 // 1. a name for the destination, formed from the standard OOo bookmark name
2839 // 2. the destination, obtained from where the bookmark destination lies
2840 IDocumentMarkAccess
* const pMarkAccess
= mrSh
.GetDoc()->getIDocumentMarkAccess();
2841 for(auto ppMark
= pMarkAccess
->getBookmarksBegin();
2842 ppMark
!= pMarkAccess
->getBookmarksEnd();
2846 const ::sw::mark::MarkBase
* pBkmk
= *ppMark
;
2847 mrSh
.SwCursorShell::ClearMark();
2848 const OUString
& sBkName
= pBkmk
->GetName();
2851 if (! JumpToSwMark( &mrSh
, sBkName
))
2856 // Destination Rectangle
2857 const SwRect
& rDestRect
= mrSh
.GetCharRect();
2859 const SwPageFrame
* pCurrPage
=
2860 static_cast<const SwPageFrame
*>( mrSh
.GetLayout()->Lower() );
2862 // Destination PageNum
2863 const sal_Int32 nDestPageNum
= CalcOutputPageNum( rDestRect
);
2865 // Destination Export
2866 if ( -1 != nDestPageNum
)
2868 tools::Rectangle
aRect(SwRectToPDFRect(pCurrPage
, rDestRect
.SVRect()));
2869 pPDFExtOutDevData
->CreateNamedDest(sBkName
, aRect
, nDestPageNum
);
2872 mrSh
.SwCursorShell::ClearMark();
2879 // LINKS FROM EDITENGINE
2881 std::vector
< vcl::PDFExtOutDevBookmarkEntry
>& rBookmarks
= pPDFExtOutDevData
->GetBookmarks();
2882 for ( const auto& rBookmark
: rBookmarks
)
2884 OUString
aBookmarkName( rBookmark
.aBookmark
);
2885 const bool bInternal
= '#' == aBookmarkName
[0];
2888 aBookmarkName
= aBookmarkName
.copy( 1 );
2889 JumpToSwMark( &mrSh
, aBookmarkName
);
2891 // Destination Rectangle
2892 const SwRect
& rDestRect
= mrSh
.GetCharRect();
2894 const SwPageFrame
* pCurrPage
=
2895 static_cast<const SwPageFrame
*>( mrSh
.GetLayout()->Lower() );
2897 // Destination PageNum
2898 const sal_Int32 nDestPageNum
= CalcOutputPageNum( rDestRect
);
2900 if ( -1 != nDestPageNum
)
2902 tools::Rectangle
aRect(SwRectToPDFRect(pCurrPage
, rDestRect
.SVRect()));
2903 if ( rBookmark
.nLinkId
!= -1 )
2905 // Destination Export
2906 const sal_Int32 nDestId
= pPDFExtOutDevData
->CreateDest(aRect
, nDestPageNum
);
2908 // Connect Link and Destination:
2909 pPDFExtOutDevData
->SetLinkDest( rBookmark
.nLinkId
, nDestId
);
2913 pPDFExtOutDevData
->DescribeRegisteredDest(rBookmark
.nDestId
, aRect
, nDestPageNum
);
2918 pPDFExtOutDevData
->SetLinkURL( rBookmark
.nLinkId
, aBookmarkName
);
2921 assert(pPDFExtOutDevData
->GetSwPDFState());
2922 delete pPDFExtOutDevData
->GetSwPDFState();
2923 pPDFExtOutDevData
->SetSwPDFState(nullptr);
2926 // Restore view, cursor, and outdev:
2927 mrSh
.LockView( bOldLockView
);
2928 mrSh
.SwCursorShell::Pop(SwCursorShell::PopMode::DeleteCurrent
);
2932 void SwEnhancedPDFExportHelper::ExportAuthorityEntryLinks()
2934 auto pPDFExtOutDevData
= dynamic_cast<vcl::PDFExtOutDevData
*>(mrOut
.GetExtOutDevData());
2935 if (!pPDFExtOutDevData
)
2940 // Create PDF destinations for bibliography table entries
2941 std::vector
<std::tuple
<const SwTOXBase
*, const OUString
*, sal_Int32
>> vDestinations
;
2942 // string is the row node text, sal_Int32 is number of the destination
2943 // Note: This way of iterating doesn't seem to take into account TOXes
2944 // that are in a frame, probably in some other cases too
2947 while (mrSh
.GotoNextTOXBase())
2949 const SwTOXBase
* pIteratedTOX
= nullptr;
2950 while ((pIteratedTOX
= mrSh
.GetCurTOX()) != nullptr
2951 && pIteratedTOX
->GetType() == TOX_AUTHORITIES
)
2953 if (const SwNode
& rCurrentNode
= mrSh
.GetCursor()->GetPoint()->GetNode();
2954 rCurrentNode
.GetNodeType() == SwNodeType::Text
)
2956 if (mrSh
.GetCursor()->GetPoint()->GetNode().FindSectionNode()->GetSection().GetType()
2957 == SectionType::ToxContent
) // this checks it's not a heading
2959 // Destination Rectangle
2960 const SwRect
& rDestRect
= mrSh
.GetCharRect();
2962 const SwPageFrame
* pCurrPage
=
2963 static_cast<const SwPageFrame
*>( mrSh
.GetLayout()->Lower() );
2965 // Destination PageNum
2966 const sal_Int32 nDestPageNum
= CalcOutputPageNum( rDestRect
);
2968 // Destination Export
2969 if ( -1 != nDestPageNum
)
2971 tools::Rectangle
aRect(SwRectToPDFRect(pCurrPage
, rDestRect
.SVRect()));
2972 const sal_Int32 nDestId
= pPDFExtOutDevData
->CreateDest(aRect
, nDestPageNum
);
2973 const OUString
* vNodeText
= &static_cast<const SwTextNode
*>(&rCurrentNode
)->GetText();
2974 vDestinations
.emplace_back(pIteratedTOX
, vNodeText
, nDestId
);
2978 if (!mrSh
.MovePara(GoNextPara
, fnParaStart
))
2979 { // Cursor is stuck in the TOX due to document ending immediately afterwards
2986 // Generate links to matching entries in the bibliography tables
2987 std::vector
<SwFormatField
*> aFields
;
2988 SwFieldType
* pType
= mrSh
.GetFieldType(SwFieldIds::TableOfAuthorities
, OUString());
2994 pType
->GatherFields(aFields
);
2995 const auto pPageFrame
= static_cast<const SwPageFrame
*>(mrSh
.GetLayout()->Lower());
2996 for (const auto pFormatField
: aFields
)
2998 if (!pFormatField
->GetTextField() || !pFormatField
->IsFieldInDoc())
3003 const auto& rAuthorityField
3004 = *static_cast<const SwAuthorityField
*>(pFormatField
->GetField());
3006 if (auto targetType
= rAuthorityField
.GetTargetType();
3007 targetType
== SwAuthorityField::TargetType::UseDisplayURL
3008 || targetType
== SwAuthorityField::TargetType::UseTargetURL
)
3010 // Since the target type specifies to use an URL, link to it
3011 const OUString aURL
= rAuthorityField
.GetAbsoluteURL();
3012 if (aURL
.getLength() == 0)
3017 const SwTextNode
& rTextNode
= pFormatField
->GetTextField()->GetTextNode();
3018 if (!lcl_TryMoveToNonHiddenField(mrSh
, rTextNode
, *pFormatField
))
3023 OUString
const content(rAuthorityField
.ExpandField(true, mrSh
.GetLayout()));
3025 // Select the field.
3026 mrSh
.SwCursorShell::SetMark();
3027 mrSh
.SwCursorShell::Right(1, SwCursorSkipMode::Chars
);
3029 // Create the links.
3030 SwRects
const rects(GetCursorRectsContainingText(mrSh
));
3031 for (const auto& rLinkRect
: rects
)
3033 for (const auto& rLinkPageNum
: CalcOutputPageNums(rLinkRect
))
3035 tools::Rectangle
aRect(SwRectToPDFRect(pPageFrame
, rLinkRect
.SVRect()));
3036 sal_Int32 nLinkId
= pPDFExtOutDevData
->CreateLink(aRect
, content
, rLinkPageNum
);
3037 IdMapEntry
aLinkEntry(rLinkRect
, nLinkId
);
3038 pPDFExtOutDevData
->GetSwPDFState()->m_LinkIdMap
.push_back(aLinkEntry
);
3039 pPDFExtOutDevData
->SetLinkURL(nLinkId
, aURL
);
3042 mrSh
.SwCursorShell::ClearMark();
3044 else if (targetType
== SwAuthorityField::TargetType::BibliographyTableRow
)
3046 // As the target type specifies, try linking to a bibliography table row
3047 sal_Int32 nDestId
= -1;
3049 std::unordered_map
<const SwTOXBase
*, OUString
> vFormattedFieldStrings
;
3050 for (const auto& rDestinationTuple
: vDestinations
)
3052 if (vFormattedFieldStrings
.find(std::get
<0>(rDestinationTuple
))
3053 == vFormattedFieldStrings
.end())
3054 vFormattedFieldStrings
.emplace(std::get
<0>(rDestinationTuple
),
3055 rAuthorityField
.GetAuthority(mrSh
.GetLayout(),
3056 &std::get
<0>(rDestinationTuple
)->GetTOXForm()));
3058 if (vFormattedFieldStrings
.at(std::get
<0>(rDestinationTuple
)) == *std::get
<1>(rDestinationTuple
))
3060 nDestId
= std::get
<2>(rDestinationTuple
);
3068 const SwTextNode
& rTextNode
= pFormatField
->GetTextField()->GetTextNode();
3069 if (!lcl_TryMoveToNonHiddenField(mrSh
, rTextNode
, *pFormatField
))
3074 OUString
const content(rAuthorityField
.ExpandField(true, mrSh
.GetLayout()));
3076 // Select the field.
3077 mrSh
.SwCursorShell::SetMark();
3078 mrSh
.SwCursorShell::Right(1, SwCursorSkipMode::Chars
);
3080 // Create the links.
3081 SwRects
const rects(GetCursorRectsContainingText(mrSh
));
3082 for (const auto& rLinkRect
: rects
)
3084 for (const auto& rLinkPageNum
: CalcOutputPageNums(rLinkRect
))
3086 tools::Rectangle
aRect(SwRectToPDFRect(pPageFrame
, rLinkRect
.SVRect()));
3087 sal_Int32 nLinkId
= pPDFExtOutDevData
->CreateLink(aRect
, content
, rLinkPageNum
);
3088 IdMapEntry
aLinkEntry(rLinkRect
, nLinkId
);
3089 pPDFExtOutDevData
->GetSwPDFState()->m_LinkIdMap
.push_back(aLinkEntry
);
3090 pPDFExtOutDevData
->SetLinkDest(nLinkId
, nDestId
);
3093 mrSh
.SwCursorShell::ClearMark();
3098 // Returns the page number in the output pdf on which the given rect is located.
3099 // If this page is duplicated, method will return first occurrence of it.
3100 sal_Int32
SwEnhancedPDFExportHelper::CalcOutputPageNum( const SwRect
& rRect
) const
3102 std::vector
< sal_Int32
> aPageNums
= CalcOutputPageNums( rRect
);
3103 if ( !aPageNums
.empty() )
3104 return aPageNums
[0];
3108 // Returns a vector of the page numbers in the output pdf on which the given
3109 // rect is located. There can be many such pages since StringRangeEnumerator
3110 // allows duplication of its entries.
3111 std::vector
< sal_Int32
> SwEnhancedPDFExportHelper::CalcOutputPageNums(
3112 const SwRect
& rRect
) const
3114 std::vector
< sal_Int32
> aPageNums
;
3116 // Document page number.
3117 sal_Int32 nPageNumOfRect
= mrSh
.GetPageNumAndSetOffsetForPDF( mrOut
, rRect
);
3118 if ( nPageNumOfRect
< 0 )
3121 // What will be the page numbers of page nPageNumOfRect in the output pdf?
3124 if ( mbSkipEmptyPages
)
3125 // Map the page number to the range without empty pages.
3126 nPageNumOfRect
= maPageNumberMap
[ nPageNumOfRect
];
3128 if ( mpRangeEnum
->hasValue( nPageNumOfRect
) )
3130 sal_Int32 nOutputPageNum
= 0;
3131 StringRangeEnumerator::Iterator aIter
= mpRangeEnum
->begin();
3132 StringRangeEnumerator::Iterator aEnd
= mpRangeEnum
->end();
3133 for ( ; aIter
!= aEnd
; ++aIter
)
3135 if ( *aIter
== nPageNumOfRect
)
3136 aPageNums
.push_back( nOutputPageNum
);
3143 if ( mbSkipEmptyPages
)
3145 sal_Int32 nOutputPageNum
= 0;
3146 for ( size_t i
= 0; i
< maPageNumberMap
.size(); ++i
)
3148 if ( maPageNumberMap
[i
] >= 0 ) // is not empty?
3150 if ( i
== static_cast<size_t>( nPageNumOfRect
) )
3152 aPageNums
.push_back( nOutputPageNum
);
3160 aPageNums
.push_back( nPageNumOfRect
);
3166 void SwEnhancedPDFExportHelper::MakeHeaderFooterLinks( vcl::PDFExtOutDevData
& rPDFExtOutDevData
,
3167 const SwTextNode
& rTNd
,
3168 const SwRect
& rLinkRect
,
3170 const OUString
& rURL
,
3172 OUString
const& rContent
) const
3174 // We assume, that the primary link has just been exported. Therefore
3175 // the offset of the link rectangle calculates as follows:
3176 const Point aOffset
= rLinkRect
.Pos() + mrOut
.GetMapMode().GetOrigin();
3178 SwIterator
<SwTextFrame
, SwTextNode
, sw::IteratorMode::UnwrapMulti
> aIter(rTNd
);
3179 for ( SwTextFrame
* pTmpFrame
= aIter
.First(); pTmpFrame
; pTmpFrame
= aIter
.Next() )
3181 // Add offset to current page:
3182 const SwPageFrame
* pPageFrame
= pTmpFrame
->FindPageFrame();
3183 SwRect
aHFLinkRect( rLinkRect
);
3184 aHFLinkRect
.Pos() = pPageFrame
->getFrameArea().Pos() + aOffset
;
3186 // #i97135# the gcc_x64 optimizer gets aHFLinkRect != rLinkRect wrong
3187 // fool it by comparing the position only (the width and height are the
3189 if ( aHFLinkRect
.Pos() != rLinkRect
.Pos() )
3192 std::vector
<sal_Int32
> aHFLinkPageNums
= CalcOutputPageNums( aHFLinkRect
);
3194 for (sal_Int32 aHFLinkPageNum
: aHFLinkPageNums
)
3197 tools::Rectangle
aRect(SwRectToPDFRect(pPageFrame
, aHFLinkRect
.SVRect()));
3198 const sal_Int32 nHFLinkId
=
3199 rPDFExtOutDevData
.CreateLink(aRect
, rContent
, aHFLinkPageNum
);
3201 // Connect Link and Destination:
3203 rPDFExtOutDevData
.SetLinkDest( nHFLinkId
, nDestId
);
3205 rPDFExtOutDevData
.SetLinkURL( nHFLinkId
, rURL
);
3211 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */