discuss-gnustep
[Top][All Lists]
Advanced

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





reply via email to

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