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 <tools/multisel.hxx>
32 #include <editeng/adjustitem.hxx>
33 #include <editeng/lrspitem.hxx>
34 #include <editeng/langitem.hxx>
35 #include <tools/urlobj.hxx>
36 #include <svl/languageoptions.hxx>
37 #include <svl/numformat.hxx>
38 #include <svl/zforlist.hxx>
39 #include <swatrset.hxx>
44 #include <section.hxx>
47 #include <txtinet.hxx>
48 #include <fmtinfmt.hxx>
49 #include <fchrfmt.hxx>
50 #include <charfmt.hxx>
51 #include <fmtanchr.hxx>
58 #include <IDocumentOutlineNodes.hxx>
60 #include <docufld.hxx>
63 #include <rootfrm.hxx>
64 #include <pagefrm.hxx>
68 #include <cellfrm.hxx>
69 #include <sectfrm.hxx>
72 #include <notxtfrm.hxx>
74 #include <SwStyleNameMapper.hxx>
75 #include "itrpaint.hxx"
76 #include <i18nlangtag/languagetag.hxx>
78 #include <printdata.hxx>
80 #include <SwNodeNum.hxx>
82 #include <frmtool.hxx>
83 #include <strings.hrc>
84 #include <frameformats.hxx>
85 #include <tblafmt.hxx>
86 #include <authfld.hxx>
87 #include <dcontact.hxx>
89 #include <tools/globname.hxx>
90 #include <svx/svdobj.hxx>
97 using namespace ::com::sun::star
;
99 #if OSL_DEBUG_LEVEL > 1
101 static std::vector
< sal_uInt16
> aStructStack
;
103 void lcl_DBGCheckStack()
105 /* NonStructElement = 0 Document = 1 Part = 2
106 * Article = 3 Section = 4 Division = 5
107 * BlockQuote = 6 Caption = 7 TOC = 8
108 * TOCI = 9 Index = 10 Paragraph = 11
109 * Heading = 12 H1-6 = 13 - 18 List = 19
110 * ListItem = 20 LILabel = 21 LIBody = 22
111 * Table = 23 TableRow = 24 TableHeader = 25
112 * TableData = 26 Span = 27 Quote = 28
113 * Note = 29 Reference = 30 BibEntry = 31
114 * Code = 32 Link = 33 Figure = 34
115 * Formula = 35 Form = 36 Continued frame = 99
119 for ( const auto& rItem
: aStructStack
)
128 typedef std::set
< tools::Long
, lt_TableColumn
> TableColumnsMapEntry
;
129 typedef std::pair
< SwRect
, sal_Int32
> IdMapEntry
;
130 typedef std::vector
< IdMapEntry
> LinkIdMap
;
131 typedef std::map
< const SwTable
*, TableColumnsMapEntry
> TableColumnsMap
;
132 typedef std::map
< const SwNumberTreeNode
*, sal_Int32
> NumListIdMap
;
133 typedef std::map
< const SwNumberTreeNode
*, sal_Int32
> NumListBodyIdMap
;
134 typedef std::set
<const void*> FrameTagSet
;
136 struct SwEnhancedPDFState
138 TableColumnsMap m_TableColumnsMap
;
139 LinkIdMap m_LinkIdMap
;
140 NumListIdMap m_NumListIdMap
;
141 NumListBodyIdMap m_NumListBodyIdMap
;
142 FrameTagSet m_FrameTagSet
;
144 LanguageType m_eLanguageDefault
;
148 FontLineStyle eUnderline
;
149 FontLineStyle eOverline
;
150 FontStrikeout eStrikeout
;
151 FontEmphasisMark eFontEmphasis
;
153 SwFontScript nScript
;
158 ::std::optional
<Span
> m_oCurrentSpan
;
159 ::std::optional
<SwTextAttr
const*> m_oCurrentLink
;
161 SwEnhancedPDFState(LanguageType
const eLanguageDefault
)
162 : m_eLanguageDefault(eLanguageDefault
)
170 const char aTableHeadingName
[] = "Table Heading";
171 const char aQuotations
[] = "Quotations";
172 const char aCaption
[] = "Caption";
173 const char aHeading
[] = "Heading";
174 const char aQuotation
[] = "Quotation";
175 const char aSourceText
[] = "Source Text";
178 constexpr OUStringLiteral aDocumentString
= u
"Document";
179 constexpr OUStringLiteral aDivString
= u
"Div";
180 constexpr OUStringLiteral aSectString
= u
"Sect";
181 constexpr OUStringLiteral aHString
= u
"H";
182 constexpr OUStringLiteral aH1String
= u
"H1";
183 constexpr OUStringLiteral aH2String
= u
"H2";
184 constexpr OUStringLiteral aH3String
= u
"H3";
185 constexpr OUStringLiteral aH4String
= u
"H4";
186 constexpr OUStringLiteral aH5String
= u
"H5";
187 constexpr OUStringLiteral aH6String
= u
"H6";
188 constexpr OUStringLiteral aH7String
= u
"H7";
189 constexpr OUStringLiteral aH8String
= u
"H8";
190 constexpr OUStringLiteral aH9String
= u
"H9";
191 constexpr OUStringLiteral aH10String
= u
"H10";
192 constexpr OUStringLiteral aListString
= u
"L";
193 constexpr OUStringLiteral aListItemString
= u
"LI";
194 constexpr OUStringLiteral aListLabelString
= u
"Lbl";
195 constexpr OUStringLiteral aListBodyString
= u
"LBody";
196 constexpr OUStringLiteral aBlockQuoteString
= u
"BlockQuote";
197 constexpr OUStringLiteral aCaptionString
= u
"Caption";
198 constexpr OUStringLiteral aIndexString
= u
"Index";
199 constexpr OUStringLiteral aTOCString
= u
"TOC";
200 constexpr OUStringLiteral aTOCIString
= u
"TOCI";
201 constexpr OUStringLiteral aTableString
= u
"Table";
202 constexpr OUStringLiteral aTRString
= u
"TR";
203 constexpr OUStringLiteral aTDString
= u
"TD";
204 constexpr OUStringLiteral aTHString
= u
"TH";
205 constexpr OUStringLiteral aBibEntryString
= u
"BibEntry";
206 constexpr OUStringLiteral aQuoteString
= u
"Quote";
207 constexpr OUStringLiteral aSpanString
= u
"Span";
208 constexpr OUStringLiteral aCodeString
= u
"Code";
209 constexpr OUStringLiteral aFigureString
= u
"Figure";
210 constexpr OUStringLiteral aFormulaString
= u
"Formula";
211 constexpr OUStringLiteral aLinkString
= u
"Link";
212 constexpr OUStringLiteral aNoteString
= u
"Note";
214 // returns true if first paragraph in cell frame has 'table heading' style
215 bool lcl_IsHeadlineCell( const SwCellFrame
& rCellFrame
)
219 const SwContentFrame
*pCnt
= rCellFrame
.ContainsContent();
220 if ( pCnt
&& pCnt
->IsTextFrame() )
222 SwTextNode
const*const pTextNode
= static_cast<const SwTextFrame
*>(pCnt
)->GetTextNodeForParaProps();
223 const SwFormat
* pTextFormat
= pTextNode
->GetFormatColl();
226 SwStyleNameMapper::FillProgName( pTextFormat
->GetName(), sStyleName
, SwGetPoolIdFromName::TxtColl
);
227 bRet
= sStyleName
== aTableHeadingName
;
230 // tdf#153935 wild guessing for 1st row based on table autoformat
231 if (!bRet
&& !rCellFrame
.GetUpper()->GetPrev())
233 SwTable
const*const pTable(rCellFrame
.FindTabFrame()->GetTable());
235 OUString
const& rStyleName(pTable
->GetTableStyleName());
236 if (!rStyleName
.isEmpty())
238 if (SwTableAutoFormat
const*const pTableAF
=
239 pTable
->GetFrameFormat()->GetDoc()->GetTableStyles().FindAutoFormat(rStyleName
))
241 bRet
|= pTableAF
->HasHeaderRow();
249 // List all frames for which the NonStructElement tag is set:
250 bool lcl_IsInNonStructEnv( const SwFrame
& rFrame
)
254 if ( nullptr != rFrame
.FindFooterOrHeader() &&
255 !rFrame
.IsHeaderFrame() && !rFrame
.IsFooterFrame() )
259 else if ( rFrame
.IsInTab() && !rFrame
.IsTabFrame() )
261 const SwTabFrame
* pTabFrame
= rFrame
.FindTabFrame();
262 if ( rFrame
.GetUpper() != pTabFrame
&&
263 pTabFrame
->IsFollow() && pTabFrame
->IsInHeadline( rFrame
) )
270 // Generate key from frame for reopening tags:
271 void const* lcl_GetKeyFromFrame( const SwFrame
& rFrame
)
273 void const* pKey
= nullptr;
275 if ( rFrame
.IsPageFrame() )
276 pKey
= static_cast<void const *>(&(static_cast<const SwPageFrame
&>(rFrame
).GetFormat()->getIDocumentSettingAccess()));
277 else if ( rFrame
.IsTextFrame() )
278 pKey
= static_cast<void const *>(static_cast<const SwTextFrame
&>(rFrame
).GetTextNodeFirst());
279 else if ( rFrame
.IsSctFrame() )
280 pKey
= static_cast<void const *>(static_cast<const SwSectionFrame
&>(rFrame
).GetSection());
281 else if ( rFrame
.IsTabFrame() )
282 pKey
= static_cast<void const *>(static_cast<const SwTabFrame
&>(rFrame
).GetTable());
283 else if ( rFrame
.IsRowFrame() )
284 pKey
= static_cast<void const *>(static_cast<const SwRowFrame
&>(rFrame
).GetTabLine());
285 else if ( rFrame
.IsCellFrame() )
287 const SwTabFrame
* pTabFrame
= rFrame
.FindTabFrame();
288 const SwTable
* pTable
= pTabFrame
->GetTable();
289 pKey
= static_cast<void const *>(& static_cast<const SwCellFrame
&>(rFrame
).GetTabBox()->FindStartOfRowSpan(*pTable
));
291 else if (rFrame
.IsFootnoteFrame())
293 pKey
= static_cast<void const*>(static_cast<SwFootnoteFrame
const&>(rFrame
).GetAttr());
299 bool lcl_HasPreviousParaSameNumRule(SwTextFrame
const& rTextFrame
, const SwTextNode
& rNode
)
302 SwNodeIndex
aIdx( rNode
);
303 const SwDoc
& rDoc
= rNode
.GetDoc();
304 const SwNodes
& rNodes
= rDoc
.GetNodes();
305 const SwNode
* pNode
= &rNode
;
306 const SwNumRule
* pNumRule
= rNode
.GetNumRule();
308 while (pNode
!= rNodes
.DocumentSectionStartNode(const_cast<SwNode
*>(static_cast<SwNode
const *>(&rNode
))) )
310 sw::GotoPrevLayoutTextFrame(aIdx
, rTextFrame
.getRootFrame());
312 if (aIdx
.GetNode().IsTextNode())
314 const SwTextNode
*const pPrevTextNd
= sw::GetParaPropsNode(
315 *rTextFrame
.getRootFrame(), *aIdx
.GetNode().GetTextNode());
316 const SwNumRule
* pPrevNumRule
= pPrevTextNd
->GetNumRule();
318 // We find the previous text node. Now check, if the previous text node
319 // has the same numrule like rNode:
320 if ( (pPrevNumRule
== pNumRule
) &&
321 (!pPrevTextNd
->IsOutline() == !rNode
.IsOutline()))
327 pNode
= &aIdx
.GetNode();
332 bool lcl_TryMoveToNonHiddenField(SwEditShell
& rShell
, const SwTextNode
& rNd
, const SwFormatField
& rField
)
334 // 1. Check if the whole paragraph is hidden
335 // 2. Move to the field
336 // 3. Check for hidden text attribute
339 if(!rShell
.GotoFormatField(rField
) || rShell
.IsInHiddenRange(/*bSelect=*/false))
341 rShell
.SwCursorShell::ClearMark();
349 SwTaggedPDFHelper::SwTaggedPDFHelper( const Num_Info
* pNumInfo
,
350 const Frame_Info
* pFrameInfo
,
351 const Por_Info
* pPorInfo
,
352 OutputDevice
const & rOut
)
353 : m_nEndStructureElement( 0 ),
354 m_nRestoreCurrentTag( -1 ),
355 mpNumInfo( pNumInfo
),
356 mpFrameInfo( pFrameInfo
),
357 mpPorInfo( pPorInfo
)
360 dynamic_cast< vcl::PDFExtOutDevData
*>( rOut
.GetExtOutDevData() );
362 if ( !(mpPDFExtOutDevData
&& mpPDFExtOutDevData
->GetIsExportTaggedPDF()) )
365 #if OSL_DEBUG_LEVEL > 1
366 sal_Int32 nCurrentStruct
= mpPDFExtOutDevData
->GetCurrentStructureElement();
370 BeginNumberedListStructureElements();
371 else if ( mpFrameInfo
)
372 BeginBlockStructureElements();
373 else if ( mpPorInfo
)
374 BeginInlineStructureElements();
376 BeginTag( vcl::PDFWriter::NonStructElement
, OUString() );
378 #if OSL_DEBUG_LEVEL > 1
379 nCurrentStruct
= mpPDFExtOutDevData
->GetCurrentStructureElement();
381 (void)nCurrentStruct
;
385 SwTaggedPDFHelper::~SwTaggedPDFHelper()
387 if ( !(mpPDFExtOutDevData
&& mpPDFExtOutDevData
->GetIsExportTaggedPDF()) )
390 #if OSL_DEBUG_LEVEL > 1
391 sal_Int32 nCurrentStruct
= mpPDFExtOutDevData
->GetCurrentStructureElement();
394 EndStructureElements();
396 #if OSL_DEBUG_LEVEL > 1
397 nCurrentStruct
= mpPDFExtOutDevData
->GetCurrentStructureElement();
399 (void)nCurrentStruct
;
403 void const* SwDrawContact::GetPDFAnchorStructureElementKey(SdrObject
const& rObj
)
405 SwFrame
const*const pAnchorFrame(GetAnchoredObj(&rObj
)->GetAnchorFrame());
406 return pAnchorFrame
? lcl_GetKeyFromFrame(*pAnchorFrame
) : nullptr;
409 bool SwTaggedPDFHelper::CheckReopenTag()
412 void const* pReopenKey(nullptr);
413 bool bContinue
= false; // in some cases we just have to reopen a tag without early returning
417 const SwFrame
& rFrame
= mpFrameInfo
->mrFrame
;
418 const SwFrame
* pKeyFrame
= nullptr;
420 // Reopen an existing structure element if
421 // - rFrame is not the first page frame (reopen Document tag)
422 // - rFrame is a follow frame (reopen Master tag)
423 // - rFrame is a fly frame anchored at content (reopen Anchor paragraph tag)
424 // - rFrame is a fly frame anchored at page (reopen Document tag)
425 // - rFrame is a follow flow row (reopen TableRow tag)
426 // - rFrame is a cell frame in a follow flow row (reopen TableData tag)
427 if ( ( rFrame
.IsPageFrame() && static_cast<const SwPageFrame
&>(rFrame
).GetPrev() ) ||
428 ( rFrame
.IsFlowFrame() && SwFlowFrame::CastFlowFrame(&rFrame
)->IsFollow() ) ||
429 (rFrame
.IsFootnoteFrame() && static_cast<SwFootnoteFrame
const&>(rFrame
).GetMaster()) ||
430 ( rFrame
.IsRowFrame() && rFrame
.IsInFollowFlowRow() ) ||
431 ( rFrame
.IsCellFrame() && const_cast<SwFrame
&>(rFrame
).GetPrevCellLeaf() ) )
435 else if (rFrame
.IsFlyFrame() && !mpFrameInfo
->m_isLink
)
437 const SwFormatAnchor
& rAnchor
=
438 static_cast<const SwFlyFrame
*>(&rFrame
)->GetFormat()->GetAnchor();
439 if ((RndStdIds::FLY_AT_PARA
== rAnchor
.GetAnchorId()) ||
440 (RndStdIds::FLY_AT_CHAR
== rAnchor
.GetAnchorId()) ||
441 (RndStdIds::FLY_AT_PAGE
== rAnchor
.GetAnchorId()))
443 pKeyFrame
= static_cast<const SwFlyFrame
&>(rFrame
).GetAnchorFrame();
450 void const*const pKey
= lcl_GetKeyFromFrame(*pKeyFrame
);
451 FrameTagSet
& rFrameTagSet(mpPDFExtOutDevData
->GetSwPDFState()->m_FrameTagSet
);
452 if (rFrameTagSet
.find(pKey
) != rFrameTagSet
.end()
453 || rFrame
.IsFlyFrame()) // for hell layer flys
462 // note: it would be possible to get rid of the SetCurrentStructureElement()
463 // - which is quite ugly - for most cases by recreating the parents until the
464 // current ancestor, but there are special cases cell frame rowspan > 1 follow
465 // and footnote frame follow where the parent of the follow is different from
466 // the parent of the first one, and so in PDFExtOutDevData the wrong parent
467 // would be restored and used for next elements.
468 m_nRestoreCurrentTag
= mpPDFExtOutDevData
->GetCurrentStructureElement();
469 sal_Int32
const id
= mpPDFExtOutDevData
->EnsureStructureElement(pReopenKey
);
470 mpPDFExtOutDevData
->SetCurrentStructureElement(id
);
475 return bRet
&& !bContinue
;
478 void SwTaggedPDFHelper::CheckRestoreTag() const
480 if ( m_nRestoreCurrentTag
!= -1 )
482 const bool bSuccess
= mpPDFExtOutDevData
->SetCurrentStructureElement( m_nRestoreCurrentTag
);
483 OSL_ENSURE( bSuccess
, "Failed to restore reopened tag" );
485 #if OSL_DEBUG_LEVEL > 1
486 aStructStack
.pop_back();
491 void SwTaggedPDFHelper::OpenTagImpl(void const*const pKey
)
493 sal_Int32
const id
= mpPDFExtOutDevData
->EnsureStructureElement(pKey
);
494 mpPDFExtOutDevData
->BeginStructureElement(id
);
495 ++m_nEndStructureElement
;
497 #if OSL_DEBUG_LEVEL > 1
498 aStructStack
.push_back( 99 );
502 sal_Int32
SwTaggedPDFHelper::BeginTagImpl(void const*const pKey
,
503 vcl::PDFWriter::StructElement
const eType
, const OUString
& rString
)
506 const sal_Int32 nId
= mpPDFExtOutDevData
->EnsureStructureElement(pKey
);
507 mpPDFExtOutDevData
->InitStructureElement(nId
, eType
, rString
);
508 mpPDFExtOutDevData
->BeginStructureElement(nId
);
509 ++m_nEndStructureElement
;
511 #if OSL_DEBUG_LEVEL > 1
512 aStructStack
.push_back( o3tl::narrowing
<sal_uInt16
>(eType
) );
518 void SwTaggedPDFHelper::BeginTag( vcl::PDFWriter::StructElement eType
, const OUString
& rString
)
520 void const* pKey(nullptr);
524 const SwFrame
& rFrame
= mpFrameInfo
->mrFrame
;
526 if ( ( rFrame
.IsPageFrame() && !static_cast<const SwPageFrame
&>(rFrame
).GetPrev() ) ||
527 ( rFrame
.IsFlowFrame() && !SwFlowFrame::CastFlowFrame(&rFrame
)->IsFollow() && SwFlowFrame::CastFlowFrame(&rFrame
)->HasFollow() ) ||
528 rFrame
.IsSctFrame() || // all of them, so that opening parent sections works
529 ( rFrame
.IsTextFrame() && rFrame
.GetDrawObjs() ) ||
530 (rFrame
.IsFootnoteFrame() && static_cast<SwFootnoteFrame
const&>(rFrame
).GetFollow()) ||
531 ( rFrame
.IsRowFrame() && rFrame
.IsInSplitTableRow() ) ||
532 ( rFrame
.IsCellFrame() && const_cast<SwFrame
&>(rFrame
).GetNextCellLeaf() ) )
534 pKey
= lcl_GetKeyFromFrame(rFrame
);
538 FrameTagSet
& rFrameTagSet(mpPDFExtOutDevData
->GetSwPDFState()->m_FrameTagSet
);
539 assert(rFrameTagSet
.find(pKey
) == rFrameTagSet
.end());
540 rFrameTagSet
.emplace(pKey
);
545 sal_Int32
const nId
= BeginTagImpl(pKey
, eType
, rString
);
547 // Store the id of the current structure element if
548 // - it is a list structure element
549 // - it is a list body element with children
550 // - rFrame is the first page frame
551 // - rFrame is a master frame
552 // - rFrame has objects anchored to it
553 // - rFrame is a row frame or cell frame in a split table row
557 const SwTextFrame
& rTextFrame
= static_cast<const SwTextFrame
&>(mpNumInfo
->mrFrame
);
558 SwTextNode
const*const pTextNd
= rTextFrame
.GetTextNodeForParaProps();
559 const SwNodeNum
* pNodeNum
= pTextNd
->GetNum(rTextFrame
.getRootFrame());
561 if ( vcl::PDFWriter::List
== eType
)
563 NumListIdMap
& rNumListIdMap(mpPDFExtOutDevData
->GetSwPDFState()->m_NumListIdMap
);
564 rNumListIdMap
[ pNodeNum
] = nId
;
566 else if ( vcl::PDFWriter::LIBody
== eType
)
568 NumListBodyIdMap
& rNumListBodyIdMap(mpPDFExtOutDevData
->GetSwPDFState()->m_NumListBodyIdMap
);
569 rNumListBodyIdMap
[ pNodeNum
] = nId
;
573 SetAttributes( eType
);
576 void SwTaggedPDFHelper::EndTag()
578 mpPDFExtOutDevData
->EndStructureElement();
580 #if OSL_DEBUG_LEVEL > 1
581 aStructStack
.pop_back();
587 // link the link annotation to the link structured element
588 void LinkLinkLink(vcl::PDFExtOutDevData
& rPDFExtOutDevData
, SwRect
const& rRect
)
590 const LinkIdMap
& rLinkIdMap(rPDFExtOutDevData
.GetSwPDFState()->m_LinkIdMap
);
591 const Point aCenter
= rRect
.Center();
592 auto aIter
= std::find_if(rLinkIdMap
.begin(), rLinkIdMap
.end(),
593 [&aCenter
](const IdMapEntry
& rEntry
) { return rEntry
.first
.Contains(aCenter
); });
594 if (aIter
!= rLinkIdMap
.end())
596 sal_Int32 nLinkId
= (*aIter
).second
;
597 rPDFExtOutDevData
.SetStructureAttributeNumerical(vcl::PDFWriter::LinkAnnotation
, nLinkId
);
602 // Sets the attributes according to the structure type.
603 void SwTaggedPDFHelper::SetAttributes( vcl::PDFWriter::StructElement eType
)
608 * ATTRIBUTES FOR BLSE
612 vcl::PDFWriter::StructAttributeValue eVal
;
613 const SwFrame
* pFrame
= &mpFrameInfo
->mrFrame
;
614 SwRectFnSet
aRectFnSet(pFrame
);
616 bool bPlacement
= false;
617 bool bWritingMode
= false;
618 bool bSpaceBefore
= false;
619 bool bSpaceAfter
= false;
620 bool bStartIndent
= false;
621 bool bEndIndent
= false;
622 bool bTextIndent
= false;
623 bool bTextAlign
= false;
625 bool bHeight
= false;
627 bool bRowSpan
= false;
628 bool bAltText
= false;
630 // Check which attributes to set:
634 case vcl::PDFWriter::Document
:
638 case vcl::PDFWriter::Note
:
642 case vcl::PDFWriter::Table
:
654 case vcl::PDFWriter::TableRow
:
659 case vcl::PDFWriter::TableHeader
:
660 mpPDFExtOutDevData
->SetStructureAttribute(vcl::PDFWriter::Scope
, vcl::PDFWriter::Column
);
662 case vcl::PDFWriter::TableData
:
670 case vcl::PDFWriter::Caption
:
671 if (pFrame
->IsSctFrame())
676 case vcl::PDFWriter::H1
:
677 case vcl::PDFWriter::H2
:
678 case vcl::PDFWriter::H3
:
679 case vcl::PDFWriter::H4
:
680 case vcl::PDFWriter::H5
:
681 case vcl::PDFWriter::H6
:
682 case vcl::PDFWriter::Paragraph
:
683 case vcl::PDFWriter::Heading
:
684 case vcl::PDFWriter::BlockQuote
:
696 case vcl::PDFWriter::Formula
:
697 case vcl::PDFWriter::Figure
:
705 case vcl::PDFWriter::Division
:
706 if (pFrame
->IsFlyFrame()) // this can be something else too
713 case vcl::PDFWriter::NonStructElement
:
714 if (pFrame
->IsHeaderFrame() || pFrame
->IsFooterFrame())
716 // ISO 14289-1:2014, Clause: 7.8
717 mpPDFExtOutDevData
->SetStructureAttribute(vcl::PDFWriter::Type
, vcl::PDFWriter::Pagination
);
718 mpPDFExtOutDevData
->SetStructureAttribute(vcl::PDFWriter::Subtype
,
719 pFrame
->IsHeaderFrame()
720 ? vcl::PDFWriter::Header
721 : vcl::PDFWriter::Footer
);
729 // Set the attributes:
733 eVal
= vcl::PDFWriter::TableHeader
== eType
||
734 vcl::PDFWriter::TableData
== eType
?
735 vcl::PDFWriter::Inline
:
736 vcl::PDFWriter::Block
;
738 mpPDFExtOutDevData
->SetStructureAttribute( vcl::PDFWriter::Placement
, eVal
);
743 eVal
= pFrame
->IsVertical() ?
744 vcl::PDFWriter::TbRl
:
745 pFrame
->IsRightToLeft() ?
746 vcl::PDFWriter::RlTb
:
747 vcl::PDFWriter::LrTb
;
749 if ( vcl::PDFWriter::LrTb
!= eVal
)
750 mpPDFExtOutDevData
->SetStructureAttribute( vcl::PDFWriter::WritingMode
, eVal
);
755 nVal
= aRectFnSet
.GetTopMargin(*pFrame
);
757 mpPDFExtOutDevData
->SetStructureAttributeNumerical( vcl::PDFWriter::SpaceBefore
, nVal
);
762 nVal
= aRectFnSet
.GetBottomMargin(*pFrame
);
764 mpPDFExtOutDevData
->SetStructureAttributeNumerical( vcl::PDFWriter::SpaceAfter
, nVal
);
769 nVal
= aRectFnSet
.GetLeftMargin(*pFrame
);
771 mpPDFExtOutDevData
->SetStructureAttributeNumerical( vcl::PDFWriter::StartIndent
, nVal
);
776 nVal
= aRectFnSet
.GetRightMargin(*pFrame
);
778 mpPDFExtOutDevData
->SetStructureAttributeNumerical( vcl::PDFWriter::EndIndent
, nVal
);
783 OSL_ENSURE( pFrame
->IsTextFrame(), "Frame type <-> tag attribute mismatch" );
784 const SvxFirstLineIndentItem
& rFirstLine(
785 static_cast<const SwTextFrame
*>(pFrame
)->GetTextNodeForParaProps()->GetSwAttrSet().GetFirstLineIndent());
786 nVal
= rFirstLine
.GetTextFirstLineOffset();
788 mpPDFExtOutDevData
->SetStructureAttributeNumerical( vcl::PDFWriter::TextIndent
, nVal
);
793 OSL_ENSURE( pFrame
->IsTextFrame(), "Frame type <-> tag attribute mismatch" );
794 const SwAttrSet
& aSet
= static_cast<const SwTextFrame
*>(pFrame
)->GetTextNodeForParaProps()->GetSwAttrSet();
795 const SvxAdjust nAdjust
= aSet
.GetAdjust().GetAdjust();
796 if ( SvxAdjust::Block
== nAdjust
|| SvxAdjust::Center
== nAdjust
||
797 ( (pFrame
->IsRightToLeft() && SvxAdjust::Left
== nAdjust
) ||
798 (!pFrame
->IsRightToLeft() && SvxAdjust::Right
== nAdjust
) ) )
800 eVal
= SvxAdjust::Block
== nAdjust
?
801 vcl::PDFWriter::Justify
:
802 SvxAdjust::Center
== nAdjust
?
803 vcl::PDFWriter::Center
:
806 mpPDFExtOutDevData
->SetStructureAttribute( vcl::PDFWriter::TextAlign
, eVal
);
810 // ISO 14289-1:2014, Clause: 7.3
811 // ISO 14289-1:2014, Clause: 7.7
812 // For images (but not embedded objects), an ObjectInfoPrimitive2D is
813 // created, but it's not evaluated by VclMetafileProcessor2D any more;
814 // that would require producing StructureTagPrimitive2D here but that
815 // looks impossible so instead duplicate the code that sets the Alt
819 SwFlyFrameFormat
const& rFly(*static_cast<SwFlyFrame
const*>(pFrame
)->GetFormat());
821 (rFly
.GetObjTitle().isEmpty() || rFly
.GetObjDescription().isEmpty())
822 ? OUString() : OUString(" - "));
823 OUString
const altText(rFly
.GetObjTitle() + sep
+ rFly
.GetObjDescription());
824 if (!altText
.isEmpty())
826 mpPDFExtOutDevData
->SetAlternateText(altText
);
832 nVal
= aRectFnSet
.GetWidth(pFrame
->getFrameArea());
833 mpPDFExtOutDevData
->SetStructureAttributeNumerical( vcl::PDFWriter::Width
, nVal
);
838 nVal
= aRectFnSet
.GetHeight(pFrame
->getFrameArea());
839 mpPDFExtOutDevData
->SetStructureAttributeNumerical( vcl::PDFWriter::Height
, nVal
);
844 // BBox only for non-split tables:
845 if ( vcl::PDFWriter::Table
!= eType
||
846 ( pFrame
->IsTabFrame() &&
847 !static_cast<const SwTabFrame
*>(pFrame
)->IsFollow() &&
848 !static_cast<const SwTabFrame
*>(pFrame
)->HasFollow() ) )
850 mpPDFExtOutDevData
->SetStructureBoundingBox(pFrame
->getFrameArea().SVRect());
856 if ( pFrame
->IsCellFrame() )
858 const SwCellFrame
* pThisCell
= static_cast<const SwCellFrame
*>(pFrame
);
859 nVal
= pThisCell
->GetTabBox()->getRowSpan();
861 mpPDFExtOutDevData
->SetStructureAttributeNumerical( vcl::PDFWriter::RowSpan
, nVal
);
863 // calculate colspan:
864 const SwTabFrame
* pTabFrame
= pThisCell
->FindTabFrame();
865 const SwTable
* pTable
= pTabFrame
->GetTable();
867 SwRectFnSet
fnRectX(pTabFrame
);
869 const TableColumnsMapEntry
& rCols(mpPDFExtOutDevData
->GetSwPDFState()->m_TableColumnsMap
[pTable
]);
871 const tools::Long nLeft
= fnRectX
.GetLeft(pThisCell
->getFrameArea());
872 const tools::Long nRight
= fnRectX
.GetRight(pThisCell
->getFrameArea());
873 const TableColumnsMapEntry::const_iterator aLeftIter
= rCols
.find( nLeft
);
874 const TableColumnsMapEntry::const_iterator aRightIter
= rCols
.find( nRight
);
876 OSL_ENSURE( aLeftIter
!= rCols
.end() && aRightIter
!= rCols
.end(), "Colspan trouble" );
877 if ( aLeftIter
!= rCols
.end() && aRightIter
!= rCols
.end() )
879 nVal
= std::distance( aLeftIter
, aRightIter
);
881 mpPDFExtOutDevData
->SetStructureAttributeNumerical( vcl::PDFWriter::ColSpan
, nVal
);
886 if (mpFrameInfo
->m_isLink
)
888 SwRect
const aRect(mpFrameInfo
->mrFrame
.getFrameArea());
889 LinkLinkLink(*mpPDFExtOutDevData
, aRect
);
894 * ATTRIBUTES FOR ILSE
896 else if ( mpPorInfo
)
898 const SwLinePortion
* pPor
= &mpPorInfo
->mrPor
;
899 const SwTextPaintInfo
& rInf
= mpPorInfo
->mrTextPainter
.GetInfo();
901 bool bActualText
= false;
902 bool bBaselineShift
= false;
903 bool bTextDecorationType
= false;
904 bool bLinkAttribute
= false;
905 bool bLanguage
= false;
907 // Check which attributes to set:
911 case vcl::PDFWriter::Span
:
912 case vcl::PDFWriter::Quote
:
913 case vcl::PDFWriter::Code
:
914 if( PortionType::HyphenStr
== pPor
->GetWhichPor() || PortionType::SoftHyphenStr
== pPor
->GetWhichPor() ||
915 PortionType::Hyphen
== pPor
->GetWhichPor() || PortionType::SoftHyphen
== pPor
->GetWhichPor() )
920 bTextDecorationType
=
925 case vcl::PDFWriter::Link
:
926 bTextDecorationType
=
932 case vcl::PDFWriter::BibEntry
:
933 bTextDecorationType
=
945 OUString aActualText
;
946 if (pPor
->GetWhichPor() == PortionType::SoftHyphen
|| pPor
->GetWhichPor() == PortionType::Hyphen
)
947 aActualText
= OUString(u
'\x00ad'); // soft hyphen
949 aActualText
= rInf
.GetText().copy(sal_Int32(rInf
.GetIdx()), sal_Int32(pPor
->GetLen()));
950 mpPDFExtOutDevData
->SetActualText( aActualText
);
953 if ( bBaselineShift
)
955 // TODO: Calculate correct values!
956 nVal
= rInf
.GetFont()->GetEscapement();
957 if ( nVal
> 0 ) nVal
= 33;
958 else if ( nVal
< 0 ) nVal
= -33;
962 nVal
= nVal
* pPor
->Height() / 100;
963 mpPDFExtOutDevData
->SetStructureAttributeNumerical( vcl::PDFWriter::BaselineShift
, nVal
);
967 if ( bTextDecorationType
)
969 if ( LINESTYLE_NONE
!= rInf
.GetFont()->GetUnderline() )
970 mpPDFExtOutDevData
->SetStructureAttribute( vcl::PDFWriter::TextDecorationType
, vcl::PDFWriter::Underline
);
971 if ( LINESTYLE_NONE
!= rInf
.GetFont()->GetOverline() )
972 mpPDFExtOutDevData
->SetStructureAttribute( vcl::PDFWriter::TextDecorationType
, vcl::PDFWriter::Overline
);
973 if ( STRIKEOUT_NONE
!= rInf
.GetFont()->GetStrikeout() )
974 mpPDFExtOutDevData
->SetStructureAttribute( vcl::PDFWriter::TextDecorationType
, vcl::PDFWriter::LineThrough
);
975 if ( FontEmphasisMark::NONE
!= rInf
.GetFont()->GetEmphasisMark() )
976 mpPDFExtOutDevData
->SetStructureAttribute( vcl::PDFWriter::TextDecorationType
, vcl::PDFWriter::Overline
);
982 const LanguageType nCurrentLanguage
= rInf
.GetFont()->GetLanguage();
983 const LanguageType
nDefaultLang(mpPDFExtOutDevData
->GetSwPDFState()->m_eLanguageDefault
);
985 if ( nDefaultLang
!= nCurrentLanguage
)
986 mpPDFExtOutDevData
->SetStructureAttributeNumerical( vcl::PDFWriter::Language
, static_cast<sal_uInt16
>(nCurrentLanguage
) );
989 if ( bLinkAttribute
)
992 rInf
.CalcRect( *pPor
, &aPorRect
);
993 LinkLinkLink(*mpPDFExtOutDevData
, aPorRect
);
996 else if (mpNumInfo
&& eType
== vcl::PDFWriter::List
)
998 SwTextFrame
const& rFrame(static_cast<SwTextFrame
const&>(mpNumInfo
->mrFrame
));
999 SwTextNode
const& rNode(*rFrame
.GetTextNodeForParaProps());
1000 SwNumRule
const*const pNumRule
= rNode
.GetNumRule();
1001 assert(pNumRule
); // was required for List
1003 auto ToPDFListNumbering
= [](SvxNumberFormat
const& rFormat
) {
1004 switch (rFormat
.GetNumberingType())
1006 case css::style::NumberingType::CHARS_UPPER_LETTER
:
1007 return vcl::PDFWriter::UpperAlpha
;
1008 case css::style::NumberingType::CHARS_LOWER_LETTER
:
1009 return vcl::PDFWriter::LowerAlpha
;
1010 case css::style::NumberingType::ROMAN_UPPER
:
1011 return vcl::PDFWriter::UpperRoman
;
1012 case css::style::NumberingType::ROMAN_LOWER
:
1013 return vcl::PDFWriter::LowerRoman
;
1014 case css::style::NumberingType::ARABIC
:
1015 return vcl::PDFWriter::Decimal
;
1016 case css::style::NumberingType::CHAR_SPECIAL
:
1017 switch (rFormat
.GetBulletChar())
1019 case u
'\u2022': case u
'\uE12C': case u
'\uE01E': case u
'\uE437':
1020 return vcl::PDFWriter::Disc
;
1021 case u
'\u2218': case u
'\u25CB': case u
'\u25E6':
1022 return vcl::PDFWriter::Circle
;
1023 case u
'\u25A0': case u
'\u25AA': case u
'\uE00A':
1024 return vcl::PDFWriter::Square
;
1026 return vcl::PDFWriter::NONE
;
1028 default: // the other 50 types
1029 return vcl::PDFWriter::NONE
;
1033 // Note: for every level, BeginNumberedListStructureElements() produces
1034 // a separate List element, so even though in PDF this is limited to
1035 // the whole List we can just export the current level here.
1036 vcl::PDFWriter::StructAttributeValue
const value(
1037 ToPDFListNumbering(pNumRule
->Get(rNode
.GetActualListLevel())));
1038 // ISO 14289-1:2014, Clause: 7.6
1039 mpPDFExtOutDevData
->SetStructureAttribute(vcl::PDFWriter::ListNumbering
, value
);
1043 void SwTaggedPDFHelper::BeginNumberedListStructureElements()
1045 OSL_ENSURE( mpNumInfo
, "List without mpNumInfo?" );
1049 const SwFrame
& rFrame
= mpNumInfo
->mrFrame
;
1050 assert(rFrame
.IsTextFrame());
1051 const SwTextFrame
& rTextFrame
= static_cast<const SwTextFrame
&>(rFrame
);
1053 // Lowers of NonStructureElements should not be considered:
1054 if (lcl_IsInNonStructEnv(rTextFrame
))
1057 // do it for the first one in the follow chain that has content
1058 for (SwFlowFrame
const* pPrecede
= rTextFrame
.GetPrecede(); pPrecede
; pPrecede
= pPrecede
->GetPrecede())
1060 SwTextFrame
const*const pText(static_cast<SwTextFrame
const*>(pPrecede
));
1061 if (!pText
->HasPara() || pText
->GetPara()->HasContentPortions())
1067 const SwTextNode
*const pTextNd
= rTextFrame
.GetTextNodeForParaProps();
1068 const SwNumRule
* pNumRule
= pTextNd
->GetNumRule();
1069 const SwNodeNum
* pNodeNum
= pTextNd
->GetNum(rTextFrame
.getRootFrame());
1071 const bool bNumbered
= !pTextNd
->IsOutline() && pNodeNum
&& pNodeNum
->GetParent() && pNumRule
;
1073 // Check, if we have to reopen a list or a list body:
1075 // Paragraph is numbered/bulleted
1079 const SwNumberTreeNode
* pParent
= pNodeNum
->GetParent();
1080 const bool bSameNumbering
= lcl_HasPreviousParaSameNumRule(rTextFrame
, *pTextNd
);
1082 // Second condition: current numbering is not 'interrupted'
1083 if ( bSameNumbering
)
1085 sal_Int32 nReopenTag
= -1;
1088 // 1. We have to reopen an existing list body tag:
1089 // - If the current node is either the first child of its parent
1090 // and its level > 1 or
1091 // - Numbering should restart at the current node and its level > 1
1092 // - The current item has no label
1093 const bool bNewSubListStart
= pParent
->GetParent() && (pParent
->IsFirst( pNodeNum
) || pTextNd
->IsListRestart() );
1094 const bool bNoLabel
= !pTextNd
->IsCountedInList() && !pTextNd
->IsListRestart();
1095 if ( bNewSubListStart
|| bNoLabel
)
1097 // Fine, we try to reopen the appropriate list body
1098 NumListBodyIdMap
& rNumListBodyIdMap(mpPDFExtOutDevData
->GetSwPDFState()->m_NumListBodyIdMap
);
1100 if ( bNewSubListStart
)
1102 // The list body tag associated with the parent has to be reopened
1103 // to start a new list inside the list body
1104 NumListBodyIdMap::const_iterator aIter
;
1107 aIter
= rNumListBodyIdMap
.find( pParent
);
1108 while ( aIter
== rNumListBodyIdMap
.end() && nullptr != ( pParent
= pParent
->GetParent() ) );
1110 if ( aIter
!= rNumListBodyIdMap
.end() )
1111 nReopenTag
= (*aIter
).second
;
1113 else // if(bNoLabel)
1115 // The list body tag of a 'counted' predecessor has to be reopened
1116 const SwNumberTreeNode
* pPrevious
= pNodeNum
->GetPred(true);
1119 if ( pPrevious
->IsCounted())
1121 // get id of list body tag
1122 const NumListBodyIdMap::const_iterator aIter
= rNumListBodyIdMap
.find( pPrevious
);
1123 if ( aIter
!= rNumListBodyIdMap
.end() )
1125 nReopenTag
= (*aIter
).second
;
1129 pPrevious
= pPrevious
->GetPred(true);
1133 // 2. We have to reopen an existing list tag:
1134 else if ( !pParent
->IsFirst( pNodeNum
) && !pTextNd
->IsListRestart() )
1136 // any other than the first node in a list level has to reopen the current
1137 // list. The current list is associated in a map with the first child of the list:
1138 NumListIdMap
& rNumListIdMap(mpPDFExtOutDevData
->GetSwPDFState()->m_NumListIdMap
);
1140 // Search backwards and check if any of the previous nodes has a list associated with it:
1141 const SwNumberTreeNode
* pPrevious
= pNodeNum
->GetPred(true);
1144 // get id of list tag
1145 const NumListIdMap::const_iterator aIter
= rNumListIdMap
.find( pPrevious
);
1146 if ( aIter
!= rNumListIdMap
.end() )
1148 nReopenTag
= (*aIter
).second
;
1152 pPrevious
= pPrevious
->GetPred(true);
1156 if ( -1 != nReopenTag
)
1158 m_nRestoreCurrentTag
= mpPDFExtOutDevData
->GetCurrentStructureElement();
1159 mpPDFExtOutDevData
->SetCurrentStructureElement( nReopenTag
);
1161 #if OSL_DEBUG_LEVEL > 1
1162 aStructStack
.push_back( 99 );
1168 // clear list maps in case a list has been interrupted
1169 NumListIdMap
& rNumListIdMap(mpPDFExtOutDevData
->GetSwPDFState()->m_NumListIdMap
);
1170 rNumListIdMap
.clear();
1171 NumListBodyIdMap
& rNumListBodyIdMap(mpPDFExtOutDevData
->GetSwPDFState()->m_NumListBodyIdMap
);
1172 rNumListBodyIdMap
.clear();
1176 const bool bNewListTag
= (pNodeNum
->GetParent()->IsFirst( pNodeNum
) || pTextNd
->IsListRestart() || !bSameNumbering
);
1177 const bool bNewItemTag
= bNewListTag
|| pTextNd
->IsCountedInList(); // If the text node is not counted, we do not start a new list item:
1180 BeginTag( vcl::PDFWriter::List
, aListString
);
1184 BeginTag( vcl::PDFWriter::ListItem
, aListItemString
);
1185 assert(rTextFrame
.GetPara());
1186 // check whether to open LBody now or delay until after Lbl
1187 if (!rTextFrame
.GetPara()->HasNumberingPortion(SwParaPortion::OnlyNumbering
))
1189 BeginTag(vcl::PDFWriter::LIBody
, aListBodyString
);
1194 void SwTaggedPDFHelper::BeginBlockStructureElements()
1196 const SwFrame
* pFrame
= &mpFrameInfo
->mrFrame
;
1198 // Lowers of NonStructureElements should not be considered:
1200 if (lcl_IsInNonStructEnv(*pFrame
) && !pFrame
->IsFlyFrame())
1203 // Check if we have to reopen an existing structure element.
1204 // This has to be done e.g., if pFrame is a follow frame.
1205 if ( CheckReopenTag() )
1208 sal_uInt16 nPDFType
= USHRT_MAX
;
1211 switch ( pFrame
->GetType() )
1217 case SwFrameType::Page
:
1219 // Document: Document
1221 nPDFType
= vcl::PDFWriter::Document
;
1222 aPDFType
= aDocumentString
;
1225 case SwFrameType::Header
:
1226 case SwFrameType::Footer
:
1228 // Header, Footer: NonStructElement
1230 nPDFType
= vcl::PDFWriter::NonStructElement
;
1233 case SwFrameType::FtnCont
:
1235 // Footnote container: Division
1237 nPDFType
= vcl::PDFWriter::Division
;
1238 aPDFType
= aDivString
;
1241 case SwFrameType::Ftn
:
1243 // Footnote frame: Note
1245 // Note: vcl::PDFWriter::Note is actually a ILSE. Nevertheless
1246 // we treat it like a grouping element!
1247 nPDFType
= vcl::PDFWriter::Note
;
1248 aPDFType
= aNoteString
;
1251 case SwFrameType::Section
:
1253 // Section: TOX, Index, or Sect
1256 const SwSection
* pSection
=
1257 static_cast<const SwSectionFrame
*>(pFrame
)->GetSection();
1259 // open all parent sections, so that the SEs of sections
1260 // are nested in the same way as their SwSectionNodes
1261 std::vector
<SwSection
const*> parents
;
1262 for (SwSection
const* pParent
= pSection
->GetParent();
1263 pParent
!= nullptr; pParent
= pParent
->GetParent())
1265 parents
.push_back(pParent
);
1267 for (auto it
= parents
.rbegin(); it
!= parents
.rend(); ++it
)
1269 // key is the SwSection - see lcl_GetKeyFromFrame()
1273 FrameTagSet
& rFrameTagSet(mpPDFExtOutDevData
->GetSwPDFState()->m_FrameTagSet
);
1274 if (rFrameTagSet
.find(pSection
) != rFrameTagSet
.end())
1276 // special case: section may have *multiple* master frames,
1277 // when it is interrupted by nested section - reopen!
1278 OpenTagImpl(pSection
);
1281 else if (SectionType::ToxHeader
== pSection
->GetType())
1283 nPDFType
= vcl::PDFWriter::Caption
;
1284 aPDFType
= aCaptionString
;
1286 else if (SectionType::ToxContent
== pSection
->GetType())
1288 const SwTOXBase
* pTOXBase
= pSection
->GetTOXBase();
1291 if ( TOX_INDEX
== pTOXBase
->GetType() )
1293 nPDFType
= vcl::PDFWriter::Index
;
1294 aPDFType
= aIndexString
;
1298 nPDFType
= vcl::PDFWriter::TOC
;
1299 aPDFType
= aTOCString
;
1303 else if ( SectionType::Content
== pSection
->GetType() )
1305 nPDFType
= vcl::PDFWriter::Section
;
1306 aPDFType
= aSectString
;
1312 * BLOCK-LEVEL STRUCTURE ELEMENTS
1315 case SwFrameType::Txt
:
1317 SwTextFrame
const& rTextFrame(*static_cast<const SwTextFrame
*>(pFrame
));
1318 const SwTextNode
*const pTextNd(rTextFrame
.GetTextNodeForParaProps());
1320 // lazy open LBody after Lbl
1321 if (!pTextNd
->IsOutline()
1322 && rTextFrame
.GetPara()->HasNumberingPortion(SwParaPortion::OnlyNumbering
))
1324 sal_Int32
const nId
= BeginTagImpl(nullptr, vcl::PDFWriter::LIBody
, aListBodyString
);
1325 SwNodeNum
const*const pNodeNum(pTextNd
->GetNum(rTextFrame
.getRootFrame()));
1326 NumListBodyIdMap
& rNumListBodyIdMap(mpPDFExtOutDevData
->GetSwPDFState()->m_NumListBodyIdMap
);
1327 rNumListBodyIdMap
[ pNodeNum
] = nId
;
1330 const SwFormat
* pTextFormat
= pTextNd
->GetFormatColl();
1331 const SwFormat
* pParentTextFormat
= pTextFormat
? pTextFormat
->DerivedFrom() : nullptr;
1333 OUString sStyleName
;
1334 OUString sParentStyleName
;
1337 SwStyleNameMapper::FillProgName( pTextFormat
->GetName(), sStyleName
, SwGetPoolIdFromName::TxtColl
);
1338 if ( pParentTextFormat
)
1339 SwStyleNameMapper::FillProgName( pParentTextFormat
->GetName(), sParentStyleName
, SwGetPoolIdFromName::TxtColl
);
1341 // This is the default. If the paragraph could not be mapped to
1342 // any of the standard pdf tags, we write a user defined tag
1343 // <stylename> with role = P
1344 nPDFType
= vcl::PDFWriter::Paragraph
;
1345 aPDFType
= sStyleName
;
1347 // Quotations: BlockQuote
1349 if (sStyleName
== aQuotations
)
1351 nPDFType
= vcl::PDFWriter::BlockQuote
;
1352 aPDFType
= aBlockQuoteString
;
1357 else if (sStyleName
== aCaption
)
1359 nPDFType
= vcl::PDFWriter::Caption
;
1360 aPDFType
= aCaptionString
;
1365 else if (sParentStyleName
== aCaption
)
1367 nPDFType
= vcl::PDFWriter::Caption
;
1368 aPDFType
= sStyleName
+ aCaptionString
;
1373 else if (sStyleName
== aHeading
)
1375 nPDFType
= vcl::PDFWriter::Heading
;
1376 aPDFType
= aHString
;
1381 if (int nRealLevel
= pTextNd
->GetAttrOutlineLevel() - 1;
1383 && !pTextNd
->IsInRedlines()
1384 && sw::IsParaPropsNode(*pFrame
->getRootFrame(), *pTextNd
))
1389 aPDFType
= aH1String
;
1392 aPDFType
= aH2String
;
1395 aPDFType
= aH3String
;
1398 aPDFType
= aH4String
;
1401 aPDFType
= aH5String
;
1404 aPDFType
= aH6String
;
1407 aPDFType
= aH7String
;
1410 aPDFType
= aH8String
;
1413 aPDFType
= aH9String
;
1416 aPDFType
= aH10String
;
1423 // PDF/UA allows unlimited headings, but PDF only up to H6
1424 // ... and apparently the extra H7.. must be declared in
1425 // RoleMap, or veraPDF complains.
1426 nRealLevel
= std::min(nRealLevel
, 5);
1427 nPDFType
= o3tl::narrowing
<sal_uInt16
>(vcl::PDFWriter::H1
+ nRealLevel
);
1432 else if ( pFrame
->IsInSct() )
1434 const SwSectionFrame
* pSctFrame
= pFrame
->FindSctFrame();
1435 const SwSection
* pSection
= pSctFrame
->GetSection();
1437 if ( SectionType::ToxContent
== pSection
->GetType() )
1439 const SwTOXBase
* pTOXBase
= pSection
->GetTOXBase();
1440 if ( pTOXBase
&& TOX_INDEX
!= pTOXBase
->GetType() )
1442 // Special case: Open additional TOCI tag:
1443 BeginTagImpl(nullptr, vcl::PDFWriter::TOCI
, aTOCIString
);
1450 case SwFrameType::Tab
:
1454 nPDFType
= vcl::PDFWriter::Table
;
1455 aPDFType
= aTableString
;
1458 // set up table column data:
1459 const SwTabFrame
* pTabFrame
= static_cast<const SwTabFrame
*>(pFrame
);
1460 const SwTable
* pTable
= pTabFrame
->GetTable();
1462 TableColumnsMap
& rTableColumnsMap(mpPDFExtOutDevData
->GetSwPDFState()->m_TableColumnsMap
);
1463 const TableColumnsMap::const_iterator aIter
= rTableColumnsMap
.find( pTable
);
1465 if ( aIter
== rTableColumnsMap
.end() )
1467 SwRectFnSet
aRectFnSet(pTabFrame
);
1468 TableColumnsMapEntry
& rCols
= rTableColumnsMap
[ pTable
];
1470 const SwTabFrame
* pMasterFrame
= pTabFrame
->IsFollow() ? pTabFrame
->FindMaster( true ) : pTabFrame
;
1472 while ( pMasterFrame
)
1474 const SwRowFrame
* pRowFrame
= static_cast<const SwRowFrame
*>(pMasterFrame
->GetLower());
1478 const SwFrame
* pCellFrame
= pRowFrame
->GetLower();
1480 const tools::Long nLeft
= aRectFnSet
.GetLeft(pCellFrame
->getFrameArea());
1481 rCols
.insert( nLeft
);
1483 while ( pCellFrame
)
1485 const tools::Long nRight
= aRectFnSet
.GetRight(pCellFrame
->getFrameArea());
1486 rCols
.insert( nRight
);
1487 pCellFrame
= pCellFrame
->GetNext();
1489 pRowFrame
= static_cast<const SwRowFrame
*>(pRowFrame
->GetNext());
1491 pMasterFrame
= pMasterFrame
->GetFollow();
1502 case SwFrameType::Row
:
1506 if ( !static_cast<const SwRowFrame
*>(pFrame
)->IsRepeatedHeadline() )
1508 nPDFType
= vcl::PDFWriter::TableRow
;
1509 aPDFType
= aTRString
;
1513 nPDFType
= vcl::PDFWriter::NonStructElement
;
1517 case SwFrameType::Cell
:
1519 // CellFrame: TH, TD
1522 const SwTabFrame
* pTable
= static_cast<const SwCellFrame
*>(pFrame
)->FindTabFrame();
1523 if ( pTable
->IsInHeadline( *pFrame
) || lcl_IsHeadlineCell( *static_cast<const SwCellFrame
*>(pFrame
) ) )
1525 nPDFType
= vcl::PDFWriter::TableHeader
;
1526 aPDFType
= aTHString
;
1530 nPDFType
= vcl::PDFWriter::TableData
;
1531 aPDFType
= aTDString
;
1540 case SwFrameType::Fly
:
1542 // FlyFrame: Figure, Formula, Control
1543 // fly in content or fly at page
1544 if (mpFrameInfo
->m_isLink
)
1545 { // tdf#154939 additional inner link element for flys
1546 nPDFType
= vcl::PDFWriter::Link
;
1547 aPDFType
= aLinkString
;
1551 const SwFlyFrame
* pFly
= static_cast<const SwFlyFrame
*>(pFrame
);
1552 if (pFly
->GetAnchorFrame()->FindFooterOrHeader() != nullptr
1553 || pFly
->GetFrameFormat().GetAttrSet().Get(RES_DECORATIVE
).GetValue())
1555 nPDFType
= vcl::PDFWriter::NonStructElement
;
1557 else if (pFly
->Lower() && pFly
->Lower()->IsNoTextFrame())
1559 bool bFormula
= false;
1561 const SwNoTextFrame
* pNoTextFrame
= static_cast<const SwNoTextFrame
*>(pFly
->Lower());
1562 SwOLENode
* pOLENd
= const_cast<SwOLENode
*>(pNoTextFrame
->GetNode()->GetOLENode());
1565 SwOLEObj
& aOLEObj
= pOLENd
->GetOLEObj();
1566 uno::Reference
< embed::XEmbeddedObject
> aRef
= aOLEObj
.GetOleRef();
1569 bFormula
= 0 != SotExchange::IsMath( SvGlobalName( aRef
->getClassID() ) );
1574 nPDFType
= vcl::PDFWriter::Formula
;
1575 aPDFType
= aFormulaString
;
1579 nPDFType
= vcl::PDFWriter::Figure
;
1580 aPDFType
= aFigureString
;
1585 nPDFType
= vcl::PDFWriter::Division
;
1586 aPDFType
= aDivString
;
1594 if ( USHRT_MAX
!= nPDFType
)
1596 BeginTag( static_cast<vcl::PDFWriter::StructElement
>(nPDFType
), aPDFType
);
1600 void SwTaggedPDFHelper::EndStructureElements()
1602 if (mpFrameInfo
!= nullptr)
1604 if (mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentSpan
)
1605 { // close span at end of paragraph
1606 mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentSpan
.reset();
1607 ++m_nEndStructureElement
;
1609 if (mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentLink
)
1610 { // close link at end of paragraph
1611 mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentLink
.reset();
1612 ++m_nEndStructureElement
;
1616 while ( m_nEndStructureElement
> 0 )
1619 --m_nEndStructureElement
;
1625 void SwTaggedPDFHelper::EndCurrentSpan()
1627 mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentSpan
.reset();
1628 EndTag(); // close span
1631 void SwTaggedPDFHelper::CreateCurrentSpan(
1632 SwTextPaintInfo
const& rInf
, OUString
const& rStyleName
)
1634 assert(!mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentSpan
);
1635 mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentSpan
.emplace(
1636 SwEnhancedPDFState::Span
{
1637 rInf
.GetFont()->GetUnderline(),
1638 rInf
.GetFont()->GetOverline(),
1639 rInf
.GetFont()->GetStrikeout(),
1640 rInf
.GetFont()->GetEmphasisMark(),
1641 rInf
.GetFont()->GetEscapement(),
1642 rInf
.GetFont()->GetActual(),
1643 rInf
.GetFont()->GetLanguage(),
1645 // leave it open to let next portion decide to merge or close
1646 --m_nEndStructureElement
;
1649 bool SwTaggedPDFHelper::CheckContinueSpan(
1650 SwTextPaintInfo
const& rInf
, std::u16string_view
const rStyleName
,
1651 SwTextAttr
const*const pInetFormatAttr
)
1653 // for now, don't create span inside of link - this should be very rare
1654 // situation and it looks complicated to implement.
1655 assert(!mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentSpan
1656 || !mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentLink
);
1657 if (mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentLink
)
1659 if (pInetFormatAttr
&& pInetFormatAttr
== *mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentLink
)
1665 mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentLink
.reset();
1670 if (mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentSpan
&& pInetFormatAttr
)
1676 if (!mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentSpan
)
1679 SwEnhancedPDFState::Span
const& rCurrent(*mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentSpan
);
1681 bool const ret(rCurrent
.eUnderline
== rInf
.GetFont()->GetUnderline()
1682 && rCurrent
.eOverline
== rInf
.GetFont()->GetOverline()
1683 && rCurrent
.eStrikeout
== rInf
.GetFont()->GetStrikeout()
1684 && rCurrent
.eFontEmphasis
== rInf
.GetFont()->GetEmphasisMark()
1685 && rCurrent
.nEscapement
== rInf
.GetFont()->GetEscapement()
1686 && rCurrent
.nScript
== rInf
.GetFont()->GetActual()
1687 && rCurrent
.nLang
== rInf
.GetFont()->GetLanguage()
1688 && rCurrent
.StyleName
== rStyleName
);
1696 void SwTaggedPDFHelper::BeginInlineStructureElements()
1698 const SwLinePortion
* pPor
= &mpPorInfo
->mrPor
;
1699 const SwTextPaintInfo
& rInf
= mpPorInfo
->mrTextPainter
.GetInfo();
1700 const SwTextFrame
* pFrame
= rInf
.GetTextFrame();
1702 // Lowers of NonStructureElements should not be considered:
1704 if ( lcl_IsInNonStructEnv( *pFrame
) )
1707 std::pair
<SwTextNode
const*, sal_Int32
> const pos(
1708 pFrame
->MapViewToModel(rInf
.GetIdx()));
1709 SwTextAttr
const*const pInetFormatAttr
=
1710 pos
.first
->GetTextAttrAt(pos
.second
, RES_TXTATR_INETFMT
);
1712 OUString sStyleName
;
1713 if (!pInetFormatAttr
)
1715 std::vector
<SwTextAttr
*> const charAttrs(
1716 pos
.first
->GetTextAttrsAt(pos
.second
, RES_TXTATR_CHARFMT
));
1717 // TODO: handle more than 1 char style?
1718 const SwCharFormat
* pCharFormat
= (charAttrs
.size())
1719 ? (*charAttrs
.begin())->GetCharFormat().GetCharFormat() : nullptr;
1721 SwStyleNameMapper::FillProgName( pCharFormat
->GetName(), sStyleName
, SwGetPoolIdFromName::TxtColl
);
1724 // note: ILSE may be nested, so only end the span if needed to start new one
1725 bool const isContinueSpan(CheckContinueSpan(rInf
, sStyleName
, pInetFormatAttr
));
1727 sal_uInt16 nPDFType
= USHRT_MAX
;
1730 switch ( pPor
->GetWhichPor() )
1732 case PortionType::Hyphen
:
1733 case PortionType::SoftHyphen
:
1734 // Check for alternative spelling:
1735 case PortionType::HyphenStr
:
1736 case PortionType::SoftHyphenStr
:
1737 nPDFType
= vcl::PDFWriter::Span
;
1738 aPDFType
= aSpanString
;
1741 case PortionType::Lay
:
1742 case PortionType::Text
:
1743 case PortionType::Para
:
1746 if( pInetFormatAttr
)
1748 if (!isContinueSpan
)
1750 nPDFType
= vcl::PDFWriter::Link
;
1751 aPDFType
= aLinkString
;
1752 assert(!mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentLink
);
1753 mpPDFExtOutDevData
->GetSwPDFState()->m_oCurrentLink
.emplace(pInetFormatAttr
);
1754 // leave it open to let next portion decide to merge or close
1755 --m_nEndStructureElement
;
1758 // Check for Quote/Code character style:
1759 else if (sStyleName
== aQuotation
)
1761 if (!isContinueSpan
)
1763 nPDFType
= vcl::PDFWriter::Quote
;
1764 aPDFType
= aQuoteString
;
1765 CreateCurrentSpan(rInf
, sStyleName
);
1768 else if (sStyleName
== aSourceText
)
1770 if (!isContinueSpan
)
1772 nPDFType
= vcl::PDFWriter::Code
;
1773 aPDFType
= aCodeString
;
1774 CreateCurrentSpan(rInf
, sStyleName
);
1777 else if (!isContinueSpan
)
1779 const LanguageType nCurrentLanguage
= rInf
.GetFont()->GetLanguage();
1780 const SwFontScript nFont
= rInf
.GetFont()->GetActual();
1781 const LanguageType
nDefaultLang(mpPDFExtOutDevData
->GetSwPDFState()->m_eLanguageDefault
);
1783 if ( LINESTYLE_NONE
!= rInf
.GetFont()->GetUnderline() ||
1784 LINESTYLE_NONE
!= rInf
.GetFont()->GetOverline() ||
1785 STRIKEOUT_NONE
!= rInf
.GetFont()->GetStrikeout() ||
1786 FontEmphasisMark::NONE
!= rInf
.GetFont()->GetEmphasisMark() ||
1787 0 != rInf
.GetFont()->GetEscapement() ||
1788 SwFontScript::Latin
!= nFont
||
1789 nCurrentLanguage
!= nDefaultLang
||
1790 !sStyleName
.isEmpty())
1792 nPDFType
= vcl::PDFWriter::Span
;
1793 if (!sStyleName
.isEmpty())
1794 aPDFType
= sStyleName
;
1796 aPDFType
= aSpanString
;
1797 CreateCurrentSpan(rInf
, sStyleName
);
1803 case PortionType::Footnote
:
1804 nPDFType
= vcl::PDFWriter::Link
;
1805 aPDFType
= aLinkString
;
1808 case PortionType::Field
:
1810 // check field type:
1811 TextFrameIndex
const nIdx
= static_cast<const SwFieldPortion
*>(pPor
)->IsFollow()
1812 ? rInf
.GetIdx() - TextFrameIndex(1)
1814 const SwTextAttr
* pHint
= mpPorInfo
->mrTextPainter
.GetAttr( nIdx
);
1815 if ( pHint
&& RES_TXTATR_FIELD
== pHint
->Which() )
1817 const SwField
* pField
= pHint
->GetFormatField().GetField();
1818 if ( SwFieldIds::GetRef
== pField
->Which() )
1820 nPDFType
= vcl::PDFWriter::Link
;
1821 aPDFType
= aLinkString
;
1823 else if ( SwFieldIds::TableOfAuthorities
== pField
->Which() )
1825 nPDFType
= vcl::PDFWriter::BibEntry
;
1826 aPDFType
= aBibEntryString
;
1832 // for FootnoteNum, is called twice: outer generates Lbl, inner Link
1833 case PortionType::FootnoteNum
:
1834 assert(!isContinueSpan
); // is at start
1835 if (!mpPorInfo
->m_isNumberingLabel
)
1836 { // tdf#152218 link both directions
1837 nPDFType
= vcl::PDFWriter::Link
;
1838 aPDFType
= aLinkString
;
1842 case PortionType::Number
:
1843 case PortionType::Bullet
:
1844 case PortionType::GrfNum
:
1845 assert(!isContinueSpan
); // is at start
1846 if (mpPorInfo
->m_isNumberingLabel
)
1847 { // only works for multiple lines via wrapper from PaintSwFrame
1848 nPDFType
= vcl::PDFWriter::LILabel
;
1849 aPDFType
= aListLabelString
;
1853 case PortionType::Tab
:
1854 case PortionType::TabRight
:
1855 case PortionType::TabCenter
:
1856 case PortionType::TabDecimal
:
1857 nPDFType
= vcl::PDFWriter::NonStructElement
;
1862 if ( USHRT_MAX
!= nPDFType
)
1864 BeginTag( static_cast<vcl::PDFWriter::StructElement
>(nPDFType
), aPDFType
);
1868 bool SwTaggedPDFHelper::IsExportTaggedPDF( const OutputDevice
& rOut
)
1870 vcl::PDFExtOutDevData
* pPDFExtOutDevData
= dynamic_cast< vcl::PDFExtOutDevData
*>( rOut
.GetExtOutDevData() );
1871 return pPDFExtOutDevData
&& pPDFExtOutDevData
->GetIsExportTaggedPDF();
1874 SwEnhancedPDFExportHelper::SwEnhancedPDFExportHelper( SwEditShell
& rSh
,
1876 const OUString
& rPageRange
,
1877 bool bSkipEmptyPages
,
1878 bool bEditEngineOnly
,
1879 const SwPrintData
& rPrintData
)
1882 mbSkipEmptyPages( bSkipEmptyPages
),
1883 mbEditEngineOnly( bEditEngineOnly
),
1884 mrPrintData( rPrintData
)
1886 if ( !rPageRange
.isEmpty() )
1887 mpRangeEnum
.reset( new StringRangeEnumerator( rPageRange
, 0, mrSh
.GetPageCount()-1 ) );
1889 if ( mbSkipEmptyPages
)
1891 maPageNumberMap
.resize( mrSh
.GetPageCount() );
1892 const SwPageFrame
* pCurrPage
=
1893 static_cast<const SwPageFrame
*>( mrSh
.GetLayout()->Lower() );
1894 sal_Int32 nPageNumber
= 0;
1895 for ( size_t i
= 0, n
= maPageNumberMap
.size(); i
< n
&& pCurrPage
; ++i
)
1897 if ( pCurrPage
->IsEmptyPage() )
1898 maPageNumberMap
[i
] = -1;
1900 maPageNumberMap
[i
] = nPageNumber
++;
1902 pCurrPage
= static_cast<const SwPageFrame
*>( pCurrPage
->GetNext() );
1906 #if OSL_DEBUG_LEVEL > 1
1907 aStructStack
.clear();
1910 const sal_Int16 nScript
= SvtLanguageOptions::GetI18NScriptTypeOfLanguage( GetAppLanguage() );
1911 sal_uInt16 nLangRes
= RES_CHRATR_LANGUAGE
;
1913 if ( i18n::ScriptType::ASIAN
== nScript
)
1914 nLangRes
= RES_CHRATR_CJK_LANGUAGE
;
1915 else if ( i18n::ScriptType::COMPLEX
== nScript
)
1916 nLangRes
= RES_CHRATR_CTL_LANGUAGE
;
1918 auto const eLanguageDefault
= static_cast<const SvxLanguageItem
*>(&mrSh
.GetDoc()->GetDefault( nLangRes
))->GetLanguage();
1920 EnhancedPDFExport(eLanguageDefault
);
1923 SwEnhancedPDFExportHelper::~SwEnhancedPDFExportHelper()
1927 tools::Rectangle
SwEnhancedPDFExportHelper::SwRectToPDFRect(const SwPageFrame
* pCurrPage
,
1928 const tools::Rectangle
& rRectangle
) const
1930 if (!::sw::IsShrinkPageForPostIts(mrSh
, mrPrintData
)) // tdf#148729
1934 //the page has been scaled by 75% and vertically centered, so adjust these
1935 //rectangles equivalently
1936 tools::Rectangle
aRect(rRectangle
);
1937 Size
aRectSize(aRect
.GetSize());
1938 double fScale
= 0.75;
1939 aRectSize
.setWidth( aRectSize
.Width() * fScale
);
1940 aRectSize
.setHeight( aRectSize
.Height() * fScale
);
1941 tools::Long nOrigHeight
= pCurrPage
->getFrameArea().Height();
1942 tools::Long nNewHeight
= nOrigHeight
*fScale
;
1943 tools::Long nShiftY
= (nOrigHeight
-nNewHeight
)/2;
1944 aRect
.SetLeft( aRect
.Left() * fScale
);
1945 aRect
.SetTop( aRect
.Top() * fScale
);
1946 aRect
.Move(0, nShiftY
);
1947 aRect
.SetSize(aRectSize
);
1951 void SwEnhancedPDFExportHelper::EnhancedPDFExport(LanguageType
const eLanguageDefault
)
1953 vcl::PDFExtOutDevData
* pPDFExtOutDevData
=
1954 dynamic_cast< vcl::PDFExtOutDevData
*>( mrOut
.GetExtOutDevData() );
1956 if ( !pPDFExtOutDevData
)
1959 // set the document locale
1961 lang::Locale
const aDocLocale( LanguageTag(eLanguageDefault
).getLocale() );
1962 pPDFExtOutDevData
->SetDocumentLocale( aDocLocale
);
1964 // Prepare the output device:
1966 mrOut
.Push( vcl::PushFlags::MAPMODE
);
1967 MapMode
aMapMode( mrOut
.GetMapMode() );
1968 aMapMode
.SetMapUnit( MapUnit::MapTwip
);
1969 mrOut
.SetMapMode( aMapMode
);
1971 // Create new cursor and lock the view:
1973 SwDoc
* pDoc
= mrSh
.GetDoc();
1974 mrSh
.SwCursorShell::Push();
1975 mrSh
.SwCursorShell::ClearMark();
1976 const bool bOldLockView
= mrSh
.IsViewLocked();
1977 mrSh
.LockView( true );
1979 if ( !mbEditEngineOnly
)
1981 assert(pPDFExtOutDevData
->GetSwPDFState() == nullptr);
1982 pPDFExtOutDevData
->SetSwPDFState(new SwEnhancedPDFState(eLanguageDefault
));
1986 if ( pPDFExtOutDevData
->GetIsExportNotes() )
1988 std::vector
<SwFormatField
*> vpFields
;
1989 mrSh
.GetFieldType(SwFieldIds::Postit
, OUString())->GatherFields(vpFields
);
1990 for(auto pFormatField
: vpFields
)
1992 const SwTextNode
* pTNd
= pFormatField
->GetTextField()->GetpTextNode();
1993 OSL_ENSURE(nullptr != pTNd
, "Enhanced pdf export - text node is missing");
1994 if(!lcl_TryMoveToNonHiddenField(mrSh
, *pTNd
, *pFormatField
))
1997 const SwRect
& rNoteRect
= mrSh
.GetCharRect();
1998 const SwPageFrame
* pCurrPage
= static_cast<const SwPageFrame
*>(mrSh
.GetLayout()->Lower());
2001 std::vector
<sal_Int32
> aNotePageNums
= CalcOutputPageNums(rNoteRect
);
2002 for (sal_Int32 aNotePageNum
: aNotePageNums
)
2005 // Use the NumberFormatter to get the date string:
2006 const SwPostItField
* pField
= static_cast<SwPostItField
*>(pFormatField
->GetField());
2007 SvNumberFormatter
* pNumFormatter
= pDoc
->GetNumberFormatter();
2008 const Date
aDateDiff(pField
->GetDate() - pNumFormatter
->GetNullDate());
2009 const sal_uLong nFormat
= pNumFormatter
->GetStandardFormat(SvNumFormatType::DATE
, pField
->GetLanguage());
2011 const Color
* pColor
;
2012 pNumFormatter
->GetOutputString(aDateDiff
.GetDate(), nFormat
, sDate
, &pColor
);
2015 // The title should consist of the author and the date:
2016 aNote
.Title
= pField
->GetPar1() + ", " + sDate
+ ", " + (pField
->GetResolved() ? SwResId(STR_RESOLVED
) : "");
2017 // Guess what the contents contains...
2018 aNote
.Contents
= pField
->GetText();
2021 tools::Rectangle
aRect(SwRectToPDFRect(pCurrPage
, rNoteRect
.SVRect()));
2022 pPDFExtOutDevData
->CreateNote(aRect
, aNote
, aNotePageNum
);
2024 mrSh
.SwCursorShell::ClearMark();
2030 SwGetINetAttrs aArr
;
2031 mrSh
.GetINetAttrs( aArr
);
2032 for( auto &rAttr
: aArr
)
2034 SwGetINetAttr
* p
= &rAttr
;
2035 OSL_ENSURE( nullptr != p
, "Enhanced pdf export - SwGetINetAttr is missing" );
2037 const SwTextNode
* pTNd
= p
->rINetAttr
.GetpTextNode();
2038 OSL_ENSURE( nullptr != pTNd
, "Enhanced pdf export - text node is missing" );
2040 // 1. Check if the whole paragraph is hidden
2041 // 2. Move to the hyperlink
2042 // 3. Check for hidden text attribute
2043 if ( !pTNd
->IsHidden() &&
2044 mrSh
.GotoINetAttr( p
->rINetAttr
) &&
2045 !mrSh
.IsInHiddenRange(/*bSelect=*/false) )
2047 // Select the hyperlink:
2048 mrSh
.SwCursorShell::Right( 1, SwCursorSkipMode::Chars
);
2049 if ( mrSh
.SwCursorShell::SelectTextAttr( RES_TXTATR_INETFMT
, true ) )
2051 // First, we create the destination, because there may be more
2052 // than one link to this destination:
2053 OUString
aURL( INetURLObject::decode(
2054 p
->rINetAttr
.GetINetFormat().GetValue(),
2055 INetURLObject::DecodeMechanism::Unambiguous
) );
2057 // We have to distinguish between internal and real URLs
2058 const bool bInternal
= '#' == aURL
[0];
2060 // GetCursor_() is a SwShellCursor, which is derived from
2061 // SwSelPaintRects, therefore the rectangles of the current
2062 // selection can be easily obtained:
2063 // Note: We make a copy of the rectangles, because they may
2064 // be deleted again in JumpToSwMark.
2066 aTmp
.insert( aTmp
.begin(), mrSh
.SwCursorShell::GetCursor_()->begin(), mrSh
.SwCursorShell::GetCursor_()->end() );
2067 OSL_ENSURE( !aTmp
.empty(), "Enhanced pdf export - rectangles are missing" );
2068 OUString
const altText(mrSh
.GetSelText());
2070 const SwPageFrame
* pSelectionPage
=
2071 static_cast<const SwPageFrame
*>( mrSh
.GetLayout()->Lower() );
2073 // Create the destination for internal links:
2074 sal_Int32 nDestId
= -1;
2077 aURL
= aURL
.copy( 1 );
2078 mrSh
.SwCursorShell::ClearMark();
2079 if (! JumpToSwMark( &mrSh
, aURL
))
2081 continue; // target deleted
2084 // Destination Rectangle
2085 const SwRect
& rDestRect
= mrSh
.GetCharRect();
2087 const SwPageFrame
* pCurrPage
=
2088 static_cast<const SwPageFrame
*>( mrSh
.GetLayout()->Lower() );
2090 // Destination PageNum
2091 const sal_Int32 nDestPageNum
= CalcOutputPageNum( rDestRect
);
2093 // Destination Export
2094 if ( -1 != nDestPageNum
)
2096 tools::Rectangle
aRect(SwRectToPDFRect(pCurrPage
, rDestRect
.SVRect()));
2097 nDestId
= pPDFExtOutDevData
->CreateDest(aRect
, nDestPageNum
);
2101 if ( !bInternal
|| -1 != nDestId
)
2103 // #i44368# Links in Header/Footer
2104 const bool bHeaderFooter
= pDoc
->IsInHeaderFooter( *pTNd
);
2106 // Create links for all selected rectangles:
2107 const size_t nNumOfRects
= aTmp
.size();
2108 for ( size_t i
= 0; i
< nNumOfRects
; ++i
)
2111 const SwRect
& rLinkRect( aTmp
[ i
] );
2114 std::vector
<sal_Int32
> aLinkPageNums
= CalcOutputPageNums( rLinkRect
);
2116 for (sal_Int32 aLinkPageNum
: aLinkPageNums
)
2119 tools::Rectangle
aRect(SwRectToPDFRect(pSelectionPage
, rLinkRect
.SVRect()));
2120 const sal_Int32 nLinkId
=
2121 pPDFExtOutDevData
->CreateLink(aRect
, altText
, aLinkPageNum
);
2123 // Store link info for tagged pdf output:
2124 const IdMapEntry
aLinkEntry( rLinkRect
, nLinkId
);
2125 pPDFExtOutDevData
->GetSwPDFState()->m_LinkIdMap
.push_back(aLinkEntry
);
2127 // Connect Link and Destination:
2129 pPDFExtOutDevData
->SetLinkDest( nLinkId
, nDestId
);
2131 pPDFExtOutDevData
->SetLinkURL( nLinkId
, aURL
);
2133 // #i44368# Links in Header/Footer
2134 if ( bHeaderFooter
)
2135 MakeHeaderFooterLinks(*pPDFExtOutDevData
, *pTNd
, rLinkRect
, nDestId
, aURL
, bInternal
, altText
);
2141 mrSh
.SwCursorShell::ClearMark();
2144 // HYPERLINKS (Graphics, Frames, OLEs )
2146 for(sw::SpzFrameFormat
* pFrameFormat
: *pDoc
->GetSpzFrameFormats())
2148 const SwFormatURL
* pItem
;
2149 if ( RES_DRAWFRMFMT
!= pFrameFormat
->Which() &&
2150 GetFrameOfModify(mrSh
.GetLayout(), *pFrameFormat
, SwFrameType::Fly
) &&
2151 (pItem
= pFrameFormat
->GetAttrSet().GetItemIfSet( RES_URL
)) )
2153 const SwPageFrame
* pCurrPage
=
2154 static_cast<const SwPageFrame
*>( mrSh
.GetLayout()->Lower() );
2156 OUString
aURL( pItem
->GetURL() );
2159 const bool bInternal
= '#' == aURL
[0];
2161 // Create the destination for internal links:
2162 sal_Int32 nDestId
= -1;
2165 aURL
= aURL
.copy( 1 );
2166 mrSh
.SwCursorShell::ClearMark();
2167 if (! JumpToSwMark( &mrSh
, aURL
))
2169 continue; // target deleted
2172 // Destination Rectangle
2173 const SwRect
& rDestRect
= mrSh
.GetCharRect();
2175 pCurrPage
= static_cast<const SwPageFrame
*>( mrSh
.GetLayout()->Lower() );
2177 // Destination PageNum
2178 const sal_Int32 nDestPageNum
= CalcOutputPageNum( rDestRect
);
2180 // Destination Export
2181 if ( -1 != nDestPageNum
)
2183 tools::Rectangle
aRect(SwRectToPDFRect(pCurrPage
, rDestRect
.SVRect()));
2184 nDestId
= pPDFExtOutDevData
->CreateDest(aRect
, nDestPageNum
);
2188 if ( !bInternal
|| -1 != nDestId
)
2191 const SwRect aLinkRect
= pFrameFormat
->FindLayoutRect( false, &aNullPt
);
2192 OUString
const formatName(pFrameFormat
->GetName());
2194 std::vector
<sal_Int32
> aLinkPageNums
= CalcOutputPageNums( aLinkRect
);
2197 for (sal_Int32 aLinkPageNum
: aLinkPageNums
)
2199 tools::Rectangle
aRect(SwRectToPDFRect(pCurrPage
, aLinkRect
.SVRect()));
2200 const sal_Int32 nLinkId
=
2201 pPDFExtOutDevData
->CreateLink(aRect
, formatName
, aLinkPageNum
);
2203 // Store link info for tagged pdf output:
2204 const IdMapEntry
aLinkEntry(aLinkRect
, nLinkId
);
2205 pPDFExtOutDevData
->GetSwPDFState()->m_LinkIdMap
.push_back(aLinkEntry
);
2207 // Connect Link and Destination:
2209 pPDFExtOutDevData
->SetLinkDest( nLinkId
, nDestId
);
2211 pPDFExtOutDevData
->SetLinkURL( nLinkId
, aURL
);
2213 // #i44368# Links in Header/Footer
2214 const SwFormatAnchor
&rAnch
= pFrameFormat
->GetAnchor();
2215 if (RndStdIds::FLY_AT_PAGE
!= rAnch
.GetAnchorId())
2217 const SwNode
* pAnchorNode
= rAnch
.GetAnchorNode();
2218 if ( pAnchorNode
&& pDoc
->IsInHeaderFooter( *pAnchorNode
) )
2220 const SwTextNode
* pTNd
= pAnchorNode
->GetTextNode();
2222 MakeHeaderFooterLinks(*pPDFExtOutDevData
, *pTNd
, aLinkRect
, nDestId
, aURL
, bInternal
, formatName
);
2228 else if (pFrameFormat
->Which() == RES_DRAWFRMFMT
)
2230 // Turn media shapes into Screen annotations.
2231 if (SdrObject
* pObject
= pFrameFormat
->FindRealSdrObject())
2233 SwRect
aSnapRect(pObject
->GetSnapRect());
2234 std::vector
<sal_Int32
> aScreenPageNums
= CalcOutputPageNums(aSnapRect
);
2235 if (aScreenPageNums
.empty())
2238 uno::Reference
<drawing::XShape
> xShape(pObject
->getUnoShape(), uno::UNO_QUERY
);
2239 if (xShape
->getShapeType() == "com.sun.star.drawing.MediaShape")
2241 uno::Reference
<beans::XPropertySet
> xShapePropSet(xShape
, uno::UNO_QUERY
);
2243 xShapePropSet
->getPropertyValue("Title") >>= title
;
2244 OUString description
;
2245 xShapePropSet
->getPropertyValue("Description") >>= description
;
2246 OUString
const altText(title
.isEmpty()
2248 : description
.isEmpty()
2250 : OUString::Concat(title
) + OUString::Concat("\n") + OUString::Concat(description
));
2253 xShapePropSet
->getPropertyValue("MediaURL") >>= aMediaURL
;
2254 if (!aMediaURL
.isEmpty())
2256 OUString
const mimeType(xShapePropSet
->getPropertyValue("MediaMimeType").get
<OUString
>());
2257 const SwPageFrame
* pCurrPage
= mrSh
.GetLayout()->GetPageAtPos(aSnapRect
.Center());
2258 tools::Rectangle
aPDFRect(SwRectToPDFRect(pCurrPage
, aSnapRect
.SVRect()));
2259 for (sal_Int32 nScreenPageNum
: aScreenPageNums
)
2261 sal_Int32 nScreenId
= pPDFExtOutDevData
->CreateScreen(aPDFRect
, altText
, mimeType
, nScreenPageNum
, pObject
);
2262 if (aMediaURL
.startsWith("vnd.sun.star.Package:"))
2265 OUString aTempFileURL
;
2266 xShapePropSet
->getPropertyValue("PrivateTempFileURL") >>= aTempFileURL
;
2267 pPDFExtOutDevData
->SetScreenStream(nScreenId
, aTempFileURL
);
2271 pPDFExtOutDevData
->SetScreenURL(nScreenId
, aMediaURL
);
2277 mrSh
.SwCursorShell::ClearMark();
2282 std::vector
<SwFormatField
*> vpFields
;
2283 mrSh
.GetFieldType( SwFieldIds::GetRef
, OUString() )->GatherFields(vpFields
);
2284 for(auto pFormatField
: vpFields
)
2286 if( pFormatField
->GetTextField() && pFormatField
->IsFieldInDoc() )
2288 const SwTextNode
* pTNd
= pFormatField
->GetTextField()->GetpTextNode();
2289 OSL_ENSURE( nullptr != pTNd
, "Enhanced pdf export - text node is missing" );
2290 if(!lcl_TryMoveToNonHiddenField(mrSh
, *pTNd
, *pFormatField
))
2292 // Select the field:
2293 mrSh
.SwCursorShell::SetMark();
2294 mrSh
.SwCursorShell::Right( 1, SwCursorSkipMode::Chars
);
2298 aTmp
.insert( aTmp
.begin(), mrSh
.SwCursorShell::GetCursor_()->begin(), mrSh
.SwCursorShell::GetCursor_()->end() );
2299 OSL_ENSURE( !aTmp
.empty(), "Enhanced pdf export - rectangles are missing" );
2301 mrSh
.SwCursorShell::ClearMark();
2303 // Destination Rectangle
2304 const SwGetRefField
* pField
= static_cast<SwGetRefField
*>(pFormatField
->GetField());
2305 const OUString
& rRefName
= pField
->GetSetRefName();
2306 mrSh
.GotoRefMark( rRefName
, pField
->GetSubType(), pField
->GetSeqNo() );
2307 const SwRect
& rDestRect
= mrSh
.GetCharRect();
2309 const SwPageFrame
* pCurrPage
= static_cast<const SwPageFrame
*>( mrSh
.GetLayout()->Lower() );
2311 // Destination PageNum
2312 const sal_Int32 nDestPageNum
= CalcOutputPageNum( rDestRect
);
2314 if ( -1 != nDestPageNum
)
2316 // Destination Export
2317 tools::Rectangle
aRect(SwRectToPDFRect(pCurrPage
, rDestRect
.SVRect()));
2318 const sal_Int32 nDestId
= pPDFExtOutDevData
->CreateDest(aRect
, nDestPageNum
);
2320 // #i44368# Links in Header/Footer
2321 const bool bHeaderFooter
= pDoc
->IsInHeaderFooter( *pTNd
);
2322 OUString
const content(pField
->ExpandField(true, mrSh
.GetLayout()));
2324 // Create links for all selected rectangles:
2325 const size_t nNumOfRects
= aTmp
.size();
2326 for ( size_t i
= 0; i
< nNumOfRects
; ++i
)
2329 const SwRect
& rLinkRect( aTmp
[ i
] );
2332 std::vector
<sal_Int32
> aLinkPageNums
= CalcOutputPageNums( rLinkRect
);
2334 for (sal_Int32 aLinkPageNum
: aLinkPageNums
)
2337 aRect
= SwRectToPDFRect(pCurrPage
, rLinkRect
.SVRect());
2338 const sal_Int32 nLinkId
=
2339 pPDFExtOutDevData
->CreateLink(aRect
, content
, aLinkPageNum
);
2341 // Store link info for tagged pdf output:
2342 const IdMapEntry
aLinkEntry( rLinkRect
, nLinkId
);
2343 pPDFExtOutDevData
->GetSwPDFState()->m_LinkIdMap
.push_back(aLinkEntry
);
2345 // Connect Link and Destination:
2346 pPDFExtOutDevData
->SetLinkDest( nLinkId
, nDestId
);
2348 // #i44368# Links in Header/Footer
2349 if ( bHeaderFooter
)
2351 MakeHeaderFooterLinks(*pPDFExtOutDevData
, *pTNd
, rLinkRect
, nDestId
, "", true, content
);
2357 mrSh
.SwCursorShell::ClearMark();
2360 ExportAuthorityEntryLinks();
2364 const size_t nFootnoteCount
= pDoc
->GetFootnoteIdxs().size();
2365 for ( size_t nIdx
= 0; nIdx
< nFootnoteCount
; ++nIdx
)
2367 // Set cursor to text node that contains the footnote:
2368 const SwTextFootnote
* pTextFootnote
= pDoc
->GetFootnoteIdxs()[ nIdx
];
2369 SwTextNode
& rTNd
= const_cast<SwTextNode
&>(pTextFootnote
->GetTextNode());
2371 mrSh
.GetCursor_()->GetPoint()->Assign(rTNd
, pTextFootnote
->GetStart());
2373 // 1. Check if the whole paragraph is hidden
2374 // 2. Check for hidden text attribute
2375 if (rTNd
.GetTextNode()->IsHidden() || mrSh
.IsInHiddenRange(/*bSelect=*/false)
2376 || (mrSh
.GetLayout()->IsHideRedlines()
2377 && sw::IsFootnoteDeleted(pDoc
->getIDocumentRedlineAccess(), *pTextFootnote
)))
2382 SwCursorSaveState
aSaveState( *mrSh
.GetCursor_() );
2384 // Select the footnote:
2385 mrSh
.SwCursorShell::SetMark();
2386 mrSh
.SwCursorShell::Right( 1, SwCursorSkipMode::Chars
);
2390 aTmp
.insert( aTmp
.begin(), mrSh
.SwCursorShell::GetCursor_()->begin(), mrSh
.SwCursorShell::GetCursor_()->end() );
2391 OSL_ENSURE( !aTmp
.empty(), "Enhanced pdf export - rectangles are missing" );
2393 mrSh
.GetCursor_()->RestoreSavePos();
2394 mrSh
.SwCursorShell::ClearMark();
2399 const SwRect
aLinkRect( aTmp
[ 0 ] );
2401 // Goto footnote text:
2402 if ( mrSh
.GotoFootnoteText() )
2404 // Destination Rectangle
2405 const SwRect
& rDestRect
= mrSh
.GetCharRect();
2406 const sal_Int32 nDestPageNum
= CalcOutputPageNum( rDestRect
);
2407 if ( -1 != nDestPageNum
)
2409 const SwPageFrame
* pCurrPage
= static_cast<const SwPageFrame
*>( mrSh
.GetLayout()->Lower() );
2410 // Destination PageNum
2411 tools::Rectangle aRect
= SwRectToPDFRect(pCurrPage
, rDestRect
.SVRect());
2412 // Back link rectangle calculation
2413 const SwPageFrame
* fnBodyPage
= pCurrPage
->getRootFrame()->GetPageByPageNum(nDestPageNum
+1);
2414 SwRect fnSymbolRect
;
2415 if (fnBodyPage
->IsVertical()){
2416 tools::Long fnSymbolTop
= fnBodyPage
->GetTopMargin() + fnBodyPage
->getFrameArea().Top();
2417 tools::Long symbolHeight
= rDestRect
.Top() - fnSymbolTop
;
2418 fnSymbolRect
= SwRect(rDestRect
.Pos().X(),fnSymbolTop
,rDestRect
.Width(),symbolHeight
);
2420 if (fnBodyPage
->IsRightToLeft()){
2421 tools::Long fnSymbolRight
= fnBodyPage
->getFrameArea().Right() - fnBodyPage
->GetRightMargin();
2422 tools::Long symbolWidth
= fnSymbolRight
- rDestRect
.Right();
2423 fnSymbolRect
= SwRect(rDestRect
.Pos().X(),rDestRect
.Pos().Y(),symbolWidth
,rDestRect
.Height());
2425 tools::Long fnSymbolLeft
= fnBodyPage
->GetLeftMargin() + fnBodyPage
->getFrameArea().Left();
2426 tools::Long symbolWidth
= rDestRect
.Left() - fnSymbolLeft
;
2427 fnSymbolRect
= SwRect(fnSymbolLeft
,rDestRect
.Pos().Y(),symbolWidth
,rDestRect
.Height());
2430 tools::Rectangle aFootnoteSymbolRect
= SwRectToPDFRect(pCurrPage
, fnSymbolRect
.SVRect());
2432 OUString
const numStrSymbol(pTextFootnote
->GetFootnote().GetViewNumStr(*pDoc
, mrSh
.GetLayout(), true));
2433 OUString
const numStrRef(pTextFootnote
->GetFootnote().GetViewNumStr(*pDoc
, mrSh
.GetLayout(), false));
2436 const sal_Int32 nBackLinkId
= pPDFExtOutDevData
->CreateLink(aFootnoteSymbolRect
, numStrSymbol
, nDestPageNum
);
2437 // Destination Export
2438 const sal_Int32 nDestId
= pPDFExtOutDevData
->CreateDest(aRect
, nDestPageNum
);
2439 mrSh
.GotoFootnoteAnchor();
2441 sal_Int32 aLinkPageNum
= CalcOutputPageNum( aLinkRect
);
2442 pCurrPage
= static_cast<const SwPageFrame
*>( mrSh
.GetLayout()->Lower() );
2444 aRect
= SwRectToPDFRect(pCurrPage
, aLinkRect
.SVRect());
2445 const sal_Int32 nLinkId
= pPDFExtOutDevData
->CreateLink(aRect
, numStrRef
, aLinkPageNum
);
2446 // Back link destination Export
2447 const sal_Int32 nBackDestId
= pPDFExtOutDevData
->CreateDest(aRect
, aLinkPageNum
);
2448 // Store link info for tagged pdf output:
2449 const IdMapEntry
aLinkEntry( aLinkRect
, nLinkId
);
2450 pPDFExtOutDevData
->GetSwPDFState()->m_LinkIdMap
.push_back(aLinkEntry
);
2452 // Store backlink info for tagged pdf output:
2453 const IdMapEntry
aBackLinkEntry( aFootnoteSymbolRect
, nBackLinkId
);
2454 pPDFExtOutDevData
->GetSwPDFState()->m_LinkIdMap
.push_back(aBackLinkEntry
);
2455 // Connect Links and Destinations:
2456 pPDFExtOutDevData
->SetLinkDest( nLinkId
, nDestId
);
2457 pPDFExtOutDevData
->SetLinkDest( nBackLinkId
, nBackDestId
);
2464 if( pPDFExtOutDevData
->GetIsExportBookmarks() )
2466 typedef std::pair
< sal_Int8
, sal_Int32
> StackEntry
;
2467 std::stack
< StackEntry
> aOutlineStack
;
2468 aOutlineStack
.push( StackEntry( -1, -1 ) ); // push default value
2470 const SwOutlineNodes::size_type nOutlineCount
=
2471 mrSh
.getIDocumentOutlineNodesAccess()->getOutlineNodesCount();
2472 for ( SwOutlineNodes::size_type i
= 0; i
< nOutlineCount
; ++i
)
2474 // Check if outline is hidden
2475 const SwTextNode
* pTNd
= mrSh
.GetNodes().GetOutLineNds()[ i
]->GetTextNode();
2476 OSL_ENSURE( nullptr != pTNd
, "Enhanced pdf export - text node is missing" );
2478 if ( pTNd
->IsHidden() ||
2479 !sw::IsParaPropsNode(*mrSh
.GetLayout(), *pTNd
) ||
2480 // #i40292# Skip empty outlines:
2481 pTNd
->GetText().isEmpty())
2484 // Get parent id from stack:
2485 const sal_Int8 nLevel
= static_cast<sal_Int8
>(mrSh
.getIDocumentOutlineNodesAccess()->getOutlineLevel( i
));
2486 sal_Int8 nLevelOnTopOfStack
= aOutlineStack
.top().first
;
2487 while ( nLevelOnTopOfStack
>= nLevel
&&
2488 nLevelOnTopOfStack
!= -1 )
2490 aOutlineStack
.pop();
2491 nLevelOnTopOfStack
= aOutlineStack
.top().first
;
2493 const sal_Int32 nParent
= aOutlineStack
.top().second
;
2495 // Destination rectangle
2496 mrSh
.GotoOutline(i
);
2497 const SwRect
& rDestRect
= mrSh
.GetCharRect();
2499 const SwPageFrame
* pCurrPage
=
2500 static_cast<const SwPageFrame
*>( mrSh
.GetLayout()->Lower() );
2502 // Destination PageNum
2503 const sal_Int32 nDestPageNum
= CalcOutputPageNum( rDestRect
);
2505 if ( -1 != nDestPageNum
)
2507 // Destination Export
2508 tools::Rectangle
aRect(SwRectToPDFRect(pCurrPage
, rDestRect
.SVRect()));
2509 const sal_Int32 nDestId
=
2510 pPDFExtOutDevData
->CreateDest(aRect
, nDestPageNum
);
2512 // Outline entry text
2513 const OUString
& rEntry
= mrSh
.getIDocumentOutlineNodesAccess()->getOutlineText(
2514 i
, mrSh
.GetLayout(), true, false, false );
2516 // Create a new outline item:
2517 const sal_Int32 nOutlineId
=
2518 pPDFExtOutDevData
->CreateOutlineItem( nParent
, rEntry
, nDestId
);
2520 // Push current level and nOutlineId on stack:
2521 aOutlineStack
.push( StackEntry( nLevel
, nOutlineId
) );
2526 if( pPDFExtOutDevData
->GetIsExportNamedDestinations() )
2528 // #i56629# the iteration to convert the OOo bookmark (#bookmark)
2529 // into PDF named destination, see section 8.2.1 in PDF 1.4 spec
2531 // 1. a name for the destination, formed from the standard OOo bookmark name
2532 // 2. the destination, obtained from where the bookmark destination lies
2533 IDocumentMarkAccess
* const pMarkAccess
= mrSh
.GetDoc()->getIDocumentMarkAccess();
2534 for(IDocumentMarkAccess::const_iterator_t ppMark
= pMarkAccess
->getBookmarksBegin();
2535 ppMark
!= pMarkAccess
->getBookmarksEnd();
2539 const ::sw::mark::IMark
* pBkmk
= *ppMark
;
2540 mrSh
.SwCursorShell::ClearMark();
2541 const OUString
& sBkName
= pBkmk
->GetName();
2544 if (! JumpToSwMark( &mrSh
, sBkName
))
2549 // Destination Rectangle
2550 const SwRect
& rDestRect
= mrSh
.GetCharRect();
2552 const SwPageFrame
* pCurrPage
=
2553 static_cast<const SwPageFrame
*>( mrSh
.GetLayout()->Lower() );
2555 // Destination PageNum
2556 const sal_Int32 nDestPageNum
= CalcOutputPageNum( rDestRect
);
2558 // Destination Export
2559 if ( -1 != nDestPageNum
)
2561 tools::Rectangle
aRect(SwRectToPDFRect(pCurrPage
, rDestRect
.SVRect()));
2562 pPDFExtOutDevData
->CreateNamedDest(sBkName
, aRect
, nDestPageNum
);
2565 mrSh
.SwCursorShell::ClearMark();
2572 // LINKS FROM EDITENGINE
2574 std::vector
< vcl::PDFExtOutDevBookmarkEntry
>& rBookmarks
= pPDFExtOutDevData
->GetBookmarks();
2575 for ( const auto& rBookmark
: rBookmarks
)
2577 OUString
aBookmarkName( rBookmark
.aBookmark
);
2578 const bool bInternal
= '#' == aBookmarkName
[0];
2581 aBookmarkName
= aBookmarkName
.copy( 1 );
2582 JumpToSwMark( &mrSh
, aBookmarkName
);
2584 // Destination Rectangle
2585 const SwRect
& rDestRect
= mrSh
.GetCharRect();
2587 const SwPageFrame
* pCurrPage
=
2588 static_cast<const SwPageFrame
*>( mrSh
.GetLayout()->Lower() );
2590 // Destination PageNum
2591 const sal_Int32 nDestPageNum
= CalcOutputPageNum( rDestRect
);
2593 if ( -1 != nDestPageNum
)
2595 tools::Rectangle
aRect(SwRectToPDFRect(pCurrPage
, rDestRect
.SVRect()));
2596 if ( rBookmark
.nLinkId
!= -1 )
2598 // Destination Export
2599 const sal_Int32 nDestId
= pPDFExtOutDevData
->CreateDest(aRect
, nDestPageNum
);
2601 // Connect Link and Destination:
2602 pPDFExtOutDevData
->SetLinkDest( rBookmark
.nLinkId
, nDestId
);
2606 pPDFExtOutDevData
->DescribeRegisteredDest(rBookmark
.nDestId
, aRect
, nDestPageNum
);
2611 pPDFExtOutDevData
->SetLinkURL( rBookmark
.nLinkId
, aBookmarkName
);
2614 assert(pPDFExtOutDevData
->GetSwPDFState());
2615 delete pPDFExtOutDevData
->GetSwPDFState();
2616 pPDFExtOutDevData
->SetSwPDFState(nullptr);
2619 // Restore view, cursor, and outdev:
2620 mrSh
.LockView( bOldLockView
);
2621 mrSh
.SwCursorShell::Pop(SwCursorShell::PopMode::DeleteCurrent
);
2625 void SwEnhancedPDFExportHelper::ExportAuthorityEntryLinks()
2627 auto pPDFExtOutDevData
= dynamic_cast<vcl::PDFExtOutDevData
*>(mrOut
.GetExtOutDevData());
2628 if (!pPDFExtOutDevData
)
2633 // Create PDF destinations for bibliography table entries
2634 std::vector
<std::tuple
<const SwTOXBase
*, const OUString
*, sal_Int32
>> vDestinations
;
2635 // string is the row node text, sal_Int32 is number of the destination
2636 // Note: This way of iterating doesn't seem to take into account TOXes
2637 // that are in a frame, probably in some other cases too
2640 while (mrSh
.GotoNextTOXBase())
2642 const SwTOXBase
* pIteratedTOX
= nullptr;
2643 while ((pIteratedTOX
= mrSh
.GetCurTOX()) != nullptr
2644 && pIteratedTOX
->GetType() == TOX_AUTHORITIES
)
2646 if (const SwNode
& rCurrentNode
= mrSh
.GetCursor()->GetPoint()->GetNode();
2647 rCurrentNode
.GetNodeType() == SwNodeType::Text
)
2649 if (mrSh
.GetCursor()->GetPoint()->GetNode().FindSectionNode()->GetSection().GetType()
2650 == SectionType::ToxContent
) // this checks it's not a heading
2652 // Destination Rectangle
2653 const SwRect
& rDestRect
= mrSh
.GetCharRect();
2655 const SwPageFrame
* pCurrPage
=
2656 static_cast<const SwPageFrame
*>( mrSh
.GetLayout()->Lower() );
2658 // Destination PageNum
2659 const sal_Int32 nDestPageNum
= CalcOutputPageNum( rDestRect
);
2661 // Destination Export
2662 if ( -1 != nDestPageNum
)
2664 tools::Rectangle
aRect(SwRectToPDFRect(pCurrPage
, rDestRect
.SVRect()));
2665 const sal_Int32 nDestId
= pPDFExtOutDevData
->CreateDest(aRect
, nDestPageNum
);
2666 const OUString
* vNodeText
= &static_cast<const SwTextNode
*>(&rCurrentNode
)->GetText();
2667 vDestinations
.emplace_back(pIteratedTOX
, vNodeText
, nDestId
);
2671 mrSh
.MovePara(GoNextPara
, fnParaStart
);
2676 // Generate links to matching entries in the bibliography tables
2677 std::vector
<SwFormatField
*> aFields
;
2678 SwFieldType
* pType
= mrSh
.GetFieldType(SwFieldIds::TableOfAuthorities
, OUString());
2684 pType
->GatherFields(aFields
);
2685 const auto pPageFrame
= static_cast<const SwPageFrame
*>(mrSh
.GetLayout()->Lower());
2686 for (const auto pFormatField
: aFields
)
2688 if (!pFormatField
->GetTextField() || !pFormatField
->IsFieldInDoc())
2693 const auto& rAuthorityField
2694 = *static_cast<const SwAuthorityField
*>(pFormatField
->GetField());
2696 if (auto targetType
= rAuthorityField
.GetTargetType();
2697 targetType
== SwAuthorityField::TargetType::UseDisplayURL
2698 || targetType
== SwAuthorityField::TargetType::UseTargetURL
)
2700 // Since the target type specifies to use an URL, link to it
2701 const OUString
& rURL
= rAuthorityField
.GetAbsoluteURL();
2702 if (rURL
.getLength() == 0)
2707 const SwTextNode
& rTextNode
= pFormatField
->GetTextField()->GetTextNode();
2708 if (!lcl_TryMoveToNonHiddenField(mrSh
, rTextNode
, *pFormatField
))
2713 OUString
const content(rAuthorityField
.ExpandField(true, mrSh
.GetLayout()));
2715 // Select the field.
2716 mrSh
.SwCursorShell::SetMark();
2717 mrSh
.SwCursorShell::Right(1, SwCursorSkipMode::Chars
);
2719 // Create the links.
2720 for (const auto& rLinkRect
: *mrSh
.SwCursorShell::GetCursor_())
2722 for (const auto& rLinkPageNum
: CalcOutputPageNums(rLinkRect
))
2724 tools::Rectangle
aRect(SwRectToPDFRect(pPageFrame
, rLinkRect
.SVRect()));
2725 sal_Int32 nLinkId
= pPDFExtOutDevData
->CreateLink(aRect
, content
, rLinkPageNum
);
2726 IdMapEntry
aLinkEntry(rLinkRect
, nLinkId
);
2727 pPDFExtOutDevData
->GetSwPDFState()->m_LinkIdMap
.push_back(aLinkEntry
);
2728 pPDFExtOutDevData
->SetLinkURL(nLinkId
, rURL
);
2731 mrSh
.SwCursorShell::ClearMark();
2733 else if (targetType
== SwAuthorityField::TargetType::BibliographyTableRow
)
2735 // As the target type specifies, try linking to a bibliography table row
2736 sal_Int32 nDestId
= -1;
2738 std::unordered_map
<const SwTOXBase
*, OUString
> vFormattedFieldStrings
;
2739 for (const auto& rDestinationTuple
: vDestinations
)
2741 if (vFormattedFieldStrings
.find(std::get
<0>(rDestinationTuple
))
2742 == vFormattedFieldStrings
.end())
2743 vFormattedFieldStrings
.emplace(std::get
<0>(rDestinationTuple
),
2744 rAuthorityField
.GetAuthority(mrSh
.GetLayout(),
2745 &std::get
<0>(rDestinationTuple
)->GetTOXForm()));
2747 if (vFormattedFieldStrings
.at(std::get
<0>(rDestinationTuple
)) == *std::get
<1>(rDestinationTuple
))
2749 nDestId
= std::get
<2>(rDestinationTuple
);
2757 const SwTextNode
& rTextNode
= pFormatField
->GetTextField()->GetTextNode();
2758 if (!lcl_TryMoveToNonHiddenField(mrSh
, rTextNode
, *pFormatField
))
2763 OUString
const content(rAuthorityField
.ExpandField(true, mrSh
.GetLayout()));
2765 // Select the field.
2766 mrSh
.SwCursorShell::SetMark();
2767 mrSh
.SwCursorShell::Right(1, SwCursorSkipMode::Chars
);
2769 // Create the links.
2770 for (const auto& rLinkRect
: *mrSh
.SwCursorShell::GetCursor_())
2772 for (const auto& rLinkPageNum
: CalcOutputPageNums(rLinkRect
))
2774 tools::Rectangle
aRect(SwRectToPDFRect(pPageFrame
, rLinkRect
.SVRect()));
2775 sal_Int32 nLinkId
= pPDFExtOutDevData
->CreateLink(aRect
, content
, rLinkPageNum
);
2776 IdMapEntry
aLinkEntry(rLinkRect
, nLinkId
);
2777 pPDFExtOutDevData
->GetSwPDFState()->m_LinkIdMap
.push_back(aLinkEntry
);
2778 pPDFExtOutDevData
->SetLinkDest(nLinkId
, nDestId
);
2781 mrSh
.SwCursorShell::ClearMark();
2786 // Returns the page number in the output pdf on which the given rect is located.
2787 // If this page is duplicated, method will return first occurrence of it.
2788 sal_Int32
SwEnhancedPDFExportHelper::CalcOutputPageNum( const SwRect
& rRect
) const
2790 std::vector
< sal_Int32
> aPageNums
= CalcOutputPageNums( rRect
);
2791 if ( !aPageNums
.empty() )
2792 return aPageNums
[0];
2796 // Returns a vector of the page numbers in the output pdf on which the given
2797 // rect is located. There can be many such pages since StringRangeEnumerator
2798 // allows duplication of its entries.
2799 std::vector
< sal_Int32
> SwEnhancedPDFExportHelper::CalcOutputPageNums(
2800 const SwRect
& rRect
) const
2802 std::vector
< sal_Int32
> aPageNums
;
2804 // Document page number.
2805 sal_Int32 nPageNumOfRect
= mrSh
.GetPageNumAndSetOffsetForPDF( mrOut
, rRect
);
2806 if ( nPageNumOfRect
< 0 )
2809 // What will be the page numbers of page nPageNumOfRect in the output pdf?
2812 if ( mbSkipEmptyPages
)
2813 // Map the page number to the range without empty pages.
2814 nPageNumOfRect
= maPageNumberMap
[ nPageNumOfRect
];
2816 if ( mpRangeEnum
->hasValue( nPageNumOfRect
) )
2818 sal_Int32 nOutputPageNum
= 0;
2819 StringRangeEnumerator::Iterator aIter
= mpRangeEnum
->begin();
2820 StringRangeEnumerator::Iterator aEnd
= mpRangeEnum
->end();
2821 for ( ; aIter
!= aEnd
; ++aIter
)
2823 if ( *aIter
== nPageNumOfRect
)
2824 aPageNums
.push_back( nOutputPageNum
);
2831 if ( mbSkipEmptyPages
)
2833 sal_Int32 nOutputPageNum
= 0;
2834 for ( size_t i
= 0; i
< maPageNumberMap
.size(); ++i
)
2836 if ( maPageNumberMap
[i
] >= 0 ) // is not empty?
2838 if ( i
== static_cast<size_t>( nPageNumOfRect
) )
2840 aPageNums
.push_back( nOutputPageNum
);
2848 aPageNums
.push_back( nPageNumOfRect
);
2854 void SwEnhancedPDFExportHelper::MakeHeaderFooterLinks( vcl::PDFExtOutDevData
& rPDFExtOutDevData
,
2855 const SwTextNode
& rTNd
,
2856 const SwRect
& rLinkRect
,
2858 const OUString
& rURL
,
2860 OUString
const& rContent
) const
2862 // We assume, that the primary link has just been exported. Therefore
2863 // the offset of the link rectangle calculates as follows:
2864 const Point aOffset
= rLinkRect
.Pos() + mrOut
.GetMapMode().GetOrigin();
2866 SwIterator
<SwTextFrame
, SwTextNode
, sw::IteratorMode::UnwrapMulti
> aIter(rTNd
);
2867 for ( SwTextFrame
* pTmpFrame
= aIter
.First(); pTmpFrame
; pTmpFrame
= aIter
.Next() )
2869 // Add offset to current page:
2870 const SwPageFrame
* pPageFrame
= pTmpFrame
->FindPageFrame();
2871 SwRect
aHFLinkRect( rLinkRect
);
2872 aHFLinkRect
.Pos() = pPageFrame
->getFrameArea().Pos() + aOffset
;
2874 // #i97135# the gcc_x64 optimizer gets aHFLinkRect != rLinkRect wrong
2875 // fool it by comparing the position only (the width and height are the
2877 if ( aHFLinkRect
.Pos() != rLinkRect
.Pos() )
2880 std::vector
<sal_Int32
> aHFLinkPageNums
= CalcOutputPageNums( aHFLinkRect
);
2882 for (sal_Int32 aHFLinkPageNum
: aHFLinkPageNums
)
2885 tools::Rectangle
aRect(SwRectToPDFRect(pPageFrame
, aHFLinkRect
.SVRect()));
2886 const sal_Int32 nHFLinkId
=
2887 rPDFExtOutDevData
.CreateLink(aRect
, rContent
, aHFLinkPageNum
);
2889 // Connect Link and Destination:
2891 rPDFExtOutDevData
.SetLinkDest( nHFLinkId
, nDestId
);
2893 rPDFExtOutDevData
.SetLinkURL( nHFLinkId
, rURL
);
2899 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */