discuss-gnustep
[Top][All Lists]
Advanced

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

Re: font substitution


From: Yen-Ju Chen
Subject: Re: font substitution
Date: Tue, 7 Aug 2007 10:11:43 -0700

On 8/7/07, Fred Kiefer <fredkiefer@gmx.de> wrote:
> 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.

  If speed is the concern,
  I would say caching only preferredFontNames is enough.
  Because OS X ship their own fonts for each language,
  I believe they can cache such information in advance for each language
  and only fallback when all the defaults font fails, which hardly happens.
  I also wonder whether fonts have language information inside.
  If so, we can actually have a table of glyph range of each language
  and only match language instead of each glyph.
  That would be much faster.
  And I believe that there is hardly a font supporting
  only half of the glyphs for a language.
  In another word, if a font has 'A', it must have [A-Z][a-z][0-9].
  In that case, we only need to look at the unicode table
  and pick probably 2 glyphs for each language
  and use them to check the font.

  Yen-Ju

>
> 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
> >>
> >
>
>




reply via email to

[Prev in Thread] Current Thread [Next in Thread]