tdf#97662 - Try to preserve original compressed JPEGs harder.

Avoiding de-compressing and re-compressing them saves lots of time too.

Change-Id: Ie8eb68554627581b2f0584a55bbbdb43c9482bed
Reviewed-on: https://gerrit.libreoffice.org/22219
Reviewed-by: Michael Meeks <michael.meeks@collabora.com>
Tested-by: Michael Meeks <michael.meeks@collabora.com>
diff --git a/filter/source/pdf/pdfexport.cxx b/filter/source/pdf/pdfexport.cxx
index 1a749eb..d0f5796 100644
--- a/filter/source/pdf/pdfexport.cxx
+++ b/filter/source/pdf/pdfexport.cxx
@@ -818,6 +818,8 @@ bool PDFExport::Export( const OUString& rFile, const Sequence< PropertyValue >& 
                pPDFExtOutDevData->SetIsExportBookmarks( mbExportBookmarks );
                pPDFExtOutDevData->SetIsExportHiddenSlides( mbExportHiddenSlides );
                pPDFExtOutDevData->SetIsLosslessCompression( mbUseLosslessCompression );
                pPDFExtOutDevData->SetCompressionQuality( mnQuality );
                pPDFExtOutDevData->SetMaxImageResolution( mnMaxImageResolution );
                pPDFExtOutDevData->SetIsReduceImageResolution( mbReduceImageResolution );
                pPDFExtOutDevData->SetIsExportNamedDestinations( mbExportBmkToDest );

diff --git a/include/vcl/pdfextoutdevdata.hxx b/include/vcl/pdfextoutdevdata.hxx
index 265a5b3..d681ab0 100644
--- a/include/vcl/pdfextoutdevdata.hxx
+++ b/include/vcl/pdfextoutdevdata.hxx
@@ -86,6 +86,8 @@ class VCL_DLLPUBLIC PDFExtOutDevData : public ExtOutDevData
    bool                        mbExportNDests; //i56629
    sal_Int32                   mnFormsFormat;
    sal_Int32                   mnPage;
    sal_Int32                   mnCompressionQuality;
    sal_Int32                   mnMaxImageResolution;
    css::lang::Locale           maDocLocale;

    PageSyncData*               mpPageSyncData;
@@ -103,8 +105,6 @@ public:

    void PlayGlobalActions( PDFWriter& rWriter );



    bool    GetIsExportNotes() const { return mbExportNotes;}
    void        SetIsExportNotes( const bool bExportNotes );

@@ -135,10 +135,16 @@ public:
    sal_Int32   GetCurrentPageNumber() const { return mnPage;}
    void        SetCurrentPageNumber( const sal_Int32 nPage );

    bool    GetIsLosslessCompression() const { return mbUseLosslessCompression;}
    bool        GetIsLosslessCompression() const { return mbUseLosslessCompression;}
    void        SetIsLosslessCompression( const bool bLosslessCompression );

    bool    GetIsReduceImageResolution() const { return mbReduceImageResolution;}
    sal_Int32   GetCompressionQuality() const { return mnCompressionQuality; }
    void        SetCompressionQuality( const sal_Int32 nQuality );

    sal_Int32   GetMaxImageResolution() const { return mnMaxImageResolution; }
    void        SetMaxImageResolution( const sal_Int32 nQuality );

    bool        GetIsReduceImageResolution() const { return mbReduceImageResolution;}
    void        SetIsReduceImageResolution( const bool bReduceImageResolution );

    const css::lang::Locale& GetDocumentLocale() const { return maDocLocale;}
@@ -176,9 +182,15 @@ public:
        rOutputRect, e.g. for cropped graphics.
     */
    void        EndGroup( const Graphic&    rGraphic,
                          sal_uInt8             nTransparency,
                          sal_uInt8         nTransparency,
                          const Rectangle&  rOutputRect,
                          const Rectangle&  rVisibleOutputRect );

    /// Detect if stream is compressed enough to avoid de-compress / scale & re-compress
    bool        HasAdequateCompression( const Graphic &rGraphic,
                                        const Rectangle &rOutputRect,
                                        const Rectangle &rVisibleOutputRect ) const;

//--->i56629
    /** Create a new named destination to be used in a link to this document from another PDF document
 (see PDF spec 1.4, 8.2.1)
diff --git a/vcl/source/gdi/pdfextoutdevdata.cxx b/vcl/source/gdi/pdfextoutdevdata.cxx
index 78304fa..2030b3a 100644
--- a/vcl/source/gdi/pdfextoutdevdata.cxx
+++ b/vcl/source/gdi/pdfextoutdevdata.cxx
@@ -305,6 +305,7 @@ struct PageSyncData
    void PushAction( const OutputDevice& rOutDev, const PDFExtOutDevDataSync::Action eAct );
    bool PlaySyncPageAct( PDFWriter& rWriter, sal_uInt32& rCurGDIMtfAction, const PDFExtOutDevData& rOutDevData );
};

void PageSyncData::PushAction( const OutputDevice& rOutDev, const PDFExtOutDevDataSync::Action eAct )
{
    GDIMetaFile* pMtf = rOutDev.GetConnectMetaFile();
@@ -403,13 +404,14 @@ bool PageSyncData::PlaySyncPageAct( PDFWriter& rWriter, sal_uInt32& rCurGDIMtfAc
                    }
                    else if ( aBeg->eAct == PDFExtOutDevDataSync::EndGroupGfxLink )
                    {
                        if ( rOutDevData.GetIsLosslessCompression() && !rOutDevData.GetIsReduceImageResolution() )
                        Graphic& rGraphic = mGraphics.front();
                        if ( rGraphic.IsLink() &&
                             rGraphic.GetLink().GetType() == GFX_LINK_TYPE_NATIVE_JPG &&
                             mParaRects.size() >= 2 )
                        {
                            Graphic& rGraphic = mGraphics.front();
                            if ( rGraphic.IsLink() && rGraphic.GetLink().GetType() == GFX_LINK_TYPE_NATIVE_JPG )
                            {
                                mbGroupIgnoreGDIMtfActions = true;
                            }
                            mbGroupIgnoreGDIMtfActions =
                                rOutDevData.HasAdequateCompression(
                                        rGraphic, mParaRects[0], mParaRects[1]);
                        }
                        break;
                    }
@@ -504,6 +506,8 @@ PDFExtOutDevData::PDFExtOutDevData( const OutputDevice& rOutDev ) :
    mbExportNDests          ( false ),
    mnFormsFormat           ( 0 ),
    mnPage                  ( -1 ),
    mnCompressionQuality    ( 90 ),
    mnMaxImageResolution    ( 300 ),
    mpPageSyncData          ( nullptr ),
    mpGlobalSyncData        ( new GlobalSyncData() )
{
@@ -528,6 +532,14 @@ void PDFExtOutDevData::SetIsLosslessCompression( const bool bUseLosslessCompress
{
    mbUseLosslessCompression = bUseLosslessCompression;
}
void PDFExtOutDevData::SetCompressionQuality( const sal_Int32 nQuality )
{
    mnCompressionQuality = nQuality;
}
void PDFExtOutDevData::SetMaxImageResolution( const sal_Int32 nMaxImageResolution )
{
    mnMaxImageResolution = nMaxImageResolution;
}
void PDFExtOutDevData::SetIsReduceImageResolution( const bool bReduceImageResolution )
{
    mbReduceImageResolution = bReduceImageResolution;
@@ -748,7 +760,7 @@ void PDFExtOutDevData::BeginGroup()
}

void PDFExtOutDevData::EndGroup( const Graphic&     rGraphic,
                                 sal_uInt8              nTransparency,
                                 sal_uInt8          nTransparency,
                                 const Rectangle&   rOutputRect,
                                 const Rectangle&   rVisibleOutputRect )
{
@@ -759,6 +771,49 @@ void PDFExtOutDevData::EndGroup( const Graphic&     rGraphic,
    mpPageSyncData->mParaRects.push_back( rVisibleOutputRect );
}

// Avoids expensive de-compression and re-compression of large images.
bool PDFExtOutDevData::HasAdequateCompression( const Graphic &rGraphic,
                                               const Rectangle & /* rOutputRect */,
                                               const Rectangle & /* rVisibleOutputRect */ ) const
{
    bool bReduceResolution = false;

    assert( rGraphic.IsLink() && rGraphic.GetLink().GetType() == GFX_LINK_TYPE_NATIVE_JPG );

    // small items better off as PNG anyway
    if ( rGraphic.GetSizePixel().Width() < 32 &&
         rGraphic.GetSizePixel().Height() < 32 )
        return false;

    // FIXME: ideally we'd also pre-empt the DPI related scaling too.

    Size aSize = rGraphic.GetSizePixel();
    sal_Int32 nCurrentRatio = (100 * aSize.Width() * aSize.Height() * 4) /
                               rGraphic.GetLink().GetDataSize();

    if ( GetIsLosslessCompression() )
        return !bReduceResolution && !GetIsReduceImageResolution();
    else
    {
        static const struct {
            sal_Int32 mnQuality;
            sal_Int32 mnRatio;
        } aRatios[] = { // minium tolerable compression ratios
            { 100, 400 }, { 95, 700 }, { 90, 1000 }, { 85, 1200 },
            { 80, 1500 }, { 75, 1700 }
        };
        sal_Int32 nTargetRatio = 10000;
        for ( size_t i = 0 ; i < SAL_N_ELEMENTS( aRatios ); ++i )
        {
            if ( mnCompressionQuality > aRatios[i].mnQuality )
                break;
            nTargetRatio = aRatios[i].mnRatio;
        }

        return nCurrentRatio > nTargetRatio;
    }
}

}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */