diff options
Diffstat (limited to 'sd/source/ui/tools/PreviewRenderer.cxx')
-rw-r--r-- | sd/source/ui/tools/PreviewRenderer.cxx | 524 |
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: */ |