1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sd.hxx"
26 
27 #include "PreviewRenderer.hxx"
28 
29 #include "DrawDocShell.hxx"
30 #include "drawdoc.hxx"
31 #include "drawview.hxx"
32 #include "sdpage.hxx"
33 #include "ViewShell.hxx"
34 #include <vcl/virdev.hxx>
35 #include <svx/svdpagv.hxx>
36 #include <svx/svdoutl.hxx>
37 #include <editeng/eeitem.hxx>
38 #include <editeng/editstat.hxx>
39 #include <tools/link.hxx>
40 #include <vcl/svapp.hxx>
41 #include <tools/diagnose_ex.h>
42 #include <svx/sdr/contact/viewobjectcontact.hxx>
43 #include <svx/sdr/contact/viewcontact.hxx>
44 
45 using namespace ::com::sun::star;
46 using namespace ::com::sun::star::uno;
47 
48 
49 namespace sd {
50 
51 const int PreviewRenderer::snSubstitutionTextSize = 11;
52 const int PreviewRenderer::snFrameWidth = 1;
53 
54 namespace {
55     /** This incarnation of the ViewObjectContactRedirector filters away all
56         PageObj objects, unconditionally.
57     */
58     class ViewRedirector : public ::sdr::contact::ViewObjectContactRedirector
59     {
60     public:
61         ViewRedirector (void);
62         virtual ~ViewRedirector (void);
63         virtual drawinglayer::primitive2d::Primitive2DSequence createRedirectedPrimitive2DSequence(
64             const sdr::contact::ViewObjectContact& rOriginal,
65                 const sdr::contact::DisplayInfo& rDisplayInfo);
66     };
67 }
68 
69 
70 
71 
72 //===== PreviewRenderer =======================================================
73 
74 PreviewRenderer::PreviewRenderer (
75     OutputDevice* pTemplate,
76     const bool bHasFrame)
77     : mpPreviewDevice (new VirtualDevice()),
78       mpView(NULL),
79       mpDocShellOfView(NULL),
80       mnWidthOfView(0),
81       maFrameColor (svtools::ColorConfig().GetColorValue(svtools::DOCBOUNDARIES).nColor),
82       mbHasFrame(bHasFrame)
83 {
84     if (pTemplate != NULL)
85     {
86         mpPreviewDevice->SetDigitLanguage (pTemplate->GetDigitLanguage());
87         mpPreviewDevice->SetBackground(pTemplate->GetBackground());
88     }
89     else
90     {
91         mpPreviewDevice->SetBackground(Wallpaper(
92             Application::GetSettings().GetStyleSettings().GetWindowColor()));
93     }
94 }
95 
96 
97 
98 
99 PreviewRenderer::~PreviewRenderer (void)
100 {
101     if (mpDocShellOfView != NULL)
102         EndListening (*mpDocShellOfView);
103 }
104 
105 
106 
107 
108 Image PreviewRenderer::RenderPage (
109     const SdPage* pPage,
110     const sal_Int32 nWidth,
111     const String& rSubstitutionText,
112     const bool bObeyHighContrastMode,
113     const bool bDisplayPresentationObjects)
114 {
115     if (pPage != NULL)
116     {
117         const Size aPageModelSize (pPage->GetSize());
118         const double nAspectRatio (
119             double(aPageModelSize.Width()) / double(aPageModelSize.Height()));
120         const sal_Int32 nFrameWidth (mbHasFrame ? snFrameWidth : 0);
121         const sal_Int32 nHeight (sal::static_int_cast<sal_Int32>(
122             (nWidth - 2*nFrameWidth) / nAspectRatio + 2*nFrameWidth + 0.5));
123         return RenderPage (
124             pPage,
125             Size(nWidth,nHeight),
126             rSubstitutionText,
127             bObeyHighContrastMode,
128             bDisplayPresentationObjects);
129     }
130     else
131         return Image();
132 }
133 
134 
135 
136 
137 Image PreviewRenderer::RenderPage (
138     const SdPage* pPage,
139     Size aPixelSize,
140     const String& rSubstitutionText,
141     const bool bObeyHighContrastMode,
142     const bool bDisplayPresentationObjects)
143 {
144     Image aPreview;
145 
146     if (pPage != NULL)
147     {
148         try
149         {
150             if (Initialize(pPage, aPixelSize, bObeyHighContrastMode))
151             {
152                 PaintPage(pPage, bDisplayPresentationObjects);
153                 PaintSubstitutionText(rSubstitutionText);
154                 PaintFrame();
155 
156                 Size aSize (mpPreviewDevice->GetOutputSizePixel());
157                 aPreview = mpPreviewDevice->GetBitmap (
158                     mpPreviewDevice->PixelToLogic(Point(0,0)),
159                     mpPreviewDevice->PixelToLogic(aSize));
160 
161                 Cleanup();
162             }
163         }
164         catch (const com::sun::star::uno::Exception&)
165         {
166             DBG_UNHANDLED_EXCEPTION();
167         }
168     }
169 
170     return aPreview;
171 }
172 
173 
174 
175 
176 Image PreviewRenderer::RenderSubstitution (
177     const Size& rPreviewPixelSize,
178     const String& rSubstitutionText)
179 {
180     Image aPreview;
181 
182     try
183     {
184         // Set size.
185         mpPreviewDevice->SetOutputSizePixel(rPreviewPixelSize);
186 
187         // Adjust contrast mode.
188         const bool bUseContrast (
189             Application::GetSettings().GetStyleSettings().GetHighContrastMode());
190         mpPreviewDevice->SetDrawMode (bUseContrast
191             ? ViewShell::OUTPUT_DRAWMODE_CONTRAST
192             : ViewShell::OUTPUT_DRAWMODE_COLOR);
193 
194         // Set a map mode that makes a typical substitution text completely
195         // visible.
196         MapMode aMapMode (mpPreviewDevice->GetMapMode());
197         aMapMode.SetMapUnit(MAP_100TH_MM);
198         const double nFinalScale (25.0 * rPreviewPixelSize.Width() / 28000.0);
199         aMapMode.SetScaleX(nFinalScale);
200         aMapMode.SetScaleY(nFinalScale);
201         const sal_Int32 nFrameWidth (mbHasFrame ? snFrameWidth : 0);
202         aMapMode.SetOrigin(mpPreviewDevice->PixelToLogic(
203             Point(nFrameWidth,nFrameWidth),aMapMode));
204         mpPreviewDevice->SetMapMode (aMapMode);
205 
206         // Clear the background.
207         const Rectangle aPaintRectangle (
208             Point(0,0),
209             mpPreviewDevice->GetOutputSizePixel());
210         mpPreviewDevice->EnableMapMode(sal_False);
211         mpPreviewDevice->SetLineColor();
212         svtools::ColorConfig aColorConfig;
213         mpPreviewDevice->SetFillColor(aColorConfig.GetColorValue(svtools::DOCCOLOR).nColor);
214         mpPreviewDevice->DrawRect (aPaintRectangle);
215         mpPreviewDevice->EnableMapMode(sal_True);
216 
217         // Paint substitution text and a frame around it.
218         PaintSubstitutionText (rSubstitutionText);
219         PaintFrame();
220 
221         const Size aSize (mpPreviewDevice->GetOutputSizePixel());
222         aPreview = mpPreviewDevice->GetBitmap (
223             mpPreviewDevice->PixelToLogic(Point(0,0)),
224             mpPreviewDevice->PixelToLogic(aSize));
225     }
226     catch (const com::sun::star::uno::Exception&)
227     {
228         DBG_UNHANDLED_EXCEPTION();
229     }
230 
231     return aPreview;
232 }
233 
234 
235 
236 
237 bool PreviewRenderer::Initialize (
238     const SdPage* pPage,
239     const Size& rPixelSize,
240     const bool bObeyHighContrastMode)
241 {
242     bool bSuccess = false;
243     do
244     {
245         if (pPage == NULL)
246             break;
247 
248         SdrModel* pModel = pPage->GetModel();
249         if (pModel == NULL)
250             break;
251 
252         SetupOutputSize(*pPage, rPixelSize);
253 
254         SdDrawDocument* pDocument
255             = static_cast<SdDrawDocument*>(pPage->GetModel());
256         DrawDocShell* pDocShell = pDocument->GetDocSh();
257 
258         // Create view
259         ProvideView (pDocShell);
260         if (mpView.get() == NULL)
261             break;
262 
263         // Adjust contrast mode.
264         bool bUseContrast (bObeyHighContrastMode
265             && Application::GetSettings().GetStyleSettings().GetHighContrastMode());
266         mpPreviewDevice->SetDrawMode (bUseContrast
267             ? ViewShell::OUTPUT_DRAWMODE_CONTRAST
268             : ViewShell::OUTPUT_DRAWMODE_COLOR);
269         mpPreviewDevice->SetSettings(Application::GetSettings());
270 
271         // Tell the view to show the given page.
272         SdPage* pNonConstPage = const_cast<SdPage*>(pPage);
273         if (pPage->IsMasterPage())
274 		{
275 			mpView->ShowSdrPage(mpView->GetModel()->GetMasterPage(pPage->GetPageNum()));
276 		}
277         else
278 		{
279             mpView->ShowSdrPage(pNonConstPage);
280 		}
281 
282         // Make sure that a page view exists.
283         SdrPageView* pPageView = mpView->GetSdrPageView();
284         if (pPageView == NULL)
285             break;
286         // Set background color of page view and outliner.
287         svtools::ColorConfig aColorConfig;
288         const Color aPageBackgroundColor(pPage->GetPageBackgroundColor(pPageView));
289         pPageView->SetApplicationBackgroundColor(aPageBackgroundColor);
290         SdrOutliner& rOutliner (pDocument->GetDrawOutliner(NULL));
291         rOutliner.SetBackgroundColor(aPageBackgroundColor);
292         rOutliner.SetDefaultLanguage(pDocument->GetLanguage(EE_CHAR_LANGUAGE));
293         mpView->SetApplicationBackgroundColor(
294             Color(aColorConfig.GetColorValue(svtools::APPBACKGROUND).nColor));
295         mpPreviewDevice->SetBackground(Wallpaper(aPageBackgroundColor));
296         mpPreviewDevice->Erase();
297 
298         bSuccess = true;
299     }
300     while (false);
301 
302     return bSuccess;
303 }
304 
305 
306 
307 
308 void PreviewRenderer::Cleanup (void)
309 {
310     mpView->HideSdrPage();
311 }
312 
313 
314 
315 
316 void PreviewRenderer::PaintPage (
317     const SdPage* pPage,
318     const bool bDisplayPresentationObjects)
319 {
320     // Paint the page.
321     Rectangle aPaintRectangle (Point(0,0), pPage->GetSize());
322     Region aRegion (aPaintRectangle);
323 
324     // Turn off online spelling and redlining.
325     SdrOutliner* pOutliner = NULL;
326     sal_uLong nSavedControlWord (0);
327     if (mpDocShellOfView!=NULL && mpDocShellOfView->GetDoc()!=NULL)
328     {
329         pOutliner = &mpDocShellOfView->GetDoc()->GetDrawOutliner();
330         nSavedControlWord = pOutliner->GetControlWord();
331         pOutliner->SetControlWord((nSavedControlWord & ~EE_CNTRL_ONLINESPELLING));
332     }
333 
334     // Use a special redirector to prevent PresObj shapes from being painted.
335     boost::scoped_ptr<ViewRedirector> pRedirector;
336     if ( ! bDisplayPresentationObjects)
337         pRedirector.reset(new ViewRedirector());
338 
339     try
340     {
341         mpView->CompleteRedraw(mpPreviewDevice.get(), aRegion, pRedirector.get());
342     }
343     catch (const ::com::sun::star::uno::Exception&)
344     {
345         DBG_UNHANDLED_EXCEPTION();
346     }
347 
348     // Restore the previous online spelling and redlining states.
349     if (pOutliner != NULL)
350         pOutliner->SetControlWord(nSavedControlWord);
351 }
352 
353 
354 
355 
356 void PreviewRenderer::PaintSubstitutionText (const String& rSubstitutionText)
357 {
358     if (rSubstitutionText.Len() > 0)
359     {
360         // Set the font size.
361         const Font& rOriginalFont (mpPreviewDevice->GetFont());
362         Font aFont (mpPreviewDevice->GetSettings().GetStyleSettings().GetAppFont());
363         sal_Int32 nHeight (mpPreviewDevice->PixelToLogic(Size(0,snSubstitutionTextSize)).Height());
364         aFont.SetHeight(nHeight);
365         mpPreviewDevice->SetFont (aFont);
366 
367         // Paint the substitution text.
368         Rectangle aTextBox (
369             Point(0,0),
370             mpPreviewDevice->PixelToLogic(
371                 mpPreviewDevice->GetOutputSizePixel()));
372         sal_uInt16 nTextStyle =
373             TEXT_DRAW_CENTER
374             | TEXT_DRAW_VCENTER
375             | TEXT_DRAW_MULTILINE
376             | TEXT_DRAW_WORDBREAK;
377         mpPreviewDevice->DrawText (aTextBox, rSubstitutionText, nTextStyle);
378 
379         // Restore the font.
380         mpPreviewDevice->SetFont (rOriginalFont);
381     }
382 }
383 
384 
385 
386 
387 void PreviewRenderer::PaintFrame (void)
388 {
389     if (mbHasFrame)
390     {
391         // Paint a frame arround the preview.
392         Rectangle aPaintRectangle (
393             Point(0,0),
394             mpPreviewDevice->GetOutputSizePixel());
395         mpPreviewDevice->EnableMapMode(sal_False);
396         mpPreviewDevice->SetLineColor(maFrameColor);
397         mpPreviewDevice->SetFillColor();
398         mpPreviewDevice->DrawRect(aPaintRectangle);
399         mpPreviewDevice->EnableMapMode(sal_True);
400      }
401 }
402 
403 
404 
405 
406 void PreviewRenderer::SetupOutputSize (
407     const SdPage& rPage,
408     const Size& rFramePixelSize)
409 {
410     // First set the map mode to some arbitrary scale that is numerically
411     // stable.
412     MapMode aMapMode (mpPreviewDevice->GetMapMode());
413 	aMapMode.SetMapUnit(MAP_PIXEL);
414 
415     // Adapt it to the desired width.
416     const Size aPageModelSize (rPage.GetSize());
417     if (aPageModelSize.Width()>0 || aPageModelSize.Height()>0)
418     {
419         const sal_Int32 nFrameWidth (mbHasFrame ? snFrameWidth : 0);
420         aMapMode.SetScaleX(
421             Fraction(rFramePixelSize.Width()-2*nFrameWidth-1, aPageModelSize.Width()));
422         aMapMode.SetScaleY(
423             Fraction(rFramePixelSize.Height()-2*nFrameWidth-1, aPageModelSize.Height()));
424         aMapMode.SetOrigin(mpPreviewDevice->PixelToLogic(Point(nFrameWidth,nFrameWidth),aMapMode));
425     }
426     else
427     {
428         // We should never get here.
429         OSL_ASSERT(false);
430         aMapMode.SetScaleX(1.0);
431         aMapMode.SetScaleY(1.0);
432     }
433     mpPreviewDevice->SetMapMode (aMapMode);
434     mpPreviewDevice->SetOutputSizePixel(rFramePixelSize);
435 }
436 
437 
438 
439 
440 void PreviewRenderer::ProvideView (DrawDocShell* pDocShell)
441 {
442     if (pDocShell != mpDocShellOfView)
443     {
444         // Destroy the view that is connected to the current doc shell.
445         mpView.reset (NULL);
446 
447         // Switch our attention, i.e. listening for DYING events, to
448         // the new doc shell.
449         if (mpDocShellOfView != NULL)
450             EndListening (*mpDocShellOfView);
451         mpDocShellOfView = pDocShell;
452         if (mpDocShellOfView != NULL)
453             StartListening (*mpDocShellOfView);
454     }
455     if (mpView.get() == NULL)
456     {
457         mpView.reset (new DrawView (pDocShell, mpPreviewDevice.get(), NULL));
458     }
459 	mpView->SetPreviewRenderer(true);
460 #if 1
461     mpView->SetPageVisible(false);
462 	mpView->SetPageBorderVisible(true);
463 	mpView->SetBordVisible(false);
464 #else
465     // This works in the slide sorter but prevents the master page
466     // background being painted in the list of current master pages in the
467     // task manager.
468     mpView->SetPagePaintingAllowed(false);
469 #endif
470 }
471 
472 
473 
474 
475 Image PreviewRenderer::ScaleBitmap (
476     const BitmapEx& rBitmapEx,
477     int nWidth)
478 {
479     Image aPreview;
480 
481     do
482     {
483         // Adjust contrast mode.
484         bool bUseContrast = Application::GetSettings().GetStyleSettings().
485             GetHighContrastMode();
486         mpPreviewDevice->SetDrawMode (bUseContrast
487             ? ViewShell::OUTPUT_DRAWMODE_CONTRAST
488             : ViewShell::OUTPUT_DRAWMODE_COLOR);
489 
490         // Set output size.
491         Size aSize (rBitmapEx.GetSizePixel());
492         if (aSize.Width() <= 0)
493             break;
494         Size aFrameSize (
495             nWidth,
496             (long)((nWidth*1.0 * aSize.Height()) / aSize.Width() + 0.5));
497         Size aPreviewSize (aFrameSize.Width()-2,aFrameSize.Height()-2);
498         MapMode aMapMode (mpPreviewDevice->GetMapMode());
499         aMapMode.SetMapUnit(MAP_PIXEL);
500         aMapMode.SetOrigin (Point());
501         aMapMode.SetScaleX (1.0);
502         aMapMode.SetScaleY (1.0);
503         mpPreviewDevice->SetMapMode (aMapMode);
504         mpPreviewDevice->SetOutputSize (aFrameSize);
505 
506         // Paint a frame arround the preview.
507         mpPreviewDevice->SetLineColor (maFrameColor);
508         mpPreviewDevice->SetFillColor ();
509         mpPreviewDevice->DrawRect (Rectangle(Point(0,0), aFrameSize));
510 
511         // Paint the bitmap scaled to the desired width.
512         BitmapEx aScaledBitmap (rBitmapEx.GetBitmap());
513         aScaledBitmap.Scale (aPreviewSize, BMP_SCALE_INTERPOLATE);
514         mpPreviewDevice->DrawBitmap (
515             Point(1,1),
516             aPreviewSize,
517             aScaledBitmap.GetBitmap());
518 
519         // Get the resulting bitmap.
520         aPreview = mpPreviewDevice->GetBitmap (Point(0,0), aFrameSize);
521     }
522     while (false);
523 
524     return aPreview;
525 }
526 
527 
528 
529 
530 void PreviewRenderer::Notify(SfxBroadcaster&, const SfxHint& rHint)
531 {
532 	if (rHint.IsA(TYPE(SfxSimpleHint))
533         && mpDocShellOfView != NULL)
534     {
535         const SfxSimpleHint* pSimpleHint = PTR_CAST(SfxSimpleHint, &rHint);
536         if (pSimpleHint != NULL
537             && pSimpleHint->GetId() == SFX_HINT_DYING)
538 		{
539             // The doc shell is dying.  Our view uses its item pool and
540             // has to be destroyed as well.  The next call to
541             // ProvideView will create a new one (for another
542             // doc shell, of course.)
543             mpView.reset (NULL);
544             mpDocShellOfView = NULL;
545         }
546     }
547 }
548 
549 
550 
551 
552 //===== ViewRedirector ========================================================
553 
554 namespace {
555 
556 ViewRedirector::ViewRedirector (void)
557 {
558 }
559 
560 
561 
562 
563 ViewRedirector::~ViewRedirector (void)
564 {
565 }
566 
567 
568 
569 
570 drawinglayer::primitive2d::Primitive2DSequence ViewRedirector::createRedirectedPrimitive2DSequence(
571 	const sdr::contact::ViewObjectContact& rOriginal,
572 	const sdr::contact::DisplayInfo& rDisplayInfo)
573 {
574 	SdrObject* pObject = rOriginal.GetViewContact().TryToGetSdrObject();
575 
576 	if (pObject==NULL || pObject->GetPage() == NULL)
577 	{
578 		// not a SdrObject visualisation (maybe e.g. page) or no page
579 		return sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence(
580             rOriginal,
581             rDisplayInfo);
582     }
583 
584     const bool bDoCreateGeometry (pObject->GetPage()->checkVisibility( rOriginal, rDisplayInfo, true));
585 
586     if ( ! bDoCreateGeometry
587         && (pObject->GetObjInventor() != SdrInventor || pObject->GetObjIdentifier() != OBJ_PAGE))
588     {
589         return drawinglayer::primitive2d::Primitive2DSequence();
590     }
591 
592     if (pObject->IsEmptyPresObj())
593         return drawinglayer::primitive2d::Primitive2DSequence();
594 
595     return sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence(
596         rOriginal,
597         rDisplayInfo);
598 }
599 
600 } // end of anonymous namespace
601 
602 
603 } // end of namespace ::sd
604