/******************************Module*Header*******************************\ * Module Name: ttgdi.cxx * * These are TrueType specific calls introduced into GDI by Win 3.1. They * all assume the existence of the TrueType font driver (or rasterizer * as it is known in Win 3.1). * * Created: 11-Feb-1992 15:03:45 * Author: Gilman Wong [gilmanw] * * Copyright (c) 1992-1999 Microsoft Corporation * \**************************************************************************/ #include "precomp.hxx" /* ;********************************Public*Routine******************************** ;This function is used to create a font directory for a given engine font file ;in it's native format. This font directory can be used to create .FON like ;DLLs. ;Returns: DX:AX # of bytes copied into lpFontDir buffer or -1L in case of ; some error. ; ; GDI uses the Serif Style value in the Panose record of the OS/2 table to ; drive the Font Family for Windows. This is based on a simple table look ; up and the current mapping is as follows: ; ; Serif Style Font Family ; ---------------------------------------------------------------- ; 0 (Any) FF_DONTCARE ; 1 (No Fit) FF_DONTCARE ; 2 (Cove) FF_ROMAN ; 3 (Obtuse Cove) FF_ROMAN ; 4 (Square Cove) FF_ROMAN ; 5 (Obtuse Square Cove) FF_ROMAN ; 6 (Square) FF_ROMAN ; 7 (Thin) FF_ROMAN ; 8 (Bone) FF_ROMAN ; 9 (Exaggerated) FF_ROMAN ;10 (Triangle) FF_ROMAN ;11 (Normal Sans) FF_SWISS ;12 (Obtuse Sans) FF_SWISS ;13 (Perp Sans) FF_SWISS ;14 (Flared) FF_SWISS ;15 (Rounded) FF_SWISS ; ;****************************************************************************** */ // Generic FFH header information. #define HEADERSTUFFLEN1 (5 * sizeof(USHORT)) #define COPYRIGHTLEN 60 #define MOREHEADERSTUFFLEN (2 * sizeof(USHORT)) #define HEADERSTUFFLEN (HEADERSTUFFLEN1 + COPYRIGHTLEN) static USHORT ausHeaderStuff[5] = { 1, 0, 0x0200, ((SIZEFFH)+4+LF_FACESIZE), 0 }; static USHORT ausMoreHeaderStuff[2] = { WIN_VERSION, GDI_VERSION }; #define MAXPMWEIGHT 9 #define WOW_EMBEDING 2 /**************************************************************************\ * NtGdiMakeFontDir * * Code is over here! \**************************************************************************/ ULONG GreMakeFontDir( FLONG flEmbed, // mark file as "hidden" PBYTE pjFontDir, // pointer to structure to fill PWSZ pwszPathname // path of font file to use ) { ULONG cjNames; // localW nNamesLength HFF hff; // localD lhFontFile PIFIMETRICS pifi; // localV pIfiMetrics, %(size IFIMETRICS) ULONG_PTR idifi; ULONG cjIFI; // If TrueType disabled, then fail. // Not needed since our TrueType driver is part of the engine DLL and // should never be disabled. At least, not yet... if (gppdevTrueType == NULL) { return ( 0); } // Use TrueType driver to load font file. PDEVOBJ pdo((HDEV)gppdevTrueType); // Create a bogus PFF that only has the file name set. This insures that // the call to EngMapFontFile will suceed. FONTFILEVIEW fv, *pfv = &fv; memset( (PVOID) &fv, 0, sizeof(fv) ); fv.pwszPath = pwszPathname; PVOID pvView; ULONG cjView; if (!EngMapFontFileFDInternal((ULONG_PTR)&fv, (PULONG *)&pvView, &cjView, FALSE)) { WARNING("GreMakeFontDir: EngMapFontFile failed\n"); return(FALSE); } hff = pdo.LoadFontFile( 1 , (ULONG_PTR *)&pfv , &pvView , &cjView , 0 // pdv , (ULONG) gusLanguageID , 0 ); if ( !hff ) { KdPrint(("gdisrv!cjMakeFontDir(): failed to load TrueType file %ws\n", pwszPathname)); return ( 0); } EngUnmapFontFileFD((ULONG_PTR)&fv); // Grab a pointer to the IFIMETRICS as well as the size of the structure. if ( (pifi = pdo.QueryFont( 0, hff, 1, // currently, only 1 .TTF per .FOT &idifi)) == (PIFIMETRICS) NULL ) { // Make sure to unload on error exit. if ( !pdo.UnloadFontFile(hff) ) { WARNING("cjMakeFontDir(): IFI error--failed to unload file\n"); return (FALSE); } // Error exit. WARNING("cjMakeFontDir(): IFI error in TrueType driver\n"); return (FALSE); } cjIFI = pifi->cjThis; // NOTE PERF: [GilmanW] 01-Nov-1992 A note to myself... // // Tsk-tsk! Gilman, this is very inefficient. You should create a stack // object that loads the font file and ifimetrics. Its destructor will // automatically free the ifimetrics and unload the file. That saves // having to do the MALLOCOBJ and copy. // Copy the IFIMETRICS so we can unload the font file NOW (and simplify // error cleanup). MALLOCOBJ moIFI(cjIFI); if ( !moIFI.bValid() ) { // Make sure to unload on error exit. if ( !pdo.UnloadFontFile(hff) ) { WARNING("cjMakeFontDir(): IFI error--failed to unload file\n"); return (0); } // Error exit. WARNING("cjMakeFontDir(): could not allocate buffer for IFIMETRICS\n"); return ( 0); } RtlCopyMemory(moIFI.pv(), (PVOID) pifi, cjIFI); // Tell the TrueType driver to free the IFIMETRICS. if ( PPFNVALID(pdo, Free) ) { pdo.Free(pifi, idifi); } pifi = (PIFIMETRICS) moIFI.pv(); IFIOBJ ifio(pifi); // Tell the TrueType driver to unload the font file. if ( !pdo.UnloadFontFile(hff) ) { WARNING("cjMakeFontDir(): IFI error--failed to unload file\n"); return (0); } // Copy header info into the font directory. PBYTE pjWritePointer = pjFontDir; RtlCopyMemory(pjWritePointer, ausHeaderStuff, HEADERSTUFFLEN1); pjWritePointer += HEADERSTUFFLEN1; // // Add the copyright string. // ULONG cjTmp = strlen("Windows! Windows! Windows!") + 1; RtlCopyMemory(pjWritePointer, "Windows! Windows! Windows!", cjTmp); // If this is an embeded font we need to embed either a PID or TID depending on // whether or not we were called from WOW. If we were called from WOW then we // expect flEmbeded to be WOW_EMBEDING. if( flEmbed ) { ULONG pid = (flEmbed == WOW_EMBEDING) ? (ULONG) W32GetCurrentTID() : (ULONG) W32GetCurrentPID(); // we are overwriting the copyright string with the PID or TID but // since this is an embeded font we don't care about the copyright // string // // Unaligned write // RtlCopyMemory( pjWritePointer, &pid, sizeof( ULONG ) ); } RtlZeroMemory(pjWritePointer + cjTmp, COPYRIGHTLEN - cjTmp); // pjWritePointer += COPYRIGHTLEN; pjWritePointer += cjTmp; // Note: version stamps (Win version, Engine version) are put in the // copyright field immediately after the copyright string. RtlCopyMemory(pjWritePointer, ausMoreHeaderStuff, MOREHEADERSTUFFLEN); // pjWritePointer += MOREHEADERSTUFFLEN; pjWritePointer += (COPYRIGHTLEN - cjTmp); // Engine type and embedded flags. *pjWritePointer++ = (BYTE) ( PF_ENGINE_TYPE | ((flEmbed) ? PF_ENCAPSULATED : 0) | ((flEmbed == WOW_EMBEDING) ? PF_TID : 0)); // Selection type flag. *pjWritePointer++ = (BYTE) (ifio.fsSelection() & 0x00ff); // Em square. WRITE_WORD(pjWritePointer, ifio.fwdUnitsPerEm()); pjWritePointer += 2; // Horizontal and vertical resolutions. WRITE_WORD(pjWritePointer, 72); pjWritePointer += 2; WRITE_WORD(pjWritePointer, 72); pjWritePointer += 2; // Ascent. WRITE_WORD(pjWritePointer, ifio.fwdWinAscender()); pjWritePointer += 2; // Internal leading. WRITE_WORD(pjWritePointer, ifio.fwdInternalLeading()); pjWritePointer += 2; // External leading. WRITE_WORD(pjWritePointer, ifio.fwdExternalLeading()); pjWritePointer += 2; // Italic, strikeout, and underline flags. *pjWritePointer++ = ifio.bItalic() ? 0xffff : 0; *pjWritePointer++ = ifio.lfUnderline() ? 0xffff : 0; *pjWritePointer++ = ifio.lfStrikeOut() ? 0xffff : 0; WRITE_WORD(pjWritePointer, ifio.lfWeight()); pjWritePointer += 2; // Character set. // Old Comment: // - is this right? Maybe we should check. At least make sure ttfd // handles this so ifi.usCharSet is correct. *pjWritePointer++ = ifio.lfCharSet(); // Pix width (set to zero for some reason). [Windows 3.1 compatibility] WRITE_WORD(pjWritePointer, 0); pjWritePointer += 2; // Font height. WRITE_WORD(pjWritePointer, (WORD) ifio.lfHeight()); pjWritePointer += 2; // PitchAndFamily. *pjWritePointer++ = ifio.tmPitchAndFamily(); // Average character width (if zero, estimate as fwdMaxCharInc/2). WRITE_WORD( pjWritePointer, ifio.lfWidth() ? (WORD) ifio.lfWidth() : ifio.fwdMaxCharInc()/2 ); pjWritePointer += 2; // Maximum width. WRITE_WORD(pjWritePointer, ifio.fwdMaxCharInc()); pjWritePointer += 2; // The special characters (first, last, default, break). *pjWritePointer++ = ifio.chFirstChar(); *pjWritePointer++ = ifio.chLastChar(); WRITE_WORD(pjWritePointer, DEF_BRK_CHARACTER); // write it in one shot pjWritePointer += 2; // Force WidthBytes entry to zero, no device name. *pjWritePointer++ = 0; *pjWritePointer++ = 0; *pjWritePointer++ = 0; *pjWritePointer++ = 0; *pjWritePointer++ = 0; *pjWritePointer++ = 0; // Offset to facename. WRITE_DWORD(pjWritePointer, (DWORD) SIZEFFH + 4 + 1); pjWritePointer += 4; // Store rasterization thresholds. WRITE_WORD(pjWritePointer, (WORD) ifio.fwdLowestPPEm()); pjWritePointer += 2; WRITE_WORD(pjWritePointer, ifio.wCharBias()); pjWritePointer += 2; // Move pointer to where facenames belong. pjWritePointer = pjFontDir + SIZEFFH + 4 + 1; // Write out family name. vToASCIIN((PSZ) pjWritePointer, LF_FACESIZE, ifio.pwszFamilyName(), wcslen(ifio.pwszFamilyName()) + 1); // measure the ansi string again. If dbcs, it may be longer than cwcTmp cjNames = strlen((PSZ) pjWritePointer) + 1; pjWritePointer += cjNames; // Write out face name. vToASCIIN((PSZ) pjWritePointer, LF_FULLFACESIZE, ifio.pwszFaceName(), wcslen(ifio.pwszFaceName()) + 1); // measure the ansi string again. If dbcs, it may be longer than cwcTmp cjTmp = strlen((PSZ) pjWritePointer) + 1; cjNames += cjTmp; pjWritePointer += cjTmp; // Write out style name. vToASCIIN((PSZ) pjWritePointer, LF_FACESIZE, ifio.pwszStyleName(), wcslen(ifio.pwszStyleName()) + 1); cjNames += (strlen((PSZ) pjWritePointer) + 1); return (cjNames + SIZEFFH + 4 + 1); } /******************************Public*Routine******************************\ * GreGetRasterizerCaps * * Fills the RASTERIZER_STATUS structure. * * Returns: * TRUE if successful; FALSE otherwise. * * History: * 16-Feb-1992 -by- Gilman Wong [gilmanw] * Wrote it. \**************************************************************************/ BOOL GreGetRasterizerCaps ( LPRASTERIZER_STATUS praststat // pointer to a RASTERIZER_STATUS struc ) { // Parameter check. if (praststat == (LPRASTERIZER_STATUS) NULL) { WARNING("GreGetRasterizerCaps(): bad parameter\n"); SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER); return (FALSE); } // Fill in size. praststat->nSize = sizeof(RASTERIZER_STATUS); // Fill in TrueType driver flags. praststat->wFlags = (USHORT) ((gppdevTrueType != NULL) ? TT_ENABLED : 0); praststat->wFlags |= (gcTrueTypeFonts != 0) ? TT_AVAILABLE : 0; // Fill in language id. praststat->nLanguageID = gusLanguageID; return (TRUE); } /******************************Public*Routine******************************\ * * ulGetFontData2 * * Effects: * * History: * 17-Jul-1995 -by- Bodin Dresevic [BodinD] * Wrote it. \**************************************************************************/ ULONG ulGetFontData2 ( DCOBJ& dco, DWORD dwTable, DWORD dwOffset, PVOID pvBuffer, ULONG cjData ) { ULONG cjRet = (ULONG) -1; // Get RFONT user object. Need this to realize font. // Old Comment: // - This should get changed to an LFONTOBJ (paulb) RFONTOBJ rfo(dco, FALSE); if (!rfo.bValid()) { WARNING("GetFontData(): could not lock HRFONT\n"); return (cjRet); } // Get PFE user object. Need this for iFont. PFEOBJ pfeo(rfo.ppfe()); if (!pfeo.bValid()) { WARNING("GetFontData(): could not lock HPFE\n"); SAVE_ERROR_CODE(ERROR_INVALID_HANDLE); return (cjRet); } // Get PFF user object. Need this for HFF. PFFOBJ pffo(pfeo.pPFF()); if (!pffo.bValid()) { WARNING("GetFontData(): could not lock HPFF\n"); SAVE_ERROR_CODE(ERROR_INVALID_HANDLE); return (cjRet); } // Get FDEV user object. PDEVOBJ pdo(rfo.hdevProducer()); // As long as the driver LOOKS like the TrueType driver, we will allow the // call to succeed. Otherwise, we quit right now! // // In this case, TrueType means supporting the TrueType Tagged File Format. cjRet = pdo.QueryTrueTypeTable ( pffo.hff(), pfeo.iFont(), (ULONG) dwTable, (PTRDIFF) dwOffset, (ULONG) cjData, (PBYTE) pvBuffer, 0, 0 ); return (cjRet); } /******************************Public*Routine******************************\ * GreGetFontData * * History: * 16-Feb-1992 -by- Gilman Wong [gilmanw] * Wrote it. \**************************************************************************/ ULONG ulGetFontData ( HDC hdc, DWORD dwTable, DWORD dwOffset, PVOID pvBuffer, ULONG cjData ) { ULONG cjRet = (ULONG) -1; // Get DC user object. DCOBJ dco(hdc); if (!dco.bValid()) { WARNING("GetFontData(): bad handle for DC\n"); SAVE_ERROR_CODE(ERROR_INVALID_HANDLE); return (cjRet); } return ulGetFontData2(dco, dwTable, dwOffset, pvBuffer, cjData); } /******************************Public*Routine******************************\ * * vFixedToEf * * History: * Thu 17-Nov-1994 07:15:12 by Kirk Olynyk [kirko] * Made it simpler. * 11-Jun-1992 -by- Bodin Dresevic [BodinD] * Wrote it. \**************************************************************************/ VOID vFixedToEf ( EFLOAT *pef, FIXED& fxd ) { *pef = *(LONG*) &fxd; pef->vMultByPowerOf2(-16); } /*********************************Class************************************\ * class RESETFCOBJ * * (brief description) * * Public Interface: * * History: * 10-Jun-1992 -by- Bodin Dresevic [BodinD] * Wrote it. \**************************************************************************/ #define B_ONE(e) (((e).value == 1) && ((e).fract == 0)) #define B_ZERO(e) (((e).value == 0) && ((e).fract == 0)) class RESETFCOBJ // resetfco { private: BOOL bValid_; BOOL bTrivialXform; RFONTOBJ *prfo; public: RESETFCOBJ( DCOBJ& dco, RFONTOBJ& rfo, LPMAT2 lpmat2, // "extra" xform applied after the existing xform in dc BOOL bIgnoreRotation, FLONG flType ); ~RESETFCOBJ(); BOOL bValid() {return bValid_;}; BOOL bTrivXform() {return bTrivialXform;}; }; /******************************Public*Routine******************************\ * * RESETFCOBJ::RESETFCOBJ * * * resets the xform in rfo.hfc() to be what it used to be times lpma2 * * * History: * 01-Nov-1992 Gilman Wong [gilmanw] * IFI/DDI merge. * * 11-Jun-1992 -by- Bodin Dresevic [BodinD] * Wrote it. \**************************************************************************/ RESETFCOBJ::RESETFCOBJ( DCOBJ& dco, RFONTOBJ& rfo, LPMAT2 lpmat2, // "extra" xform applied after the existing xform in dc BOOL bIgnoreRotation, FLONG flType ) { ASSERTGDI(lpmat2 != (LPMAT2)NULL, "RESETFCOBJ:lpmat2\n"); bValid_ = TRUE; prfo = &rfo; bTrivialXform = ( B_ONE(lpmat2->eM11) && B_ONE(lpmat2->eM22) && B_ZERO(lpmat2->eM12) && B_ZERO(lpmat2->eM21) ); // If the escapement or orientation values of the LOGFONT are non-zero and // we are in compatible mode, we will need to recompute the NtoD transform // while ingnoring these values. This is for the sake of Win 3.1 compatablity. LFONTOBJ lfo(dco.pdc->hlfntNew()); if (!lfo.bValid()) { WARNING("GreGetGlyphOutline(): bad LFONTHANDLE\n"); bValid_ = FALSE; return; } if( ( lfo.plfw()->lfEscapement || lfo.plfw()->lfOrientation ) && ( bIgnoreRotation ) ) { bTrivialXform = FALSE; } if (!bTrivialXform) { // Create an EXFORMOBJ. We will use this to hold the transform passed // in via the LPMAT2. MATRIX mxExtra; EXFORMOBJ xoExtra(&mxExtra, XFORM_FORMAT_LTOL); // EXFORMOBJ should not be able to fail. ASSERTGDI ( xoExtra.bValid(), "GreGetGlyphOutline(): EXFORMOBJ failed\n" ); // Stuff lpMat2 into the "extra" EXFORMOBJ. EFLOAT ef11, ef12, ef21, ef22; vFixedToEf(&ef11, lpmat2->eM11); vFixedToEf(&ef22, lpmat2->eM22); vFixedToEf(&ef12, lpmat2->eM12); vFixedToEf(&ef21, lpmat2->eM21); { ef12.vNegate(); ef21.vNegate(); xoExtra.vSetElementsLToL(ef11, ef12, ef21, ef22); } // note that the section above is different from // xoExtra.vSetElementsLToL(ef11, ef12, ef21, ef22); // because of our interpretation of NtoD xform. with our conventions // ntod xform transforms notional space defined as having y axis pointing down // to device space also with y axis down by left vector mult. // vD = vN * N2D. The matrix passed by the user uses different convention: // y axis up in both spaces. so we have to use // Sigma_3 M Sigma_3 instead of M, (Sigma_3 = diag(1,-1)) to convert // from app convetions to our conventions [bodind] xoExtra.vRemoveTranslation(); // don't leave translations uninitialized // Need these EXFORMOBJs to calculate the new CONTEXTINFO. MATRIX mxN2D; EXFORMOBJ xoN2D(&mxN2D, XFORM_FORMAT_LTOFX); MATRIX mxNewN2D; EXFORMOBJ xoNewN2D(&mxNewN2D, XFORM_FORMAT_LTOFX); // EXFORMOBJs should not be able to fail. ASSERTGDI ( xoN2D.bValid() && xoNewN2D.bValid(), "GreGetGlyphOutline(): EXFORMOBJ failed\n" ); FD_XFORM fdx; if( bIgnoreRotation ) { // If bIgnoreRotation is set it means we've been called from WOW and // need toingore the orientation and escapment values in the LOGFONT. // To do this we will need to recompute the font driver transform. // This behavior is neccesary for Corel Draw 5.0 to be able to print // rotated text properly. PFEOBJ pfeo(rfo.ppfe()); ASSERTGDI(pfeo.bValid(), "gdisrv!RFONTOBJ(dco): bad ppfe from mapping\n"); IFIOBJ ifio(pfeo.pifi()); POINTL ptlSim; ptlSim.x = ptlSim.y = 0; if ( !pfeo.bSetFontXform( dco, lfo.plfw(), &fdx, ND_IGNORE_ESC_AND_ORIENT, 0, (POINTL* const) &ptlSim, ifio, FALSE ) ) { WARNING("RESETFCOBJ: failed to compute font transform\n"); bValid_ = FALSE; return; } xoN2D.vRemoveTranslation(); xoN2D.vSetElementsLToFx( fdx.eXX, fdx.eXY, fdx.eYX, fdx.eYY ); xoN2D.vComputeAccelFlags(XFORM_FORMAT_LTOFX); } else { rfo.vSetNotionalToDevice(xoN2D); } // Combine the transforms. if ( !xoNewN2D.bMultiply(xoN2D, xoExtra, DONT_COMPUTE_FLAGS | XFORM_FORMAT_LTOFX) ) { WARNING("GreGetGlyphOutline(): EXFORMOBJ::bMultiply failed\n"); bValid_ = FALSE; return; } // Get the new transform as an FD_XFORM. xoNewN2D.vGetCoefficient(&fdx); // Attempt to get an RFONT with the new transform. bValid_ = rfo.bSetNewFDX(dco, fdx, flType); } } /******************************Public*Routine******************************\ * * RESETFCOBJ::~RESETFCOBJ() * * resets the xform in rfo.hfc to its original value * * History: * 01-Nov-1992 Gilman Wong [gilmanw] * IFI/DDI merge. * * 11-Jun-1992 -by- Bodin Dresevic [BodinD] * Wrote it. \**************************************************************************/ RESETFCOBJ::~RESETFCOBJ() { if (bValid_ && !bTrivialXform) { // Release the cache semaphore and make inactive. prfo->vReleaseCache(); prfo->vMakeInactive(); } } #define BITS_OFFSET (offsetof(GLYPHBITS,aj)) BOOL IsSingularEudcGlyph ( GLYPHDATA *wpgd, BOOL bSimulatedBold ); /******************************Public*Routine******************************\ * GreGetGlyphOutline * * History: * 05-Mar-1995 Kirk Olynyk [kirko] * Added support for GGO_GRAY2_BITMAP, GGO_GRAY4_BITMAP, GGO_GRAY8_BITMAP. * I have introduced new modes for DrvQueryFontData that require * that the bitmaps have scans that begin and end on DWORD * boundaries as required by GetGlyphOutline(). This is the * natural format of the TrueType driver. * I also rearranged the code to have a single return point. * 01-Nov-1992 Gilman Wong [gilmanw] * IFI/DDI merge. * * 16-Feb-1992 -by- Gilman Wong [gilmanw] * Wrote it. \**************************************************************************/ ULONG GreGetGlyphOutlineInternal ( HDC hdc, WCHAR wch, // WCHAR or HGLYPH??? UINT ulFormat, // data format GLYPHMETRICS *lpgm, // glyph metrics ULONG cjBuffer, // size of buffer void *pvBuffer, // buffer for data in the format, ulFormat MAT2 *lpmat2, // "extra" xform applied after existing Notional to Device xform BOOL bIgnoreRotation ) { HGLYPH hg; GLYPHDATA gd; ULONG iMode, uRet, cjRet; BOOL bGlyphIndex, bBufferSizeWanted, flTTO; BOOL bUnhinted; FLONG flType; cjRet = GDI_ERROR; // assume error bGlyphIndex = (ulFormat & GGO_GLYPH_INDEX); // record glyph index bit bUnhinted = (ulFormat & GGO_UNHINTED); // remember if unhinted outlines are wanted ulFormat &= ~(GGO_GLYPH_INDEX | GGO_UNHINTED); // and then erase it from ulFormat bBufferSizeWanted = (pvBuffer == 0) || (cjBuffer == 0); flType = bGlyphIndex ? RFONT_TYPE_HGLYPH : RFONT_TYPE_UNICODE; if ( (lpgm == 0) || (lpmat2 == 0) ) { WARNING("GreGetGlyphOutline(): bad parameter\n"); SAVE_ERROR_CODE( ERROR_INVALID_PARAMETER ); } else { DCOBJ dco(hdc); if ( !dco.bValid() ) { WARNING("GreGetGlyphOutline(): bad handle for DC\n"); SAVE_ERROR_CODE( ERROR_INVALID_HANDLE ); } else { RFONTOBJ rfo(dco, FALSE); RFONTTMPOBJ rfoLinkSystem; RFONTTMPOBJ rfoLinkDefault; RFONTTMPOBJ rfoLinkFace; GLYPHDATA *wpgdTemp; PRFONT prfnt; PRFONT prfntTmp; UINT EudcType; UINT numFaceName; BOOL bEUDC = FALSE; // Set Basefont as default. RFONTOBJ *prfo = &rfo; // restructure this later if(!rfo.bValid()) { goto GetGlyphOutlineData; } // Get the HGLYPH for the WCHAR. Note we only check bGlyphIndex for the // original font. If this is an hglyph rfont then we wont allow linked // glyphs hg = (bGlyphIndex) ? (HGLYPH) wch : rfo.hgXlat(wch); // Check the target glyph is linked font or base font. if ((hg == rfo.hgDefault()) && !bGlyphIndex && rfo.bIsLinkedGlyph(wch)) { prfnt = rfo.prfntFont(); GreAcquireSemaphore(prfnt->hsemEUDC); HGLYPH hgFound = HGLYPH_INVALID; HGLYPH hgLink = HGLYPH_INVALID; // this value will be decremented in RFONTOBJ::dtHeler() INCREMENTEUDCCOUNT; FLINKMESSAGE2(DEBUG_FONTLINK_RFONT, "GreGetGlyphOutlineInternal():No request to change EUDC \ data %d\n",gcEUDCCount); // initialize EUDC info rfo.vInitEUDC(dco); // if we have system wide eudc, lock the cache. if( prfnt->prfntSysEUDC != NULL ) { RFONTTMPOBJ rfoTemp( prfnt->prfntSysEUDC ); rfoTemp.vGetCache(); } // The linked RFONT is initialized for Default EUDC grab the semaphore. if( prfnt->prfntDefEUDC != NULL ) { RFONTTMPOBJ rfoTemp( prfnt->prfntDefEUDC ); rfoTemp.vGetCache(); } // if we have face name eudc, lock the cache for all the linked fonts for( UINT ii = 0; ii < prfnt->uiNumLinks; ii++ ) { RFONTTMPOBJ rfoTemp( prfnt->paprfntFaceName[ii] ); rfoTemp.vGetCache(); } // Need to indicate that this RFONT's EUDC data has been initialized. prfnt->flEUDCState |= EUDC_INITIALIZED; GreReleaseSemaphore(prfnt->hsemEUDC); // First, try to find out target glyph from facename linked font. numFaceName = 0; for( ii = 0; ii < prfnt->uiNumLinks; ii++ ) { rfoLinkFace.vInit( prfnt->paprfntFaceName[ii] ); if ((hgLink = rfoLinkFace.hgXlat(wch)) != rfoLinkFace.hgDefault()) { RFONTTMPOBJ rfoTemp( prfnt->paprfntFaceName[ii] ); if (rfoTemp.bValid()) { if( (wpgdTemp = rfoTemp.pgdGetEudcMetrics( wch, &rfo)) != NULL ) { if( !IsSingularEudcGlyph(wpgdTemp, rfoTemp.pfo()->flFontType & FO_SIM_BOLD)) { numFaceName = ii; EudcType = EUDCTYPE_FACENAME; hgFound = hgLink; prfo = &rfoLinkFace; break; } } } } } // Check if the glyph is in the DEFAULT EUDC font if( (hgFound == HGLYPH_INVALID) && (prfnt->prfntDefEUDC != NULL) ) { rfoLinkDefault.vInit( prfnt->prfntDefEUDC ); if ((hgLink = rfoLinkDefault.hgXlat(wch)) != rfoLinkDefault.hgDefault()) { RFONTTMPOBJ rfoTemp( prfnt->prfntDefEUDC ); if (rfoTemp.bValid()) { if( (wpgdTemp = rfoTemp.pgdGetEudcMetrics( wch , &rfo)) != NULL ) { if( !IsSingularEudcGlyph(wpgdTemp, rfoTemp.pfo()->flFontType & FO_SIM_BOLD) ) { numFaceName = 0; EudcType = EUDCTYPE_DEFAULT; hgFound = hgLink; prfo = &rfoLinkDefault; } } } } } // Try to find out System EUDC. if( (hgFound == HGLYPH_INVALID) && (prfnt->prfntSysEUDC != NULL) ) { rfoLinkSystem.vInit( prfnt->prfntSysEUDC ); if ((hgLink = rfoLinkSystem.hgXlat(wch)) != rfoLinkSystem.hgDefault()) { numFaceName = 0; EudcType = EUDCTYPE_SYSTEM_WIDE; hgFound = hgLink; prfo = &rfoLinkSystem; } } if( hgFound != HGLYPH_INVALID ) { hg = hgFound; bEUDC = TRUE; // find any EUDC object. prfo is not rfo object. } else { rfo.dtHelper(); prfnt->flEUDCState = FALSE; } } GetGlyphOutlineData: if ( !prfo->bValid() ) { WARNING("GreGetGlyphOutline(): could not lock HRFONT\n"); SAVE_ERROR_CODE( ERROR_CAN_NOT_COMPLETE ); } else { PDEVOBJ pdo( prfo->hdevProducer() ); if ( !pdo.bValid() ) { WARNING("GreGetGlyphOutline -- invalid PDEV\n"); SAVE_ERROR_CODE( ERROR_CAN_NOT_COMPLETE ); } else if ( !PPFNVALID(pdo, QueryTrueTypeOutline) ) { WARNING1("GreGetGlyphOuline -- DrvQueryTrueTypeOutline\n"); SAVE_ERROR_CODE( ERROR_CAN_NOT_COMPLETE ); } else { // reset the xform in the rfo.hfc() if needed: RESETFCOBJ resetfco( dco, *prfo, lpmat2, bIgnoreRotation, flType ); if ( !resetfco.bValid() ) { WARNING("GreGetGlyphOutline(): resetfco\n"); SAVE_ERROR_CODE( ERROR_CAN_NOT_COMPLETE ); } else { prfntTmp = NULL; if (bEUDC && !resetfco.bTrivXform()) { switch (EudcType) { case EUDCTYPE_SYSTEM_WIDE: prfntTmp = prfnt->prfntSysEUDC; prfnt->prfntSysEUDC = NULL; break; case EUDCTYPE_DEFAULT: prfntTmp = prfnt->prfntDefEUDC; prfnt->prfntDefEUDC = NULL; break; case EUDCTYPE_FACENAME: prfntTmp = prfnt->paprfntFaceName[numFaceName]; prfnt->paprfntFaceName[numFaceName] = NULL; break; default: break; } } switch ( ulFormat ) { case GGO_BITMAP: case GGO_GRAY2_BITMAP: case GGO_GRAY4_BITMAP: case GGO_GRAY8_BITMAP: switch ( ulFormat ) { case GGO_BITMAP: iMode = QFD_TT_GRAY1_BITMAP; // 8 pixels per byte break; case GGO_GRAY2_BITMAP: iMode = QFD_TT_GRAY2_BITMAP; // one byte per pixel: 0..4 break; case GGO_GRAY4_BITMAP: iMode = QFD_TT_GRAY4_BITMAP; // one byte per pixel: 0..16 break; case GGO_GRAY8_BITMAP: iMode = QFD_TT_GRAY8_BITMAP; // one byte per pixel: 0..64 break; } cjRet = pdo.QueryFontData( 0, // device handle of PDEV prfo->pfo(), iMode, // QFD_TT_GRAY[1248]_BITMAP hg, // glyph handle &gd, // pointer to GLYPHDATA structure pvBuffer, // pointer to dest buffer cjBuffer // size of dest buffer ); break; case GGO_NATIVE: case GGO_BEZIER: flTTO = 0; if (ulFormat == GGO_BEZIER) flTTO |= TTO_QUBICS; if (bUnhinted) flTTO |= TTO_UNHINTED; // We're lucky, FdQueryTrueTypeOutline will return size if EITHER // a NULL buffer or size of zero is passed in. So we don't need // separate cases. Note that this assumes that the outline is // appropriate to a monochrome bitmap. There should be no scaling // for the case of antialiased fonts. cjRet = pdo.QueryTrueTypeOutline( 0, prfo->pfo(), hg, flTTO, &gd, cjBuffer, (TTPOLYGONHEADER *) pvBuffer ); if ( cjRet == FD_ERROR ) { ASSERTGDI(cjRet == GDI_ERROR, "FD_ERROR != GDI_ERROR\n"); WARNING( "GreGetGlyphOutline(): FdQueryTrueTypeOutline()" "--couldn't get buffer size\n" ); } break; case GGO_METRICS: // Call to get just the metrics. cjRet = pdo.QueryFontData( 0, // device handle of PDEV prfo->pfo(), QFD_TT_GLYPHANDBITMAP, // mode of call hg, // glyph handle &gd, // pointer to GLYPHDATA structure 0, // pointer to destination buffer 0 // size of destination buffer in bytes ); if ( cjRet == FD_ERROR ) { ASSERTGDI(cjRet == GDI_ERROR, "FD_ERROR != GDI_ERROR\n"); WARNING( "GreGetGlyphOutline(): FdQueryFontData()" "--couldn't get GLYPHMETRICS\n" ); } break; default: WARNING("GreGetGlyphOutline(): bad parameter, unknown format\n"); break; } } if ( cjRet != GDI_ERROR ) { // Convert the GLYPHDATA metrics to GLYPHMETRICS. lpgm->gmBlackBoxX = (UINT) (gd.rclInk.right - gd.rclInk.left); lpgm->gmBlackBoxY = (UINT) (gd.rclInk.bottom - gd.rclInk.top); // this is true by virtue of the fact that for tt fonts bitmap // is of the same size as the black box. The exception to this // rule is the empty space char which is a blank 1x1 bitmap lpgm->gmptGlyphOrigin.x = gd.rclInk.left; lpgm->gmptGlyphOrigin.y = - gd.rclInk.top; lpgm->gmCellIncX = (WORD) FXTOLROUND(gd.ptqD.x.u.HighPart); lpgm->gmCellIncY = (WORD) FXTOLROUND(gd.ptqD.y.u.HighPart); } } if (bEUDC) { rfo.dtHelper(FALSE); prfnt->flEUDCState = FALSE; if (prfntTmp) { switch (EudcType) { case EUDCTYPE_SYSTEM_WIDE: prfnt->prfntSysEUDC = prfntTmp; break; case EUDCTYPE_DEFAULT: prfnt->prfntDefEUDC = prfntTmp; break; case EUDCTYPE_FACENAME: prfnt->paprfntFaceName[numFaceName] = prfntTmp; break; default: break; } } ASSERTGDI(gcEUDCCount > 0, "gcEUDCCount <= 0"); DECREMENTEUDCCOUNT; } } } } return( cjRet ); } VOID vIFIMetricsToETM( EXTTEXTMETRIC *petm, RFONTOBJ& rfo, DCOBJ& dco, IFIMETRICS *pifi ); /******************************Public*Routine******************************\ * * GreGetETM * * support for aldus escape * * History: * 19-Oct-1993 -by- Bodin Dresevic [BodinD] * Wrote it. \**************************************************************************/ BOOL APIENTRY NtGdiGetETM( HDC hdc, EXTTEXTMETRIC *petm ) { EXTTEXTMETRIC kmETM; BOOL bRet = FALSE; // Get DC user object. DCOBJ dco(hdc); if (petm && dco.bValid()) { RFONTOBJ rfo(dco, FALSE); if (rfo.bValid()) { // see if we can dispatch the call directly to the device driver PDEVOBJ pdo(rfo.hdevProducer()); if (PPFNDRV(pdo,FontManagement)) { ULONG iMode = GETEXTENDEDTEXTMETRICS; SURFOBJ *pso = NULL; if (pdo.bUMPD()) { // we need to have a dhpdev when calling out to UMPD pso = (SURFOBJ *)pdo.dhpdev(); } BOOL bSupported = GetETMFontManagement( rfo, pdo, pso, NULL, QUERYESCSUPPORT, // iMode sizeof(ULONG), // cjIn (PVOID)&iMode, // pvIn 0, // cjOut (PVOID)NULL // pvOut ); if (bSupported) { SURFOBJ soFake; SURFOBJ *pso = pdo.pSurface()->pSurfobj(); if (pso == (SURFOBJ *) NULL) // create vanilla surfobj { RtlFillMemory((BYTE *) &soFake,sizeof(SURFOBJ),0); soFake.dhpdev = rfo.prfnt->dhpdev; soFake.hdev = rfo.hdevConsumer(); soFake.iType = (USHORT)STYPE_DEVICE; pso = &soFake; } bRet = pdo.FontManagement( pso, rfo.pfo(), GETEXTENDEDTEXTMETRICS, 0, (PVOID)NULL, (ULONG)sizeof(EXTTEXTMETRIC), (PVOID)&kmETM ); } } // if GETEXTENDEDTEXTMETRIC is not supported do something: // Get PFE user object. if (!bRet) { PFEOBJ pfeo(rfo.ppfe()); if (pfeo.bValid()) { if (pfeo.flFontType() & TRUETYPE_FONTTYPE) { vIFIMetricsToETM(&kmETM,rfo,dco,pfeo.pifi()); bRet = TRUE; } } } } } if (bRet) { __try { ProbeForWrite(petm,sizeof(EXTTEXTMETRIC),sizeof(ULONG)); RtlMoveMemory(petm,&kmETM,sizeof(EXTTEXTMETRIC)); } __except(EXCEPTION_EXECUTE_HANDLER) { // SetLastError(GetExceptionCode()); bRet = FALSE; } } return bRet; } /******************************Public*Routine******************************\ * GreGetOutlineTextMetricsInternalW * * History: * * 20-Apr-1993 -by- Gerrit van Wingerden [gerritv] * Added bTTOnly field so we can service the Aldus escape for Win 3.1 compat. * Changed to GreGe...InternalW to avoid a header file change in wingdip.h * * 16-Feb-1992 -by- Gilman Wong [gilmanw] * Wrote it. \**************************************************************************/ UINT cjOTMAWSize ( PIFIMETRICS pifi, // compute size of OTM produced by this buffer UINT *pcjotmw ); ULONG GreGetOutlineTextMetricsInternalW( HDC hdc, ULONG cjotm, OUTLINETEXTMETRICW *potmw, TMDIFF *ptmd ) { ULONG cjRet = 0; // Early out test. Zero data requested. if ( (cjotm == 0) && (potmw != (OUTLINETEXTMETRICW*) NULL) ) { WARNING("GreGetOutlineTextMetrics(): bad parameter\n"); SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER); return (cjRet); } // Get DC user object. DCOBJ dco(hdc); if (!dco.bValid()) { WARNING("GreGetOutlineTextMetrics(): bad handle for DC\n"); SAVE_ERROR_CODE(ERROR_INVALID_HANDLE); return (cjRet); } // the following is a copy of what is done during GreGetCharSet(), to fix 735873 // where bIFIMetricsToTextMetricWStrict() get called while DIRTY_CHARSET is set and // copy an unitialized value from dco.pdc->iCS_CP to ptmw->tmCharSet // if the font is not mapped, we need to map it if (dco.ulDirty() & DIRTY_CHARSET) { // force mapping FLONG flSim; POINTL ptlSim; FLONG flAboutMatch; PFE *ppfe; PDEVOBJ pdo(dco.hdev()); ASSERTGDI(pdo.bValid(), "GreGetOutlineTextMetrics: bad pdev in dc\n"); if (!pdo.bGotFonts()) pdo.bGetDeviceFonts(); LFONTOBJ lfo(dco.pdc->hlfntNew(), &pdo); if (!lfo.bValid()) { WARNING("GreGetOutlineTextMetrics!RFONTOBJ(dco): bad LFONT handle\n"); return(cjRet); } { // Stabilize the public PFT for mapping. SEMOBJ so(ghsemPublicPFT); // LFONTOBJ::ppfeMapFont returns a pointer to the physical font face and // a simulation type (ist) // also store charset to the DC ppfe = lfo.ppfeMapFont(dco, &flSim, &ptlSim, &flAboutMatch); ASSERTGDI(!(dco.ulDirty() & DIRTY_CHARSET), "NtGdiGetCharSet, charset is dirty\n"); } } // Get RFONT user object. // Old Comment: // - This should really be an LFONTOBJ RFONTOBJ rfo(dco, FALSE); if (!rfo.bValid()) { WARNING("GreGetOutlineTextMetrics(): could not lock HRFONT\n"); SAVE_ERROR_CODE(ERROR_INVALID_HANDLE); return (cjRet); } // Get PFE user object. PFEOBJ pfeo(rfo.ppfe()); if (!pfeo.bValid()) { WARNING("GreGetOutlineTextMetrics(): could not lock HPFE\n"); SAVE_ERROR_CODE(ERROR_INVALID_HANDLE); return (cjRet); } // Get LDEV user object for the font driver. PDEVOBJ pdo(rfo.hdevProducer()); // Check the font driver. If we are in TT only mode, we will allow only // the TrueType driver to succeed. However, in this sense, the TrueType // driver is any driver that exports DrvQueryTrueTypeOutline. Afterall, // if it can supply the actual outlines, it should be able to supply the // metrics. // // Actually, it would be nice to allow this function to work for all // drivers since all drivers supply the IFIMETRICS and therefore can // answer this question. However, we are much too afraid that this will // break some obscure compatibility so we will let our TT driver and // 3rd part TT-like drivers succeed. // // If we not in TT only mode, then everybody succeed this function! Yay! if (PPFNVALID(pdo, QueryTrueTypeOutline)) { // Size if full OUTLINETEXTMETRICW (including strings) is copied. UINT cjotmw; // use cjotma field of tmd to ship cjotma to the client side, [bodind] ptmd->cjotma = (ULONG)cjOTMAWSize(pfeo.pifi(), &cjotmw); // If return buffer is NULL, then only size needs to be returned. if (potmw == NULL) { cjRet = cjotmw; } else { // Is return buffer big enough for the conversion routine (which is not // capable of converting a partial OUTLINETEXTMETRICW structure [unless, // of course, it's one without the strings]). if (cjotm <= sizeof(OUTLINETEXTMETRICW)) { // Allocate a buffer for a full OUTLINETEXTMETRICW, since conversion // routine needs at least that much memory. OUTLINETEXTMETRICW otmwTmp; RtlZeroMemory(&otmwTmp, sizeof(OUTLINETEXTMETRICW)); // Convert IFIMETRICS to OUTLINETEXTMETRICW using temp buffer. if ( (cjRet = cjIFIMetricsToOTMW( ptmd, &otmwTmp, rfo, dco, pfeo.pifi(), FALSE // do not need strings )) == 0 ) { WARNING("GreGetOutlineTextMetrics(): error creating OUTLINETEXTMETRIC\n"); return (cjRet); } // Copy needed part of OUTLINETEXTMETRICW into return buffer. RtlCopyMemory((PVOID) potmw, (PVOID) &otmwTmp, cjotm); return cjotm; } // Otherwise asking for strings // We have to assume that all the strings are desired. If // cjCopy > sizeof(OUTLINETEXTMETRICW) how can we // know how many strings are requested? Afterall, the app is not // supposed to have apriori knowledge of the length of the strings. // Note that this is also a Win3.1 compatible assumption. (They // assume the same thing). if ( cjotm >= cjotmw ) { // Convert IFIMETRICS to OUTLINETEXTMETRICW using return buffer. cjRet = cjIFIMetricsToOTMW(ptmd,potmw, rfo, dco, pfeo.pifi(), TRUE); // clean up the rest of the buffer so that neilc - " Mr. c2 guy" is happy { LONG lDiff = (LONG)cjotm - (LONG)cjRet; ASSERTGDI(lDiff >= 0, "GetOTM, lDiff < 0"); if (lDiff > 0) RtlZeroMemory(((BYTE*)potmw + cjRet), lDiff); } } } } return (cjRet); }