sw/inc/IDocumentTimerAccess.hxx             |    5 +
 sw/source/core/inc/DocumentTimerManager.hxx |    4 +
 sw/source/uibase/uno/unotxdoc.cxx           |   85 ++++++++++++++++++++++------
 vcl/osx/printaccessoryview.mm               |   58 +++++++++++++++----
 vcl/osx/salprn.cxx                          |   29 +++++++--
 vcl/quartz/AquaGraphicsBackend.cxx          |   11 ++-
 vcl/quartz/salgdi.cxx                       |    6 -
 vcl/source/gdi/print3.cxx                   |    7 ++
 8 files changed, 167 insertions(+), 38 deletions(-)

New commits:
commit 5bd6ef607d68d371b93ad438fb24e2d8fc352b10
Author:     Patrick Luby <guibmac...@gmail.com>
AuthorDate: Sat Dec 21 11:51:22 2024 -0500
Commit:     Patrick Luby <guibomac...@gmail.com>
CommitDate: Fri Dec 27 18:03:59 2024 +0100

    tdf#163126 Fix incorrect paper size and rotation when printing a brochure
    
    Printing a brochure in Writer that has A5 pages would set the page size
    to 10 times the size of an A4 page instead of the expected A4 page size
    and would sometimes print the content at 90 degrees relative to the
    paper.
    
    This bug ended up being caused by several paper matching bugs:
    
    - The page size and rotation would fail to be set if the printer was
      nil. A printer is not needed to use the native macOS print dialog so
      if the printer is nil, use the native default printer instead.
    
    - Expand page size match range to 1/10th of an inch. The A4 page size in
      Apple's "no printer installed" printer can differ from LibreOffice's
      A4 page size by more than a millimeter so increase the match range to
      1/10th of an inch since an A4 match would fail when using the previous
      0.5 millimeter match range.
    
    - When brochure printing in Writer, set the printer's page size to match
      the preferred page size. Unlike most other platforms, the native print
      dialog is always displayed on macOS and it displays a preview of the
      print output which the user can make layout changes to. The user can
      also change the printer or even send the print output to PDF instead
      of a printer from within the native print dialog so set the printer's
      paper to the preferred size so that the initial preview more closely
      matches the page size and rotation that LibreOffice expects.
    
    Lastly, the fix for this bug includes fixes for the following bugs in
    the "print selection only" checkbox:
    
    - The "print selection only" checkbox would remain unchecked after
      checking it had restarted the print job so load the checkbox state
      from where it was updated in
      ControllerProperties::changePropertyWithBoolValue(). This fix was
      copied from the following NeoOffice source code file which is licensed
      under the Mozilla Public License, v. 2.0:
    
      
https://github.com/neooffice/NeoOffice/blob/NeoOffice-2022_7/vcl/osx/printaccessoryview.mm
    
    - If the "print selection only" checkbox is changed when the content is
      only a single page, calling
      -[AquaPrintPanelAccessoryController updatePrintOperation:] won't
      restart the print job so force the print job to restart.
    
    - If the the "print selection only" checkbox is changed and then the
      restarted print dialog is cancelled, idling will have already been
      unblocked so check if it is blocked before unblocking it.
    
    Change-Id: I99e9b3f73e3d3d860762c168573d2bb520934df3
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/179096
    Tested-by: Jenkins
    Reviewed-by: Patrick Luby <guibomac...@gmail.com>

diff --git a/sw/inc/IDocumentTimerAccess.hxx b/sw/inc/IDocumentTimerAccess.hxx
index dbc4963b1d16..5cfb1a3ecc41 100644
--- a/sw/inc/IDocumentTimerAccess.hxx
+++ b/sw/inc/IDocumentTimerAccess.hxx
@@ -64,6 +64,11 @@ public:
      */
     virtual bool IsDocIdle() const = 0;
 
+    /**
+     * Is idling blocked?
+     */
+    virtual bool IsIdlingBlocked() const = 0;
+
 protected:
     virtual ~IDocumentTimerAccess(){};
 };
diff --git a/sw/source/core/inc/DocumentTimerManager.hxx 
b/sw/source/core/inc/DocumentTimerManager.hxx
index 895aa4b86b15..d22852b910bb 100644
--- a/sw/source/core/inc/DocumentTimerManager.hxx
+++ b/sw/source/core/inc/DocumentTimerManager.hxx
@@ -55,6 +55,8 @@ public:
 
     bool IsDocIdle() const override;
 
+    bool IsIdlingBlocked() const override;
+
 private:
     DocumentTimerManager(DocumentTimerManager const&) = delete;
     DocumentTimerManager& operator=(DocumentTimerManager const&) = delete;
@@ -80,6 +82,8 @@ inline bool DocumentTimerManager::IsDocIdle() const
 {
     return ((0 == m_nIdleBlockCount) && (GetNextIdleJob() != IdleJob::Busy));
 }
+
+inline bool DocumentTimerManager::IsIdlingBlocked() const { return (0 != 
m_nIdleBlockCount); }
 }
 
 #endif
diff --git a/sw/source/uibase/uno/unotxdoc.cxx 
b/sw/source/uibase/uno/unotxdoc.cxx
index 8b0de66032de..b17a3098719e 100644
--- a/sw/source/uibase/uno/unotxdoc.cxx
+++ b/sw/source/uibase/uno/unotxdoc.cxx
@@ -95,6 +95,7 @@
 #include <com/sun/star/frame/XController.hpp>
 #include <com/sun/star/frame/XFrame.hpp>
 #include <com/sun/star/script/XInvocation.hpp>
+#include <com/sun/star/view/PaperOrientation.hpp>
 #include <com/sun/star/view/XSelectionSupplier.hpp>
 #include <sfx2/linkmgr.hxx>
 #include <svx/unofill.hxx>
@@ -2864,10 +2865,22 @@ uno::Sequence< beans::PropertyValue > SAL_CALL 
SwXTextDocument::getRenderer(
                 nPrinterPaperTray = aIt->second;
         }
 
+        // TODO/mba: we really need a generic way to get the SwViewShell!
+        SwViewShell* pVwSh = nullptr;
+        SwView* pSwView = dynamic_cast<SwView*>( pView );
+        if ( pSwView )
+            pVwSh = pSwView->GetWrtShellPtr();
+        else
+            pVwSh = static_cast<SwPagePreview*>(pView)->GetViewShell();
+
         awt::Size aPageSize;
         awt::Point aPagePos;
         awt::Size aPreferredPageSize;
         Size aTmpSize;
+#ifdef MACOSX
+        bool bChangeOrientation = false;
+        css::view::PaperOrientation nNewOrientation = 
css::view::PaperOrientation::PaperOrientation_PORTRAIT;
+#endif
         if (bIsSwSrcView || bPrintProspect)
         {
             // for printing of HTML source code and prospect printing we 
should use
@@ -2889,19 +2902,50 @@ uno::Sequence< beans::PropertyValue > SAL_CALL 
SwXTextDocument::getRenderer(
                 aTmpSize = OutputDevice::LogicToLogic( aTmpSize,
                             pPrinter->GetMapMode(), MapMode( 
MapUnit::Map100thMM ));
                 aPageSize = awt::Size( aTmpSize.Width(), aTmpSize.Height() );
-                #if 0
-                // #i115048# it seems users didn't like getting double the 
formatted page size
-                // revert to "old" behavior scaling to the current paper size 
of the printer
+#ifdef MACOSX
+                // Related: tdf#163126 when brochure printing on macOS, set the
+                // printer's page size to match the preferred page size. Unlike
+                // most other platforms, the native print dialog is always
+                // displayed on macOS and it displays a preview of the print
+                // output which the user can make layout changes to. The user
+                // can also change the printer or even send the print output
+                // to PDF instead of a printer from within the native print
+                // dialog so set the printer's paper to the preferred size so
+                // that the initial preview more closely matches the page size
+                // and rotation that LibreOffice expects.
                 if (bPrintProspect)
                 {
                     // we just state what output size we would need
                     // which may cause vcl to set that page size on the printer
                     // (if available and not overridden by the user)
-                    aTmpSize = pVwSh->GetPageSize( nPage, bIsSkipEmptyPages );
-                    aPreferredPageSize = awt::Size ( convertTwipToMm100( 2 * 
aTmpSize.Width() ),
-                                                     convertTwipToMm100( 
aTmpSize.Height() ));
+                    if( pVwSh )
+                        aTmpSize = pVwSh->GetPageSize( nPage, 
bIsSkipEmptyPages );
+
+                    // Related: tdf#163126 set both page size properties as
+                    // they do two different things. The preferred page size
+                    // tells the printer to set its paper size. But the page
+                    // size is the size that LibreOffice will actually draw.
+                    // Fortunately on macOS, there is a good chance that the
+                    // print output will closely match the preferred page size
+                    // since there is no problem printing to a page size not
+                    // supported by an underlying physical printer. In such
+                    // cases, the native print dialog will display at the
+                    // requested paper size as will saving to PDF or opening
+                    // in the Preview application.
+                    aPreferredPageSize = aPageSize = awt::Size( 
convertTwipToMm100( 2 * aTmpSize.Width() ),
+                                                                
convertTwipToMm100( aTmpSize.Height() ));
+
+                    // Related: tdf#163126 try to match any paper rotations in
+                    // the native printing code in vcl.
+                    bChangeOrientation = true;
+                    if( aPageSize.Width < aPageSize.Height )
+                        nNewOrientation = 
css::view::PaperOrientation::PaperOrientation_PORTRAIT;
+                    else
+                        nNewOrientation = 
css::view::PaperOrientation::PaperOrientation_LANDSCAPE;
                 }
-                #else
+#else
+                // #i115048# it seems users didn't like getting double the 
formatted page size
+                // revert to "old" behavior scaling to the current paper size 
of the printer
                 if( bPrintProspect )
                 {
                     // just switch to an appropriate portrait/landscape format
@@ -2914,19 +2958,11 @@ uno::Sequence< beans::PropertyValue > SAL_CALL 
SwXTextDocument::getRenderer(
                         aPreferredPageSize.Height = aPageSize.Width;
                     }
                 }
-                #endif
+#endif
             }
         }
         else
         {
-            // TODO/mba: we really need a generic way to get the SwViewShell!
-            SwViewShell* pVwSh = nullptr;
-            SwView* pSwView = dynamic_cast<SwView*>( pView );
-            if ( pSwView )
-                pVwSh = pSwView->GetWrtShellPtr();
-            else
-                pVwSh = static_cast<SwPagePreview*>(pView)->GetViewShell();
-
             if (pVwSh)
             {
                 aTmpSize = pVwSh->GetPageSize( nPage, bIsSkipEmptyPages );
@@ -2957,6 +2993,16 @@ uno::Sequence< beans::PropertyValue > SAL_CALL 
SwXTextDocument::getRenderer(
             pRenderer[ nLen - 1 ].Name  = "PrinterPaperTray";
             pRenderer[ nLen - 1 ].Value <<= nPrinterPaperTray;
         }
+#ifdef MACOSX
+        if (bChangeOrientation)
+        {
+            ++nLen;
+            aRenderer.realloc( nLen );
+            auto pRenderer = aRenderer.getArray();
+            pRenderer[ nLen - 1 ].Name  = "PaperOrientation";
+            pRenderer[ nLen - 1 ].Value <<= nNewOrientation;
+        }
+#endif
     }
 
     // #i117783#
@@ -3209,7 +3255,12 @@ void SAL_CALL SwXTextDocument::render(
     if( bLastPage )
     {
         // tdf#144989 enable DoIdleJobs() again after last page
-        pDoc->getIDocumentTimerAccess().UnblockIdling();
+        // Related: tdf#163126 on macOS, if the the "print selection only"
+        // checkbox is changed and then the restarted print dialog is
+        // cancelled, idling will have already been unblocked so check
+        // if it is blocked before unblocking it.
+        if( pDoc->getIDocumentTimerAccess().IsIdlingBlocked() )
+            pDoc->getIDocumentTimerAccess().UnblockIdling();
         m_pRenderData.reset();
         m_pPrintUIOptions.reset();
     }
diff --git a/vcl/osx/printaccessoryview.mm b/vcl/osx/printaccessoryview.mm
index 7b61adf9887a..4fdd1c873d01 100644
--- a/vcl/osx/printaccessoryview.mm
+++ b/vcl/osx/printaccessoryview.mm
@@ -85,6 +85,7 @@ class ControllerProperties;
 -(NSArray*)localizedSummaryItems;
 
 -(sal_Int32)updatePrintOperation:(sal_Int32)pLastPageCount;
+-(sal_Int32)updatePrintOperation:(sal_Int32)pLastPageCount 
forceRestart:(BOOL)bForceRestart;
 
 @end
 
@@ -120,16 +121,24 @@ class ControllerProperties;
 }
 
 -(sal_Int32)updatePrintOperation:(sal_Int32)pLastPageCount
+{
+    return [self updatePrintOperation: pLastPageCount forceRestart: NO];
+}
+
+-(sal_Int32)updatePrintOperation:(sal_Int32)pLastPageCount 
forceRestart:(BOOL)bForceRestart
 {
     // page range may be changed by option choice
     sal_Int32 nPages = mpPrinterController->getFilteredPageCount();
 
     mpViewState->bNeedRestart = false;
-    if( nPages != pLastPageCount )
+    if( bForceRestart || nPages != pLastPageCount )
     {
         #if OSL_DEBUG_LEVEL > 1
-        SAL_INFO( "vcl.osx.print", "number of pages changed" <<
-                  " from " << pLastPageCount << " to " << nPages );
+        if( nPages != pLastPageCount )
+        {
+            SAL_INFO( "vcl.osx.print", "number of pages changed" <<
+                      " from " << pLastPageCount << " to " << nPages );
+        }
         #endif
         mpViewState->bNeedRestart = true;
     }
@@ -148,7 +157,12 @@ class ControllerProperties;
         // hack: send a cancel message to the modal window displaying views
         NSWindow* pNSWindow = [NSApp modalWindow];
         if( pNSWindow )
+        {
             [pNSWindow cancelOperation: nil];
+            [pNSWindow close];
+            [NSApp abortModal];
+        }
+        [mpPrintOperation setShowsProgressPanel: NO];
         [[mpPrintOperation printInfo] setJobDisposition: NSPrintCancelJob];
     }
 
@@ -264,12 +278,18 @@ public:
             if( pVal )
             {
                 // ugly
-                if( name_it->second == "PrintContent" )
+                bool bPrintContent = name_it->second == "PrintContent";
+                if( bPrintContent )
                    pVal->Value <<= i_bValue ? sal_Int32(2) : sal_Int32(0);
                 else
                    pVal->Value <<= i_bValue;
 
-                mnLastPageCount = [mpAccessoryController updatePrintOperation: 
mnLastPageCount];
+                // Related: tdf#163126 if the "print selection only" checkbox
+                // is changed when the content is only a single page, calling
+                // -[AquaPrintPanelAccessoryController updatePrintOperation:]
+                // won't restart the print job so force the print job to
+                // restart.
+                mnLastPageCount = [mpAccessoryController updatePrintOperation: 
mnLastPageCount forceRestart: bPrintContent];
             }
         }
     }
@@ -926,12 +946,12 @@ static void addEdit( NSView* pCurParent, CGFloat rCurX, 
CGFloat& rCurY, CGFloat
         [pFormatter setMaximumFractionDigits: 0];
         if( nMinValue != nMaxValue )
         {
-            [pFormatter setMinimum: [[NSNumber numberWithInt: nMinValue] 
autorelease]];
+            [pFormatter setMinimum: [NSNumber numberWithInt: nMinValue]];
             [pStep setMinValue: nMinValue];
-            [pFormatter setMaximum: [[NSNumber numberWithInt: nMaxValue] 
autorelease]];
+            [pFormatter setMaximum: [NSNumber numberWithInt: nMaxValue]];
             [pStep setMaxValue: nMaxValue];
         }
-        [pFieldView setFormatter: pFormatter];
+        [pFieldView setFormatter: [pFormatter autorelease]];
 
         sal_Int64 nSelectVal = 0;
         if( pValue && pValue->Value.hasValue() )
@@ -1046,6 +1066,24 @@ static void addEdit( NSView* pCurParent, CGFloat rCurX, 
CGFloat& rCurY, CGFloat
             aPropertyName == "PrintContent" &&
             aChoices.getLength() > 2 )
         {
+            // Related: tdf#163126 the "print selection only" checkbox would
+            // remain unchecked after checking it had restarted the print
+            // job so load the checkbox state from where it was updated in
+            // ControllerProperties::changePropertyWithBoolValue().
+            const PropertyValue* pVal = pController->getValue( 
"PrintSelectionOnly" );
+            if( pVal )
+            {
+                bool bPrintSelectionOnly;
+                pVal->Value >>= bPrintSelectionOnly;
+                aSelectionChecked = bPrintSelectionOnly ? 2 : 0;
+            }
+            else
+            {
+                pVal = pController->getValue( "PrintContent" );
+                if( pVal )
+                    pVal->Value >>= aSelectionChecked;
+            }
+
             bAddSelectionCheckBox = true;
             bSelectionBoxEnabled = aChoicesDisabled.getLength() < 2 || ! 
aChoicesDisabled[2];
             bSelectionBoxChecked = (aSelectionChecked==2);
@@ -1144,9 +1182,9 @@ static void addEdit( NSView* pCurParent, CGFloat rCurX, 
CGFloat& rCurY, CGFloat
                 NSString* pLabel = CreateNSString( aGroupTitle );
                 NSTabViewItem* pItem = [[NSTabViewItem alloc] 
initWithIdentifier: pLabel ];
                 [pItem setLabel: pLabel];
-                [pTabView addTabViewItem: pItem];
+                [pTabView addTabViewItem: [pItem autorelease]];
                 pCurParent = [[NSView alloc] initWithFrame: aTabViewFrame];
-                [pItem setView: pCurParent];
+                [pItem setView: [pCurParent autorelease]];
                 [pLabel release];
 
                 nCurX = 20; // reset indent
diff --git a/vcl/osx/salprn.cxx b/vcl/osx/salprn.cxx
index 3fe37a9ab55e..4232f653e974 100644
--- a/vcl/osx/salprn.cxx
+++ b/vcl/osx/salprn.cxx
@@ -58,11 +58,22 @@ AquaSalInfoPrinter::AquaSalInfoPrinter( const 
SalPrinterQueueInfo& i_rQueue ) :
     mnCurPageRangeStart( 0 ),
     mnCurPageRangeCount( 0 )
 {
+    NSPrintInfo* pShared = [NSPrintInfo sharedPrintInfo];
+
     NSString* pStr = CreateNSString( i_rQueue.maPrinterName );
     mpPrinter = [NSPrinter printerWithName: pStr];
     [pStr release];
 
-    NSPrintInfo* pShared = [NSPrintInfo sharedPrintInfo];
+    // Related: tdf#163126 a printer is not needed to use the native
+    // macOS print dialog so if the printer is nil, use the native
+    // default printer instead.
+    if( !mpPrinter )
+        mpPrinter = [NSPrintInfo defaultPrinter];
+    if( !mpPrinter && pShared )
+        mpPrinter = [pShared printer];
+    if( mpPrinter )
+        [mpPrinter retain];
+
     if( pShared )
     {
         mpPrintInfo = [pShared copy];
@@ -89,6 +100,8 @@ AquaSalInfoPrinter::AquaSalInfoPrinter( const 
SalPrinterQueueInfo& i_rQueue ) :
 AquaSalInfoPrinter::~AquaSalInfoPrinter()
 {
     delete mpGraphics;
+    if( mpPrinter )
+        [mpPrinter release];
     if( mpPrintInfo )
         [mpPrintInfo release];
     if( mrContext )
@@ -101,8 +114,8 @@ void AquaSalInfoPrinter::SetupPrinterGraphics( CGContextRef 
i_rContext ) const
     {
         if( mpPrintInfo )
         {
-            // FIXME: get printer resolution
-            sal_Int32 nDPIX = 720, nDPIY = 720;
+            sal_Int32 nDPIX = 72, nDPIY = 72;
+            mpGraphics->GetResolution( nDPIX, nDPIY );
             NSSize aPaperSize = [mpPrintInfo paperSize];
 
             NSRect aImageRect = [mpPrintInfo imageablePageBounds];
@@ -666,8 +679,14 @@ const PaperInfo* AquaSalInfoPrinter::matchPaper( 
tools::Long i_nWidth, tools::Lo
     {
         for( size_t i = 0; i < m_aPaperFormats.size(); i++ )
         {
-            if( std::abs( m_aPaperFormats[i].getWidth() - i_nWidth ) < 50 &&
-                std::abs( m_aPaperFormats[i].getHeight() - i_nHeight ) < 50 )
+            // Related: tdf#163126 expand match range to 1/10th of an inch
+            // The A4 page size in Apple's "no printer installed" printer
+            // can differ from LibreOffice's A4 page size by more than a
+            // millimeter so increase the match range to 1/10th of an inch
+            // since an A4 match would fail when using the previous 0.5
+            // millimeter match range.
+            if( std::abs( m_aPaperFormats[i].getWidth() - i_nWidth ) < 254 &&
+                std::abs( m_aPaperFormats[i].getHeight() - i_nHeight ) < 254 )
             {
                 pMatch = &m_aPaperFormats[i];
                 return pMatch;
diff --git a/vcl/quartz/AquaGraphicsBackend.cxx 
b/vcl/quartz/AquaGraphicsBackend.cxx
index 1a3b45949801..f6d1134072c6 100644
--- a/vcl/quartz/AquaGraphicsBackend.cxx
+++ b/vcl/quartz/AquaGraphicsBackend.cxx
@@ -1019,8 +1019,15 @@ void AquaSalGraphics::GetResolution(sal_Int32& rDPIX, 
sal_Int32& rDPIY)
                                                                : nil);
     }
 
-    rDPIX = mnRealDPIX;
-    rDPIY = mnRealDPIY;
+    if (maShared.mbPrinter)
+    {
+        rDPIX = rDPIY = 720;
+    }
+    else
+    {
+        rDPIX = mnRealDPIX;
+        rDPIY = mnRealDPIY;
+    }
 #else
     // This *must* be 96 or else the iOS app will behave very badly (tiles are 
scaled wrongly and
     // don't match each others at their boundaries, and other issues). But 
*why* it must be 96 I
diff --git a/vcl/quartz/salgdi.cxx b/vcl/quartz/salgdi.cxx
index 6673a6fd6824..ab09718d86a7 100644
--- a/vcl/quartz/salgdi.cxx
+++ b/vcl/quartz/salgdi.cxx
@@ -144,13 +144,11 @@ AquaSalGraphics::AquaSalGraphics(bool bPrinter)
     // always disable Skia for print graphics contexts.
     if(!bPrinter && SkiaHelper::isVCLSkiaEnabled())
         mpBackend.reset(new AquaSkiaSalGraphicsImpl(*this, maShared));
+    else
 #else
     (void)bPrinter;
-    if(false)
-        ;
 #endif
-    else
-        mpBackend.reset(new AquaGraphicsBackend(maShared));
+    mpBackend.reset(new AquaGraphicsBackend(maShared));
 
     for (int i = 0; i < MAX_FALLBACK; ++i)
         mpFont[i] = nullptr;
diff --git a/vcl/source/gdi/print3.cxx b/vcl/source/gdi/print3.cxx
index 934a80fe991a..b813f1899e42 100644
--- a/vcl/source/gdi/print3.cxx
+++ b/vcl/source/gdi/print3.cxx
@@ -47,6 +47,7 @@
 #include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
 #include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
 #include <com/sun/star/view/DuplexMode.hpp>
+#include <com/sun/star/view/PaperOrientation.hpp>
 
 #include <unordered_map>
 #include <unordered_set>
@@ -959,6 +960,12 @@ PrinterController::PageSize 
vcl::ImplPrinterControllerData::modifyJobSetup( cons
             if( nBin >= 0 && o3tl::make_unsigned(nBin) < 
mxPrinter->GetPaperBinCount() )
                 nPaperBin = nBin;
         }
+        else if ( rProp.Name == "PaperOrientation" )
+        {
+            css::view::PaperOrientation nOrientation = 
css::view::PaperOrientation::PaperOrientation_PORTRAIT;
+            rProp.Value >>= nOrientation;
+            mxPrinter->SetOrientation( nOrientation == 
css::view::PaperOrientation::PaperOrientation_LANDSCAPE ? 
Orientation::Landscape : Orientation::Portrait );
+        }
     }
 
     Size aCurSize( mxPrinter->GetPaperSize() );

Reply via email to