/************************************************************** * * 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. * *************************************************************/ #define INCL_GRE_STRINGS #define INCL_GPI #define INCL_DOS #include //#include //#include #include #include #include "tools/svwin.h" #include "os2/saldata.hxx" #include "os2/salgdi.h" #include "vcl/svapp.hxx" #include "outfont.hxx" #include "vcl/font.hxx" #include "fontsubset.hxx" #include "sallayout.hxx" #include "rtl/logfile.hxx" #include "rtl/tencinfo.h" #include "rtl/textcvt.h" #include "rtl/bootstrap.hxx" #include "osl/module.h" #include "osl/file.hxx" #include "osl/thread.hxx" #include "osl/process.h" #include "tools/poly.hxx" #include "tools/debug.hxx" #include "tools/stream.hxx" #include "basegfx/polygon/b2dpolygon.hxx" #include "basegfx/polygon/b2dpolypolygon.hxx" #include "basegfx/matrix/b2dhommatrix.hxx" #ifndef __H_FT2LIB #include #include #endif #include "sft.hxx" #ifdef GCP_KERN_HACK #include #endif using namespace vcl; // ----------- // - Inlines - // ----------- inline W32FIXED FixedFromDouble( double d ) { const long l = (long) ( d * 65536. ); return *(W32FIXED*) &l; } // ----------------------------------------------------------------------- inline int IntTimes256FromFixed(W32FIXED f) { int nFixedTimes256 = (f.value << 8) + ((f.fract+0x80) >> 8); return nFixedTimes256; } // ----------- // - Defines - // ----------- // this is a special codepage code, used to identify OS/2 symbol font. #define SYMBOL_CHARSET 65400 // ======================================================================= UniString ImplSalGetUniString( const sal_Char* pStr, xub_StrLen nLen = STRING_LEN) { return UniString( pStr, nLen, gsl_getSystemTextEncoding(), RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT | RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT ); } // ======================================================================= static USHORT ImplSalToCharSet( CharSet eCharSet ) { // !!! Fuer DBCS-Systeme muss dieser Code auskommentiert werden und 0 // !!! zurueckgegeben werden, solange die DBCS-Charsets nicht // !!! durchgereicht werden switch ( eCharSet ) { case RTL_TEXTENCODING_IBM_437: return 437; case RTL_TEXTENCODING_IBM_850: return 850; case RTL_TEXTENCODING_IBM_860: return 860; case RTL_TEXTENCODING_IBM_861: return 861; case RTL_TEXTENCODING_IBM_863: return 863; case RTL_TEXTENCODING_IBM_865: return 865; case RTL_TEXTENCODING_MS_1252: return 1004; case RTL_TEXTENCODING_SYMBOL: return 65400; } return 0; } // ----------------------------------------------------------------------- static CharSet ImplCharSetToSal( USHORT usCodePage ) { switch ( usCodePage ) { case 437: return RTL_TEXTENCODING_IBM_437; case 850: return RTL_TEXTENCODING_IBM_850; case 860: return RTL_TEXTENCODING_IBM_860; case 861: return RTL_TEXTENCODING_IBM_861; case 863: return RTL_TEXTENCODING_IBM_863; case 865: return RTL_TEXTENCODING_IBM_865; case 1004: return RTL_TEXTENCODING_MS_1252; case 65400: return RTL_TEXTENCODING_SYMBOL; } return RTL_TEXTENCODING_DONTKNOW; } // ----------------------------------------------------------------------- static FontFamily ImplFamilyToSal( PM_BYTE bFamilyType ) { switch ( bFamilyType ) { case 4: return FAMILY_DECORATIVE; case 3: return FAMILY_SCRIPT; } return FAMILY_DONTKNOW; } // ----------------------------------------------------------------------- static FontWeight ImplWeightToSal( USHORT nWeight ) { // Falls sich jemand an die alte Doku gehalten hat if ( nWeight > 999 ) nWeight /= 1000; switch ( nWeight ) { case 1: return WEIGHT_THIN; case 2: return WEIGHT_ULTRALIGHT; case 3: return WEIGHT_LIGHT; case 4: return WEIGHT_SEMILIGHT; case 5: return WEIGHT_NORMAL; case 6: return WEIGHT_SEMIBOLD; case 7: return WEIGHT_BOLD; case 8: return WEIGHT_ULTRABOLD; case 9: return WEIGHT_BLACK; } return WEIGHT_DONTKNOW; } // ----------------------------------------------------------------------- static UniString ImpStyleNameToSal( const char* pFamilyName, const char* pFaceName, USHORT nLen ) { if ( !nLen ) nLen = strlen(pFamilyName); // strip FamilyName from FaceName if ( strncmp( pFamilyName, pFaceName, nLen ) == 0 ) { USHORT nFaceLen = (USHORT)strlen( pFaceName+nLen ); // Ist Facename laenger, schneiden wir den FamilyName ab if ( nFaceLen > 1 ) return UniString( pFaceName+(nLen+1), gsl_getSystemTextEncoding()); else return UniString(); } else return UniString( pFaceName, gsl_getSystemTextEncoding()); } // ----------------------------------------------------------------------- inline FontPitch ImplLogPitchToSal( PM_BYTE fsType ) { if ( fsType & FM_TYPE_FIXED ) return PITCH_FIXED; else return PITCH_VARIABLE; } // ----------------------------------------------------------------------- inline PM_BYTE ImplPitchToWin( FontPitch ePitch ) { if ( ePitch == PITCH_FIXED ) return FM_TYPE_FIXED; //else if ( ePitch == PITCH_VARIABLE ) return 0; } // ----------------------------------------------------------------------- static ImplDevFontAttributes Os2Font2DevFontAttributes( const PFONTMETRICS pFontMetric) { ImplDevFontAttributes aDFA; // get font face attributes aDFA.meFamily = ImplFamilyToSal( pFontMetric->panose.bFamilyType); aDFA.meWidthType = WIDTH_DONTKNOW; aDFA.meWeight = ImplWeightToSal( pFontMetric->usWeightClass); aDFA.meItalic = (pFontMetric->fsSelection & FM_SEL_ITALIC) ? ITALIC_NORMAL : ITALIC_NONE; aDFA.mePitch = ImplLogPitchToSal( pFontMetric->fsType ); aDFA.mbSymbolFlag = (pFontMetric->usCodePage == SYMBOL_CHARSET); // get the font face name // the maName field stores the font name without the style, so under OS/2 // we must use the family name aDFA.maName = UniString( pFontMetric->szFamilyname, gsl_getSystemTextEncoding()); aDFA.maStyleName = ImpStyleNameToSal( pFontMetric->szFamilyname, pFontMetric->szFacename, strlen( pFontMetric->szFamilyname) ); // get device specific font attributes aDFA.mbOrientation = (pFontMetric->fsDefn & FM_DEFN_OUTLINE) != 0; aDFA.mbDevice = (pFontMetric->fsDefn & FM_DEFN_GENERIC) ? FALSE : TRUE; aDFA.mbEmbeddable = false; aDFA.mbSubsettable = false; DWORD fontType = Ft2QueryFontType( 0, pFontMetric->szFamilyname); if( fontType == FT2_FONTTYPE_TRUETYPE && !aDFA.mbDevice) aDFA.mbSubsettable = true; // for now we can only embed Type1 fonts if( fontType == FT2_FONTTYPE_TYPE1 ) aDFA.mbEmbeddable = true; // heuristics for font quality // - standard-type1 > opentypeTT > truetype > non-standard-type1 > raster // - subsetting > embedding > none aDFA.mnQuality = 0; if( fontType == FT2_FONTTYPE_TRUETYPE ) aDFA.mnQuality += 50; if( aDFA.mbSubsettable ) aDFA.mnQuality += 200; else if( aDFA.mbEmbeddable ) aDFA.mnQuality += 100; // #i38665# prefer Type1 versions of the standard postscript fonts if( aDFA.mbEmbeddable ) { if( aDFA.maName.EqualsAscii( "AvantGarde" ) || aDFA.maName.EqualsAscii( "Bookman" ) || aDFA.maName.EqualsAscii( "Courier" ) || aDFA.maName.EqualsAscii( "Helvetica" ) || aDFA.maName.EqualsAscii( "NewCenturySchlbk" ) || aDFA.maName.EqualsAscii( "Palatino" ) || aDFA.maName.EqualsAscii( "Symbol" ) || aDFA.maName.EqualsAscii( "Times" ) || aDFA.maName.EqualsAscii( "ZapfChancery" ) || aDFA.maName.EqualsAscii( "ZapfDingbats" ) ) aDFA.mnQuality += 500; } // TODO: add alias names return aDFA; } // ======================================================================= // raw font data with a scoped lifetime class RawFontData { public: explicit RawFontData( HDC, DWORD nTableTag=0 ); ~RawFontData() { delete[] mpRawBytes; } const unsigned char* get() const { return mpRawBytes; } const unsigned char* steal() { unsigned char* p = mpRawBytes; mpRawBytes = NULL; return p; } const int size() const { return mnByteCount; } private: unsigned char* mpRawBytes; int mnByteCount; }; RawFontData::RawFontData( HPS hPS, DWORD nTableTag ) : mpRawBytes( NULL ) , mnByteCount( 0 ) { // get required size in bytes mnByteCount = ::Ft2GetFontData( hPS, nTableTag, 0, NULL, 0 ); if( mnByteCount == FT2_ERROR ) return; else if( !mnByteCount ) return; // allocate the array mpRawBytes = new unsigned char[ mnByteCount ]; // get raw data in chunks small enough for GetFontData() int nRawDataOfs = 0; DWORD nMaxChunkSize = 0x100000; for(;;) { // calculate remaining raw data to get DWORD nFDGet = mnByteCount - nRawDataOfs; if( nFDGet <= 0 ) break; // #i56745# limit GetFontData requests if( nFDGet > nMaxChunkSize ) nFDGet = nMaxChunkSize; const DWORD nFDGot = ::Ft2GetFontData( hPS, nTableTag, nRawDataOfs, (void*)(mpRawBytes + nRawDataOfs), nFDGet ); if( !nFDGot ) break; else if( nFDGot != FT2_ERROR ) nRawDataOfs += nFDGot; else { // was the chunk too big? reduce it nMaxChunkSize /= 2; if( nMaxChunkSize < 0x10000 ) break; } } // cleanup if the raw data is incomplete if( nRawDataOfs != mnByteCount ) { delete[] mpRawBytes; mpRawBytes = NULL; } } // ----------------------------------------------------------------------- // ======================================================================= ImplOs2FontData::ImplOs2FontData( PFONTMETRICS _pFontMetric, int nHeight, PM_BYTE nPitchAndFamily ) : ImplFontData( Os2Font2DevFontAttributes(_pFontMetric), 0 ), pFontMetric( _pFontMetric ), meOs2CharSet( _pFontMetric->usCodePage), mnPitchAndFamily( nPitchAndFamily ), mpFontCharSets( NULL ), mpUnicodeMap( NULL ), mbDisableGlyphApi( false ), mbHasKoreanRange( false ), mbHasCJKSupport( false ), mbAliasSymbolsLow( false ), mbAliasSymbolsHigh( false ), mnId( 0 ) { SetBitmapSize( 0, nHeight ); } // ----------------------------------------------------------------------- ImplOs2FontData::~ImplOs2FontData() { delete[] mpFontCharSets; if( mpUnicodeMap ) mpUnicodeMap->DeReference(); } // ----------------------------------------------------------------------- sal_IntPtr ImplOs2FontData::GetFontId() const { return mnId; } // ----------------------------------------------------------------------- void ImplOs2FontData::UpdateFromHPS( HPS hPS ) const { // short circuit if already initialized if( mpUnicodeMap != NULL ) return; ReadCmapTable( hPS ); ReadOs2Table( hPS ); // even if the font works some fonts have problems with the glyph API // => the heuristic below tries to figure out which fonts have the problem DWORD fontType = Ft2QueryFontType( 0, pFontMetric->szFacename); if( fontType != FT2_FONTTYPE_TRUETYPE && (pFontMetric->fsDefn & FM_DEFN_GENERIC) == 0) mbDisableGlyphApi = true; } // ----------------------------------------------------------------------- bool ImplOs2FontData::HasGSUBstitutions( HPS hPS ) const { if( !mbGsubRead ) ReadGsubTable( hPS ); return !maGsubTable.empty(); } // ----------------------------------------------------------------------- bool ImplOs2FontData::IsGSUBstituted( sal_Ucs cChar ) const { return( maGsubTable.find( cChar ) != maGsubTable.end() ); } // ----------------------------------------------------------------------- const ImplFontCharMap* ImplOs2FontData::GetImplFontCharMap() const { mpUnicodeMap->AddReference(); return mpUnicodeMap; } // ----------------------------------------------------------------------- static unsigned GetUInt( const unsigned char* p ) { return((p[0]<<24)+(p[1]<<16)+(p[2]<<8)+p[3]);} static unsigned GetUShort( const unsigned char* p ){ return((p[0]<<8)+p[1]);} static signed GetSShort( const unsigned char* p ){ return((short)((p[0]<<8)+p[1]));} static inline DWORD CalcTag( const char p[4]) { return (p[0]+(p[1]<<8)+(p[2]<<16)+(p[3]<<24)); } void ImplOs2FontData::ReadOs2Table( HPS hPS ) const { const DWORD Os2Tag = CalcTag( "OS/2" ); DWORD nLength = Ft2GetFontData( hPS, Os2Tag, 0, NULL, 0 ); if( (nLength == FT2_ERROR) || !nLength ) return; std::vector aOS2map( nLength ); unsigned char* pOS2map = &aOS2map[0]; DWORD nRC = Ft2GetFontData( hPS, Os2Tag, 0, pOS2map, nLength ); sal_uInt32 nVersion = GetUShort( pOS2map ); if ( nVersion >= 0x0001 && nLength >= 58 ) { // We need at least version 0x0001 (TrueType rev 1.66) // to have access to the needed struct members. sal_uInt32 ulUnicodeRange1 = GetUInt( pOS2map + 42 ); sal_uInt32 ulUnicodeRange2 = GetUInt( pOS2map + 46 ); #if 0 sal_uInt32 ulUnicodeRange3 = GetUInt( pOS2map + 50 ); sal_uInt32 ulUnicodeRange4 = GetUInt( pOS2map + 54 ); #endif // Check for CJK capabilities of the current font mbHasCJKSupport = (ulUnicodeRange2 & 0x2DF00000); mbHasKoreanRange= (ulUnicodeRange1 & 0x10000000) | (ulUnicodeRange2 & 0x01100000); mbHasArabicSupport = (ulUnicodeRange1 & 0x00002000); } } // ----------------------------------------------------------------------- void ImplOs2FontData::ReadGsubTable( HPS hPS ) const { mbGsubRead = true; // check the existence of a GSUB table const DWORD GsubTag = CalcTag( "GSUB" ); DWORD nRC = Ft2GetFontData( hPS, GsubTag, 0, NULL, 0 ); if( (nRC == FT2_ERROR) || !nRC ) return; // parse the GSUB table through sft // TODO: parse it directly // sft needs the full font file data => get it const RawFontData aRawFontData( hPS ); if( !aRawFontData.get() ) return; // open font file sal_uInt32 nFaceNum = 0; if( !*aRawFontData.get() ) // TTC candidate nFaceNum = ~0U; // indicate "TTC font extracts only" TrueTypeFont* pTTFont = NULL; ::OpenTTFontBuffer( (void*)aRawFontData.get(), aRawFontData.size(), nFaceNum, &pTTFont ); if( !pTTFont ) return; // add vertically substituted characters to list static const sal_Unicode aGSUBCandidates[] = { 0x0020, 0x0080, // ASCII 0x2000, 0x2600, // misc 0x3000, 0x3100, // CJK punctutation 0x3300, 0x3400, // squared words 0xFF00, 0xFFF0, // halfwidth|fullwidth forms 0 }; for( const sal_Unicode* pPair = aGSUBCandidates; *pPair; pPair += 2 ) for( sal_Unicode cChar = pPair[0]; cChar < pPair[1]; ++cChar ) if( ::MapChar( pTTFont, cChar, 0 ) != ::MapChar( pTTFont, cChar, 1 ) ) maGsubTable.insert( cChar ); // insert GSUBbed unicodes CloseTTFont( pTTFont ); } // ----------------------------------------------------------------------- void ImplOs2FontData::ReadCmapTable( HPS hPS ) const { if( mpUnicodeMap != NULL ) return; bool bIsSymbolFont = (meOs2CharSet == SYMBOL_CHARSET); // get the CMAP table from the font which is selected into the DC const DWORD nCmapTag = CalcTag( "cmap" ); const RawFontData aRawFontData( hPS, nCmapTag ); // parse the CMAP table if available if( aRawFontData.get() ) { CmapResult aResult; ParseCMAP( aRawFontData.get(), aRawFontData.size(), aResult ); mbDisableGlyphApi |= aResult.mbRecoded; aResult.mbSymbolic = bIsSymbolFont; if( aResult.mnRangeCount > 0 ) mpUnicodeMap = new ImplFontCharMap( aResult ); } if( !mpUnicodeMap ) mpUnicodeMap = ImplFontCharMap::GetDefaultMap( bIsSymbolFont ); mpUnicodeMap->AddReference(); } // ======================================================================= void Os2SalGraphics::SetTextColor( SalColor nSalColor ) { CHARBUNDLE cb; cb.lColor = MAKE_SALCOLOR( SALCOLOR_RED( nSalColor ), SALCOLOR_GREEN( nSalColor ), SALCOLOR_BLUE( nSalColor ) ); // set default color attributes Ft2SetAttrs( mhPS, PRIM_CHAR, CBB_COLOR, 0, &cb ); } // ----------------------------------------------------------------------- USHORT Os2SalGraphics::ImplDoSetFont( ImplFontSelectData* i_pFont, float& o_rFontScale, int nFallbackLevel) { #if OSL_DEBUG_LEVEL>10 debug_printf( "Os2SalGraphics::ImplDoSetFont mbPrinter=%d\n", mbPrinter); #endif ImplOs2FontData* pFontData = (ImplOs2FontData*)i_pFont->mpFontData; PFONTMETRICS pFontMetric = NULL; FATTRS aFAttrs; PM_BOOL bOutline = FALSE; APIRET rc; memset( &aFAttrs, 0, sizeof( FATTRS ) ); aFAttrs.usRecordLength = sizeof( FATTRS ); aFAttrs.lMaxBaselineExt = i_pFont->mnHeight; aFAttrs.lAveCharWidth = i_pFont->mnWidth; // do we have a pointer to the FONTMETRICS of the selected font? -> use it! if ( pFontData ) { pFontMetric = pFontData->GetFontMetrics(); bOutline = (pFontMetric->fsDefn & FM_DEFN_OUTLINE) != 0; // use match®istry fields to get correct match aFAttrs.lMatch = pFontMetric->lMatch; aFAttrs.idRegistry = pFontMetric->idRegistry; aFAttrs.usCodePage = pFontMetric->usCodePage; if ( bOutline ) { aFAttrs.fsFontUse |= FATTR_FONTUSE_OUTLINE; if ( i_pFont->mnOrientation ) aFAttrs.fsFontUse |= FATTR_FONTUSE_TRANSFORMABLE; } else { aFAttrs.lMaxBaselineExt = pFontMetric->lMaxBaselineExt; aFAttrs.lAveCharWidth = pFontMetric->lAveCharWidth; } } #if OSL_DEBUG_LEVEL>1 if (pFontMetric->szFacename[0] == 'D') { rc = 0; // debugger breakpoint } #endif // use family name for outline fonts if ( mbPrinter ) { // use font face name for printers because otherwise ft2lib will fail // to select the correct font for GPI (ticket#117) strncpy( (char*)(aFAttrs.szFacename), pFontMetric->szFacename, sizeof( aFAttrs.szFacename ) ); } else if ( !pFontMetric) { // use OOo name if fontmetrics not available! ByteString aName( i_pFont->maName.GetToken( 0 ), gsl_getSystemTextEncoding()); strncpy( (char*)(aFAttrs.szFacename), aName.GetBuffer(), sizeof( aFAttrs.szFacename ) ); } else if ( bOutline) { // use fontmetric family name for outline fonts strncpy( (char*)(aFAttrs.szFacename), pFontMetric->szFamilyname, sizeof( aFAttrs.szFacename ) ); } else { // use real font face name for bitmaps (WarpSans only) strncpy( (char*)(aFAttrs.szFacename), pFontMetric->szFacename, sizeof( aFAttrs.szFacename ) ); } if ( i_pFont->meItalic != ITALIC_NONE ) aFAttrs.fsSelection |= FATTR_SEL_ITALIC; if ( i_pFont->meWeight > WEIGHT_MEDIUM ) aFAttrs.fsSelection |= FATTR_SEL_BOLD; #if OSL_DEBUG_LEVEL>1 if (pFontMetric->szFacename[0] == 'A') { debug_printf( "Os2SalGraphics::SetFont hps %x lMatch '%d'\n", mhPS, pFontMetric->lMatch); debug_printf( "Os2SalGraphics::SetFont hps %x fontmetrics facename '%s'\n", mhPS, pFontMetric->szFacename); debug_printf( "Os2SalGraphics::SetFont hps %x fattrs facename '%s'\n", mhPS, aFAttrs.szFacename); debug_printf( "Os2SalGraphics::SetFont hps %x fattrs height '%d'\n", mhPS, aFAttrs.lMaxBaselineExt); debug_printf( "Os2SalGraphics::SetFont hps %x fattrs width '%d'\n", mhPS, aFAttrs.lAveCharWidth); } #endif // set default font rc = Ft2SetCharSet( mhPS, LCID_DEFAULT); // delete selected font rc = Ft2DeleteSetId( mhPS, nFallbackLevel + LCID_BASE); // create new logical font if ( (rc=Ft2CreateLogFont( mhPS, NULL, nFallbackLevel + LCID_BASE, &aFAttrs)) == GPI_ERROR ) { #if OSL_DEBUG_LEVEL>1 ERRORID nLastError = WinGetLastError( GetSalData()->mhAB ); debug_printf( "Os2SalGraphics::SetFont hps %x Ft2CreateLogFont failed err %x\n", mhPS, nLastError ); #endif return SAL_SETFONT_REMOVEANDMATCHNEW; } #if OSL_DEBUG_LEVEL>1 // select new font rc = Ft2SetCharSet( mhPS, nFallbackLevel + LCID_BASE); // query fontmetric of new font FONTMETRICS aOS2Metric = {0}; rc = Ft2QueryFontMetrics( mhPS, sizeof( aOS2Metric ), &aOS2Metric ); if (pFontMetric->szFacename[0] == 'D') { debug_printf( "Os2SalGraphics::SetFont Ft2QueryFontMetrics fattrs facename '%s'\n", aOS2Metric.szFacename); debug_printf( "Os2SalGraphics::SetFont Ft2QueryFontMetrics fattrs height '%d'\n", aOS2Metric.lMaxBaselineExt); debug_printf( "Os2SalGraphics::SetFont Ft2QueryFontMetrics fattrs width '%d'\n", aOS2Metric.lAveCharWidth); } #endif // apply font sizing, rotation CHARBUNDLE aBundle; ULONG nAttrsDefault = 0; ULONG nAttrs = CBB_SET; aBundle.usSet = nFallbackLevel + LCID_BASE; if ( bOutline ) { nAttrs |= CBB_BOX; aBundle.sizfxCell.cy = MAKEFIXED( i_pFont->mnHeight, 0 ); if ( !i_pFont->mnWidth ) { LONG nXFontRes; LONG nYFontRes; LONG nHeight; // Auf die Aufloesung achten, damit das Ergebnis auch auf // Drucken mit 180*360 DPI stimmt. Ausserdem muss gerundet // werden, da auf meinem OS2 beispielsweise als // Bildschirmaufloesung 3618*3622 PixelPerMeter zurueck- // gegeben wird GetResolution( nXFontRes, nYFontRes ); nHeight = i_pFont->mnHeight; nHeight *= nXFontRes; nHeight += nYFontRes/2; nHeight /= nYFontRes; aBundle.sizfxCell.cx = MAKEFIXED( nHeight, 0 ); } else aBundle.sizfxCell.cx = MAKEFIXED( i_pFont->mnWidth, 0 ); } // set orientation for outlinefonts if ( i_pFont->mnOrientation ) { if ( bOutline ) { nAttrs |= CBB_ANGLE; double alpha = (double)(i_pFont->mnOrientation); alpha *= 0.0017453292; // *PI / 1800 mnOrientationY = (long) (1000.0 * sin( alpha )); mnOrientationX = (long) (1000.0 * cos( alpha )); aBundle.ptlAngle.x = mnOrientationX; aBundle.ptlAngle.y = mnOrientationY; } else { mnOrientationX = 1; mnOrientationY = 0; nAttrs |= CBB_ANGLE; aBundle.ptlAngle.x = 1; aBundle.ptlAngle.y = 0; } } else { mnOrientationX = 1; mnOrientationY = 0; nAttrs |= CBB_ANGLE; aBundle.ptlAngle.x = 1; aBundle.ptlAngle.y = 0; } rc = Ft2SetAttrs( mhPS, PRIM_CHAR, nAttrs, nAttrsDefault, &aBundle ); #if OSL_DEBUG_LEVEL>1 rc = Ft2QueryFontMetrics( mhPS, sizeof( aOS2Metric ), &aOS2Metric ); if (pFontMetric->szFacename[0] == 'D') { debug_printf( "Os2SalGraphics::SetFont Ft2QueryFontMetrics fattrs facename '%s'\n", aOS2Metric.szFacename); debug_printf( "Os2SalGraphics::SetFont Ft2QueryFontMetrics fattrs height '%d'\n", aOS2Metric.lMaxBaselineExt); debug_printf( "Os2SalGraphics::SetFont Ft2QueryFontMetrics fattrs width '%d'\n", aOS2Metric.lAveCharWidth); } #endif return 0; } USHORT Os2SalGraphics::SetFont( ImplFontSelectData* pFont, int nFallbackLevel ) { // return early if there is no new font if( !pFont ) { #if 0 // deselect still active font if( mhDefFont ) Ft2SetCharSet( mhPS, mhDefFont ); // release no longer referenced font handles for( int i = nFallbackLevel; i < MAX_FALLBACK; ++i ) { if( mhFonts[i] ) Ft2DeleteSetId( mhPS, mhFonts[i] ); mhFonts[ i ] = 0; } #endif mhDefFont = 0; return 0; } #if OSL_DEBUG_LEVEL>10 debug_printf( "Os2SalGraphics::SetFont\n"); #endif DBG_ASSERT( pFont->mpFontData, "WinSalGraphics mpFontData==NULL"); mpOs2FontEntry[ nFallbackLevel ] = reinterpret_cast( pFont->mpFontEntry ); mpOs2FontData[ nFallbackLevel ] = static_cast( pFont->mpFontData ); ImplDoSetFont( pFont, mfFontScale, nFallbackLevel); if( !mhDefFont ) { // keep default font mhDefFont = nFallbackLevel + LCID_BASE; } else { // release no longer referenced font handles for( int i = nFallbackLevel; i < MAX_FALLBACK; ++i ) { if( mhFonts[i] ) { #if 0 Ft2DeleteSetId( mhPS, mhFonts[i] ); #endif mhFonts[i] = 0; } } } // store new font in correct layer mhFonts[ nFallbackLevel ] = nFallbackLevel + LCID_BASE; // now the font is live => update font face if( mpOs2FontData[ nFallbackLevel ] ) mpOs2FontData[ nFallbackLevel ]->UpdateFromHPS( mhPS ); if( !nFallbackLevel ) { mbFontKernInit = TRUE; if ( mpFontKernPairs ) { delete[] mpFontKernPairs; mpFontKernPairs = NULL; } mnFontKernPairCount = 0; } // some printers have higher internal resolution, so their // text output would be different from what we calculated // => suggest DrawTextArray to workaround this problem if ( mbPrinter ) return SAL_SETFONT_USEDRAWTEXTARRAY; else return 0; } // ----------------------------------------------------------------------- void Os2SalGraphics::GetFontMetric( ImplFontMetricData* pMetric, int nFallbackLevel ) { FONTMETRICS aOS2Metric; Ft2QueryFontMetrics( mhPS, sizeof( aOS2Metric ), &aOS2Metric ); // @TODO sync , int nFallbackLevel in 340 #if OSL_DEBUG_LEVEL>1 debug_printf( "Os2SalGraphics::GetFontMetric hps %x\n", mhPS); if (aOS2Metric.szFacename[0] == 'A') { debug_printf( "Os2SalGraphics::GetFontMetric hps %x fontmetrics facename '%s'\n", mhPS, aOS2Metric.szFacename); debug_printf( "Os2SalGraphics::GetFontMetric hps %x fontmetrics lMatch '%d'\n", mhPS, aOS2Metric.lMatch); } #endif pMetric->maName = UniString( aOS2Metric.szFamilyname, gsl_getSystemTextEncoding()); pMetric->maStyleName = ImpStyleNameToSal( aOS2Metric.szFamilyname, aOS2Metric.szFacename, strlen( aOS2Metric.szFamilyname ) ); // device independent font attributes pMetric->meFamily = ImplFamilyToSal( aOS2Metric.panose.bFamilyType); pMetric->mbSymbolFlag = (aOS2Metric.usCodePage == SYMBOL_CHARSET); pMetric->meWeight = ImplWeightToSal( aOS2Metric.usWeightClass ); pMetric->mePitch = ImplLogPitchToSal( aOS2Metric.fsType ); pMetric->meItalic = (aOS2Metric.fsSelection & FM_SEL_ITALIC) ? ITALIC_NORMAL : ITALIC_NONE; pMetric->mnSlant = 0; // device dependend font attributes pMetric->mbDevice = (aOS2Metric.fsDefn & FM_DEFN_GENERIC) ? FALSE : TRUE; pMetric->mbScalableFont = (aOS2Metric.fsDefn & FM_DEFN_OUTLINE) ? true : false; if( pMetric->mbScalableFont ) { // check if there are kern pairs // TODO: does this work with GPOS kerning? pMetric->mbKernableFont = (aOS2Metric.sKerningPairs > 0); } else { // bitmap fonts cannot be rotated directly pMetric->mnOrientation = 0; // bitmap fonts have no kerning pMetric->mbKernableFont = false; } // transformation dependend font metrics if ( aOS2Metric.fsDefn & FM_DEFN_OUTLINE ) { pMetric->mnWidth = aOS2Metric.lEmInc; } else { pMetric->mnWidth = aOS2Metric.lAveCharWidth; pMetric->mnOrientation = 0; } pMetric->mnIntLeading = aOS2Metric.lInternalLeading; pMetric->mnExtLeading = aOS2Metric.lExternalLeading; pMetric->mnAscent = aOS2Metric.lMaxAscender; pMetric->mnDescent = aOS2Metric.lMaxDescender; // #107888# improved metric compatibility for Asian fonts... // TODO: assess workaround below for CWS >= extleading // TODO: evaluate use of aWinMetric.sTypo* members for CJK if( mpOs2FontData[0] && mpOs2FontData[0]->SupportsCJK() ) { pMetric->mnIntLeading += pMetric->mnExtLeading; // #109280# The line height for Asian fonts is too small. // Therefore we add half of the external leading to the // ascent, the other half is added to the descent. const long nHalfTmpExtLeading = pMetric->mnExtLeading / 2; const long nOtherHalfTmpExtLeading = pMetric->mnExtLeading - nHalfTmpExtLeading; // #110641# external leading for Asian fonts. // The factor 0.3 has been confirmed with experiments. long nCJKExtLeading = static_cast(0.30 * (pMetric->mnAscent + pMetric->mnDescent)); nCJKExtLeading -= pMetric->mnExtLeading; pMetric->mnExtLeading = (nCJKExtLeading > 0) ? nCJKExtLeading : 0; pMetric->mnAscent += nHalfTmpExtLeading; pMetric->mnDescent += nOtherHalfTmpExtLeading; // #109280# HACK korean only: increase descent for wavelines and impr // YD win9x only } } // ----------------------------------------------------------------------- ULONG Os2SalGraphics::GetKernPairs( ULONG nPairs, ImplKernPairData* pKernPairs ) { DBG_ASSERT( sizeof( KERNINGPAIRS ) == sizeof( ImplKernPairData ), "Os2SalGraphics::GetKernPairs(): KERNINGPAIRS != ImplKernPairData" ); if ( mbFontKernInit ) { if( mpFontKernPairs ) { delete[] mpFontKernPairs; mpFontKernPairs = NULL; } mnFontKernPairCount = 0; { KERNINGPAIRS* pPairs = NULL; FONTMETRICS aOS2Metric; Ft2QueryFontMetrics( mhPS, sizeof( aOS2Metric ), &aOS2Metric ); int nCount = aOS2Metric.sKerningPairs; if( nCount ) { #ifdef GCP_KERN_HACK pPairs = new KERNINGPAIRS[ nCount+1 ]; mpFontKernPairs = pPairs; mnFontKernPairCount = nCount; Ft2QueryKerningPairs( mhPS, nCount, (KERNINGPAIRS*)pPairs ); #else // GCP_KERN_HACK pPairs = (KERNINGPAIRS*)pKernPairs; nCount = (nCount < nPairs) ? nCount : nPairs; Ft2QueryKerningPairs( mhPS, nCount, (KERNINGPAIRS*)pPairs ); return nCount; #endif // GCP_KERN_HACK } } mbFontKernInit = FALSE; std::sort( mpFontKernPairs, mpFontKernPairs + mnFontKernPairCount, ImplCmpKernData ); } if( !pKernPairs ) return mnFontKernPairCount; else if( mpFontKernPairs ) { if ( nPairs < mnFontKernPairCount ) nPairs = mnFontKernPairCount; memcpy( pKernPairs, mpFontKernPairs, nPairs*sizeof( ImplKernPairData ) ); return nPairs; } return 0; } // ----------------------------------------------------------------------- static const ImplFontCharMap* pOs2DefaultImplFontCharMap = NULL; static const sal_uInt32 pOs2DefaultRangeCodes[] = {0x0020,0x00FF}; const ImplFontCharMap* Os2SalGraphics::GetImplFontCharMap() const { if( !mpOs2FontData[0] ) return ImplFontCharMap::GetDefaultMap(); return mpOs2FontData[0]->GetImplFontCharMap(); } // ----------------------------------------------------------------------- bool Os2SalGraphics::AddTempDevFont( ImplDevFontList* pFontList, const String& rFontFileURL, const String& rFontName ) { #if OSL_DEBUG_LEVEL>0 debug_printf("Os2SalGraphics::AddTempDevFont\n"); #endif return false; } // ----------------------------------------------------------------------- void Os2SalGraphics::GetDevFontList( ImplDevFontList* pList ) { PFONTMETRICS pFontMetrics; ULONG nFontMetricCount; SalData* pSalData; #if OSL_DEBUG_LEVEL>0 debug_printf("Os2SalGraphics::GetDevFontList mbPrinter=%d\n", mbPrinter); #endif // install OpenSymbol HMODULE hMod; ULONG ObjNum, Offset, rc; CHAR Buff[2*_MAX_PATH]; char drive[_MAX_DRIVE], dir[_MAX_DIR]; char fname[_MAX_FNAME], ext[_MAX_EXT]; // get module handle (and name) rc = DosQueryModFromEIP( &hMod, &ObjNum, sizeof( Buff), Buff, &Offset, (ULONG)ImplSalGetUniString); DosQueryModuleName(hMod, sizeof(Buff), Buff); // replace module path with font path char* slash = strrchr( Buff, '\\'); *slash = '\0'; slash = strrchr( Buff, '\\'); *slash = '\0'; strcat( Buff, "\\FONTS\\OPENS___.TTF"); rc = GpiLoadPublicFonts( GetSalData()->mhAB, Buff); if ( !mbPrinter ) { // Bei Bildschirm-Devices cachen wir die Liste global, da // dies im unabhaengigen Teil auch so gemacht wird und wir // ansonsten auf geloeschten Systemdaten arbeiten koennten pSalData = GetSalData(); nFontMetricCount = pSalData->mnFontMetricCount; pFontMetrics = pSalData->mpFontMetrics; // Bei Bildschirm-Devices holen wir uns die Fontliste jedesmal neu if ( pFontMetrics ) { delete pFontMetrics; pFontMetrics = NULL; nFontMetricCount = 0; } } else { nFontMetricCount = mnFontMetricCount; pFontMetrics = mpFontMetrics; } // do we have to create the cached font list first? if ( !pFontMetrics ) { // query the number of fonts available LONG nTemp = 0; nFontMetricCount = Ft2QueryFonts( mhPS, QF_PUBLIC | QF_PRIVATE | QF_NO_DEVICE, NULL, &nTemp, sizeof( FONTMETRICS ), NULL ); // procede only if at least one is available! if ( nFontMetricCount ) { // allocate memory for font list pFontMetrics = new FONTMETRICS[nFontMetricCount]; // query font list Ft2QueryFonts( mhPS, QF_PUBLIC | QF_PRIVATE | QF_NO_DEVICE, NULL, (PLONG)&nFontMetricCount, (LONG) sizeof( FONTMETRICS ), pFontMetrics ); } if ( !mbPrinter ) { pSalData->mnFontMetricCount = nFontMetricCount; pSalData->mpFontMetrics = pFontMetrics; } else { mnFontMetricCount = nFontMetricCount; mpFontMetrics = pFontMetrics; } } // copy data from the font list for( ULONG i = 0; i < nFontMetricCount; i++ ) { PFONTMETRICS pFontMetric = &pFontMetrics[i]; #if OSL_DEBUG_LEVEL>2 debug_printf("Os2SalGraphics::GetDevFontList #%d,'%s'\n", i, pFontMetric->szFacename); #endif // skip font starting with '@', this is an alias internally // used by truetype engine. if (pFontMetric->szFacename[0] == '@') continue; // skip bitmap fonts (but keep WarpSans) if ( (pFontMetric->fsDefn & FM_DEFN_OUTLINE) == 0 && strncmp( pFontMetric->szFacename, "WarpSans", 8) ) // Font nicht aufnehmen continue; // replace '-' in facename with ' ' (for ft2lib) char* dash = pFontMetric->szFacename; while( (dash=strchr( dash, '-'))) *dash++ = ' '; // create new font list element ImplOs2FontData* pData = new ImplOs2FontData( pFontMetric, 0, 0 ); // ticket#80: font id field is used for pdf font cache code. pData->SetFontId( i); // add font list element to font list pList->Add( pData ); } } // ---------------------------------------------------------------------------- void Os2SalGraphics::GetDevFontSubstList( OutputDevice* pOutDev ) { } // ----------------------------------------------------------------------- bool Os2SalGraphics::GetGlyphBoundRect( sal_GlyphId aGlyphId, Rectangle& rRect ) { // use unity matrix MAT2 aMat; aMat.eM11 = aMat.eM22 = FixedFromDouble( 1.0 ); aMat.eM12 = aMat.eM21 = FixedFromDouble( 0.0 ); UINT nGGOFlags = GGO_METRICS; if( !(aGlyphId & GF_ISCHAR) ) nGGOFlags |= GGO_GLYPH_INDEX; aGlyphId &= GF_IDXMASK; GLYPHMETRICS aGM; DWORD nSize = FT2_ERROR; nSize = Ft2GetGlyphOutline( mhPS, aGlyphId, nGGOFlags, &aGM, 0, NULL, &aMat ); if( nSize == FT2_ERROR ) return false; rRect = Rectangle( Point( +aGM.gmptGlyphOrigin.x, -aGM.gmptGlyphOrigin.y ), Size( aGM.gmBlackBoxX, aGM.gmBlackBoxY ) ); rRect.Left() = static_cast( mfFontScale * rRect.Left() ); rRect.Right() = static_cast( mfFontScale * rRect.Right() ); rRect.Top() = static_cast( mfFontScale * rRect.Top() ); rRect.Bottom() = static_cast( mfFontScale * rRect.Bottom() ); return true; } // ----------------------------------------------------------------------- bool Os2SalGraphics::GetGlyphOutline( sal_GlyphId aGlyphId, ::basegfx::B2DPolyPolygon& rB2DPolyPoly ) { #if OSL_DEBUG_LEVEL>0 debug_printf("Os2SalGraphics::GetGlyphOutline\n"); #endif rB2DPolyPoly.clear(); PM_BOOL bRet = FALSE; // use unity matrix MAT2 aMat; aMat.eM11 = aMat.eM22 = FixedFromDouble( 1.0 ); aMat.eM12 = aMat.eM21 = FixedFromDouble( 0.0 ); UINT nGGOFlags = GGO_NATIVE; if( !(aGlyphId & GF_ISCHAR) ) nGGOFlags |= GGO_GLYPH_INDEX; aGlyphId &= GF_IDXMASK; GLYPHMETRICS aGlyphMetrics; DWORD nSize1 = FT2_ERROR; nSize1 = Ft2GetGlyphOutline( mhPS, aGlyphId, nGGOFlags, &aGlyphMetrics, 0, NULL, &aMat ); if( !nSize1 ) // blank glyphs are ok bRet = TRUE; else if( nSize1 != FT2_ERROR ) { PM_BYTE* pData = new PM_BYTE[ nSize1 ]; ULONG nTotalCount = 0; DWORD nSize2; nSize2 = Ft2GetGlyphOutline( mhPS, aGlyphId, nGGOFlags, &aGlyphMetrics, nSize1, pData, &aMat ); if( nSize1 == nSize2 ) { bRet = TRUE; int nPtSize = 512; Point* pPoints = new Point[ nPtSize ]; sal_uInt8* pFlags = new sal_uInt8[ nPtSize ]; TTPOLYGONHEADER* pHeader = (TTPOLYGONHEADER*)pData; while( (PM_BYTE*)pHeader < pData+nSize2 ) { // only outline data is interesting if( pHeader->dwType != TT_POLYGON_TYPE ) break; // get start point; next start points are end points // of previous segment int nPnt = 0; long nX = IntTimes256FromFixed( pHeader->pfxStart.x ); long nY = IntTimes256FromFixed( pHeader->pfxStart.y ); pPoints[ nPnt ] = Point( nX, nY ); pFlags[ nPnt++ ] = POLY_NORMAL; bool bHasOfflinePoints = false; TTPOLYCURVE* pCurve = (TTPOLYCURVE*)( pHeader + 1 ); pHeader = (TTPOLYGONHEADER*)( (PM_BYTE*)pHeader + pHeader->cb ); while( (PM_BYTE*)pCurve < (PM_BYTE*)pHeader ) { int nNeededSize = nPnt + 16 + 3 * pCurve->cpfx; if( nPtSize < nNeededSize ) { Point* pOldPoints = pPoints; sal_uInt8* pOldFlags = pFlags; nPtSize = 2 * nNeededSize; pPoints = new Point[ nPtSize ]; pFlags = new sal_uInt8[ nPtSize ]; for( int i = 0; i < nPnt; ++i ) { pPoints[ i ] = pOldPoints[ i ]; pFlags[ i ] = pOldFlags[ i ]; } delete[] pOldPoints; delete[] pOldFlags; } int i = 0; if( TT_PRIM_LINE == pCurve->wType ) { while( i < pCurve->cpfx ) { nX = IntTimes256FromFixed( pCurve->apfx[ i ].x ); nY = IntTimes256FromFixed( pCurve->apfx[ i ].y ); ++i; pPoints[ nPnt ] = Point( nX, nY ); pFlags[ nPnt ] = POLY_NORMAL; ++nPnt; } } else if( TT_PRIM_QSPLINE == pCurve->wType ) { bHasOfflinePoints = true; while( i < pCurve->cpfx ) { // get control point of quadratic bezier spline nX = IntTimes256FromFixed( pCurve->apfx[ i ].x ); nY = IntTimes256FromFixed( pCurve->apfx[ i ].y ); ++i; Point aControlP( nX, nY ); // calculate first cubic control point // P0 = 1/3 * (PBeg + 2 * PQControl) nX = pPoints[ nPnt-1 ].X() + 2 * aControlP.X(); nY = pPoints[ nPnt-1 ].Y() + 2 * aControlP.Y(); pPoints[ nPnt+0 ] = Point( (2*nX+3)/6, (2*nY+3)/6 ); pFlags[ nPnt+0 ] = POLY_CONTROL; // calculate endpoint of segment nX = IntTimes256FromFixed( pCurve->apfx[ i ].x ); nY = IntTimes256FromFixed( pCurve->apfx[ i ].y ); if ( i+1 >= pCurve->cpfx ) { // endpoint is either last point in segment => advance ++i; } else { // or endpoint is the middle of two control points nX += IntTimes256FromFixed( pCurve->apfx[ i-1 ].x ); nY += IntTimes256FromFixed( pCurve->apfx[ i-1 ].y ); nX = (nX + 1) / 2; nY = (nY + 1) / 2; // no need to advance, because the current point // is the control point in next bezier spline } pPoints[ nPnt+2 ] = Point( nX, nY ); pFlags[ nPnt+2 ] = POLY_NORMAL; // calculate second cubic control point // P1 = 1/3 * (PEnd + 2 * PQControl) nX = pPoints[ nPnt+2 ].X() + 2 * aControlP.X(); nY = pPoints[ nPnt+2 ].Y() + 2 * aControlP.Y(); pPoints[ nPnt+1 ] = Point( (2*nX+3)/6, (2*nY+3)/6 ); pFlags[ nPnt+1 ] = POLY_CONTROL; nPnt += 3; } } // next curve segment pCurve = (TTPOLYCURVE*)&pCurve->apfx[ i ]; } // end point is start point for closed contour // disabled, because Polygon class closes the contour itself // pPoints[nPnt++] = pPoints[0]; // #i35928# // Added again, but add only when not yet closed if(pPoints[nPnt - 1] != pPoints[0]) { if( bHasOfflinePoints ) pFlags[nPnt] = pFlags[0]; pPoints[nPnt++] = pPoints[0]; } // convert y-coordinates W32 -> VCL for( int i = 0; i < nPnt; ++i ) pPoints[i].Y() = -pPoints[i].Y(); // insert into polypolygon Polygon aPoly( nPnt, pPoints, (bHasOfflinePoints ? pFlags : NULL) ); // convert to B2DPolyPolygon // TODO: get rid of the intermediate PolyPolygon rB2DPolyPoly.append( aPoly.getB2DPolygon() ); } delete[] pPoints; delete[] pFlags; } delete[] pData; } // rescaling needed for the PolyPolygon conversion if( rB2DPolyPoly.count() ) { ::basegfx::B2DHomMatrix aMatrix; aMatrix.scale( 1.0/256, 1.0/256 ); aMatrix.scale( mfFontScale, mfFontScale ); rB2DPolyPoly.transform( aMatrix ); } return bRet; } // ----------------------------------------------------------------------- // TODO: Replace this class with boost::scoped_array class ScopedCharArray { public: inline explicit ScopedCharArray(char * pArray): m_pArray(pArray) {} inline ~ScopedCharArray() { delete[] m_pArray; } inline char * get() const { return m_pArray; } private: char * m_pArray; }; class ScopedFont { public: explicit ScopedFont(Os2SalGraphics & rData); ~ScopedFont(); private: Os2SalGraphics & m_rData; ULONG m_hOrigFont; }; ScopedFont::ScopedFont(Os2SalGraphics & rData): m_rData(rData) { #if 0 m_hOrigFont = m_rData.mhFonts[0]; m_rData.mhFonts[0] = 0; // avoid deletion of current font #endif } ScopedFont::~ScopedFont() { #if 0 if( m_hOrigFont ) { // restore original font, destroy temporary font HFONT hTempFont = m_rData.mhFonts[0]; m_rData.mhFonts[0] = m_hOrigFont; SelectObject( m_rData.mhDC, m_hOrigFont ); DeleteObject( hTempFont ); } #endif } class ScopedTrueTypeFont { public: inline ScopedTrueTypeFont(): m_pFont(0) {} ~ScopedTrueTypeFont(); int open(void * pBuffer, sal_uInt32 nLen, sal_uInt32 nFaceNum); inline TrueTypeFont * get() const { return m_pFont; } private: TrueTypeFont * m_pFont; }; ScopedTrueTypeFont::~ScopedTrueTypeFont() { if (m_pFont != 0) CloseTTFont(m_pFont); } int ScopedTrueTypeFont::open(void * pBuffer, sal_uInt32 nLen, sal_uInt32 nFaceNum) { OSL_ENSURE(m_pFont == 0, "already open"); return OpenTTFontBuffer(pBuffer, nLen, nFaceNum, &m_pFont); } sal_Bool Os2SalGraphics::CreateFontSubset( const rtl::OUString& rToFile, const ImplFontData* pFont, sal_GlyphId* pGlyphIds, sal_uInt8* pEncoding, sal_Int32* pGlyphWidths, int nGlyphCount, FontSubsetInfo& rInfo ) { // TODO: use more of the central font-subsetting code, move stuff there if needed // create matching ImplFontSelectData // we need just enough to get to the font file data // use height=1000 for easier debugging (to match psprint's font units) ImplFontSelectData aIFSD( *pFont, Size(0,1000), 1000.0, 0, false ); // TODO: much better solution: move SetFont and restoration of old font to caller ScopedFont aOldFont(*this); SetFont( &aIFSD, 0 ); ImplOs2FontData* pWinFontData = (ImplOs2FontData*)aIFSD.mpFontData; pWinFontData->UpdateFromHPS( mhPS ); const ImplFontCharMap* pImplFontCharMap = pWinFontData->GetImplFontCharMap(); #if OSL_DEBUG_LEVEL > 100 // get font metrics TEXTMETRICA aWinMetric; if( !::GetTextMetricsA( mhDC, &aWinMetric ) ) return FALSE; DBG_ASSERT( !(aWinMetric.tmPitchAndFamily & TMPF_DEVICE), "cannot subset device font" ); DBG_ASSERT( aWinMetric.tmPitchAndFamily & TMPF_TRUETYPE, "can only subset TT font" ); #endif rtl::OUString aSysPath; if( osl_File_E_None != osl_getSystemPathFromFileURL( rToFile.pData, &aSysPath.pData ) ) return FALSE; const rtl_TextEncoding aThreadEncoding = osl_getThreadTextEncoding(); const ByteString aToFile( aSysPath.getStr(), (xub_StrLen)aSysPath.getLength(), aThreadEncoding ); // check if the font has a CFF-table const DWORD nCffTag = CalcTag( "CFF " ); const RawFontData aRawCffData( mhPS, nCffTag ); if( aRawCffData.get() ) { sal_GlyphId aRealGlyphIds[ 256 ]; for( int i = 0; i < nGlyphCount; ++i ) { // TODO: remap notdef glyph if needed // TODO: use GDI's GetGlyphIndices instead? Does it handle GSUB properly? sal_uInt32 aGlyphId = pGlyphIds[i] & GF_IDXMASK; if( pGlyphIds[i] & GF_ISCHAR ) // remaining pseudo-glyphs need to be translated aGlyphId = pImplFontCharMap->GetGlyphIndex( aGlyphId ); if( (pGlyphIds[i] & (GF_ROTMASK|GF_GSUB)) != 0) // TODO: vertical substitution {/*####*/} aRealGlyphIds[i] = aGlyphId; } // provide a font subset from the CFF-table FILE* pOutFile = fopen( aToFile.GetBuffer(), "wb" ); rInfo.LoadFont( FontSubsetInfo::CFF_FONT, aRawCffData.get(), aRawCffData.size() ); bool bRC = rInfo.CreateFontSubset( FontSubsetInfo::TYPE1_PFB, pOutFile, NULL, aRealGlyphIds, pEncoding, nGlyphCount, pGlyphWidths ); fclose( pOutFile ); return bRC; } // get raw font file data const RawFontData xRawFontData( mhPS, NULL ); if( !xRawFontData.get() ) return FALSE; // open font file sal_uInt32 nFaceNum = 0; if( !*xRawFontData.get() ) // TTC candidate nFaceNum = ~0U; // indicate "TTC font extracts only" ScopedTrueTypeFont aSftTTF; int nRC = aSftTTF.open( (void*)xRawFontData.get(), xRawFontData.size(), nFaceNum ); if( nRC != SF_OK ) return FALSE; TTGlobalFontInfo aTTInfo; ::GetTTGlobalFontInfo( aSftTTF.get(), &aTTInfo ); rInfo.m_nFontType = FontSubsetInfo::SFNT_TTF; rInfo.m_aPSName = ImplSalGetUniString( aTTInfo.psname ); rInfo.m_nAscent = +aTTInfo.winAscent; rInfo.m_nDescent = -aTTInfo.winDescent; rInfo.m_aFontBBox = Rectangle( Point( aTTInfo.xMin, aTTInfo.yMin ), Point( aTTInfo.xMax, aTTInfo.yMax ) ); rInfo.m_nCapHeight = aTTInfo.yMax; // Well ... // subset TTF-glyphs and get their properties // take care that subset fonts require the NotDef glyph in pos 0 int nOrigCount = nGlyphCount; USHORT aShortIDs[ 256 ]; sal_uInt8 aTempEncs[ 256 ]; int nNotDef=-1, i; for( i = 0; i < nGlyphCount; ++i ) { aTempEncs[i] = pEncoding[i]; sal_GlyphId aGlyphId = pGlyphIds[i] & GF_IDXMASK; if( pGlyphIds[i] & GF_ISCHAR ) { sal_Unicode cChar = static_cast(aGlyphId); // TODO: sal_UCS4 const bool bVertical = ((pGlyphIds[i] & (GF_ROTMASK|GF_GSUB)) != 0); aGlyphId = ::MapChar( aSftTTF.get(), cChar, bVertical ); if( (aGlyphId == 0) && pFont->IsSymbolFont() ) { // #i12824# emulate symbol aliasing U+FXXX <-> U+0XXX cChar = (cChar & 0xF000) ? (cChar & 0x00FF) : (cChar | 0xF000); aGlyphId = ::MapChar( aSftTTF.get(), cChar, bVertical ); } } aShortIDs[i] = static_cast( aGlyphId ); if( !aGlyphId ) if( nNotDef < 0 ) nNotDef = i; // first NotDef glyph found } if( nNotDef != 0 ) { // add fake NotDef glyph if needed if( nNotDef < 0 ) nNotDef = nGlyphCount++; // NotDef glyph must be in pos 0 => swap glyphids aShortIDs[ nNotDef ] = aShortIDs[0]; aTempEncs[ nNotDef ] = aTempEncs[0]; aShortIDs[0] = 0; aTempEncs[0] = 0; } DBG_ASSERT( nGlyphCount < 257, "too many glyphs for subsetting" ); // fill pWidth array TTSimpleGlyphMetrics* pMetrics = ::GetTTSimpleGlyphMetrics( aSftTTF.get(), aShortIDs, nGlyphCount, aIFSD.mbVertical ); if( !pMetrics ) return FALSE; sal_uInt16 nNotDefAdv = pMetrics[0].adv; pMetrics[0].adv = pMetrics[nNotDef].adv; pMetrics[nNotDef].adv = nNotDefAdv; for( i = 0; i < nOrigCount; ++i ) pGlyphWidths[i] = pMetrics[i].adv; free( pMetrics ); // write subset into destination file nRC = ::CreateTTFromTTGlyphs( aSftTTF.get(), aToFile.GetBuffer(), aShortIDs, aTempEncs, nGlyphCount, 0, NULL, 0 ); return (nRC == SF_OK); } //-------------------------------------------------------------------------- const void* Os2SalGraphics::GetEmbedFontData( const ImplFontData* pFont, const sal_Ucs* pUnicodes, sal_Int32* pCharWidths, FontSubsetInfo& rInfo, long* pDataLen ) { // create matching ImplFontSelectData // we need just enough to get to the font file data ImplFontSelectData aIFSD( *pFont, Size(0,1000), 1000.0, 0, false ); // TODO: much better solution: move SetFont and restoration of old font to caller ScopedFont aOldFont(*this); SetFont( &aIFSD, 0 ); // get the raw font file data RawFontData aRawFontData( mhPS ); *pDataLen = aRawFontData.size(); if( !aRawFontData.get() ) return NULL; // get important font properties FONTMETRICS aOS2Metric; if (Ft2QueryFontMetrics( mhPS, sizeof( aOS2Metric ), &aOS2Metric ) == GPI_ERROR) *pDataLen = 0; rInfo.m_nFontType = FontSubsetInfo::ANY_TYPE1; rInfo.m_aPSName = ImplSalGetUniString( aOS2Metric.szFacename ); rInfo.m_nAscent = +aOS2Metric.lMaxAscender; rInfo.m_nDescent = -aOS2Metric.lMaxDescender; rInfo.m_aFontBBox = Rectangle( Point( 0, -aOS2Metric.lMaxDescender ), Point( aOS2Metric.lMaxCharInc, aOS2Metric.lMaxAscender+aOS2Metric.lExternalLeading ) ); rInfo.m_nCapHeight = aOS2Metric.lMaxAscender; // Well ... // get individual character widths for( int i = 0; i < 256; ++i ) { LONG nCharWidth = 0; const sal_Ucs cChar = pUnicodes[i]; if( !Ft2QueryStringWidthW( mhPS, (LPWSTR)&cChar, 1, &nCharWidth ) ) *pDataLen = 0; pCharWidths[i] = nCharWidth; } if( !*pDataLen ) return NULL; const unsigned char* pData = aRawFontData.steal(); return (void*)pData; } //-------------------------------------------------------------------------- void Os2SalGraphics::FreeEmbedFontData( const void* pData, long /*nLen*/ ) { delete[] reinterpret_cast(const_cast(pData)); } const Ucs2SIntMap* Os2SalGraphics::GetFontEncodingVector( const ImplFontData* pFont, const Ucs2OStrMap** pNonEncoded ) { // TODO: even for builtin fonts we get here... why? if( !pFont->IsEmbeddable() ) return NULL; // fill the encoding vector // currently no nonencoded vector if( pNonEncoded ) *pNonEncoded = NULL; const ImplOs2FontData* pWinFontData = static_cast(pFont); const Ucs2SIntMap* pEncoding = pWinFontData->GetEncodingVector(); if( pEncoding == NULL ) { Ucs2SIntMap* pNewEncoding = new Ucs2SIntMap; #if 0 // TODO: get correct encoding vector GLYPHSET aGlyphSet; aGlyphSet.cbThis = sizeof(aGlyphSet); DWORD aW = ::GetFontUnicodeRanges( mhPS, &aGlyphSet); #else for( sal_Unicode i = 32; i < 256; ++i ) (*pNewEncoding)[i] = i; #endif pWinFontData->SetEncodingVector( pNewEncoding ); pEncoding = pNewEncoding; } return pEncoding; } //-------------------------------------------------------------------------- void Os2SalGraphics::GetGlyphWidths( const ImplFontData* pFont, bool bVertical, Int32Vector& rWidths, Ucs2UIntMap& rUnicodeEnc ) { // create matching ImplFontSelectData // we need just enough to get to the font file data ImplFontSelectData aIFSD( *pFont, Size(0,1000), 1000.0, 0, false ); // TODO: much better solution: move SetFont and restoration of old font to caller ScopedFont aOldFont(*this); float fScale = 0.0; ImplDoSetFont( &aIFSD, fScale, 0); if( pFont->IsSubsettable() ) { // get raw font file data const RawFontData xRawFontData( mhPS ); if( !xRawFontData.get() ) return; // open font file sal_uInt32 nFaceNum = 0; if( !*xRawFontData.get() ) // TTC candidate nFaceNum = ~0U; // indicate "TTC font extracts only" ScopedTrueTypeFont aSftTTF; int nRC = aSftTTF.open( (void*)xRawFontData.get(), xRawFontData.size(), nFaceNum ); if( nRC != SF_OK ) return; int nGlyphs = GetTTGlyphCount( aSftTTF.get() ); if( nGlyphs > 0 ) { rWidths.resize(nGlyphs); std::vector aGlyphIds(nGlyphs); for( int i = 0; i < nGlyphs; i++ ) aGlyphIds[i] = sal_uInt16(i); TTSimpleGlyphMetrics* pMetrics = ::GetTTSimpleGlyphMetrics( aSftTTF.get(), &aGlyphIds[0], nGlyphs, bVertical ? 1 : 0 ); if( pMetrics ) { for( int i = 0; i< nGlyphs; i++ ) rWidths[i] = pMetrics[i].adv; free( pMetrics ); rUnicodeEnc.clear(); } const ImplOs2FontData* pWinFont = static_cast(pFont); const ImplFontCharMap* pMap = pWinFont->GetImplFontCharMap(); DBG_ASSERT( pMap && pMap->GetCharCount(), "no map" ); int nCharCount = pMap->GetCharCount(); sal_uInt32 nChar = pMap->GetFirstChar(); for( int i = 0; i < nCharCount; i++ ) { if( nChar < 0x00010000 ) { sal_uInt16 nGlyph = ::MapChar( aSftTTF.get(), static_cast(nChar), bVertical ? 1 : 0 ); if( nGlyph ) rUnicodeEnc[ static_cast(nChar) ] = nGlyph; } nChar = pMap->GetNextChar( nChar ); } } } else if( pFont->IsEmbeddable() ) { // get individual character widths rWidths.clear(); rUnicodeEnc.clear(); rWidths.reserve( 224 ); for( sal_Unicode i = 32; i < 256; ++i ) { int nCharWidth = 0; if( Ft2QueryStringWidthW( mhPS, (LPWSTR)&i, 1, (LONG*)&nCharWidth ) ) { rUnicodeEnc[ i ] = rWidths.size(); rWidths.push_back( nCharWidth ); } } } } //-------------------------------------------------------------------------- void Os2SalGraphics::DrawServerFontLayout( const ServerFontLayout& ) {} //-------------------------------------------------------------------------- SystemFontData Os2SalGraphics::GetSysFontData( int nFallbacklevel ) const { SystemFontData aSysFontData; if (nFallbacklevel >= MAX_FALLBACK) nFallbacklevel = MAX_FALLBACK - 1; if (nFallbacklevel < 0 ) nFallbacklevel = 0; aSysFontData.nSize = sizeof( SystemFontData ); aSysFontData.hFont = mhFonts[nFallbacklevel]; aSysFontData.bFakeBold = false; aSysFontData.bFakeItalic = false; aSysFontData.bAntialias = true; aSysFontData.bVerticalCharacterType = false; return aSysFontData; } //--------------------------------------------------------------------------