| /* |
| * FreeType font engine interface |
| * |
| * Copyright 2001 Huw D M Davies for CodeWeavers. |
| * |
| * This file contains the WineEng* functions. |
| */ |
| |
| |
| #include "config.h" |
| |
| #include "windef.h" |
| #include "winerror.h" |
| #include "winreg.h" |
| #include "wingdi.h" |
| #include "wine/unicode.h" |
| #include "gdi.h" |
| #include "font.h" |
| #include "debugtools.h" |
| |
| #include <string.h> |
| #include <dirent.h> |
| #include <stdio.h> |
| #include <assert.h> |
| |
| DEFAULT_DEBUG_CHANNEL(font); |
| |
| #ifdef HAVE_FREETYPE |
| |
| #ifdef HAVE_FREETYPE_FREETYPE_H |
| #include <freetype/freetype.h> |
| #endif |
| #ifdef HAVE_FREETYPE_FTGLYPH_H |
| #include <freetype/ftglyph.h> |
| #endif |
| #ifdef HAVE_FREETYPE_TTTABLES_H |
| #include <freetype/tttables.h> |
| #endif |
| #ifdef HAVE_FREETYPE_FTNAMES_H |
| #include <freetype/ftnames.h> |
| #endif |
| #ifdef HAVE_FREETYPE_FTSNAMES_H |
| #include <freetype/ftsnames.h> |
| #endif |
| #ifdef HAVE_FREETYPE_TTNAMEID_H |
| #include <freetype/ttnameid.h> |
| #endif |
| #ifdef HAVE_FREETYPE_FTOUTLN_H |
| #include <freetype/ftoutln.h> |
| #endif |
| #ifdef HAVE_FREETYPE_INTERNAL_SFNT_H |
| #include <freetype/internal/sfnt.h> |
| #endif |
| |
| static FT_Library library = 0; |
| |
| typedef struct tagFace { |
| WCHAR *StyleName; |
| char *file; |
| BOOL Italic; |
| BOOL Bold; |
| struct tagFace *next; |
| } Face; |
| |
| typedef struct tagFamily { |
| WCHAR *FamilyName; |
| Face *FirstFace; |
| struct tagFamily *next; |
| } Family; |
| |
| struct tagGdiFont { |
| DWORD ref; |
| FT_Face ft_face; |
| }; |
| |
| static Family *FontList = NULL; |
| |
| static BOOL AddFontFileToList(char *file) |
| { |
| FT_Face ft_face; |
| WCHAR *FamilyW, *StyleW; |
| DWORD len; |
| Family *family = FontList; |
| Family **insert = &FontList; |
| Face **insertface; |
| |
| TRACE("Loading font file %s\n", debugstr_a(file)); |
| if(FT_New_Face(library, file, 0, &ft_face)) { |
| ERR("Unable to load font file %s\n", debugstr_a(file)); |
| return FALSE; |
| } |
| |
| if(!FT_IS_SFNT(ft_face)) { /* for now we'll skip everything but TT/OT */ |
| FT_Done_Face(ft_face); |
| return FALSE; |
| } |
| |
| len = MultiByteToWideChar(CP_ACP, 0, ft_face->family_name, -1, NULL, 0); |
| FamilyW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); |
| MultiByteToWideChar(CP_ACP, 0, ft_face->family_name, -1, FamilyW, len); |
| |
| while(family) { |
| if(!strcmpW(family->FamilyName, FamilyW)) |
| break; |
| insert = &family->next; |
| family = family->next; |
| } |
| if(!family) { |
| family = *insert = HeapAlloc(GetProcessHeap(), 0, sizeof(*family)); |
| family->FamilyName = FamilyW; |
| family->FirstFace = NULL; |
| family->next = NULL; |
| } else { |
| HeapFree(GetProcessHeap(), 0, FamilyW); |
| } |
| |
| len = MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1, NULL, 0); |
| StyleW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); |
| MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1, StyleW, len); |
| |
| |
| for(insertface = &family->FirstFace; *insertface; |
| insertface = &(*insertface)->next) { |
| if(!strcmpW((*insertface)->StyleName, StyleW)) { |
| ERR("Already loaded font %s %s\n", debugstr_w(family->FamilyName), |
| debugstr_w(StyleW)); |
| HeapFree(GetProcessHeap(), 0, StyleW); |
| FT_Done_Face(ft_face); |
| return FALSE; |
| } |
| } |
| *insertface = HeapAlloc(GetProcessHeap(), 0, sizeof(**insertface)); |
| (*insertface)->StyleName = StyleW; |
| (*insertface)->file = HeapAlloc(GetProcessHeap(),0,strlen(file)+1); |
| strcpy((*insertface)->file, file); |
| (*insertface)->next = NULL; |
| (*insertface)->Italic = (ft_face->style_flags & FT_STYLE_FLAG_ITALIC) ? 1 : 0; |
| (*insertface)->Bold = (ft_face->style_flags & FT_STYLE_FLAG_BOLD) ? 1 : 0; |
| FT_Done_Face(ft_face); |
| |
| TRACE("Added font %s %s\n", debugstr_w(family->FamilyName), |
| debugstr_w(StyleW)); |
| return TRUE; |
| } |
| |
| static void DumpFontList(void) |
| { |
| Family *family; |
| Face *face; |
| |
| for(family = FontList; family; family = family->next) { |
| TRACE("Family: %s\n", debugstr_w(family->FamilyName)); |
| for(face = family->FirstFace; face; face = face->next) { |
| TRACE("\t%s\n", debugstr_w(face->StyleName)); |
| } |
| } |
| return; |
| } |
| |
| static BOOL ReadFontDir(char *dirname) |
| { |
| DIR *dir; |
| struct dirent *dent; |
| char path[MAX_PATH]; |
| |
| dir = opendir(dirname); |
| if(!dir) { |
| ERR("Can't open directory %s\n", debugstr_a(dirname)); |
| return FALSE; |
| } |
| while((dent = readdir(dir)) != NULL) { |
| if(!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) |
| continue; |
| sprintf(path, "%s/%s", dirname, dent->d_name); |
| AddFontFileToList(path); |
| } |
| return TRUE; |
| } |
| |
| /************************************************************* |
| * WineEngInit |
| * |
| * Initialize FreeType library and create a list of available faces |
| */ |
| BOOL WineEngInit(void) |
| { |
| HKEY hkey; |
| DWORD valuelen, datalen, i = 0, type, dlen, vlen; |
| LPSTR value; |
| LPVOID data; |
| |
| if(FT_Init_FreeType(&library) != 0) { |
| ERR("Can't init FreeType library\n"); |
| return FALSE; |
| } |
| |
| if(RegOpenKeyA(HKEY_LOCAL_MACHINE, |
| "Software\\Wine\\Wine\\Config\\FontDirs", |
| &hkey) != ERROR_SUCCESS) { |
| TRACE("Can't open FontDirs key in config file\n"); |
| return FALSE; |
| } |
| |
| RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &valuelen, |
| &datalen, NULL, NULL); |
| |
| valuelen++; /* returned value doesn't include room for '\0' */ |
| value = HeapAlloc(GetProcessHeap(), 0, valuelen); |
| data = HeapAlloc(GetProcessHeap(), 0, datalen); |
| |
| dlen = datalen; |
| vlen = valuelen; |
| while(RegEnumValueA(hkey, i++, value, &vlen, NULL, &type, data, |
| &dlen) == ERROR_SUCCESS) { |
| TRACE("Got %s=%s\n", value, (LPSTR)data); |
| ReadFontDir((LPSTR)data); |
| /* reset dlen and vlen */ |
| dlen = datalen; |
| vlen = valuelen; |
| } |
| HeapFree(GetProcessHeap(), 0, data); |
| HeapFree(GetProcessHeap(), 0, value); |
| RegCloseKey(hkey); |
| DumpFontList(); |
| return TRUE; |
| } |
| |
| |
| static FT_Face OpenFontFile(char *file, LONG height) |
| { |
| FT_Error err; |
| TT_OS2 *pOS2; |
| FT_Face ft_face; |
| LONG ppem; |
| |
| err = FT_New_Face(library, file, 0, &ft_face); |
| if(err) { |
| ERR("FT_New_Face rets %d\n", err); |
| return 0; |
| } |
| |
| pOS2 = FT_Get_Sfnt_Table(ft_face, ft_sfnt_os2); |
| |
| if(height == 0) height = 16; |
| |
| /* Calc. height of EM square: |
| * |
| * For +ve lfHeight we have |
| * lfHeight = (winAscent + winDescent) * ppem / units_per_em |
| * Re-arranging gives: |
| * ppem = units_per_em * lfheight / (winAscent + winDescent) |
| * |
| * For -ve lfHeight we have |
| * |lfHeight| = ppem |
| * [i.e. |lfHeight| = (winAscent + winDescent - il) * ppem / units_per_em |
| * with il = winAscent + winDescent - units_per_em] |
| * |
| */ |
| |
| if(height > 0) |
| ppem = ft_face->units_per_EM * height / |
| (pOS2->usWinAscent + pOS2->usWinDescent); |
| else |
| ppem = -height; |
| |
| FT_Set_Pixel_Sizes(ft_face, 0, ppem); |
| return ft_face; |
| } |
| |
| /************************************************************* |
| * WineEngCreateFontInstance |
| * |
| */ |
| GdiFont WineEngCreateFontInstance(HFONT hfont) |
| { |
| GdiFont ret; |
| Face *face; |
| Family *family = NULL; |
| WCHAR FaceName[LF_FACESIZE]; |
| BOOL bd, it; |
| FONTOBJ *font = GDI_GetObjPtr(hfont, FONT_MAGIC); |
| LOGFONTW *plf = &font->logfont; |
| |
| TRACE("%s, h=%ld, it=%d, weight=%ld\n", debugstr_w(plf->lfFaceName), |
| plf->lfHeight, plf->lfItalic, plf->lfWeight); |
| |
| ret = HeapAlloc(GetProcessHeap(), 0, sizeof(*ret)); |
| ret->ref = 1; |
| |
| strcpyW(FaceName, plf->lfFaceName); |
| |
| if(FaceName[0] != '\0') { |
| for(family = FontList; family; family = family->next) { |
| if(!strcmpiW(family->FamilyName, FaceName)) |
| break; |
| } |
| } |
| |
| if(!family) { |
| family = FontList; |
| FIXME("just using first face for now\n"); |
| } |
| |
| it = plf->lfItalic ? 1 : 0; |
| bd = plf->lfWeight > 550 ? 1 : 0; |
| |
| for(face = family->FirstFace; face; face = face->next) { |
| if(!(face->Italic ^ it) && !(face->Bold ^ bd)) |
| break; |
| } |
| if(!face) face = family->FirstFace; |
| |
| TRACE("Choosen %s %s\n", debugstr_w(family->FamilyName), |
| debugstr_w(face->StyleName)); |
| |
| ret->ft_face = OpenFontFile(face->file, plf->lfHeight); |
| |
| GDI_ReleaseObj(hfont); |
| TRACE("returning %p\n", ret); |
| return ret; |
| } |
| |
| /************************************************************* |
| * WineEngAddRefFont |
| * |
| */ |
| DWORD WineEngAddRefFont(GdiFont font) |
| { |
| return ++font->ref; |
| } |
| |
| /************************************************************* |
| * WineEngDecRefFont |
| * |
| */ |
| DWORD WineEngDecRefFont(GdiFont font) |
| { |
| DWORD ret = --font->ref; |
| |
| if(ret == 0) { |
| FT_Done_Face(font->ft_face); |
| HeapFree(GetProcessHeap(), 0, font); |
| } |
| return ret; |
| } |
| |
| static void GetEnumStructs(Face *face, LPENUMLOGFONTEXW pelf, |
| LPNEWTEXTMETRICEXW pntm, LPDWORD ptype) |
| { |
| OUTLINETEXTMETRICW *potm; |
| UINT size; |
| GdiFont font = HeapAlloc(GetProcessHeap(),0,sizeof(*font)); |
| |
| font->ref = 1; |
| font->ft_face = OpenFontFile(face->file, 100); |
| |
| memset(&pelf->elfLogFont, 0, sizeof(LOGFONTW)); |
| |
| size = WineEngGetOutlineTextMetrics(font, 0, NULL); |
| potm = HeapAlloc(GetProcessHeap(), 0, size); |
| WineEngGetOutlineTextMetrics(font, size, potm); |
| |
| #define TM potm->otmTextMetrics |
| |
| pntm->ntmTm.tmHeight = pelf->elfLogFont.lfHeight = TM.tmHeight; |
| pntm->ntmTm.tmAscent = TM.tmAscent; |
| pntm->ntmTm.tmDescent = TM.tmDescent; |
| pntm->ntmTm.tmInternalLeading = TM.tmInternalLeading; |
| pntm->ntmTm.tmExternalLeading = TM.tmExternalLeading; |
| pntm->ntmTm.tmAveCharWidth = pelf->elfLogFont.lfWeight = TM.tmAveCharWidth; |
| pntm->ntmTm.tmMaxCharWidth = TM.tmMaxCharWidth; |
| pntm->ntmTm.tmWeight = pelf->elfLogFont.lfWeight = TM.tmWeight; |
| pntm->ntmTm.tmOverhang = TM.tmOverhang; |
| pntm->ntmTm.tmDigitizedAspectX = TM.tmDigitizedAspectX; |
| pntm->ntmTm.tmDigitizedAspectY = TM.tmDigitizedAspectY; |
| pntm->ntmTm.tmFirstChar = TM.tmFirstChar; |
| pntm->ntmTm.tmLastChar = TM.tmLastChar; |
| pntm->ntmTm.tmDefaultChar = TM.tmDefaultChar; |
| pntm->ntmTm.tmBreakChar = TM.tmBreakChar; |
| pntm->ntmTm.tmItalic = pelf->elfLogFont.lfItalic = TM.tmItalic; |
| pntm->ntmTm.tmUnderlined = pelf->elfLogFont.lfUnderline = TM.tmUnderlined; |
| pntm->ntmTm.tmStruckOut = pelf->elfLogFont.lfStrikeOut = TM.tmStruckOut; |
| pntm->ntmTm.tmPitchAndFamily = TM.tmPitchAndFamily; |
| pelf->elfLogFont.lfPitchAndFamily = (TM.tmPitchAndFamily & 0xf1) + 1; |
| pntm->ntmTm.tmCharSet = pelf->elfLogFont.lfCharSet = TM.tmCharSet; |
| |
| pntm->ntmTm.ntmFlags = TM.tmItalic ? NTM_ITALIC : 0; |
| if(TM.tmWeight > 550) pntm->ntmTm.ntmFlags |= NTM_BOLD; |
| if(pntm->ntmTm.ntmFlags == 0) pntm->ntmTm.ntmFlags = NTM_REGULAR; |
| |
| pntm->ntmTm.ntmSizeEM = potm->otmEMSquare; |
| pntm->ntmTm.ntmCellHeight = 0; |
| pntm->ntmTm.ntmAvgWidth = 0; |
| |
| *ptype = TM.tmPitchAndFamily & TMPF_TRUETYPE ? TRUETYPE_FONTTYPE : 0; |
| if(!(TM.tmPitchAndFamily & TMPF_VECTOR)) |
| *ptype |= RASTER_FONTTYPE; |
| |
| #undef TM |
| memset(&pntm->ntmFontSig, 0, sizeof(FONTSIGNATURE)); |
| |
| strncpyW(pelf->elfLogFont.lfFaceName, |
| (WCHAR*)((char*)potm + (ptrdiff_t)potm->otmpFamilyName), |
| LF_FACESIZE); |
| strncpyW(pelf->elfFullName, |
| (WCHAR*)((char*)potm + (ptrdiff_t)potm->otmpFullName), |
| LF_FULLFACESIZE); |
| strncpyW(pelf->elfStyle, |
| (WCHAR*)((char*)potm + (ptrdiff_t)potm->otmpStyleName), |
| LF_FACESIZE); |
| pelf->elfScript[0] = '\0'; /* FIXME */ |
| |
| HeapFree(GetProcessHeap(), 0, potm); |
| WineEngDecRefFont(font); |
| return; |
| } |
| |
| /************************************************************* |
| * WineEngEnumFonts |
| * |
| */ |
| DWORD WineEngEnumFonts(LPLOGFONTW plf, DEVICEFONTENUMPROC proc, |
| LPARAM lparam) |
| { |
| Family *family; |
| Face *face; |
| ENUMLOGFONTEXW elf; |
| NEWTEXTMETRICEXW ntm; |
| DWORD type, ret = 1; |
| |
| TRACE("facename = %s\n", debugstr_w(plf->lfFaceName)); |
| if(plf->lfFaceName[0]) { |
| for(family = FontList; family; family = family->next) { |
| if(!strcmpiW(plf->lfFaceName, family->FamilyName)) { |
| for(face = family->FirstFace; face; face = face->next) { |
| GetEnumStructs(face, &elf, &ntm, &type); |
| TRACE("enuming '%s'\n", |
| debugstr_w(elf.elfLogFont.lfFaceName)); |
| ret = proc(&elf, &ntm, type, lparam); |
| if(!ret) break; |
| } |
| } |
| } |
| } else { |
| for(family = FontList; family; family = family->next) { |
| GetEnumStructs(family->FirstFace, &elf, &ntm, &type); |
| TRACE("enuming '%s'\n", debugstr_w(elf.elfLogFont.lfFaceName)); |
| ret = proc(&elf, &ntm, type, lparam); |
| if(!ret) break; |
| } |
| } |
| |
| return ret; |
| } |
| |
| /************************************************************* |
| * WineEngGetGlyphOutline |
| * |
| * Behaves in exactly the same way as the win32 api GetGlyphOutline |
| * except that the first parameter is the HWINEENGFONT of the font in |
| * question rather than an HDC. |
| * |
| */ |
| DWORD WineEngGetGlyphOutline(GdiFont font, UINT glyph, UINT format, |
| LPGLYPHMETRICS lpgm, DWORD buflen, LPVOID buf, |
| const MAT2* lpmat) |
| { |
| FT_Face ft_face = font->ft_face; |
| FT_UInt glyph_index; |
| DWORD width, height, pitch, needed; |
| FT_Bitmap ft_bitmap; |
| |
| TRACE("%p, %04x, %08x, %p, %08lx, %p, %p\n", font, glyph, format, lpgm, |
| buflen, buf, lpmat); |
| |
| if(format & GGO_GLYPH_INDEX) |
| glyph_index = glyph; |
| else |
| glyph_index = FT_Get_Char_Index(ft_face, glyph); |
| |
| FT_Load_Glyph(ft_face, glyph_index, FT_LOAD_DEFAULT); |
| |
| lpgm->gmBlackBoxX = ft_face->glyph->metrics.width >> 6; |
| lpgm->gmBlackBoxY = ft_face->glyph->metrics.height >> 6; |
| lpgm->gmptGlyphOrigin.x = ft_face->glyph->metrics.horiBearingX >> 6; |
| lpgm->gmptGlyphOrigin.y = ft_face->glyph->metrics.horiBearingY >> 6; |
| lpgm->gmCellIncX = ft_face->glyph->metrics.horiAdvance >> 6; |
| lpgm->gmCellIncY = 0; |
| |
| if(format == GGO_METRICS) |
| return TRUE; |
| |
| if(ft_face->glyph->format != ft_glyph_format_outline) { |
| FIXME("loaded a bitmap\n"); |
| return GDI_ERROR; |
| } |
| |
| if(format == GGO_BITMAP) { |
| width = lpgm->gmBlackBoxX; |
| height = lpgm->gmBlackBoxY; |
| pitch = (width + 31) / 32 * 4; |
| needed = pitch * height; |
| |
| if(!buf || !buflen) return needed; |
| ft_bitmap.width = width; |
| ft_bitmap.rows = height; |
| ft_bitmap.pitch = pitch; |
| ft_bitmap.pixel_mode = ft_pixel_mode_mono; |
| ft_bitmap.buffer = buf; |
| |
| FT_Outline_Translate(&ft_face->glyph->outline, |
| - ft_face->glyph->metrics.horiBearingX, |
| - (ft_face->glyph->metrics.horiBearingY - |
| ft_face->glyph->metrics.height) ); |
| |
| FT_Outline_Get_Bitmap(library, &ft_face->glyph->outline, &ft_bitmap); |
| } else { |
| FIXME("Unsupported format %d\n", format); |
| return GDI_ERROR; |
| } |
| return TRUE; |
| } |
| |
| /************************************************************* |
| * WineEngGetTextMetrics |
| * |
| */ |
| BOOL WineEngGetTextMetrics(GdiFont font, LPTEXTMETRICW ptm) |
| { |
| FT_Face ft_face = font->ft_face; |
| TT_OS2 *pOS2; |
| TT_HoriHeader *pHori; |
| FT_Fixed x_scale, y_scale; |
| |
| x_scale = ft_face->size->metrics.x_scale; |
| y_scale = ft_face->size->metrics.y_scale; |
| |
| pOS2 = FT_Get_Sfnt_Table(ft_face, ft_sfnt_os2); |
| if(!pOS2) { |
| FIXME("Can't find OS/2 table - not TT font?\n"); |
| return 0; |
| } |
| |
| pHori = FT_Get_Sfnt_Table(ft_face, ft_sfnt_hhea); |
| if(!pHori) { |
| FIXME("Can't find HHEA table - not TT font?\n"); |
| return 0; |
| } |
| |
| TRACE("OS/2 winA = %d winD = %d typoA = %d typoD = %d typoLG = %d FT_Face a = %d, d = %d, h = %d: HORZ a = %d, d = %d lg = %d maxY = %ld minY = %ld\n", |
| pOS2->usWinAscent, pOS2->usWinDescent, |
| pOS2->sTypoAscender, pOS2->sTypoDescender, pOS2->sTypoLineGap, |
| ft_face->ascender, ft_face->descender, ft_face->height, |
| pHori->Ascender, pHori->Descender, pHori->Line_Gap, |
| ft_face->bbox.yMax, ft_face->bbox.yMin); |
| |
| ptm->tmAscent = (FT_MulFix(pOS2->usWinAscent, y_scale) + 32) >> 6; |
| ptm->tmDescent = (FT_MulFix(pOS2->usWinDescent, y_scale) + 32) >> 6; |
| ptm->tmHeight = ptm->tmAscent + ptm->tmDescent; |
| ptm->tmInternalLeading = (FT_MulFix(pOS2->usWinAscent + pOS2->usWinDescent |
| - ft_face->units_per_EM, y_scale) + 32) >> 6; |
| |
| /* MSDN says: |
| el = MAX(0, LineGap - ((WinAscent + WinDescent) - (Ascender - Descender))) |
| */ |
| ptm->tmExternalLeading = max(0, (FT_MulFix(pHori->Line_Gap - |
| ((pOS2->usWinAscent + pOS2->usWinDescent) - |
| (pHori->Ascender - pHori->Descender)), y_scale) + 32) >> 6); |
| |
| ptm->tmAveCharWidth = (FT_MulFix(pOS2->xAvgCharWidth, x_scale) + 32) >> 6; |
| ptm->tmMaxCharWidth = (FT_MulFix(ft_face->bbox.xMax - ft_face->bbox.xMin, x_scale) + 32) >> 6; |
| ptm->tmWeight = pOS2->usWeightClass; |
| ptm->tmOverhang = 0; |
| ptm->tmDigitizedAspectX = 300; |
| ptm->tmDigitizedAspectY = 300; |
| ptm->tmFirstChar = pOS2->usFirstCharIndex; |
| ptm->tmLastChar = pOS2->usLastCharIndex; |
| ptm->tmDefaultChar = pOS2->usDefaultChar; |
| ptm->tmBreakChar = pOS2->usBreakChar; |
| ptm->tmItalic = (ft_face->style_flags & FT_STYLE_FLAG_ITALIC) ? 1 : 0; |
| ptm->tmUnderlined = 0; /* entry in OS2 table */ |
| ptm->tmStruckOut = 0; /* entry in OS2 table */ |
| |
| /* Yes this is correct; braindead api */ |
| ptm->tmPitchAndFamily = FT_IS_FIXED_WIDTH(ft_face) ? 0 : TMPF_FIXED_PITCH; |
| if(FT_IS_SCALABLE(ft_face)) |
| ptm->tmPitchAndFamily |= TMPF_VECTOR; |
| if(FT_IS_SFNT(ft_face)) |
| ptm->tmPitchAndFamily |= TMPF_TRUETYPE; |
| |
| ptm->tmCharSet = ANSI_CHARSET; |
| return TRUE; |
| } |
| /************************************************************* |
| * WineEngGetOutlineTextMetrics |
| * |
| */ |
| UINT WineEngGetOutlineTextMetrics(GdiFont font, UINT cbSize, |
| OUTLINETEXTMETRICW *potm) |
| { |
| FT_Face ft_face = font->ft_face; |
| UINT needed, lenfam, lensty, ret; |
| TT_OS2 *pOS2; |
| TT_HoriHeader *pHori; |
| FT_Fixed x_scale, y_scale; |
| WCHAR *family_nameW, *style_nameW; |
| WCHAR spaceW[] = {' ', '\0'}; |
| char *cp; |
| |
| needed = sizeof(*potm); |
| |
| lenfam = MultiByteToWideChar(CP_ACP, 0, ft_face->family_name, -1, NULL, 0) |
| * sizeof(WCHAR); |
| family_nameW = HeapAlloc(GetProcessHeap(), 0, lenfam); |
| MultiByteToWideChar(CP_ACP, 0, ft_face->family_name, -1, |
| family_nameW, lenfam); |
| |
| lensty = MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1, NULL, 0) |
| * sizeof(WCHAR); |
| style_nameW = HeapAlloc(GetProcessHeap(), 0, lensty); |
| MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1, |
| style_nameW, lensty); |
| |
| /* These names should be read from the TT name table */ |
| |
| /* length of otmpFamilyName */ |
| needed += lenfam; |
| |
| /* length of otmpFaceName */ |
| if(!strcasecmp(ft_face->style_name, "regular")) { |
| needed += lenfam; /* just the family name */ |
| } else { |
| needed += lenfam + lensty; /* family + " " + style */ |
| } |
| |
| /* length of otmpStyleName */ |
| needed += lensty; |
| |
| /* length of otmpFullName */ |
| needed += lenfam + lensty; |
| |
| if(needed > cbSize) { |
| ret = needed; |
| goto end; |
| } |
| |
| x_scale = ft_face->size->metrics.x_scale; |
| y_scale = ft_face->size->metrics.y_scale; |
| |
| pOS2 = FT_Get_Sfnt_Table(ft_face, ft_sfnt_os2); |
| if(!pOS2) { |
| FIXME("Can't find OS/2 table - not TT font?\n"); |
| ret = 0; |
| goto end; |
| } |
| |
| pHori = FT_Get_Sfnt_Table(ft_face, ft_sfnt_hhea); |
| if(!pHori) { |
| FIXME("Can't find HHEA table - not TT font?\n"); |
| ret = 0; |
| goto end; |
| } |
| |
| potm->otmSize = needed; |
| |
| WineEngGetTextMetrics(font, &potm->otmTextMetrics); |
| |
| potm->otmFiller = 0; |
| memcpy(&potm->otmPanoseNumber, pOS2->panose, PANOSE_COUNT); |
| potm->otmfsSelection = pOS2->fsSelection; |
| potm->otmfsType = pOS2->fsType; |
| potm->otmsCharSlopeRise = pHori->caret_Slope_Rise; |
| potm->otmsCharSlopeRun = pHori->caret_Slope_Run; |
| potm->otmItalicAngle = 0; /* POST table */ |
| potm->otmEMSquare = ft_face->units_per_EM; |
| potm->otmAscent = pOS2->sTypoAscender; |
| potm->otmDescent = pOS2->sTypoDescender; |
| potm->otmLineGap = pOS2->sTypoLineGap; |
| potm->otmsCapEmHeight = pOS2->sCapHeight; |
| potm->otmsXHeight = pOS2->sxHeight; |
| potm->otmrcFontBox.left = ft_face->bbox.xMin; |
| potm->otmrcFontBox.right = ft_face->bbox.xMax; |
| potm->otmrcFontBox.top = ft_face->bbox.yMin; |
| potm->otmrcFontBox.bottom = ft_face->bbox.yMax; |
| potm->otmMacAscent = 0; /* where do these come from ? */ |
| potm->otmMacDescent = 0; |
| potm->otmMacLineGap = 0; |
| potm->otmusMinimumPPEM = 0; /* TT Header */ |
| potm->otmptSubscriptSize.x = pOS2->ySubscriptXSize; |
| potm->otmptSubscriptSize.y = pOS2->ySubscriptYSize; |
| potm->otmptSubscriptOffset.x = pOS2->ySubscriptXOffset; |
| potm->otmptSubscriptOffset.y = pOS2->ySubscriptYOffset; |
| potm->otmptSuperscriptSize.x = pOS2->ySuperscriptXSize; |
| potm->otmptSuperscriptSize.y = pOS2->ySuperscriptYSize; |
| potm->otmptSuperscriptOffset.x = pOS2->ySuperscriptXOffset; |
| potm->otmptSuperscriptOffset.y = pOS2->ySuperscriptYOffset; |
| potm->otmsStrikeoutSize = pOS2->yStrikeoutSize; |
| potm->otmsStrikeoutPosition = pOS2->yStrikeoutPosition; |
| potm->otmsUnderscoreSize = 0; /* POST Header */ |
| potm->otmsUnderscorePosition = 0; /* POST Header */ |
| |
| /* otmp* members should clearly have type ptrdiff_t, but M$ knows best */ |
| cp = (char*)potm + sizeof(*potm); |
| potm->otmpFamilyName = (LPSTR)(cp - (char*)potm); |
| strcpyW((WCHAR*)cp, family_nameW); |
| cp += lenfam; |
| potm->otmpStyleName = (LPSTR)(cp - (char*)potm); |
| strcpyW((WCHAR*)cp, style_nameW); |
| cp += lensty; |
| potm->otmpFaceName = (LPSTR)(cp - (char*)potm); |
| strcpyW((WCHAR*)cp, family_nameW); |
| if(strcasecmp(ft_face->style_name, "regular")) { |
| strcatW((WCHAR*)cp, spaceW); |
| strcatW((WCHAR*)cp, style_nameW); |
| cp += lenfam + lensty; |
| } else |
| cp += lenfam; |
| potm->otmpFullName = (LPSTR)(cp - (char*)potm); |
| strcpyW((WCHAR*)cp, family_nameW); |
| strcatW((WCHAR*)cp, spaceW); |
| strcatW((WCHAR*)cp, style_nameW); |
| ret = needed; |
| |
| end: |
| HeapFree(GetProcessHeap(), 0, style_nameW); |
| HeapFree(GetProcessHeap(), 0, family_nameW); |
| |
| return ret; |
| } |
| |
| |
| /************************************************************* |
| * WineEngGetCharWidth |
| * |
| */ |
| BOOL WineEngGetCharWidth(GdiFont font, UINT firstChar, UINT lastChar, |
| LPINT buffer) |
| { |
| UINT c; |
| GLYPHMETRICS gm; |
| TRACE("%p, %d, %d, %p\n", font, firstChar, lastChar, buffer); |
| |
| for(c = firstChar; c <= lastChar; c++) { |
| WineEngGetGlyphOutline(font, c, GGO_METRICS, &gm, 0, NULL, NULL); |
| buffer[c - firstChar] = gm.gmCellIncX; |
| } |
| return TRUE; |
| } |
| |
| /************************************************************* |
| * WineEngGetTextExtentPoint |
| * |
| */ |
| BOOL WineEngGetTextExtentPoint(GdiFont font, LPCWSTR wstr, INT count, |
| LPSIZE size) |
| { |
| UINT idx; |
| GLYPHMETRICS gm; |
| TEXTMETRICW tm; |
| |
| TRACE("%p, %s, %d, %p\n", font, debugstr_wn(wstr, count), count, |
| size); |
| |
| size->cx = 0; |
| WineEngGetTextMetrics(font, &tm); |
| size->cy = tm.tmHeight; |
| |
| for(idx = 0; idx < count; idx++) { |
| WineEngGetGlyphOutline(font, wstr[idx], GGO_METRICS, &gm, 0, NULL, |
| NULL); |
| size->cx += gm.gmCellIncX; |
| } |
| TRACE("return %ld,%ld\n", size->cx, size->cy); |
| return TRUE; |
| } |
| |
| /************************************************************* |
| * WineEngGetFontData |
| * |
| */ |
| DWORD WineEngGetFontData(GdiFont font, DWORD table, DWORD offset, LPVOID buf, |
| DWORD cbData) |
| { |
| FT_Face ft_face = font->ft_face; |
| TT_Face tt_face; |
| SFNT_Interface *sfnt; |
| DWORD len; |
| FT_Error err; |
| |
| if(!FT_IS_SFNT(ft_face)) |
| return GDI_ERROR; |
| |
| tt_face = (TT_Face) ft_face; |
| sfnt = (SFNT_Interface*)tt_face->sfnt; |
| |
| if(!buf || !cbData) |
| len = 0; |
| else |
| len = cbData; |
| |
| if(table) { /* MS tags differ in endidness from FT ones */ |
| table = table >> 24 | table << 24 | |
| (table >> 8 & 0xff00) | (table << 8 & 0xff0000); |
| } |
| |
| err = sfnt->load_any(tt_face, table, offset, buf, &len); |
| if(err) { |
| ERR("Can't find table %08lx\n", table); |
| return GDI_ERROR; |
| } |
| return len; |
| } |
| |
| #else /* HAVE_FREETYPE */ |
| |
| BOOL WineEngInit(void) |
| { |
| return FALSE; |
| } |
| GdiFont WineEngCreateFontInstance(HFONT hfont) |
| { |
| return NULL; |
| } |
| DWORD WineEngAddRefFont(GdiFont font) |
| { |
| ERR("called but we don't have FreeType\n"); |
| return 0; |
| } |
| DWORD WineEngDecRefFont(GdiFont font) |
| { |
| ERR("called but we don't have FreeType\n"); |
| return 0; |
| } |
| |
| DWORD WineEngEnumFonts(LPLOGFONTW plf, DEVICEFONTENUMPROC proc, LPARAM lparam) |
| { |
| return 1; |
| } |
| |
| DWORD WineEngGetGlyphOutline(GdiFont font, UINT glyph, UINT format, |
| LPGLYPHMETRICS lpgm, DWORD buflen, LPVOID buf, |
| const MAT2* lpmat) |
| { |
| ERR("called but we don't have FreeType\n"); |
| return GDI_ERROR; |
| } |
| |
| BOOL WineEngGetTextMetrics(GdiFont font, LPTEXTMETRICW ptm) |
| { |
| ERR("called but we don't have FreeType\n"); |
| return FALSE; |
| } |
| |
| UINT WineEngGetOutlineTextMetrics(GdiFont font, UINT cbSize, |
| OUTLINETEXTMETRICW *potm) |
| { |
| ERR("called but we don't have FreeType\n"); |
| return 0; |
| } |
| |
| BOOL WineEngGetCharWidth(GdiFont font, UINT firstChar, UINT lastChar, |
| LPINT buffer) |
| { |
| ERR("called but we don't have FreeType\n"); |
| return FALSE; |
| } |
| |
| BOOL WineEngGetTextExtentPoint(GdiFont font, LPCWSTR wstr, INT count, |
| LPSIZE size) |
| { |
| ERR("called but we don't have FreeType\n"); |
| return FALSE; |
| } |
| |
| DWORD WineEngGetFontData(GdiFont font, DWORD table, DWORD offset, LPVOID buf, |
| DWORD cbData) |
| { |
| ERR("called but we don't have FreeType\n"); |
| return GDI_ERROR; |
| } |
| #endif /* HAVE_FREETYPE */ |
| |