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 .
21 #include <hintids.hxx>
22 #include <sfx2/printer.hxx>
23 #include <svx/svdview.hxx>
24 #include <osl/diagnose.h>
25 #include <tools/UnitConversion.hxx>
29 #include <fmtfsize.hxx>
30 #include <rootfrm.hxx>
31 #include <pagefrm.hxx>
34 #include <IDocumentUndoRedo.hxx>
35 #include <IDocumentDeviceAccess.hxx>
36 #include <IDocumentFieldsAccess.hxx>
37 #include <IDocumentLayoutAccess.hxx>
40 #include <viewimp.hxx>
43 #include <viewopt.hxx>
44 #include <printdata.hxx>
45 #include <pagedesc.hxx>
46 #include <ptqueue.hxx>
48 #include <fmtpdsc.hxx>
49 #include <PostItMgr.hxx>
52 using namespace ::com::sun::star
;
62 SwQueuedPaint( SwViewShell
*pNew
, const SwRect
&rRect
) :
69 SwQueuedPaint
*SwPaintQueue::s_pPaintQueue
= nullptr;
73 // saves some settings from the draw view
79 explicit SwDrawViewSave( SdrView
* pSdrView
);
85 void SwPaintQueue::Add( SwViewShell
*pNew
, const SwRect
&rNew
)
87 SwQueuedPaint
*pPt
= s_pPaintQueue
;
90 while ( pPt
->pSh
!= pNew
&& pPt
->pNext
)
92 if ( pPt
->pSh
== pNew
)
94 pPt
->aRect
.Union( rNew
);
98 SwQueuedPaint
*pNQ
= new SwQueuedPaint( pNew
, rNew
);
105 void SwPaintQueue::Repaint()
107 if (SwRootFrame::IsInPaint() || !s_pPaintQueue
)
110 SwQueuedPaint
*pPt
= s_pPaintQueue
;
112 { SwViewShell
*pSh
= pPt
->pSh
;
113 CurrShell
aCurr( pSh
);
114 if ( pSh
->IsPreview() )
118 // for previewing, since rows/columns are known in PaintHdl (UI)
119 pSh
->GetWin()->Invalidate();
123 pSh
->Paint(*pSh
->GetOut(), pPt
->aRect
.SVRect());
130 s_pPaintQueue
= s_pPaintQueue
->pNext
;
132 } while (s_pPaintQueue
);
135 void SwPaintQueue::Remove( SwViewShell
const *pSh
)
137 SwQueuedPaint
*pPt
= s_pPaintQueue
;
141 SwQueuedPaint
*pPrev
= nullptr;
142 while ( pPt
&& pPt
->pSh
!= pSh
)
150 pPrev
->pNext
= pPt
->pNext
;
151 else if (pPt
== s_pPaintQueue
)
152 s_pPaintQueue
= nullptr;
157 void SetSwVisArea( SwViewShell
*pSh
, const SwRect
&rRect
)
159 OSL_ENSURE( !pSh
->GetWin(), "Print with window?" );
160 pSh
->maVisArea
= rRect
;
161 pSh
->Imp()->SetFirstVisPageInvalid();
162 Point
aPt( rRect
.Pos() );
164 // calculate an offset for the rectangle of the n-th page to
165 // move the start point of the output operation to a position
166 // such that in the output device all pages will be painted
167 // at the same position
168 aPt
.setX( -aPt
.X() ); aPt
.setY( -aPt
.Y() );
170 vcl::RenderContext
*pOut
= pSh
->GetOut();
172 MapMode
aMapMode( pOut
->GetMapMode() );
173 aMapMode
.SetOrigin( aPt
);
174 pOut
->SetMapMode( aMapMode
);
177 void SwViewShell::InitPrt( OutputDevice
*pOutDev
)
179 // For printing we use a negative offset (exactly the offset of OutputSize).
180 // This is necessary because the origin is in the upper left corner of the
181 // physical page while the output uses OutputOffset as origin.
184 maPrtOffset
= Point();
186 maPrtOffset
+= pOutDev
->GetMapMode().GetOrigin();
187 MapMode
aMapMode( pOutDev
->GetMapMode() );
188 aMapMode
.SetMapUnit( MapUnit::MapTwip
);
189 pOutDev
->SetMapMode( aMapMode
);
190 pOutDev
->SetLineColor();
191 pOutDev
->SetFillColor();
203 void SwViewShell::ChgAllPageOrientation( Orientation eOri
)
205 OSL_ENSURE( mnStartAction
, "missing an Action" );
206 CurrShell
aCurr( this );
208 const size_t nAll
= GetDoc()->GetPageDescCnt();
209 bool bNewOri
= eOri
!= Orientation::Portrait
;
211 for( size_t i
= 0; i
< nAll
; ++ i
)
213 const SwPageDesc
& rOld
= GetDoc()->GetPageDesc( i
);
215 if( rOld
.GetLandscape() != bNewOri
)
217 SwPageDesc
aNew( rOld
);
219 ::sw::UndoGuard
const ug(GetDoc()->GetIDocumentUndoRedo());
220 GetDoc()->CopyPageDesc(rOld
, aNew
);
222 aNew
.SetLandscape( bNewOri
);
223 SwFrameFormat
& rFormat
= aNew
.GetMaster();
224 SwFormatFrameSize
aSz( rFormat
.GetFrameSize() );
226 // PORTRAIT -> higher than wide
227 // LANDSCAPE -> wider than high
228 // Height is the VarSize, width the FixSize (per Def.)
229 if( bNewOri
? aSz
.GetHeight() > aSz
.GetWidth()
230 : aSz
.GetHeight() < aSz
.GetWidth() )
232 SwTwips aTmp
= aSz
.GetHeight();
233 aSz
.SetHeight( aSz
.GetWidth() );
234 aSz
.SetWidth( aTmp
);
235 rFormat
.SetFormatAttr( aSz
);
237 GetDoc()->ChgPageDesc( i
, aNew
);
242 void SwViewShell::ChgAllPageSize( Size
const &rSz
)
244 OSL_ENSURE( mnStartAction
, "missing an Action" );
245 CurrShell
aCurr( this );
247 SwDoc
* pMyDoc
= GetDoc();
248 const size_t nAll
= pMyDoc
->GetPageDescCnt();
250 for( size_t i
= 0; i
< nAll
; ++i
)
252 const SwPageDesc
&rOld
= pMyDoc
->GetPageDesc( i
);
253 SwPageDesc
aNew( rOld
);
255 ::sw::UndoGuard
const ug(GetDoc()->GetIDocumentUndoRedo());
256 GetDoc()->CopyPageDesc( rOld
, aNew
);
258 SwFrameFormat
& rPgFormat
= aNew
.GetMaster();
260 const bool bOri
= aNew
.GetLandscape();
261 if( bOri
? aSz
.Height() > aSz
.Width()
262 : aSz
.Height() < aSz
.Width() )
264 SwTwips aTmp
= aSz
.Height();
265 aSz
.setHeight( aSz
.Width() );
266 aSz
.setWidth( aTmp
);
269 SwFormatFrameSize
aFrameSz( rPgFormat
.GetFrameSize() );
270 aFrameSz
.SetSize( aSz
);
271 rPgFormat
.SetFormatAttr( aFrameSz
);
272 pMyDoc
->ChgPageDesc( i
, aNew
);
276 void SwViewShell::CalcPagesForPrint( sal_uInt16 nMax
)
278 CurrShell
aCurr( this );
280 SwRootFrame
* pMyLayout
= GetLayout();
282 const SwFrame
*pPage
= pMyLayout
->Lower();
283 SwLayAction
aAction( pMyLayout
, Imp() );
285 pMyLayout
->StartAllAction();
286 for ( sal_uInt16 i
= 1; pPage
&& i
<= nMax
; pPage
= pPage
->GetNext(), ++i
)
288 pPage
->Calc(GetOut());
289 SwRect
aOldVis( VisArea() );
290 maVisArea
= pPage
->getFrameArea();
291 Imp()->SetFirstVisPageInvalid();
293 aAction
.SetPaint( false );
294 aAction
.SetWaitAllowed( false );
295 aAction
.SetReschedule( true );
297 aAction
.Action(GetOut());
299 maVisArea
= aOldVis
; //reset due to the paints
300 Imp()->SetFirstVisPageInvalid();
303 pMyLayout
->EndAllAction();
306 void SwViewShell::FillPrtDoc( SwDoc
& rPrtDoc
, const SfxPrinter
* pPrt
)
308 assert( dynamic_cast<const SwFEShell
*>( this) && "SwViewShell::Prt for FEShell only");
309 SwFEShell
* pFESh
= static_cast<SwFEShell
*>(this);
310 rPrtDoc
.getIDocumentFieldsAccess().LockExpFields();
313 //! Make a copy of it since it gets destroyed with the temporary document
314 //! used for PDF export
316 rPrtDoc
.getIDocumentDeviceAccess().setPrinter( VclPtr
<SfxPrinter
>::Create(*pPrt
), true, true );
318 const SfxItemPool
& rPool
= GetAttrPool();
319 for( sal_uInt16 nWh
= POOLATTR_BEGIN
; nWh
< POOLATTR_END
; ++nWh
)
321 const SfxPoolItem
* pCpyItem
= rPool
.GetPoolDefaultItem( nWh
);
322 if( nullptr != pCpyItem
)
323 rPrtDoc
.GetAttrPool().SetPoolDefaultItem( *pCpyItem
);
326 // JP 29.07.99 - Bug 67951 - set all Styles from the SourceDoc into
327 // the PrintDoc - will be replaced!
328 rPrtDoc
.ReplaceStyles( *GetDoc() );
330 SwShellCursor
*pActCursor
= pFESh
->GetCursor_();
331 SwShellCursor
*pFirstCursor
= pActCursor
->GetNext();
332 if( !pActCursor
->HasMark() ) // with a multi-selection the current cursor might be empty
334 pActCursor
= pActCursor
->GetPrev();
337 // Y-position of the first selection
339 if( pFESh
->IsTableMode() )
341 SwShellTableCursor
* pShellTableCursor
= pFESh
->GetTableCursor();
343 const SwContentNode
*const pContentNode
=
344 pShellTableCursor
->Start()->GetNode().GetContentNode();
345 const SwContentFrame
*const pContentFrame
= pContentNode
? pContentNode
->getLayoutFrame(GetLayout(), pShellTableCursor
->Start()) : nullptr;
349 SwCursorMoveState
aTmpState( CursorMoveState::NONE
);
350 pContentFrame
->GetCharRect( aCharRect
, *pShellTableCursor
->Start(), &aTmpState
);
351 aSelPoint
= Point( aCharRect
.Left(), aCharRect
.Top() );
354 else if (pFirstCursor
)
356 aSelPoint
= pFirstCursor
->GetSttPos();
359 const SwPageFrame
* pPage
= GetLayout()->GetPageAtPos( aSelPoint
);
360 OSL_ENSURE( pPage
, "no page found!" );
362 // get page descriptor - fall back to the first one if pPage could not be found
363 const SwPageDesc
* pPageDesc
= pPage
? rPrtDoc
.FindPageDesc(
364 pPage
->GetPageDesc()->GetName() ) : &rPrtDoc
.GetPageDesc( 0 );
366 if( !pFESh
->IsTableMode() && pActCursor
&& pActCursor
->HasMark() )
367 { // Tweak paragraph attributes of last paragraph
368 SwNodeIndex
aNodeIdx( *rPrtDoc
.GetNodes().GetEndOfContent().StartOfSectionNode() );
369 SwTextNode
* pTextNd
= rPrtDoc
.GetNodes().GoNext( &aNodeIdx
)->GetTextNode();
370 SwContentNode
*pLastNd
=
371 (*pActCursor
->GetMark()) <= (*pActCursor
->GetPoint())
372 ? pActCursor
->GetPointContentNode()
373 : pActCursor
->GetMarkContentNode();
374 // copy the paragraph attributes of the first paragraph
375 if( pLastNd
&& pLastNd
->IsTextNode() )
376 static_cast<SwTextNode
*>(pLastNd
)->CopyCollFormat( *pTextNd
);
379 // fill it with the selected content
380 pFESh
->Copy(rPrtDoc
);
382 // set the page style at the first paragraph
384 SwNodeIndex
aNodeIdx( *rPrtDoc
.GetNodes().GetEndOfContent().StartOfSectionNode() );
385 SwContentNode
* pCNd
= rPrtDoc
.GetNodes().GoNext( &aNodeIdx
); // go to 1st ContentNode
386 if( pFESh
->IsTableMode() )
388 SwTableNode
* pTNd
= pCNd
->FindTableNode();
390 pTNd
->GetTable().GetFrameFormat()->SetFormatAttr( SwFormatPageDesc( pPageDesc
) );
394 pCNd
->SetAttr( SwFormatPageDesc( pPageDesc
) );
395 if( pFirstCursor
&& pFirstCursor
->HasMark() )
397 SwTextNode
*pTextNd
= pCNd
->GetTextNode();
400 SwContentNode
*pFirstNd
=
401 (*pFirstCursor
->GetMark()) > (*pFirstCursor
->GetPoint())
402 ? pFirstCursor
->GetPointContentNode()
403 : pFirstCursor
->GetMarkContentNode();
404 // copy paragraph attributes of the first paragraph
405 if( pFirstNd
&& pFirstNd
->IsTextNode() )
406 static_cast<SwTextNode
*>(pFirstNd
)->CopyCollFormat( *pTextNd
);
413 // TODO: there is already a GetPageByPageNum, but it checks some physical page
414 // number; unsure if we want that here, should find out what that is...
416 sw_getPage(SwRootFrame
const& rLayout
, sal_Int32
const nPage
)
418 // yes this is O(n^2) but at least it does not crash...
419 SwPageFrame
const* pPage
= dynamic_cast<const SwPageFrame
*>(rLayout
.Lower());
420 for (sal_Int32 i
= nPage
; pPage
&& (i
> 0); --i
)
422 if (1 == i
) { // note: nPage is 1-based, i.e. 0 is invalid!
425 pPage
= dynamic_cast<SwPageFrame
const*>(pPage
->GetNext());
427 OSL_ENSURE(pPage
, "ERROR: SwPageFrame expected");
428 OSL_FAIL("non-existent page requested");
434 // tdf#91680 Reserve space in margin for comments only if there are comments
435 bool IsShrinkPageForPostIts(SwViewShell
const& rShell
, SwPrintData
const& rPrintData
)
437 SwPostItMode
const nPostItMode(rPrintData
.GetPrintPostIts());
438 return nPostItMode
== SwPostItMode::InMargins
439 && sw_GetPostIts(rShell
.GetDoc()->getIDocumentFieldsAccess(), nullptr);
443 bool SwViewShell::PrintOrPDFExport(
444 OutputDevice
*pOutDev
,
445 SwPrintData
const& rPrintData
,
446 sal_Int32 nRenderer
, /* the index in the vector of pages to be printed */
449 // CAUTION: Do also always update the printing routines in viewpg.cxx (PrintProspect)!
451 const sal_Int32 nMaxRenderer
= rPrintData
.GetRenderData().GetPagesToPrint().size() - 1;
452 OSL_ENSURE( 0 <= nRenderer
&& nRenderer
<= nMaxRenderer
, "nRenderer out of bounds");
453 if (!pOutDev
|| nMaxRenderer
< 0 || nRenderer
< 0 || nRenderer
> nMaxRenderer
)
456 // save settings of OutputDevice (should be done always since the
457 // output device is now provided by a call from outside the Writer)
461 const bool bHasPostItsToPrintInMargins(::sw::IsShrinkPageForPostIts(*this, rPrintData
));
462 ::std::optional
<tools::Long
> oOrigHeight
;
464 // Print/PDF export for (multi-)selection has already generated a
465 // temporary document with the selected text.
466 // (see XRenderable implementation in unotxdoc.cxx)
467 // It is implemented this way because PDF export calls this Prt function
468 // once per page and we do not like to always have the temporary document
469 // to be created that often here.
470 std::unique_ptr
<SwViewShell
> pShell(new SwViewShell(*this, nullptr, pOutDev
));
472 SdrView
*pDrawView
= pShell
->GetDrawView();
475 pDrawView
->SetBufferedOutputAllowed( false );
476 pDrawView
->SetBufferedOverlayAllowed( false );
479 { // additional scope so that the CurrShell is reset before destroying the shell
481 CurrShell
aCurr( pShell
.get() );
483 //JP 01.02.99: Bug 61335 - the ReadOnly flag is never copied
484 if( mpOpt
->IsReadonly() )
485 pShell
->mpOpt
->SetReadonly( true );
487 // save options at draw view:
488 SwDrawViewSave
aDrawViewSave( pShell
->GetDrawView() );
489 pShell
->PrepareForPrint( rPrintData
, bIsPDFExport
);
491 const sal_Int32 nPage
= rPrintData
.GetRenderData().GetPagesToPrint()[ nRenderer
];
492 OSL_ENSURE( nPage
< 0 ||
493 rPrintData
.GetRenderData().GetValidPagesSet().count( nPage
) == 1,
494 "SwViewShell::PrintOrPDFExport: nPage not valid" );
495 SwViewShell
*const pViewSh2
= (nPage
< 0)
496 ? rPrintData
.GetRenderData().m_pPostItShell
.get()// post-it page
497 : pShell
.get(); // a 'regular' page, not one from the post-it doc
499 SwPageFrame
const*const pStPage
=
500 sw_getPage(*pViewSh2
->GetLayout(), abs(nPage
));
501 OSL_ENSURE( pStPage
, "failed to get start page" );
507 //!! applying view options and formatting the document should now only be done in getRendererCount!
509 ::SetSwVisArea( pViewSh2
, pStPage
->getFrameArea() );
511 pShell
->InitPrt(pOutDev
);
513 ::SetSwVisArea( pViewSh2
, pStPage
->getFrameArea() );
515 pStPage
->GetUpper()->PaintSwFrame( *pOutDev
, pStPage
->getFrameArea(), &rPrintData
);
517 SwPaintQueue::Repaint();
519 SwPostItMgr
*pPostItManager
= bHasPostItsToPrintInMargins
? pShell
->GetPostItMgr() : nullptr;
523 pPostItManager
->CalcRects();
524 pPostItManager
->LayoutPostIts();
525 pPostItManager
->DrawNotesForPage(pOutDev
, nPage
-1);
526 oOrigHeight
.emplace(pStPage
->getFrameArea().Height());
532 // restore settings of OutputDevice (should be done always now since the
533 // output device is now provided by a call from outside the Writer)
536 // avoid a warning about unbalanced Push/Pop by doing this last:
539 // fdo#36815 Now scale the recorded page down so the comments in
540 // margins will fit in the final page
541 double fScale
= 0.75;
542 tools::Long nNewHeight
= *oOrigHeight
*fScale
;
543 tools::Long nShiftY
= (*oOrigHeight
-nNewHeight
)/2;
544 GDIMetaFile
*const pMetaFile
= pOutDev
->GetConnectMetaFile();
545 pMetaFile
->ScaleActions(fScale
, fScale
);
546 //Move the scaled page down to center it
547 //the other variant of Move does not map pixels
548 //back to the logical units correctly
549 pMetaFile
->Move(0, convertTwipToMm100(nShiftY
), pOutDev
->GetDPIX(), pOutDev
->GetDPIY());
555 void SwViewShell::PrtOle2( SwDoc
*pDoc
, const SwViewOption
*pOpt
, const SwPrintData
& rOptions
,
556 vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& rRect
,
557 bool bOutputForScreen
)
559 // For printing a shell is needed. Either the Doc already has one, then we
560 // create a new view, or it has none, then we create the first view.
561 std::unique_ptr
<SwViewShell
> pSh
;
562 if( pDoc
->getIDocumentLayoutAccess().GetCurrentViewShell() )
563 pSh
.reset(new SwViewShell( *pDoc
->getIDocumentLayoutAccess().GetCurrentViewShell(), nullptr, &rRenderContext
,VSHELLFLAG_SHARELAYOUT
));
565 pSh
.reset(new SwViewShell( *pDoc
, nullptr, pOpt
, &rRenderContext
));
567 pSh
->setOutputToWindow(bOutputForScreen
);
570 CurrShell
aCurr( pSh
.get() );
571 pSh
->PrepareForPrint( rOptions
);
572 pSh
->SetPrtFormatOption( true );
574 SwRect
aSwRect( rRect
);
575 pSh
->maVisArea
= aSwRect
;
577 if ( pSh
->GetViewOptions()->getBrowseMode() &&
578 pSh
->GetRingContainer().size() == 1 )
580 pSh
->InvalidateLayout( false );
581 pSh
->GetLayout()->Lower()->InvalidateSize();
584 // CalcPagesForPrint() should not be necessary here. The pages in the
585 // visible area will be formatted in SwRootFrame::PaintSwFrame().
586 // Removing this gives us a performance gain during saving the
587 // document because the thumbnail creation will not trigger a complete
588 // formatting of the document.
590 rRenderContext
.Push( vcl::PushFlags::CLIPREGION
);
591 rRenderContext
.IntersectClipRegion( aSwRect
.SVRect() );
592 pSh
->GetLayout()->PaintSwFrame( rRenderContext
, aSwRect
);
594 rRenderContext
.Pop();
595 // first the CurrShell object needs to be destroyed!
599 /// Check if the DocNodesArray contains fields.
600 bool SwViewShell::IsAnyFieldInDoc() const
602 for (const SfxPoolItem
* pItem
: mxDoc
->GetAttrPool().GetItemSurrogates(RES_TXTATR_FIELD
))
604 auto pFormatField
= dynamic_cast<const SwFormatField
*>(pItem
);
607 const SwTextField
* pTextField
= pFormatField
->GetTextField();
608 if( pTextField
&& pTextField
->GetTextNode().GetNodes().IsDocNodes() )
615 for (const SfxPoolItem
* pItem
: mxDoc
->GetAttrPool().GetItemSurrogates(RES_TXTATR_INPUTFIELD
))
617 const SwFormatField
* pFormatField
= dynamic_cast<const SwFormatField
*>(pItem
);
620 const SwTextField
* pTextField
= pFormatField
->GetTextField();
621 if( pTextField
&& pTextField
->GetTextNode().GetNodes().IsDocNodes() )
631 /// Saves some settings at the draw view
632 SwDrawViewSave::SwDrawViewSave( SdrView
* pSdrView
)
634 , bPrintControls(true)
638 bPrintControls
= pDV
->IsLayerPrintable( "Controls" );
642 SwDrawViewSave::~SwDrawViewSave()
646 pDV
->SetLayerPrintable( "Controls", bPrintControls
);
650 // OD 09.01.2003 #i6467# - method also called for page preview
651 void SwViewShell::PrepareForPrint( const SwPrintData
&rOptions
, bool bIsPDFExport
)
653 mpOpt
->SetGraphic ( rOptions
.m_bPrintGraphic
);
654 mpOpt
->SetTable ( rOptions
.m_bPrintTable
);
655 mpOpt
->SetDraw ( rOptions
.m_bPrintDraw
);
656 mpOpt
->SetControl ( rOptions
.m_bPrintControl
);
657 mpOpt
->SetPageBack ( rOptions
.m_bPrintPageBackground
);
658 // Font should not be black if it's a PDF Export
659 mpOpt
->SetBlackFont( rOptions
.m_bPrintBlackFont
&& !bIsPDFExport
);
661 if ( !HasDrawView() )
664 SdrView
*pDrawView
= GetDrawView();
665 // OD 09.01.2003 #i6467# - consider, if view shell belongs to page preview
668 pDrawView
->SetLayerPrintable( "Controls", rOptions
.m_bPrintControl
);
672 pDrawView
->SetLayerVisible( "Controls", rOptions
.m_bPrintControl
);
676 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */