blob: 21e5546df60fc174582539be24f22c2b96a6e8e3 [file] [log] [blame]
/**************************************************************
*
* 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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
#
#include <basegfx/range/b2drange.hxx>
#include <basegfx/range/b2irange.hxx>
#include <basegfx/polygon/b2dpolypolygon.hxx>
#include <basebmp/scanlineformats.hxx>
#include <tools/debug.hxx>
#if OSL_DEBUG_LEVEL > 2
#include <basebmp/debug.hxx>
#endif
#include <outfont.hxx>
#include <glyphcache.hxx>
#include <impfont.hxx>
#include "svpgdi.hxx"
#include "svpbmp.hxx"
#include "svppspgraphics.hxx"
using namespace basegfx;
using namespace basebmp;
// ===========================================================================
class SvpGlyphPeer
: public GlyphCachePeer
{
public:
SvpGlyphPeer() {}
BitmapDeviceSharedPtr GetGlyphBmp( ServerFont&, sal_GlyphId,
sal_uInt32 nBmpFormat, B2IPoint& rTargetPos );
protected:
virtual void RemovingFont( ServerFont& );
virtual void RemovingGlyph( ServerFont&, GlyphData&, sal_GlyphId );
class SvpGcpHelper
{
public:
RawBitmap maRawBitmap;
BitmapDeviceSharedPtr maBitmapDev;
};
};
// ===========================================================================
class SvpGlyphCache : public GlyphCache
{
public:
SvpGlyphPeer& GetPeer() { return reinterpret_cast<SvpGlyphPeer&>( mrPeer ); }
static SvpGlyphCache& GetInstance();
private:
SvpGlyphCache( SvpGlyphPeer& rPeer ) : GlyphCache( rPeer) {}
};
//--------------------------------------------------------------------------
SvpGlyphCache& SvpGlyphCache::GetInstance()
{
static SvpGlyphPeer aSvpGlyphPeer;
static SvpGlyphCache aGC( aSvpGlyphPeer );
return aGC;
}
// ===========================================================================
BitmapDeviceSharedPtr SvpGlyphPeer::GetGlyphBmp( ServerFont& rServerFont,
sal_GlyphId aGlyphId, sal_uInt32 nBmpFormat, B2IPoint& rTargetPos )
{
GlyphData& rGlyphData = rServerFont.GetGlyphData( aGlyphId );
SvpGcpHelper* pGcpHelper = (SvpGcpHelper*)rGlyphData.ExtDataRef().mpData;
// nothing to do if the GlyphPeer hasn't allocated resources for the glyph
if( rGlyphData.ExtDataRef().meInfo != sal::static_int_cast<int>(nBmpFormat) )
{
if( rGlyphData.ExtDataRef().meInfo == Format::NONE )
pGcpHelper = new SvpGcpHelper;
RawBitmap& rRawBitmap = pGcpHelper->maRawBitmap;
// get glyph bitmap in matching format
bool bFound = false;
switch( nBmpFormat )
{
case Format::ONE_BIT_LSB_GREY:
bFound = rServerFont.GetGlyphBitmap1( aGlyphId, pGcpHelper->maRawBitmap );
break;
case Format::EIGHT_BIT_GREY:
bFound = rServerFont.GetGlyphBitmap8( aGlyphId, pGcpHelper->maRawBitmap );
break;
default:
DBG_ERROR( "SVP GCP::GetGlyphBmp(): illegal scanline format");
// fall back to black&white mask
nBmpFormat = Format::ONE_BIT_LSB_GREY;
bFound = false;
break;
}
// return .notdef glyph if needed
if( !bFound && (aGlyphId != 0) )
{
delete pGcpHelper;
return GetGlyphBmp( rServerFont, 0, nBmpFormat, rTargetPos );
}
// construct alpha mask from raw bitmap
const B2IVector aSize( rRawBitmap.mnScanlineSize, rRawBitmap.mnHeight );
if( aSize.getX() && aSize.getY() )
{
static PaletteMemorySharedVector aDummyPAL;
RawMemorySharedArray aRawPtr( rRawBitmap.mpBits );
pGcpHelper->maBitmapDev = createBitmapDevice( aSize, true, nBmpFormat, aRawPtr, aDummyPAL );
}
rServerFont.SetExtended( nBmpFormat, (void*)pGcpHelper );
}
rTargetPos += B2IPoint( pGcpHelper->maRawBitmap.mnXOffset, pGcpHelper->maRawBitmap.mnYOffset );
return pGcpHelper->maBitmapDev;
}
//--------------------------------------------------------------------------
void SvpGlyphPeer::RemovingFont( ServerFont& )
{
// nothing to do: no font resources held in SvpGlyphPeer
}
//--------------------------------------------------------------------------
void SvpGlyphPeer::RemovingGlyph( ServerFont&, GlyphData& rGlyphData, sal_GlyphId /*aGlyphId*/ )
{
if( rGlyphData.ExtDataRef().mpData != Format::NONE )
{
// release the glyph related resources
DBG_ASSERT( (rGlyphData.ExtDataRef().meInfo <= Format::MAX), "SVP::RG() invalid alpha format" );
SvpGcpHelper* pGcpHelper = (SvpGcpHelper*)rGlyphData.ExtDataRef().mpData;
delete[] pGcpHelper->maRawBitmap.mpBits;
delete pGcpHelper;
}
}
// ===========================================================================
// PspKernInfo allows on-demand-querying of psprint provided kerning info (#i29881#)
class PspKernInfo : public ExtraKernInfo
{
public:
PspKernInfo( int nFontId ) : ExtraKernInfo(nFontId) {}
protected:
virtual void Initialize() const;
};
//--------------------------------------------------------------------------
void PspKernInfo::Initialize() const
{
mbInitialized = true;
// get the kerning pairs from psprint
const psp::PrintFontManager& rMgr = psp::PrintFontManager::get();
typedef std::list< psp::KernPair > PspKernPairs;
const PspKernPairs& rKernPairs = rMgr.getKernPairs( mnFontId );
if( rKernPairs.empty() )
return;
// feed psprint's kerning list into a lookup-friendly container
maUnicodeKernPairs.rehash( rKernPairs.size() );
PspKernPairs::const_iterator it = rKernPairs.begin();
for(; it != rKernPairs.end(); ++it )
{
ImplKernPairData aKernPair = { it->first, it->second, it->kern_x };
maUnicodeKernPairs.insert( aKernPair );
}
}
// ===========================================================================
sal_uInt16 SvpSalGraphics::SetFont( ImplFontSelectData* pIFSD, int nFallbackLevel )
{
// release all no longer needed font resources
for( int i = nFallbackLevel; i < MAX_FALLBACK; ++i )
{
if( m_pServerFont[i] != NULL )
{
// old server side font is no longer referenced
SvpGlyphCache::GetInstance().UncacheFont( *m_pServerFont[i] );
m_pServerFont[i] = NULL;
}
}
// return early if there is no new font
if( !pIFSD )
return 0;
// handle the request for a non-native X11-font => use the GlyphCache
ServerFont* pServerFont = SvpGlyphCache::GetInstance().CacheFont( *pIFSD );
if( !pServerFont )
return SAL_SETFONT_BADFONT;
// check selected font
if( !pServerFont->TestFont() )
{
SvpGlyphCache::GetInstance().UncacheFont( *pServerFont );
return SAL_SETFONT_BADFONT;
}
// update SalGraphics font settings
m_pServerFont[ nFallbackLevel ] = pServerFont;
return SAL_SETFONT_USEDRAWTEXTARRAY;
}
// ---------------------------------------------------------------------------
void SvpSalGraphics::GetFontMetric( ImplFontMetricData* pMetric, int nFallbackLevel )
{
if( nFallbackLevel >= MAX_FALLBACK )
return;
if( m_pServerFont[nFallbackLevel] != NULL )
{
long rDummyFactor;
m_pServerFont[nFallbackLevel]->FetchFontMetric( *pMetric, rDummyFactor );
}
}
// ---------------------------------------------------------------------------
sal_uLong SvpSalGraphics::GetKernPairs( sal_uLong nPairs, ImplKernPairData* pKernPairs )
{
sal_uLong nGotPairs = 0;
if( m_pServerFont[0] != NULL )
{
ImplKernPairData* pTmpKernPairs = NULL;
nGotPairs = m_pServerFont[0]->GetKernPairs( &pTmpKernPairs );
for( sal_uLong i = 0; i < nPairs && i < nGotPairs; ++i )
pKernPairs[ i ] = pTmpKernPairs[ i ];
delete[] pTmpKernPairs;
}
return nGotPairs;
}
// ---------------------------------------------------------------------------
const ImplFontCharMap* SvpSalGraphics::GetImplFontCharMap() const
{
if( !m_pServerFont[0] )
return NULL;
const ImplFontCharMap* pIFCMap = m_pServerFont[0]->GetImplFontCharMap();
return pIFCMap;
}
// ---------------------------------------------------------------------------
void SvpSalGraphics::GetDevFontList( ImplDevFontList* pDevFontList )
{
GlyphCache& rGC = SvpGlyphCache::GetInstance();
psp::PrintFontManager& rMgr = psp::PrintFontManager::get();
psp::FastPrintFontInfo aInfo;
::std::list< psp::fontID > aList;
rMgr.getFontList( aList );
::std::list< psp::fontID >::iterator it;
for( it = aList.begin(); it != aList.end(); ++it )
{
if( !rMgr.getFontFastInfo( *it, aInfo ) )
continue;
// the GlyphCache must not bother with builtin fonts because
// it cannot access or use them anyway
if( aInfo.m_eType == psp::fonttype::Builtin )
continue;
// normalize face number to the GlyphCache
int nFaceNum = rMgr.getFontFaceNumber( aInfo.m_nID );
if( nFaceNum < 0 )
nFaceNum = 0;
// for fonts where extra kerning info can be provided on demand
// an ExtraKernInfo object is supplied
const ExtraKernInfo* pExtraKernInfo = NULL;
if( aInfo.m_eType == psp::fonttype::Type1 )
pExtraKernInfo = new PspKernInfo( *it );
// inform GlyphCache about this font provided by the PsPrint subsystem
ImplDevFontAttributes aDFA = PspGraphics::Info2DevFontAttributes( aInfo );
aDFA.mnQuality += 4096;
const rtl::OString& rFileName = rMgr.getFontFileSysPath( aInfo.m_nID );
rGC.AddFontFile( rFileName, nFaceNum, aInfo.m_nID, aDFA, pExtraKernInfo );
}
// announce glyphcache fonts
rGC.AnnounceFonts( pDevFontList );
}
// ---------------------------------------------------------------------------
void SvpSalGraphics::GetDevFontSubstList( OutputDevice* )
{}
// ---------------------------------------------------------------------------
bool SvpSalGraphics::AddTempDevFont( ImplDevFontList*,
const String&, const String& )
{
return false;
}
// ---------------------------------------------------------------------------
sal_Bool SvpSalGraphics::CreateFontSubset(
const rtl::OUString& rToFile,
const ImplFontData* pFont,
sal_GlyphId* pGlyphIds,
sal_uInt8* pEncoding,
sal_Int32* pWidths,
int nGlyphCount,
FontSubsetInfo& rInfo
)
{
// in this context the pFont->GetFontId() is a valid PSP
// font since they are the only ones left after the PDF
// export has filtered its list of subsettable fonts (for
// which this method was created). The correct way would
// be to have the GlyphCache search for the ImplFontData pFont
psp::fontID aFont = pFont->GetFontId();
psp::PrintFontManager& rMgr = psp::PrintFontManager::get();
bool bSuccess = rMgr.createFontSubset( rInfo,
aFont,
rToFile,
pGlyphIds,
pEncoding,
pWidths,
nGlyphCount );
return bSuccess;
}
// ---------------------------------------------------------------------------
const Ucs2SIntMap* SvpSalGraphics::GetFontEncodingVector( const ImplFontData* pFont, const Ucs2OStrMap** pNonEncoded )
{
// in this context the pFont->GetFontId() is a valid PSP
// font since they are the only ones left after the PDF
// export has filtered its list of subsettable fonts (for
// which this method was created). The correct way would
// be to have the GlyphCache search for the ImplFontData pFont
psp::fontID aFont = pFont->GetFontId();
return PspGraphics::DoGetFontEncodingVector( aFont, pNonEncoded );
}
// ---------------------------------------------------------------------------
const void* SvpSalGraphics::GetEmbedFontData(
const ImplFontData* pFont,
const sal_Ucs* pUnicodes,
sal_Int32* pWidths,
FontSubsetInfo& rInfo,
long* pDataLen
)
{
// in this context the pFont->GetFontId() is a valid PSP
// font since they are the only ones left after the PDF
// export has filtered its list of subsettable fonts (for
// which this method was created). The correct way would
// be to have the GlyphCache search for the ImplFontData pFont
psp::fontID aFont = pFont->GetFontId();
return PspGraphics::DoGetEmbedFontData( aFont, pUnicodes, pWidths, rInfo, pDataLen );
}
// ---------------------------------------------------------------------------
void SvpSalGraphics::FreeEmbedFontData( const void* pData, long nLen )
{
PspGraphics::DoFreeEmbedFontData( pData, nLen );
}
void SvpSalGraphics::GetGlyphWidths( const ImplFontData* pFont,
bool bVertical,
Int32Vector& rWidths,
Ucs2UIntMap& rUnicodeEnc )
{
// in this context the pFont->GetFontId() is a valid PSP
// font since they are the only ones left after the PDF
// export has filtered its list of subsettable fonts (for
// which this method was created). The correct way would
// be to have the GlyphCache search for the ImplFontData pFont
psp::fontID aFont = pFont->GetFontId();
PspGraphics::DoGetGlyphWidths( aFont, bVertical, rWidths, rUnicodeEnc );
}
// ---------------------------------------------------------------------------
bool SvpSalGraphics::GetGlyphBoundRect( sal_GlyphId aGlyphId, Rectangle& rRect )
{
const int nLevel = aGlyphId >> GF_FONTSHIFT;
if( nLevel >= MAX_FALLBACK )
return false;
ServerFont* pSF = m_pServerFont[ nLevel ];
if( !pSF )
return false;
aGlyphId &= ~GF_FONTMASK;
const GlyphMetric& rGM = pSF->GetGlyphMetric( aGlyphId );
rRect = Rectangle( rGM.GetOffset(), rGM.GetSize() );
return true;
}
// ---------------------------------------------------------------------------
bool SvpSalGraphics::GetGlyphOutline( sal_GlyphId aGlyphId, B2DPolyPolygon& rPolyPoly )
{
const int nLevel = aGlyphId >> GF_FONTSHIFT;
if( nLevel >= MAX_FALLBACK )
return false;
const ServerFont* pSF = m_pServerFont[ nLevel ];
if( !pSF )
return false;
aGlyphId &= ~GF_FONTMASK;
bool bOK = pSF->GetGlyphOutline( aGlyphId, rPolyPoly );
return bOK;
}
// ---------------------------------------------------------------------------
SalLayout* SvpSalGraphics::GetTextLayout( ImplLayoutArgs&, int nFallbackLevel )
{
GenericSalLayout* pLayout = NULL;
if( m_pServerFont[ nFallbackLevel ] )
pLayout = new ServerFontLayout( *m_pServerFont[ nFallbackLevel ] );
return pLayout;
}
// ---------------------------------------------------------------------------
void SvpSalGraphics::DrawServerFontLayout( const ServerFontLayout& rSalLayout )
{
// iterate over all glyphs in the layout
Point aPos;
sal_GlyphId aGlyphId;
SvpGlyphPeer& rGlyphPeer = SvpGlyphCache::GetInstance().GetPeer();
for( int nStart = 0; rSalLayout.GetNextGlyphs( 1, &aGlyphId, aPos, nStart ); )
{
int nLevel = aGlyphId >> GF_FONTSHIFT;
DBG_ASSERT( nLevel < MAX_FALLBACK, "SvpGDI: invalid glyph fallback level" );
ServerFont* pSF = m_pServerFont[ nLevel ];
if( !pSF )
continue;
// get the glyph's alpha mask and adjust the drawing position
aGlyphId &= ~GF_FONTMASK;
B2IPoint aDstPoint( aPos.X(), aPos.Y() );
BitmapDeviceSharedPtr aAlphaMask
= rGlyphPeer.GetGlyphBmp( *pSF, aGlyphId, m_eTextFmt, aDstPoint );
if( !aAlphaMask ) // ignore empty glyphs
continue;
// blend text color into target using the glyph's mask
const B2IRange aSrcRect( B2ITuple(0,0), aAlphaMask->getSize() );
m_aDevice->drawMaskedColor( m_aTextColor, aAlphaMask, aSrcRect, aDstPoint, m_aClipMap );
}
}
// ===========================================================================