cui/source/inc/grfpage.hxx                                           |    3 
 cui/source/tabpages/grfpage.cxx                                      |   32 +
 include/sfx2/sfxsids.hrc                                             |    1 
 include/svx/GenericCheckDialog.hxx                                   |   83 +++
 include/svx/strings.hrc                                              |    7 
 include/svx/svdmodel.hxx                                             |    2 
 officecfg/registry/data/org/openoffice/Office/UI/GenericCommands.xcu |    8 
 sd/Library_sd.mk                                                     |    1 
 sd/inc/drawdoc.hxx                                                   |    2 
 sd/sdi/_drvwsh.sdi                                                   |    5 
 sd/source/ui/inc/tools/GraphicSizeCheck.hxx                          |  103 
++++
 sd/source/ui/tools/GraphicSizeCheck.cxx                              |  213 
++++++++++
 sd/source/ui/view/drviews2.cxx                                       |   12 
 sd/source/ui/view/drviewsj.cxx                                       |    4 
 sd/uiconfig/sdraw/menubar/menubar.xml                                |    1 
 sd/uiconfig/simpress/menubar/menubar.xml                             |    1 
 svx/Library_svx.mk                                                   |    1 
 svx/UIConfig_svx.mk                                                  |    2 
 svx/sdi/svx.sdi                                                      |   17 
 svx/source/dialog/GenericCheckDialog.cxx                             |   70 +++
 svx/source/tbxctrls/grafctrl.cxx                                     |    2 
 svx/uiconfig/ui/genericcheckdialog.ui                                |  132 
++++++
 svx/uiconfig/ui/genericcheckentry.ui                                 |   56 ++
 sw/Library_sw.mk                                                     |    1 
 sw/sdi/_basesh.sdi                                                   |    7 
 sw/source/core/graphic/GraphicSizeCheck.cxx                          |  164 
+++++++
 sw/source/core/inc/GraphicSizeCheck.hxx                              |   99 
++++
 sw/source/ui/frmdlg/frmdlg.cxx                                       |    7 
 sw/source/uibase/shells/basesh.cxx                                   |   17 
 sw/uiconfig/sglobal/menubar/menubar.xml                              |    1 
 sw/uiconfig/swriter/menubar/menubar.xml                              |    1 
 31 files changed, 1047 insertions(+), 8 deletions(-)

New commits:
commit e34067483ef78c1569641becfe99b79a97600aed
Author:     Tomaž Vajngerl <[email protected]>
AuthorDate: Sun Dec 19 22:31:31 2021 +0900
Commit:     Tomaž Vajngerl <[email protected]>
CommitDate: Mon Dec 20 06:21:49 2021 +0100

    Set the original size in crop dialog to preferred DPI calc. size
    
    If we have the document setting preferred image size set, then
    use that as the default DPI and recalcualte the logical image
    size using the DPI and the size in pixels.
    This is useful so we have the preferred DPI size as 100% in the
    crop dialog, so we can adjust the size in relation to that value.
    
    Change-Id: I50806f194032e228ee2cf56a39e5735a57358d46
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/127096
    Tested-by: Jenkins
    Reviewed-by: Tomaž Vajngerl <[email protected]>

diff --git a/cui/source/inc/grfpage.hxx b/cui/source/inc/grfpage.hxx
index 35653eaf6ade..2c7017a57189 100644
--- a/cui/source/inc/grfpage.hxx
+++ b/cui/source/inc/grfpage.hxx
@@ -56,6 +56,7 @@ class SvxGrfCropPage : public SfxTabPage
     tools::Long            nOldWidth;
     tools::Long            nOldHeight;
     bool            bSetOrigSize;
+    sal_Int32 m_aPreferredDPI;
 
     SvxCropExample m_aExampleWN;
 
@@ -92,7 +93,7 @@ class SvxGrfCropPage : public SfxTabPage
     void            GraphicHasChanged(bool bFound);
     virtual void    ActivatePage(const SfxItemSet& rSet) override;
 
-    static Size     GetGrfOrigSize(const Graphic&);
+    Size GetGrfOrigSize(const Graphic& rGraphic);
 public:
     SvxGrfCropPage(weld::Container* pPage, weld::DialogController* 
pController, const SfxItemSet &rSet);
     static std::unique_ptr<SfxTabPage> Create( weld::Container* pPage, 
weld::DialogController* pController, const SfxItemSet *rSet );
diff --git a/cui/source/tabpages/grfpage.cxx b/cui/source/tabpages/grfpage.cxx
index 8742044ab1c3..b83e0bbc0426 100644
--- a/cui/source/tabpages/grfpage.cxx
+++ b/cui/source/tabpages/grfpage.cxx
@@ -57,6 +57,7 @@ SvxGrfCropPage::SvxGrfCropPage(weld::Container* pPage, 
weld::DialogController* p
     , nOldWidth(0)
     , nOldHeight(0)
     , bSetOrigSize(false)
+    , m_aPreferredDPI(0)
     , m_xCropFrame(m_xBuilder->weld_widget("cropframe"))
     , m_xZoomConstRB(m_xBuilder->weld_radio_button("keepscale"))
     , m_xSizeConstRB(m_xBuilder->weld_radio_button("keepsize"))
@@ -287,6 +288,11 @@ void SvxGrfCropPage::ActivatePage(const SfxItemSet& rSet)
     DBG_ASSERT( pPool, "Where is the pool?" );
 #endif
 
+    if (!GetUserData().isEmpty())
+    {
+        m_aPreferredDPI = GetUserData().toInt32();
+    }
+
     bSetOrigSize = false;
 
     // Size
@@ -670,13 +676,27 @@ void SvxGrfCropPage::GraphicHasChanged( bool bFound )
 
 Size SvxGrfCropPage::GetGrfOrigSize(const Graphic& rGrf)
 {
-    const MapMode aMapTwip( MapUnit::MapTwip );
-    Size aSize( rGrf.GetPrefSize() );
-    if( MapUnit::MapPixel == rGrf.GetPrefMapMode().GetMapUnit() )
-        aSize = Application::GetDefaultDevice()->PixelToLogic(aSize, aMapTwip);
+    Size aSize;
+
+    if (m_aPreferredDPI > 0)
+    {
+        Size aPixelSize = rGrf.GetSizePixel();
+        double fWidth = aPixelSize.Width() / double(m_aPreferredDPI);
+        double fHeight = aPixelSize.Height() / double(m_aPreferredDPI);
+        fWidth = o3tl::convert(fWidth, o3tl::Length::in, o3tl::Length::twip);
+        fHeight = o3tl::convert(fHeight, o3tl::Length::in, o3tl::Length::twip);
+        aSize = Size(fWidth, fHeight);
+    }
     else
-        aSize = OutputDevice::LogicToLogic( aSize,
-                                        rGrf.GetPrefMapMode(), aMapTwip );
+    {
+        const MapMode aMapTwip( MapUnit::MapTwip );
+        aSize = rGrf.GetPrefSize();
+        if( MapUnit::MapPixel == rGrf.GetPrefMapMode().GetMapUnit() )
+            aSize = Application::GetDefaultDevice()->PixelToLogic(aSize, 
aMapTwip);
+        else
+            aSize = OutputDevice::LogicToLogic( aSize,
+                                            rGrf.GetPrefMapMode(), aMapTwip );
+    }
     return aSize;
 }
 
diff --git a/include/svx/svdmodel.hxx b/include/svx/svdmodel.hxx
index ab9578a7bc93..831201de4d60 100644
--- a/include/svx/svdmodel.hxx
+++ b/include/svx/svdmodel.hxx
@@ -609,6 +609,8 @@ public:
     bool DoesMakePageObjectsNamesUnique() const { return 
mbMakePageObjectsNamesUnique; }
     void DoMakePageObjectsNamesUnique(bool bDo) { mbMakePageObjectsNamesUnique 
= bDo; }
 
+    virtual sal_Int32 getImagePreferredDPI() const { return 0; }
+
     virtual void dumpAsXml(xmlTextWriterPtr pWriter) const;
 };
 
diff --git a/sd/inc/drawdoc.hxx b/sd/inc/drawdoc.hxx
index aaed07cae960..a99c27c49bd1 100644
--- a/sd/inc/drawdoc.hxx
+++ b/sd/inc/drawdoc.hxx
@@ -616,7 +616,7 @@ public:
     SAL_DLLPRIVATE void SetEmbedFontScriptAsian(bool bUse) { 
mbEmbedFontScriptAsian = bUse; }
     SAL_DLLPRIVATE void SetEmbedFontScriptComplex(bool bUse) { 
mbEmbedFontScriptComplex = bUse; }
 
-    sal_Int32 getImagePreferredDPI() { return mnImagePreferredDPI; }
+    sal_Int32 getImagePreferredDPI() const override { return 
mnImagePreferredDPI; }
     void setImagePreferredDPI(sal_Int32 nValue) { mnImagePreferredDPI = 
nValue; }
 
     void dumpAsXml(xmlTextWriterPtr pWriter) const override;
diff --git a/svx/source/tbxctrls/grafctrl.cxx b/svx/source/tbxctrls/grafctrl.cxx
index 7f644be79052..0d1913fb4780 100644
--- a/svx/source/tbxctrls/grafctrl.cxx
+++ b/svx/source/tbxctrls/grafctrl.cxx
@@ -654,6 +654,8 @@ void SvxGrafAttrHelper::ExecuteGrafAttr( SfxRequest& rReq, 
SdrView& rView )
                     SfxAbstractDialogFactory* pFact = 
SfxAbstractDialogFactory::Create();
                     ::CreateTabPage fnCreatePage = 
pFact->GetTabPageCreatorFunc( RID_SVXPAGE_GRFCROP );
                     std::unique_ptr<SfxTabPage> xTabPage = 
(*fnCreatePage)(aCropDialog.get_content_area(), &aCropDialog, &aCropDlgAttr);
+                    OUString sPreferredDPI = 
OUString::number(rView.getSdrModelFromSdrView().getImagePreferredDPI());
+                    xTabPage->SetUserData(sPreferredDPI);
                     xTabPage->SetPageTitle(aCropStr);
                     aCropDialog.SetTabPage(std::move(xTabPage));
 
diff --git a/sw/source/ui/frmdlg/frmdlg.cxx b/sw/source/ui/frmdlg/frmdlg.cxx
index ffb5a8ed76c9..fbb27c7d5f94 100644
--- a/sw/source/ui/frmdlg/frmdlg.cxx
+++ b/sw/source/ui/frmdlg/frmdlg.cxx
@@ -31,6 +31,7 @@
 #include <wrap.hxx>
 #include <column.hxx>
 #include <macassgn.hxx>
+#include <IDocumentSettingAccess.hxx>
 
 #include <strings.hrc>
 #include <svl/eitem.hxx>
@@ -189,6 +190,12 @@ void SwFrameDlg::PageCreated(const OString& rId, 
SfxTabPage &rPage)
     {
         rPage.PageCreated(m_rSet);
     }
+    else if (rId == "crop")
+    {
+        sal_Int32 nDPI = 
m_pWrtShell->GetDoc()->getIDocumentSettingAccess().getImagePreferredDPI();
+        if (nDPI)
+            rPage.SetUserData(OUString::number(nDPI));
+    }
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
commit 046e6cfa544d2ffd67fd29ba7dde41b495744618
Author:     Tomaž Vajngerl <[email protected]>
AuthorDate: Tue Dec 7 23:25:54 2021 +0100
Commit:     Tomaž Vajngerl <[email protected]>
CommitDate: Mon Dec 20 06:21:34 2021 +0100

    Add graphic size checker for the preferred document DPI
    
    This change adds a graphic size checker, which checks all the
    images in the document, if they largely differ (outside of 50% and
    110% of the image size) from the set preferred image DPI document
    setting. For all images that don't fall under this bounds, list
    them in the dialog and offer the posibility to select/goto the
    image and pop-up the properties dialog for the image to change
    its size.
    
    Change-Id: I06efce77c291fdb6ec3864d72c2f4d15dba9c42b
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/127094
    Tested-by: Jenkins
    Reviewed-by: Tomaž Vajngerl <[email protected]>

diff --git a/include/sfx2/sfxsids.hrc b/include/sfx2/sfxsids.hrc
index d9a8b14eb514..e4bf9bbbe71b 100644
--- a/include/sfx2/sfxsids.hrc
+++ b/include/sfx2/sfxsids.hrc
@@ -447,6 +447,7 @@ class SvxSearchItem;
 // Used for redaction
 #define SID_SHAPE_NAME                      (SID_SFX_START + 808)
     // FREE: SID_SFX_START + 809
+#define SID_GRAPHIC_SIZE_CHECK              (SID_SFX_START + 809)
 #define SID_ACCESSIBILITY_CHECK             (SID_SFX_START + 810)
 #define SID_ASYNCHRON                       (SID_SFX_START + 811)
 
diff --git a/include/svx/GenericCheckDialog.hxx 
b/include/svx/GenericCheckDialog.hxx
new file mode 100644
index 000000000000..ef2a41159737
--- /dev/null
+++ b/include/svx/GenericCheckDialog.hxx
@@ -0,0 +1,83 @@
+/* -*- 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/.
+ *
+ */
+
+#pragma once
+
+#include <svx/svxdllapi.h>
+#include <tools/link.hxx>
+#include <vcl/weld.hxx>
+
+namespace svx
+{
+class CheckData
+{
+public:
+    virtual ~CheckData() {}
+
+    virtual OUString getText() = 0;
+
+    virtual bool canMarkObject() = 0;
+    virtual void markObject() = 0;
+
+    virtual bool hasProperties() = 0;
+    virtual void runProperties() = 0;
+};
+
+class CheckDataCollection
+{
+protected:
+    std::vector<std::unique_ptr<CheckData>> m_aCollection;
+
+public:
+    virtual ~CheckDataCollection() {}
+
+    std::vector<std::unique_ptr<CheckData>>& getCollection() { return 
m_aCollection; }
+
+    virtual OUString getTitle() = 0;
+};
+
+class GenericCheckEntry final
+{
+private:
+    std::unique_ptr<weld::Builder> m_xBuilder;
+    std::unique_ptr<weld::Container> m_xContainer;
+    std::unique_ptr<weld::Label> m_xLabel;
+    std::unique_ptr<weld::Button> m_xMarkButton;
+    std::unique_ptr<weld::Button> m_xPropertiesButton;
+
+    std::unique_ptr<CheckData>& m_pCheckData;
+
+public:
+    GenericCheckEntry(weld::Container* pParent, std::unique_ptr<CheckData>& 
rCheckData);
+
+    weld::Widget* get_widget() const { return m_xContainer.get(); }
+
+    DECL_LINK(MarkButtonClicked, weld::Button&, void);
+    DECL_LINK(PropertiesButtonClicked, weld::Button&, void);
+};
+
+class SVX_DLLPUBLIC GenericCheckDialog final : public 
weld::GenericDialogController
+{
+private:
+    std::vector<std::unique_ptr<GenericCheckEntry>> m_aCheckEntries;
+    CheckDataCollection& m_rCheckDataCollection;
+
+    // Controls
+    std::unique_ptr<weld::Box> m_xCheckBox;
+
+public:
+    GenericCheckDialog(weld::Window* pParent, CheckDataCollection& 
rCheckDataCollection);
+    virtual ~GenericCheckDialog() override;
+    virtual short run() override;
+};
+
+} // end svx namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/svx/strings.hrc b/include/svx/strings.hrc
index 2e00e3c4c1a7..49c170b0a4bb 100644
--- a/include/svx/strings.hrc
+++ b/include/svx/strings.hrc
@@ -1816,6 +1816,13 @@
 #define RID_SVXSTR_HOR_PRESET_ONLYHOR               
NC_("RID_SVXSTR_HOR_PRESET_ONLYHOR", "Top and Bottom Borders, and All Inner 
Lines")
 #define RID_SVXSTR_VER_PRESET_ONLYVER               
NC_("RID_SVXSTR_VER_PRESET_ONLYVER", "Left and Right Borders, and All Inner 
Lines")
 
+/*--------------------------------------------------------------------
+    Description: GraphicSizeCheck strings
+ --------------------------------------------------------------------*/
+#define STR_GRAPHIC_SIZE_CHECK_DIALOG_TITLE   
NC_("STR_GRAPHIC_SIZE_CHECK_DIALOG_TITLE", "Graphic Size Check")
+#define STR_WARNING_GRAPHIC_PIXEL_COUNT_LOW   
NC_("STR_WARNING_GRAPHIC_PIXEL_COUNT_LOW", "Image '%NAME%' has too few pixels 
for the current size (%DPIX% x %DPIY% DPI)")
+#define STR_WARNING_GRAPHIC_PIXEL_COUNT_HIGH  
NC_("STR_WARNING_GRAPHIC_PIXEL_COUNT_HIGH", "Image '%NAME%' has too many pixels 
for the current size (%DPIX% x %DPIY% DPI)")
+
 #endif
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git 
a/officecfg/registry/data/org/openoffice/Office/UI/GenericCommands.xcu 
b/officecfg/registry/data/org/openoffice/Office/UI/GenericCommands.xcu
index 2e3bf1f57647..a51ab13d077c 100644
--- a/officecfg/registry/data/org/openoffice/Office/UI/GenericCommands.xcu
+++ b/officecfg/registry/data/org/openoffice/Office/UI/GenericCommands.xcu
@@ -7389,6 +7389,14 @@ bit 3 (0x8): #define 
UICOMMANDDESCRIPTION_PROPERTIES_TOGGLEBUTTON 8
           <value>1</value>
         </prop>
       </node>
+      <node oor:name=".uno:GraphicSizeCheck" oor:op="replace">
+        <prop oor:name="Label" oor:type="xs:string">
+          <value xml:lang="en-US">Graphic Size Check...</value>
+        </prop>
+        <prop oor:name="Properties" oor:type="xs:int">
+          <value>1</value>
+        </prop>
+      </node>
       <node oor:name=".uno:SidebarDeck.PropertyDeck" oor:op="replace">
         <prop oor:name="Label" oor:type="xs:string">
           <value xml:lang="en-US">Open the Properties Deck</value>
diff --git a/sd/Library_sd.mk b/sd/Library_sd.mk
index f8e77848e4d3..da11d900f564 100644
--- a/sd/Library_sd.mk
+++ b/sd/Library_sd.mk
@@ -401,6 +401,7 @@ $(eval $(call gb_Library_add_exception_objects,sd,\
        sd/source/ui/tools/AsynchronousCall \
        sd/source/ui/tools/ConfigurationAccess \
        sd/source/ui/tools/EventMultiplexer \
+       sd/source/ui/tools/GraphicSizeCheck \
        sd/source/ui/tools/IconCache \
        sd/source/ui/tools/IdleDetection \
        sd/source/ui/tools/PreviewRenderer \
diff --git a/sd/sdi/_drvwsh.sdi b/sd/sdi/_drvwsh.sdi
index 7060d8dd6434..73c2181d0743 100644
--- a/sd/sdi/_drvwsh.sdi
+++ b/sd/sdi/_drvwsh.sdi
@@ -76,6 +76,11 @@ interface DrawView
         ExecMethod = FuTemporary ;
         StateMethod = GetMenuState ;
     ]
+    SID_GRAPHIC_SIZE_CHECK
+    [
+        ExecMethod = FuTemporary ;
+        StateMethod = GetMenuState ;
+    ]
     SID_EXTERNAL_EDIT
     [
         ExecMethod = FuTemporary ;
diff --git a/sd/source/ui/inc/tools/GraphicSizeCheck.hxx 
b/sd/source/ui/inc/tools/GraphicSizeCheck.hxx
new file mode 100644
index 000000000000..9da3d569bd3a
--- /dev/null
+++ b/sd/source/ui/inc/tools/GraphicSizeCheck.hxx
@@ -0,0 +1,103 @@
+/* -*- 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/.
+ *
+ */
+
+#pragma once
+
+#include <memory>
+#include <drawdoc.hxx>
+#include <svx/GenericCheckDialog.hxx>
+#include <svx/svdograf.hxx>
+
+namespace sd
+{
+class GraphicSizeViolation final
+{
+private:
+    SdrGrafObj* m_pGraphicObject;
+
+    sal_Int32 m_nLowDPILimit = 0;
+    sal_Int32 m_nHighDPILimit = 0;
+
+    sal_Int32 m_nDPIX = 0;
+    sal_Int32 m_nDPIY = 0;
+
+public:
+    GraphicSizeViolation(sal_Int32 nDPI, SdrGrafObj* pGraphicObject);
+    bool check();
+
+    OUString getGraphicName();
+
+    SdrGrafObj* getObject() const { return m_pGraphicObject; }
+
+    bool isDPITooLow() { return m_nDPIX < m_nLowDPILimit || m_nDPIY < 
m_nLowDPILimit; }
+
+    bool isDPITooHigh() { return m_nDPIX > m_nHighDPILimit || m_nDPIY > 
m_nHighDPILimit; }
+
+    sal_Int32 getDPIX() { return m_nDPIX; }
+
+    sal_Int32 getDPIY() { return m_nDPIY; }
+};
+
+class GraphicSizeCheck final
+{
+private:
+    SdDrawDocument* m_pDocument;
+    std::vector<std::unique_ptr<GraphicSizeViolation>> 
m_aGraphicSizeViolationList;
+
+public:
+    GraphicSizeCheck(SdDrawDocument* pDocument)
+        : m_pDocument(pDocument)
+    {
+    }
+
+    void check();
+
+    std::vector<std::unique_ptr<GraphicSizeViolation>>& getViolationList()
+    {
+        return m_aGraphicSizeViolationList;
+    }
+};
+
+class GraphicSizeCheckGUIEntry : public svx::CheckData
+{
+private:
+    SdDrawDocument* m_pDocument;
+    std::unique_ptr<GraphicSizeViolation> m_pViolation;
+
+public:
+    GraphicSizeCheckGUIEntry(SdDrawDocument* pDocument,
+                             std::unique_ptr<GraphicSizeViolation>&& 
pViolation)
+        : m_pDocument(pDocument)
+        , m_pViolation(std::move(pViolation))
+    {
+    }
+
+    OUString getText() override;
+
+    bool canMarkObject() override { return true; }
+
+    void markObject() override;
+
+    bool hasProperties() override { return true; }
+
+    void runProperties() override;
+};
+
+class GraphicSizeCheckGUIResult : public svx::CheckDataCollection
+{
+public:
+    GraphicSizeCheckGUIResult(SdDrawDocument* m_pDocument);
+
+    OUString getTitle() override;
+};
+
+} // end of namespace sd
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/tools/GraphicSizeCheck.cxx 
b/sd/source/ui/tools/GraphicSizeCheck.cxx
new file mode 100644
index 000000000000..14bfcf3353b9
--- /dev/null
+++ b/sd/source/ui/tools/GraphicSizeCheck.cxx
@@ -0,0 +1,213 @@
+/* -*- 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/.
+ *
+ */
+
+#include <memory>
+#include <tools/GraphicSizeCheck.hxx>
+#include <svx/strings.hrc>
+#include <svx/svdobj.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svxids.hrc>
+#include <sfx2/dispatch.hxx>
+
+#include <sdresid.hxx>
+#include <DrawViewShell.hxx>
+#include <DrawDocShell.hxx>
+
+namespace sd
+{
+namespace
+{
+class ModelTraverseHandler
+{
+public:
+    virtual ~ModelTraverseHandler() {}
+
+    virtual void handleSdrObject(SdrObject* pObject) = 0;
+};
+
+class ModelTraverser
+{
+private:
+    std::vector<std::shared_ptr<ModelTraverseHandler>> m_pNodeHandler;
+    SdDrawDocument* m_pDocument;
+
+public:
+    ModelTraverser(SdDrawDocument* pDocument)
+        : m_pDocument(pDocument)
+    {
+    }
+
+    void traverse()
+    {
+        if (!m_pDocument)
+            return;
+
+        for (sal_uInt16 nPage = 0; nPage < m_pDocument->GetPageCount(); 
++nPage)
+        {
+            SdrPage* pPage = m_pDocument->GetPage(nPage);
+            if (pPage)
+            {
+                for (size_t nObject = 0; nObject < pPage->GetObjCount(); 
++nObject)
+                {
+                    SdrObject* pObject = pPage->GetObj(nObject);
+                    if (pObject)
+                    {
+                        for (auto& pNodeHandler : m_pNodeHandler)
+                        {
+                            pNodeHandler->handleSdrObject(pObject);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    void addNodeHandler(std::shared_ptr<ModelTraverseHandler> pHandler)
+    {
+        m_pNodeHandler.push_back(pHandler);
+    }
+};
+}
+
+GraphicSizeViolation::GraphicSizeViolation(sal_Int32 nDPI, SdrGrafObj* 
pGraphicObject)
+    : m_pGraphicObject(pGraphicObject)
+{
+    constexpr double fLowPercentage = 110;
+    constexpr double fHighPercentage = 50;
+
+    m_nLowDPILimit = sal_Int32(100.0 / fLowPercentage * nDPI);
+    m_nHighDPILimit = sal_Int32(100.0 / fHighPercentage * nDPI);
+}
+
+bool GraphicSizeViolation::check()
+{
+    Graphic aGraphic = m_pGraphicObject->GetGraphic();
+    Size aSizePixel = aGraphic.GetSizePixel();
+    Size aGraphicSize = m_pGraphicObject->GetLogicRect().GetSize();
+
+    double nSizeXInch
+        = o3tl::convert(double(aGraphicSize.Width()), o3tl::Length::mm100, 
o3tl::Length::in);
+    double nSizeYInch
+        = o3tl::convert(double(aGraphicSize.Height()), o3tl::Length::mm100, 
o3tl::Length::in);
+
+    m_nDPIX = sal_Int32(aSizePixel.Width() / nSizeXInch);
+    m_nDPIY = sal_Int32(aSizePixel.Height() / nSizeYInch);
+
+    return isDPITooLow() || isDPITooHigh();
+}
+
+OUString GraphicSizeViolation::getGraphicName() { return 
m_pGraphicObject->GetName(); }
+
+namespace
+{
+class GraphicSizeCheckHandler : public ModelTraverseHandler
+{
+    sal_Int32 m_nDPI;
+    std::vector<std::unique_ptr<GraphicSizeViolation>>& 
m_rGraphicSizeViolationList;
+
+public:
+    GraphicSizeCheckHandler(
+        sal_Int32 nDPI,
+        std::vector<std::unique_ptr<GraphicSizeViolation>>& 
rGraphicSizeViolationList)
+        : m_nDPI(nDPI)
+        , m_rGraphicSizeViolationList(rGraphicSizeViolationList)
+    {
+    }
+
+    void handleSdrObject(SdrObject* pObject) override
+    {
+        auto* pGraphicObject = dynamic_cast<SdrGrafObj*>(pObject);
+        if (!pGraphicObject)
+            return;
+
+        auto pEntry = std::make_unique<GraphicSizeViolation>(m_nDPI, 
pGraphicObject);
+        if (pEntry->check())
+        {
+            m_rGraphicSizeViolationList.push_back(std::move(pEntry));
+        }
+    }
+};
+
+} // end anonymous namespace
+
+void GraphicSizeCheck::check()
+{
+    if (!m_pDocument)
+        return;
+
+    sal_Int32 nDPI = m_pDocument->getImagePreferredDPI();
+    if (nDPI == 0)
+        return;
+
+    auto pHandler = std::make_shared<GraphicSizeCheckHandler>(nDPI, 
m_aGraphicSizeViolationList);
+
+    ModelTraverser aModelTraverser(m_pDocument);
+    aModelTraverser.addNodeHandler(pHandler);
+    aModelTraverser.traverse();
+}
+
+OUString GraphicSizeCheckGUIEntry::getText()
+{
+    OUString sText;
+
+    if (m_pViolation->isDPITooLow())
+    {
+        sText = SdResId(STR_WARNING_GRAPHIC_PIXEL_COUNT_LOW);
+    }
+    else if (m_pViolation->isDPITooHigh())
+    {
+        sText = SdResId(STR_WARNING_GRAPHIC_PIXEL_COUNT_HIGH);
+    }
+
+    sText = sText.replaceAll("%NAME%", m_pViolation->getGraphicName());
+    sText = sText.replaceAll("%DPIX%", 
OUString::number(m_pViolation->getDPIX()));
+    sText = sText.replaceAll("%DPIY%", 
OUString::number(m_pViolation->getDPIY()));
+
+    return sText;
+}
+
+void GraphicSizeCheckGUIEntry::markObject()
+{
+    sd::ViewShell* pViewShell = m_pDocument->GetDocSh()->GetViewShell();
+    SdrView* pView = pViewShell->GetView();
+    pView->ShowSdrPage(m_pViolation->getObject()->getSdrPageFromSdrObject());
+    pView->UnmarkAll();
+    pView->MarkObj(m_pViolation->getObject(), pView->GetSdrPageView());
+}
+
+void GraphicSizeCheckGUIEntry::runProperties()
+{
+    markObject();
+    sd::ViewShell* pViewShell = m_pDocument->GetDocSh()->GetViewShell();
+    pViewShell->GetDispatcher()->Execute(SID_ATTR_GRAF_CROP, 
SfxCallMode::SYNCHRON);
+}
+
+GraphicSizeCheckGUIResult::GraphicSizeCheckGUIResult(SdDrawDocument* pDocument)
+{
+    GraphicSizeCheck aCheck(pDocument);
+    aCheck.check();
+
+    auto& rCollection = getCollection();
+    for (auto& rpViolation : aCheck.getViolationList())
+    {
+        auto rGUIEntry
+            = std::make_unique<GraphicSizeCheckGUIEntry>(pDocument, 
std::move(rpViolation));
+        rCollection.push_back(std::move(rGUIEntry));
+    }
+}
+
+OUString GraphicSizeCheckGUIResult::getTitle()
+{
+    return SdResId(STR_GRAPHIC_SIZE_CHECK_DIALOG_TITLE);
+}
+
+} // end of namespace sd
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/view/drviews2.cxx b/sd/source/ui/view/drviews2.cxx
index b951231f2c5b..87d1a34e2ebe 100644
--- a/sd/source/ui/view/drviews2.cxx
+++ b/sd/source/ui/view/drviews2.cxx
@@ -183,6 +183,7 @@
 #include <SlideSorterViewShell.hxx>
 #include <controller/SlideSorterController.hxx>
 #include <controller/SlsPageSelector.hxx>
+#include <tools/GraphicSizeCheck.hxx>
 
 #include <ViewShellBase.hxx>
 #include <memory>
@@ -1462,6 +1463,17 @@ void DrawViewShell::FuTemporary(SfxRequest& rReq)
         }
         break;
 
+        case SID_GRAPHIC_SIZE_CHECK:
+        {
+            sd::GraphicSizeCheckGUIResult aResult(GetDoc());
+            svx::GenericCheckDialog aDialog(GetFrameWeld(), aResult);
+            aDialog.run();
+
+            Cancel();
+            rReq.Ignore();
+        }
+        break;
+
         case SID_ATTRIBUTES_LINE:  // BASIC
         {
             SetCurrentFunction( FuLine::Create( this, GetActiveWindow(), 
mpDrawView.get(), GetDoc(), rReq ) );
diff --git a/sd/source/ui/view/drviewsj.cxx b/sd/source/ui/view/drviewsj.cxx
index 9eebdbc57c29..6bbf88338f06 100644
--- a/sd/source/ui/view/drviewsj.cxx
+++ b/sd/source/ui/view/drviewsj.cxx
@@ -539,6 +539,10 @@ void DrawViewShell::GetMenuStateSel( SfxItemSet &rSet )
         rSet.DisableItem(SID_SAVE_GRAPHIC);
         rSet.DisableItem(SID_EXTERNAL_EDIT);
     }
+    if (GetDoc()->getImagePreferredDPI() <= 0)
+    {
+        rSet.DisableItem(SID_GRAPHIC_SIZE_CHECK);
+    }
 }
 
 } // end of namespace sd
diff --git a/sd/uiconfig/sdraw/menubar/menubar.xml 
b/sd/uiconfig/sdraw/menubar/menubar.xml
index fd327d6bc91b..9c5a997bcc5a 100644
--- a/sd/uiconfig/sdraw/menubar/menubar.xml
+++ b/sd/uiconfig/sdraw/menubar/menubar.xml
@@ -417,6 +417,7 @@
           <menu:menuitem menu:id=".uno:ColorSettings"/>
           <menu:menuseparator/>
           <menu:menuitem menu:id=".uno:GrafAttrCrop"/>
+          <menu:menuitem menu:id=".uno:GraphicSizeCheck"/>
         </menu:menupopup>
       </menu:menu>
       <menu:menuseparator/>
diff --git a/sd/uiconfig/simpress/menubar/menubar.xml 
b/sd/uiconfig/simpress/menubar/menubar.xml
index fc64d6050e6e..855a26991a0b 100644
--- a/sd/uiconfig/simpress/menubar/menubar.xml
+++ b/sd/uiconfig/simpress/menubar/menubar.xml
@@ -448,6 +448,7 @@
           <menu:menuitem menu:id=".uno:ColorSettings"/>
           <menu:menuseparator/>
           <menu:menuitem menu:id=".uno:GrafAttrCrop"/>
+          <menu:menuitem menu:id=".uno:GraphicSizeCheck"/>
         </menu:menupopup>
       </menu:menu>
       <menu:menu menu:id=".uno:FormatObjectMenu">
diff --git a/svx/Library_svx.mk b/svx/Library_svx.mk
index efb5a6e1b031..a13f1cade697 100644
--- a/svx/Library_svx.mk
+++ b/svx/Library_svx.mk
@@ -105,6 +105,7 @@ $(eval $(call gb_Library_add_exception_objects,svx,\
     svx/source/customshapes/EnhancedCustomShapeFontWork \
     svx/source/customshapes/EnhancedCustomShapeHandle \
     svx/source/dialog/AccessibilityCheckDialog \
+    svx/source/dialog/GenericCheckDialog \
     svx/source/dialog/_bmpmask \
     svx/source/dialog/charmap \
     svx/source/dialog/searchcharmap \
diff --git a/svx/UIConfig_svx.mk b/svx/UIConfig_svx.mk
index 2186185f10c2..33bc1797f20f 100644
--- a/svx/UIConfig_svx.mk
+++ b/svx/UIConfig_svx.mk
@@ -78,6 +78,8 @@ $(eval $(call gb_UIConfig_add_uifiles,svx,\
        svx/uiconfig/ui/functionmenu \
        svx/uiconfig/ui/gallerymenu1 \
        svx/uiconfig/ui/gallerymenu2 \
+       svx/uiconfig/ui/genericcheckdialog \
+       svx/uiconfig/ui/genericcheckentry \
        svx/uiconfig/ui/grafctrlbox \
        svx/uiconfig/ui/grafmodebox \
        svx/uiconfig/ui/headfootformatpage \
diff --git a/svx/sdi/svx.sdi b/svx/sdi/svx.sdi
index 1e08145d3644..f0314fe63360 100644
--- a/svx/sdi/svx.sdi
+++ b/svx/sdi/svx.sdi
@@ -12425,6 +12425,23 @@ SfxVoidItem SpellCheckApplySuggestion 
SID_SPELLCHECK_APPLY_SUGGESTION
     GroupId = SfxGroupId::Format;
 ]
 
+SfxVoidItem GraphicSizeCheck SID_GRAPHIC_SIZE_CHECK
+()
+[
+    AutoUpdate = FALSE,
+    FastCall = FALSE,
+    ReadOnlyDoc = TRUE,
+    Toggle = FALSE,
+    Container = FALSE,
+    RecordAbsolute = FALSE,
+    RecordPerSet;
+
+    AccelConfig = FALSE,
+    MenuConfig = FALSE,
+    ToolBoxConfig = FALSE,
+    GroupId = SfxGroupId::Modify;
+]
+
 SfxVoidItem AccessibilityCheck SID_ACCESSIBILITY_CHECK
 ()
 [
diff --git a/svx/source/dialog/GenericCheckDialog.cxx 
b/svx/source/dialog/GenericCheckDialog.cxx
new file mode 100644
index 000000000000..09fc3d678769
--- /dev/null
+++ b/svx/source/dialog/GenericCheckDialog.cxx
@@ -0,0 +1,70 @@
+/* -*- 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/.
+ *
+ */
+
+#include <svx/GenericCheckDialog.hxx>
+#include <vcl/svapp.hxx>
+
+namespace svx
+{
+GenericCheckEntry::GenericCheckEntry(weld::Container* pParent,
+                                     std::unique_ptr<CheckData>& pCheckData)
+    : m_xBuilder(Application::CreateBuilder(pParent, 
"svx/ui/genericcheckentry.ui"))
+    , m_xContainer(m_xBuilder->weld_container("checkEntryBox"))
+    , m_xLabel(m_xBuilder->weld_label("label"))
+    , m_xMarkButton(m_xBuilder->weld_button("markButton"))
+    , m_xPropertiesButton(m_xBuilder->weld_button("propertiesButton"))
+    , m_pCheckData(pCheckData)
+{
+    m_xLabel->set_label(m_pCheckData->getText());
+    m_xMarkButton->set_visible(m_pCheckData->canMarkObject());
+    m_xMarkButton->connect_clicked(LINK(this, GenericCheckEntry, 
MarkButtonClicked));
+    m_xPropertiesButton->set_visible(m_pCheckData->hasProperties());
+    m_xPropertiesButton->connect_clicked(LINK(this, GenericCheckEntry, 
PropertiesButtonClicked));
+
+    m_xContainer->show();
+}
+
+IMPL_LINK_NOARG(GenericCheckEntry, MarkButtonClicked, weld::Button&, void)
+{
+    m_pCheckData->markObject();
+}
+
+IMPL_LINK_NOARG(GenericCheckEntry, PropertiesButtonClicked, weld::Button&, 
void)
+{
+    m_pCheckData->runProperties();
+}
+
+GenericCheckDialog::GenericCheckDialog(weld::Window* pParent,
+                                       CheckDataCollection& 
rCheckDataCollection)
+    : GenericDialogController(pParent, "svx/ui/genericcheckdialog.ui", 
"GenericCheckDialog")
+    , m_rCheckDataCollection(rCheckDataCollection)
+    , m_xCheckBox(m_xBuilder->weld_box("checkBox"))
+{
+    set_title(m_rCheckDataCollection.getTitle());
+}
+
+GenericCheckDialog::~GenericCheckDialog() {}
+
+short GenericCheckDialog::run()
+{
+    sal_Int32 i = 0;
+
+    for (std::unique_ptr<CheckData>& pCheckData : 
m_rCheckDataCollection.getCollection())
+    {
+        auto xEntry = std::make_unique<GenericCheckEntry>(m_xCheckBox.get(), 
pCheckData);
+        m_xCheckBox->reorder_child(xEntry->get_widget(), i++);
+        m_aCheckEntries.push_back(std::move(xEntry));
+    }
+    return GenericDialogController::run();
+}
+
+} // end svx namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/uiconfig/ui/genericcheckdialog.ui 
b/svx/uiconfig/ui/genericcheckdialog.ui
new file mode 100644
index 000000000000..232221c0a878
--- /dev/null
+++ b/svx/uiconfig/ui/genericcheckdialog.ui
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.38.2 -->
+<interface domain="svx">
+  <requires lib="gtk+" version="3.20"/>
+  <object class="GtkDialog" id="GenericCheckDialog">
+    <property name="width-request">850</property>
+    <property name="height-request">480</property>
+    <property name="can-focus">False</property>
+    <property name="hexpand">True</property>
+    <property name="vexpand">True</property>
+    <property name="border-width">6</property>
+    <property name="modal">True</property>
+    <property name="type-hint">dialog</property>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="dialogBox1">
+        <property name="can-focus">False</property>
+        <property name="hexpand">True</property>
+        <property name="vexpand">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">12</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox" id="dialogButtons">
+            <property name="can-focus">False</property>
+            <property name="layout-style">end</property>
+            <child>
+              <object class="GtkButton" id="ok">
+                <property name="label" translatable="yes" 
context="stock">_OK</property>
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="can-default">True</property>
+                <property name="has-default">True</property>
+                <property name="receives-default">True</property>
+                <property name="use-underline">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="cancel">
+                <property name="label" translatable="yes" 
context="stock">_Cancel</property>
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="receives-default">True</property>
+                <property name="use-underline">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="help">
+                <property name="label" translatable="yes" 
context="stock">_Help</property>
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="receives-default">True</property>
+                <property name="use-underline">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">2</property>
+                <property name="secondary">True</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="pack-type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="can-focus">False</property>
+            <property name="hexpand">True</property>
+            <property name="vexpand">True</property>
+            <property name="orientation">vertical</property>
+            <child>
+              <object class="GtkScrolledWindow">
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="hexpand">True</property>
+                <property name="vexpand">True</property>
+                <property name="shadow-type">in</property>
+                <child>
+                  <object class="GtkViewport">
+                    <property name="visible">True</property>
+                    <property name="can-focus">False</property>
+                    <property name="hexpand">True</property>
+                    <property name="vexpand">True</property>
+                    <child>
+                      <object class="GtkBox" id="checkBox">
+                        <property name="visible">True</property>
+                        <property name="can-focus">False</property>
+                        <property name="orientation">vertical</property>
+                        <child>
+                          <placeholder/>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="-5">ok</action-widget>
+      <action-widget response="-6">cancel</action-widget>
+      <action-widget response="-11">help</action-widget>
+    </action-widgets>
+  </object>
+</interface>
diff --git a/svx/uiconfig/ui/genericcheckentry.ui 
b/svx/uiconfig/ui/genericcheckentry.ui
new file mode 100644
index 000000000000..1b02ca13eeac
--- /dev/null
+++ b/svx/uiconfig/ui/genericcheckentry.ui
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.38.2 -->
+<interface domain="svx">
+  <requires lib="gtk+" version="3.20"/>
+  <object class="GtkBox" id="checkEntryBox">
+    <property name="visible">True</property>
+    <property name="can-focus">False</property>
+    <property name="border-width">3</property>
+    <property name="spacing">6</property>
+    <child>
+      <object class="GtkLabel" id="label">
+        <property name="visible">True</property>
+        <property name="can-focus">False</property>
+        <property name="hexpand">True</property>
+        <property name="selectable">True</property>
+        <property name="xalign">0</property>
+        <child internal-child="accessible">
+          <object class="AtkObject" id="label-atkobject">
+            <property name="AtkObject::accessible-role">static</property>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">True</property>
+        <property name="position">0</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkButton" id="markButton">
+        <property name="label" translatable="yes" 
context="genericcheckentry|markButton">Mark</property>
+        <property name="visible">True</property>
+        <property name="can-focus">True</property>
+        <property name="receives-default">True</property>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">False</property>
+        <property name="position">1</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkButton" id="propertiesButton">
+        <property name="label" translatable="yes" 
context="genericcheckentry|propertiesButton">Properties</property>
+        <property name="visible">True</property>
+        <property name="can-focus">True</property>
+        <property name="receives-default">True</property>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">False</property>
+        <property name="position">2</property>
+      </packing>
+    </child>
+  </object>
+</interface>
diff --git a/sw/Library_sw.mk b/sw/Library_sw.mk
index 6989bce49cea..58639859bb0e 100644
--- a/sw/Library_sw.mk
+++ b/sw/Library_sw.mk
@@ -312,6 +312,7 @@ $(eval $(call gb_Library_add_exception_objects,sw,\
     sw/source/core/frmedt/tblsel \
     sw/source/core/graphic/grfatr \
     sw/source/core/graphic/ndgrf \
+    sw/source/core/graphic/GraphicSizeCheck \
     sw/source/core/layout/anchoreddrawobject \
     sw/source/core/layout/anchoredobject \
     sw/source/core/layout/atrfrm \
diff --git a/sw/sdi/_basesh.sdi b/sw/sdi/_basesh.sdi
index 44d6c5338e18..dd195496d1a8 100644
--- a/sw/sdi/_basesh.sdi
+++ b/sw/sdi/_basesh.sdi
@@ -605,4 +605,11 @@ interface BaseTextSelection
         ExecMethod = ExecDlg;
         DisableFlags="SfxDisableFlags::SwOnProtectedCursor";
     ]
+
+    SID_GRAPHIC_SIZE_CHECK
+    [
+        ExecMethod = ExecDlg;
+        StateMethod = GetState;
+        DisableFlags="SfxDisableFlags::SwOnProtectedCursor";
+    ]
 }
diff --git a/sw/source/core/graphic/GraphicSizeCheck.cxx 
b/sw/source/core/graphic/GraphicSizeCheck.cxx
new file mode 100644
index 000000000000..50b1cb70a8f2
--- /dev/null
+++ b/sw/source/core/graphic/GraphicSizeCheck.cxx
@@ -0,0 +1,164 @@
+/* -*- 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/.
+ *
+ */
+
+#include <GraphicSizeCheck.hxx>
+#include <svx/strings.hrc>
+#include <svx/svdobj.hxx>
+#include <unotools/viewoptions.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/dispatch.hxx>
+
+#include <ModelTraverser.hxx>
+#include <ndgrf.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <fmtfsize.hxx>
+#include <wrtsh.hxx>
+#include <wview.hxx>
+#include <cmdid.h>
+
+using namespace css;
+
+namespace sw
+{
+GraphicSizeViolation::GraphicSizeViolation(sal_Int32 nDPI, const SwGrfNode* 
pGraphicNode)
+    : m_pGraphicNode(pGraphicNode)
+{
+    constexpr double fLowPercentage = 110;
+    constexpr double fHighPercentage = 50;
+
+    m_nLowDPILimit = sal_Int32(100.0 / fLowPercentage * nDPI);
+    m_nHighDPILimit = sal_Int32(100.0 / fHighPercentage * nDPI);
+}
+
+bool GraphicSizeViolation::check()
+{
+    auto pFrameFormat = m_pGraphicNode->GetFlyFormat();
+    Graphic aGraphic = m_pGraphicNode->GetGraphic();
+    Size aSizePixel = aGraphic.GetSizePixel();
+    Size aFrameSize(pFrameFormat->GetFrameSize().GetSize());
+
+    double nSizeXInch
+        = o3tl::convert(double(aFrameSize.Width()), o3tl::Length::twip, 
o3tl::Length::in);
+    double nSizeYInch
+        = o3tl::convert(double(aFrameSize.Height()), o3tl::Length::twip, 
o3tl::Length::in);
+
+    m_nDPIX = sal_Int32(aSizePixel.Width() / nSizeXInch);
+    m_nDPIY = sal_Int32(aSizePixel.Height() / nSizeYInch);
+
+    return isDPITooLow() || isDPITooHigh();
+}
+
+OUString GraphicSizeViolation::getGraphicName()
+{
+    return m_pGraphicNode->GetFlyFormat()->GetName();
+}
+
+namespace
+{
+class GraphicSizeCheckHandler : public ModelTraverseHandler
+{
+private:
+    sal_Int32 m_nDPI;
+    std::vector<std::unique_ptr<GraphicSizeViolation>>& 
m_rGraphicSizeViolationList;
+
+public:
+    GraphicSizeCheckHandler(
+        sal_Int32 nDPI,
+        std::vector<std::unique_ptr<GraphicSizeViolation>>& 
rGraphicSizeViolationList)
+        : m_nDPI(nDPI)
+        , m_rGraphicSizeViolationList(rGraphicSizeViolationList)
+    {
+    }
+
+    void handleNode(SwNode* pNode) override
+    {
+        if (!pNode->IsGrfNode())
+            return;
+
+        auto pEntry = std::make_unique<GraphicSizeViolation>(m_nDPI, 
pNode->GetGrfNode());
+        if (pEntry->check())
+        {
+            m_rGraphicSizeViolationList.push_back(std::move(pEntry));
+        }
+    }
+
+    void handleSdrObject(SdrObject* /*pObject*/) override {}
+};
+
+} // end anonymous namespace
+
+void GraphicSizeCheck::check()
+{
+    sal_Int32 nDPI = 
m_pDocument->getIDocumentSettingAccess().getImagePreferredDPI();
+    if (nDPI == 0)
+        return;
+
+    auto pHandler = std::make_shared<GraphicSizeCheckHandler>(nDPI, 
m_aGraphicSizeViolationList);
+    ModelTraverser aModelTraverser(m_pDocument);
+    aModelTraverser.addNodeHandler(pHandler);
+    aModelTraverser.traverse();
+}
+
+OUString GraphicSizeCheckGUIEntry::getText()
+{
+    OUString sText;
+
+    if (m_pViolation->isDPITooLow())
+    {
+        sText = SwResId(STR_WARNING_GRAPHIC_PIXEL_COUNT_LOW);
+    }
+    else if (m_pViolation->isDPITooHigh())
+    {
+        sText = SwResId(STR_WARNING_GRAPHIC_PIXEL_COUNT_HIGH);
+    }
+
+    sText = sText.replaceAll("%NAME%", m_pViolation->getGraphicName());
+    sText = sText.replaceAll("%DPIX%", 
OUString::number(m_pViolation->getDPIX()));
+    sText = sText.replaceAll("%DPIY%", 
OUString::number(m_pViolation->getDPIY()));
+
+    return sText;
+}
+
+void GraphicSizeCheckGUIEntry::markObject()
+{
+    SwWrtShell* pWrtShell = m_pDocument->GetDocShell()->GetWrtShell();
+    pWrtShell->GotoFly(m_pViolation->getGraphicName(), FLYCNTTYPE_ALL, true);
+}
+
+void GraphicSizeCheckGUIEntry::runProperties()
+{
+    markObject();
+    SwWrtShell* pWrtShell = m_pDocument->GetDocShell()->GetWrtShell();
+    
pWrtShell->GetView().GetViewFrame()->GetDispatcher()->Execute(FN_FORMAT_GRAFIC_DLG,
+                                                                  
SfxCallMode::SYNCHRON);
+}
+
+GraphicSizeCheckGUIResult::GraphicSizeCheckGUIResult(SwDoc* pDocument)
+{
+    GraphicSizeCheck aCheck(pDocument);
+    aCheck.check();
+
+    auto& rCollection = getCollection();
+    for (auto& rpViolation : aCheck.getViolationList())
+    {
+        auto rGUIEntry
+            = std::make_unique<GraphicSizeCheckGUIEntry>(pDocument, 
std::move(rpViolation));
+        rCollection.push_back(std::move(rGUIEntry));
+    }
+}
+
+OUString GraphicSizeCheckGUIResult::getTitle()
+{
+    return SwResId(STR_GRAPHIC_SIZE_CHECK_DIALOG_TITLE);
+}
+
+} // end sw namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/GraphicSizeCheck.hxx 
b/sw/source/core/inc/GraphicSizeCheck.hxx
new file mode 100644
index 000000000000..f708b0a5f4dc
--- /dev/null
+++ b/sw/source/core/inc/GraphicSizeCheck.hxx
@@ -0,0 +1,99 @@
+/* -*- 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/.
+ *
+ */
+
+#pragma once
+
+#include <doc.hxx>
+#include "ModelTraverser.hxx"
+#include <svx/GenericCheckDialog.hxx>
+
+namespace sw
+{
+class GraphicSizeViolation final
+{
+private:
+    const SwGrfNode* m_pGraphicNode;
+
+    sal_Int32 m_nLowDPILimit = 0;
+    sal_Int32 m_nHighDPILimit = 0;
+
+    sal_Int32 m_nDPIX = 0;
+    sal_Int32 m_nDPIY = 0;
+
+public:
+    GraphicSizeViolation(sal_Int32 nDPI, const SwGrfNode* pGraphicNode);
+    bool check();
+
+    OUString getGraphicName();
+
+    bool isDPITooLow() { return m_nDPIX < m_nLowDPILimit || m_nDPIY < 
m_nLowDPILimit; }
+
+    bool isDPITooHigh() { return m_nDPIX > m_nHighDPILimit || m_nDPIY > 
m_nHighDPILimit; }
+
+    sal_Int32 getDPIX() { return m_nDPIX; }
+
+    sal_Int32 getDPIY() { return m_nDPIY; }
+};
+
+class GraphicSizeCheck final
+{
+private:
+    SwDoc* m_pDocument;
+    std::vector<std::unique_ptr<GraphicSizeViolation>> 
m_aGraphicSizeViolationList;
+
+public:
+    GraphicSizeCheck(SwDoc* pDocument)
+        : m_pDocument(pDocument)
+    {
+    }
+
+    void check();
+
+    std::vector<std::unique_ptr<GraphicSizeViolation>>& getViolationList()
+    {
+        return m_aGraphicSizeViolationList;
+    }
+};
+
+class GraphicSizeCheckGUIEntry : public svx::CheckData
+{
+private:
+    SwDoc* m_pDocument;
+    std::unique_ptr<GraphicSizeViolation> m_pViolation;
+
+public:
+    GraphicSizeCheckGUIEntry(SwDoc* pDocument, 
std::unique_ptr<GraphicSizeViolation>&& pViolation)
+        : m_pDocument(pDocument)
+        , m_pViolation(std::move(pViolation))
+    {
+    }
+
+    OUString getText() override;
+
+    bool canMarkObject() override { return true; }
+
+    void markObject() override;
+
+    bool hasProperties() override { return true; }
+
+    void runProperties() override;
+};
+
+class GraphicSizeCheckGUIResult : public svx::CheckDataCollection
+{
+public:
+    GraphicSizeCheckGUIResult(SwDoc* pDocument);
+
+    OUString getTitle() override;
+};
+
+} // end sw namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/shells/basesh.cxx 
b/sw/source/uibase/shells/basesh.cxx
index 05d631bd7b3e..5b4911ff70ad 100644
--- a/sw/source/uibase/shells/basesh.cxx
+++ b/sw/source/uibase/shells/basesh.cxx
@@ -85,6 +85,7 @@
 #include <modcfg.hxx>
 #include <svx/fmshell.hxx>
 #include <SwRewriter.hxx>
+#include <GraphicSizeCheck.hxx>
 #include <svx/galleryitem.hxx>
 #include <sfx2/devtools/DevelopmentToolChildWindow.hxx>
 #include <com/sun/star/gallery/GalleryItemType.hpp>
@@ -1963,6 +1964,13 @@ void SwBaseShell::GetState( SfxItemSet &rSet )
                 else
                     rSet.Put( SfxVisibilityItem( nWhich, false ) );
                 break;
+            case SID_GRAPHIC_SIZE_CHECK:
+            {
+                sal_Int32 nDPI = 
rSh.GetDoc()->getIDocumentSettingAccess().getImagePreferredDPI();
+                if (nDPI <= 0)
+                    rSet.DisableItem(nWhich);
+            }
+            break;
         }
         nWhich = aIter.NextWhich();
     }
@@ -2785,6 +2793,15 @@ void SwBaseShell::ExecDlg(SfxRequest &rReq)
             aDialog.run();
         }
         break;
+
+        case SID_GRAPHIC_SIZE_CHECK:
+        {
+            sw::GraphicSizeCheckGUIResult aResult(rSh.GetDoc());
+            svx::GenericCheckDialog aDialog(pMDI, aResult);
+            aDialog.run();
+        }
+        break;
+
         default:OSL_FAIL("wrong Dispatcher (basesh.cxx)");
     }
     if(!bDone)
diff --git a/sw/uiconfig/sglobal/menubar/menubar.xml 
b/sw/uiconfig/sglobal/menubar/menubar.xml
index b8dafe715afe..852877152a3c 100644
--- a/sw/uiconfig/sglobal/menubar/menubar.xml
+++ b/sw/uiconfig/sglobal/menubar/menubar.xml
@@ -479,6 +479,7 @@
           <menu:menuitem menu:id=".uno:ColorSettings"/>
           <menu:menuseparator/>
           <menu:menuitem menu:id=".uno:GraphicDialog"/>
+          <menu:menuitem menu:id=".uno:GraphicSizeCheck"/>
         </menu:menupopup>
       </menu:menu>
       <menu:menu menu:id=".uno:FormatObjectMenu">
diff --git a/sw/uiconfig/swriter/menubar/menubar.xml 
b/sw/uiconfig/swriter/menubar/menubar.xml
index e27ca8aa3cad..2fb1d51d8cc6 100644
--- a/sw/uiconfig/swriter/menubar/menubar.xml
+++ b/sw/uiconfig/swriter/menubar/menubar.xml
@@ -490,6 +490,7 @@
           <menu:menuitem menu:id=".uno:ColorSettings"/>
           <menu:menuseparator/>
           <menu:menuitem menu:id=".uno:GraphicDialog"/>
+          <menu:menuitem menu:id=".uno:GraphicSizeCheck"/>
         </menu:menupopup>
       </menu:menu>
       <menu:menu menu:id=".uno:FormatObjectMenu">

Reply via email to