[Date Prev][Date Next][Thread Prev][Thread Next][Thread Index]

[XaraXtreme-dev] Current/default font fix

Find attached below a patch to replace the current font when loading  
a document (or template) in case the current font as saved with the 
document is not installed and not used in the document. This finally 
means that on machines without Arial, the initial font displayed in 
the text tool is no longer "Arial (missing)" but whatever the font 
system thinks is the best replacement for Arial.

It was agreed that a similar strategy should be used for the default 
font but that is not practicable because it turned out that the 
default font is always an installed font (though, obviously, not 
necessarily Times New Roman in case it is not available), which means 
that the whole discussion about changing the default font in case it 
is not installed was pointless because this situation does not occur. 
Still, the discussion was valuable because it has highlighted the fact 
that in contrast to all other default attributes, the default font 
attribute is never inherited by any object in the tree and it now 
looks like this is by design (though not documented anywhere up to 
now). So, the default font attribute does not take part in attribute 

What might have confused the issue is the misconception that font 
attributes store font names - they do not, each attribute only holds a 
font handle, which identifies the font in the font manager's list. The 
default font attribute always holds the default font handle. Not all 
of the fonts in the font manager's list need to be installed (some may 
be replaced, in which case the font appears under the name of the 
unknown font in the font manager's font list and has "(missing)" 
appended in the font menu), but the default font is cached under its 
real name.

Index: wxOil/fontbase.h
--- wxOil/fontbase.h	(Revision 1640)
+++ wxOil/fontbase.h	(Arbeitskopie)
@@ -180,6 +180,7 @@
 		static void FindClosestFont();
 		static FontBase* CreateNewFont(FontClass Class, String_64* pFontName);
 		static OUTLINETEXTMETRIC *GetOutlineTextMetric(FontClass Class, LOGFONT *pLogFont);
+		static String_64* GetNativeFontName(FontClass Class, LOGFONT *pLogFont);
 		static void InvalidateCharMetrics();
 		static BOOL GetCharMetrics(wxDC* pDC, WCHAR ch, CharDescription& FontDesc, CharMetrics* pCharMetrics);
 		static MILLIPOINT GetCharsKerning(wxDC* pDC, WCHAR chLeft, WCHAR chRight,
Index: wxOil/fontbase.cpp
--- wxOil/fontbase.cpp	(Revision 1640)
+++ wxOil/fontbase.cpp	(Arbeitskopie)
@@ -518,6 +518,24 @@
+>	String_64* OILFontMan::GetNativeFontName(FontClass Class, LOGFONT *pLogFont)
+	Author:		Martin Wuerthner <xara@xxxxxxxxxxxxxxx>
+	Created:	01/08/06
+	Inputs:     Class - the font class of the font (not used in wxOil but supplied in analogy
+						to GetOutlineTextMetrics)
+				pLogFont - a pointer to it's LOGFONT structure
+	Returns:    A pointer to the underlying native font name
+	Purpose:	Return the underlying native font name for a font
+String_64* OILFontMan::GetNativeFontName(FontClass Class, LOGFONT *pLogFont)
+	return &pLogFont->FaceName;
 >	OILFontMan::InvalidateCharMetrics()
 	Author:		Martin Wuerthner <xara@xxxxxxxxxxxxxxx>
Index: Kernel/fontman.h
--- Kernel/fontman.h	(Revision 1640)
+++ Kernel/fontman.h	(Arbeitskopie)
@@ -223,6 +223,9 @@
 		BOOL			IsFontReplaced(WORD Handle);
 		BOOL 			IsFontReplaced(String_64* pFontName, FontClass Class=FC_UNDEFINED);
+		BOOL            IsFontUsedInDoc(WORD Handle, Document* pDocument);
+		void            OnDocumentLoaded(Document* pDocument);
 		void			GetCompatibleFont(const String_64& EncodedName, String_64& CompatibleFont, INT32& Style);
 		void			EncodeFontName(String_64& FontName, String_64& Encoded, INT32 Styles);
 		void			EncodeAndMapFontName(String_64& FontName, String_64& Encoded, INT32 Styles);
@@ -286,6 +289,7 @@
 		CachedFontItem* AddTempFont(String_64* pFontName, FontClass Class, WORD& hndle);
 		void			InvalidateCache();
 		void			ResetDefaultFont();
+		BOOL            IsFontUsedInSiblings(Node* pNode, WORD Handle, WORD CurrentHandle, UINT32 Level);
 		WORD 			UniqueHandle;
Index: Kernel/fontman.cpp
--- Kernel/fontman.cpp	(Revision 1640)
+++ Kernel/fontman.cpp	(Arbeitskopie)
@@ -102,6 +102,9 @@
 #include "camtypes.h"
 // #include "fontbase.h" - included in fontman.h
+#include "nodetxts.h"
+#include "nodetxtl.h"
+#include "nodetext.h"
 #include "fontman.h"
 //#include "errors.h" - in camtypes.h [AUTOMATICALLY REMOVED]
 //#include "txtattr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
@@ -333,7 +336,7 @@
 	PORTNOTE("text","CachedFontItem::IsFullyCached - do nothing");
-	TRACEUSER("wuerthne", _T("CachedFontItem::IsFullyCached called") );
+	// TRACEUSER("wuerthne", _T("CachedFontItem::IsFullyCached called") );
 	ERROR2IF(pFontClass==NULL, FALSE, "A CachedFontItem structure exists without a FontClass!!!");
 	return (pEnumLogFont!=NULL);
@@ -592,7 +595,7 @@
 CachedFontItem* FontManager::AddFont(String_64* Name, FontClass fclass, WORD& retHandle)
+	TRACEUSER("wuerthne", _T("FontManager::AddFont %s"), (TCHAR*)*Name);
 	CachedFontItem* pItem = new	CachedFontItem;
 	if (pItem==NULL)
 		return NULL;
@@ -610,6 +613,7 @@
 	retHandle = pItem->Handle;
+	TRACEUSER("wuerthne", _T("AddFont, returning %d"), retHandle);
 	return pItem;
@@ -656,6 +660,10 @@
 				NULL if unable to find the font
 	Purpose:	Find the entry in the font managers font list which corresponds to this
 				fontname and font class
+	Note:       One might be tempted to include the default font item in the comparison
+				and return the default font handle when the default font name is passed,
+				but this does not happen and this is by design. See OnDocumentLoaded for
+				further information.
@@ -963,6 +971,136 @@
+>	BOOL FontManager::IsFontUsedInSiblings(Node* pNode, WORD Handle, WORD CurrentHandle, UINT32 Level)
+	Author:		Martin Wuerthner <xara@xxxxxxxxxxxxxxx>
+	Created:	01/08/06
+	Inputs:		pNode - the root node of the subtree to check
+				Handle - a font handle
+				CurrentHandle - the font handle applying to the current subtree
+				Level - level in the tree (level 0 is NodeDocument, level 1 are default attributes)
+	Returns:    Returns TRUE if a font attribute with the given handle applies to a text
+				object.
+	Purpose:	Find out whether a given font is used in a document.
+BOOL FontManager::IsFontUsedInSiblings(Node* pNode, WORD Handle, WORD CurrentHandle, UINT32 Level)
+	// perform a standard child-first recursion keeping track of the current font
+	while(pNode)
+	{
+		Node* pFirstChild = pNode->FindFirstChild();
+		if (pFirstChild && IsFontUsedInSiblings(pFirstChild, Handle, CurrentHandle, Level + 1)) return TRUE;
+		if (IS_A(pNode, AttrTxtFontTypeface))
+		{
+			AttrTxtFontTypeface* pAttr = (AttrTxtFontTypeface*)pNode;
+			CurrentHandle = pAttr->Value.HTypeface;
+			ENSURE(!(CurrentHandle == DEFAULTHANDLE && Level > 1), "FontManager::IsFontUsedInSiblings is based on the assumption that we do not have non-default attribute nodes referencing the default font");
+		}
+		if (CurrentHandle == Handle && (IS_A(pNode, TextStory) || IS_A(pNode, TextLine) || IS_A(pNode, TextChar)))
+			return TRUE;
+		pNode = pNode->FindNext();
+	}
+	return FALSE;
+>	BOOL FontManager::IsFontUsedInDoc(WORD Handle, Document* pDocument)
+	Author:		Martin Wuerthner <xara@xxxxxxxxxxxxxxx>
+	Created:	01/08/06
+	Inputs:		Handle - a font handle
+				pDocument - pointer to a document
+	Returns:    When called with Handle == DEFAULTHANDLE it returns TRUE if the default
+				attribute applies to a text object. Otherwise, it returns TRUE if the supplied
+				font handle is used in the document
+	Purpose:	Find out whether a given font is used in a document.
+BOOL FontManager::IsFontUsedInDoc(WORD Handle, Document* pDocument)
+	Node* pNode = pDocument->GetFirstNode();
+	return IsFontUsedInSiblings(pNode, Handle, ILLEGALFHANDLE, 0);
+>	void FontManager::OnDocumentLoaded(Document* pDocument)
+	Author:		Martin Wuerthner <xara@xxxxxxxxxxxxxxx>
+	Created:	01/08/06
+	Inputs:		pDocument - pointer to the newly created/loaded document
+	Purpose:	Called after a document has been loaded - allows us to fix the current and
+				default fonts if they are not installed. They are only changed if they are
+				not used in the document.
+				Fixing the current font is useful because it stops "Arial (missing)" from
+				being displayed after start-up if Arial is not installed. The same happens
+				when loading a document with the current font set to Arial (or any other
+				font that is not installed). We do not touch the current font if it is
+				actually used in the document because then, it is possible that it was set
+				deliberately (maybe even in a template).
+				One might consider a similar approach for the default font, but by definition,
+				the default font is always installed (though it may or may not be Times New
+				Roman), so there is no need to change anything. The default attributes are not
+				saved with the document, so, in contrast to the current font, there cannot be
+				any surprises with fonts that are not installed. The font name referenced by
+				the default font does not matter because the default font attribute is never
+				inherited by any text object in the document.
+				This is down to the behaviour of FindFont(String_64*,FontClass), which never
+				returns the default font handle, so any font attribute created by the text tool
+				based on a font name (which involves calling FindFont at some stage) is always
+				different from the default attribute (even if it happens to be the same font as
+				the default font) and hence this attribute is not optimised away. Therefore, each
+				and every text object has a non-default font attribute applied to it.
+void FontManager::OnDocumentLoaded(Document* pDocument)
+	// check if the current font is installed and change it in case it is not (only if the font is not used)
+	NodeAttribute* pAttr = pDocument->GetAttributeMgr().GetCurrentAttribute(CC_RUNTIME_CLASS(BaseTextClass),
+																			CC_RUNTIME_CLASS(AttrTxtFontTypeface));
+	if (pAttr)
+	{
+		AttrTxtFontTypeface* pTypeface = (AttrTxtFontTypeface*)pAttr;
+		WORD Handle = pTypeface->Value.HTypeface;
+		TRACEUSER("wuerthne", _T("found current font attribute, handle = %d"), Handle);
+		CachedFontItem *pItem = FindFont(Handle);
+		if (pItem)
+		{
+			String_64* SurfaceFontName = pItem->GetFontName();
+			TRACEUSER("wuerthne", _T("current font name is %s"), (TCHAR*)*SurfaceFontName);
+			ENUMLOGFONT *pEnumLogFont = pItem->GetEnumLogFont();
+			if (pEnumLogFont)
+			{
+				if (!IsFontInstalled(SurfaceFontName) && !IsFontUsedInDoc(Handle, pDocument))
+				{
+					// the current font is not installed and not used in the document,
+					// so find out what the replacement font is and use that instead
+					TRACEUSER("wuerthne", _T("font replaced and not used"));
+					String_64* NativeFontName = OILFontMan::GetNativeFontName(pItem->GetFontClass(),
+																			  &pEnumLogFont->elfLogFont);
+					WORD NewHandle = CacheNamedFont(NativeFontName, pItem->GetFontClass());
+					if (NewHandle != ILLEGALFHANDLE)
+					{
+						TRACEUSER("wuerthne", _T("change current font to handle %d"), NewHandle);
+						// modify the current attribute directly
+						pTypeface->Value.HTypeface = NewHandle;
+					}
+				}
+			}
+		}
+	}
 >	CachedFontItem* FontManager::GetFirstFontType(FontClass Class)
 	Author:		Mike_Kenny (Xara Group Ltd) <camelotdev@xxxxxxxx>
@@ -1552,7 +1690,8 @@
 	// we've done a search for it and it does not exist so we'll use the default
 	AddTempFont(pFontName, Class, hndle);
+	TRACEUSER("wuerthne", _T("FontManager::CacheNamedFont = %d"), hndle);
 	return (hndle);
Index: Kernel/camfiltr.cpp
--- Kernel/camfiltr.cpp	(Revision 1640)
+++ Kernel/camfiltr.cpp	(Arbeitskopie)
@@ -3488,8 +3488,14 @@
 	// Flag this as a new format document
 	// But only flag it if we are opening the document rather than importing into an exisiting one
 	if (TheDocument && !TheDocument->IsImporting())
+	{
+		// allow the font manager to fix the current font
+		if (!TheDocument->IsAClipboard())
+			GetApplication()->GetFontManager()->OnDocumentLoaded(TheDocument);
+	}
 #if !defined(EXCLUDE_FROM_RALPH)
 	BOOL UpdateBars = TheDocument ? !TheDocument->IsAClipboard() : TRUE;