[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: font substitution
From: |
Fred Kiefer |
Subject: |
Re: font substitution |
Date: |
Tue, 07 Aug 2007 17:39:26 +0200 |
User-agent: |
Thunderbird 1.5.0.12 (X11/20060911) |
OK, so here is a reworked version of this patch. I had to fix the
coveredCharacterSet method of ftfont to get this working. So it may not
be possible for you to actually check this code. What I would like to
get from the mailing list is a short review, if you agree that this code
is fast enough to be included.
There are a few noteworthy changes to Alexander's patch. I set the
substitution font directly as the new font for the character. I am not
sure, if this is compatible with what Apple does. This patch also tries
to find a substitution by looking at all available fonts. This may be
really slow, when it fails. It will also result in the character sets
for all fonts being cached. I am not sure, if we really want this,
perhaps we should do this caching only for the fonts in preferredFontNames?
As you can see, I am still not that sure about this patch.
Cheers,
Fred
static NSString *lastFont = nil;
static NSCharacterSet *lastSet = nil;
static NSMutableDictionary *cachedCSets = nil;
- (NSFont*)_substituteFontWithName: (NSString*)fontName font:
(NSFont*)baseFont
{
// FIXME: Catch case were baseFont is nil
return [NSFont fontWithName: fontName matrix: [baseFont matrix]];
}
- (NSFont*)_substituteFontFor: (unichar)uchar font: (NSFont*)baseFont
fromList: (NSArray *)fonts
{
unsigned int count;
unsigned int i;
count = [fonts count];
for (i = 0; i < count; i++)
{
NSFont *newFont;
NSString *fName;
NSCharacterSet *newSet;
fName = [fonts objectAtIndex: i];
newSet = [cachedCSets objectForKey: fName];
if (newSet == nil)
{
newFont = [self _substituteFontWithName: fName font: baseFont];
newSet = [newFont coveredCharacterSet];
if (newSet != nil)
{
[cachedCSets setObject: newSet forKey: fName];
}
}
else
{
newFont = nil;
}
if ([newSet characterIsMember: uchar])
{
ASSIGN(lastFont, fName);
ASSIGN(lastSet, newSet);
if (newFont != nil)
{
return newFont;
}
else
{
return [self _substituteFontWithName: fName font:
baseFont];
}
}
}
return nil;
}
- (NSFont*)_substituteFontFor: (unichar)uchar font: (NSFont*)baseFont
{
NSFont *subFont;
// Caching one font may lead to the selected substitution font not being
// from the prefered list, although there is one there with this
character.
if (lastSet && [lastSet characterIsMember: uchar])
{
return [self _substituteFontWithName: lastFont font: baseFont];
}
if (cachedCSets == nil)
{
cachedCSets = [NSMutableDictionary new];
}
subFont = [self _substituteFontFor: uchar font: baseFont fromList:
[NSFont preferredFontNames]];
if (subFont != nil)
{
return subFont;
}
subFont = [self _substituteFontFor: uchar font: baseFont fromList:
[[NSFontManager sharedFontManager] availableFonts]];
if (subFont != nil)
{
return subFont;
}
return nil;
}
- (void) fixFontAttributeInRange: (NSRange)range
{
NSString *string;
NSFont *font;
NSCharacterSet *charset = nil;
NSRange fontRange = NSMakeRange(NSNotFound, 0);
unsigned int i;
unsigned int lastMax;
unsigned int start;
unichar chars[64];
CREATE_AUTORELEASE_POOL(pool);
if (NSMaxRange (range) > [self length])
{
[NSException raise: NSRangeException
format: @"RangeError in method -fixFontAttributeInRange: "];
}
// Check for each character if it is supported by the
// assigned font
/*
Note that this needs to be done on a script basis. Per-character checks
are difficult to do at all, don't give reasonable results, and would have
really poor performance.
*/
string = [self string];
lastMax = range.location;
start = lastMax;
for (i = range.location; i < NSMaxRange(range); i++)
{
unichar uchar;
if (i >= lastMax)
{
unsigned int dist;
start = lastMax;
dist = MIN(64, NSMaxRange(range) - start);
lastMax = start + dist;
[string getCharacters: chars range: NSMakeRange(start, dist)];
}
uchar = chars[i - start];
if (!NSLocationInRange(i, fontRange))
{
font = [self attribute: NSFontAttributeName
atIndex: i
effectiveRange: &fontRange];
charset = [font coveredCharacterSet];
}
if (charset != nil && ![charset characterIsMember: uchar])
{
// Find a replacement font
NSFont *subFont;
subFont = [self _substituteFontFor: uchar font: font];
if (subFont != nil)
{
// Set substitution font permanently
[self addAttribute: NSFontAttributeName
value: subFont
range: NSMakeRange(i, 1)];
}
}
}
RELEASE(pool);
}
Yen-Ju Chen wrote:
> On 8/5/07, Fred Kiefer <fredkiefer@gmx.de> wrote:
>> Hi Rob,
>>
>> there was a reason why Alexander was reluctant at that time to add this
>> patch to the main branch of GNUstep. This is a very expensive operation
>> that we are doing for every change of a string and it isn't even optimised.
>> I would kindly ask you to measure the time it take to perform text
>> operations on a reasonable large file with and without this patch in
>> place (Where all the characters are available in the assigned font).
>> Only when we are sure this change wont slow GNUstep down too much,
>> should we apply it. There are already some people complaining about the
>> slowness of GNUstep and I don't want to make things harder for them.
>>
>> What would be even better is to come up with a highly optimized version
>> of this patch. For example cache for all substitution fonts the
>> supported character set and check that instead of creating each font in
>> turn for each character that gets checked. Also the substitutions fonts
>> should not come from a separate user setting instead we should take he
>> preferred fonts first and if that fails check all available fonts (This
>> can be really slow without a cache!). Another small optimisation could
>> be to remember the last match and to try that font first the next time
>> we are missing a character. And in the fixFontAttributeInRange: method
>> we could get the characters more efficiently and reuse the same font for
>> its effective range.
>> The method used here to check if a font is valid for a character
>> glyphForCharacter: is an extension that only exists for the art backend.
>> We would need to replace it with coveredCharacterSet and implement that
>> method for all backends.
>>
>> I really would love to integrate this patch and as you can see I spend
>> some time thinking about it.
>
> I think -coveredCharacterSet work mostly on art backend,
> which use FreeType functions.
> I believe the same thing can be done with Cairo by retrieving
> FT_Face from cairo_scaled_font_t (?).
> But for short term solution, if any of above-mentioned methods are
> unavailable, ex in cairo backend, it can just fall back to do nothing
> as current implementation.
> I do meet some issues of -coveredCharacterSet with some fonts, though.
>
> Yen-Ju
>
>> Cheers
>> Fred
>>
>> foobix@comcast.net wrote:
>>> Quite some time ago Alexander Malmberg created a patch that gave
>>> GNUstep font substitution capabilities. It has worked quite well.
>>> Though, recent changes in GNUstep svn meant that the patch can no
>>> longer be applied. I've manually applied his patch to svn (from a few
>>> weeks ago), and created a new patch (attached). I was hoping this
>>> could be commited to svn, so as to not become out of date again.
>>>
>>
>> _______________________________________________
>> Discuss-gnustep mailing list
>> Discuss-gnustep@gnu.org
>> http://lists.gnu.org/mailman/listinfo/discuss-gnustep
>>
>
- font substitution, foobix, 2007/08/05
- Re: font substitution, Fred Kiefer, 2007/08/05
- Re: font substitution, Yen-Ju Chen, 2007/08/05
- Re: font substitution,
Fred Kiefer <=
- Re: font substitution, Yen-Ju Chen, 2007/08/07
- Re: font substitution, Yen-Ju Chen, 2007/08/07
- Re: font substitution, Fred Kiefer, 2007/08/07
- Re: font substitution, Yen-Ju Chen, 2007/08/07
- Re: font substitution, Fred Kiefer, 2007/08/08
- Re: font substitution, Yen-Ju Chen, 2007/08/08
- Re: font substitution, Fred Kiefer, 2007/08/09
- Re: font substitution, Yen-Ju Chen, 2007/08/09
- Re: font substitution, Yen-Ju Chen, 2007/08/10
- Re: font substitution, Yen-Ju Chen, 2007/08/10