Coverage Report

Created: 2025-11-16 09:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sd/source/ui/tools/PreviewRenderer.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
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/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
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 .
18
 */
19
20
#include <PreviewRenderer.hxx>
21
22
#include <DrawDocShell.hxx>
23
#include <drawdoc.hxx>
24
#include <drawview.hxx>
25
#include <sdpage.hxx>
26
#include <ViewShell.hxx>
27
#include <vcl/virdev.hxx>
28
#include <vcl/settings.hxx>
29
30
#include <svx/svdpagv.hxx>
31
#include <svx/svdoutl.hxx>
32
#include <editeng/eeitem.hxx>
33
#include <editeng/editstat.hxx>
34
#include <vcl/svapp.hxx>
35
#include <comphelper/diagnose_ex.hxx>
36
#include <svx/sdr/contact/viewobjectcontact.hxx>
37
#include <svx/sdr/contact/viewcontact.hxx>
38
39
#include <memory>
40
41
using namespace ::com::sun::star;
42
using namespace ::com::sun::star::uno;
43
44
namespace sd {
45
46
const int PreviewRenderer::snSubstitutionTextSize = 11;
47
const int PreviewRenderer::snFrameWidth = 1;
48
49
namespace {
50
    /** This incarnation of the ViewObjectContactRedirector filters away all
51
        PageObj objects, unconditionally.
52
    */
53
    class ViewRedirector : public sdr::contact::ViewObjectContactRedirector
54
    {
55
    public:
56
        ViewRedirector();
57
58
        virtual void createRedirectedPrimitive2DSequence(
59
            const sdr::contact::ViewObjectContact& rOriginal,
60
            const sdr::contact::DisplayInfo& rDisplayInfo,
61
            drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) override;
62
    };
63
}
64
65
//===== PreviewRenderer =======================================================
66
67
PreviewRenderer::PreviewRenderer (
68
    const bool bHasFrame)
69
0
    : mpPreviewDevice (VclPtr<VirtualDevice>::Create()),
70
0
      mpDocShellOfView(nullptr),
71
0
      maFrameColor (svtools::ColorConfig().GetColorValue(svtools::DOCBOUNDARIES).nColor),
72
0
      mbHasFrame(bHasFrame)
73
0
{
74
0
    mpPreviewDevice->SetBackground(Wallpaper(
75
0
        Application::GetSettings().GetStyleSettings().GetWindowColor()));
76
0
}
77
78
PreviewRenderer::~PreviewRenderer()
79
0
{
80
0
    if (mpDocShellOfView != nullptr)
81
0
        EndListening (*mpDocShellOfView);
82
0
}
83
84
Image PreviewRenderer::RenderPage (
85
    const SdPage* pPage,
86
    const sal_Int32 nWidth)
87
0
{
88
0
    if (pPage != nullptr)
89
0
    {
90
0
        const Size aPageModelSize (pPage->GetSize());
91
0
        const double nAspectRatio (
92
0
            double(aPageModelSize.Width()) / double(aPageModelSize.Height()));
93
0
        const sal_Int32 nFrameWidth (mbHasFrame ? snFrameWidth : 0);
94
0
        const sal_Int32 nHeight (sal::static_int_cast<sal_Int32>(
95
0
            (nWidth - 2*nFrameWidth) / nAspectRatio + 2*nFrameWidth + 0.5));
96
0
        return RenderPage (
97
0
            pPage,
98
0
            Size(nWidth,nHeight),
99
0
            false/*bObeyHighContrastMode*/);
100
0
    }
101
0
    else
102
0
        return Image();
103
0
}
104
105
Image PreviewRenderer::RenderPage (
106
    const SdPage* pPage,
107
    Size aPixelSize,
108
    const bool bObeyHighContrastMode,
109
    const bool bDisplayPresentationObjects)
110
0
{
111
0
    Image aPreview;
112
113
0
    if (pPage != nullptr)
114
0
    {
115
0
        try
116
0
        {
117
0
            if (Initialize(pPage, aPixelSize, bObeyHighContrastMode))
118
0
            {
119
0
                PaintPage(pPage, bDisplayPresentationObjects);
120
0
                PaintSubstitutionText(u""_ustr);
121
0
                PaintFrame();
122
123
0
                Size aSize (mpPreviewDevice->GetOutputSizePixel());
124
0
                aPreview = Image(mpPreviewDevice->GetBitmap(
125
0
                    mpPreviewDevice->PixelToLogic(Point(0,0)),
126
0
                    mpPreviewDevice->PixelToLogic(aSize)));
127
128
0
                mpView->HideSdrPage();
129
0
            }
130
0
        }
131
0
        catch (const css::uno::Exception&)
132
0
        {
133
0
            DBG_UNHANDLED_EXCEPTION("sd.tools");
134
0
        }
135
0
    }
136
137
0
    return aPreview;
138
0
}
139
140
Image PreviewRenderer::RenderSubstitution (
141
    const Size& rPreviewPixelSize,
142
    const OUString& rSubstitutionText)
143
0
{
144
0
    Image aPreview;
145
146
0
    try
147
0
    {
148
        // Set size.
149
0
        mpPreviewDevice->SetOutputSizePixel(rPreviewPixelSize);
150
151
        // Adjust contrast mode.
152
0
        const bool bUseContrast (
153
0
            Application::GetSettings().GetStyleSettings().GetHighContrastMode());
154
0
        mpPreviewDevice->SetDrawMode (bUseContrast
155
0
            ? sd::OUTPUT_DRAWMODE_CONTRAST
156
0
            : sd::OUTPUT_DRAWMODE_COLOR);
157
158
        // Set a map mode that makes a typical substitution text completely
159
        // visible.
160
0
        MapMode aMapMode (mpPreviewDevice->GetMapMode());
161
0
        aMapMode.SetMapUnit(MapUnit::Map100thMM);
162
0
        Fraction aFinalScale(25 * rPreviewPixelSize.Width(), 28000);
163
0
        aMapMode.SetScaleX(aFinalScale);
164
0
        aMapMode.SetScaleY(aFinalScale);
165
0
        const sal_Int32 nFrameWidth (mbHasFrame ? snFrameWidth : 0);
166
0
        aMapMode.SetOrigin(mpPreviewDevice->PixelToLogic(
167
0
            Point(nFrameWidth,nFrameWidth),aMapMode));
168
0
        mpPreviewDevice->SetMapMode (aMapMode);
169
170
        // Clear the background.
171
0
        const ::tools::Rectangle aPaintRectangle (
172
0
            Point(0,0),
173
0
            mpPreviewDevice->GetOutputSizePixel());
174
0
        mpPreviewDevice->EnableMapMode(false);
175
0
        mpPreviewDevice->SetLineColor();
176
0
        svtools::ColorConfig aColorConfig;
177
0
        mpPreviewDevice->SetFillColor(aColorConfig.GetColorValue(svtools::DOCCOLOR).nColor);
178
0
        mpPreviewDevice->DrawRect (aPaintRectangle);
179
0
        mpPreviewDevice->EnableMapMode();
180
181
        // Paint substitution text and a frame around it.
182
0
        PaintSubstitutionText (rSubstitutionText);
183
0
        PaintFrame();
184
185
0
        const Size aSize (mpPreviewDevice->GetOutputSizePixel());
186
0
        aPreview = Image(mpPreviewDevice->GetBitmap(
187
0
            mpPreviewDevice->PixelToLogic(Point(0,0)),
188
0
            mpPreviewDevice->PixelToLogic(aSize)));
189
0
    }
190
0
    catch (const css::uno::Exception&)
191
0
    {
192
0
        DBG_UNHANDLED_EXCEPTION("sd.tools");
193
0
    }
194
195
0
    return aPreview;
196
0
}
197
198
bool PreviewRenderer::Initialize (
199
    const SdPage* pPage,
200
    const Size& rPixelSize,
201
    const bool bObeyHighContrastMode)
202
0
{
203
0
    if (!pPage)
204
0
        return false;
205
206
0
    SetupOutputSize(*pPage, rPixelSize);
207
0
    SdDrawDocument& rDocument(static_cast< SdDrawDocument& >(pPage->getSdrModelFromSdrPage()));
208
0
    DrawDocShell* pDocShell = rDocument.GetDocSh();
209
210
0
    if (!pDocShell)
211
0
        return false;
212
213
    // Create view
214
0
    ProvideView (pDocShell);
215
0
    if (mpView == nullptr)
216
0
        return false;
217
218
    // Adjust contrast mode.
219
0
    bool bUseContrast (bObeyHighContrastMode
220
0
        && Application::GetSettings().GetStyleSettings().GetHighContrastMode());
221
0
    mpPreviewDevice->SetDrawMode (bUseContrast
222
0
        ? sd::OUTPUT_DRAWMODE_CONTRAST
223
0
        : sd::OUTPUT_DRAWMODE_COLOR);
224
0
    mpPreviewDevice->SetSettings(Application::GetSettings());
225
226
    // Tell the view to show the given page.
227
0
    SdPage* pNonConstPage = const_cast<SdPage*>(pPage);
228
0
    if (pPage->IsMasterPage())
229
0
    {
230
0
        mpView->ShowSdrPage(mpView->GetModel().GetMasterPage(pPage->GetPageNum()));
231
0
    }
232
0
    else
233
0
    {
234
0
        mpView->ShowSdrPage(pNonConstPage);
235
0
    }
236
237
    // Make sure that a page view exists.
238
0
    SdrPageView* pPageView = mpView->GetSdrPageView();
239
240
0
    if (pPageView == nullptr)
241
0
        return false;
242
243
    // #i121224# No need to set SetApplicationBackgroundColor (which is the color
244
    // of the area 'behind' the page (formerly called 'Wiese') since the page previews
245
    // produced exactly cover the page's area, so it would never be visible. What
246
    // needs to be set is the ApplicationDocumentColor which is derived from
247
    // svtools::DOCCOLOR normally
248
0
    Color aApplicationDocumentColor;
249
250
0
    if (pPageView->GetApplicationDocumentColor() == COL_AUTO)
251
0
    {
252
0
        svtools::ColorConfig aColorConfig;
253
0
        aApplicationDocumentColor = aColorConfig.GetColorValue( svtools::DOCCOLOR ).nColor;
254
0
    }
255
0
    else
256
0
    {
257
0
        aApplicationDocumentColor = pPageView->GetApplicationDocumentColor();
258
0
    }
259
260
0
    pPageView->SetApplicationDocumentColor(aApplicationDocumentColor);
261
0
    SdrOutliner& rOutliner(rDocument.GetDrawOutliner());
262
0
    rOutliner.SetBackgroundColor(aApplicationDocumentColor);
263
0
    rOutliner.SetDefaultLanguage(rDocument.GetLanguage(EE_CHAR_LANGUAGE));
264
0
    mpPreviewDevice->SetBackground(Wallpaper(aApplicationDocumentColor));
265
0
    mpPreviewDevice->Erase();
266
267
0
    return true;
268
0
}
269
270
void PreviewRenderer::PaintPage (
271
    const SdPage* pPage,
272
    const bool bDisplayPresentationObjects)
273
0
{
274
    // Paint the page.
275
0
    ::tools::Rectangle aPaintRectangle (Point(0,0), pPage->GetSize());
276
0
    vcl::Region aRegion (aPaintRectangle);
277
278
    // Turn off online spelling and redlining.
279
0
    SdrOutliner* pOutliner = nullptr;
280
0
    EEControlBits nSavedControlWord = EEControlBits::NONE;
281
0
    if (mpDocShellOfView!=nullptr && mpDocShellOfView->GetDoc()!=nullptr)
282
0
    {
283
0
        pOutliner = &mpDocShellOfView->GetDoc()->GetDrawOutliner();
284
0
        nSavedControlWord = pOutliner->GetControlWord();
285
0
        pOutliner->SetControlWord(nSavedControlWord & ~EEControlBits::ONLINESPELLING);
286
0
    }
287
288
    // Use a special redirector to prevent PresObj shapes from being painted.
289
0
    std::unique_ptr<ViewRedirector> pRedirector;
290
0
    if ( ! bDisplayPresentationObjects)
291
0
        pRedirector.reset(new ViewRedirector());
292
293
0
    try
294
0
    {
295
0
        mpView->CompleteRedraw(mpPreviewDevice.get(), aRegion, pRedirector.get());
296
0
    }
297
0
    catch (const css::uno::Exception&)
298
0
    {
299
0
        DBG_UNHANDLED_EXCEPTION("sd.tools");
300
0
    }
301
302
    // Restore the previous online spelling and redlining states.
303
0
    if (pOutliner != nullptr)
304
0
        pOutliner->SetControlWord(nSavedControlWord);
305
0
}
306
307
void PreviewRenderer::PaintSubstitutionText (const OUString& rSubstitutionText)
308
0
{
309
0
    if (rSubstitutionText.isEmpty())
310
0
        return;
311
312
    // Set the font size.
313
0
    const vcl::Font& rOriginalFont (mpPreviewDevice->GetFont());
314
0
    vcl::Font aFont (mpPreviewDevice->GetSettings().GetStyleSettings().GetAppFont());
315
0
    sal_Int32 nHeight (mpPreviewDevice->PixelToLogic(Size(0,snSubstitutionTextSize)).Height());
316
0
    aFont.SetFontHeight(nHeight);
317
0
    mpPreviewDevice->SetFont (aFont);
318
319
    // Paint the substitution text.
320
0
    ::tools::Rectangle aTextBox (
321
0
        Point(0,0),
322
0
        mpPreviewDevice->PixelToLogic(
323
0
            mpPreviewDevice->GetOutputSizePixel()));
324
0
    DrawTextFlags const nTextStyle =
325
0
        DrawTextFlags::Center
326
0
        | DrawTextFlags::VCenter
327
0
        | DrawTextFlags::MultiLine
328
0
        | DrawTextFlags::WordBreak;
329
0
    mpPreviewDevice->DrawText (aTextBox, rSubstitutionText, nTextStyle);
330
331
    // Restore the font.
332
0
    mpPreviewDevice->SetFont (rOriginalFont);
333
0
}
334
335
void PreviewRenderer::PaintFrame()
336
0
{
337
0
    if (mbHasFrame)
338
0
    {
339
        // Paint a frame around the preview.
340
0
        ::tools::Rectangle aPaintRectangle (
341
0
            Point(0,0),
342
0
            mpPreviewDevice->GetOutputSizePixel());
343
0
        mpPreviewDevice->EnableMapMode(false);
344
0
        mpPreviewDevice->SetLineColor(maFrameColor);
345
0
        mpPreviewDevice->SetFillColor();
346
0
        mpPreviewDevice->DrawRect(aPaintRectangle);
347
0
        mpPreviewDevice->EnableMapMode();
348
0
    }
349
0
}
350
351
void PreviewRenderer::SetupOutputSize (
352
    const SdPage& rPage,
353
    const Size& rFramePixelSize)
354
0
{
355
    // First set the map mode to some arbitrary scale that is numerically
356
    // stable.
357
0
    MapMode aMapMode (mpPreviewDevice->GetMapMode());
358
0
    aMapMode.SetMapUnit(MapUnit::MapPixel);
359
360
    // Adapt it to the desired width.
361
0
    const Size aPageModelSize (rPage.GetSize());
362
0
    if (!aPageModelSize.IsEmpty())
363
0
    {
364
0
        const sal_Int32 nFrameWidth (mbHasFrame ? snFrameWidth : 0);
365
0
        aMapMode.SetScaleX(
366
0
            Fraction(rFramePixelSize.Width()-2*nFrameWidth-1, aPageModelSize.Width()));
367
0
        aMapMode.SetScaleY(
368
0
            Fraction(rFramePixelSize.Height()-2*nFrameWidth-1, aPageModelSize.Height()));
369
0
        aMapMode.SetOrigin(mpPreviewDevice->PixelToLogic(Point(nFrameWidth,nFrameWidth),aMapMode));
370
0
    }
371
0
    else
372
0
    {
373
        // We should never get here.
374
0
        OSL_ASSERT(false);
375
0
        aMapMode.SetScaleX(Fraction(1.0));
376
0
        aMapMode.SetScaleY(Fraction(1.0));
377
0
    }
378
0
    mpPreviewDevice->SetMapMode (aMapMode);
379
0
    mpPreviewDevice->SetOutputSizePixel(rFramePixelSize);
380
0
}
381
382
void PreviewRenderer::ProvideView (DrawDocShell* pDocShell)
383
0
{
384
0
    if (pDocShell != mpDocShellOfView)
385
0
    {
386
        // Destroy the view that is connected to the current doc shell.
387
0
        mpView.reset();
388
389
        // Switch our attention, i.e. listening for DYING events, to
390
        // the new doc shell.
391
0
        if (mpDocShellOfView != nullptr)
392
0
            EndListening (*mpDocShellOfView);
393
0
        mpDocShellOfView = pDocShell;
394
0
        if (mpDocShellOfView != nullptr)
395
0
            StartListening (*mpDocShellOfView);
396
0
    }
397
0
    if (mpView == nullptr)
398
0
    {
399
0
        mpView.reset (new DrawView (pDocShell, mpPreviewDevice.get(), nullptr));
400
0
    }
401
0
    mpView->SetPreviewRenderer(true);
402
0
    mpView->SetPageVisible(false);
403
0
    mpView->SetPageBorderVisible();
404
0
    mpView->SetBordVisible(false);
405
0
    mpView->SetGridVisible(false);
406
0
    mpView->SetHlplVisible(false);
407
0
    mpView->SetGlueVisible(false);
408
0
}
409
410
Image PreviewRenderer::ScaleBitmap (
411
    const Bitmap& rBitmap,
412
    int nWidth)
413
0
{
414
0
    Image aPreview;
415
416
0
    do
417
0
    {
418
        // Adjust contrast mode.
419
0
        bool bUseContrast = Application::GetSettings().GetStyleSettings().
420
0
            GetHighContrastMode();
421
0
        mpPreviewDevice->SetDrawMode (bUseContrast
422
0
            ? sd::OUTPUT_DRAWMODE_CONTRAST
423
0
            : sd::OUTPUT_DRAWMODE_COLOR);
424
425
        // Set output size.
426
0
        Size aSize (rBitmap.GetSizePixel());
427
0
        if (aSize.Width() <= 0)
428
0
            break;
429
0
        Size aFrameSize (
430
0
            nWidth,
431
0
            static_cast<::tools::Long>((nWidth*1.0 * aSize.Height()) / aSize.Width() + 0.5));
432
0
        Size aPreviewSize (aFrameSize.Width()-2,aFrameSize.Height()-2);
433
0
        MapMode aMapMode (mpPreviewDevice->GetMapMode());
434
0
        aMapMode.SetMapUnit(MapUnit::MapPixel);
435
0
        aMapMode.SetOrigin (Point());
436
0
        aMapMode.SetScaleX (Fraction(1.0));
437
0
        aMapMode.SetScaleY (Fraction(1.0));
438
0
        mpPreviewDevice->SetMapMode (aMapMode);
439
0
        mpPreviewDevice->SetOutputSize (aFrameSize);
440
441
        // Paint a frame around the preview.
442
0
        mpPreviewDevice->SetLineColor (maFrameColor);
443
0
        mpPreviewDevice->SetFillColor ();
444
0
        mpPreviewDevice->DrawRect (::tools::Rectangle(Point(0,0), aFrameSize));
445
446
        // Paint the bitmap scaled to the desired width.
447
0
        Bitmap aScaledBitmap(rBitmap);
448
0
        aScaledBitmap.Scale (aPreviewSize, BmpScaleFlag::BestQuality);
449
0
        mpPreviewDevice->DrawBitmap (
450
0
            Point(1,1),
451
0
            aPreviewSize,
452
0
            aScaledBitmap);
453
454
        // Get the resulting bitmap.
455
0
        aPreview = Image(mpPreviewDevice->GetBitmap(Point(0,0), aFrameSize));
456
0
    }
457
0
    while (false);
458
459
0
    return aPreview;
460
0
}
461
462
void PreviewRenderer::Notify(SfxBroadcaster&, const SfxHint& rHint)
463
0
{
464
0
    if (!mpDocShellOfView)
465
0
        return;
466
467
0
    if (rHint.GetId() == SfxHintId::Dying)
468
0
    {
469
        // The doc shell is dying.  Our view uses its item pool and
470
        // has to be destroyed as well.  The next call to
471
        // ProvideView will create a new one (for another
472
        // doc shell, of course.)
473
0
        mpView.reset();
474
0
        mpDocShellOfView = nullptr;
475
0
    }
476
0
}
477
478
//===== ViewRedirector ========================================================
479
480
namespace {
481
482
ViewRedirector::ViewRedirector()
483
0
{
484
0
}
485
486
void ViewRedirector::createRedirectedPrimitive2DSequence(
487
    const sdr::contact::ViewObjectContact& rOriginal,
488
    const sdr::contact::DisplayInfo& rDisplayInfo,
489
    drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor)
490
0
{
491
0
    SdrObject* pObject = rOriginal.GetViewContact().TryToGetSdrObject();
492
493
0
    if (pObject==nullptr || pObject->getSdrPageFromSdrObject() == nullptr)
494
0
    {
495
        // not a SdrObject visualisation (maybe e.g. page) or no page
496
0
        sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence(
497
0
            rOriginal,
498
0
            rDisplayInfo,
499
0
            rVisitor);
500
0
        return;
501
0
    }
502
503
0
    const bool bDoCreateGeometry (pObject->getSdrPageFromSdrObject()->checkVisibility( rOriginal, rDisplayInfo, true));
504
505
0
    if ( ! bDoCreateGeometry
506
0
        && (pObject->GetObjInventor() != SdrInventor::Default || pObject->GetObjIdentifier() != SdrObjKind::Page))
507
0
    {
508
0
        return;
509
0
    }
510
511
0
    if (pObject->IsEmptyPresObj())
512
0
        return;
513
514
0
    sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence(
515
0
        rOriginal,
516
0
        rDisplayInfo,
517
0
        rVisitor);
518
0
}
519
520
} // end of anonymous namespace
521
522
} // end of namespace ::sd
523
524
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */