/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace vcl { namespace { struct PDFExtOutDevDataSync { enum Action{ CreateNamedDest, CreateDest, CreateLink, CreateScreen, SetLinkDest, SetLinkURL, SetScreenURL, SetScreenStream, RegisterDest, CreateOutlineItem, CreateNote, SetPageTransition, BeginStructureElement, EndStructureElement, SetCurrentStructureElement, SetStructureAttribute, SetStructureAttributeNumerical, SetStructureBoundingBox, SetActualText, SetAlternateText, CreateControl, BeginGroup, EndGroupGfxLink }; sal_uInt32 nIdx; Action eAct; }; struct PDFLinkDestination { tools::Rectangle mRect; MapMode mMapMode; sal_Int32 mPageNr; PDFWriter::DestAreaType mAreaType; }; } struct GlobalSyncData { std::deque< PDFExtOutDevDataSync::Action > mActions; std::deque< MapMode > mParaMapModes; std::deque< tools::Rectangle > mParaRects; std::deque< sal_Int32 > mParaInts; std::deque< sal_uInt32 > mParauInts; std::deque< OUString > mParaOUStrings; std::deque< PDFWriter::DestAreaType > mParaDestAreaTypes; std::deque< PDFNote > mParaPDFNotes; std::deque< PDFWriter::PageTransition > mParaPageTransitions; ::std::map< sal_Int32, PDFLinkDestination > mFutureDestinations; sal_Int32 GetMappedId(); sal_Int32 GetMappedStructId( sal_Int32 ); sal_Int32 mCurId; std::vector< sal_Int32 > mParaIds; std::vector< sal_Int32 > mStructIdMap; sal_Int32 mCurrentStructElement; std::vector< sal_Int32 > mStructParents; GlobalSyncData() : mCurId ( 0 ), mCurrentStructElement( 0 ) { mStructParents.push_back( 0 ); mStructIdMap.push_back( 0 ); } void PlayGlobalActions( PDFWriter& rWriter ); }; sal_Int32 GlobalSyncData::GetMappedId() { sal_Int32 nLinkId = mParaInts.front(); mParaInts.pop_front(); /* negative values are intentionally passed as invalid IDs * e.g. to create a new top level outline item */ if( nLinkId >= 0 ) { if ( o3tl::make_unsigned(nLinkId) < mParaIds.size() ) nLinkId = mParaIds[ nLinkId ]; else nLinkId = -1; SAL_WARN_IF( nLinkId < 0, "vcl", "unmapped id in GlobalSyncData" ); } return nLinkId; } sal_Int32 GlobalSyncData::GetMappedStructId( sal_Int32 nStructId ) { if ( o3tl::make_unsigned(nStructId) < mStructIdMap.size() ) nStructId = mStructIdMap[ nStructId ]; else nStructId = -1; SAL_WARN_IF( nStructId < 0, "vcl", "unmapped structure id in GlobalSyncData" ); return nStructId; } void GlobalSyncData::PlayGlobalActions( PDFWriter& rWriter ) { for (auto const& action : mActions) { switch (action) { case PDFExtOutDevDataSync::CreateNamedDest : //i56629 { rWriter.Push( PushFlags::MAPMODE ); rWriter.SetMapMode( mParaMapModes.front() ); mParaMapModes.pop_front(); mParaIds.push_back( rWriter.CreateNamedDest( mParaOUStrings.front(), mParaRects.front(), mParaInts.front(), mParaDestAreaTypes.front() ) ); mParaOUStrings.pop_front(); mParaRects.pop_front(); mParaInts.pop_front(); mParaDestAreaTypes.pop_front(); rWriter.Pop(); } break; case PDFExtOutDevDataSync::CreateDest : { rWriter.Push( PushFlags::MAPMODE ); rWriter.SetMapMode( mParaMapModes.front() ); mParaMapModes.pop_front(); mParaIds.push_back( rWriter.CreateDest( mParaRects.front(), mParaInts.front(), mParaDestAreaTypes.front() ) ); mParaRects.pop_front(); mParaInts.pop_front(); mParaDestAreaTypes.pop_front(); rWriter.Pop(); } break; case PDFExtOutDevDataSync::CreateLink : { rWriter.Push( PushFlags::MAPMODE ); rWriter.SetMapMode( mParaMapModes.front() ); mParaMapModes.pop_front(); mParaIds.push_back( rWriter.CreateLink( mParaRects.front(), mParaInts.front() ) ); // resolve LinkAnnotation structural attribute rWriter.SetLinkPropertyID( mParaIds.back(), sal_Int32(mParaIds.size()-1) ); mParaRects.pop_front(); mParaInts.pop_front(); rWriter.Pop(); } break; case PDFExtOutDevDataSync::CreateScreen: { rWriter.Push(PushFlags::MAPMODE); rWriter.SetMapMode(mParaMapModes.front()); mParaMapModes.pop_front(); mParaIds.push_back(rWriter.CreateScreen(mParaRects.front(), mParaInts.front())); mParaRects.pop_front(); mParaInts.pop_front(); rWriter.Pop(); } break; case PDFExtOutDevDataSync::SetLinkDest : { sal_Int32 nLinkId = GetMappedId(); sal_Int32 nDestId = GetMappedId(); rWriter.SetLinkDest( nLinkId, nDestId ); } break; case PDFExtOutDevDataSync::SetLinkURL : { sal_Int32 nLinkId = GetMappedId(); rWriter.SetLinkURL( nLinkId, mParaOUStrings.front() ); mParaOUStrings.pop_front(); } break; case PDFExtOutDevDataSync::SetScreenURL: { sal_Int32 nScreenId = GetMappedId(); rWriter.SetScreenURL(nScreenId, mParaOUStrings.front()); mParaOUStrings.pop_front(); } break; case PDFExtOutDevDataSync::SetScreenStream: { sal_Int32 nScreenId = GetMappedId(); rWriter.SetScreenStream(nScreenId, mParaOUStrings.front()); mParaOUStrings.pop_front(); } break; case PDFExtOutDevDataSync::RegisterDest : { const sal_Int32 nDestId = mParaInts.front(); mParaInts.pop_front(); OSL_ENSURE( mFutureDestinations.find( nDestId ) != mFutureDestinations.end(), "GlobalSyncData::PlayGlobalActions: DescribeRegisteredRequest has not been called for that destination!" ); PDFLinkDestination& rDest = mFutureDestinations[ nDestId ]; rWriter.Push( PushFlags::MAPMODE ); rWriter.SetMapMode( rDest.mMapMode ); mParaIds.push_back( rWriter.RegisterDestReference( nDestId, rDest.mRect, rDest.mPageNr, rDest.mAreaType ) ); rWriter.Pop(); } break; case PDFExtOutDevDataSync::CreateOutlineItem : { sal_Int32 nParent = GetMappedId(); sal_Int32 nLinkId = GetMappedId(); mParaIds.push_back( rWriter.CreateOutlineItem( nParent, mParaOUStrings.front(), nLinkId ) ); mParaOUStrings.pop_front(); } break; case PDFExtOutDevDataSync::CreateNote : { rWriter.Push( PushFlags::MAPMODE ); rWriter.SetMapMode( mParaMapModes.front() ); rWriter.CreateNote( mParaRects.front(), mParaPDFNotes.front(), mParaInts.front() ); mParaMapModes.pop_front(); mParaRects.pop_front(); mParaPDFNotes.pop_front(); mParaInts.pop_front(); } break; case PDFExtOutDevDataSync::SetPageTransition : { rWriter.SetPageTransition( mParaPageTransitions.front(), mParauInts.front(), mParaInts.front() ); mParaPageTransitions.pop_front(); mParauInts.pop_front(); mParaInts.pop_front(); } break; case PDFExtOutDevDataSync::BeginStructureElement: case PDFExtOutDevDataSync::EndStructureElement: case PDFExtOutDevDataSync::SetCurrentStructureElement: case PDFExtOutDevDataSync::SetStructureAttribute: case PDFExtOutDevDataSync::SetStructureAttributeNumerical: case PDFExtOutDevDataSync::SetStructureBoundingBox: case PDFExtOutDevDataSync::SetActualText: case PDFExtOutDevDataSync::SetAlternateText: case PDFExtOutDevDataSync::CreateControl: case PDFExtOutDevDataSync::BeginGroup: case PDFExtOutDevDataSync::EndGroupGfxLink: break; } } } struct PageSyncData { std::deque< PDFExtOutDevDataSync > mActions; std::deque< tools::Rectangle > mParaRects; std::deque< sal_Int32 > mParaInts; std::deque< OUString > mParaOUStrings; std::deque< PDFWriter::StructElement > mParaStructElements; std::deque< PDFWriter::StructAttribute > mParaStructAttributes; std::deque< PDFWriter::StructAttributeValue > mParaStructAttributeValues; std::deque< Graphic > mGraphics; Graphic mCurrentGraphic; std::deque< std::shared_ptr< PDFWriter::AnyWidget > > mControls; GlobalSyncData* mpGlobalData; bool mbGroupIgnoreGDIMtfActions; explicit PageSyncData( GlobalSyncData* pGlobal ) : mbGroupIgnoreGDIMtfActions ( false ) { mpGlobalData = pGlobal; } void PushAction( const OutputDevice& rOutDev, const PDFExtOutDevDataSync::Action eAct ); bool PlaySyncPageAct( PDFWriter& rWriter, sal_uInt32& rCurGDIMtfAction, const GDIMetaFile& rMtf, const PDFExtOutDevData& rOutDevData ); }; void PageSyncData::PushAction( const OutputDevice& rOutDev, const PDFExtOutDevDataSync::Action eAct ) { GDIMetaFile* pMtf = rOutDev.GetConnectMetaFile(); SAL_WARN_IF( !pMtf, "vcl", "PageSyncData::PushAction -> no ConnectMetaFile !!!" ); PDFExtOutDevDataSync aSync; aSync.eAct = eAct; if ( pMtf ) aSync.nIdx = pMtf->GetActionSize(); else aSync.nIdx = 0x7fffffff; // sync not possible mActions.push_back( aSync ); } bool PageSyncData::PlaySyncPageAct( PDFWriter& rWriter, sal_uInt32& rCurGDIMtfAction, const GDIMetaFile& rMtf, const PDFExtOutDevData& rOutDevData ) { bool bRet = false; if ( !mActions.empty() && ( mActions.front().nIdx == rCurGDIMtfAction ) ) { bRet = true; PDFExtOutDevDataSync aDataSync = mActions.front(); mActions.pop_front(); switch( aDataSync.eAct ) { case PDFExtOutDevDataSync::BeginStructureElement : { sal_Int32 nNewEl = rWriter.BeginStructureElement( mParaStructElements.front(), mParaOUStrings.front() ) ; mParaStructElements.pop_front(); mParaOUStrings.pop_front(); mpGlobalData->mStructIdMap.push_back( nNewEl ); } break; case PDFExtOutDevDataSync::EndStructureElement : { rWriter.EndStructureElement(); } break; case PDFExtOutDevDataSync::SetCurrentStructureElement: { rWriter.SetCurrentStructureElement( mpGlobalData->GetMappedStructId( mParaInts.front() ) ); mParaInts.pop_front(); } break; case PDFExtOutDevDataSync::SetStructureAttribute : { rWriter.SetStructureAttribute( mParaStructAttributes.front(), mParaStructAttributeValues.front() ); mParaStructAttributeValues.pop_front(); mParaStructAttributes.pop_front(); } break; case PDFExtOutDevDataSync::SetStructureAttributeNumerical : { rWriter.SetStructureAttributeNumerical( mParaStructAttributes.front(), mParaInts.front() ); mParaStructAttributes.pop_front(); mParaInts.pop_front(); } break; case PDFExtOutDevDataSync::SetStructureBoundingBox : { rWriter.SetStructureBoundingBox( mParaRects.front() ); mParaRects.pop_front(); } break; case PDFExtOutDevDataSync::SetActualText : { rWriter.SetActualText( mParaOUStrings.front() ); mParaOUStrings.pop_front(); } break; case PDFExtOutDevDataSync::SetAlternateText : { rWriter.SetAlternateText( mParaOUStrings.front() ); mParaOUStrings.pop_front(); } break; case PDFExtOutDevDataSync::CreateControl: { std::shared_ptr< PDFWriter::AnyWidget > pControl( mControls.front() ); SAL_WARN_IF( !pControl, "vcl", "PageSyncData::PlaySyncPageAct: invalid widget!" ); if ( pControl ) rWriter.CreateControl( *pControl ); mControls.pop_front(); } break; case PDFExtOutDevDataSync::BeginGroup : { /* first determining if this BeginGroup is starting a GfxLink, by searching for an EndGroup or an EndGroupGfxLink */ mbGroupIgnoreGDIMtfActions = false; auto isStartingGfxLink = std::any_of(mActions.begin(), mActions.end(), [](const PDFExtOutDevDataSync& rAction) { return rAction.eAct == PDFExtOutDevDataSync::EndGroupGfxLink; }); if ( isStartingGfxLink ) { Graphic& rGraphic = mGraphics.front(); if ( rGraphic.IsGfxLink() && mParaRects.size() >= 2 ) { GfxLinkType eType = rGraphic.GetGfxLink().GetType(); if ( eType == GfxLinkType::NativeJpg ) { mbGroupIgnoreGDIMtfActions = rOutDevData.HasAdequateCompression(rGraphic, mParaRects[0], mParaRects[1]); if ( !mbGroupIgnoreGDIMtfActions ) mCurrentGraphic = rGraphic; } else if ( eType == GfxLinkType::NativePng || eType == GfxLinkType::NativePdf ) { if ( eType == GfxLinkType::NativePdf || rOutDevData.HasAdequateCompression(rGraphic, mParaRects[0], mParaRects[1]) ) mCurrentGraphic = rGraphic; } } } } break; case PDFExtOutDevDataSync::EndGroupGfxLink : { tools::Rectangle aOutputRect, aVisibleOutputRect; Graphic aGraphic( mGraphics.front() ); mGraphics.pop_front(); sal_Int32 nTransparency = mParaInts.front(); mParaInts.pop_front(); aOutputRect = mParaRects.front(); mParaRects.pop_front(); aVisibleOutputRect = mParaRects.front(); mParaRects.pop_front(); if ( mbGroupIgnoreGDIMtfActions ) { bool bClippingNeeded = ( aOutputRect != aVisibleOutputRect ) && !aVisibleOutputRect.IsEmpty(); GfxLink aGfxLink( aGraphic.GetGfxLink() ); if ( aGfxLink.GetType() == GfxLinkType::NativeJpg ) { if ( bClippingNeeded ) { rWriter.Push(); basegfx::B2DPolyPolygon aRect( basegfx::utils::createPolygonFromRect( vcl::unotools::b2DRectangleFromRectangle(aVisibleOutputRect) ) ); rWriter.SetClipRegion( aRect); } AlphaMask aAlphaMask; if (nTransparency) { aAlphaMask = AlphaMask(aGraphic.GetSizePixel()); aAlphaMask.Erase(nTransparency); } SvMemoryStream aTmp; const sal_uInt8* pData = aGfxLink.GetData(); sal_uInt32 nBytes = aGfxLink.GetDataSize(); if( pData && nBytes ) { aTmp.WriteBytes( pData, nBytes ); // Look up the output rectangle from the previous // bitmap scale action if possible. This has the // correct position and size for images with a // custom translation (Writer header) or scaling // (Impress notes page). if (rCurGDIMtfAction > 0) { const MetaAction* pAction = rMtf.GetAction(rCurGDIMtfAction - 1); if (pAction && pAction->GetType() == MetaActionType::BMPSCALE) { const MetaBmpScaleAction* pA = static_cast(pAction); aOutputRect.SetPos(pA->GetPoint()); aOutputRect.SetSize(pA->GetSize()); } } auto ePixelFormat = aGraphic.GetBitmapEx().getPixelFormat(); rWriter.DrawJPGBitmap(aTmp, ePixelFormat > vcl::PixelFormat::N8_BPP, aGraphic.GetSizePixel(), aOutputRect, aAlphaMask, aGraphic); } if ( bClippingNeeded ) rWriter.Pop(); } mbGroupIgnoreGDIMtfActions = false; } mCurrentGraphic.Clear(); } break; case PDFExtOutDevDataSync::CreateNamedDest: case PDFExtOutDevDataSync::CreateDest: case PDFExtOutDevDataSync::CreateLink: case PDFExtOutDevDataSync::CreateScreen: case PDFExtOutDevDataSync::SetLinkDest: case PDFExtOutDevDataSync::SetLinkURL: case PDFExtOutDevDataSync::SetScreenURL: case PDFExtOutDevDataSync::SetScreenStream: case PDFExtOutDevDataSync::RegisterDest: case PDFExtOutDevDataSync::CreateOutlineItem: case PDFExtOutDevDataSync::CreateNote: case PDFExtOutDevDataSync::SetPageTransition: break; } } else if ( mbGroupIgnoreGDIMtfActions ) { rCurGDIMtfAction++; bRet = true; } return bRet; } PDFExtOutDevData::PDFExtOutDevData( const OutputDevice& rOutDev ) : mrOutDev ( rOutDev ), mbTaggedPDF ( false ), mbExportNotes ( true ), mbExportNotesPages ( false ), mbTransitionEffects ( true ), mbUseLosslessCompression( true ), mbReduceImageResolution ( false ), mbExportFormFields ( false ), mbExportBookmarks ( false ), mbExportHiddenSlides ( false ), mbSinglePageSheets ( false ), mbExportNDests ( false ), mnPage ( -1 ), mnCompressionQuality ( 90 ), mpGlobalSyncData ( new GlobalSyncData() ) { mpPageSyncData.reset( new PageSyncData( mpGlobalSyncData.get() ) ); } PDFExtOutDevData::~PDFExtOutDevData() { mpPageSyncData.reset(); mpGlobalSyncData.reset(); } const Graphic& PDFExtOutDevData::GetCurrentGraphic() const { return mpPageSyncData->mCurrentGraphic; } void PDFExtOutDevData::SetDocumentLocale( const css::lang::Locale& rLoc ) { maDocLocale = rLoc; } void PDFExtOutDevData::SetCurrentPageNumber( const sal_Int32 nPage ) { mnPage = nPage; } void PDFExtOutDevData::SetIsLosslessCompression( const bool bUseLosslessCompression ) { mbUseLosslessCompression = bUseLosslessCompression; } void PDFExtOutDevData::SetCompressionQuality( const sal_Int32 nQuality ) { mnCompressionQuality = nQuality; } void PDFExtOutDevData::SetIsReduceImageResolution( const bool bReduceImageResolution ) { mbReduceImageResolution = bReduceImageResolution; } void PDFExtOutDevData::SetIsExportNotes( const bool bExportNotes ) { mbExportNotes = bExportNotes; } void PDFExtOutDevData::SetIsExportNotesPages( const bool bExportNotesPages ) { mbExportNotesPages = bExportNotesPages; } void PDFExtOutDevData::SetIsExportTaggedPDF( const bool bTaggedPDF ) { mbTaggedPDF = bTaggedPDF; } void PDFExtOutDevData::SetIsExportTransitionEffects( const bool bTransitionEffects ) { mbTransitionEffects = bTransitionEffects; } void PDFExtOutDevData::SetIsExportFormFields( const bool bExportFomtFields ) { mbExportFormFields = bExportFomtFields; } void PDFExtOutDevData::SetIsExportBookmarks( const bool bExportBookmarks ) { mbExportBookmarks = bExportBookmarks; } void PDFExtOutDevData::SetIsExportHiddenSlides( const bool bExportHiddenSlides ) { mbExportHiddenSlides = bExportHiddenSlides; } void PDFExtOutDevData::SetIsSinglePageSheets( const bool bSinglePageSheets ) { mbSinglePageSheets = bSinglePageSheets; } void PDFExtOutDevData::SetIsExportNamedDestinations( const bool bExportNDests ) { mbExportNDests = bExportNDests; } void PDFExtOutDevData::ResetSyncData() { *mpPageSyncData = PageSyncData( mpGlobalSyncData.get() ); } bool PDFExtOutDevData::PlaySyncPageAct( PDFWriter& rWriter, sal_uInt32& rIdx, const GDIMetaFile& rMtf ) { return mpPageSyncData->PlaySyncPageAct( rWriter, rIdx, rMtf, *this ); } void PDFExtOutDevData::PlayGlobalActions( PDFWriter& rWriter ) { mpGlobalSyncData->PlayGlobalActions( rWriter ); } /* global actions, synchronisation to the recorded metafile isn't needed, all actions will be played after the last page was recorded */ //--->i56629 sal_Int32 PDFExtOutDevData::CreateNamedDest(const OUString& sDestName, const tools::Rectangle& rRect, sal_Int32 nPageNr ) { mpGlobalSyncData->mActions.push_back( PDFExtOutDevDataSync::CreateNamedDest ); mpGlobalSyncData->mParaOUStrings.push_back( sDestName ); mpGlobalSyncData->mParaRects.push_back( rRect ); mpGlobalSyncData->mParaMapModes.push_back( mrOutDev.GetMapMode() ); mpGlobalSyncData->mParaInts.push_back( nPageNr == -1 ? mnPage : nPageNr ); mpGlobalSyncData->mParaDestAreaTypes.push_back( PDFWriter::DestAreaType::XYZ ); return mpGlobalSyncData->mCurId++; } //<---i56629 sal_Int32 PDFExtOutDevData::RegisterDest() { const sal_Int32 nLinkDestID = mpGlobalSyncData->mCurId++; mpGlobalSyncData->mActions.push_back( PDFExtOutDevDataSync::RegisterDest ); mpGlobalSyncData->mParaInts.push_back( nLinkDestID ); return nLinkDestID; } void PDFExtOutDevData::DescribeRegisteredDest( sal_Int32 nDestId, const tools::Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType ) { OSL_PRECOND( nDestId != -1, "PDFExtOutDevData::DescribeRegisteredDest: invalid destination Id!" ); PDFLinkDestination aLinkDestination; aLinkDestination.mRect = rRect; aLinkDestination.mMapMode = mrOutDev.GetMapMode(); aLinkDestination.mPageNr = nPageNr == -1 ? mnPage : nPageNr; aLinkDestination.mAreaType = eType; mpGlobalSyncData->mFutureDestinations[ nDestId ] = aLinkDestination; } sal_Int32 PDFExtOutDevData::CreateDest( const tools::Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType ) { mpGlobalSyncData->mActions.push_back( PDFExtOutDevDataSync::CreateDest ); mpGlobalSyncData->mParaRects.push_back( rRect ); mpGlobalSyncData->mParaMapModes.push_back( mrOutDev.GetMapMode() ); mpGlobalSyncData->mParaInts.push_back( nPageNr == -1 ? mnPage : nPageNr ); mpGlobalSyncData->mParaDestAreaTypes.push_back( eType ); return mpGlobalSyncData->mCurId++; } sal_Int32 PDFExtOutDevData::CreateLink( const tools::Rectangle& rRect, sal_Int32 nPageNr ) { mpGlobalSyncData->mActions.push_back( PDFExtOutDevDataSync::CreateLink ); mpGlobalSyncData->mParaRects.push_back( rRect ); mpGlobalSyncData->mParaMapModes.push_back( mrOutDev.GetMapMode() ); mpGlobalSyncData->mParaInts.push_back( nPageNr == -1 ? mnPage : nPageNr ); return mpGlobalSyncData->mCurId++; } sal_Int32 PDFExtOutDevData::CreateScreen(const tools::Rectangle& rRect, sal_Int32 nPageNr) { mpGlobalSyncData->mActions.push_back(PDFExtOutDevDataSync::CreateScreen); mpGlobalSyncData->mParaRects.push_back(rRect); mpGlobalSyncData->mParaMapModes.push_back(mrOutDev.GetMapMode()); mpGlobalSyncData->mParaInts.push_back(nPageNr); return mpGlobalSyncData->mCurId++; } void PDFExtOutDevData::SetLinkDest( sal_Int32 nLinkId, sal_Int32 nDestId ) { mpGlobalSyncData->mActions.push_back( PDFExtOutDevDataSync::SetLinkDest ); mpGlobalSyncData->mParaInts.push_back( nLinkId ); mpGlobalSyncData->mParaInts.push_back( nDestId ); } void PDFExtOutDevData::SetLinkURL( sal_Int32 nLinkId, const OUString& rURL ) { mpGlobalSyncData->mActions.push_back( PDFExtOutDevDataSync::SetLinkURL ); mpGlobalSyncData->mParaInts.push_back( nLinkId ); mpGlobalSyncData->mParaOUStrings.push_back( rURL ); } void PDFExtOutDevData::SetScreenURL(sal_Int32 nScreenId, const OUString& rURL) { mpGlobalSyncData->mActions.push_back(PDFExtOutDevDataSync::SetScreenURL); mpGlobalSyncData->mParaInts.push_back(nScreenId); mpGlobalSyncData->mParaOUStrings.push_back(rURL); } void PDFExtOutDevData::SetScreenStream(sal_Int32 nScreenId, const OUString& rURL) { mpGlobalSyncData->mActions.push_back(PDFExtOutDevDataSync::SetScreenStream); mpGlobalSyncData->mParaInts.push_back(nScreenId); mpGlobalSyncData->mParaOUStrings.push_back(rURL); } sal_Int32 PDFExtOutDevData::CreateOutlineItem( sal_Int32 nParent, const OUString& rText, sal_Int32 nDestID ) { if (nParent == -1) // Has no parent, it's a chapter / heading 1. maChapterNames.push_back(rText); mpGlobalSyncData->mActions.push_back( PDFExtOutDevDataSync::CreateOutlineItem ); mpGlobalSyncData->mParaInts.push_back( nParent ); mpGlobalSyncData->mParaOUStrings.push_back( rText ); mpGlobalSyncData->mParaInts.push_back( nDestID ); return mpGlobalSyncData->mCurId++; } void PDFExtOutDevData::CreateNote( const tools::Rectangle& rRect, const PDFNote& rNote, sal_Int32 nPageNr ) { mpGlobalSyncData->mActions.push_back( PDFExtOutDevDataSync::CreateNote ); mpGlobalSyncData->mParaRects.push_back( rRect ); mpGlobalSyncData->mParaMapModes.push_back( mrOutDev.GetMapMode() ); mpGlobalSyncData->mParaPDFNotes.push_back( rNote ); mpGlobalSyncData->mParaInts.push_back( nPageNr == -1 ? mnPage : nPageNr ); } void PDFExtOutDevData::SetPageTransition( PDFWriter::PageTransition eType, sal_uInt32 nMilliSec ) { mpGlobalSyncData->mActions.push_back( PDFExtOutDevDataSync::SetPageTransition ); mpGlobalSyncData->mParaPageTransitions.push_back( eType ); mpGlobalSyncData->mParauInts.push_back( nMilliSec ); mpGlobalSyncData->mParaInts.push_back( mnPage ); } /* local (page), actions have to be played synchronously to the actions of of the recorded metafile (created by each xRenderable->render()) */ sal_Int32 PDFExtOutDevData::BeginStructureElement( PDFWriter::StructElement eType, const OUString& rAlias ) { mpPageSyncData->PushAction( mrOutDev, PDFExtOutDevDataSync::BeginStructureElement ); mpPageSyncData->mParaStructElements.push_back( eType ); mpPageSyncData->mParaOUStrings.push_back( rAlias ); // need a global id sal_Int32 nNewId = mpGlobalSyncData->mStructParents.size(); mpGlobalSyncData->mStructParents.push_back( mpGlobalSyncData->mCurrentStructElement ); mpGlobalSyncData->mCurrentStructElement = nNewId; return nNewId; } void PDFExtOutDevData::EndStructureElement() { mpPageSyncData->PushAction( mrOutDev, PDFExtOutDevDataSync::EndStructureElement ); mpGlobalSyncData->mCurrentStructElement = mpGlobalSyncData->mStructParents[ mpGlobalSyncData->mCurrentStructElement ]; } bool PDFExtOutDevData::SetCurrentStructureElement( sal_Int32 nStructId ) { bool bSuccess = false; if( o3tl::make_unsigned(nStructId) < mpGlobalSyncData->mStructParents.size() ) { mpGlobalSyncData->mCurrentStructElement = nStructId; mpPageSyncData->PushAction( mrOutDev, PDFExtOutDevDataSync::SetCurrentStructureElement ); mpPageSyncData->mParaInts.push_back( nStructId ); bSuccess = true; } return bSuccess; } sal_Int32 PDFExtOutDevData::GetCurrentStructureElement() const { return mpGlobalSyncData->mCurrentStructElement; } void PDFExtOutDevData::SetStructureAttribute( PDFWriter::StructAttribute eAttr, PDFWriter::StructAttributeValue eVal ) { mpPageSyncData->PushAction( mrOutDev, PDFExtOutDevDataSync::SetStructureAttribute ); mpPageSyncData->mParaStructAttributes.push_back( eAttr ); mpPageSyncData->mParaStructAttributeValues.push_back( eVal ); } void PDFExtOutDevData::SetStructureAttributeNumerical( PDFWriter::StructAttribute eAttr, sal_Int32 nValue ) { mpPageSyncData->PushAction( mrOutDev, PDFExtOutDevDataSync::SetStructureAttributeNumerical ); mpPageSyncData->mParaStructAttributes.push_back( eAttr ); mpPageSyncData->mParaInts.push_back( nValue ); } void PDFExtOutDevData::SetStructureBoundingBox( const tools::Rectangle& rRect ) { mpPageSyncData->PushAction( mrOutDev, PDFExtOutDevDataSync::SetStructureBoundingBox ); mpPageSyncData->mParaRects.push_back( rRect ); } void PDFExtOutDevData::SetActualText( const OUString& rText ) { mpPageSyncData->PushAction( mrOutDev, PDFExtOutDevDataSync::SetActualText ); mpPageSyncData->mParaOUStrings.push_back( rText ); } void PDFExtOutDevData::SetAlternateText( const OUString& rText ) { mpPageSyncData->PushAction( mrOutDev, PDFExtOutDevDataSync::SetAlternateText ); mpPageSyncData->mParaOUStrings.push_back( rText ); } void PDFExtOutDevData::CreateControl( const PDFWriter::AnyWidget& rControlType ) { mpPageSyncData->PushAction( mrOutDev, PDFExtOutDevDataSync::CreateControl ); std::shared_ptr< PDFWriter::AnyWidget > pClone( rControlType.Clone() ); mpPageSyncData->mControls.push_back( pClone ); } void PDFExtOutDevData::BeginGroup() { mpPageSyncData->PushAction( mrOutDev, PDFExtOutDevDataSync::BeginGroup ); } void PDFExtOutDevData::EndGroup( const Graphic& rGraphic, sal_uInt8 nTransparency, const tools::Rectangle& rOutputRect, const tools::Rectangle& rVisibleOutputRect ) { mpPageSyncData->PushAction( mrOutDev, PDFExtOutDevDataSync::EndGroupGfxLink ); mpPageSyncData->mGraphics.push_back( rGraphic ); mpPageSyncData->mParaInts.push_back( nTransparency ); mpPageSyncData->mParaRects.push_back( rOutputRect ); mpPageSyncData->mParaRects.push_back( rVisibleOutputRect ); } // Avoids expensive de-compression and re-compression of large images. bool PDFExtOutDevData::HasAdequateCompression( const Graphic &rGraphic, const tools::Rectangle & rOutputRect, const tools::Rectangle & rVisibleOutputRect ) const { assert(rGraphic.IsGfxLink() && (rGraphic.GetGfxLink().GetType() == GfxLinkType::NativeJpg || rGraphic.GetGfxLink().GetType() == GfxLinkType::NativePng || rGraphic.GetGfxLink().GetType() == GfxLinkType::NativePdf)); if (rOutputRect != rVisibleOutputRect) // rOutputRect is the crop rectangle, re-compress cropped image. return false; if (mbReduceImageResolution) // Reducing resolution was requested, implies that re-compressing is // wanted. return false; auto nSize = rGraphic.GetGfxLink().GetDataSize(); if (nSize == 0) return false; GfxLink aLink = rGraphic.GetGfxLink(); SvMemoryStream aMemoryStream(const_cast(aLink.GetData()), aLink.GetDataSize(), StreamMode::READ | StreamMode::WRITE); GraphicDescriptor aDescriptor(aMemoryStream, nullptr); if (aDescriptor.Detect(true) && aDescriptor.GetNumberOfImageComponents() == 4) // 4 means CMYK, which is not handled. return false; const Size aSize = rGraphic.GetSizePixel(); // small items better off as PNG anyway if ( aSize.Width() < 32 && aSize.Height() < 32 ) return false; if (GetIsLosslessCompression()) return !GetIsReduceImageResolution(); // FIXME: ideally we'd also pre-empt the DPI related scaling too. sal_Int32 nCurrentRatio = (100 * aSize.Width() * aSize.Height() * 4) / nSize; static const struct { sal_Int32 mnQuality; sal_Int32 mnRatio; } aRatios[] = { // minimum tolerable compression ratios { 100, 400 }, { 95, 700 }, { 90, 1000 }, { 85, 1200 }, { 80, 1500 }, { 75, 1700 } }; sal_Int32 nTargetRatio = 10000; bool bIsTargetRatioReached = false; for (auto & rRatio : aRatios) { if ( mnCompressionQuality > rRatio.mnQuality ) { bIsTargetRatioReached = true; break; } nTargetRatio = rRatio.mnRatio; } return ((nCurrentRatio > nTargetRatio) && bIsTargetRatioReached); } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */