diff --git a/headers/os/interface/Font.h b/headers/os/interface/Font.h index 5277950a0d..53219c2228 100644 --- a/headers/os/interface/Font.h +++ b/headers/os/interface/Font.h @@ -282,6 +282,11 @@ public: void PrintToStream() const; + status_t LoadFont(const char* path); + status_t LoadFont(const area_id fontAreaID, + uint32 size = 0, uint32 offset = 0); + status_t UnloadFont(); + private: friend void _init_global_fonts_(); diff --git a/headers/private/app/ServerProtocol.h b/headers/private/app/ServerProtocol.h index f76af07ada..49cf377f17 100644 --- a/headers/private/app/ServerProtocol.h +++ b/headers/private/app/ServerProtocol.h @@ -146,6 +146,9 @@ enum { AS_GET_TRUNCATED_STRINGS, AS_GET_UNICODE_BLOCKS, AS_GET_HAS_UNICODE_BLOCK, + AS_ADD_FONT_FILE, + AS_ADD_FONT_MEMORY, + AS_REMOVE_FONT, // Screen methods AS_VALID_SCREEN_ID, diff --git a/src/kits/interface/Font.cpp b/src/kits/interface/Font.cpp index 6ced82680b..58a2cf5d7c 100644 --- a/src/kits/interface/Font.cpp +++ b/src/kits/interface/Font.cpp @@ -559,6 +559,7 @@ BFont::SetFamilyAndStyle(const font_family family, const font_style style) link.Read(&fFamilyID); link.Read(&fStyleID); link.Read(&fFace); + fHeight.ascent = kUninitializedAscent; fExtraFlags = kUninitializedExtraFlags; @@ -943,6 +944,7 @@ BFont::GetTunedInfo(int32 index, tuned_font_info* info) const } +// Truncates a string to a given _pixel_ width based on the font and size void BFont::TruncateString(BString* inOut, uint32 mode, float width) const { @@ -1452,3 +1454,76 @@ BFont::_GetExtraFlags() const link.Read(&fExtraFlags); } + + +status_t +BFont::LoadFont(const char* path) +{ + BPrivate::AppServerLink link; + link.StartMessage(AS_ADD_FONT_FILE); + link.AttachString(path); + status_t status = B_ERROR; + if (link.FlushWithReply(status) != B_OK || status != B_OK) { + return status; + } + + link.Read(&fFamilyID); + link.Read(&fStyleID); + link.Read(&fFace); + fHeight.ascent = kUninitializedAscent; + fExtraFlags = kUninitializedExtraFlags; + + return B_OK; +} + + +status_t +BFont::LoadFont(const area_id fontAreaID, uint32 size, uint32 offset) +{ + BPrivate::AppServerLink link; + + link.StartMessage(AS_ADD_FONT_MEMORY); + + link.Attach(fontAreaID); + link.Attach(size); + link.Attach(offset); + + status_t status = B_ERROR; + if (link.FlushWithReply(status) != B_OK || status != B_OK) { + return status; + } + + link.Read(&fFamilyID); + link.Read(&fStyleID); + link.Read(&fFace); + fHeight.ascent = kUninitializedAscent; + fExtraFlags = kUninitializedExtraFlags; + + return B_OK; +} + + +status_t +BFont::UnloadFont() +{ + BPrivate::AppServerLink link; + + link.StartMessage(AS_REMOVE_FONT); + + link.Attach(fFamilyID); + link.Attach(fStyleID); + + status_t status = B_ERROR; + if (link.FlushWithReply(status) != B_OK || status != B_OK) { + return status; + } + + // reset to plain font + fFamilyID = 0; + fStyleID = 0; + fFace = 0; + fHeight.ascent = kUninitializedAscent; + fExtraFlags = kUninitializedExtraFlags; + + return B_OK; +} diff --git a/src/servers/app/AppServer.cpp b/src/servers/app/AppServer.cpp index a67d554c1c..2a0c4b1f54 100644 --- a/src/servers/app/AppServer.cpp +++ b/src/servers/app/AppServer.cpp @@ -20,7 +20,7 @@ #include "BitmapManager.h" #include "Desktop.h" -#include "FontManager.h" +#include "GlobalFontManager.h" #include "InputManager.h" #include "ScreenManager.h" #include "ServerProtocol.h" @@ -58,7 +58,7 @@ AppServer::AppServer(status_t* status) gInputManager = new InputManager(); // Create the font server and scan the proper directories. - gFontManager = new FontManager; + gFontManager = new GlobalFontManager; if (gFontManager->InitCheck() != B_OK) debugger("font manager could not be initialized!"); diff --git a/src/servers/app/Desktop.cpp b/src/servers/app/Desktop.cpp index a9256da59f..04d7c225c4 100644 --- a/src/servers/app/Desktop.cpp +++ b/src/servers/app/Desktop.cpp @@ -46,7 +46,7 @@ #include "DecorManager.h" #include "DesktopSettingsPrivate.h" #include "DrawingEngine.h" -#include "FontManager.h" +#include "GlobalFontManager.h" #include "HWInterface.h" #include "InputManager.h" #include "Screen.h" diff --git a/src/servers/app/DesktopSettings.cpp b/src/servers/app/DesktopSettings.cpp index e4b1eb75c8..ca5a1f9649 100644 --- a/src/servers/app/DesktopSettings.cpp +++ b/src/servers/app/DesktopSettings.cpp @@ -23,7 +23,9 @@ #include #include "Desktop.h" -#include "FontManager.h" +#include "FontCache.h" +#include "FontCacheEntry.h" +#include "GlobalFontManager.h" #include "GlobalSubpixelSettings.h" #include "ServerConfig.h" diff --git a/src/servers/app/DrawState.cpp b/src/servers/app/DrawState.cpp index 8c26c004a1..2d6aa75fd8 100644 --- a/src/servers/app/DrawState.cpp +++ b/src/servers/app/DrawState.cpp @@ -145,7 +145,8 @@ DrawState::PopState() uint16 -DrawState::ReadFontFromLink(BPrivate::LinkReceiver& link) +DrawState::ReadFontFromLink(BPrivate::LinkReceiver& link, + AppFontManager* fontManager) { uint16 mask; link.Read(&mask); @@ -153,7 +154,7 @@ DrawState::ReadFontFromLink(BPrivate::LinkReceiver& link) if ((mask & B_FONT_FAMILY_AND_STYLE) != 0) { uint32 fontID; link.Read(&fontID); - fFont.SetFamilyAndStyle(fontID); + fFont.SetFamilyAndStyle(fontID, fontManager); } if ((mask & B_FONT_SIZE) != 0) { diff --git a/src/servers/app/DrawState.h b/src/servers/app/DrawState.h index 83d2af0dd9..79692c89b2 100644 --- a/src/servers/app/DrawState.h +++ b/src/servers/app/DrawState.h @@ -22,6 +22,7 @@ #include #include +#include "AppFontManager.h" #include "ServerFont.h" #include "PatternHandler.h" #include "SimpleTransform.h" @@ -48,7 +49,8 @@ public: DrawState* PreviousState() const { return fPreviousState.Get(); } - uint16 ReadFontFromLink(BPrivate::LinkReceiver& link); + uint16 ReadFontFromLink(BPrivate::LinkReceiver& link, + AppFontManager* fontManager = NULL); // NOTE: ReadFromLink() does not read Font state!! // It was separate in ServerWindow, and I didn't // want to change it without knowing implications. diff --git a/src/servers/app/Jamfile b/src/servers/app/Jamfile index 784e045551..299da605d2 100644 --- a/src/servers/app/Jamfile +++ b/src/servers/app/Jamfile @@ -29,6 +29,8 @@ local font_src = FontFamily.cpp FontManager.cpp FontStyle.cpp + GlobalFontManager.cpp + AppFontManager.cpp ; UseBuildFeatureHeaders freetype ; diff --git a/src/servers/app/PictureBoundingBoxPlayer.cpp b/src/servers/app/PictureBoundingBoxPlayer.cpp index 7c324b7f60..74d8c1f746 100644 --- a/src/servers/app/PictureBoundingBoxPlayer.cpp +++ b/src/servers/app/PictureBoundingBoxPlayer.cpp @@ -15,7 +15,7 @@ #include #include "DrawState.h" -#include "FontManager.h" +#include "GlobalFontManager.h" #include "Layer.h" #include "ServerApp.h" #include "ServerBitmap.h" diff --git a/src/servers/app/ProfileMessageSupport.cpp b/src/servers/app/ProfileMessageSupport.cpp index 1021778ef5..c81fe5d96f 100644 --- a/src/servers/app/ProfileMessageSupport.cpp +++ b/src/servers/app/ProfileMessageSupport.cpp @@ -126,6 +126,9 @@ string_for_message_code(uint32 code) CODE(AS_GET_TRUNCATED_STRINGS); CODE(AS_GET_UNICODE_BLOCKS); CODE(AS_GET_HAS_UNICODE_BLOCK); + CODE(AS_ADD_FONT_FILE); + CODE(AS_ADD_FONT_MEMORY); + CODE(AS_REMOVE_FONT); // Screen methods CODE(AS_VALID_SCREEN_ID); diff --git a/src/servers/app/ServerApp.cpp b/src/servers/app/ServerApp.cpp index 36044e7012..16fc6d0188 100644 --- a/src/servers/app/ServerApp.cpp +++ b/src/servers/app/ServerApp.cpp @@ -44,6 +44,7 @@ #include #include +#include "AppFontManager.h" #include "AppServer.h" #include "BitmapManager.h" #include "CursorManager.h" @@ -52,7 +53,7 @@ #include "DecorManager.h" #include "DrawingEngine.h" #include "EventStream.h" -#include "FontManager.h" +#include "GlobalFontManager.h" #include "HWInterface.h" #include "InputManager.h" #include "OffscreenServerWindow.h" @@ -107,7 +108,8 @@ ServerApp::ServerApp(Desktop* desktop, port_id clientReplyPort, fViewCursor(NULL), fCursorHideLevel(0), fIsActive(false), - fMemoryAllocator(new (std::nothrow) ClientMemoryAllocator(this), true) + fMemoryAllocator(new (std::nothrow) ClientMemoryAllocator(this), true), + fAppFontManager(NULL) { if (fSignature.IsEmpty()) fSignature = "application/no-signature"; @@ -142,6 +144,9 @@ ServerApp::ServerApp(Desktop* desktop, port_id clientReplyPort, settings.GetDefaultFixedFont(fFixedFont); desktop->UnlockSingleWindow(); + fAppFontManager = new AppFontManager(); + fAppFontManager->Run(); + STRACE(("ServerApp %s:\n", Signature())); STRACE(("\tBApp port: %" B_PRId32 "\n", fClientReplyPort)); STRACE(("\tReceiver port: %" B_PRId32 "\n", fMessagePort)); @@ -205,6 +210,9 @@ ServerApp::~ServerApp() fDesktop->GetCursorManager().DeleteCursors(fClientTeam); + fAppFontManager->Lock(); + fAppFontManager->Quit(); + STRACE(("ServerApp %s::~ServerApp(): Exiting\n", Signature())); } @@ -1565,6 +1573,214 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) /* font messages */ + case AS_ADD_FONT_FILE: + { + FTRACE(("ServerApp %s: Received BFont creation request\n", + Signature())); + + // Add a font for an application from a file + + // Attached Data: + // 1) char* - path to font on disk + + // Returns: + // 1) uint16 - family ID of added font + // 2) uint16 - style ID of added font + // 3) uint16 - face of added font + + fAppFontManager->Lock(); + if (fAppFontManager->CountFamilies() > MAX_USER_FONTS) { + fLink.StartMessage(B_NOT_ALLOWED); + fAppFontManager->Unlock(); + fLink.Flush(); + break; + } + + uint16 familyID, styleID; + char* fontPath; + link.ReadString(&fontPath); + + status_t status = fAppFontManager->AddUserFontFromFile(fontPath, + familyID, styleID); + + fAppFontManager->Unlock(); + + if (status != B_OK) { + fLink.StartMessage(status); + } else { + ServerFont* font = new(std::nothrow) ServerFont(); + if (font == NULL) { + fLink.StartMessage(B_NO_MEMORY); + fLink.Flush(); + break; + } + + status = font->SetFamilyAndStyle(familyID, styleID, + fAppFontManager); + + if (status == B_OK) { + fLink.StartMessage(B_OK); + fLink.Attach(font->FamilyID()); + fLink.Attach(font->StyleID()); + fLink.Attach(font->Face()); + } else { + fLink.StartMessage(status); + delete font; + } + } + + fLink.Flush(); + break; + } + + case AS_ADD_FONT_MEMORY: + { + FTRACE(("ServerApp %s: Received BFont memory creation request\n", + Signature())); + + // Add a font for an application from a memory area + + // Attached Data: + // 1) area_id - id of memory area where font resides + // 2) uint32 - size of memory area for font + // 3) uint32 - offset to start of font memory + + // Returns: + // 1) uint16 - family ID of added font + // 2) uint16 - style ID of added font + // 3) uint16 - face of added font + + if (fAppFontManager->CountFamilies() > MAX_USER_FONTS) { + fLink.StartMessage(B_NOT_ALLOWED); + fLink.Flush(); + break; + } + + area_id fontAreaID, fontAreaCloneID; + area_info fontAreaInfo; + char* area_addr; + uint32 size, offset; + + link.Read(&fontAreaID); + link.Read(&size); + link.Read(&offset); + fontAreaCloneID = clone_area("user font", + (void **)&area_addr, + B_ANY_ADDRESS, + B_READ_AREA, + fontAreaID); + + if (fontAreaCloneID < B_OK) { + fLink.StartMessage(fontAreaCloneID); + fLink.Flush(); + break; + } + + status_t status = get_area_info(fontAreaCloneID, &fontAreaInfo); + if (status != B_OK) { + fLink.StartMessage(status); + fLink.Flush(); + delete_area(fontAreaCloneID); + break; + } + + uint32 fontMemorySize = fontAreaInfo.size - offset; + + if (size == 0) + size = fontMemorySize; + + // Check size of font area and reject if it's too large + if (size > MAX_FONT_DATA_SIZE_BYTES + || size > fontMemorySize) { + fLink.StartMessage(B_BAD_DATA); + fLink.Flush(); + delete_area(fontAreaCloneID); + break; + } + + FT_Byte* fontData = (FT_Byte*)(malloc (sizeof(FT_Byte) * size)); + if (fontData == NULL) { + delete_area(fontAreaCloneID); + fLink.StartMessage(B_BAD_DATA); + fLink.Flush(); + break; + } + + memcpy(fontData, (FT_Byte*)fontAreaInfo.address + offset, size); + + delete_area(fontAreaCloneID); + + uint16 familyID, styleID; + + fAppFontManager->Lock(); + status = fAppFontManager->AddUserFontFromMemory(fontData, size, + familyID, styleID); + + if (status != B_OK) { + fLink.StartMessage(status); + free(fontData); + } else { + ServerFont* font = new(std::nothrow) ServerFont(); + if (font == NULL) { + free(fontData); + fLink.StartMessage(B_NO_MEMORY); + fLink.Flush(); + break; + } + + status = font->SetFamilyAndStyle(familyID, styleID, + fAppFontManager); + + if (status == B_OK) { + font->SetFontData(fontData, size); + fLink.StartMessage(B_OK); + fLink.Attach(font->FamilyID()); + fLink.Attach(font->StyleID()); + fLink.Attach(font->Face()); + } else { + fLink.StartMessage(status); + free(fontData); + delete font; + } + } + + fAppFontManager->Unlock(); + fLink.Flush(); + break; + } + + case AS_REMOVE_FONT: + { + STRACE(("ServerApp %s: Received BFont removal request\n", + Signature())); + + // Remove an application-added font + + // Attached Data: + // 1) uint16 - familyID of font to remove + // 2) uint16 - styleID of font to remove + + uint16 familyID, styleID; + link.Read(&familyID); + link.Read(&styleID); + + status_t status = B_OK; + + fAppFontManager->Lock(); + FontStyle* style = fAppFontManager->GetStyle(familyID, styleID); + + if (style != NULL) { + status = fAppFontManager->RemoveUserFont(familyID, styleID); + } else + status = B_BAD_VALUE; + + fAppFontManager->Unlock(); + + fLink.StartMessage(status); + fLink.Flush(); + break; + } + case AS_SET_SYSTEM_FONT: { FTRACE(("ServerApp %s: AS_SET_SYSTEM_FONT\n", Signature())); @@ -1708,7 +1924,7 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) case AS_GET_FONT_LIST_REVISION: { - STRACE(("ServerApp %s: AS_GET_FONT_LIST_REVISION\n", Signature())); + FTRACE(("ServerApp %s: AS_GET_FONT_LIST_REVISION\n", Signature())); fLink.StartMessage(B_OK); fLink.Attach( @@ -1739,12 +1955,19 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) gFontManager->Lock(); FontFamily* family = gFontManager->FamilyAt(index); + if (family == NULL) { + gFontManager->Unlock(); + fAppFontManager->Lock(); + family = fAppFontManager->FamilyAt(index); + } + if (family) { fLink.StartMessage(B_OK); fLink.AttachString(family->Name()); fLink.Attach(family->Flags()); int32 count = family->CountStyles(); + fLink.Attach(count); for (int32 i = 0; i < count; i++) { @@ -1757,7 +1980,11 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) } else fLink.StartMessage(B_BAD_VALUE); - gFontManager->Unlock(); + if (gFontManager->IsLocked()) + gFontManager->Unlock(); + if (fAppFontManager->IsLocked()) + fAppFontManager->Unlock(); + fLink.Flush(); break; } @@ -1780,7 +2007,13 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) gFontManager->Lock(); - FontStyle *fontStyle = gFontManager->GetStyle(familyID, styleID); + FontStyle* fontStyle = gFontManager->GetStyle(familyID, styleID); + if (fontStyle == NULL) { + gFontManager->Unlock(); + fAppFontManager->Lock(); + fontStyle = fAppFontManager->GetStyle(familyID, styleID); + } + if (fontStyle != NULL) { fLink.StartMessage(B_OK); fLink.AttachString(fontStyle->Family()->Name()); @@ -1789,7 +2022,10 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) fLink.StartMessage(B_BAD_VALUE); fLink.Flush(); - gFontManager->Unlock(); + if (gFontManager->IsLocked()) + gFontManager->Unlock(); + if (fAppFontManager->IsLocked()) + fAppFontManager->Unlock(); break; } @@ -1822,8 +2058,14 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) // get the font and return IDs and face gFontManager->Lock(); - FontStyle *fontStyle = gFontManager->GetStyle(family, style, + FontStyle* fontStyle = gFontManager->GetStyle(family, style, familyID, styleID, face); + if (fontStyle == NULL) { + gFontManager->Unlock(); + fAppFontManager->Lock(); + fontStyle = fAppFontManager->GetStyle(family, style, + familyID, styleID, face); + } if (fontStyle != NULL) { fLink.StartMessage(B_OK); @@ -1837,7 +2079,10 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) } else fLink.StartMessage(B_NAME_NOT_FOUND); - gFontManager->Unlock(); + if (gFontManager->IsLocked()) + gFontManager->Unlock(); + if (fAppFontManager->IsLocked()) + fAppFontManager->Unlock(); } else fLink.StartMessage(B_BAD_VALUE); @@ -1862,14 +2107,24 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) gFontManager->Lock(); - FontStyle *fontStyle = gFontManager->GetStyle(familyID, styleID); - if (fontStyle) { + FontStyle* fontStyle = gFontManager->GetStyle(familyID, styleID); + if (fontStyle == NULL) { + gFontManager->Unlock(); + fAppFontManager->Lock(); + fontStyle = fAppFontManager->GetStyle(familyID, styleID); + } + + if (fontStyle != NULL) { fLink.StartMessage(B_OK); fLink.Attach((uint16)fontStyle->FileFormat()); } else fLink.StartMessage(B_BAD_VALUE); - gFontManager->Unlock(); + if (gFontManager->IsLocked()) + gFontManager->Unlock(); + if (fAppFontManager->IsLocked()) + fAppFontManager->Unlock(); + fLink.Flush(); break; } @@ -1890,12 +2145,12 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) // Returns: // 1) float - width of the string in pixels (numStrings times) - uint16 family, style; + uint16 familyID, styleID; float size; uint8 spacing; - link.Read(&family); - link.Read(&style); + link.Read(&familyID); + link.Read(&styleID); link.Read(&size); link.Read(&spacing); int32 numStrings; @@ -1923,7 +2178,10 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) ServerFont font; - if (font.SetFamilyAndStyle(family, style) == B_OK && size > 0) { + status_t status = font.SetFamilyAndStyle(familyID, styleID, + fAppFontManager); + + if (status == B_OK && size > 0) { font.SetSize(size); font.SetSpacing(spacing); @@ -1970,7 +2228,10 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) ServerFont font; - if (font.SetFamilyAndStyle(familyID, styleID) == B_OK && size > 0) { + status_t status = font.SetFamilyAndStyle(familyID, styleID, + fAppFontManager); + + if (status == B_OK && size > 0) { font.SetSize(size); fLink.StartMessage(B_OK); @@ -1999,14 +2260,24 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) gFontManager->Lock(); - FontStyle *fontStyle = gFontManager->GetStyle(familyID, styleID); + FontStyle* fontStyle = gFontManager->GetStyle(familyID, styleID); + if (fontStyle == NULL) { + gFontManager->Unlock(); + fAppFontManager->Lock(); + fontStyle = fAppFontManager->GetStyle(familyID, styleID); + } + if (fontStyle != NULL) { fLink.StartMessage(B_OK); fLink.Attach(fontStyle->TunedCount()); } else fLink.StartMessage(B_BAD_VALUE); - gFontManager->Unlock(); + if (gFontManager->IsLocked()) + gFontManager->Unlock(); + if (fAppFontManager->IsLocked()) + fAppFontManager->Unlock(); + fLink.Flush(); break; } @@ -2048,14 +2319,24 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) gFontManager->Lock(); - FontStyle *fontStyle = gFontManager->GetStyle(familyID, styleID); + FontStyle* fontStyle = gFontManager->GetStyle(familyID, styleID); + if (fontStyle == NULL) { + gFontManager->Unlock(); + fAppFontManager->Lock(); + fontStyle = fAppFontManager->GetStyle(familyID, styleID); + } + if (fontStyle != NULL) { fLink.StartMessage(B_OK); fLink.Attach(fontStyle->Flags()); } else fLink.StartMessage(B_BAD_VALUE); - gFontManager->Unlock(); + if (gFontManager->IsLocked()) + gFontManager->Unlock(); + if (fAppFontManager->IsLocked()) + fAppFontManager->Unlock(); + fLink.Flush(); break; } @@ -2077,7 +2358,13 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) gFontManager->Lock(); - FontStyle *fontStyle = gFontManager->GetStyle(familyID, styleID); + FontStyle* fontStyle = gFontManager->GetStyle(familyID, styleID); + if (fontStyle == NULL) { + gFontManager->Unlock(); + fAppFontManager->Lock(); + fontStyle = fAppFontManager->GetStyle(familyID, styleID); + } + if (fontStyle != NULL) { font_height height; fontStyle->GetHeight(size, height); @@ -2087,7 +2374,11 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) } else fLink.StartMessage(B_BAD_VALUE); - gFontManager->Unlock(); + if (gFontManager->IsLocked()) + gFontManager->Unlock(); + if (fAppFontManager->IsLocked()) + fAppFontManager->Unlock(); + fLink.Flush(); break; } @@ -2108,7 +2399,9 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) link.Read(&styleID); ServerFont font; - status_t status = font.SetFamilyAndStyle(familyID, styleID); + status_t status = font.SetFamilyAndStyle(familyID, styleID, + fAppFontManager); + if (status == B_OK) { unicode_block blocksForFont; font.GetUnicodeBlocks(blocksForFont); @@ -2143,7 +2436,9 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) link.Read(&end); ServerFont font; - status_t status = font.SetFamilyAndStyle(familyID, styleID); + status_t status = font.SetFamilyAndStyle(familyID, styleID, + fAppFontManager); + if (status == B_OK) { bool hasBlock; @@ -2204,7 +2499,9 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) link.Read(charArray, numBytes); ServerFont font; - status_t status = font.SetFamilyAndStyle(familyID, styleID); + status_t status = font.SetFamilyAndStyle(familyID, styleID, + fAppFontManager); + if (status == B_OK) { font.SetSize(size); font.SetShear(shear); @@ -2259,7 +2556,9 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) link.Read(charArray, numBytes); ServerFont font; - status_t status = font.SetFamilyAndStyle(familyID, styleID); + status_t status = font.SetFamilyAndStyle(familyID, styleID, + fAppFontManager); + if (status == B_OK) { status = font.GetHasGlyphs(charArray, numBytes, numChars, hasArray); @@ -2308,7 +2607,9 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) link.Read(charArray, numBytes); ServerFont font; - status_t status = font.SetFamilyAndStyle(familyID, styleID); + status_t status = font.SetFamilyAndStyle(familyID, styleID, + fAppFontManager); + if (status == B_OK) { status = font.GetEdges(charArray, numBytes, numChars, edgeArray); @@ -2386,7 +2687,9 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) link.Read(charArray, numBytes); ServerFont font; - status_t status = font.SetFamilyAndStyle(familyID, styleID); + status_t status = font.SetFamilyAndStyle(familyID, styleID, + fAppFontManager); + if (status == B_OK) { font.SetSize(size); font.SetSpacing(spacing); @@ -2471,7 +2774,9 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) // figure out escapements ServerFont font; - status_t status = font.SetFamilyAndStyle(familyID, styleID); + status_t status = font.SetFamilyAndStyle(familyID, styleID, + fAppFontManager); + if (status == B_OK) { font.SetSize(size); font.SetSpacing(spacing); @@ -2558,7 +2863,9 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) // figure out escapements ServerFont font; - status_t status = font.SetFamilyAndStyle(familyID, styleID); + status_t status = font.SetFamilyAndStyle(familyID, styleID, + fAppFontManager); + if (status == B_OK) { font.SetSize(size); font.SetRotation(rotation); @@ -2646,7 +2953,9 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) } ServerFont font; - status_t status = font.SetFamilyAndStyle(familyID, styleID); + status_t status = font.SetFamilyAndStyle(familyID, styleID, + fAppFontManager); + if (status == B_OK) { font.SetSize(ptsize); font.SetRotation(rotation); diff --git a/src/servers/app/ServerApp.h b/src/servers/app/ServerApp.h index 3908ef32b1..c21869d7d8 100644 --- a/src/servers/app/ServerApp.h +++ b/src/servers/app/ServerApp.h @@ -13,6 +13,7 @@ #define SERVER_APP_H +#include "AppFontManager.h" #include "ClientMemoryAllocator.h" #include "MessageLooper.h" #include "ServerFont.h" @@ -96,6 +97,7 @@ public: BPrivate::BTokenSpace& ViewTokens() { return fViewTokens; } void NotifyDeleteClientArea(area_id serverArea); + AppFontManager* FontManager() { return fAppFontManager; } private: virtual void _GetLooperName(char* name, size_t size); @@ -160,6 +162,8 @@ private: bool fIsActive; BReference fMemoryAllocator; + + AppFontManager* fAppFontManager; }; diff --git a/src/servers/app/ServerFont.cpp b/src/servers/app/ServerFont.cpp index fd86c70756..36122bfc56 100644 --- a/src/servers/app/ServerFont.cpp +++ b/src/servers/app/ServerFont.cpp @@ -13,8 +13,9 @@ #include "ServerFont.h" #include "Angle.h" +#include "AppFontManager.h" #include "GlyphLayoutEngine.h" -#include "FontManager.h" +#include "GlobalFontManager.h" #include "truncate_string.h" #include "utf8_functions.h" @@ -272,8 +273,10 @@ ServerFont::SetStyle(FontStyle* style) \return B_OK if successful, B_ERROR if not */ status_t -ServerFont::SetFamilyAndStyle(uint16 familyID, uint16 styleID) +ServerFont::SetFamilyAndStyle(uint16 familyID, uint16 styleID, + AppFontManager* fontManager) { + BReference style; if (gFontManager->Lock()) { @@ -282,8 +285,16 @@ ServerFont::SetFamilyAndStyle(uint16 familyID, uint16 styleID) gFontManager->Unlock(); } - if (style == NULL) - return B_ERROR; + if (style == NULL) { + if (fontManager != NULL && fontManager->Lock()) { + style.SetTo(fontManager->GetStyle(familyID, styleID), false); + + fontManager->Unlock(); + } + + if (style == NULL) + return B_ERROR; + } SetStyle(style); @@ -300,12 +311,12 @@ ServerFont::SetFamilyAndStyle(uint16 familyID, uint16 styleID) \return B_OK if successful, B_ERROR if not */ status_t -ServerFont::SetFamilyAndStyle(uint32 fontID) +ServerFont::SetFamilyAndStyle(uint32 fontID, AppFontManager* fontManager) { uint16 style = fontID & 0xFFFF; uint16 family = (fontID & 0xFFFF0000) >> 16; - return SetFamilyAndStyle(family, style); + return SetFamilyAndStyle(family, style, fontManager); } @@ -369,6 +380,9 @@ ServerFont::SetFace(uint16 face) uint32 ServerFont::GetFamilyAndStyle() const { + if (fStyle == NULL || fStyle->Family() == NULL) + return 0; + return (FamilyID() << 16) | StyleID(); } @@ -1188,3 +1202,10 @@ ServerFont::EmbeddedTransformation() const return transform; } + +void +ServerFont::SetFontData(FT_Byte* location, uint32 size) +{ + if (fStyle != NULL) + fStyle->SetFontData(location, size); +} diff --git a/src/servers/app/ServerFont.h b/src/servers/app/ServerFont.h index 244b02d625..f500b0b39a 100644 --- a/src/servers/app/ServerFont.h +++ b/src/servers/app/ServerFont.h @@ -15,7 +15,9 @@ #include #include +#include "AppFontManager.h" #include "FontFamily.h" +#include "FontManager.h" #include "GlobalSubpixelSettings.h" #include "Transformable.h" @@ -69,8 +71,10 @@ class ServerFont { void SetStyle(FontStyle* style); status_t SetFamilyAndStyle(uint16 familyID, - uint16 styleID); - status_t SetFamilyAndStyle(uint32 fontID); + uint16 styleID, + AppFontManager* fontManager = NULL); + status_t SetFamilyAndStyle(uint32 fontID, + AppFontManager* fontManager = NULL); uint16 StyleID() const { return fStyle->ID(); } @@ -167,8 +171,15 @@ class ServerFont { status_t IncludesUnicodeBlock(uint32 start, uint32 end, bool &hasBlock); + void SetFontData(FT_Byte* location, uint32 size); + uint32 FontDataSize() const + { return fStyle->FontDataSize(); } + FT_Byte* FontData() const + { return fStyle->FontData(); } + protected: friend class FontStyle; + FT_Face GetTransformedFace(bool rotate, bool shear) const; void PutTransformedFace(FT_Face face) const; diff --git a/src/servers/app/ServerPicture.cpp b/src/servers/app/ServerPicture.cpp index 69de264097..120746b013 100644 --- a/src/servers/app/ServerPicture.cpp +++ b/src/servers/app/ServerPicture.cpp @@ -19,7 +19,7 @@ #include "AlphaMask.h" #include "DrawingEngine.h" #include "DrawState.h" -#include "FontManager.h" +#include "GlobalFontManager.h" #include "Layer.h" #include "ServerApp.h" #include "ServerBitmap.h" diff --git a/src/servers/app/ServerWindow.cpp b/src/servers/app/ServerWindow.cpp index ffe2e03125..dd80f0837b 100644 --- a/src/servers/app/ServerWindow.cpp +++ b/src/servers/app/ServerWindow.cpp @@ -1299,7 +1299,8 @@ fDesktop->LockSingleWindow(); DTRACE(("ServerWindow %s: Message AS_VIEW_SET_FONT_STATE: " "View name: %s\n", fTitle, fCurrentView->Name())); - fCurrentView->CurrentState()->ReadFontFromLink(link); + fCurrentView->CurrentState()->ReadFontFromLink(link, + fServerApp->FontManager()); fWindow->GetDrawingEngine()->SetFont( fCurrentView->CurrentState()); break; diff --git a/src/servers/app/font/AppFontManager.cpp b/src/servers/app/font/AppFontManager.cpp new file mode 100644 index 0000000000..4f9fe0ba23 --- /dev/null +++ b/src/servers/app/font/AppFontManager.cpp @@ -0,0 +1,205 @@ +/* + * Copyright 2001-2016, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * DarkWyrm + * Axel Dörfler, axeld@pinc-software.de + */ + + +/*! Manages user font families and styles */ + + +#include "AppFontManager.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "FontFamily.h" +#include "FontManager.h" +#include "ServerConfig.h" +#include "ServerFont.h" + + +#define TRACE_FONT_MANAGER +#ifdef TRACE_FONT_MANAGER +# define FTRACE(x) debug_printf x +#else +# define FTRACE(x) ; +#endif + + +// #pragma mark - + + +/*! Sets high id number to avoid collisions with GlobalFontManager + The result of a collision would be that the global font is selected + rather than the application font. +*/ +AppFontManager::AppFontManager() + : FontManagerBase(false, "AppFontManager") +{ + fNextID = UINT16_MAX; +} + + +//! Frees all families and styles loaded by the application +AppFontManager::~AppFontManager() +{ + while (fFamilies.CountItems() > 0) { + FontFamily* family = fFamilies.ItemAt(0); + while (family->CountStyles() > 0) { + uint16 familyID = family->ID(); + uint16 styleID = family->StyleAt(0)->ID(); + FontKey fKey(familyID, styleID); + FontStyle* styleRef = fStyleHashTable.Get(fKey); + family->RemoveStyle(styleRef, this); + styleRef->ReleaseReference(); + } + + fFamilies.RemoveItem(family); + delete family; + } +} + + +void +AppFontManager::MessageReceived(BMessage* message) +{ + FontManagerBase::MessageReceived(message); +} + + +status_t +AppFontManager::_AddUserFont(FT_Face face, node_ref nodeRef, const char* path, + uint16& familyID, uint16& styleID) +{ + FontFamily* family = _FindFamily(face->family_name); + if (family != NULL + && family->HasStyle(face->style_name)) { + // prevent adding the same style twice + // (this indicates a problem with the installed fonts maybe?) + FT_Done_Face(face); + return B_NAME_IN_USE; + } + + if (family == NULL) { + family = new (std::nothrow) FontFamily(face->family_name, fNextID--); + + if (family == NULL + || !fFamilies.BinaryInsert(family, compare_font_families)) { + delete family; + FT_Done_Face(face); + return B_NO_MEMORY; + } + } + + FTRACE(("\tadd style: %s, %s\n", face->family_name, face->style_name)); + + // the FontStyle takes over ownership of the FT_Face object + FontStyle* style = new (std::nothrow) FontStyle(nodeRef, path, face); + + if (style == NULL || !family->AddStyle(style, this)) { + delete style; + delete family; + return B_NO_MEMORY; + } + + familyID = style->Family()->ID(); + styleID = style->ID(); + + fStyleHashTable.Put(FontKey(familyID, styleID), style); + + return B_OK; +} + + +/*! \brief Adds the FontFamily/FontStyle that is represented by this path. +*/ +status_t +AppFontManager::AddUserFontFromFile(const char* path, + uint16& familyID, uint16& styleID) +{ + ASSERT(IsLocked()); + + BEntry entry; + status_t status = entry.SetTo(path); + if (status != B_OK) + return status; + + node_ref nodeRef; + status = entry.GetNodeRef(&nodeRef); + if (status < B_OK) + return status; + + FT_Face face; + FT_Error error = FT_New_Face(gFreeTypeLibrary, path, 0, &face); + if (error != 0) + return error; + + status = _AddUserFont(face, nodeRef, path, familyID, styleID); + + return status; +} + + +/*! \brief Adds the FontFamily/FontStyle that is represented by the area in memory. +*/ +status_t +AppFontManager::AddUserFontFromMemory(const FT_Byte* fontAddress, uint32 size, + uint16& familyID, uint16& styleID) +{ + ASSERT(IsLocked()); + + node_ref nodeRef; + status_t status; + + FT_Face face; + FT_Error error = FT_New_Memory_Face(gFreeTypeLibrary, fontAddress, size, 0, + &face); + if (error != 0) + return error; + + status = _AddUserFont(face, nodeRef, "", familyID, styleID); + + return status; +} + + +/*! \brief Removes the FontFamily/FontStyle from the font manager. +*/ +status_t +AppFontManager::RemoveUserFont(uint16 familyID, uint16 styleID) +{ + ASSERT(IsLocked()); + + FontKey fKey(familyID, styleID); + FontStyle* styleRef = fStyleHashTable.Get(fKey); + fStyleHashTable.Remove(fKey); + + FontFamily* family = styleRef->Family(); + bool removed = family->RemoveStyle(styleRef, this); + + if (!removed) + syslog(LOG_DEBUG, "AppFontManager::RemoveUserFont style not removed from family\n"); + + fFamilies.RemoveItem(family); + delete family; + + styleRef->ReleaseReference(); + + return B_OK; +} diff --git a/src/servers/app/font/AppFontManager.h b/src/servers/app/font/AppFontManager.h new file mode 100644 index 0000000000..9b73f2ae92 --- /dev/null +++ b/src/servers/app/font/AppFontManager.h @@ -0,0 +1,66 @@ +/* + * Copyright 2001-2009, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * DarkWyrm + * Axel Dörfler, axeld@pinc-software.de + */ +#ifndef APP_FONT_MANAGER_H +#define APP_FONT_MANAGER_H + + +#include "FontManager.h" + + +#include +#include +#include +#include +#include + + +#include +#include FT_FREETYPE_H + +class BEntry; +class BPath; +struct node_ref; + +class FontFamily; +class FontStyle; +class ServerFont; + + +// font areas should be less than 20MB +#define MAX_FONT_DATA_SIZE_BYTES 20 * 1024 * 1024 +#define MAX_USER_FONTS 128 + +/*! + \class AppFontManager AppFontManager.h + \brief Manager for application-added fonts in the font subsystem +*/ +class AppFontManager : public FontManagerBase { +public: + AppFontManager(); + virtual ~AppFontManager(); + + virtual void MessageReceived(BMessage* message); + + status_t AddUserFontFromFile(const char* path, + uint16& familyID, uint16& styleID); + status_t AddUserFontFromMemory(const FT_Byte* fontAddress, + uint32 size, uint16& familyID, uint16& styleID); + status_t RemoveUserFont(uint16 familyID, uint16 styleID); + +private: + status_t _AddUserFont(FT_Face face, node_ref nodeRef, + const char* path, + uint16& familyID, uint16& styleID); +private: + + int32 fNextID; +}; + + +#endif /* APP_FONT_MANAGER_H */ diff --git a/src/servers/app/font/FontCacheEntry.cpp b/src/servers/app/font/FontCacheEntry.cpp index ef8af33af3..ea2b343fbf 100644 --- a/src/servers/app/font/FontCacheEntry.cpp +++ b/src/servers/app/font/FontCacheEntry.cpp @@ -158,12 +158,20 @@ FontCacheEntry::Init(const ServerFont& font, bool forceVector) FT_Encoding charMap = FT_ENCODING_NONE; bool hinting = font.Hinting(); - if (!fEngine.Init(font.Path(), 0, font.Size(), charMap, - renderingType, hinting)) { + bool success; + if (font.FontData() != NULL) + success = fEngine.Init(NULL, 0, font.Size(), charMap, + renderingType, hinting, (const void*)font.FontData(), font.FontDataSize()); + else + success = fEngine.Init(font.Path(), 0, font.Size(), charMap, + renderingType, hinting); + + if (!success) { fprintf(stderr, "FontCacheEntry::Init() - some error loading font " "file %s\n", font.Path()); return false; } + if (fGlyphCache->Init() != B_OK) { fprintf(stderr, "FontCacheEntry::Init() - failed to allocate " "GlyphCache table for font file %s\n", font.Path()); diff --git a/src/servers/app/font/FontEngine.cpp b/src/servers/app/font/FontEngine.cpp index bc3467a70d..7f4bb1d2ea 100644 --- a/src/servers/app/font/FontEngine.cpp +++ b/src/servers/app/font/FontEngine.cpp @@ -638,7 +638,7 @@ FontEngine::GetKerning(uint32 first, uint32 second, double* x, double* y) bool FontEngine::Init(const char* fontFilePath, unsigned faceIndex, double size, FT_Encoding charMap, glyph_rendering ren_type, bool hinting, - const char* fontFileBuffer, const long fontFileBufferSize) + const void* fontFileBuffer, const long fontFileBufferSize) { if (!fLibraryInitialized) return false; diff --git a/src/servers/app/font/FontEngine.h b/src/servers/app/font/FontEngine.h index d599d07e8a..fb6de74124 100644 --- a/src/servers/app/font/FontEngine.h +++ b/src/servers/app/font/FontEngine.h @@ -82,7 +82,7 @@ class FontEngine { FT_Encoding char_map, glyph_rendering ren_type, bool hinting, - const char* fontFileBuffer = NULL, + const void* fontFileBuffer = NULL, const long fontFileBufferSize = 0); int LastError() const diff --git a/src/servers/app/font/FontFamily.cpp b/src/servers/app/font/FontFamily.cpp index eef0ea0300..041495e81e 100644 --- a/src/servers/app/font/FontFamily.cpp +++ b/src/servers/app/font/FontFamily.cpp @@ -12,7 +12,7 @@ #include "FontFamily.h" -#include "FontManager.h" +#include "GlobalFontManager.h" #include @@ -78,6 +78,8 @@ FontFamily::~FontFamily() // we remove us before deleting the style, so that the font manager // is not contacted to remove the style from us style->_SetFontFamily(NULL, -1); + + style->ReleaseReference(); } } @@ -98,7 +100,7 @@ FontFamily::Name() const \param style pointer to FontStyle object to be added */ bool -FontFamily::AddStyle(FontStyle *style) +FontFamily::AddStyle(FontStyle* style, AppFontManager* fontManager) { if (!style) return false; @@ -115,6 +117,7 @@ FontFamily::AddStyle(FontStyle *style) return false; style->_SetFontFamily(this, fNextID++); + style->_SetFontManager(fontManager); // force a refresh if a request for font flags is needed fFlags = kInvalidFamilyFlags; @@ -129,9 +132,9 @@ FontFamily::AddStyle(FontStyle *style) The font style will not be deleted. */ bool -FontFamily::RemoveStyle(FontStyle* style) +FontFamily::RemoveStyle(FontStyle* style, AppFontManager* fontManager) { - if (!gFontManager->IsLocked()) { + if (!gFontManager->IsLocked() && fontManager == NULL) { debugger("FontFamily::RemoveStyle() called without having the font manager locked!"); return false; } diff --git a/src/servers/app/font/FontFamily.h b/src/servers/app/font/FontFamily.h index bb72641977..db874ff01e 100644 --- a/src/servers/app/font/FontFamily.h +++ b/src/servers/app/font/FontFamily.h @@ -13,6 +13,7 @@ #include #include +#include "AppFontManager.h" #include "FontStyle.h" @@ -30,8 +31,10 @@ public: const char* Name() const; - bool AddStyle(FontStyle* style); - bool RemoveStyle(FontStyle* style); + bool AddStyle(FontStyle* style, + AppFontManager* fontManager = NULL); + bool RemoveStyle(FontStyle* style, + AppFontManager* fontManager = NULL); FontStyle* GetStyle(const char* style) const; FontStyle* GetStyleMatchingFace(uint16 face) const; diff --git a/src/servers/app/font/FontManager.cpp b/src/servers/app/font/FontManager.cpp index a1f2561ebd..2aa000a102 100644 --- a/src/servers/app/font/FontManager.cpp +++ b/src/servers/app/font/FontManager.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -38,795 +39,51 @@ #endif -// TODO: needs some more work for multi-user support - FT_Library gFreeTypeLibrary; -FontManager *gFontManager = NULL; -struct FontManager::font_directory { - node_ref directory; - uid_t user; - gid_t group; - uint32 revision; - BObjectList styles; - - bool AlreadyScanned() const { return revision != 0; } - FontStyle* FindStyle(const node_ref& nodeRef) const; -}; - -struct FontManager::font_mapping { - BString family; - BString style; - entry_ref ref; -}; +// #pragma mark - -FontStyle* -FontManager::font_directory::FindStyle(const node_ref& nodeRef) const -{ - for (int32 i = styles.CountItems(); i-- > 0;) { - FontStyle* style = styles.ItemAt(i); - - if (nodeRef == style->NodeRef()) - return style; - } - - return NULL; -} - - -static status_t -set_entry(node_ref& nodeRef, const char* name, BEntry& entry) -{ - entry_ref ref; - ref.device = nodeRef.device; - ref.directory = nodeRef.node; - - status_t status = ref.set_name(name); - if (status != B_OK) - return status; - - return entry.SetTo(&ref); -} - - -static int -compare_font_families(const FontFamily* a, const FontFamily* b) +int +FontManagerBase::compare_font_families(const FontFamily* a, const FontFamily* b) { return strcmp(a->Name(), b->Name()); } -// #pragma mark - - - -//! Does basic set up so that directories can be scanned -FontManager::FontManager() - : BLooper("Font Manager"), - fDirectories(10, true), - fMappings(10, true), +//! Initializes FreeType if requested +FontManagerBase::FontManagerBase(bool init_freetype, const char* className) + : BLooper(className), fFamilies(20), - fDefaultPlainFont(NULL), - fDefaultBoldFont(NULL), - fDefaultFixedFont(NULL), - fScanned(false), - fNextID(0) + fNextID(0), + fHasFreetypeLibrary(init_freetype) { - fInitStatus = FT_Init_FreeType(&gFreeTypeLibrary) == 0 ? B_OK : B_ERROR; - - if (fInitStatus == B_OK) { - _AddSystemPaths(); - _LoadRecentFontMappings(); - - fInitStatus = _SetDefaultFonts(); - - if (fInitStatus == B_OK) { - // Precache the plain and bold fonts - _PrecacheFontFile(fDefaultPlainFont.Get()); - _PrecacheFontFile(fDefaultBoldFont.Get()); - - // Post a message so we scan the initial paths. - PostMessage(B_PULSE); - } - } + if (init_freetype == true) + fInitStatus = FT_Init_FreeType(&gFreeTypeLibrary) == 0 ? B_OK : B_ERROR; } -//! Frees items allocated in the constructor and shuts down FreeType -FontManager::~FontManager() +//! Frees font families shuts down FreeType if it was initialized +FontManagerBase::~FontManagerBase() { - fDefaultPlainFont.Unset(); - fDefaultBoldFont.Unset(); - fDefaultFixedFont.Unset(); - // free families before we're done with FreeType for (int32 i = fFamilies.CountItems(); i-- > 0;) delete fFamilies.ItemAt(i); - FT_Done_FreeType(gFreeTypeLibrary); + if (fHasFreetypeLibrary == true) + FT_Done_FreeType(gFreeTypeLibrary); } void -FontManager::MessageReceived(BMessage* message) +FontManagerBase::MessageReceived(BMessage* message) { switch (message->what) { - case B_NODE_MONITOR: - { - int32 opcode; - if (message->FindInt32("opcode", &opcode) != B_OK) - return; - - switch (opcode) { - case B_ENTRY_CREATED: - { - const char* name; - node_ref nodeRef; - if (message->FindInt32("device", &nodeRef.device) != B_OK - || message->FindInt64("directory", &nodeRef.node) != B_OK - || message->FindString("name", &name) != B_OK) - break; - - // TODO: make this better (possible under Haiku) - snooze(100000); - // let the font be written completely before trying to open it - - BEntry entry; - if (set_entry(nodeRef, name, entry) != B_OK) - break; - - if (entry.IsDirectory()) { - // a new directory to watch for us - _AddPath(entry); - } else { - // a new font - font_directory* directory = _FindDirectory(nodeRef); - if (directory == NULL) { - // unknown directory? how come? - break; - } - - _AddFont(*directory, entry); - } - break; - } - - case B_ENTRY_MOVED: - { - // has the entry been moved into a monitored directory or has - // it been removed from one? - const char* name; - node_ref nodeRef; - uint64 fromNode; - uint64 node; - if (message->FindInt32("device", &nodeRef.device) != B_OK - || message->FindInt64("to directory", &nodeRef.node) != B_OK - || message->FindInt64("from directory", (int64 *)&fromNode) != B_OK - || message->FindInt64("node", (int64 *)&node) != B_OK - || message->FindString("name", &name) != B_OK) - break; - - font_directory* directory = _FindDirectory(nodeRef); - - BEntry entry; - if (set_entry(nodeRef, name, entry) != B_OK) - break; - - if (directory != NULL) { - // something has been added to our watched font directories - - // test, if the source directory is one of ours as well - nodeRef.node = fromNode; - font_directory* fromDirectory = _FindDirectory(nodeRef); - - if (entry.IsDirectory()) { - if (fromDirectory == NULL) { - // there is a new directory to watch for us - _AddPath(entry); - FTRACE(("new directory moved in")); - } else { - // A directory from our watched directories has - // been renamed or moved within the watched - // directories - we only need to update the - // path names of the styles in that directory - nodeRef.node = node; - directory = _FindDirectory(nodeRef); - if (directory != NULL) { - for (int32 i = 0; i < directory->styles.CountItems(); i++) { - FontStyle* style = directory->styles.ItemAt(i); - style->UpdatePath(directory->directory); - } - } - FTRACE(("directory renamed")); - } - } else { - if (fromDirectory != NULL) { - // find style in source and move it to the target - nodeRef.node = node; - FontStyle* style = fromDirectory->FindStyle(nodeRef); - if (style != NULL) { - fromDirectory->styles.RemoveItem(style, false); - directory->styles.AddItem(style); - style->UpdatePath(directory->directory); - } - FTRACE(("font moved")); - } else { - FTRACE(("font added: %s\n", name)); - _AddFont(*directory, entry); - } - } - } else { - // and entry has been removed from our font directories - if (entry.IsDirectory()) { - if (entry.GetNodeRef(&nodeRef) == B_OK - && (directory = _FindDirectory(nodeRef)) != NULL) - _RemoveDirectory(directory); - } else { - // remove font style from directory - _RemoveStyle(nodeRef.device, fromNode, node); - } - } - break; - } - - case B_ENTRY_REMOVED: - { - node_ref nodeRef; - uint64 directoryNode; - if (message->FindInt32("device", &nodeRef.device) != B_OK - || message->FindInt64("directory", (int64 *)&directoryNode) != B_OK - || message->FindInt64("node", &nodeRef.node) != B_OK) - break; - - font_directory* directory = _FindDirectory(nodeRef); - if (directory != NULL) { - // the directory has been removed, so we remove it as well - _RemoveDirectory(directory); - } else { - // remove font style from directory - _RemoveStyle(nodeRef.device, directoryNode, nodeRef.node); - } - break; - } - } - break; - } - default: BLooper::MessageReceived(message); break; } - - // Scan fonts here if we need to, preventing other threads from having to do so. - _ScanFontsIfNecessary(); -} - - -void -FontManager::SaveRecentFontMappings() -{ -} - - -void -FontManager::_AddDefaultMapping(const char* family, const char* style, - const char* path) -{ - font_mapping* mapping = new (std::nothrow) font_mapping; - if (mapping == NULL) - return; - - mapping->family = family; - mapping->style = style; - BEntry entry(path); - - if (entry.GetRef(&mapping->ref) != B_OK - || !entry.Exists() - || !fMappings.AddItem(mapping)) - delete mapping; -} - - -bool -FontManager::_LoadRecentFontMappings() -{ - // default known mappings - // TODO: load them for real, and use these as a fallback - - BPath ttfontsPath; - if (find_directory(B_BEOS_FONTS_DIRECTORY, &ttfontsPath) == B_OK) { - ttfontsPath.Append("ttfonts"); - - BPath veraFontPath = ttfontsPath; - veraFontPath.Append("NotoSansDisplay-Regular.ttf"); - _AddDefaultMapping("Noto Sans Display", "Book", veraFontPath.Path()); - - veraFontPath.SetTo(ttfontsPath.Path()); - veraFontPath.Append("NotoSansDisplay-Bold.ttf"); - _AddDefaultMapping("Noto Sans Display", "Bold", veraFontPath.Path()); - - veraFontPath.SetTo(ttfontsPath.Path()); - veraFontPath.Append("NotoSansMono-Regular.ttf"); - _AddDefaultMapping("Noto Sans Mono", "Regular", veraFontPath.Path()); - - return true; - } - - return false; -} - - -status_t -FontManager::_AddMappedFont(const char* familyName, const char* styleName) -{ - FTRACE(("_AddMappedFont(family = \"%s\", style = \"%s\")\n", - familyName, styleName)); - - for (int32 i = 0; i < fMappings.CountItems(); i++) { - font_mapping* mapping = fMappings.ItemAt(i); - - if (mapping->family == familyName) { - if (styleName != NULL && mapping->style != styleName) - continue; - - BEntry entry(&mapping->ref); - if (entry.InitCheck() != B_OK) - continue; - - // find parent directory - - node_ref nodeRef; - nodeRef.device = mapping->ref.device; - nodeRef.node = mapping->ref.directory; - font_directory* directory = _FindDirectory(nodeRef); - if (directory == NULL) { - // unknown directory, maybe this is a user font - try - // to create the missing directory - BPath path(&entry); - if (path.GetParent(&path) != B_OK - || _CreateDirectories(path.Path()) != B_OK - || (directory = _FindDirectory(nodeRef)) == NULL) - continue; - } - - return _AddFont(*directory, entry); - } - } - - return B_ENTRY_NOT_FOUND; -} - - -/*! \brief Removes the style from the font directory. - - It doesn't necessary delete the font style, if it's still - in use, though. -*/ -void -FontManager::_RemoveStyle(font_directory& directory, FontStyle* style) -{ - FTRACE(("font removed: %s\n", style->Name())); - - directory.styles.RemoveItem(style); - directory.revision++; - - fStyleHashTable.Remove(FontKey(style->Family()->ID(), style->ID())); - - style->ReleaseReference(); -} - - -void -FontManager::_RemoveStyle(dev_t device, uint64 directoryNode, uint64 node) -{ - // remove font style from directory - node_ref nodeRef; - nodeRef.device = device; - nodeRef.node = directoryNode; - - font_directory* directory = _FindDirectory(nodeRef); - if (directory != NULL) { - // find style in directory and remove it - nodeRef.node = node; - FontStyle* style = directory->FindStyle(nodeRef); - if (style != NULL) - _RemoveStyle(*directory, style); - } -} - - -FontStyle* -FontManager::_GetDefaultStyle(const char *familyName, const char *styleName, - const char *fallbackFamily, const char *fallbackStyle, - uint16 fallbackFace) -{ - // try to find a matching font - - FontStyle* style = GetStyle(familyName, styleName); - if (style == NULL) { - style = GetStyle(fallbackFamily, fallbackStyle); - if (style == NULL) { - style = FindStyleMatchingFace(fallbackFace); - if (style == NULL && FamilyAt(0) != NULL) - style = FamilyAt(0)->StyleAt(0); - } - } - - return style; -} - - -/*! \brief Sets the fonts that will be used when you create an empty - ServerFont without specifying a style, as well as the default - Desktop fonts if there are no settings available. -*/ -status_t -FontManager::_SetDefaultFonts() -{ - FontStyle* style = NULL; - - // plain font - style = _GetDefaultStyle(DEFAULT_PLAIN_FONT_FAMILY, DEFAULT_PLAIN_FONT_STYLE, - FALLBACK_PLAIN_FONT_FAMILY, FALLBACK_PLAIN_FONT_STYLE, B_REGULAR_FACE); - if (style == NULL) - return B_ERROR; - - fDefaultPlainFont.SetTo(new (std::nothrow) ServerFont(*style, DEFAULT_FONT_SIZE)); - if (!fDefaultPlainFont.IsSet()) - return B_NO_MEMORY; - - // bold font - style = _GetDefaultStyle(DEFAULT_BOLD_FONT_FAMILY, DEFAULT_BOLD_FONT_STYLE, - FALLBACK_BOLD_FONT_FAMILY, FALLBACK_BOLD_FONT_STYLE, B_BOLD_FACE); - - fDefaultBoldFont.SetTo(new (std::nothrow) ServerFont(*style, DEFAULT_FONT_SIZE)); - if (!fDefaultBoldFont.IsSet()) - return B_NO_MEMORY; - - // fixed font - style = _GetDefaultStyle(DEFAULT_FIXED_FONT_FAMILY, DEFAULT_FIXED_FONT_STYLE, - FALLBACK_FIXED_FONT_FAMILY, DEFAULT_FIXED_FONT_STYLE, B_REGULAR_FACE); - - fDefaultFixedFont.SetTo(new (std::nothrow) ServerFont(*style, DEFAULT_FONT_SIZE)); - if (!fDefaultFixedFont.IsSet()) - return B_NO_MEMORY; - - fDefaultFixedFont->SetSpacing(B_FIXED_SPACING); - - return B_OK; -} - - -void -FontManager::_PrecacheFontFile(const ServerFont* font) -{ - if (font == NULL) - return; - - size_t bufferSize = 32768; - uint8* buffer = new (std::nothrow) uint8[bufferSize]; - if (buffer == NULL) { - // We don't care. Pre-caching doesn't make sense anyways when there - // is not enough RAM... - return; - } - - BFile file(font->Path(), B_READ_ONLY); - if (file.InitCheck() != B_OK) { - delete[] buffer; - return; - } - - while (true) { - // We just want the file in the kernel file cache... - ssize_t read = file.Read(buffer, bufferSize); - if (read < (ssize_t)bufferSize) - break; - } - - delete[] buffer; -} - - -void -FontManager::_AddSystemPaths() -{ - BPath path; - if (find_directory(B_SYSTEM_FONTS_DIRECTORY, &path, true) == B_OK) - _AddPath(path.Path()); - - // We don't scan these in test mode to help shave off some startup time -#if !TEST_MODE - if (find_directory(B_SYSTEM_NONPACKAGED_FONTS_DIRECTORY, &path, true) == B_OK) - _AddPath(path.Path()); -#endif -} - - -void -FontManager::_ScanFontsIfNecessary() -{ - if (!fScanned) - _ScanFonts(); -} - - -//! Scans all currently known font directories -void -FontManager::_ScanFonts() -{ - if (fScanned) - return; - - for (int32 i = fDirectories.CountItems(); i-- > 0;) { - font_directory* directory = fDirectories.ItemAt(i); - - if (directory->AlreadyScanned()) - continue; - - _ScanFontDirectory(*directory); - } - - fScanned = true; -} - - -/*! \brief Adds the FontFamily/FontStyle that is represented by this path. -*/ -status_t -FontManager::_AddFont(font_directory& directory, BEntry& entry) -{ - node_ref nodeRef; - status_t status = entry.GetNodeRef(&nodeRef); - if (status < B_OK) - return status; - - BPath path; - status = entry.GetPath(&path); - if (status < B_OK) - return status; - - FT_Face face; - FT_Error error = FT_New_Face(gFreeTypeLibrary, path.Path(), 0, &face); - if (error != 0) - return B_ERROR; - - FontFamily *family = _FindFamily(face->family_name); - if (family != NULL && family->HasStyle(face->style_name)) { - // prevent adding the same style twice - // (this indicates a problem with the installed fonts maybe?) - FT_Done_Face(face); - return B_OK; - } - - if (family == NULL) { - family = new (std::nothrow) FontFamily(face->family_name, fNextID++); - if (family == NULL - || !fFamilies.BinaryInsert(family, compare_font_families)) { - delete family; - FT_Done_Face(face); - return B_NO_MEMORY; - } - } - - FTRACE(("\tadd style: %s, %s\n", face->family_name, face->style_name)); - - // the FontStyle takes over ownership of the FT_Face object - FontStyle *style = new (std::nothrow) FontStyle(nodeRef, path.Path(), face); - if (style == NULL || !family->AddStyle(style)) { - delete style; - delete family; - return B_NO_MEMORY; - } - - directory.styles.AddItem(style); - fStyleHashTable.Put(FontKey(style->Family()->ID(), style->ID()), style); - - if (directory.AlreadyScanned()) - directory.revision++; - - return B_OK; -} - - -FontManager::font_directory* -FontManager::_FindDirectory(node_ref& nodeRef) -{ - for (int32 i = fDirectories.CountItems(); i-- > 0;) { - font_directory* directory = fDirectories.ItemAt(i); - - if (directory->directory == nodeRef) - return directory; - } - - return NULL; -} - - -void -FontManager::_RemoveDirectory(font_directory* directory) -{ - FTRACE(("FontManager: Remove directory (%" B_PRIdINO ")!\n", - directory->directory.node)); - - fDirectories.RemoveItem(directory, false); - - // TODO: remove styles from this directory! - - watch_node(&directory->directory, B_STOP_WATCHING, this); - delete directory; -} - - -status_t -FontManager::_AddPath(const char* path) -{ - BEntry entry; - status_t status = entry.SetTo(path); - if (status != B_OK) - return status; - - return _AddPath(entry); -} - - -status_t -FontManager::_AddPath(BEntry& entry, font_directory** _newDirectory) -{ - node_ref nodeRef; - status_t status = entry.GetNodeRef(&nodeRef); - if (status != B_OK) - return status; - - // check if we are already know this directory - - font_directory* directory = _FindDirectory(nodeRef); - if (directory != NULL) { - if (_newDirectory) - *_newDirectory = directory; - return B_OK; - } - - // it's a new one, so let's add it - - directory = new (std::nothrow) font_directory; - if (directory == NULL) - return B_NO_MEMORY; - - struct stat stat; - status = entry.GetStat(&stat); - if (status != B_OK) { - delete directory; - return status; - } - - directory->directory = nodeRef; - directory->user = stat.st_uid; - directory->group = stat.st_gid; - directory->revision = 0; - - status = watch_node(&nodeRef, B_WATCH_DIRECTORY, this); - if (status != B_OK) { - // we cannot watch this directory - while this is unfortunate, - // it's not a critical error - printf("could not watch directory %" B_PRIdDEV ":%" B_PRIdINO "\n", - nodeRef.device, nodeRef.node); - // TODO: should go into syslog() - } else { - BPath path(&entry); - FTRACE(("FontManager: now watching: %s\n", path.Path())); - } - - fDirectories.AddItem(directory); - - if (_newDirectory) - *_newDirectory = directory; - - fScanned = false; - return B_OK; -} - - -/*! \brief Creates all unknown font_directories of the specified path - but - only if one of its parent directories is already known. - - This method is used to create the font_directories for font_mappings. - It recursively walks upwards in the directory hierarchy until it finds - a known font_directory (or hits the root directory, in which case it - bails out). -*/ -status_t -FontManager::_CreateDirectories(const char* path) -{ - FTRACE(("_CreateDirectories(path = %s)\n", path)); - - if (!strcmp(path, "/")) { - // we walked our way up to the root - return B_ENTRY_NOT_FOUND; - } - - BEntry entry; - status_t status = entry.SetTo(path); - if (status != B_OK) - return status; - - node_ref nodeRef; - status = entry.GetNodeRef(&nodeRef); - if (status != B_OK) - return status; - - // check if we are already know this directory - - font_directory* directory = _FindDirectory(nodeRef); - if (directory != NULL) - return B_OK; - - // We don't know this one yet - keep walking the path upwards - // and try to find a match. - - BPath parent(path); - status = parent.GetParent(&parent); - if (status != B_OK) - return status; - - status = _CreateDirectories(parent.Path()); - if (status != B_OK) - return status; - - // We have our match, create sub directory - - return _AddPath(path); -} - - -/*! \brief Scan a folder for all valid fonts - \param directoryPath Path of the folder to scan. -*/ -status_t -FontManager::_ScanFontDirectory(font_directory& fontDirectory) -{ - // This bad boy does all the real work. It loads each entry in the - // directory. If a valid font file, it adds both the family and the style. - - BDirectory directory; - status_t status = directory.SetTo(&fontDirectory.directory); - if (status != B_OK) - return status; - - BEntry entry; - while (directory.GetNextEntry(&entry) == B_OK) { - if (entry.IsDirectory()) { - // scan this directory recursively - font_directory* newDirectory; - if (_AddPath(entry, &newDirectory) == B_OK && newDirectory != NULL) - _ScanFontDirectory(*newDirectory); - - continue; - } - -// TODO: Commenting this out makes my "Unicode glyph lookup" -// work with our default fonts. The real fix is to select the -// Unicode char map (if supported), and/or adjust the -// utf8 -> glyph-index mapping everywhere to handle other -// char maps. We could also ignore fonts that don't support -// the Unicode lookup as a temporary "solution". -#if 0 - FT_CharMap charmap = _GetSupportedCharmap(face); - if (!charmap) { - FT_Done_Face(face); - continue; - } - - face->charmap = charmap; -#endif - - _AddFont(fontDirectory, entry); - // takes over ownership of the FT_Face object - } - - fontDirectory.revision = 1; - return B_OK; } @@ -836,7 +93,7 @@ FontManager::_ScanFontDirectory(font_directory& fontDirectory) \return An FT_CharMap or NULL if unsuccessful */ FT_CharMap -FontManager::_GetSupportedCharmap(const FT_Face& face) +FontManagerBase::_GetSupportedCharmap(const FT_Face& face) { for (int32 i = 0; i < face->num_charmaps; i++) { FT_CharMap charmap = face->charmaps[i]; @@ -869,33 +126,13 @@ FontManager::_GetSupportedCharmap(const FT_Face& face) } -int32 -FontManager::CheckRevision(uid_t user) -{ - BAutolock locker(this); - int32 revision = 0; - - _ScanFontsIfNecessary(); - - for (int32 i = 0; i < fDirectories.CountItems(); i++) { - font_directory* directory = fDirectories.ItemAt(i); - - // TODO: for now, add all directories - revision += directory->revision; - } - - return revision; -} - /*! \brief Counts the number of font families available \return The number of unique font families currently available */ int32 -FontManager::CountFamilies() +FontManagerBase::CountFamilies() { - _ScanFontsIfNecessary(); - return fFamilies.CountItems(); } @@ -905,10 +142,8 @@ FontManager::CountFamilies() \return The number of font styles currently available for the font family */ int32 -FontManager::CountStyles(const char *familyName) +FontManagerBase::CountStyles(const char *familyName) { - _ScanFontsIfNecessary(); - FontFamily *family = GetFamily(familyName); if (family) return family->CountStyles(); @@ -922,10 +157,8 @@ FontManager::CountStyles(const char *familyName) \return The number of font styles currently available for the font family */ int32 -FontManager::CountStyles(uint16 familyID) +FontManagerBase::CountStyles(uint16 familyID) { - _ScanFontsIfNecessary(); - FontFamily *family = GetFamily(familyID); if (family) return family->CountStyles(); @@ -935,30 +168,20 @@ FontManager::CountStyles(uint16 familyID) FontFamily* -FontManager::FamilyAt(int32 index) const +FontManagerBase::FamilyAt(int32 index) const { + ASSERT(IsLocked()); + return fFamilies.ItemAt(index); } -FontFamily* -FontManager::_FindFamily(const char* name) const -{ - if (name == NULL) - return NULL; - - FontFamily family(name, 0); - return const_cast(fFamilies.BinarySearch(family, - compare_font_families)); -} - - /*! \brief Locates a FontFamily object by name \param name The family to find \return Pointer to the specified family or NULL if not found. */ FontFamily* -FontManager::GetFamily(const char* name) +FontManagerBase::GetFamily(const char* name) { if (name == NULL) return NULL; @@ -967,20 +190,12 @@ FontManager::GetFamily(const char* name) if (family != NULL) return family; - if (fScanned) - return NULL; - - // try font mappings before failing - if (_AddMappedFont(name) == B_OK) - return _FindFamily(name); - - _ScanFonts(); return _FindFamily(name); } FontFamily* -FontManager::GetFamily(uint16 familyID) const +FontManagerBase::GetFamily(uint16 familyID) const { FontKey key(familyID, 0); FontStyle* style = fStyleHashTable.Get(key); @@ -992,7 +207,7 @@ FontManager::GetFamily(uint16 familyID) const FontStyle* -FontManager::GetStyleByIndex(const char* familyName, int32 index) +FontManagerBase::GetStyleByIndex(const char* familyName, int32 index) { FontFamily* family = GetFamily(familyName); if (family != NULL) @@ -1003,7 +218,7 @@ FontManager::GetStyleByIndex(const char* familyName, int32 index) FontStyle* -FontManager::GetStyleByIndex(uint16 familyID, int32 index) +FontManagerBase::GetStyleByIndex(uint16 familyID, int32 index) { FontFamily* family = GetFamily(familyID); if (family != NULL) @@ -1013,6 +228,21 @@ FontManager::GetStyleByIndex(uint16 familyID, int32 index) } +/*! \brief Retrieves the FontStyle object + \param family ID for the font's family + \param style ID of the font's style + \return The FontStyle having those attributes or NULL if not available +*/ +FontStyle* +FontManagerBase::GetStyle(uint16 familyID, uint16 styleID) const +{ + ASSERT(IsLocked()); + + FontKey key(familyID, styleID); + return fStyleHashTable.Get(key); +} + + /*! \brief Retrieves the FontStyle object that comes closest to the one specified. @@ -1026,9 +256,11 @@ FontManager::GetStyleByIndex(uint16 familyID, int32 index) \return The FontStyle having those attributes or NULL if not available */ FontStyle* -FontManager::GetStyle(const char* familyName, const char* styleName, +FontManagerBase::GetStyle(const char* familyName, const char* styleName, uint16 familyID, uint16 styleID, uint16 face) { + ASSERT(IsLocked()); + FontFamily* family; // find family @@ -1048,14 +280,6 @@ FontManager::GetStyle(const char* familyName, const char* styleName, if (fontStyle != NULL) return fontStyle; - // before we fail, we try the mappings for a match - if (_AddMappedFont(family->Name(), styleName) == B_OK) { - fontStyle = family->GetStyle(styleName); - if (fontStyle != NULL) - return fontStyle; - } - - _ScanFonts(); return family->GetStyle(styleName); } @@ -1067,24 +291,11 @@ FontManager::GetStyle(const char* familyName, const char* styleName, } -/*! \brief Retrieves the FontStyle object - \param family ID for the font's family - \param style ID of the font's style - \return The FontStyle having those attributes or NULL if not available -*/ -FontStyle* -FontManager::GetStyle(uint16 familyID, uint16 styleID) const -{ - FontKey key(familyID, styleID); - return fStyleHashTable.Get(key); -} - - /*! \brief If you don't find your preferred font style, but are anxious to have one fitting your needs, you may want to use this method. */ FontStyle* -FontManager::FindStyleMatchingFace(uint16 face) const +FontManagerBase::FindStyleMatchingFace(uint16 face) const { int32 count = fFamilies.CountItems(); @@ -1104,8 +315,10 @@ FontManager::FindStyleMatchingFace(uint16 face) const At this point, the style is already no longer available to the user. */ void -FontManager::RemoveStyle(FontStyle* style) +FontManagerBase::RemoveStyle(FontStyle* style) { + ASSERT(IsLocked()); + FontFamily* family = style->Family(); if (family == NULL) debugger("family is NULL!"); @@ -1120,51 +333,13 @@ FontManager::RemoveStyle(FontStyle* style) } -const ServerFont* -FontManager::DefaultPlainFont() const +FontFamily* +FontManagerBase::_FindFamily(const char* name) const { - return fDefaultPlainFont.Get(); -} - - -const ServerFont* -FontManager::DefaultBoldFont() const -{ - return fDefaultBoldFont.Get(); -} - - -const ServerFont* -FontManager::DefaultFixedFont() const -{ - return fDefaultFixedFont.Get(); -} - - -void -FontManager::AttachUser(uid_t userID) -{ - BAutolock locker(this); - -#if !TEST_MODE - // TODO: actually, find_directory() cannot know which user ID we want here - // TODO: avoids user fonts in safe mode - BPath path; - if (find_directory(B_USER_FONTS_DIRECTORY, &path, true) == B_OK) - _AddPath(path.Path()); - if (find_directory(B_USER_NONPACKAGED_FONTS_DIRECTORY, &path, true) - == B_OK) { - _AddPath(path.Path()); - } -#endif -} - - -void -FontManager::DetachUser(uid_t userID) -{ - BAutolock locker(this); - - // TODO! -} + if (name == NULL) + return NULL; + FontFamily family(name, 0); + return const_cast(fFamilies.BinarySearch(family, + compare_font_families)); +} \ No newline at end of file diff --git a/src/servers/app/font/FontManager.h b/src/servers/app/font/FontManager.h index 818c86d31d..ddd42fae96 100644 --- a/src/servers/app/font/FontManager.h +++ b/src/servers/app/font/FontManager.h @@ -31,89 +31,55 @@ class ServerFont; /*! \class FontManager FontManager.h - \brief Manager for the largest part of the font subsystem + \brief Base class interface used by GlobalFontManager and AppFontManager */ -class FontManager : public BLooper { +class FontManagerBase : public BLooper { public: - FontManager(); - virtual ~FontManager(); + FontManagerBase(bool init_freetype, + const char* className = "FontManagerBase"); + virtual ~FontManagerBase(); status_t InitCheck() { return fInitStatus; } - void SaveRecentFontMappings(); + void SetInitStatus(status_t new_status) + { fInitStatus = new_status; } virtual void MessageReceived(BMessage* message); - int32 CheckRevision(uid_t user); - int32 CountFamilies(); + virtual int32 CountFamilies(); - int32 CountStyles(const char* family); - int32 CountStyles(uint16 familyID); + virtual int32 CountStyles(const char* family); + virtual int32 CountStyles(uint16 familyID); FontFamily* FamilyAt(int32 index) const; - FontFamily* GetFamily(uint16 familyID) const; - FontFamily* GetFamily(const char* name); + virtual FontFamily* GetFamily(uint16 familyID) const; + virtual FontFamily* GetFamily(const char* name); FontStyle* GetStyleByIndex(const char* family, int32 index); FontStyle* GetStyleByIndex(uint16 familyID, int32 index); - FontStyle* GetStyle(const char* family, const char* style, - uint16 familyID = 0xffff, - uint16 styleID = 0xffff, uint16 face = 0); - FontStyle* GetStyle(const char *family, uint16 styleID); - FontStyle* GetStyle(uint16 familyID, + + virtual FontStyle* GetStyle(uint16 familyID, uint16 styleID) const; + virtual FontStyle* GetStyle(const char* familyName, + const char* styleName, + uint16 familyID = 0xffff, + uint16 styleID = 0xffff, + uint16 face = 0); FontStyle* FindStyleMatchingFace(uint16 face) const; void RemoveStyle(FontStyle* style); // This call must not be used by anything else than class // FontStyle. - const ServerFont* DefaultPlainFont() const; - const ServerFont* DefaultBoldFont() const; - const ServerFont* DefaultFixedFont() const; - - void AttachUser(uid_t userID); - void DetachUser(uid_t userID); - -private: - struct font_directory; - struct font_mapping; - - void _AddDefaultMapping(const char* family, - const char* style, const char* path); - bool _LoadRecentFontMappings(); - status_t _AddMappedFont(const char* family, - const char* style = NULL); - FontStyle* _GetDefaultStyle(const char* familyName, - const char* styleName, - const char* fallbackFamily, - const char* fallbackStyle, - uint16 fallbackFace); - status_t _SetDefaultFonts(); - void _PrecacheFontFile(const ServerFont* font); - void _AddSystemPaths(); - font_directory* _FindDirectory(node_ref& nodeRef); - void _RemoveDirectory(font_directory* directory); - status_t _CreateDirectories(const char* path); - status_t _AddPath(const char* path); - status_t _AddPath(BEntry& entry, - font_directory** _newDirectory = NULL); - - void _RemoveStyle(font_directory& directory, - FontStyle* style); - void _RemoveStyle(dev_t device, uint64 directory, - uint64 node); - FontFamily* _FindFamily(const char* family) const; - - void _ScanFontsIfNecessary(); - void _ScanFonts(); - status_t _ScanFontDirectory(font_directory& directory); - status_t _AddFont(font_directory& directory, - BEntry& entry); FT_CharMap _GetSupportedCharmap(const FT_Face& face); -private: +protected: + FontFamily* _FindFamily(const char* family) const; + + static int compare_font_families(const FontFamily* a, + const FontFamily* b); + struct FontKey { FontKey(uint16 family, uint16 style) : familyID(family), styleID(style) {} @@ -132,31 +98,17 @@ private: uint16 familyID, styleID; }; -private: status_t fInitStatus; - typedef BObjectList DirectoryList; - typedef BObjectList MappingList; typedef BObjectList FamilyList; - - DirectoryList fDirectories; - MappingList fMappings; FamilyList fFamilies; HashMap > fStyleHashTable; - ObjectDeleter - fDefaultPlainFont; - ObjectDeleter - fDefaultBoldFont; - ObjectDeleter - fDefaultFixedFont; - - bool fScanned; - int32 fNextID; + uint16 fNextID; + bool fHasFreetypeLibrary; }; extern FT_Library gFreeTypeLibrary; -extern FontManager* gFontManager; #endif /* FONT_MANAGER_H */ diff --git a/src/servers/app/font/FontStyle.cpp b/src/servers/app/font/FontStyle.cpp index 878c62c394..600e494d69 100644 --- a/src/servers/app/font/FontStyle.cpp +++ b/src/servers/app/font/FontStyle.cpp @@ -12,7 +12,7 @@ #include "FontFamily.h" #include "ServerFont.h" -#include "FontManager.h" +#include "GlobalFontManager.h" #include @@ -38,7 +38,8 @@ FontStyle::FontStyle(node_ref& nodeRef, const char* path, FT_Face face) fID(0), fBounds(0, 0, 0, 0), fFace(_TranslateStyleToFace(face->style_name)), - fFullAndHalfFixed(false) + fFullAndHalfFixed(false), + fFontData(NULL) { fName.Truncate(B_FONT_STYLE_LENGTH); // make sure this style can be found using the Be API @@ -93,12 +94,20 @@ FontStyle::FontStyle(node_ref& nodeRef, const char* path, FT_Face face) FontStyle::~FontStyle() { // make sure the font server is ours - if (fFamily != NULL && gFontManager->Lock()) { - gFontManager->RemoveStyle(this); - gFontManager->Unlock(); + if (fFamily != NULL) { + if (fFontManager != NULL && fFontManager->Lock()) { + fFontManager->RemoveStyle(this); + fFontManager->Unlock(); + } else if (gFontManager->Lock()) { + gFontManager->RemoveStyle(this); + gFontManager->Unlock(); + } } FT_Done_Face(fFreeTypeFace); + + if (fFontData != NULL) + free(fFontData); } @@ -260,3 +269,13 @@ FontStyle::_TranslateStyleToFace(const char* name) const } +void +FontStyle::SetFontData(FT_Byte* location, uint32 size) +{ + // if memory was already allocated here, we should free it so it's not leaked + if (fFontData != NULL) + free(fFontData); + + fFontDataSize = size; + fFontData = location; +} \ No newline at end of file diff --git a/src/servers/app/font/FontStyle.h b/src/servers/app/font/FontStyle.h index 06b36932ae..bed57d4b4c 100644 --- a/src/servers/app/font/FontStyle.h +++ b/src/servers/app/font/FontStyle.h @@ -22,8 +22,11 @@ #include #include FT_FREETYPE_H +#include "AppFontManager.h" + struct node_ref; +class AppFontManager; class FontFamily; class ServerFont; @@ -131,11 +134,19 @@ class FontStyle : public BReferenceable { status_t UpdateFace(FT_Face face); + uint32 FontDataSize() const + { return fFontDataSize; } + + void SetFontData(FT_Byte* location, uint32 size); + FT_Byte* FontData() const + { return fFontData; } + private: friend class FontFamily; uint16 _TranslateStyleToFace(const char *name) const; void _SetFontFamily(FontFamily* family, uint16 id); - + void _SetFontManager(AppFontManager* fontManager) + { fFontManager = fontManager; } private: FT_Face fFreeTypeFace; BString fName; @@ -150,6 +161,10 @@ class FontStyle : public BReferenceable { font_height fHeight; uint16 fFace; bool fFullAndHalfFixed; + + FT_Byte* fFontData; + uint32 fFontDataSize; + AppFontManager* fFontManager; }; #endif // FONT_STYLE_H_ diff --git a/src/servers/app/font/GlobalFontManager.cpp b/src/servers/app/font/GlobalFontManager.cpp new file mode 100644 index 0000000000..fd9842e14d --- /dev/null +++ b/src/servers/app/font/GlobalFontManager.cpp @@ -0,0 +1,1039 @@ +/* + * Copyright 2001-2016, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * DarkWyrm + * Axel Dörfler, axeld@pinc-software.de + */ + + +/*! Manages font families and styles */ + + +#include "GlobalFontManager.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "FontFamily.h" +#include "FontManager.h" +#include "ServerConfig.h" +#include "ServerFont.h" + + +//#define TRACE_FONT_MANAGER +#ifdef TRACE_GLOBAL_FONT_MANAGER +# define FTRACE(x) printf x +#else +# define FTRACE(x) ; +#endif + + +// TODO: needs some more work for multi-user support + +GlobalFontManager* gFontManager = NULL; + + +struct GlobalFontManager::font_directory { + node_ref directory; + uid_t user; + gid_t group; + uint32 revision; + BObjectList styles; + + bool AlreadyScanned() const { return revision != 0; } + FontStyle* FindStyle(const node_ref& nodeRef) const; +}; + + +struct GlobalFontManager::font_mapping { + BString family; + BString style; + entry_ref ref; +}; + + +FontStyle* +GlobalFontManager::font_directory::FindStyle(const node_ref& nodeRef) const +{ + for (int32 i = styles.CountItems(); i-- > 0;) { + FontStyle* style = styles.ItemAt(i); + + if (nodeRef == style->NodeRef()) + return style; + } + + return NULL; +} + + +static status_t +set_entry(node_ref& nodeRef, const char* name, BEntry& entry) +{ + entry_ref ref; + ref.device = nodeRef.device; + ref.directory = nodeRef.node; + + status_t status = ref.set_name(name); + if (status != B_OK) + return status; + + return entry.SetTo(&ref); +} + + +// #pragma mark - + + +//! Does basic set up so that directories can be scanned +GlobalFontManager::GlobalFontManager() + : FontManagerBase(true, "GlobalFontManager"), + fDirectories(10, true), + fMappings(10, true), + + fDefaultPlainFont(NULL), + fDefaultBoldFont(NULL), + fDefaultFixedFont(NULL), + + fScanned(false) +{ + if (InitCheck() == B_OK) { + _AddSystemPaths(); + _LoadRecentFontMappings(); + + status_t status = _SetDefaultFonts(); + + if (status == B_OK) { + // Precache the plain and bold fonts + _PrecacheFontFile(fDefaultPlainFont.Get()); + _PrecacheFontFile(fDefaultBoldFont.Get()); + + // Post a message so we scan the initial paths. + PostMessage(B_PULSE); + } + + SetInitStatus(status); + } +} + + +//! Frees items allocated in the constructor and deletes all global families +GlobalFontManager::~GlobalFontManager() +{ + fDefaultPlainFont.Unset(); + fDefaultBoldFont.Unset(); + fDefaultFixedFont.Unset(); + + while (fFamilies.CountItems() > 0) + delete fFamilies.ItemAt(0); +} + + +void +GlobalFontManager::MessageReceived(BMessage* message) +{ + switch (message->what) { + case B_NODE_MONITOR: + { + int32 opcode; + if (message->FindInt32("opcode", &opcode) != B_OK) + return; + + switch (opcode) { + case B_ENTRY_CREATED: + { + const char* name; + node_ref nodeRef; + if (message->FindInt32("device", &nodeRef.device) != B_OK + || message->FindInt64("directory", &nodeRef.node) != B_OK + || message->FindString("name", &name) != B_OK) + break; + + // TODO: make this better (possible under Haiku) + snooze(100000); + // let the font be written completely before trying to open it + + BEntry entry; + if (set_entry(nodeRef, name, entry) != B_OK) + break; + + if (entry.IsDirectory()) { + // a new directory to watch for us + _AddPath(entry); + } else { + // a new font + font_directory* directory = _FindDirectory(nodeRef); + if (directory == NULL) { + // unknown directory? how come? + break; + } + + _AddFont(*directory, entry); + } + break; + } + + case B_ENTRY_MOVED: + { + // has the entry been moved into a monitored directory or has + // it been removed from one? + const char* name; + node_ref nodeRef; + uint64 fromNode; + uint64 node; + if (message->FindInt32("device", &nodeRef.device) != B_OK + || message->FindInt64("to directory", &nodeRef.node) != B_OK + || message->FindInt64("from directory", (int64 *)&fromNode) != B_OK + || message->FindInt64("node", (int64 *)&node) != B_OK + || message->FindString("name", &name) != B_OK) + break; + + font_directory* directory = _FindDirectory(nodeRef); + + BEntry entry; + if (set_entry(nodeRef, name, entry) != B_OK) + break; + + if (directory != NULL) { + // something has been added to our watched font directories + + // test, if the source directory is one of ours as well + nodeRef.node = fromNode; + font_directory* fromDirectory = _FindDirectory(nodeRef); + + if (entry.IsDirectory()) { + if (fromDirectory == NULL) { + // there is a new directory to watch for us + _AddPath(entry); + FTRACE(("new directory moved in")); + } else { + // A directory from our watched directories has + // been renamed or moved within the watched + // directories - we only need to update the + // path names of the styles in that directory + nodeRef.node = node; + directory = _FindDirectory(nodeRef); + if (directory != NULL) { + for (int32 i = 0; i < directory->styles.CountItems(); i++) { + FontStyle* style = directory->styles.ItemAt(i); + style->UpdatePath(directory->directory); + } + } + FTRACE(("directory renamed")); + } + } else { + if (fromDirectory != NULL) { + // find style in source and move it to the target + nodeRef.node = node; + FontStyle* style = fromDirectory->FindStyle(nodeRef); + if (style != NULL) { + fromDirectory->styles.RemoveItem(style, false); + directory->styles.AddItem(style); + style->UpdatePath(directory->directory); + } + FTRACE(("font moved")); + } else { + FTRACE(("font added: %s\n", name)); + _AddFont(*directory, entry); + } + } + } else { + // and entry has been removed from our font directories + if (entry.IsDirectory()) { + if (entry.GetNodeRef(&nodeRef) == B_OK + && (directory = _FindDirectory(nodeRef)) != NULL) + _RemoveDirectory(directory); + } else { + // remove font style from directory + _RemoveStyle(nodeRef.device, fromNode, node); + } + } + break; + } + + case B_ENTRY_REMOVED: + { + node_ref nodeRef; + uint64 directoryNode; + if (message->FindInt32("device", &nodeRef.device) != B_OK + || message->FindInt64("directory", (int64 *)&directoryNode) != B_OK + || message->FindInt64("node", &nodeRef.node) != B_OK) + break; + + font_directory* directory = _FindDirectory(nodeRef); + if (directory != NULL) { + // the directory has been removed, so we remove it as well + _RemoveDirectory(directory); + } else { + // remove font style from directory + _RemoveStyle(nodeRef.device, directoryNode, nodeRef.node); + } + break; + } + } + break; + } + + default: + FontManagerBase::MessageReceived(message); + break; + } + + // Scan fonts here if we need to, preventing other threads from having to do so. + _ScanFontsIfNecessary(); +} + + +int32 +GlobalFontManager::CheckRevision(uid_t user) +{ + BAutolock locker(this); + int32 revision = 0; + + _ScanFontsIfNecessary(); + + for (int32 i = 0; i < fDirectories.CountItems(); i++) { + font_directory* directory = fDirectories.ItemAt(i); + + // TODO: for now, add all directories + revision += directory->revision; + } + + return revision; +} + + +void +GlobalFontManager::SaveRecentFontMappings() +{ +} + + +void +GlobalFontManager::_AddDefaultMapping(const char* family, const char* style, + const char* path) +{ + font_mapping* mapping = new (std::nothrow) font_mapping; + if (mapping == NULL) + return; + + mapping->family = family; + mapping->style = style; + BEntry entry(path); + + if (entry.GetRef(&mapping->ref) != B_OK + || !entry.Exists() + || !fMappings.AddItem(mapping)) + delete mapping; +} + + +bool +GlobalFontManager::_LoadRecentFontMappings() +{ + // default known mappings + // TODO: load them for real, and use these as a fallback + + BPath ttfontsPath; + if (find_directory(B_BEOS_FONTS_DIRECTORY, &ttfontsPath) == B_OK) { + ttfontsPath.Append("ttfonts"); + + BPath veraFontPath = ttfontsPath; + veraFontPath.Append("NotoSansDisplay-Regular.ttf"); + _AddDefaultMapping("Noto Sans Display", "Book", veraFontPath.Path()); + + veraFontPath.SetTo(ttfontsPath.Path()); + veraFontPath.Append("NotoSansDisplay-Bold.ttf"); + _AddDefaultMapping("Noto Sans Display", "Bold", veraFontPath.Path()); + + veraFontPath.SetTo(ttfontsPath.Path()); + veraFontPath.Append("NotoSansMono-Regular.ttf"); + _AddDefaultMapping("Noto Sans Mono", "Regular", veraFontPath.Path()); + + return true; + } + + return false; +} + + +status_t +GlobalFontManager::_AddMappedFont(const char* familyName, const char* styleName) +{ + FTRACE(("_AddMappedFont(family = \"%s\", style = \"%s\")\n", + familyName, styleName)); + + for (int32 i = 0; i < fMappings.CountItems(); i++) { + font_mapping* mapping = fMappings.ItemAt(i); + + if (mapping->family == familyName) { + if (styleName != NULL && mapping->style != styleName) + continue; + + BEntry entry(&mapping->ref); + if (entry.InitCheck() != B_OK) + continue; + + // find parent directory + + node_ref nodeRef; + nodeRef.device = mapping->ref.device; + nodeRef.node = mapping->ref.directory; + font_directory* directory = _FindDirectory(nodeRef); + if (directory == NULL) { + // unknown directory, maybe this is a user font - try + // to create the missing directory + BPath path(&entry); + if (path.GetParent(&path) != B_OK + || _CreateDirectories(path.Path()) != B_OK + || (directory = _FindDirectory(nodeRef)) == NULL) + continue; + } + + return _AddFont(*directory, entry); + } + } + + return B_ENTRY_NOT_FOUND; +} + + +FontStyle* +GlobalFontManager::_GetDefaultStyle(const char* familyName, const char* styleName, + const char* fallbackFamily, const char* fallbackStyle, + uint16 fallbackFace) +{ + // try to find a matching font + FontStyle* style = GetStyle(familyName, styleName); + if (style == NULL) { + style = GetStyle(fallbackFamily, fallbackStyle); + if (style == NULL) { + style = FindStyleMatchingFace(fallbackFace); + if (style == NULL && FamilyAt(0) != NULL) + style = FamilyAt(0)->StyleAt(0); + } + } + + return style; +} + + +/*! \brief Sets the fonts that will be used when you create an empty + ServerFont without specifying a style, as well as the default + Desktop fonts if there are no settings available. +*/ +status_t +GlobalFontManager::_SetDefaultFonts() +{ + // plain font + FontStyle* style = _GetDefaultStyle(DEFAULT_PLAIN_FONT_FAMILY, + DEFAULT_PLAIN_FONT_STYLE, FALLBACK_PLAIN_FONT_FAMILY, + DEFAULT_PLAIN_FONT_STYLE, + B_REGULAR_FACE); + if (style == NULL) + return B_ERROR; + + fDefaultPlainFont.SetTo(new (std::nothrow) ServerFont(*style, + DEFAULT_FONT_SIZE)); + if (!fDefaultPlainFont.IsSet()) + return B_NO_MEMORY; + + // bold font + style = _GetDefaultStyle(DEFAULT_BOLD_FONT_FAMILY, DEFAULT_BOLD_FONT_STYLE, + FALLBACK_BOLD_FONT_FAMILY, DEFAULT_BOLD_FONT_STYLE, B_BOLD_FACE); + + fDefaultBoldFont.SetTo(new (std::nothrow) ServerFont(*style, + DEFAULT_FONT_SIZE)); + if (!fDefaultBoldFont.IsSet()) + return B_NO_MEMORY; + + // fixed font + style = _GetDefaultStyle(DEFAULT_FIXED_FONT_FAMILY, DEFAULT_FIXED_FONT_STYLE, + FALLBACK_FIXED_FONT_FAMILY, DEFAULT_FIXED_FONT_STYLE, B_REGULAR_FACE); + + fDefaultFixedFont.SetTo(new (std::nothrow) ServerFont(*style, + DEFAULT_FONT_SIZE)); + if (!fDefaultFixedFont.IsSet()) + return B_NO_MEMORY; + + fDefaultFixedFont->SetSpacing(B_FIXED_SPACING); + + return B_OK; +} + + +/*! \brief Removes the style from the font directory. + + It doesn't necessary delete the font style, if it's still + in use, though. +*/ +void +GlobalFontManager::_RemoveStyle(font_directory& directory, FontStyle* style) +{ + FTRACE(("font removed: %s\n", style->Name())); + + directory.styles.RemoveItem(style); + directory.revision++; + + fStyleHashTable.Remove(FontKey(style->Family()->ID(), style->ID())); + + style->ReleaseReference(); +} + + +void +GlobalFontManager::_RemoveStyle(dev_t device, uint64 directoryNode, uint64 node) +{ + // remove font style from directory + node_ref nodeRef; + nodeRef.device = device; + nodeRef.node = directoryNode; + + font_directory* directory = _FindDirectory(nodeRef); + if (directory != NULL) { + // find style in directory and remove it + nodeRef.node = node; + FontStyle* style = directory->FindStyle(nodeRef); + if (style != NULL) + _RemoveStyle(*directory, style); + } +} + + +/*! \brief Counts the number of font families available + \return The number of unique font families currently available +*/ +int32 +GlobalFontManager::CountFamilies() +{ + _ScanFontsIfNecessary(); + + return fFamilies.CountItems(); +} + + +/*! \brief Counts the number of styles available in a font family + \param family Name of the font family to scan + \return The number of font styles currently available for the font family +*/ +int32 +GlobalFontManager::CountStyles(const char* familyName) +{ + _ScanFontsIfNecessary(); + + FontFamily* family = GetFamily(familyName); + if (family) + return family->CountStyles(); + + return 0; +} + + +/*! \brief Counts the number of styles available in a font family + \param family Name of the font family to scan + \return The number of font styles currently available for the font family +*/ +int32 +GlobalFontManager::CountStyles(uint16 familyID) +{ + _ScanFontsIfNecessary(); + + FontFamily* family = GetFamily(familyID); + if (family) + return family->CountStyles(); + + return 0; +} + + +FontStyle* +GlobalFontManager::GetStyle(uint16 familyID, uint16 styleID) const +{ + return FontManagerBase::GetStyle(familyID, styleID); +} + + +/*! \brief Retrieves the FontStyle object that comes closest to the one + specified. + + \param family The font's family or NULL in which case \a familyID is used + \param style The font's style or NULL in which case \a styleID is used + \param familyID will only be used if \a family is NULL (or empty) + \param styleID will only be used if \a style is NULL (or empty) + \param face is used to specify the style if both \a style is NULL or empty + and styleID is 0xffff. + + \return The FontStyle having those attributes or NULL if not available +*/ +FontStyle* +GlobalFontManager::GetStyle(const char* familyName, const char* styleName, + uint16 familyID, uint16 styleID, uint16 face) +{ + ASSERT(IsLocked()); + + FontFamily* family; + + // find family + + if (familyName != NULL && familyName[0]) + family = GetFamily(familyName); + else + family = GetFamily(familyID); + + if (family == NULL) + return NULL; + + // find style + + if (styleName != NULL && styleName[0]) { + FontStyle* fontStyle = family->GetStyle(styleName); + if (fontStyle != NULL) + return fontStyle; + + // before we fail, we try the mappings for a match + if (_AddMappedFont(family->Name(), styleName) == B_OK) { + fontStyle = family->GetStyle(styleName); + if (fontStyle != NULL) + return fontStyle; + } + + _ScanFonts(); + return family->GetStyle(styleName); + } + + if (styleID != 0xffff) + return family->GetStyleByID(styleID); + + // try to get from face + return family->GetStyleMatchingFace(face); +} + + +void +GlobalFontManager::_PrecacheFontFile(const ServerFont* font) +{ + if (font == NULL) + return; + + size_t bufferSize = 32768; + uint8* buffer = new (std::nothrow) uint8[bufferSize]; + if (buffer == NULL) { + // We don't care. Pre-caching doesn't make sense anyways when there + // is not enough RAM... + return; + } + + BFile file(font->Path(), B_READ_ONLY); + if (file.InitCheck() != B_OK) { + delete[] buffer; + return; + } + + while (true) { + // We just want the file in the kernel file cache... + ssize_t read = file.Read(buffer, bufferSize); + if (read < (ssize_t)bufferSize) + break; + } + + delete[] buffer; +} + + +void +GlobalFontManager::_AddSystemPaths() +{ + BPath path; + if (find_directory(B_SYSTEM_FONTS_DIRECTORY, &path, true) == B_OK) + _AddPath(path.Path()); + + // We don't scan these in test mode to help shave off some startup time +#if !TEST_MODE + if (find_directory(B_SYSTEM_NONPACKAGED_FONTS_DIRECTORY, &path, true) == B_OK) + _AddPath(path.Path()); +#endif +} + + +void +GlobalFontManager::_ScanFontsIfNecessary() +{ + if (!fScanned) + _ScanFonts(); +} + + +//! Scans all currently known font directories +void +GlobalFontManager::_ScanFonts() +{ + if (fScanned) + return; + + for (int32 i = fDirectories.CountItems(); i-- > 0;) { + font_directory* directory = fDirectories.ItemAt(i); + + if (directory->AlreadyScanned()) + continue; + + _ScanFontDirectory(*directory); + } + + fScanned = true; +} + + +/*! \brief Adds the FontFamily/FontStyle that is represented by this path. +*/ +status_t +GlobalFontManager::_AddFont(font_directory& directory, BEntry& entry) +{ + node_ref nodeRef; + status_t status = entry.GetNodeRef(&nodeRef); + if (status < B_OK) + return status; + + BPath path; + status = entry.GetPath(&path); + if (status < B_OK) + return status; + + FT_Face face; + FT_Error error = FT_New_Face(gFreeTypeLibrary, path.Path(), 0, &face); + if (error != 0) + return B_ERROR; + + FontFamily* family = _FindFamily(face->family_name); + if (family != NULL && family->HasStyle(face->style_name)) { + // prevent adding the same style twice + // (this indicates a problem with the installed fonts maybe?) + FT_Done_Face(face); + return B_OK; + } + + if (family == NULL) { + family = new (std::nothrow) FontFamily(face->family_name, fNextID++); + if (family == NULL + || !fFamilies.BinaryInsert(family, compare_font_families)) { + delete family; + FT_Done_Face(face); + return B_NO_MEMORY; + } + } + + FTRACE(("\tadd style: %s, %s\n", face->family_name, face->style_name)); + + // the FontStyle takes over ownership of the FT_Face object + FontStyle* style = new (std::nothrow) FontStyle(nodeRef, path.Path(), face); + if (style == NULL || !family->AddStyle(style)) { + delete style; + delete family; + return B_NO_MEMORY; + } + + directory.styles.AddItem(style); + fStyleHashTable.Put(FontKey(style->Family()->ID(), style->ID()), style); + + if (directory.AlreadyScanned()) + directory.revision++; + + return B_OK; +} + + +GlobalFontManager::font_directory* +GlobalFontManager::_FindDirectory(node_ref& nodeRef) +{ + for (int32 i = fDirectories.CountItems(); i-- > 0;) { + font_directory* directory = fDirectories.ItemAt(i); + + if (directory->directory == nodeRef) + return directory; + } + + return NULL; +} + + +void +GlobalFontManager::_RemoveDirectory(font_directory* directory) +{ + FTRACE(("FontManager: Remove directory (%" B_PRIdINO ")!\n", + directory->directory.node)); + + fDirectories.RemoveItem(directory, false); + + // TODO: remove styles from this directory! + + watch_node(&directory->directory, B_STOP_WATCHING, this); + delete directory; +} + + +status_t +GlobalFontManager::_AddPath(const char* path) +{ + BEntry entry; + status_t status = entry.SetTo(path); + if (status != B_OK) + return status; + + return _AddPath(entry); +} + + +status_t +GlobalFontManager::_AddPath(BEntry& entry, font_directory** _newDirectory) +{ + node_ref nodeRef; + status_t status = entry.GetNodeRef(&nodeRef); + if (status != B_OK) + return status; + + // check if we are already know this directory + + font_directory* directory = _FindDirectory(nodeRef); + if (directory != NULL) { + if (_newDirectory) + *_newDirectory = directory; + return B_OK; + } + + // it's a new one, so let's add it + + directory = new (std::nothrow) font_directory; + if (directory == NULL) + return B_NO_MEMORY; + + struct stat stat; + status = entry.GetStat(&stat); + if (status != B_OK) { + delete directory; + return status; + } + + directory->directory = nodeRef; + directory->user = stat.st_uid; + directory->group = stat.st_gid; + directory->revision = 0; + + status = watch_node(&nodeRef, B_WATCH_DIRECTORY, this); + if (status != B_OK) { + // we cannot watch this directory - while this is unfortunate, + // it's not a critical error + printf("could not watch directory %" B_PRIdDEV ":%" B_PRIdINO "\n", + nodeRef.device, nodeRef.node); + // TODO: should go into syslog() + } else { + BPath path(&entry); + FTRACE(("FontManager: now watching: %s\n", path.Path())); + } + + fDirectories.AddItem(directory); + + if (_newDirectory) + *_newDirectory = directory; + + fScanned = false; + return B_OK; +} + + +/*! \brief Creates all unknown font_directories of the specified path - but + only if one of its parent directories is already known. + + This method is used to create the font_directories for font_mappings. + It recursively walks upwards in the directory hierarchy until it finds + a known font_directory (or hits the root directory, in which case it + bails out). +*/ +status_t +GlobalFontManager::_CreateDirectories(const char* path) +{ + FTRACE(("_CreateDirectories(path = %s)\n", path)); + + if (!strcmp(path, "/")) { + // we walked our way up to the root + return B_ENTRY_NOT_FOUND; + } + + BEntry entry; + status_t status = entry.SetTo(path); + if (status != B_OK) + return status; + + node_ref nodeRef; + status = entry.GetNodeRef(&nodeRef); + if (status != B_OK) + return status; + + // check if we are already know this directory + + font_directory* directory = _FindDirectory(nodeRef); + if (directory != NULL) + return B_OK; + + // We don't know this one yet - keep walking the path upwards + // and try to find a match. + + BPath parent(path); + status = parent.GetParent(&parent); + if (status != B_OK) + return status; + + status = _CreateDirectories(parent.Path()); + if (status != B_OK) + return status; + + // We have our match, create sub directory + + return _AddPath(path); +} + + +/*! \brief Scan a folder for all valid fonts + \param directoryPath Path of the folder to scan. +*/ +status_t +GlobalFontManager::_ScanFontDirectory(font_directory& fontDirectory) +{ + // This bad boy does all the real work. It loads each entry in the + // directory. If a valid font file, it adds both the family and the style. + + BDirectory directory; + status_t status = directory.SetTo(&fontDirectory.directory); + if (status != B_OK) + return status; + + BEntry entry; + while (directory.GetNextEntry(&entry) == B_OK) { + if (entry.IsDirectory()) { + // scan this directory recursively + font_directory* newDirectory; + if (_AddPath(entry, &newDirectory) == B_OK && newDirectory != NULL) + _ScanFontDirectory(*newDirectory); + + continue; + } + +// TODO: Commenting this out makes my "Unicode glyph lookup" +// work with our default fonts. The real fix is to select the +// Unicode char map (if supported), and/or adjust the +// utf8 -> glyph-index mapping everywhere to handle other +// char maps. We could also ignore fonts that don't support +// the Unicode lookup as a temporary "solution". +#if 0 + FT_CharMap charmap = _GetSupportedCharmap(face); + if (!charmap) { + FT_Done_Face(face); + continue; + } + + face->charmap = charmap; +#endif + + _AddFont(fontDirectory, entry); + // takes over ownership of the FT_Face object + } + + fontDirectory.revision = 1; + return B_OK; +} + + +/*! \brief Locates a FontFamily object by name + \param name The family to find + \return Pointer to the specified family or NULL if not found. +*/ +FontFamily* +GlobalFontManager::GetFamily(const char* name) +{ + if (name == NULL) + return NULL; + + FontFamily* family = _FindFamily(name); + if (family != NULL) + return family; + + if (fScanned) + return NULL; + + // try font mappings before failing + if (_AddMappedFont(name) == B_OK) + return _FindFamily(name); + + _ScanFonts(); + return _FindFamily(name); +} + + +FontFamily* +GlobalFontManager::GetFamily(uint16 familyID) const +{ + FontKey key(familyID, 0); + FontStyle* style = fStyleHashTable.Get(key); + if (style != NULL) + return style->Family(); + + return NULL; +} + + +const ServerFont* +GlobalFontManager::DefaultPlainFont() const +{ + return fDefaultPlainFont.Get(); +} + + +const ServerFont* +GlobalFontManager::DefaultBoldFont() const +{ + return fDefaultBoldFont.Get(); +} + + +const ServerFont* +GlobalFontManager::DefaultFixedFont() const +{ + return fDefaultFixedFont.Get(); +} + + +void +GlobalFontManager::AttachUser(uid_t userID) +{ + BAutolock locker(this); + +#if !TEST_MODE + // TODO: actually, find_directory() cannot know which user ID we want here + // TODO: avoids user fonts in safe mode + BPath path; + if (find_directory(B_USER_FONTS_DIRECTORY, &path, true) == B_OK) + _AddPath(path.Path()); + if (find_directory(B_USER_NONPACKAGED_FONTS_DIRECTORY, &path, true) + == B_OK) { + _AddPath(path.Path()); + } +#endif +} + + +void +GlobalFontManager::DetachUser(uid_t userID) +{ + BAutolock locker(this); + + // TODO! +} diff --git a/src/servers/app/font/GlobalFontManager.h b/src/servers/app/font/GlobalFontManager.h new file mode 100644 index 0000000000..a7ac9bedf4 --- /dev/null +++ b/src/servers/app/font/GlobalFontManager.h @@ -0,0 +1,130 @@ +/* + * Copyright 2001-2009, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * DarkWyrm + * Axel Dörfler, axeld@pinc-software.de + */ +#ifndef GLOBAL_FONT_MANAGER_H +#define GLOBAL_FONT_MANAGER_H + + +#include "FontManager.h" + +#include +#include +#include +#include +#include + +#include +#include FT_FREETYPE_H + + +class BEntry; +class BPath; +struct node_ref; + + +class FontFamily; +class FontStyle; +class ServerFont; + + +/*! + \class GlobalFontManager GlobalFontManager.h + \brief Manager for system fonts within the font subsystem +*/ +class GlobalFontManager : public FontManagerBase { +public: + GlobalFontManager(); + virtual ~GlobalFontManager(); + + void SaveRecentFontMappings(); + + virtual void MessageReceived(BMessage* message); + + virtual int32 CountFamilies(); + + virtual int32 CountStyles(const char* family); + virtual int32 CountStyles(uint16 familyID); + + int32 CheckRevision(uid_t user); + + const ServerFont* DefaultPlainFont() const; + const ServerFont* DefaultBoldFont() const; + const ServerFont* DefaultFixedFont() const; + + void AttachUser(uid_t userID); + void DetachUser(uid_t userID); + virtual FontFamily* GetFamily(uint16 familyID) const; + virtual FontFamily* GetFamily(const char* name); + virtual FontStyle* GetStyle(uint16 familyID, + uint16 styleID) const; + + virtual FontStyle* GetStyle(const char* familyName, + const char* styleName, + uint16 familyID = 0xffff, + uint16 styleID = 0xffff, + uint16 face = 0); + +private: + struct font_directory; + struct font_mapping; + + void _AddDefaultMapping(const char* family, + const char* style, const char* path); + bool _LoadRecentFontMappings(); + status_t _AddMappedFont(const char* family, + const char* style = NULL); + void _PrecacheFontFile(const ServerFont* font); + void _AddSystemPaths(); + font_directory* _FindDirectory(node_ref& nodeRef); + void _RemoveDirectory(font_directory* directory); + status_t _CreateDirectories(const char* path); + status_t _AddPath(const char* path); + status_t _AddPath(BEntry& entry, + font_directory** _newDirectory = NULL); + + void _ScanFontsIfNecessary(); + void _ScanFonts(); + status_t _ScanFontDirectory(font_directory& directory); + status_t _AddFont(font_directory& directory, + BEntry& entry); + void _RemoveStyle(font_directory& directory, + FontStyle* style); + void _RemoveStyle(dev_t device, uint64 directory, + uint64 node); + FontStyle* _GetDefaultStyle(const char* familyName, + const char* styleName, + const char* fallbackFamily, + const char* fallbackStyle, + uint16 fallbackFace); + status_t _SetDefaultFonts(); +private: + status_t fInitStatus; + + typedef BObjectList DirectoryList; + typedef BObjectList MappingList; + + DirectoryList fDirectories; + MappingList fMappings; + + ObjectDeleter + fDefaultPlainFont; + ObjectDeleter + fDefaultBoldFont; + ObjectDeleter + fDefaultFixedFont; + + bool fScanned; + +}; + + +extern GlobalFontManager* gFontManager; + +extern FT_Library gFreeTypeLibrary; + +#endif /* GLOBAL_FONT_MANAGER_H */ diff --git a/src/servers/app/font/GlyphLayoutEngine.h b/src/servers/app/font/GlyphLayoutEngine.h index dac7c73ba5..5e2fc3dcec 100644 --- a/src/servers/app/font/GlyphLayoutEngine.h +++ b/src/servers/app/font/GlyphLayoutEngine.h @@ -13,7 +13,7 @@ #include "FontCache.h" #include "FontCacheEntry.h" -#include "FontManager.h" +#include "GlobalFontManager.h" #include "ServerFont.h" #include