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:13:58 -0700

On 8/7/07, Yen-Ju Chen <yjchenx@gmail.com> wrote:
> 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.

  I forgot to ask, is there a user default to set the preferredFontNames ?
  Otherwise, preferredFontNames will always return nil
  unless applications explicitly set it in NSFont.

  Yen-Ju

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