#include "precomp.h" // ============================================================================================================================================================ // Font abstract class + statics to install TT fonts and Bitmap fonts // ============================================================================================================================================================ #include #include #include #ifdef WASABI_COMPILE_SKIN #include #include #endif #include #include #ifdef WASABI_COMPILE_FONT #include //#include "services/svc_fontmaker.h" #endif #ifdef WASABI_API_CONFIG #include #include #include #include #endif #include #include extern _bool cfg_options_usefontmapper; extern _string cfg_options_ttfoverridefont; extern _int cfg_options_defaultfontscale; PtrList Font::fontlist; PtrList Font::fontdefs; void Font::init() { #ifdef WASABI_API_CONFIG Wasabi::Std::setDefaultFont(cfg_options_defaultfont.getValue()); Wasabi::Std::setDefaultFontScale(cfg_options_defaultfontscale.getValueAsInt()); #endif } // ------------------------------------------------------------------------------------------------------------------------------------------------------------- void Font::dispatchTextOut(ifc_canvas *c, int style, int x, int y, int w, int h, const wchar_t *txt) { int isoverride = 0; if (WASABI_API_APP->main_isShuttingDown()) return; int size = c->getTextSize(); svc_font *f = requestSkinFont(c->getTextFont(), &size); ASSERT(f != NULL); // After we get the font we want, check to see if it is bitmap. // If bitmap fonts are disallowed, use the truetype override font. if (f->isBitmap() && useTrueTypeOverride(txt)) { int gotdefault=0; svc_font *ttFont = requestSkinFont(getTrueTypeOverride(), &size, &gotdefault); if (ttFont != NULL) { if (!gotdefault) isoverride = 1; f = ttFont; } } if (isoverride) { double f = (double)getTrueTypeOverrideScale() / 100.0f; size = (int)(size*f); } int bold = c->getTextBold(); int opaque = c->getTextOpaque(); int underline = c->getTextUnderline(); int italic = c->getTextItalic(); int align = c->getTextAlign(); int antialiased = c->getTextAntialias(); ARGB32 color = c->getTextColor(); ARGB32 bkcolor = c->getTextBkColor(); int xoffset=0, yoffset=0; c->getOffsets(&xoffset, &yoffset); /* if (!f->isBitmap() && _intVal(Main::enumRootCfgItem(0), "Force antialias on all TTF")) antialiased = 1;*/ switch (style) { case WA_FONT_TEXTOUT_NORMAL: f->textOut(c, x, y, txt, size, bold, opaque, underline, italic, color, bkcolor, xoffset, yoffset, antialiased); break; case WA_FONT_TEXTOUT_RECT: f->textOut(c, x, y, w, h, txt, size, bold, opaque, underline, italic, align, color, bkcolor, xoffset, yoffset, antialiased); break; case WA_FONT_TEXTOUT_ELLIPSED: f->textOutEllipsed(c, x, y, w, h, txt, size, bold, opaque, underline, italic, align, color, bkcolor, xoffset, yoffset, antialiased); break; case WA_FONT_TEXTOUT_WRAPPED: f->textOutWrapped(c, x, y, w, h, txt, size, bold, opaque, underline, italic, align, color, bkcolor, xoffset, yoffset, antialiased); break; case WA_FONT_TEXTOUT_WRAPPEDPATHED: f->textOutWrappedPathed(c, x, y, w, txt, size, bold, opaque, underline, italic, align, color, bkcolor, xoffset, yoffset, antialiased); break; case WA_FONT_TEXTOUT_CENTERED: RECT r; r.left = x; r.top = y; r.right = w; r.bottom = h; f->textOutCentered(c, &r, txt, size, bold, opaque, underline, italic, align, color, bkcolor, xoffset, yoffset, antialiased); break; } } // ------------------------------------------------------------------------------------------------------------------------------------------------------------- int Font::dispatchGetInfo(ifc_canvas *c, const wchar_t *font, int infoid, const wchar_t *txt, int *w, int *h) { int isoverride = 0; if (WASABI_API_APP->main_isShuttingDown()) return 0; // mig: Let's not crash if we want to see how big a NULL pointer is. if (txt == NULL) { if ( infoid == WA_FONT_GETINFO_WIDTHHEIGHT ) { if (w != NULL) { *w = 0; } if (h != NULL) { *h = 0; } } return 0; } int size = c->getTextSize(); svc_font *f = requestSkinFont(font, &size); ASSERT(f != NULL); // After we get the font we want, check to see if it is bitmap. // If bitmap fonts are disallowed, use the truetype override font. if (f->isBitmap() && useTrueTypeOverride(txt)) { int gotdefault = 0; svc_font *ttFont = requestSkinFont(getTrueTypeOverride(), &size, &gotdefault); if (ttFont != NULL) { if (!gotdefault) isoverride = 1; f = ttFont; } } if (isoverride) { double f = (double)getTrueTypeOverrideScale() / 100.0f; size = (int)(size*f); } int bold = c->getTextBold(); int underline = c->getTextUnderline(); int italic = c->getTextItalic(); int antialiased = c->getTextAntialias(); switch (infoid) { case WA_FONT_GETINFO_WIDTH: return f->getTextWidth(c, txt, size, bold, underline, italic, antialiased); case WA_FONT_GETINFO_HEIGHT: return f->getTextHeight(c, txt, size, bold, underline, italic, antialiased); case WA_FONT_GETINFO_WIDTHHEIGHT: f->getTextExtent(c, txt, w, h, size, bold, underline, italic, antialiased); return 0; } return 0; } // ------------------------------------------------------------------------------------------------------------------------------------------------------------- // Install a truetype font from its filename and associate a script_id to it // ------------------------------------------------------------------------------------------------------------------------------------------------------------- svc_font *Font::installTrueTypeFont(const wchar_t *filename, const wchar_t *path, const wchar_t *id, int scriptid, int allowmapping, int isttfreload) { if (!isttfreload) { FontDef *fd = new FontDef; fd->filename = filename; fd->path = path; fd->id = id; fd->scriptid = scriptid; fd->isbitmap = 0; fd->allowmapping = allowmapping; fontdefs.addItem(fd); } StringW file; OSFILETYPE ff=OPEN_FAILED; if (wcschr(filename, ':')) ff = WFOPEN(filename, WF_READONLY_BINARY); if (ff == OPEN_FAILED) { file = StringPathCombine(path, filename); ff = WFOPEN(file, WF_READONLY_BINARY); } #ifdef WASABI_COMPILE_SKIN if (ff == OPEN_FAILED) { file = StringPathCombine(SkinParser::getXmlRootPath(), filename); ff = WFOPEN(file, WF_READONLY_BINARY); if (ff == OPEN_FAILED) { file = StringPathCombine(Skin::getDefaultSkinPath(), filename); ff = WFOPEN(file, WF_READONLY_BINARY); if (ff == OPEN_FAILED) { DebugString("Font not found %s\n", filename); // todo: do something if still not found } } } #endif if (ff == OPEN_FAILED) { DebugString("Could not install font %s\n", filename); return 0; } StringW fs = filename; wchar_t *p = wcschr(fs.getNonConstVal(), '.'); if (p) *p = 0; PathParserW pp(fs); fs = pp.getLastString(); svc_font *f = newTrueTypeFont(); if (f && f->addFontResource( ff, fs) ) { f->setFontId(id); f->setScriptId(scriptid); fontlist.addItem(f); } else { DebugString("font.cpp ====== CAN'T LOAD FONT FILE.\n"); } FCLOSE(ff); return f; } // ------------------------------------------------------------------------------------------------------------------------------------------------------------- // Uninstall all installed fonts // ------------------------------------------------------------------------------------------------------------------------------------------------------------- void Font::uninstallAll(int ttfreload) { int i; // delete all by hand for (i = 0; i < fontlist.getNumItems(); i++) { svc_font *f = fontlist.enumItem(i); if (ttfreload && f->isBitmap()) continue; deleteFont(f); fontlist.removeByPos(i); i--; } if (!ttfreload) fontdefs.deleteAll(); } // ------------------------------------------------------------------------------------------------------------------------------------------------------------- // Uninstall by scriptid // ------------------------------------------------------------------------------------------------------------------------------------------------------------- void Font::uninstallByScriptId(int scriptid) { for (int i=0;igetScriptId() == scriptid) { fontlist.removeByPos(i); deleteFont(f); i--; } } for (int i=0;iscriptid == scriptid) { fontdefs.removeByPos(i); delete fd; i--; } } } // ------------------------------------------------------------------------------------------------------------------------------------------------------------- // Install a bitmap font and associates a script_id to it // ------------------------------------------------------------------------------------------------------------------------------------------------------------- void Font::installBitmapFont(const wchar_t *filename, const wchar_t *path, const wchar_t *id, int cw, int ch, int hs, int vs, int scriptid, int allowmapping) { FontDef *fd = new FontDef; fd->filename = filename; fd->path = path; fd->id = id; fd->scriptid = scriptid; fd->isbitmap = 1; fd->allowmapping = allowmapping; fontdefs.addItem(fd); BitmapFont *f = new BitmapFont; f->setFontBitmap(filename, path); f->setFontId(id); f->setFontMetrics(cw, ch, hs, vs); f->setScriptId(scriptid); fontlist.addItem(f); } // ------------------------------------------------------------------------------------------------------------------------------------------------------------- // Requests a Font* from its id // ------------------------------------------------------------------------------------------------------------------------------------------------------------- svc_font *Font::requestSkinFont(const wchar_t *id, int *size, int *gotdefault) { if (gotdefault) *gotdefault = 0; int oldsize = size ? *size : -1; const wchar_t *mapped_id = getFontMapping(id, size); if (mapped_id != NULL) id = mapped_id; // First try to get a font by that id foreach_reverse(fontlist) const wchar_t *thisid = fontlist.getfor()->getFontId(); if (thisid && !WCSICMP(thisid, id)) return fontlist.getfor(); endfor // if it wasnt found, try to load a wa-installed ttfont with this face name foreach_reverse(fontlist) const wchar_t *facename=fontlist.getfor()->getFaceName(); if (facename && !WCSICMP(facename, id)) return fontlist.getfor(); endfor // not found, try to reload it front the list of fonts defined by the skin foreach(fontdefs) FontDef *fd = fontdefs.getfor(); if (!WCSICMP(fd->id, id)) { if (!fd->isbitmap) { svc_font *f = installTrueTypeFont(fd->filename, fd->path, fd->id, fd->scriptid, fd->allowmapping, 1); if (f) return f; } } endfor; /* for (i=fontlist.getNumItems()-1;i>=0;i--) { const char *thisid = fontlist.enumItem(i)->getFontId(); if (thisid && STRCASEEQL(thisid, "wasabi.font.ttf.default" )) return fontlist.enumItem(i); } */ // not found ? try to find it in the windows fonts directory { wchar_t *fp = WMALLOC(WA_MAX_PATH); Wasabi::Std::getFontPath(WA_MAX_PATH, fp); StringW file; file.own(fp); // FREE(fp); // benski> no need because we now own it file.AppendPath(StringPrintfW(L"%s%s", id, WCSCASESTR(id, L".ttf") == NULL ? L".ttf":L"")); if (!WACCESS(file, 0)) { svc_font *f = newTrueTypeFont(); f->setFontFace(id); f->setFontId(id); OSFILETYPE ff = WFOPEN(file, WF_READONLY_BINARY); if (ff != OPEN_FAILED) { if (f->addFontResource(ff, id)) { DebugStringW(L"font.cpp ====== FONT FOR ID=%s NOT FOUND. USING WIN FONT FILE:\n%s\n", id, file.getValue()); fontlist.addItem(f); } } else { DebugStringW(L"font.cpp ====== FONT FOR ID=%s NOT FOUND. CANNOT OPEN WIN FONT FILE:\n%s\n", id, file.getValue()); delete f; f = NULL; } return f; } } // not found ? ask the Std:: interface for the folder and the // default fontname (ie: one you know will always be in the OS) svc_font *f = newTrueTypeFont(); if (f) { if (gotdefault) *gotdefault = 1; if (oldsize != -1 && size) { *size = oldsize; double f = (double)Wasabi::Std::getDefaultFontScale() / 100.0; *size = (int)(*size*f); } // Query Std:: and build the path to the default font file. wchar_t *fontPath = WMALLOC(WA_MAX_PATH); Wasabi::Std::getFontPath(WA_MAX_PATH, fontPath); wchar_t fontFile[WA_MAX_PATH] = {0}; Wasabi::Std::getDefaultFont(WA_MAX_PATH, fontFile); StringW defaultFont; defaultFont.own(fontPath); defaultFont.AppendPath(fontFile); // FREE(fontFile); StringW fs = defaultFont; wchar_t *p = wcschr(fs.getNonConstVal(), '.'); if (p) *p = 0; PathParserW pp(fs); fs = pp.getLastString(); f->setFontFace(fs); f->setFontId(id); // Open it and load it as the font resource. OSFILETYPE ff = WFOPEN(defaultFont, WF_READONLY_BINARY); if (ff != OPEN_FAILED) { if (f->addFontResource(ff, fs)) { DebugStringW(L"font.cpp ====== FONT FOR ID=%s NOT FOUND. USING DEFAULT FONT FILE:\n%s\n", id, defaultFont); fontlist.addItem(f); } } else { DebugStringW(L"font.cpp ====== FONT FOR ID=%s NOT FOUND. CANNOT OPEN FONT FILE:\n%s\n", id, defaultFont); delete f; f = NULL; } } else { DebugString("font.cpp ====== CAN'T GET NEW FONT FILE.\n"); delete f; f = NULL; } #ifdef _WIN32 if (f == NULL) { // not found :((((( grab the default font data and use this, whatever it is f = newTrueTypeFont(); if (f) { HDC dc = GetDC(GetDesktopWindow()); HDC dc2 = CreateCompatibleDC(dc); SelectObject(dc2, GetStockObject(DEFAULT_GUI_FONT)); int datalen = GetFontData(dc2, 0, 0, NULL, 0); if (datalen > 0) { void *mem = WASABI_API_MEMMGR->sysMalloc(datalen+1); // freed by the service !! ASSERT(mem != NULL); GetFontData(dc2, 0, 0, mem, datalen); f->setFontFace(id); f->setFontId(id); f->addFontResource2(mem, datalen, id); ReleaseDC(GetDesktopWindow(), dc); DeleteDC(dc2); fontlist.addItem(f); return f; } delete f; f = NULL; } } #else #warning port me #endif if (f == NULL) { // ok, NOW I'm getting pissed wchar_t fp[WA_MAX_PATH] = {0}; Wasabi::Std::getFontPath(WA_MAX_PATH, fp); #ifdef _WIN32 Wasabi::Std::messageBox(StringPrintfW(L"Fatal error trying to load truetype fonts.\n\nYou need arial.ttf at the very least, but it does not appear to be in %s", fp), L"Fatal Error", MB_ICONERROR); #else #warning port me #endif } //if (f == NULL) DebugString("font.cpp ====== FALLBACK FOR FONT %s CANNOT BE FOUND IN OUR LISTS.\n",f->getFontId()); return f; } // ------------------------------------------------------------------------------------------------------------------------------------------------------------- // Intelligently delete the font // ------------------------------------------------------------------------------------------------------------------------------------------------------------- void Font::deleteFont(svc_font *f) { if (f) { if (f->isBitmap()) { delete static_cast(f); // we delete our own bitmap fonts. } else { SvcEnum::release(f); } } } // ------------------------------------------------------------------------------------------------------------------------------------------------------------- // Intelligently make a new truetype font from the service interfaces // ------------------------------------------------------------------------------------------------------------------------------------------------------------- svc_font *Font::newTrueTypeFont() { /*#ifdef WASABI_COMPILE_CONFIG const GUID options_guid = { 0x280876cf, 0x48c0, 0x40bc, { 0x8e, 0x86, 0x73, 0xce, 0x6b, 0xb4, 0x62, 0xe5 } }; CfgItem *options = WASABI_API_CONFIG->config_getCfgItemByGuid(options_guid); #endif*/ svc_font *retval = NULL; const wchar_t *name = NULL; #ifdef WASABI_COMPILE_CONFIG //const wchar_t *attr = L"Font Renderer"; // First, try to find a font service that matches the attribute. // if (options) { // char buf[256]; // WHEEE for stack arrays // if (options->getData(attr, buf, sizeof buf)) { if (WASABI_API_SKIN->skin_getVersion() >= 1.3) // hardcode win32 renderer for v1.3+ skins retval = FontSvcEnum(L"Win32 TextOut").getFirst(); else retval = FontSvcEnum(cfg_options_fontrenderer.getValue()).getFirst(); #else #ifndef WASABI_FONT_RENDERER #error You need to define WASABI_FONT_RENDERER (ie: #define WASABI_FONT_RENDERER "Freetype") #endif retval = FontSvcEnum(WASABI_FONT_RENDERER).getFirst(); #endif #ifdef WASABI_COMPILE_CONFIG // } // } // If we can't find one, fallback and just take the first. if (!retval) { retval = FontSvcEnum().getFirst(); if (retval != NULL) name = retval->getFontSvcName(); } // If we had to fallback, remember the fallback service in the attribute. if (name/* && options*/) { //options->setData(attr, name); cfg_options_fontrenderer.setValue(name); } #endif return retval; } // ------------------------------------------------------------------------------------------------------------------------------------------------------------- // Test whether to forbid bitmap fonts. // ------------------------------------------------------------------------------------------------------------------------------------------------------------- int Font::useTrueTypeOverride(const wchar_t *txt) { if (cfg_options_no7bitsttfoverride.getValueAsInt()) { const wchar_t *p = (const wchar_t *)txt; while (p && *p) { // TODO: benski> some characters above 127 can be handled by the bitmap fonts - it might be worth checking those explicitly if (*p & 0xFF80) break; p++; } if (!*p) return 0; } #ifdef WASABI_COMPILE_CONFIG /* // {280876CF-48C0-40bc-8E86-73CE6BB462E5} const GUID options_guid = { 0x280876cf, 0x48c0, 0x40bc, { 0x8e, 0x86, 0x73, 0xce, 0x6b, 0xb4, 0x62, 0xe5 } }; return !_intVal(WASABI_API_CONFIG->config_getCfgItemByGuid(options_guid), "Use bitmap fonts (no international support)", 1);*/ return !cfg_options_allowbitmapfonts.getValueAsInt(); #else return WASABI_FONT_TTFOVERRIDE; #endif } // ------------------------------------------------------------------------------------------------------------------------------------------------------------- // Get the font to be used to override bitmap fonts. // ------------------------------------------------------------------------------------------------------------------------------------------------------------- const wchar_t *Font::getTrueTypeOverride() { #ifdef WASABI_COMPILE_CONFIG return cfg_options_ttfoverridefont.getValue(); #else return L"Arial"; #warning TODO #endif } int Font::getTrueTypeOverrideScale() { #ifdef WASABI_COMPILE_CONFIG return cfg_options_ttfoverridescale.getValueAsInt(); #else return 1; #warning TODO #endif } // ------------------------------------------------------------------------------------------------------------------------------------------------------------- // Returns the font mapping for this font & skin, if font mapper is on and if there is a mapping, otherwise returns null // ------------------------------------------------------------------------------------------------------------------------------------------------------------- const wchar_t *Font::getFontMapping(const wchar_t *id, int *size) { if (cfg_options_usefontmapper.getValueAsInt()) { wchar_t t[256]=L""; StringW tmp; tmp.printf(L"Skin:%s/Font Mapping/%s",WASABI_API_SKIN->getSkinName(), id); WASABI_API_CONFIG->getStringPrivate(tmp, t, 256, L""); tmp.printf(L"Skin:%s/Font Mapping/%s_scale",WASABI_API_SKIN->getSkinName(), id); int v = WASABI_API_CONFIG->getIntPrivate(tmp, -1); if (!*t) { tmp.printf(L"Font Mapping/%s", id); WASABI_API_CONFIG->getStringPrivate(tmp, t, 256, L""); tmp.printf(L"Font Mapping/%s_scale", id); v = WASABI_API_CONFIG->getIntPrivate(tmp, -1); } mapping = t; if (mapping.isempty()) return NULL; if (size != NULL) { if (v != -1) { double f = (double)v / 100.0; *size = (int)((double)*size * f); } } return mapping; } return NULL; } StringW Font::mapping;