summaryrefslogtreecommitdiffstats
path: root/sd/source/ui/tools/PreviewRenderer.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sd/source/ui/tools/PreviewRenderer.cxx')
-rw-r--r--sd/source/ui/tools/PreviewRenderer.cxx524
1 files changed, 524 insertions, 0 deletions
diff --git a/sd/source/ui/tools/PreviewRenderer.cxx b/sd/source/ui/tools/PreviewRenderer.cxx
new file mode 100644
index 0000000000..d3fc73bf93
--- /dev/null
+++ b/sd/source/ui/tools/PreviewRenderer.cxx
@@ -0,0 +1,524 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <PreviewRenderer.hxx>
+
+#include <DrawDocShell.hxx>
+#include <drawdoc.hxx>
+#include <drawview.hxx>
+#include <sdpage.hxx>
+#include <ViewShell.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/settings.hxx>
+
+#include <svx/svdpagv.hxx>
+#include <svx/svdoutl.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/editstat.hxx>
+#include <vcl/svapp.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+
+#include <memory>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace sd {
+
+const int PreviewRenderer::snSubstitutionTextSize = 11;
+const int PreviewRenderer::snFrameWidth = 1;
+
+namespace {
+ /** This incarnation of the ViewObjectContactRedirector filters away all
+ PageObj objects, unconditionally.
+ */
+ class ViewRedirector : public sdr::contact::ViewObjectContactRedirector
+ {
+ public:
+ ViewRedirector();
+
+ virtual void createRedirectedPrimitive2DSequence(
+ const sdr::contact::ViewObjectContact& rOriginal,
+ const sdr::contact::DisplayInfo& rDisplayInfo,
+ drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) override;
+ };
+}
+
+//===== PreviewRenderer =======================================================
+
+PreviewRenderer::PreviewRenderer (
+ const bool bHasFrame)
+ : mpPreviewDevice (VclPtr<VirtualDevice>::Create()),
+ mpDocShellOfView(nullptr),
+ maFrameColor (svtools::ColorConfig().GetColorValue(svtools::DOCBOUNDARIES).nColor),
+ mbHasFrame(bHasFrame)
+{
+ mpPreviewDevice->SetBackground(Wallpaper(
+ Application::GetSettings().GetStyleSettings().GetWindowColor()));
+}
+
+PreviewRenderer::~PreviewRenderer()
+{
+ if (mpDocShellOfView != nullptr)
+ EndListening (*mpDocShellOfView);
+}
+
+Image PreviewRenderer::RenderPage (
+ const SdPage* pPage,
+ const sal_Int32 nWidth)
+{
+ if (pPage != nullptr)
+ {
+ const Size aPageModelSize (pPage->GetSize());
+ const double nAspectRatio (
+ double(aPageModelSize.Width()) / double(aPageModelSize.Height()));
+ const sal_Int32 nFrameWidth (mbHasFrame ? snFrameWidth : 0);
+ const sal_Int32 nHeight (sal::static_int_cast<sal_Int32>(
+ (nWidth - 2*nFrameWidth) / nAspectRatio + 2*nFrameWidth + 0.5));
+ return RenderPage (
+ pPage,
+ Size(nWidth,nHeight),
+ false/*bObeyHighContrastMode*/);
+ }
+ else
+ return Image();
+}
+
+Image PreviewRenderer::RenderPage (
+ const SdPage* pPage,
+ Size aPixelSize,
+ const bool bObeyHighContrastMode,
+ const bool bDisplayPresentationObjects)
+{
+ Image aPreview;
+
+ if (pPage != nullptr)
+ {
+ try
+ {
+ if (Initialize(pPage, aPixelSize, bObeyHighContrastMode))
+ {
+ PaintPage(pPage, bDisplayPresentationObjects);
+ PaintSubstitutionText("");
+ PaintFrame();
+
+ Size aSize (mpPreviewDevice->GetOutputSizePixel());
+ aPreview = Image(mpPreviewDevice->GetBitmapEx(
+ mpPreviewDevice->PixelToLogic(Point(0,0)),
+ mpPreviewDevice->PixelToLogic(aSize)));
+
+ mpView->HideSdrPage();
+ }
+ }
+ catch (const css::uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("sd.tools");
+ }
+ }
+
+ return aPreview;
+}
+
+Image PreviewRenderer::RenderSubstitution (
+ const Size& rPreviewPixelSize,
+ const OUString& rSubstitutionText)
+{
+ Image aPreview;
+
+ try
+ {
+ // Set size.
+ mpPreviewDevice->SetOutputSizePixel(rPreviewPixelSize);
+
+ // Adjust contrast mode.
+ const bool bUseContrast (
+ Application::GetSettings().GetStyleSettings().GetHighContrastMode());
+ mpPreviewDevice->SetDrawMode (bUseContrast
+ ? sd::OUTPUT_DRAWMODE_CONTRAST
+ : sd::OUTPUT_DRAWMODE_COLOR);
+
+ // Set a map mode that makes a typical substitution text completely
+ // visible.
+ MapMode aMapMode (mpPreviewDevice->GetMapMode());
+ aMapMode.SetMapUnit(MapUnit::Map100thMM);
+ Fraction aFinalScale(25 * rPreviewPixelSize.Width(), 28000);
+ aMapMode.SetScaleX(aFinalScale);
+ aMapMode.SetScaleY(aFinalScale);
+ const sal_Int32 nFrameWidth (mbHasFrame ? snFrameWidth : 0);
+ aMapMode.SetOrigin(mpPreviewDevice->PixelToLogic(
+ Point(nFrameWidth,nFrameWidth),aMapMode));
+ mpPreviewDevice->SetMapMode (aMapMode);
+
+ // Clear the background.
+ const ::tools::Rectangle aPaintRectangle (
+ Point(0,0),
+ mpPreviewDevice->GetOutputSizePixel());
+ mpPreviewDevice->EnableMapMode(false);
+ mpPreviewDevice->SetLineColor();
+ svtools::ColorConfig aColorConfig;
+ mpPreviewDevice->SetFillColor(aColorConfig.GetColorValue(svtools::DOCCOLOR).nColor);
+ mpPreviewDevice->DrawRect (aPaintRectangle);
+ mpPreviewDevice->EnableMapMode();
+
+ // Paint substitution text and a frame around it.
+ PaintSubstitutionText (rSubstitutionText);
+ PaintFrame();
+
+ const Size aSize (mpPreviewDevice->GetOutputSizePixel());
+ aPreview = Image(mpPreviewDevice->GetBitmapEx(
+ mpPreviewDevice->PixelToLogic(Point(0,0)),
+ mpPreviewDevice->PixelToLogic(aSize)));
+ }
+ catch (const css::uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("sd.tools");
+ }
+
+ return aPreview;
+}
+
+bool PreviewRenderer::Initialize (
+ const SdPage* pPage,
+ const Size& rPixelSize,
+ const bool bObeyHighContrastMode)
+{
+ if (!pPage)
+ return false;
+
+ SetupOutputSize(*pPage, rPixelSize);
+ SdDrawDocument& rDocument(static_cast< SdDrawDocument& >(pPage->getSdrModelFromSdrPage()));
+ DrawDocShell* pDocShell = rDocument.GetDocSh();
+
+ if (!pDocShell)
+ return false;
+
+ // Create view
+ ProvideView (pDocShell);
+ if (mpView == nullptr)
+ return false;
+
+ // Adjust contrast mode.
+ bool bUseContrast (bObeyHighContrastMode
+ && Application::GetSettings().GetStyleSettings().GetHighContrastMode());
+ mpPreviewDevice->SetDrawMode (bUseContrast
+ ? sd::OUTPUT_DRAWMODE_CONTRAST
+ : sd::OUTPUT_DRAWMODE_COLOR);
+ mpPreviewDevice->SetSettings(Application::GetSettings());
+
+ // Tell the view to show the given page.
+ SdPage* pNonConstPage = const_cast<SdPage*>(pPage);
+ if (pPage->IsMasterPage())
+ {
+ mpView->ShowSdrPage(mpView->GetModel().GetMasterPage(pPage->GetPageNum()));
+ }
+ else
+ {
+ mpView->ShowSdrPage(pNonConstPage);
+ }
+
+ // Make sure that a page view exists.
+ SdrPageView* pPageView = mpView->GetSdrPageView();
+
+ if (pPageView == nullptr)
+ return false;
+
+ // #i121224# No need to set SetApplicationBackgroundColor (which is the color
+ // of the area 'behind' the page (formerly called 'Wiese') since the page previews
+ // produced exactly cover the page's area, so it would never be visible. What
+ // needs to be set is the ApplicationDocumentColor which is derived from
+ // svtools::DOCCOLOR normally
+ Color aApplicationDocumentColor;
+
+ if (pPageView->GetApplicationDocumentColor() == COL_AUTO)
+ {
+ svtools::ColorConfig aColorConfig;
+ aApplicationDocumentColor = aColorConfig.GetColorValue( svtools::DOCCOLOR ).nColor;
+ }
+ else
+ {
+ aApplicationDocumentColor = pPageView->GetApplicationDocumentColor();
+ }
+
+ pPageView->SetApplicationDocumentColor(aApplicationDocumentColor);
+ SdrOutliner& rOutliner(rDocument.GetDrawOutliner());
+ rOutliner.SetBackgroundColor(aApplicationDocumentColor);
+ rOutliner.SetDefaultLanguage(rDocument.GetLanguage(EE_CHAR_LANGUAGE));
+ mpPreviewDevice->SetBackground(Wallpaper(aApplicationDocumentColor));
+ mpPreviewDevice->Erase();
+
+ return true;
+}
+
+void PreviewRenderer::PaintPage (
+ const SdPage* pPage,
+ const bool bDisplayPresentationObjects)
+{
+ // Paint the page.
+ ::tools::Rectangle aPaintRectangle (Point(0,0), pPage->GetSize());
+ vcl::Region aRegion (aPaintRectangle);
+
+ // Turn off online spelling and redlining.
+ SdrOutliner* pOutliner = nullptr;
+ EEControlBits nSavedControlWord = EEControlBits::NONE;
+ if (mpDocShellOfView!=nullptr && mpDocShellOfView->GetDoc()!=nullptr)
+ {
+ pOutliner = &mpDocShellOfView->GetDoc()->GetDrawOutliner();
+ nSavedControlWord = pOutliner->GetControlWord();
+ pOutliner->SetControlWord(nSavedControlWord & ~EEControlBits::ONLINESPELLING);
+ }
+
+ // Use a special redirector to prevent PresObj shapes from being painted.
+ std::unique_ptr<ViewRedirector> pRedirector;
+ if ( ! bDisplayPresentationObjects)
+ pRedirector.reset(new ViewRedirector());
+
+ try
+ {
+ mpView->CompleteRedraw(mpPreviewDevice.get(), aRegion, pRedirector.get());
+ }
+ catch (const css::uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("sd.tools");
+ }
+
+ // Restore the previous online spelling and redlining states.
+ if (pOutliner != nullptr)
+ pOutliner->SetControlWord(nSavedControlWord);
+}
+
+void PreviewRenderer::PaintSubstitutionText (const OUString& rSubstitutionText)
+{
+ if (rSubstitutionText.isEmpty())
+ return;
+
+ // Set the font size.
+ const vcl::Font& rOriginalFont (mpPreviewDevice->GetFont());
+ vcl::Font aFont (mpPreviewDevice->GetSettings().GetStyleSettings().GetAppFont());
+ sal_Int32 nHeight (mpPreviewDevice->PixelToLogic(Size(0,snSubstitutionTextSize)).Height());
+ aFont.SetFontHeight(nHeight);
+ mpPreviewDevice->SetFont (aFont);
+
+ // Paint the substitution text.
+ ::tools::Rectangle aTextBox (
+ Point(0,0),
+ mpPreviewDevice->PixelToLogic(
+ mpPreviewDevice->GetOutputSizePixel()));
+ DrawTextFlags const nTextStyle =
+ DrawTextFlags::Center
+ | DrawTextFlags::VCenter
+ | DrawTextFlags::MultiLine
+ | DrawTextFlags::WordBreak;
+ mpPreviewDevice->DrawText (aTextBox, rSubstitutionText, nTextStyle);
+
+ // Restore the font.
+ mpPreviewDevice->SetFont (rOriginalFont);
+}
+
+void PreviewRenderer::PaintFrame()
+{
+ if (mbHasFrame)
+ {
+ // Paint a frame around the preview.
+ ::tools::Rectangle aPaintRectangle (
+ Point(0,0),
+ mpPreviewDevice->GetOutputSizePixel());
+ mpPreviewDevice->EnableMapMode(false);
+ mpPreviewDevice->SetLineColor(maFrameColor);
+ mpPreviewDevice->SetFillColor();
+ mpPreviewDevice->DrawRect(aPaintRectangle);
+ mpPreviewDevice->EnableMapMode();
+ }
+}
+
+void PreviewRenderer::SetupOutputSize (
+ const SdPage& rPage,
+ const Size& rFramePixelSize)
+{
+ // First set the map mode to some arbitrary scale that is numerically
+ // stable.
+ MapMode aMapMode (mpPreviewDevice->GetMapMode());
+ aMapMode.SetMapUnit(MapUnit::MapPixel);
+
+ // Adapt it to the desired width.
+ const Size aPageModelSize (rPage.GetSize());
+ if (!aPageModelSize.IsEmpty())
+ {
+ const sal_Int32 nFrameWidth (mbHasFrame ? snFrameWidth : 0);
+ aMapMode.SetScaleX(
+ Fraction(rFramePixelSize.Width()-2*nFrameWidth-1, aPageModelSize.Width()));
+ aMapMode.SetScaleY(
+ Fraction(rFramePixelSize.Height()-2*nFrameWidth-1, aPageModelSize.Height()));
+ aMapMode.SetOrigin(mpPreviewDevice->PixelToLogic(Point(nFrameWidth,nFrameWidth),aMapMode));
+ }
+ else
+ {
+ // We should never get here.
+ OSL_ASSERT(false);
+ aMapMode.SetScaleX(Fraction(1.0));
+ aMapMode.SetScaleY(Fraction(1.0));
+ }
+ mpPreviewDevice->SetMapMode (aMapMode);
+ mpPreviewDevice->SetOutputSizePixel(rFramePixelSize);
+}
+
+void PreviewRenderer::ProvideView (DrawDocShell* pDocShell)
+{
+ if (pDocShell != mpDocShellOfView)
+ {
+ // Destroy the view that is connected to the current doc shell.
+ mpView.reset();
+
+ // Switch our attention, i.e. listening for DYING events, to
+ // the new doc shell.
+ if (mpDocShellOfView != nullptr)
+ EndListening (*mpDocShellOfView);
+ mpDocShellOfView = pDocShell;
+ if (mpDocShellOfView != nullptr)
+ StartListening (*mpDocShellOfView);
+ }
+ if (mpView == nullptr)
+ {
+ mpView.reset (new DrawView (pDocShell, mpPreviewDevice.get(), nullptr));
+ }
+ mpView->SetPreviewRenderer(true);
+ mpView->SetPageVisible(false);
+ mpView->SetPageBorderVisible();
+ mpView->SetBordVisible(false);
+ mpView->SetGridVisible(false);
+ mpView->SetHlplVisible(false);
+ mpView->SetGlueVisible(false);
+}
+
+Image PreviewRenderer::ScaleBitmap (
+ const BitmapEx& rBitmapEx,
+ int nWidth)
+{
+ Image aPreview;
+
+ do
+ {
+ // Adjust contrast mode.
+ bool bUseContrast = Application::GetSettings().GetStyleSettings().
+ GetHighContrastMode();
+ mpPreviewDevice->SetDrawMode (bUseContrast
+ ? sd::OUTPUT_DRAWMODE_CONTRAST
+ : sd::OUTPUT_DRAWMODE_COLOR);
+
+ // Set output size.
+ Size aSize (rBitmapEx.GetSizePixel());
+ if (aSize.Width() <= 0)
+ break;
+ Size aFrameSize (
+ nWidth,
+ static_cast<::tools::Long>((nWidth*1.0 * aSize.Height()) / aSize.Width() + 0.5));
+ Size aPreviewSize (aFrameSize.Width()-2,aFrameSize.Height()-2);
+ MapMode aMapMode (mpPreviewDevice->GetMapMode());
+ aMapMode.SetMapUnit(MapUnit::MapPixel);
+ aMapMode.SetOrigin (Point());
+ aMapMode.SetScaleX (Fraction(1.0));
+ aMapMode.SetScaleY (Fraction(1.0));
+ mpPreviewDevice->SetMapMode (aMapMode);
+ mpPreviewDevice->SetOutputSize (aFrameSize);
+
+ // Paint a frame around the preview.
+ mpPreviewDevice->SetLineColor (maFrameColor);
+ mpPreviewDevice->SetFillColor ();
+ mpPreviewDevice->DrawRect (::tools::Rectangle(Point(0,0), aFrameSize));
+
+ // Paint the bitmap scaled to the desired width.
+ BitmapEx aScaledBitmap(rBitmapEx);
+ aScaledBitmap.Scale (aPreviewSize, BmpScaleFlag::BestQuality);
+ mpPreviewDevice->DrawBitmapEx (
+ Point(1,1),
+ aPreviewSize,
+ aScaledBitmap);
+
+ // Get the resulting bitmap.
+ aPreview = Image(mpPreviewDevice->GetBitmapEx(Point(0,0), aFrameSize));
+ }
+ while (false);
+
+ return aPreview;
+}
+
+void PreviewRenderer::Notify(SfxBroadcaster&, const SfxHint& rHint)
+{
+ if (!mpDocShellOfView)
+ return;
+
+ if (rHint.GetId() == SfxHintId::Dying)
+ {
+ // The doc shell is dying. Our view uses its item pool and
+ // has to be destroyed as well. The next call to
+ // ProvideView will create a new one (for another
+ // doc shell, of course.)
+ mpView.reset();
+ mpDocShellOfView = nullptr;
+ }
+}
+
+//===== ViewRedirector ========================================================
+
+namespace {
+
+ViewRedirector::ViewRedirector()
+{
+}
+
+void ViewRedirector::createRedirectedPrimitive2DSequence(
+ const sdr::contact::ViewObjectContact& rOriginal,
+ const sdr::contact::DisplayInfo& rDisplayInfo,
+ drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor)
+{
+ SdrObject* pObject = rOriginal.GetViewContact().TryToGetSdrObject();
+
+ if (pObject==nullptr || pObject->getSdrPageFromSdrObject() == nullptr)
+ {
+ // not a SdrObject visualisation (maybe e.g. page) or no page
+ sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence(
+ rOriginal,
+ rDisplayInfo,
+ rVisitor);
+ return;
+ }
+
+ const bool bDoCreateGeometry (pObject->getSdrPageFromSdrObject()->checkVisibility( rOriginal, rDisplayInfo, true));
+
+ if ( ! bDoCreateGeometry
+ && (pObject->GetObjInventor() != SdrInventor::Default || pObject->GetObjIdentifier() != SdrObjKind::Page))
+ {
+ return;
+ }
+
+ if (pObject->IsEmptyPresObj())
+ return;
+
+ sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence(
+ rOriginal,
+ rDisplayInfo,
+ rVisitor);
+}
+
+} // end of anonymous namespace
+
+} // end of namespace ::sd
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */