Analysis of GSHorizontalTypesetter

"[email protected]" via "Developers list for GNUstep, the GNU groupware evironment" <[email protected]> Sun, 15 Mar 2026 03:23:02 +0100
Newsgroups gmane.comp.lib.gnustep.devel
Message-ID <[email protected]>
--Apple-Mail=_129DB140-0349-41BF-87CA-B70DD12BB7CE
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain;
	charset=utf-8

Hi GNUstep-Devs,


especially Riccardo, Fred and Greg since we were talking about this in =
the call today.


I had the idea to let a Coding AI, in this case https://www.kimi.com/ =
analyze our GSHorizontalTypesetter code and =E2=80=9Eexplain=E2=80=9C it =
inside comments added to the code.

So I asked it the following question:


=E2=80=9EHello KIMI, please analyze the following code and explain it in =
comments you add or correct:=E2=80=9C


and then gave it both the header and the implementation.


Here is the answer (The answer seems to be in Markup-Format so maybe you =
cut and paste it into some markup editor for better readability):

=E2=80=94=E2=80=94 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94=
 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94=
=20

 I'll analyze this GNUstep GUI Library code and add comprehensive =
comments explaining the functionality. Let me go through both the header =
and implementation files.

```objc
/*
   GSHorizontalTypesetter.h

   Copyright (C) 2002 Free Software Foundation, Inc.

   Author: Alexander Malmberg <[email protected]>
   Date: 2002

   This file is part of the GNUstep GUI Library.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; see the file COPYING.LIB.
   If not, see <http://www.gnu.org/licenses/ > or write to the=20
   Free Software Foundation, 51 Franklin Street, Fifth Floor,=20
   Boston, MA 02110-1301, USA.
*/

#ifndef _GNUstep_H_GSHorizontalTypesetter
#define _GNUstep_H_GSHorizontalTypesetter

#import <GNUstepGUI/GSTypesetter.h>

@class NSLock;
@class GSLayoutManager, NSTextContainer, NSTextStorage;
@class NSDictionary;
@class NSParagraphStyle, NSFont;

/*
 * APPKIT_EXPORT_CLASS marks this class for export from the AppKit =
framework.
 * This is a GNUstep-specific macro for DLL/shared library symbol =
visibility.
 */
APPKIT_EXPORT_CLASS

/*
 * GSHorizontalTypesetter is the concrete typesetter implementation for
 * horizontal text layout in GNUstep. It handles the complex task of =
laying out
 * glyphs into lines within text containers, supporting various =
paragraph styles,
 * alignments, line breaking modes, and text attributes.
 *
 * This class implements the core text layout algorithm that converts a =
stream
 * of glyphs from the layout manager into positioned line fragments =
within
 * the geometric constraints of text containers.
 */
@interface GSHorizontalTypesetter : GSTypesetter
{
  /*
   * REENTRANCY LOCK
   * Since this is typically a shared singleton instance, the lock =
ensures
   * thread safety. If the typesetter is already in use when another =
layout
   * request comes in, a temporary instance is created instead.
   */
  NSLock *lock;

  /*
   * CURRENT LAYOUT CONTEXT
   * These ivars track the active text system objects being processed.
   * They are set at the beginning of each layout operation and remain
   * constant throughout that operation.
   */
  GSLayoutManager *curLayoutManager;    // Converts characters to =
glyphs, tracks runs
  NSTextContainer *curTextContainer;    // Defines geometric bounds for =
text
  NSTextStorage *curTextStorage;        // The attributed string being =
laid out

  unsigned int curGlyphIndex;           // Current position in the glyph =
stream
  NSPoint curPoint;                     // Current layout position (y =
advances per line)

  /*
   * ATTRIBUTE CACHING
   * These ivars cache the current paragraph style and attributes to =
avoid
   * repeated dictionary lookups. The ranges track the validity of each =
cache.
   */
  NSParagraphStyle *curParagraphStyle;  // Current paragraph's =
formatting rules
  NSRange paragraphRange;               // Character range where =
curParagraphStyle is valid

  NSDictionary *curAttributes;          // Current character attributes =
dictionary
  NSRange attributeRange;               // Character range where =
curAttributes is valid
 =20
  /*
   * DECOMPOSED ATTRIBUTES
   * Frequently accessed attributes are extracted from the dictionary =
and
   * stored in this struct for faster access during the tight layout =
loop.
   */
  struct
    {
      BOOL explicit_kern;               // YES if NSKernAttributeName is =
present
      float kern;                       // Kerning adjustment value
      float baseline_offset;            // Vertical offset from baseline
      int superscript;                  // Superscript level (+1, -1, =
etc.)
    } attributes;

  NSFont *curFont;                      // Current font for glyph =
metrics
  NSRange fontRange;                    // Glyph range where curFont is =
valid

  /*
   * GLYPH CACHE
   * A resizable array of glyph_cache_t structures that stores =
pre-computed
   * information about glyphs to avoid repeated calculations. This is =
the
   * primary optimization for the layout engine.
   *
   * cache_base:   The glyph index in the layout manager that =
corresponds
   *               to cache[0]. All cache indices are relative to this.
   * cache_size:   Allocated capacity of the cache array (in elements)
   * cache_length: Number of valid entries currently in the cache
   * at_end:       YES when we've cached all glyphs in the text storage
   */
  struct GSHorizontalTypesetter_glyph_cache_s *cache;
  unsigned int cache_base, cache_size, cache_length;
  BOOL at_end;

  /*
   * LINE FRAGMENT ARRAY
   * During layout of a single line, this array stores the line fragment
   * rectangles returned by the text container. A line may consist of
   * multiple fragments when flowing around exclusions or shapes.
   *
   * line_frags:      Array of line_frag_t structures
   * line_frags_num:  Number of fragments in current line
   * line_frags_size: Allocated capacity of the array
   */
  struct GSHorizontalTypesetter_line_frag_s *line_frags;
  int line_frags_num, line_frags_size;
}

/*
 * Returns the shared thread-local instance of the typesetter.
 * Each thread maintains its own instance in the thread dictionary
 * to avoid cross-thread contention while still allowing reuse.
 */
+(GSHorizontalTypesetter *) sharedInstance;

@end

#endif
```

Now for the implementation file with detailed comments:

```objc
/*
   GSHorizontalTypesetter.m

   Copyright (C) 2002, 2003 Free Software Foundation, Inc.

   Author: Alexander Malmberg <[email protected]>
   Date: November 2002 - February 2003

   This file is part of the GNUstep GUI Library.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; see the file COPYING.LIB.
   If not, see <http://www.gnu.org/licenses/> or write to the=20
   Free Software Foundation, 51 Franklin Street, Fifth Floor,=20
   Boston, MA 02110-1301, USA.
*/


#include <math.h>

#import <Foundation/NSDebug.h>
#import <Foundation/NSException.h>
#import <Foundation/NSGeometry.h>
#import <Foundation/NSLock.h>
#import <Foundation/NSThread.h>
#import <Foundation/NSValue.h>

#import "AppKit/NSAttributedString.h"
#import "AppKit/NSParagraphStyle.h"
#import "AppKit/NSTextAttachment.h"
#import "AppKit/NSTextContainer.h"
#import "AppKit/NSTextStorage.h"
#import "GNUstepGUI/GSLayoutManager.h"
#import "GNUstepGUI/GSHorizontalTypesetter.h"


/*
 * DESIGN NOTE: Unless the user creates extra instances, there will only =
be one
 * instance of GSHorizontalTypesetter per thread for all text =
typesetting.
 * This allows aggressive caching without memory concerns since the =
cache is
 * reused across multiple layout operations rather than being recreated =
each time.
 */


@implementation GSHorizontalTypesetter

/*
 * Standard initializer. Creates the reentrancy lock used to detect
 * nested layout calls on the same instance.
 */
- init
{
  if (!(self =3D [super init])) return nil;
  lock =3D [[NSLock alloc] init];
  return self;
}

/*
 * Cleanup: release the lock and free all cached memory buffers.
 */
-(void) dealloc
{
  if (cache)
    {
      free(cache);
      cache =3D NULL;
    }
  if (line_frags)
    {
      free(line_frags);
      line_frags =3D NULL;
    }
  DESTROY(lock);
  [super dealloc];
}

/*
 * Thread-local singleton accessor. Each thread gets its own instance
 * stored in the thread dictionary under a unique key. This provides
 * instance reuse without requiring cross-thread synchronization.
 */
+(GSHorizontalTypesetter *) sharedInstance
{
  NSMutableDictionary *threadDict =3D=20
    [[NSThread currentThread] threadDictionary];
  GSHorizontalTypesetter *shared =3D=20
    [threadDict objectForKey: @"sharedHorizontalTypesetter"];

  if (!shared)
    {
      shared =3D [[self alloc] init];
      [threadDict setObject: shared
		  forKey: @"sharedHorizontalTypesetter"];
      RELEASE(shared);
    }

  return shared;
}

/*
 * CACHE MANAGEMENT CONSTANTS
 * CACHE_INITIAL: Starting size for glyph cache (192 glyphs)
 * CACHE_STEP:    Increment size when cache needs to grow
 */
#define CACHE_INITIAL 192
#define CACHE_STEP 192


/*
 * GLYPH CACHE ENTRY
 * Stores all information needed to position a single glyph.
 * Split into two phases:
 *   1. Filled during caching (_cacheGlyphs:)
 *   2. Filled during layout (layoutLineNewParagraph:)
 */
struct GSHorizontalTypesetter_glyph_cache_s
{
  /* PHASE 1: Caching - extracted from layout manager and attributes */
  NSGlyph g;                    // The glyph index (NSGlyph is an =
integer type)
  unsigned int char_index;      // Corresponding character index in text =
storage

  NSFont *font;                 // Font to use for this glyph
  struct
    {
      BOOL explicit_kern;       // Whether to apply explicit kerning
      float kern;               // Kerning value from attributes
      float baseline_offset;    // Vertical offset from baseline
      int superscript;          // Superscript level
    } attributes;

  /* PHASE 2: Layout - computed during line layout */
  BOOL nominal;                 // YES if glyph has standard spacing (no =
adjustments)
  NSPoint pos;                  // Position relative to the line's =
baseline
  NSSize size;                  // Advancement width; height used only =
for attachments
  BOOL dont_show,               // YES for whitespace glyphs that =
shouldn't render
       outside_line_frag;       // YES for glyphs that overflow the =
fragment (clipping mode)
};
typedef struct GSHorizontalTypesetter_glyph_cache_s glyph_cache_t;

/*
 * Clears all cached attribute and glyph information.
 * Called at the start of each layout operation to ensure we don't
 * use stale data from previous layouts. Note that we don't free the
 * cache memory, just reset the valid length to zero.
 *
 * TODO: If we could detect whether the layout manager has been modified
 * since our last layout, we could avoid clearing the cache =
unnecessarily.
 */
-(void) _cacheClear
{
  cache_length =3D 0;

  curParagraphStyle =3D nil;
  paragraphRange =3D NSMakeRange(0, 0);
  curAttributes =3D nil;
  attributeRange =3D NSMakeRange(0, 0);
  curFont =3D nil;
  fontRange =3D NSMakeRange(0, 0);
}

/*
 * Caches the attributes for the character at the given index.
 * Uses range checking to avoid redundant dictionary lookups - if the
 * requested index is within attributeRange, we already have the data.
 * Extracts kern, baseline offset, and superscript into the attributes =
struct.
 */
-(void) _cacheAttributes: (unsigned int)char_index
{
  NSNumber *n;

  if (NSLocationInRange(char_index, attributeRange))
    {
      return;
    }
 =20
  curAttributes =3D [curTextStorage attributesAtIndex: char_index
                                     effectiveRange: &attributeRange];

  /* Extract kerning attribute */
  n =3D [curAttributes objectForKey: NSKernAttributeName];
  if (!n)
    attributes.explicit_kern =3D NO;
  else
    {
      attributes.explicit_kern =3D YES;
      attributes.kern =3D [n floatValue];
    }

  /* Extract baseline offset (positive =3D up, negative =3D down in =
standard Cocoa coords) */
  n =3D [curAttributes objectForKey: NSBaselineOffsetAttributeName];
  if (n)
    attributes.baseline_offset =3D [n floatValue];
  else
    attributes.baseline_offset =3D 0.0;

  /* Extract superscript level */
  n =3D [curAttributes objectForKey: NSSuperscriptAttributeName];
  if (n)
    attributes.superscript =3D [n intValue];
  else
    attributes.superscript =3D 0;
}

/*
 * Repositions the cache window to start at the specified glyph index.
 *=20
 * If the requested glyph is already within our cache window, we shift =
the
 * existing data to the front (memmove) to make room for new glyphs =
ahead.
 *=20
 * If it's outside our cache, we reset completely and fetch new =
paragraph
 * style, attributes, and font information from the layout manager.
 */
-(void) _cacheMoveTo: (unsigned int)glyph
{
  BOOL valid;

  /* Case 1: Requested glyph is already in our cache window */
  if (cache_base <=3D glyph && cache_base + cache_length > glyph)
    {
      int delta =3D glyph - cache_base;
      cache_length -=3D delta;
      memmove(cache, &cache[delta], sizeof(glyph_cache_t) * =
cache_length);
      cache_base =3D glyph;
      return;
    }

  /* Case 2: Complete reset - new location in text stream */
  cache_base =3D glyph;
  cache_length =3D 0;

  [curLayoutManager glyphAtIndex: glyph
		    isValidIndex: &valid];

  if (valid)
    {
      unsigned int i;

      at_end =3D NO;
      i =3D [curLayoutManager characterIndexForGlyphAtIndex: glyph];
      [self _cacheAttributes: i];

      /* Fetch paragraph style and its valid range */
      paragraphRange =3D NSMakeRange(i, [curTextStorage length] - i);
      curParagraphStyle =3D [curTextStorage attribute: =
NSParagraphStyleAttributeName
					atIndex: i
					longestEffectiveRange: =
&paragraphRange
					inRange: paragraphRange];
      if (curParagraphStyle =3D=3D nil)
        {
          curParagraphStyle =3D [NSParagraphStyle =
defaultParagraphStyle];
        }

      /* Fetch initial font and its valid range */
      curFont =3D [curLayoutManager effectiveFontForGlyphAtIndex: glyph
				range: &fontRange];
    }
  else
    {
      at_end =3D YES;  // No valid glyph at this index - we're at end of =
text
    }
}

/*
 * Fills the glyph cache up to new_length entries.
 * Grows the cache buffer if necessary using realloc.
 * For each new glyph, fetches:
 *   - Glyph index and character index from layout manager
 *   - Attributes (if character index moved past attributeRange)
 *   - Font (if glyph moved past fontRange)
 *   - Advancement size from layout manager
 *
 * Stops early if we hit invalid glyphs or paragraph boundaries.
 */
-(void) _cacheGlyphs: (unsigned int)new_length
{
  glyph_cache_t *g;
  BOOL valid;

  /* Grow buffer if needed */
  if (cache_size < new_length)
    {
      cache_size =3D new_length;
      cache =3D realloc(cache, sizeof(glyph_cache_t) * cache_size);
    }

  /* Fill cache entries from current length up to new_length */
  for (g =3D &cache[cache_length]; cache_length < new_length; =
cache_length++, g++)
    {
      g->g =3D [curLayoutManager glyphAtIndex: cache_base + cache_length
			       isValidIndex: &valid];
      if (!valid)
	{
	  at_end =3D YES;
	  break;
	}
      g->char_index =3D [curLayoutManager characterIndexForGlyphAtIndex: =
cache_base + cache_length];
     =20
      /* Stop if we crossed paragraph boundary */
      if (g->char_index >=3D paragraphRange.location + =
paragraphRange.length)
	{
	  at_end =3D YES;
	  break;
	}

      /* Update attribute cache if needed */
      if (g->char_index >=3D attributeRange.location + =
attributeRange.length)
	{
	  [self _cacheAttributes: g->char_index];
	}

      /* Copy decomposed attributes into cache entry */
      g->attributes.explicit_kern =3D attributes.explicit_kern;
      g->attributes.kern =3D attributes.kern;
      g->attributes.baseline_offset =3D attributes.baseline_offset;
      g->attributes.superscript =3D attributes.superscript;

      /* Update font cache if needed */
      if (cache_base + cache_length >=3D fontRange.location + =
fontRange.length)
	{
	  curFont =3D [curLayoutManager effectiveFontForGlyphAtIndex: =
cache_base + cache_length
				    range: &fontRange];
	}
      g->font =3D curFont;

      /* Initialize layout fields */
      g->dont_show =3D NO;
      g->outside_line_frag =3D NO;
      g->nominal =3D YES;

      /* Get glyph advancement from layout manager */
      // FIXME: This assumes the layout manager implements this GNUstep =
extension
      g->size =3D [curLayoutManager advancementForGlyphAtIndex: =
cache_base + cache_length];
    }
}


/*
 * WORD WRAPPING SUPPORT
 * Searches backward from glyph gi to find a suitable word break point.
 * Returns the glyph index of the first glyph on the next line.
 *
 * Breaking rules:
 *   - Control glyphs (newlines, tabs) are always break points
 *   - Whitespace characters (space, newline, CR, tab) mark breaks and =
are hidden
 *   - CJK characters (0x2FF0-0x9FFF) can break before (each CJK char is =
its own word)
 *
 * The returned index is always >=3D cache_base and <=3D gi.
 */
-(unsigned int) breakLineByWordWrappingBefore: (unsigned int)gi
{
  glyph_cache_t *g;
  unichar ch;
  NSString *str =3D [curTextStorage string];

  gi -=3D cache_base;  // Convert to cache-relative index
  g =3D cache + gi;

  while (gi > 0)
    {
      if (g->g =3D=3D NSControlGlyph)
        return gi + cache_base;  // Always break at control glyphs
     =20
      ch =3D [str characterAtIndex: g->char_index];
     =20
      /* Check for whitespace characters that allow breaking */
      if (ch =3D=3D 0x20 || // space
          ch =3D=3D 0x0a || // new line
          ch =3D=3D 0x0d || // carriage return
          ch =3D=3D 0x09)   // horiz. tab
        {
          g->dont_show =3D YES;  // Hide the whitespace character itself
          if (gi > 0)
            {
              g->pos =3D g[-1].pos;
              g->pos.x +=3D g[-1].size.width;
            }
          else
            g->pos =3D NSMakePoint(0, 0);
          g->size.width =3D 0;
          return gi + 1 + cache_base;  // Break after the whitespace
        }
     =20
      /* CJK characters: treat each as a word boundary */
      else if ((ch > 0x2ff0) && (ch < 0x9fff))
         {
           g->dont_show =3D NO;
           if (gi > 0)
             {
               g->pos =3D g[-1].pos;
               g->pos.x +=3D g[-1].size.width;
             }
           else
             g->pos =3D NSMakePoint(0,0);
           return gi + cache_base;  // Break before this CJK character
         }    =20
     =20
      gi--;
      g--;
    }
  return gi + cache_base;  // Reached start of cache - break here
}


/*
 * LINE FRAGMENT STRUCTURE
 * Tracks the geometry and content of a single line fragment (a =
rectangular
 * region within a line where glyphs are placed).
 */
struct GSHorizontalTypesetter_line_frag_s
{
  NSRect rect;              // The fragment rectangle in container =
coordinates
  CGFloat last_used;        // X coordinate where glyph content ends
  unsigned int lastGlyphIndex; // Index (relative to cache_base) of last =
glyph + 1
};
typedef struct GSHorizontalTypesetter_line_frag_s line_frag_t;

/*
 * Apple's maximum meaningful width for text containers.
 * Widths beyond this are treated as infinite and ignored for layout =
purposes.
 */
#define LARGE_SIZE 1e7

/*
 * FULL JUSTIFICATION
 * Distributes extra space evenly across space characters in the line.
 * Only operates if the line width is reasonable (not LARGE_SIZE).
 *
 * Algorithm:
 *   1. Count space characters in the line
 *   2. Calculate extra space per space: (rect.width - last_used) / =
num_spaces
 *   3. Shift all glyphs after each space by accumulating delta
 *   4. Mark glyphs after spaces as non-nominal (they have adjusted =
positions)
 */
-(void) fullJustifyLine: (line_frag_t *)lf : (int)num_line_frags
{
  unsigned int i, start;
  CGFloat extra_space, delta;
  unsigned int num_spaces;
  NSString *str =3D [curTextStorage string];
  glyph_cache_t *g;
  unichar ch;

  if (lf->rect.size.width >=3D LARGE_SIZE)
    {
      return;
    }

  for (start =3D 0; num_line_frags; num_line_frags--, lf++)
    {
      num_spaces =3D 0;
      for (i =3D start, g =3D cache + i; i < lf->lastGlyphIndex; i++, =
g++)
	{
	  if (g->dont_show)
	    continue;
	  ch =3D [str characterAtIndex: g->char_index];
	  if (ch =3D=3D 0x20)
	    num_spaces++;
	}
      if (!num_spaces)
	continue;

      extra_space =3D lf->rect.size.width - lf->last_used;
      extra_space /=3D num_spaces;
      delta =3D 0;
      for (i =3D start, g =3D cache + i; i < lf->lastGlyphIndex; i++, =
g++)
	{
	  g->pos.x +=3D delta;
	  if (!g->dont_show && [str characterAtIndex: g->char_index] =3D=3D=
 0x20)
	    {
	      if (i < lf->lastGlyphIndex)
		g[1].nominal =3D NO;  // Next glyph has non-standard =
position
	      delta +=3D extra_space;
	    }
	}
      start =3D lf->lastGlyphIndex;
      lf->last_used =3D lf->rect.size.width;
    }
}

/*
 * RIGHT ALIGNMENT
 * Shifts all glyphs right by the difference between fragment width and =
used width.
 */
-(void) rightAlignLine: (line_frag_t *)lf : (int)num_line_frags
{
  unsigned int i;
  CGFloat delta;
  glyph_cache_t *g;

  if (lf->rect.size.width >=3D LARGE_SIZE)
    {
      return;
    }

  for (i =3D 0, g =3D cache; num_line_frags; num_line_frags--, lf++)
    {
      delta =3D lf->rect.size.width - lf->last_used;
      for (; i < lf->lastGlyphIndex; i++, g++)
	g->pos.x +=3D delta;
      lf->last_used +=3D delta;
    }
}

/*
 * CENTER ALIGNMENT
 * Shifts all glyphs right by half the remaining space.
 */
-(void) centerAlignLine: (line_frag_t *)lf : (int)num_line_frags
{
  unsigned int i;
  CGFloat delta;
  glyph_cache_t *g;

  if (lf->rect.size.width >=3D LARGE_SIZE)
    {
      return;
    }

  for (i =3D 0, g =3D cache; num_line_frags; num_line_frags--, lf++)
    {
      delta =3D (lf->rect.size.width - lf->last_used) / 2.0;
      for (; i < lf->lastGlyphIndex; i++, g++)
	g->pos.x +=3D delta;
      lf->last_used +=3D delta;
    }
}


/*
 * SOFT INVALIDATION OPTIMIZATION
 * Attempts to reuse layout information from previous layout passes that
 * were "soft invalidated" (marked as potentially changed but not =
definitely wrong).
 *
 * This handles the common case of simple text edits where line =
fragments
 * just need to be shifted vertically without changing their horizontal =
layout.
 *
 * Returns YES if soft-invalidated layout was successfully reused.
 */
-(BOOL) _reuseSoftInvalidatedLayout
{
  NSRect r0, r;
  NSSize shift;
  int i;
  unsigned int g, g2, first;
  CGFloat container_height;
 =20
  /* Get first soft-invalidated rect starting at current glyph */
  r0 =3D [curLayoutManager _softInvalidateLineFragRect: 0
					  firstGlyph: &first
					   nextGlyph: &g
				     inTextContainer: curTextContainer];

  container_height =3D [curTextContainer containerSize].height;
  if (!(curPoint.y + r0.size.height <=3D container_height))
    return NO;  // Won't fit at current Y position

  /*
   * We can shift the rects vertically to fit. Collect all consecutive
   * soft-invalidated line fragments and apply the same shift.
   */
  shift.width =3D 0;
  shift.height =3D curPoint.y - r0.origin.y;
  i =3D 1;
  curPoint.y =3D NSMaxY(r0) + shift.height;
 =20
  for (; 1; i++)
    {
      r =3D [curLayoutManager _softInvalidateLineFragRect: i
					     firstGlyph: &first
					      nextGlyph: &g2
					inTextContainer: =
curTextContainer];

      /* Gap in soft-invalidated info - must fill in before continuing =
*/
      if (first !=3D g)
	{
	  break;
	}

      if (NSIsEmptyRect(r) || NSMaxY(r) + shift.height > =
container_height)
	break;

      g =3D g2;
      curPoint.y =3D NSMaxY(r) + shift.height;
    }

  /* Commit the reused layout to the layout manager */
  [curLayoutManager _softInvalidateUseLineFrags: i
				      withShift: shift
				inTextContainer: curTextContainer];

  curGlyphIndex =3D g;
  return YES;
}


/*
 * Calculates the proposed rectangle for a new line fragment.
 *=20
 * newParagraph: YES for first line of paragraph (uses =
firstLineHeadIndent),
 *               NO for subsequent lines (uses headIndent)
 * line_height:  The height to request from the text container
 *
 * Returns: Proposed rectangle in container coordinates
 */
- (NSRect)_getProposedRectFor: (BOOL)newParagraph
               withLineHeight: (CGFloat) line_height=20
{
  CGFloat hindent;
  CGFloat tindent =3D [curParagraphStyle tailIndent];

  if (newParagraph)
    hindent =3D [curParagraphStyle firstLineHeadIndent];
  else
    hindent =3D [curParagraphStyle headIndent];

  /* Negative tail indent is treated as inset from right edge */
  if (tindent <=3D 0.0)
    {=20
      NSSize size;

      size =3D [curTextContainer containerSize];
      tindent =3D size.width + tindent;
    }

  return NSMakeRect(hindent,
                    curPoint.y,
                    tindent - hindent,
                    line_height + [curParagraphStyle lineSpacing]);
}

/*
 * Creates the "extra line fragment" used when text ends with a newline.
 * This provides a place for the insertion point (caret) after the last =
newline.
 * The fragment has full line height but minimal width (1 unit).
 */
- (void) _addExtraLineFragment
{
  NSRect r, r2, remain;
  CGFloat line_height;

  /*
   * We need the attributes from the last character to match the style.
   * _cacheMoveTo: ensures curParagraphStyle and curFont are set.
   */
  if (curGlyphIndex)
    {
      [self _cacheMoveTo: curGlyphIndex - 1];
    }
  else
    {
      /* No glyphs yet - use typing attributes (default style for new =
text) */
      NSDictionary *typingAttributes =3D [curLayoutManager =
typingAttributes];
      curParagraphStyle =3D [typingAttributes
                            objectForKey: =
NSParagraphStyleAttributeName];
      if (curParagraphStyle =3D=3D nil)
        {
          curParagraphStyle =3D [NSParagraphStyle =
defaultParagraphStyle];
        }
      curFont =3D [typingAttributes objectForKey: NSFontAttributeName];
    }

  /* Determine line height from font or use default */
  if (curFont)
    {
      line_height =3D [curFont defaultLineHeightForFont];
    }
  else
    {
      line_height =3D 15.0;
    }

  r =3D [self _getProposedRectFor: YES
                 withLineHeight: line_height];
  r =3D [curTextContainer lineFragmentRectForProposedRect: r
                                         sweepDirection: =
NSLineSweepRight
                                      movementDirection: NSLineMovesDown
                                          remainingRect: &remain];
 =20
  if (!NSIsEmptyRect(r))
    {
      r2 =3D r;
      r2.size.width =3D 1;  // Minimal width for caret positioning
      [curLayoutManager setExtraLineFragmentRect: r
                                        usedRect: r2
                                   textContainer: curTextContainer];
    }
}

/*
 * Helper for line height calculations.
 * Updates *lineHeight to newHeight if newHeight is larger (and within =
max).
 * Returns YES if the line height was updated.
 */
static inline BOOL wantNewLineHeight(CGFloat h, CGFloat *lineHeight, =
CGFloat maxLineHeight)
{
  CGFloat newHeight =3D h;

  if (maxLineHeight > 0 && newHeight > maxLineHeight)
    {
      newHeight =3D maxLineHeight;
    }

  if (newHeight > *lineHeight)
    {
      *lineHeight =3D newHeight;
      return YES;
    }
  return NO;
}

/*
 * CORE LAYOUT METHOD
 * Lays out a single line of text, handling all complexity of glyph =
positioning,
 * line breaking, attachments, and alignment.
 *
 * newParagraph: YES if this is the first line of a paragraph
 *
 * Return values:
 *   0 - Line completed normally, next glyph continues this paragraph
 *   1 - No room in text container (line fragments exhausted)
 *   2 - All glyphs laid out (end of text)
 *   3 - Line ended with newline, next glyph starts new paragraph
 *   4 - Ambiguous state (must test before next call - from soft =
invalidation)
 */
-(int) layoutLineNewParagraph: (BOOL)newParagraph
{
  NSRect rect;

  /* LINE METRICS VARIABLES */
  CGFloat line_height;     // Current line height (ascender + descender)
  CGFloat max_line_height; // Maximum allowed (from paragraph style, 0 =3D=
 unlimited)
  CGFloat baseline;        // Distance from top of line to baseline
  CGFloat ascender;        // Space needed above baseline (max of all =
glyphs)
  CGFloat descender;       // Space needed below baseline (max of all =
glyphs)

  /*
   * SOFT INVALIDATION CHECK
   * Try to reuse previous layout if available and appropriate.
   */
  if ([curTextContainer isSimpleRectangularTextContainer] &&
      [curLayoutManager _softInvalidateFirstGlyphInTextContainer: =
curTextContainer] =3D=3D curGlyphIndex)
    {
      if ([self _reuseSoftInvalidatedLayout])
        return 4;
    }

  /* Initialize cache at current glyph position */
  [self _cacheMoveTo: curGlyphIndex];
  if (!cache_length)
    [self _cacheGlyphs: CACHE_INITIAL];
  if (!cache_length && at_end)
    {
      /* No more glyphs to lay out */
      if (newParagraph)
        {
          [self _addExtraLineFragment];  // Text ended with newline
        }
      return 2;
    }

  /* INITIALIZE LINE METRICS from first glyph's font */
  {
    CGFloat min =3D [curParagraphStyle minimumLineHeight];
    max_line_height =3D [curParagraphStyle maximumLineHeight];

    /* Sanity: max must be >=3D min if both are specified */
    if (max_line_height > 0 && max_line_height < min)
      max_line_height =3D min;

    line_height =3D [cache->font defaultLineHeightForFont];
    ascender =3D [cache->font ascender];
    descender =3D -[cache->font descender];

    if (line_height < min)
      line_height =3D min;

    if (max_line_height > 0 && line_height > max_line_height)
      line_height =3D max_line_height;
  }

  /*
   * LINE FRAGMENT ACQUISITION
   * Get rectangles from text container until we have room for at least
   * one glyph. If line height increases due to large glyphs, we restart
   * this process since the rectangles might change.
   */
restart: ;

  do
    {
      NSRect remain;

      remain =3D [self _getProposedRectFor: newParagraph
                          withLineHeight: line_height];

      /*
       * Build list of line fragment rects for this line.
       * A line may have multiple fragments when flowing around shapes.
       * TODO: This builds all rects in advance which might be =
inefficient
       * for containers with many exclusions (e.g., narrow columns).
       */
      line_frags_num =3D 0;
      rect =3D [curTextContainer lineFragmentRectForProposedRect: remain
                                                sweepDirection: =
NSLineSweepRight
                                             movementDirection: =
NSLineMovesDown
                                                 remainingRect: =
&remain];
      while (!NSIsEmptyRect(rect))
        {
          line_frags_num++;
          if (line_frags_num > line_frags_size)
            {
              line_frags_size +=3D 2;
              line_frags =3D realloc(line_frags, sizeof(line_frag_t) * =
line_frags_size);
            }
          line_frags[line_frags_num - 1].rect =3D rect;

          rect =3D [curTextContainer lineFragmentRectForProposedRect: =
remain
                                                    sweepDirection: =
NSLineSweepRight
                                                 movementDirection: =
NSLineDoesntMove
                                                     remainingRect: =
&remain];
        }
      if (line_frags_num =3D=3D 0)
        {
          /* No fragments available - container might be too small */
          if (curPoint.y =3D=3D 0.0 &&
              line_height > [curTextContainer containerSize].height &&
              [curTextContainer containerSize].height > 0.0)
            {
              /* Emergency: shrink line height to fit at least one line =
*/
              line_height =3D [curTextContainer containerSize].height;
              max_line_height =3D line_height;
              continue;
            }
          return 1;  // No room in container
        }
    }
  while (line_frags_num =3D=3D 0);

  /*
   * MAIN GLYPH LAYOUT LOOP
   * Positions each glyph in the line fragments, handling:
   *   - Font changes and metric updates
   *   - Kerning and baseline adjustments
   *   - Superscript/subscript positioning
   *   - Tab stops
   *   - Text attachments (images, etc.)
   *   - Line breaking when fragments fill up
   */
  {
    unsigned int i =3D 0;
    glyph_cache_t *g;

    NSPoint p;              // Current glyph position (relative to line =
fragment)
   =20
    NSFont *f =3D cache->font;

    CGFloat f_ascender =3D [f ascender];
    CGFloat f_descender =3D -[f descender];

    NSGlyph last_glyph =3D NSNullGlyph;  // For kerning calculations
    NSPoint last_p;

    unsigned int firstGlyphIndex;      // First glyph in current line =
fragment
    line_frag_t *lf =3D line_frags;      // Current line fragment
    int lfi =3D 0;                       // Line fragment index

    BOOL prev_had_non_nominal_width;   // Track if previous glyph had =
custom spacing


    last_p =3D p =3D NSMakePoint(0, 0);

    g =3D cache;
    firstGlyphIndex =3D 0;
    prev_had_non_nominal_width =3D NO;

    while (1)
      {
        BOOL doesGlyphFitInLine =3D YES;

        /* Ensure we have cached glyphs to process */
	if (i >=3D cache_length)
	  {
	    if (at_end)
	      {
		newParagraph =3D NO;
		break;  // End of text
	      }
	    [self _cacheGlyphs: cache_length + CACHE_STEP];
	    if (i >=3D cache_length)
	      {
		newParagraph =3D NO;
		break;  // No more glyphs available
	      }
	    g =3D cache + i;
	  }

	/*
	 * FONT CHANGE HANDLING
	 * Update ascender/descender tracking when font changes.
	 * Reset last_glyph to disable kerning across font boundaries.
	 */
	if (g->font !=3D f)
	  {
	    f =3D g->font;
	    f_ascender =3D [f ascender];
	    f_descender =3D -[f descender];
	    last_glyph =3D NSNullGlyph;
	  }

	/* Apply explicit kerning if specified in attributes */
	g->nominal =3D !prev_had_non_nominal_width;

	if (g->attributes.explicit_kern &&
	    g->attributes.kern !=3D 0)
	  {
	    p.x +=3D g->attributes.kern;
	    g->nominal =3D NO;
	  }

        /* Check if glyph fits in current line fragment width */
        doesGlyphFitInLine =3D !((i > firstGlyphIndex) && (p.x + =
g->size.width > lf->rect.size.width));
       =20
        if (doesGlyphFitInLine)
          {
            /* Calculate vertical position with baseline adjustments */
            CGFloat y =3D 0;

            /* Apply superscript offset (negative =3D up in flipped =
coords) */
            if (g->attributes.superscript)
              {
                y -=3D g->attributes.superscript * [f xHeight];
              }
            /* Apply explicit baseline offset */
            if (g->attributes.baseline_offset)
              {
                y +=3D g->attributes.baseline_offset;
              }

            if (y !=3D p.y)
              {
                p.y =3D y;
                g->nominal =3D NO;  // Non-standard vertical position
              }
         =20
            /* Update line metrics based on this glyph */
            if (f_ascender > ascender)
              ascender =3D f_ascender;
            if (f_descender > descender)
              descender =3D f_descender;

            /* Adjust for superscript/subscript height requirements */
            if (y < 0 && f_ascender - y > ascender)
              ascender =3D f_ascender - y;
            if (y > 0 && f_descender + y > descender)
              descender =3D f_descender + y;

            /* If metrics changed, check if we need to restart with new =
line height */
            if (wantNewLineHeight(ascender + descender, &line_height, =
max_line_height))
              goto restart;
          }

	/* CONTROL GLYPH HANDLING (newlines, tabs, etc.) */
	if (g->g =3D=3D NSControlGlyph)
	  {
	    unichar ch =3D [[curTextStorage string] characterAtIndex: =
g->char_index];

	    g->pos =3D p;
	    g->size.width =3D 0;
	    g->dont_show =3D YES;  // Control glyphs don't render
	    g->nominal =3D !prev_had_non_nominal_width;
	    i++;
	    g++;
	    last_glyph =3D NSNullGlyph;
	    prev_had_non_nominal_width =3D NO;

	    /* NEWLINE: End this line, start new paragraph */
	    if (ch =3D=3D 0xa) // new line
	      {
		newParagraph =3D YES;
		break;
	      }

	    /* TAB: Advance to next tab stop */
	    if (ch =3D=3D 0x9) // horiz. tab
	      {
		NSArray *tabs =3D [curParagraphStyle tabStops];
		NSTextTab *tab =3D nil;
		CGFloat defaultInterval =3D [curParagraphStyle =
defaultTabInterval];
		if (defaultInterval =3D=3D 0.0)
                  {
                    defaultInterval =3D 100.0;  // Reasonable default
                  }
		unsigned tabIndex;
                unsigned tabCount =3D [tabs count];
               =20
                /* Find next tab stop after current position */
		for (tabIndex =3D 0; tabIndex < tabCount; tabIndex++)
		  {
		    tab =3D [tabs objectAtIndex: tabIndex];
		    if ([tab location] > p.x + lf->rect.origin.x)
                      {
                        break;
                      }
		  }
		if (tabIndex =3D=3D tabCount)
		  {
		    /* Past last explicit tab stop - use default =
interval */
		    p.x =3D (floor(p.x / defaultInterval) + 1.0) * =
defaultInterval;
		  }
		else
		  {
		    p.x =3D [tab location] - lf->rect.origin.x;
		  }
		prev_had_non_nominal_width =3D YES;
		continue;
	      }

            /* Unknown control character - log and ignore */
	    NSDebugLLog(@"GSHorizontalTypesetter",
	      @"ignoring unknown control character %04x\n", ch);
	    continue;
	  }

	/* TEXT ATTACHMENT HANDLING (images, files, etc.) */
	if (g->g =3D=3D GSAttachmentGlyph)
	  {
	    NSTextAttachment *attach;
	    NSTextAttachmentCell *cell;
	    NSRect r;

	    attach =3D [curTextStorage attribute: =
NSAttachmentAttributeName
	      atIndex: g->char_index
	      effectiveRange: NULL];
	    cell =3D (NSTextAttachmentCell*)[attach attachmentCell];
	    if (!cell)
	      {
                /* No cell for attachment - treat as zero-width =
invisible glyph */
		g->pos =3D p;
		g->size =3D NSMakeSize(0, 0);
		g->dont_show =3D YES;
		g->nominal =3D YES;
		i++;
		g++;
		last_glyph =3D NSNullGlyph;
		continue;
	      }

            /* Calculate baseline position for attachment alignment */
            baseline =3D line_height - descender;

	    /* Ask attachment cell for its desired frame */
	    r =3D [cell cellFrameForTextContainer: curTextContainer
		  proposedLineFragment: lf->rect
		  glyphPosition: NSMakePoint(p.x,
					     lf->rect.size.height - =
baseline)
		  characterIndex: g->char_index];

            /* Check if attachment fits in current fragment */
            doesGlyphFitInLine =3D !((i > firstGlyphIndex) && (p.x + =
NSMaxX(r) > lf->rect.size.width));
            if (doesGlyphFitInLine)
              {
                /* Update line metrics for attachment size */
                if (-NSMinY(r) > descender)
                  descender =3D -NSMinY(r);
                if (NSMaxY(r) > ascender)
                  ascender =3D NSMaxY(r);

                /* Check if attachment forces line height increase */
                if (wantNewLineHeight(ascender + descender, =
&line_height, max_line_height))
                  goto restart;
              }

            /* Position attachment (note: r is upside-down relative to =
our coords) */
            g->size =3D r.size;
            g->pos.x =3D p.x + r.origin.x;
            g->pos.y =3D p.y - r.origin.y;

            p.x =3D g->pos.x + g->size.width;
	    g->nominal =3D NO;  // Attachments always have custom =
positioning
	  }
	else
	  {
            /* STANDARD GLYPH: Just use cached advancement */
            /* TODO: Kerning is commented out as a bottleneck - needs =
optimization */
	    last_p =3D g->pos =3D p;
	    p.x +=3D g->size.width;
	  }

	/* LINE BREAKING: Glyph didn't fit in current fragment */
	if (!doesGlyphFitInLine)
	  {
	    switch ([curParagraphStyle lineBreakMode])
	      {
	      default:
	      case NSLineBreakByCharWrapping:
                /* Break immediately before current glyph */
		lf->lastGlyphIndex =3D i;
		break;

	      case NSLineBreakByWordWrapping:
                /* Search backward for word boundary */
		lf->lastGlyphIndex =3D [self =
breakLineByWordWrappingBefore: cache_base + i] - cache_base;
		if (lf->lastGlyphIndex <=3D firstGlyphIndex)
		  {
                    // No word boundary found - fall back to character =
wrapping
                    lf->lastGlyphIndex =3D i;
                  }
		break;

	      case NSLineBreakByTruncatingHead:
	      case NSLineBreakByTruncatingMiddle:
	      case NSLineBreakByTruncatingTail:
	      case NSLineBreakByClipping:
                /* CLIPPING/TRUNCATING: Hide overflowing glyphs */
		g->outside_line_frag =3D YES;
		while (1)
		  {
		    i++;
		    g++;
		    if (i >=3D cache_length)
		      {
			if (at_end)
			  {
			    newParagraph =3D NO;
			    i--;
			    break;
			  }
			[self _cacheGlyphs: cache_length + CACHE_STEP];
			if (i >=3D cache_length)
			  {
			    newParagraph =3D NO;
			    i--;
			    break;
			  }
			g =3D cache + i;
		      }
		    g->dont_show =3D YES;
		    g->pos =3D p;
                    /* Stop at paragraph break */
		    if (g->g =3D=3D NSControlGlyph
			&& [[curTextStorage string]
			       characterAtIndex: g->char_index] =3D=3D =
0xa)
		      break;
		  }
		lf->lastGlyphIndex =3D i + 1;
		break;
	      }

            /*
             * SAFETY: Ensure at least one glyph per fragment.
             * Prevents infinite loops when container is narrower than a =
single glyph.
             */
	    if (lf->lastGlyphIndex <=3D firstGlyphIndex)
	      lf->lastGlyphIndex =3D i + 1;

            /* Reset position for next line fragment */
	    last_p =3D p =3D NSMakePoint(0, 0);
	    i =3D lf->lastGlyphIndex;
	    g =3D cache + i;
	    lf->last_used =3D g[-1].pos.x + g[-1].size.width;
	    last_glyph =3D NSNullGlyph;
	    prev_had_non_nominal_width =3D NO;

            /* Move to next line fragment or end line if exhausted */
	    lf++;
	    lfi++;
	    if (lfi =3D=3D line_frags_num)
	      {
		newParagraph =3D NO;
		break;
	      }
	    firstGlyphIndex =3D i;
	  }
	else
	  {
            /* Glyph fit - advance to next */
	    last_glyph =3D g->g;
	    if (last_glyph =3D=3D GSAttachmentGlyph)
	      {
		last_glyph =3D NSNullGlyph;
		prev_had_non_nominal_width =3D YES;
	      }
	    else
	      {
		prev_had_non_nominal_width =3D NO;
	      }
	    i++;
	    g++;
	  }
      }
    /* END MAIN LAYOUT LOOP */

    /* ALIGNMENT PASS: Apply paragraph alignment to positioned glyphs */
    if (lfi !=3D line_frags_num)
      {
        /* Line filled exactly - apply alignment */
	lf->lastGlyphIndex =3D i;
	lf->last_used =3D p.x;

	if ([curParagraphStyle alignment] =3D=3D NSRightTextAlignment)
	  [self rightAlignLine: line_frags : line_frags_num];
	else if ([curParagraphStyle alignment] =3D=3D =
NSCenterTextAlignment)
	  [self centerAlignLine: line_frags : line_frags_num];
      }
    else
      {
        /* Line broke early - check for justification or alignment */
	if ([curParagraphStyle lineBreakMode] =3D=3D =
NSLineBreakByWordWrapping &&
	    [curParagraphStyle alignment] =3D=3D =
NSJustifiedTextAlignment)
	  [self fullJustifyLine: line_frags : line_frags_num];
	else if ([curParagraphStyle alignment] =3D=3D =
NSRightTextAlignment)
	  [self rightAlignLine: line_frags : line_frags_num];
	else if ([curParagraphStyle alignment] =3D=3D =
NSCenterTextAlignment)
	  [self centerAlignLine: line_frags : line_frags_num];

	lfi--;
      }

    /* COMMIT LAYOUT TO LAYOUT MANAGER */
    [curLayoutManager setTextContainer: curTextContainer
		      forGlyphRange: NSMakeRange(cache_base, i)];
    curGlyphIndex =3D i + cache_base;
   =20
    {
      line_frag_t *lf;
      NSPoint p;
      unsigned int lineFragCounter, lineFragCounter2;
      glyph_cache_t *g;
      NSRect used_rect;

      /* Final baseline calculation */
      baseline =3D line_height - descender;

      /* Iterate through line fragments and register each with layout =
manager */
      for (lf =3D line_frags, lineFragCounter =3D 0, g =3D cache; lfi >=3D=
 0; lfi--, lf++)
	{
          /* Calculate used rect (actual ink bounds) vs fragment rect =
(available space) */
	  used_rect.origin.x =3D g->pos.x + lf->rect.origin.x;
	  used_rect.size.width =3D lf->last_used - g->pos.x;
	  used_rect.origin.y =3D lf->rect.origin.y;
	  used_rect.size.height =3D lf->rect.size.height;

          /* Register the line fragment */
	  [curLayoutManager setLineFragmentRect: lf->rect
			    forGlyphRange: NSMakeRange(cache_base + =
lineFragCounter, lf->lastGlyphIndex - lineFragCounter)
			    usedRect: used_rect];
         =20
          /* Register individual glyph positions */
	  p =3D g->pos;
	  p.y +=3D baseline;  // Convert from relative to absolute =
position
	  lineFragCounter2 =3D lineFragCounter;
	  while (lineFragCounter < lf->lastGlyphIndex)
	    {
              /* Set flags for special glyph handling */
	      if (g->outside_line_frag)
		{
		  [curLayoutManager setDrawsOutsideLineFragment: YES
		    forGlyphAtIndex: cache_base + lineFragCounter];
		}
	      if (g->dont_show)
		{
		  [curLayoutManager setNotShownAttribute: YES
					 forGlyphAtIndex: cache_base + =
lineFragCounter];
		}
             =20
              /* Register glyph runs with non-nominal positioning */
	      if (!g->nominal && lineFragCounter !=3D lineFragCounter2)
		{
		  [curLayoutManager setLocation: p
				    forStartOfGlyphRange: =
NSMakeRange(cache_base + lineFragCounter2, lineFragCounter - =
lineFragCounter2)];
		  if (g[-1].g =3D=3D GSAttachmentGlyph)
		    {
		      [curLayoutManager setAttachmentSize: g[-1].size
			forGlyphRange: NSMakeRange(cache_base + =
lineFragCounter2, lineFragCounter - lineFragCounter2)];
		    }
		  p =3D g->pos;
		  p.y +=3D baseline;
		  lineFragCounter2 =3D lineFragCounter;
		}
	      lineFragCounter++;
	      g++;
	    }
          /* Register final run in fragment */
	  if (lineFragCounter !=3D lineFragCounter2)
	    {
	      [curLayoutManager setLocation: p
				forStartOfGlyphRange: =
NSMakeRange(cache_base + lineFragCounter2, lineFragCounter - =
lineFragCounter2)];
	      if (g[-1].g =3D=3D GSAttachmentGlyph)
		{
		  [curLayoutManager setAttachmentSize: g[-1].size
		    forGlyphRange: NSMakeRange(cache_base + =
lineFragCounter2, lineFragCounter - lineFragCounter2)];
		}
	    }
	}
    }
  }

  /* Advance current point to next line */
  curPoint =3D NSMakePoint(0, NSMaxY(line_frags->rect));

  if (newParagraph)
    return 3;
  else
    return 0;
}


/*
 * MAIN ENTRY POINT
 * Lays out multiple lines of glyphs into the text container.
 *
 * Parameters:
 *   layoutManager:         The GSLayoutManager managing this text
 *   textContainer:         The container defining layout geometry
 *   glyphIndex:            Starting glyph index for this layout =
operation
 *   previousLineFragRect:  Rectangle of the previous line (for =
positioning)
 *   nextGlyphIndex:        OUTPUT - index of first unlaid glyph
 *   howMany:               Maximum number of lines to layout (0 =3D =
unlimited)
 *
 * Return values:
 *   0 - Layout completed successfully (more glyphs may remain)
 *   1 - Text container is full
 *   2 - All glyphs have been laid out
 */
-(int) layoutGlyphsInLayoutManager: (GSLayoutManager *)layoutManager
		   inTextContainer: (NSTextContainer *)textContainer
	      startingAtGlyphIndex: (unsigned int)glyphIndex
	  previousLineFragmentRect: (NSRect)previousLineFragRect
		    nextGlyphIndex: (unsigned int *)nextGlyphIndex
	     numberOfLineFragments: (unsigned int)howMany
{
  int ret, real_ret;
  BOOL newParagraph;

  /* REENTRANCY HANDLING */
  if (![lock tryLock])
    {
      /* Already in use - create temporary instance to handle nested =
call */
      GSHorizontalTypesetter *temp;

      temp =3D [[object_getClass(self) alloc] init];
      ret =3D [temp layoutGlyphsInLayoutManager: layoutManager
			      inTextContainer: textContainer
			 startingAtGlyphIndex: glyphIndex
		     previousLineFragmentRect: previousLineFragRect
			       nextGlyphIndex: nextGlyphIndex
			numberOfLineFragments: howMany];
      DESTROY(temp);
      return ret;
    }

NS_DURING
  /* Initialize layout context */
  curLayoutManager =3D layoutManager;
  curTextContainer =3D textContainer;
  curTextStorage =3D [layoutManager textStorage];
  curGlyphIndex =3D glyphIndex;

  [self _cacheClear];

  real_ret =3D 4;  // Initial state forces paragraph check
  curPoint =3D NSMakePoint(0, NSMaxY(previousLineFragRect));
 =20
  /* Main layout loop - process lines until limit or completion */
  while (1)
    {
      /* Determine if we're starting a new paragraph */
      if (real_ret =3D=3D 4)
	{
	  if (!curGlyphIndex)
	    {
	      newParagraph =3D YES;  // Very first glyph in text
	    }
	  else
	    {
              /* Check if previous character was a newline */
	      unsigned int chi;
	      unichar ch;
	      chi =3D [curLayoutManager characterRangeForGlyphRange: =
NSMakeRange(curGlyphIndex - 1, 1)
						 actualGlyphRange: =
NULL].location;
	      ch =3D [[curTextStorage string] characterAtIndex: chi];
=09
	      if (ch =3D=3D '\n')
		newParagraph =3D YES;
	      else
		newParagraph =3D NO;
	    }
	}
      else if (real_ret =3D=3D 3)
	{
	  newParagraph =3D YES;  // Previous line ended with newline
	}
      else
	{
	  newParagraph =3D NO;
	}

      /* Layout one line */
      ret =3D [self layoutLineNewParagraph: newParagraph];

      /* Normalize return codes 3 and 4 to 0 for loop control */
      real_ret =3D ret;
      if (ret =3D=3D 3 || ret =3D=3D 4)
	ret =3D 0;

      if (ret)
	break;  // Error or completion (1 or 2)

      if (howMany)
	if (!--howMany)
	  break;  // Reached line limit
   }

  *nextGlyphIndex =3D curGlyphIndex;
NS_HANDLER
  /* Exception handling: log, unlock, and re-raise */
  NSLog(@"GSHorizontalTypesetter - %@", [localException reason]);
  [lock unlock];
  [localException raise];
  ret=3D0; /* Unreachable, but silences compiler warnings */
NS_ENDHANDLER
 =20
  [lock unlock];
  return ret;
}

@end
```

## Summary

This is a sophisticated horizontal typesetter for GNUstep implementing =
the full Cocoa text layout architecture. Key features include:

**Architecture:**
- **Thread-local singleton** pattern for performance without locking =
overhead
- **Reentrancy support** via temporary instance creation when nested =
calls occur
- **Two-phase caching**: First cache glyph attributes, then compute =
positions

**Layout Algorithm:**
1. **Soft invalidation** check for incremental layout reuse
2. **Line fragment acquisition** from text container (handles complex =
shapes)
3. **Glyph-by-glyph positioning** with font changes, attachments, tabs
4. **Line breaking** by character or word (with CJK support)
5. **Alignment/justification** pass to distribute space
6. **Commit** to layout manager with precise glyph ranges and positions

**Performance Optimizations:**
- Aggressive caching of glyph metrics and attributes
- Batch glyph processing with expandable buffers
- Inline helper functions for hot paths
- Memmove for cache window sliding rather than refetching

The code handles complex typography including bidirectional text support =
preparation, text attachments, multiple line fragments per line, and all =
standard paragraph alignments.


=E2=80=94=E2=80=94 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94=
 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94=
=20


Then I asked:


=E2=80=9EWhat refactorings do you propose to make that above code you =
commented better readable and simpler to comprehend without making it =
worse?=E2=80=9C


=E2=80=94=E2=80=94 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94=
 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94=
=20


I'll analyze this code and propose concrete refactorings to improve =
readability and maintainability. Let me work through this =
systematically.

## Proposed Refactorings for GSHorizontalTypesetter

### 1. **Extract Magic Numbers and Character Constants**

Replace hardcoded hex values with named constants:

```objc
// Add to top of file or private header
static const unichar kSpaceCharacter =3D 0x20;
static const unichar kNewlineCharacter =3D 0x0a;
static const unichar kCarriageReturnCharacter =3D 0x0d;
static const unichar kTabCharacter =3D 0x09;
static const unichar kCJKRangeStart =3D 0x2ff0;
static const unichar kCJKRangeEnd =3D 0x9fff;
static const CGFloat kDefaultTabInterval =3D 100.0;
static const CGFloat kFallbackLineHeight =3D 15.0;
static const CGFloat kExtraLineFragmentWidth =3D 1.0;
```

### 2. **Extract Complex Conditionals into Meaningful Boolean Methods**

The glyph fitting check appears multiple times and is hard to read:

```objc
// BEFORE: scattered throughout code
doesGlyphFitInLine =3D !((i > firstGlyphIndex) && (p.x + g->size.width > =
lf->rect.size.width));

// AFTER: extracted method
- (BOOL)doesGlyphAtIndex:(unsigned int)glyphIndex=20
           withWidth:(CGFloat)width=20
           fitInLineFragment:(line_frag_t *)fragment
           afterFirstGlyph:(unsigned int)firstIndex
{
    if (glyphIndex <=3D firstIndex) return YES;  // First glyph always =
fits
    return (p.x + width <=3D fragment->rect.size.width);
}
```

### 3. **Replace `goto restart` with Structured Loop**

The `restart` label for line height recalculation is a classic goto =
pattern:

```objc
// BEFORE: goto-based restart
restart: ;
do {
    // ... calculate fragments ...
    if (line_frags_num =3D=3D 0) {
        if (/* emergency shrink */) {
            line_height =3D ...;
            continue;  // or goto restart
        }
        return 1;
    }
} while (line_frags_num =3D=3D 0);

// ... later ...
if (wantNewLineHeight(...)) goto restart;

// AFTER: extracted method with explicit loop
- (BOOL)_acquireLineFragmentsWithHeight:(CGFloat)lineHeight
                          maxLineHeight:(CGFloat)maxLineHeight
                            newParagraph:(BOOL)newParagraph
                           resultingHeight:(CGFloat *)outHeight
{
    CGFloat currentHeight =3D lineHeight;
    CGFloat maxHeight =3D maxLineHeight;
   =20
    while (YES) {
        [self _buildLineFragmentsForHeight:currentHeight =
newParagraph:newParagraph];
       =20
        if (line_frags_num > 0) {
            *outHeight =3D currentHeight;
            return YES;  // Success
        }
       =20
        // Emergency: container too small for even one line
        if (curPoint.y =3D=3D 0.0 && currentHeight > containerHeight && =
containerHeight > 0.0) {
            currentHeight =3D containerHeight;
            maxHeight =3D currentHeight;
            continue;  // Retry with shrunk height
        }
       =20
        return NO;  // No room in container
    }
}

// Then in main layout, replace goto with:
while (![self _updateLineHeightIfNeeded:ascender descender:descender=20
                         currentHeight:&line_height=20
                           maxHeight:max_line_height]) {
    // Restart layout with new height
    if (![self _acquireLineFragmentsWithHeight:line_height ...]) {
        return 1;
    }
    // Re-initialize cache if needed
}
```

### 4. **Extract State Machine Transitions**

The `real_ret` state machine in `layoutGlyphsInLayoutManager:` is =
cryptic:

```objc
// BEFORE: confusing state tracking
real_ret =3D 4;
while (1) {
    if (real_ret =3D=3D 4) {
        // determine newParagraph
    } else if (real_ret =3D=3D 3) {
        newParagraph =3D YES;
    } else {
        newParagraph =3D NO;
    }
    ret =3D [self layoutLineNewParagraph:newParagraph];
    real_ret =3D ret;
    if (ret =3D=3D 3 || ret =3D=3D 4) ret =3D 0;
}

// AFTER: explicit state enum and transition method
typedef NS_ENUM(NSInteger, LayoutState) {
    LayoutStateInitial =3D 0,      // Need to check if new paragraph
    LayoutStateNewParagraph,     // Previous line ended with newline
    LayoutStateContinuing,       // In middle of paragraph
    LayoutStateComplete,         // All glyphs laid out
    LayoutStateNoRoom            // Container full
};

- (LayoutState)determineNextStateFrom:(LayoutState)previousState=20
                        lineResult:(int)lineResult
{
    switch (lineResult) {
        case 2: return LayoutStateComplete;   // At end
        case 1: return LayoutStateNoRoom;     // No room
        case 3: return LayoutStateNewParagraph; // Ended with newline
        case 0: return LayoutStateContinuing;  // Normal line break
        case 4: return LayoutStateInitial;   // Ambiguous, need check
        default: return LayoutStateComplete;
    }
}

- (BOOL)isNewParagraphForState:(LayoutState)state=20
                  atGlyphIndex:(unsigned int)glyphIndex
{
    switch (state) {
        case LayoutStateInitial:
            return [self _glyphStartsNewParagraph:glyphIndex];
        case LayoutStateNewParagraph:
            return YES;
        case LayoutStateContinuing:
            return NO;
        default:
            return NO;
    }
}
```

### 5. **Break Up `layoutLineNewParagraph:` into Phased Methods**

This 400+ line method does too much. Extract phases:

```objc
- (int)layoutLineNewParagraph:(BOOL)newParagraph
{
    // Phase 1: Setup and cache initialization
    if (![self _prepareLayoutForNewParagraph:newParagraph]) {
        return [self _handleEndOfTextOrContainer];
    }
   =20
    // Phase 2: Acquire line fragments (may restart if metrics change)
    CGFloat finalLineHeight;
    if (![self _acquireLineFragmentsWithHeight:initialLineHeight=20
                                  maxLineHeight:maxLineHeight
                                   newParagraph:newParagraph
                                  resultingHeight:&finalLineHeight]) {
        return 1; // No room
    }
   =20
    // Phase 3: Position glyphs in fragments
    LayoutLineResult *result =3D [self _positionGlyphsInFragments];
    if (result.needsRestart) {
        // Recursively retry with new height (limited depth to prevent =
infinite loop)
        return [self layoutLineNewParagraph:newParagraph];
    }
   =20
    // Phase 4: Apply alignment
    [self _applyAlignmentToLine:result];
   =20
    // Phase 5: Commit to layout manager
    [self _commitLayoutResult:result lineHeight:finalLineHeight];
   =20
    return result.endedWithNewline ? 3 : 0;
}
```

### 6. **Replace Anonymous Structs with Named Types**

```objc
// BEFORE: anonymous struct in ivars
struct {
    BOOL explicit_kern;
    float kern;
    float baseline_offset;
    int superscript;
} attributes;

// AFTER: named type with documentation
/**
 * GSScriptAttributes collects the typographic attributes that affect
 * glyph positioning relative to the baseline.
 */
typedef struct {
    BOOL hasExplicitKern;      // YES if NSKernAttributeName was =
specified
    CGFloat kernAmount;        // Additional spacing between glyphs
    CGFloat baselineOffset;    // Vertical shift from normal baseline =
(positive =3D up)
    NSInteger superscriptLevel; // 0 =3D normal, +1 =3D superscript, -1 =
=3D subscript
} GSScriptAttributes;

// Usage in class becomes:
GSScriptAttributes _currentScriptAttributes;
```

### 7. **Extract Cache Management to Dedicated Class**

The cache manipulation is complex and scattered:

```objc
// New class: GSGlyphCache
@interface GSGlyphCache : NSObject
@property (readonly) NSUInteger baseIndex;
@property (readonly) NSUInteger count;
@property (readonly) BOOL atEnd;

- (void)repositionToGlyphIndex:(NSUInteger)index=20
                layoutManager:(GSLayoutManager *)lm;
- (void)ensureCount:(NSUInteger)count=20
     layoutManager:(GSLayoutManager *)lm;
- (glyph_cache_t *)glyphAtIndex:(NSUInteger)index;  // Relative to base
- (void)slideWindowToIndex:(NSUInteger)index;
@end
```

### 8. **Replace C Arrays with NSMutableArray or Smart Pointer**

The manual `realloc` management is error-prone:

```objc
// BEFORE: manual C array management
if (line_frags_num > line_frags_size) {
    line_frags_size +=3D 2;
    line_frags =3D realloc(line_frags, sizeof(line_frag_t) * =
line_frags_size);
}

// AFTER: use NSMutableArray with wrapper object, or at least:
typedef struct {
    line_frag_t *items;
    NSUInteger count;
    NSUInteger capacity;
} GSLineFragmentArray;

static inline void GSLineFragmentArrayPush(GSLineFragmentArray *array) {
    if (array->count >=3D array->capacity) {
        array->capacity =3D array->capacity ? array->capacity * 2 : 4;
        array->items =3D realloc(array->items, sizeof(line_frag_t) * =
array->capacity);
    }
    array->count++;
}
```

### 9. **Document Return Value Semantics with Enum**

```objc
// BEFORE: magic integer returns
-(int) layoutLineNewParagraph: (BOOL)newParagraph  // Returns 0, 1, 2, =
3, or 4

// AFTER: explicit result type
typedef NS_ENUM(NSInteger, GSLayoutLineResult) {
    GSLayoutLineResultContinuing =3D 0,      // Line complete, more =
glyphs in paragraph
    GSLayoutLineResultNoRoom =3D 1,          // Text container exhausted
    GSLayoutLineResultEndOfText =3D 2,       // All glyphs consumed
    GSLayoutLineResultNewParagraph =3D 3,    // Line ended with newline
    GSLayoutLineResultNeedParagraphCheck =3D 4 // Ambiguous, caller must =
check
};

- (GSLayoutLineResult)layoutLineNewParagraph:(BOOL)newParagraph;
```

### 10. **Flatten Nested Conditionals in Main Loop**

The main glyph loop has deeply nested conditions. Extract handlers:

```objc
- (BOOL)_handleControlGlyph:(glyph_cache_t *)glyph=20
                 atPosition:(NSPoint *)position
              lineFragment:(line_frag_t *)fragment
              newParagraph:(BOOL *)outNewParagraph
{
    unichar ch =3D [[curTextStorage string] =
characterAtIndex:glyph->char_index];
   =20
    glyph->pos =3D *position;
    glyph->size.width =3D 0;
    glyph->dont_show =3D YES;
   =20
    switch (ch) {
        case kNewlineCharacter:
            *outNewParagraph =3D YES;
            return NO;  // Stop processing this line
           =20
        case kTabCharacter:
            *position =3D [self _positionAfterTabFrom:*position =
inFragment:fragment];
            return YES;  // Continue to next glyph
           =20
        default:
            NSDebugLLog(@"GSHorizontalTypesetter", @"Unknown control =
%04x", ch);
            return YES;
    }
}

- (BOOL)_handleAttachmentGlyph:(glyph_cache_t *)glyph=20
                    atPosition:(NSPoint *)position
                 lineFragment:(line_frag_t *)fragment
                   lineHeight:(CGFloat *)lineHeight
{
    // Extract attachment logic...
    // Return YES if fits and metrics updated, NO if needs line break
}
```

### 11. **Extract Metric Calculation to Dedicated Method**

```objc
- (void)_updateLineMetricsForGlyph:(glyph_cache_t *)glyph
                    currentAscender:(CGFloat *)ascender
                   currentDescender:(CGFloat *)descender
                       currentHeight:(CGFloat *)lineHeight
                        maxLineHeight:(CGFloat)maxHeight
{
    NSFont *font =3D glyph->font;
    CGFloat glyphAscender =3D [font ascender];
    CGFloat glyphDescender =3D -[font descender];
   =20
    // Apply superscript adjustments
    CGFloat yOffset =3D 0;
    if (glyph->attributes.superscript) {
        yOffset -=3D glyph->attributes.superscript * [font xHeight];
    }
    if (glyph->attributes.baseline_offset) {
        yOffset +=3D glyph->attributes.baseline_offset;
    }
   =20
    // Update metrics
    *ascender =3D MAX(*ascender, glyphAscender - MIN(yOffset, 0));
    *descender =3D MAX(*descender, glyphDescender + MAX(yOffset, 0));
   =20
    CGFloat newHeight =3D *ascender + *descender;
    if (maxHeight > 0) {
        newHeight =3D MIN(newHeight, maxHeight);
    }
    *lineHeight =3D MAX(*lineHeight, newHeight);
}
```

### 12. **Simplify Soft Invalidation Logic**

The soft invalidation method is hard to follow. Restructure with early =
returns and clear phases:

```objc
- (BOOL)_reuseSoftInvalidatedLayout
{
    // Quick reject: not at soft invalidation point
    if (![self _isAtSoftInvalidationPoint]) return NO;
   =20
    // Quick reject: won't fit vertically
    NSRect firstRect =3D [self _firstSoftInvalidatedRect];
    if (![self _canFitVertically:firstRect]) return NO;
   =20
    // Collect consecutive valid fragments
    GSSoftInvalidationBatch *batch =3D [self =
_collectSoftInvalidatedFragments];
    if (batch.isEmpty) return NO;
   =20
    // Apply shift and commit
    [self _applySoftInvalidationBatch:batch];
    curGlyphIndex =3D batch.endGlyphIndex;
    return YES;
}
```

---

## Summary Table

| Refactoring | Benefit | Effort |
|-------------|---------|--------|
| Named constants | Eliminates magic numbers, self-documenting | Low |
| Boolean methods | Clarifies intent of complex conditions | Low |
| Replace goto | Structured flow, easier debugging | Medium |
| State machine enum | Explicit transitions, type safety | Medium |
| Method extraction | Single responsibility, testable units | Medium |
| Named structs | Self-documenting, IDE completion | Low |
| Cache class | Encapsulation, easier optimization | High |
| Array abstraction | Memory safety, bounds checking | Medium |
| Return value enum | Compiler checking, clear semantics | Low |
| Glyph handlers | Flatten nesting, isolate concerns | Medium |
| Metric extraction | Reusable, testable calculations | Low |
| Soft invalidation phases | Linear flow, early exits | Medium |

These changes maintain performance (critical for text layout) while =
significantly improving maintainability. The most impactful would be =
**#5 (method extraction)** and **#3 (goto elimination)**, which together =
would reduce the cognitive load of understanding the layout algorithm.


=E2=80=94=E2=80=94 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94=
 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94=
=20


I see you read until here. I know, that was a lot to digest. Now I am =
asking: what do you think about this? Does it make sense? Should we do =
those refactorings?


Kind regards,

	Lars=

--Apple-Mail=_129DB140-0349-41BF-87CA-B70DD12BB7CE
Content-Transfer-Encoding: quoted-printable
Content-Type: text/html;
	charset=utf-8

<html><head><meta http-equiv=3D"content-type" content=3D"text/html; =
charset=3Dutf-8"></head><body style=3D"overflow-wrap: break-word; =
-webkit-nbsp-mode: space; line-break: after-white-space;"><div>Hi =
GNUstep-Devs,</div><div><br></div><div><br></div><div>especially =
Riccardo, Fred and Greg since we were talking about this in the call =
today.</div><div><br></div><div><br></div><div>I had the idea to let a =
Coding AI, in this case&nbsp;<a =
href=3D"https://www.kimi.com/">https://www.kimi.com/</a>&nbsp;analyze =
our GSHorizontalTypesetter code and =E2=80=9Eexplain=E2=80=9C it inside =
comments added to the code.</div><div><br></div><div>So I asked it the =
following =
question:</div><div><br></div><div><br></div><div><div>=E2=80=9EHello =
KIMI, please analyze the following code and explain it in comments you =
add or correct:=E2=80=9C</div><div><br></div><div><br></div><div>and =
then gave it both the header and the =
implementation.</div><div><br></div></div><div><br></div><div>Here is =
the answer (The answer seems to be in Markup-Format so maybe you cut and =
paste it into some markup editor for better =
readability):</div><div><br></div><div>=E2=80=94=E2=80=94 =E2=80=94=E2=80=94=
 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94=
 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94&nbsp;</div><div><br></div><div>&nbs=
p;I'll analyze this GNUstep GUI Library code and add comprehensive =
comments explaining the functionality. Let me go through both the header =
and implementation =
files.</div><div><br></div><div>```objc</div><div>/*</div><div>&nbsp; =
&nbsp;GSHorizontalTypesetter.h</div><div><br></div><div>&nbsp; =
&nbsp;Copyright (C) 2002 Free Software Foundation, =
Inc.</div><div><br></div><div>&nbsp; &nbsp;Author: Alexander Malmberg =
&lt;[email protected]&gt;</div><div>&nbsp; &nbsp;Date: =
2002</div><div><br></div><div>&nbsp; &nbsp;This file is part of the =
GNUstep GUI Library.</div><div><br></div><div>&nbsp; &nbsp;This library =
is free software; you can redistribute it and/or</div><div>&nbsp; =
&nbsp;modify it under the terms of the GNU Lesser General =
Public</div><div>&nbsp; &nbsp;License as published by the Free Software =
Foundation; either</div><div>&nbsp; &nbsp;version 2 of the License, or =
(at your option) any later version.</div><div><br></div><div>&nbsp; =
&nbsp;This library is distributed in the hope that it will be =
useful,</div><div>&nbsp; &nbsp;but WITHOUT ANY WARRANTY; without even =
the implied warranty of</div><div>&nbsp; &nbsp;MERCHANTABILITY or =
FITNESS FOR A PARTICULAR PURPOSE.<span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> See the GNU</div><div>&nbsp; =
&nbsp;Lesser General Public License for more =
details.</div><div><br></div><div>&nbsp; &nbsp;You should have received =
a copy of the GNU Lesser General Public</div><div>&nbsp; &nbsp;License =
along with this library; see the file COPYING.LIB.</div><div>&nbsp; =
&nbsp;If not, see &lt;http://www.gnu.org/licenses/ &gt; or write to =
the&nbsp;</div><div>&nbsp; &nbsp;Free Software Foundation, 51 Franklin =
Street, Fifth Floor,&nbsp;</div><div>&nbsp; &nbsp;Boston, MA 02110-1301, =
USA.</div><div>*/</div><div><br></div><div>#ifndef =
_GNUstep_H_GSHorizontalTypesetter</div><div>#define =
_GNUstep_H_GSHorizontalTypesetter</div><div><br></div><div>#import =
&lt;GNUstepGUI/GSTypesetter.h&gt;</div><div><br></div><div>@class =
NSLock;</div><div>@class GSLayoutManager, NSTextContainer, =
NSTextStorage;</div><div>@class NSDictionary;</div><div>@class =
NSParagraphStyle, NSFont;</div><div><br></div><div>/*</div><div>&nbsp;* =
APPKIT_EXPORT_CLASS marks this class for export from the AppKit =
framework.</div><div>&nbsp;* This is a GNUstep-specific macro for =
DLL/shared library symbol =
visibility.</div><div>&nbsp;*/</div><div>APPKIT_EXPORT_CLASS</div><div><br=
></div><div>/*</div><div>&nbsp;* GSHorizontalTypesetter is the concrete =
typesetter implementation for</div><div>&nbsp;* horizontal text layout =
in GNUstep. It handles the complex task of laying out</div><div>&nbsp;* =
glyphs into lines within text containers, supporting various paragraph =
styles,</div><div>&nbsp;* alignments, line breaking modes, and text =
attributes.</div><div>&nbsp;*</div><div>&nbsp;* This class implements =
the core text layout algorithm that converts a stream</div><div>&nbsp;* =
of glyphs from the layout manager into positioned line fragments =
within</div><div>&nbsp;* the geometric constraints of text =
containers.</div><div>&nbsp;*/</div><div>@interface =
GSHorizontalTypesetter : GSTypesetter</div><div>{</div><div>&nbsp; =
/*</div><div>&nbsp; &nbsp;* REENTRANCY LOCK</div><div>&nbsp; &nbsp;* =
Since this is typically a shared singleton instance, the lock =
ensures</div><div>&nbsp; &nbsp;* thread safety. If the typesetter is =
already in use when another layout</div><div>&nbsp; &nbsp;* request =
comes in, a temporary instance is created instead.</div><div>&nbsp; =
&nbsp;*/</div><div>&nbsp; NSLock *lock;</div><div><br></div><div>&nbsp; =
/*</div><div>&nbsp; &nbsp;* CURRENT LAYOUT CONTEXT</div><div>&nbsp; =
&nbsp;* These ivars track the active text system objects being =
processed.</div><div>&nbsp; &nbsp;* They are set at the beginning of =
each layout operation and remain</div><div>&nbsp; &nbsp;* constant =
throughout that operation.</div><div>&nbsp; &nbsp;*/</div><div>&nbsp; =
GSLayoutManager *curLayoutManager; &nbsp; &nbsp;// Converts characters =
to glyphs, tracks runs</div><div>&nbsp; NSTextContainer =
*curTextContainer; &nbsp; &nbsp;// Defines geometric bounds for =
text</div><div>&nbsp; NSTextStorage *curTextStorage; &nbsp; &nbsp; =
&nbsp; &nbsp;// The attributed string being laid =
out</div><div><br></div><div>&nbsp; unsigned int curGlyphIndex; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; // Current position in the glyph =
stream</div><div>&nbsp; NSPoint curPoint; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Current layout position (y =
advances per line)</div><div><br></div><div>&nbsp; /*</div><div>&nbsp; =
&nbsp;* ATTRIBUTE CACHING</div><div>&nbsp; &nbsp;* These ivars cache the =
current paragraph style and attributes to avoid</div><div>&nbsp; &nbsp;* =
repeated dictionary lookups. The ranges track the validity of each =
cache.</div><div>&nbsp; &nbsp;*/</div><div>&nbsp; NSParagraphStyle =
*curParagraphStyle; &nbsp;// Current paragraph's formatting =
rules</div><div>&nbsp; NSRange paragraphRange; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; // Character range where curParagraphStyle =
is valid</div><div><br></div><div>&nbsp; NSDictionary *curAttributes; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// Current character attributes =
dictionary</div><div>&nbsp; NSRange attributeRange; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; // Character range where curAttributes is =
valid</div><div>&nbsp;&nbsp;</div><div>&nbsp; /*</div><div>&nbsp; =
&nbsp;* DECOMPOSED ATTRIBUTES</div><div>&nbsp; &nbsp;* Frequently =
accessed attributes are extracted from the dictionary =
and</div><div>&nbsp; &nbsp;* stored in this struct for faster access =
during the tight layout loop.</div><div>&nbsp; &nbsp;*/</div><div>&nbsp; =
struct</div><div>&nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; BOOL =
explicit_kern; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // YES =
if NSKernAttributeName is present</div><div>&nbsp; &nbsp; &nbsp; float =
kern; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; // Kerning adjustment value</div><div>&nbsp; &nbsp; &nbsp; =
float baseline_offset; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// =
Vertical offset from baseline</div><div>&nbsp; &nbsp; &nbsp; int =
superscript; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp;// Superscript level (+1, -1, etc.)</div><div>&nbsp; &nbsp; } =
attributes;</div><div><br></div><div>&nbsp; NSFont *curFont; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// =
Current font for glyph metrics</div><div>&nbsp; NSRange fontRange; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// =
Glyph range where curFont is valid</div><div><br></div><div>&nbsp; =
/*</div><div>&nbsp; &nbsp;* GLYPH CACHE</div><div>&nbsp; &nbsp;* A =
resizable array of glyph_cache_t structures that stores =
pre-computed</div><div>&nbsp; &nbsp;* information about glyphs to avoid =
repeated calculations. This is the</div><div>&nbsp; &nbsp;* primary =
optimization for the layout engine.</div><div>&nbsp; =
&nbsp;*</div><div>&nbsp; &nbsp;* cache_base: &nbsp; The glyph index in =
the layout manager that corresponds</div><div>&nbsp; &nbsp;* &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; to cache[0]. All cache indices =
are relative to this.</div><div>&nbsp; &nbsp;* cache_size: &nbsp; =
Allocated capacity of the cache array (in elements)</div><div>&nbsp; =
&nbsp;* cache_length: Number of valid entries currently in the =
cache</div><div>&nbsp; &nbsp;* at_end: &nbsp; &nbsp; &nbsp; YES when =
we've cached all glyphs in the text storage</div><div>&nbsp; =
&nbsp;*/</div><div>&nbsp; struct GSHorizontalTypesetter_glyph_cache_s =
*cache;</div><div>&nbsp; unsigned int cache_base, cache_size, =
cache_length;</div><div>&nbsp; BOOL =
at_end;</div><div><br></div><div>&nbsp; /*</div><div>&nbsp; &nbsp;* LINE =
FRAGMENT ARRAY</div><div>&nbsp; &nbsp;* During layout of a single line, =
this array stores the line fragment</div><div>&nbsp; &nbsp;* rectangles =
returned by the text container. A line may consist of</div><div>&nbsp; =
&nbsp;* multiple fragments when flowing around exclusions or =
shapes.</div><div>&nbsp; &nbsp;*</div><div>&nbsp; &nbsp;* line_frags: =
&nbsp; &nbsp; &nbsp;Array of line_frag_t structures</div><div>&nbsp; =
&nbsp;* line_frags_num: &nbsp;Number of fragments in current =
line</div><div>&nbsp; &nbsp;* line_frags_size: Allocated capacity of the =
array</div><div>&nbsp; &nbsp;*/</div><div>&nbsp; struct =
GSHorizontalTypesetter_line_frag_s *line_frags;</div><div>&nbsp; int =
line_frags_num, =
line_frags_size;</div><div>}</div><div><br></div><div>/*</div><div>&nbsp;*=
 Returns the shared thread-local instance of the =
typesetter.</div><div>&nbsp;* Each thread maintains its own instance in =
the thread dictionary</div><div>&nbsp;* to avoid cross-thread contention =
while still allowing =
reuse.</div><div>&nbsp;*/</div><div>+(GSHorizontalTypesetter *) =
sharedInstance;</div><div><br></div><div>@end</div><div><br></div><div>#en=
dif</div><div>```</div><div><br></div><div>Now for the implementation =
file with detailed =
comments:</div><div><br></div><div>```objc</div><div>/*</div><div>&nbsp; =
&nbsp;GSHorizontalTypesetter.m</div><div><br></div><div>&nbsp; =
&nbsp;Copyright (C) 2002, 2003 Free Software Foundation, =
Inc.</div><div><br></div><div>&nbsp; &nbsp;Author: Alexander Malmberg =
&lt;[email protected]&gt;</div><div>&nbsp; &nbsp;Date: November =
2002 - February 2003</div><div><br></div><div>&nbsp; &nbsp;This file is =
part of the GNUstep GUI Library.</div><div><br></div><div>&nbsp; =
&nbsp;This library is free software; you can redistribute it =
and/or</div><div>&nbsp; &nbsp;modify it under the terms of the GNU =
Lesser General Public</div><div>&nbsp; &nbsp;License as published by the =
Free Software Foundation; either</div><div>&nbsp; &nbsp;version 2 of the =
License, or (at your option) any later =
version.</div><div><br></div><div>&nbsp; &nbsp;This library is =
distributed in the hope that it will be useful,</div><div>&nbsp; =
&nbsp;but WITHOUT ANY WARRANTY; without even the implied warranty =
of</div><div>&nbsp; &nbsp;MERCHANTABILITY or FITNESS FOR A PARTICULAR =
PURPOSE.<span class=3D"Apple-tab-span" style=3D"white-space:pre">	=
</span> See the GNU</div><div>&nbsp; &nbsp;Lesser General Public License =
for more details.</div><div><br></div><div>&nbsp; &nbsp;You should have =
received a copy of the GNU Lesser General Public</div><div>&nbsp; =
&nbsp;License along with this library; see the file =
COPYING.LIB.</div><div>&nbsp; &nbsp;If not, see =
&lt;http://www.gnu.org/licenses/&gt; or write to =
the&nbsp;</div><div>&nbsp; &nbsp;Free Software Foundation, 51 Franklin =
Street, Fifth Floor,&nbsp;</div><div>&nbsp; &nbsp;Boston, MA 02110-1301, =
USA.</div><div>*/</div><div><br></div><div><br></div><div>#include =
&lt;math.h&gt;</div><div><br></div><div>#import =
&lt;Foundation/NSDebug.h&gt;</div><div>#import =
&lt;Foundation/NSException.h&gt;</div><div>#import =
&lt;Foundation/NSGeometry.h&gt;</div><div>#import =
&lt;Foundation/NSLock.h&gt;</div><div>#import =
&lt;Foundation/NSThread.h&gt;</div><div>#import =
&lt;Foundation/NSValue.h&gt;</div><div><br></div><div>#import =
"AppKit/NSAttributedString.h"</div><div>#import =
"AppKit/NSParagraphStyle.h"</div><div>#import =
"AppKit/NSTextAttachment.h"</div><div>#import =
"AppKit/NSTextContainer.h"</div><div>#import =
"AppKit/NSTextStorage.h"</div><div>#import =
"GNUstepGUI/GSLayoutManager.h"</div><div>#import =
"GNUstepGUI/GSHorizontalTypesetter.h"</div><div><br></div><div><br></div><=
div>/*</div><div>&nbsp;* DESIGN NOTE: Unless the user creates extra =
instances, there will only be one</div><div>&nbsp;* instance of =
GSHorizontalTypesetter per thread for all text =
typesetting.</div><div>&nbsp;* This allows aggressive caching without =
memory concerns since the cache is</div><div>&nbsp;* reused across =
multiple layout operations rather than being recreated each =
time.</div><div>&nbsp;*/</div><div><br></div><div><br></div><div>@implemen=
tation =
GSHorizontalTypesetter</div><div><br></div><div>/*</div><div>&nbsp;* =
Standard initializer. Creates the reentrancy lock used to =
detect</div><div>&nbsp;* nested layout calls on the same =
instance.</div><div>&nbsp;*/</div><div>- =
init</div><div>{</div><div>&nbsp; if (!(self =3D [super init])) return =
nil;</div><div>&nbsp; lock =3D [[NSLock alloc] init];</div><div>&nbsp; =
return self;</div><div>}</div><div><br></div><div>/*</div><div>&nbsp;* =
Cleanup: release the lock and free all cached memory =
buffers.</div><div>&nbsp;*/</div><div>-(void) =
dealloc</div><div>{</div><div>&nbsp; if (cache)</div><div>&nbsp; &nbsp; =
{</div><div>&nbsp; &nbsp; &nbsp; free(cache);</div><div>&nbsp; &nbsp; =
&nbsp; cache =3D NULL;</div><div>&nbsp; &nbsp; }</div><div>&nbsp; if =
(line_frags)</div><div>&nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; =
free(line_frags);</div><div>&nbsp; &nbsp; &nbsp; line_frags =3D =
NULL;</div><div>&nbsp; &nbsp; }</div><div>&nbsp; =
DESTROY(lock);</div><div>&nbsp; [super =
dealloc];</div><div>}</div><div><br></div><div>/*</div><div>&nbsp;* =
Thread-local singleton accessor. Each thread gets its own =
instance</div><div>&nbsp;* stored in the thread dictionary under a =
unique key. This provides</div><div>&nbsp;* instance reuse without =
requiring cross-thread =
synchronization.</div><div>&nbsp;*/</div><div>+(GSHorizontalTypesetter =
*) sharedInstance</div><div>{</div><div>&nbsp; NSMutableDictionary =
*threadDict =3D&nbsp;</div><div>&nbsp; &nbsp; [[NSThread currentThread] =
threadDictionary];</div><div>&nbsp; GSHorizontalTypesetter *shared =
=3D&nbsp;</div><div>&nbsp; &nbsp; [threadDict objectForKey: =
@"sharedHorizontalTypesetter"];</div><div><br></div><div>&nbsp; if =
(!shared)</div><div>&nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; =
shared =3D [[self alloc] init];</div><div>&nbsp; &nbsp; &nbsp; =
[threadDict setObject: shared</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span> &nbsp;forKey: =
@"sharedHorizontalTypesetter"];</div><div>&nbsp; &nbsp; &nbsp; =
RELEASE(shared);</div><div>&nbsp; &nbsp; =
}</div><div><br></div><div>&nbsp; return =
shared;</div><div>}</div><div><br></div><div>/*</div><div>&nbsp;* CACHE =
MANAGEMENT CONSTANTS</div><div>&nbsp;* CACHE_INITIAL: Starting size for =
glyph cache (192 glyphs)</div><div>&nbsp;* CACHE_STEP: &nbsp; =
&nbsp;Increment size when cache needs to =
grow</div><div>&nbsp;*/</div><div>#define CACHE_INITIAL =
192</div><div>#define CACHE_STEP =
192</div><div><br></div><div><br></div><div>/*</div><div>&nbsp;* GLYPH =
CACHE ENTRY</div><div>&nbsp;* Stores all information needed to position =
a single glyph.</div><div>&nbsp;* Split into two =
phases:</div><div>&nbsp;* &nbsp; 1. Filled during caching =
(_cacheGlyphs:)</div><div>&nbsp;* &nbsp; 2. Filled during layout =
(layoutLineNewParagraph:)</div><div>&nbsp;*/</div><div>struct =
GSHorizontalTypesetter_glyph_cache_s</div><div>{</div><div>&nbsp; /* =
PHASE 1: Caching - extracted from layout manager and attributes =
*/</div><div>&nbsp; NSGlyph g; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp;// The glyph index (NSGlyph is an integer =
type)</div><div>&nbsp; unsigned int char_index; &nbsp; &nbsp; &nbsp;// =
Corresponding character index in text =
storage</div><div><br></div><div>&nbsp; NSFont *font; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Font to use for this =
glyph</div><div>&nbsp; struct</div><div>&nbsp; &nbsp; {</div><div>&nbsp; =
&nbsp; &nbsp; BOOL explicit_kern; &nbsp; &nbsp; &nbsp; // Whether to =
apply explicit kerning</div><div>&nbsp; &nbsp; &nbsp; float kern; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Kerning value from =
attributes</div><div>&nbsp; &nbsp; &nbsp; float baseline_offset; &nbsp; =
&nbsp;// Vertical offset from baseline</div><div>&nbsp; &nbsp; &nbsp; =
int superscript; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// Superscript =
level</div><div>&nbsp; &nbsp; } =
attributes;</div><div><br></div><div>&nbsp; /* PHASE 2: Layout - =
computed during line layout */</div><div>&nbsp; BOOL nominal; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // YES if glyph has =
standard spacing (no adjustments)</div><div>&nbsp; NSPoint pos; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// Position =
relative to the line's baseline</div><div>&nbsp; NSSize size; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// Advancement =
width; height used only for attachments</div><div>&nbsp; BOOL dont_show, =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // YES for whitespace =
glyphs that shouldn't render</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp;outside_line_frag; &nbsp; &nbsp; &nbsp; // YES for glyphs that =
overflow the fragment (clipping mode)</div><div>};</div><div>typedef =
struct GSHorizontalTypesetter_glyph_cache_s =
glyph_cache_t;</div><div><br></div><div>/*</div><div>&nbsp;* Clears all =
cached attribute and glyph information.</div><div>&nbsp;* Called at the =
start of each layout operation to ensure we don't</div><div>&nbsp;* use =
stale data from previous layouts. Note that we don't free =
the</div><div>&nbsp;* cache memory, just reset the valid length to =
zero.</div><div>&nbsp;*</div><div>&nbsp;* TODO: If we could detect =
whether the layout manager has been modified</div><div>&nbsp;* since our =
last layout, we could avoid clearing the cache =
unnecessarily.</div><div>&nbsp;*/</div><div>-(void) =
_cacheClear</div><div>{</div><div>&nbsp; cache_length =3D =
0;</div><div><br></div><div>&nbsp; curParagraphStyle =3D =
nil;</div><div>&nbsp; paragraphRange =3D NSMakeRange(0, =
0);</div><div>&nbsp; curAttributes =3D nil;</div><div>&nbsp; =
attributeRange =3D NSMakeRange(0, 0);</div><div>&nbsp; curFont =3D =
nil;</div><div>&nbsp; fontRange =3D NSMakeRange(0, =
0);</div><div>}</div><div><br></div><div>/*</div><div>&nbsp;* Caches the =
attributes for the character at the given index.</div><div>&nbsp;* Uses =
range checking to avoid redundant dictionary lookups - if =
the</div><div>&nbsp;* requested index is within attributeRange, we =
already have the data.</div><div>&nbsp;* Extracts kern, baseline offset, =
and superscript into the attributes =
struct.</div><div>&nbsp;*/</div><div>-(void) _cacheAttributes: (unsigned =
int)char_index</div><div>{</div><div>&nbsp; NSNumber =
*n;</div><div><br></div><div>&nbsp; if (NSLocationInRange(char_index, =
attributeRange))</div><div>&nbsp; &nbsp; {</div><div>&nbsp; &nbsp; =
&nbsp; return;</div><div>&nbsp; &nbsp; =
}</div><div>&nbsp;&nbsp;</div><div>&nbsp; curAttributes =3D =
[curTextStorage attributesAtIndex: char_index</div><div>&nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;effectiveRange: =
&amp;attributeRange];</div><div><br></div><div>&nbsp; /* Extract kerning =
attribute */</div><div>&nbsp; n =3D [curAttributes objectForKey: =
NSKernAttributeName];</div><div>&nbsp; if (!n)</div><div>&nbsp; &nbsp; =
attributes.explicit_kern =3D NO;</div><div>&nbsp; else</div><div>&nbsp; =
&nbsp; {</div><div>&nbsp; &nbsp; &nbsp; attributes.explicit_kern =3D =
YES;</div><div>&nbsp; &nbsp; &nbsp; attributes.kern =3D [n =
floatValue];</div><div>&nbsp; &nbsp; }</div><div><br></div><div>&nbsp; =
/* Extract baseline offset (positive =3D up, negative =3D down in =
standard Cocoa coords) */</div><div>&nbsp; n =3D [curAttributes =
objectForKey: NSBaselineOffsetAttributeName];</div><div>&nbsp; if =
(n)</div><div>&nbsp; &nbsp; attributes.baseline_offset =3D [n =
floatValue];</div><div>&nbsp; else</div><div>&nbsp; &nbsp; =
attributes.baseline_offset =3D 0.0;</div><div><br></div><div>&nbsp; /* =
Extract superscript level */</div><div>&nbsp; n =3D [curAttributes =
objectForKey: NSSuperscriptAttributeName];</div><div>&nbsp; if =
(n)</div><div>&nbsp; &nbsp; attributes.superscript =3D [n =
intValue];</div><div>&nbsp; else</div><div>&nbsp; &nbsp; =
attributes.superscript =3D =
0;</div><div>}</div><div><br></div><div>/*</div><div>&nbsp;* Repositions =
the cache window to start at the specified glyph =
index.</div><div>&nbsp;*&nbsp;</div><div>&nbsp;* If the requested glyph =
is already within our cache window, we shift the</div><div>&nbsp;* =
existing data to the front (memmove) to make room for new glyphs =
ahead.</div><div>&nbsp;*&nbsp;</div><div>&nbsp;* If it's outside our =
cache, we reset completely and fetch new paragraph</div><div>&nbsp;* =
style, attributes, and font information from the layout =
manager.</div><div>&nbsp;*/</div><div>-(void) _cacheMoveTo: (unsigned =
int)glyph</div><div>{</div><div>&nbsp; BOOL =
valid;</div><div><br></div><div>&nbsp; /* Case 1: Requested glyph is =
already in our cache window */</div><div>&nbsp; if (cache_base &lt;=3D =
glyph &amp;&amp; cache_base + cache_length &gt; glyph)</div><div>&nbsp; =
&nbsp; {</div><div>&nbsp; &nbsp; &nbsp; int delta =3D glyph - =
cache_base;</div><div>&nbsp; &nbsp; &nbsp; cache_length -=3D =
delta;</div><div>&nbsp; &nbsp; &nbsp; memmove(cache, &amp;cache[delta], =
sizeof(glyph_cache_t) * cache_length);</div><div>&nbsp; &nbsp; &nbsp; =
cache_base =3D glyph;</div><div>&nbsp; &nbsp; &nbsp; =
return;</div><div>&nbsp; &nbsp; }</div><div><br></div><div>&nbsp; /* =
Case 2: Complete reset - new location in text stream */</div><div>&nbsp; =
cache_base =3D glyph;</div><div>&nbsp; cache_length =3D =
0;</div><div><br></div><div>&nbsp; [curLayoutManager glyphAtIndex: =
glyph</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">	=
	</span> &nbsp; &nbsp;isValidIndex: =
&amp;valid];</div><div><br></div><div>&nbsp; if (valid)</div><div>&nbsp; =
&nbsp; {</div><div>&nbsp; &nbsp; &nbsp; unsigned int =
i;</div><div><br></div><div>&nbsp; &nbsp; &nbsp; at_end =3D =
NO;</div><div>&nbsp; &nbsp; &nbsp; i =3D [curLayoutManager =
characterIndexForGlyphAtIndex: glyph];</div><div>&nbsp; &nbsp; &nbsp; =
[self _cacheAttributes: i];</div><div><br></div><div>&nbsp; &nbsp; =
&nbsp; /* Fetch paragraph style and its valid range */</div><div>&nbsp; =
&nbsp; &nbsp; paragraphRange =3D NSMakeRange(i, [curTextStorage length] =
- i);</div><div>&nbsp; &nbsp; &nbsp; curParagraphStyle =3D =
[curTextStorage attribute: NSParagraphStyleAttributeName</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">				=
	</span>atIndex: i</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">					=
</span>longestEffectiveRange: &amp;paragraphRange</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">				=
	</span>inRange: paragraphRange];</div><div>&nbsp; &nbsp; &nbsp; =
if (curParagraphStyle =3D=3D nil)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; =
{</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; curParagraphStyle =3D =
[NSParagraphStyle defaultParagraphStyle];</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; }</div><div><br></div><div>&nbsp; &nbsp; &nbsp; /* Fetch initial =
font and its valid range */</div><div>&nbsp; &nbsp; &nbsp; curFont =3D =
[curLayoutManager effectiveFontForGlyphAtIndex: glyph</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">				=
</span>range: &amp;fontRange];</div><div>&nbsp; &nbsp; =
}</div><div>&nbsp; else</div><div>&nbsp; &nbsp; {</div><div>&nbsp; =
&nbsp; &nbsp; at_end =3D YES; &nbsp;// No valid glyph at this index - =
we're at end of text</div><div>&nbsp; &nbsp; =
}</div><div>}</div><div><br></div><div>/*</div><div>&nbsp;* Fills the =
glyph cache up to new_length entries.</div><div>&nbsp;* Grows the cache =
buffer if necessary using realloc.</div><div>&nbsp;* For each new glyph, =
fetches:</div><div>&nbsp;* &nbsp; - Glyph index and character index from =
layout manager</div><div>&nbsp;* &nbsp; - Attributes (if character index =
moved past attributeRange)</div><div>&nbsp;* &nbsp; - Font (if glyph =
moved past fontRange)</div><div>&nbsp;* &nbsp; - Advancement size from =
layout manager</div><div>&nbsp;*</div><div>&nbsp;* Stops early if we hit =
invalid glyphs or paragraph =
boundaries.</div><div>&nbsp;*/</div><div>-(void) _cacheGlyphs: (unsigned =
int)new_length</div><div>{</div><div>&nbsp; glyph_cache_t =
*g;</div><div>&nbsp; BOOL valid;</div><div><br></div><div>&nbsp; /* Grow =
buffer if needed */</div><div>&nbsp; if (cache_size &lt; =
new_length)</div><div>&nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; =
cache_size =3D new_length;</div><div>&nbsp; &nbsp; &nbsp; cache =3D =
realloc(cache, sizeof(glyph_cache_t) * cache_size);</div><div>&nbsp; =
&nbsp; }</div><div><br></div><div>&nbsp; /* Fill cache entries from =
current length up to new_length */</div><div>&nbsp; for (g =3D =
&amp;cache[cache_length]; cache_length &lt; new_length; cache_length++, =
g++)</div><div>&nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; g-&gt;g =3D =
[curLayoutManager glyphAtIndex: cache_base + =
cache_length</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">			</span> &nbsp; &nbsp; =
&nbsp; isValidIndex: &amp;valid];</div><div>&nbsp; &nbsp; &nbsp; if =
(!valid)</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span>{</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> =
&nbsp;at_end =3D YES;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp;break;</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	=
</span>}</div><div>&nbsp; &nbsp; &nbsp; g-&gt;char_index =3D =
[curLayoutManager characterIndexForGlyphAtIndex: cache_base + =
cache_length];</div><div>&nbsp; &nbsp; &nbsp;&nbsp;</div><div>&nbsp; =
&nbsp; &nbsp; /* Stop if we crossed paragraph boundary =
*/</div><div>&nbsp; &nbsp; &nbsp; if (g-&gt;char_index &gt;=3D =
paragraphRange.location + paragraphRange.length)</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	=
</span>{</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp;at_end =3D =
YES;</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">	=
</span> &nbsp;break;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span>}</div><div><br></div><div>&nbsp; =
&nbsp; &nbsp; /* Update attribute cache if needed */</div><div>&nbsp; =
&nbsp; &nbsp; if (g-&gt;char_index &gt;=3D attributeRange.location + =
attributeRange.length)</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span>{</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> =
&nbsp;[self _cacheAttributes: g-&gt;char_index];</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	=
</span>}</div><div><br></div><div>&nbsp; &nbsp; &nbsp; /* Copy =
decomposed attributes into cache entry */</div><div>&nbsp; &nbsp; &nbsp; =
g-&gt;attributes.explicit_kern =3D =
attributes.explicit_kern;</div><div>&nbsp; &nbsp; &nbsp; =
g-&gt;attributes.kern =3D attributes.kern;</div><div>&nbsp; &nbsp; =
&nbsp; g-&gt;attributes.baseline_offset =3D =
attributes.baseline_offset;</div><div>&nbsp; &nbsp; &nbsp; =
g-&gt;attributes.superscript =3D =
attributes.superscript;</div><div><br></div><div>&nbsp; &nbsp; &nbsp; /* =
Update font cache if needed */</div><div>&nbsp; &nbsp; &nbsp; if =
(cache_base + cache_length &gt;=3D fontRange.location + =
fontRange.length)</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span>{</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> =
&nbsp;curFont =3D [curLayoutManager effectiveFontForGlyphAtIndex: =
cache_base + cache_length</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">				</span> &nbsp; =
&nbsp;range: &amp;fontRange];</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span>}</div><div>&nbsp; &nbsp; &nbsp; =
g-&gt;font =3D curFont;</div><div><br></div><div>&nbsp; &nbsp; &nbsp; /* =
Initialize layout fields */</div><div>&nbsp; &nbsp; &nbsp; =
g-&gt;dont_show =3D NO;</div><div>&nbsp; &nbsp; &nbsp; =
g-&gt;outside_line_frag =3D NO;</div><div>&nbsp; &nbsp; &nbsp; =
g-&gt;nominal =3D YES;</div><div><br></div><div>&nbsp; &nbsp; &nbsp; /* =
Get glyph advancement from layout manager */</div><div>&nbsp; &nbsp; =
&nbsp; // FIXME: This assumes the layout manager implements this GNUstep =
extension</div><div>&nbsp; &nbsp; &nbsp; g-&gt;size =3D =
[curLayoutManager advancementForGlyphAtIndex: cache_base + =
cache_length];</div><div>&nbsp; &nbsp; =
}</div><div>}</div><div><br></div><div><br></div><div>/*</div><div>&nbsp;*=
 WORD WRAPPING SUPPORT</div><div>&nbsp;* Searches backward from glyph gi =
to find a suitable word break point.</div><div>&nbsp;* Returns the glyph =
index of the first glyph on the next =
line.</div><div>&nbsp;*</div><div>&nbsp;* Breaking =
rules:</div><div>&nbsp;* &nbsp; - Control glyphs (newlines, tabs) are =
always break points</div><div>&nbsp;* &nbsp; - Whitespace characters =
(space, newline, CR, tab) mark breaks and are hidden</div><div>&nbsp;* =
&nbsp; - CJK characters (0x2FF0-0x9FFF) can break before (each CJK char =
is its own word)</div><div>&nbsp;*</div><div>&nbsp;* The returned index =
is always &gt;=3D cache_base and &lt;=3D =
gi.</div><div>&nbsp;*/</div><div>-(unsigned int) =
breakLineByWordWrappingBefore: (unsigned =
int)gi</div><div>{</div><div>&nbsp; glyph_cache_t *g;</div><div>&nbsp; =
unichar ch;</div><div>&nbsp; NSString *str =3D [curTextStorage =
string];</div><div><br></div><div>&nbsp; gi -=3D cache_base; &nbsp;// =
Convert to cache-relative index</div><div>&nbsp; g =3D cache + =
gi;</div><div><br></div><div>&nbsp; while (gi &gt; 0)</div><div>&nbsp; =
&nbsp; {</div><div>&nbsp; &nbsp; &nbsp; if (g-&gt;g =3D=3D =
NSControlGlyph)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; return gi + =
cache_base; &nbsp;// Always break at control glyphs</div><div>&nbsp; =
&nbsp; &nbsp;&nbsp;</div><div>&nbsp; &nbsp; &nbsp; ch =3D [str =
characterAtIndex: g-&gt;char_index];</div><div>&nbsp; &nbsp; =
&nbsp;&nbsp;</div><div>&nbsp; &nbsp; &nbsp; /* Check for whitespace =
characters that allow breaking */</div><div>&nbsp; &nbsp; &nbsp; if (ch =
=3D=3D 0x20 || // space</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ch =
=3D=3D 0x0a || // new line</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
ch =3D=3D 0x0d || // carriage return</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; ch =3D=3D 0x09) &nbsp; // horiz. tab</div><div>&nbsp; =
&nbsp; &nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
g-&gt;dont_show =3D YES; &nbsp;// Hide the whitespace character =
itself</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (gi &gt; =
0)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
{</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; g-&gt;pos =3D=
 g[-1].pos;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
g-&gt;pos.x +=3D g[-1].size.width;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
else</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; g-&gt;pos =3D =
NSMakePoint(0, 0);</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
g-&gt;size.width =3D 0;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
return gi + 1 + cache_base; &nbsp;// Break after the =
whitespace</div><div>&nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; =
&nbsp; &nbsp;&nbsp;</div><div>&nbsp; &nbsp; &nbsp; /* CJK characters: =
treat each as a word boundary */</div><div>&nbsp; &nbsp; &nbsp; else if =
((ch &gt; 0x2ff0) &amp;&amp; (ch &lt; 0x9fff))</div><div>&nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp;{</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp;g-&gt;dont_show =3D NO;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp;if (gi &gt; 0)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp;{</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp;g-&gt;pos =3D g[-1].pos;</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;g-&gt;pos.x +=3D =
g[-1].size.width;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp;}</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp;else</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp;g-&gt;pos =3D NSMakePoint(0,0);</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp;return gi + cache_base; &nbsp;// Break before this =
CJK character</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;} &nbsp; =
&nbsp;&nbsp;</div><div>&nbsp; &nbsp; &nbsp;&nbsp;</div><div>&nbsp; =
&nbsp; &nbsp; gi--;</div><div>&nbsp; &nbsp; &nbsp; g--;</div><div>&nbsp; =
&nbsp; }</div><div>&nbsp; return gi + cache_base; &nbsp;// Reached start =
of cache - break =
here</div><div>}</div><div><br></div><div><br></div><div>/*</div><div>&nbs=
p;* LINE FRAGMENT STRUCTURE</div><div>&nbsp;* Tracks the geometry and =
content of a single line fragment (a rectangular</div><div>&nbsp;* =
region within a line where glyphs are =
placed).</div><div>&nbsp;*/</div><div>struct =
GSHorizontalTypesetter_line_frag_s</div><div>{</div><div>&nbsp; NSRect =
rect; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// The fragment =
rectangle in container coordinates</div><div>&nbsp; CGFloat last_used; =
&nbsp; &nbsp; &nbsp; &nbsp;// X coordinate where glyph content =
ends</div><div>&nbsp; unsigned int lastGlyphIndex; // Index (relative to =
cache_base) of last glyph + 1</div><div>};</div><div>typedef struct =
GSHorizontalTypesetter_line_frag_s =
line_frag_t;</div><div><br></div><div>/*</div><div>&nbsp;* Apple's =
maximum meaningful width for text containers.</div><div>&nbsp;* Widths =
beyond this are treated as infinite and ignored for layout =
purposes.</div><div>&nbsp;*/</div><div>#define LARGE_SIZE =
1e7</div><div><br></div><div>/*</div><div>&nbsp;* FULL =
JUSTIFICATION</div><div>&nbsp;* Distributes extra space evenly across =
space characters in the line.</div><div>&nbsp;* Only operates if the =
line width is reasonable (not =
LARGE_SIZE).</div><div>&nbsp;*</div><div>&nbsp;* =
Algorithm:</div><div>&nbsp;* &nbsp; 1. Count space characters in the =
line</div><div>&nbsp;* &nbsp; 2. Calculate extra space per space: =
(rect.width - last_used) / num_spaces</div><div>&nbsp;* &nbsp; 3. Shift =
all glyphs after each space by accumulating delta</div><div>&nbsp;* =
&nbsp; 4. Mark glyphs after spaces as non-nominal (they have adjusted =
positions)</div><div>&nbsp;*/</div><div>-(void) fullJustifyLine: =
(line_frag_t *)lf : (int)num_line_frags</div><div>{</div><div>&nbsp; =
unsigned int i, start;</div><div>&nbsp; CGFloat extra_space, =
delta;</div><div>&nbsp; unsigned int num_spaces;</div><div>&nbsp; =
NSString *str =3D [curTextStorage string];</div><div>&nbsp; =
glyph_cache_t *g;</div><div>&nbsp; unichar =
ch;</div><div><br></div><div>&nbsp; if (lf-&gt;rect.size.width &gt;=3D =
LARGE_SIZE)</div><div>&nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; =
return;</div><div>&nbsp; &nbsp; }</div><div><br></div><div>&nbsp; for =
(start =3D 0; num_line_frags; num_line_frags--, lf++)</div><div>&nbsp; =
&nbsp; {</div><div>&nbsp; &nbsp; &nbsp; num_spaces =3D =
0;</div><div>&nbsp; &nbsp; &nbsp; for (i =3D start, g =3D cache + i; i =
&lt; lf-&gt;lastGlyphIndex; i++, g++)</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	=
</span>{</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp;if =
(g-&gt;dont_show)</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;continue;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp;ch =3D [str =
characterAtIndex: g-&gt;char_index];</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp;if =
(ch =3D=3D 0x20)</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;num_spaces++;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span>}</div><div>&nbsp; &nbsp; &nbsp; =
if (!num_spaces)</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	=
</span>continue;</div><div><br></div><div>&nbsp; &nbsp; &nbsp; =
extra_space =3D lf-&gt;rect.size.width - =
lf-&gt;last_used;</div><div>&nbsp; &nbsp; &nbsp; extra_space /=3D =
num_spaces;</div><div>&nbsp; &nbsp; &nbsp; delta =3D 0;</div><div>&nbsp; =
&nbsp; &nbsp; for (i =3D start, g =3D cache + i; i &lt; =
lf-&gt;lastGlyphIndex; i++, g++)</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span>{</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> =
&nbsp;g-&gt;pos.x +=3D delta;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp;if (!g-&gt;dont_show =
&amp;&amp; [str characterAtIndex: g-&gt;char_index] =3D=3D =
0x20)</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">	=
</span> &nbsp; &nbsp;{</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp; &nbsp;if (i &lt; =
lf-&gt;lastGlyphIndex)</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span>g[1].nominal =3D NO; =
&nbsp;// Next glyph has non-standard position</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp; &nbsp;delta +=3D extra_space;</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;}</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span>}</div><div>&nbsp; &nbsp; &nbsp; =
start =3D lf-&gt;lastGlyphIndex;</div><div>&nbsp; &nbsp; &nbsp; =
lf-&gt;last_used =3D lf-&gt;rect.size.width;</div><div>&nbsp; &nbsp; =
}</div><div>}</div><div><br></div><div>/*</div><div>&nbsp;* RIGHT =
ALIGNMENT</div><div>&nbsp;* Shifts all glyphs right by the difference =
between fragment width and used =
width.</div><div>&nbsp;*/</div><div>-(void) rightAlignLine: (line_frag_t =
*)lf : (int)num_line_frags</div><div>{</div><div>&nbsp; unsigned int =
i;</div><div>&nbsp; CGFloat delta;</div><div>&nbsp; glyph_cache_t =
*g;</div><div><br></div><div>&nbsp; if (lf-&gt;rect.size.width &gt;=3D =
LARGE_SIZE)</div><div>&nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; =
return;</div><div>&nbsp; &nbsp; }</div><div><br></div><div>&nbsp; for (i =
=3D 0, g =3D cache; num_line_frags; num_line_frags--, =
lf++)</div><div>&nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; delta =3D =
lf-&gt;rect.size.width - lf-&gt;last_used;</div><div>&nbsp; &nbsp; =
&nbsp; for (; i &lt; lf-&gt;lastGlyphIndex; i++, g++)</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	=
</span>g-&gt;pos.x +=3D delta;</div><div>&nbsp; &nbsp; &nbsp; =
lf-&gt;last_used +=3D delta;</div><div>&nbsp; &nbsp; =
}</div><div>}</div><div><br></div><div>/*</div><div>&nbsp;* CENTER =
ALIGNMENT</div><div>&nbsp;* Shifts all glyphs right by half the =
remaining space.</div><div>&nbsp;*/</div><div>-(void) centerAlignLine: =
(line_frag_t *)lf : (int)num_line_frags</div><div>{</div><div>&nbsp; =
unsigned int i;</div><div>&nbsp; CGFloat delta;</div><div>&nbsp; =
glyph_cache_t *g;</div><div><br></div><div>&nbsp; if =
(lf-&gt;rect.size.width &gt;=3D LARGE_SIZE)</div><div>&nbsp; &nbsp; =
{</div><div>&nbsp; &nbsp; &nbsp; return;</div><div>&nbsp; &nbsp; =
}</div><div><br></div><div>&nbsp; for (i =3D 0, g =3D cache; =
num_line_frags; num_line_frags--, lf++)</div><div>&nbsp; &nbsp; =
{</div><div>&nbsp; &nbsp; &nbsp; delta =3D (lf-&gt;rect.size.width - =
lf-&gt;last_used) / 2.0;</div><div>&nbsp; &nbsp; &nbsp; for (; i &lt; =
lf-&gt;lastGlyphIndex; i++, g++)</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span>g-&gt;pos.x +=3D =
delta;</div><div>&nbsp; &nbsp; &nbsp; lf-&gt;last_used +=3D =
delta;</div><div>&nbsp; &nbsp; =
}</div><div>}</div><div><br></div><div><br></div><div>/*</div><div>&nbsp;*=
 SOFT INVALIDATION OPTIMIZATION</div><div>&nbsp;* Attempts to reuse =
layout information from previous layout passes that</div><div>&nbsp;* =
were "soft invalidated" (marked as potentially changed but not =
definitely wrong).</div><div>&nbsp;*</div><div>&nbsp;* This handles the =
common case of simple text edits where line fragments</div><div>&nbsp;* =
just need to be shifted vertically without changing their horizontal =
layout.</div><div>&nbsp;*</div><div>&nbsp;* Returns YES if =
soft-invalidated layout was successfully =
reused.</div><div>&nbsp;*/</div><div>-(BOOL) =
_reuseSoftInvalidatedLayout</div><div>{</div><div>&nbsp; NSRect r0, =
r;</div><div>&nbsp; NSSize shift;</div><div>&nbsp; int =
i;</div><div>&nbsp; unsigned int g, g2, first;</div><div>&nbsp; CGFloat =
container_height;</div><div>&nbsp;&nbsp;</div><div>&nbsp; /* Get first =
soft-invalidated rect starting at current glyph */</div><div>&nbsp; r0 =3D=
 [curLayoutManager _softInvalidateLineFragRect: 0</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">				=
	</span> &nbsp;firstGlyph: &amp;first</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">				=
	</span> &nbsp; nextGlyph: &amp;g</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">				=
</span> &nbsp; &nbsp; inTextContainer: =
curTextContainer];</div><div><br></div><div>&nbsp; container_height =3D =
[curTextContainer containerSize].height;</div><div>&nbsp; if =
(!(curPoint.y + r0.size.height &lt;=3D =
container_height))</div><div>&nbsp; &nbsp; return NO; &nbsp;// Won't fit =
at current Y position</div><div><br></div><div>&nbsp; =
/*</div><div>&nbsp; &nbsp;* We can shift the rects vertically to fit. =
Collect all consecutive</div><div>&nbsp; &nbsp;* soft-invalidated line =
fragments and apply the same shift.</div><div>&nbsp; =
&nbsp;*/</div><div>&nbsp; shift.width =3D 0;</div><div>&nbsp; =
shift.height =3D curPoint.y - r0.origin.y;</div><div>&nbsp; i =3D =
1;</div><div>&nbsp; curPoint.y =3D NSMaxY(r0) + =
shift.height;</div><div>&nbsp;&nbsp;</div><div>&nbsp; for (; 1; =
i++)</div><div>&nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; r =3D =
[curLayoutManager _softInvalidateLineFragRect: i</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">				=
	</span> &nbsp; &nbsp; firstGlyph: &amp;first</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">				=
	</span> &nbsp; &nbsp; &nbsp;nextGlyph: &amp;g2</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">				=
	</span>inTextContainer: =
curTextContainer];</div><div><br></div><div>&nbsp; &nbsp; &nbsp; /* Gap =
in soft-invalidated info - must fill in before continuing =
*/</div><div>&nbsp; &nbsp; &nbsp; if (first !=3D g)</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	=
</span>{</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp;break;</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	=
</span>}</div><div><br></div><div>&nbsp; &nbsp; &nbsp; if =
(NSIsEmptyRect(r) || NSMaxY(r) + shift.height &gt; =
container_height)</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	=
</span>break;</div><div><br></div><div>&nbsp; &nbsp; &nbsp; g =3D =
g2;</div><div>&nbsp; &nbsp; &nbsp; curPoint.y =3D NSMaxY(r) + =
shift.height;</div><div>&nbsp; &nbsp; }</div><div><br></div><div>&nbsp; =
/* Commit the reused layout to the layout manager */</div><div>&nbsp; =
[curLayoutManager _softInvalidateUseLineFrags: i</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">				=
</span> &nbsp; &nbsp; &nbsp;withShift: shift</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">				=
</span>inTextContainer: =
curTextContainer];</div><div><br></div><div>&nbsp; curGlyphIndex =3D =
g;</div><div>&nbsp; return =
YES;</div><div>}</div><div><br></div><div><br></div><div>/*</div><div>&nbs=
p;* Calculates the proposed rectangle for a new line =
fragment.</div><div>&nbsp;*&nbsp;</div><div>&nbsp;* newParagraph: YES =
for first line of paragraph (uses =
firstLineHeadIndent),</div><div>&nbsp;* &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; NO for subsequent lines (uses =
headIndent)</div><div>&nbsp;* line_height: &nbsp;The height to request =
from the text container</div><div>&nbsp;*</div><div>&nbsp;* Returns: =
Proposed rectangle in container =
coordinates</div><div>&nbsp;*/</div><div>- (NSRect)_getProposedRectFor: =
(BOOL)newParagraph</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp;withLineHeight: (CGFloat) =
line_height&nbsp;</div><div>{</div><div>&nbsp; CGFloat =
hindent;</div><div>&nbsp; CGFloat tindent =3D [curParagraphStyle =
tailIndent];</div><div><br></div><div>&nbsp; if =
(newParagraph)</div><div>&nbsp; &nbsp; hindent =3D [curParagraphStyle =
firstLineHeadIndent];</div><div>&nbsp; else</div><div>&nbsp; &nbsp; =
hindent =3D [curParagraphStyle =
headIndent];</div><div><br></div><div>&nbsp; /* Negative tail indent is =
treated as inset from right edge */</div><div>&nbsp; if (tindent &lt;=3D =
0.0)</div><div>&nbsp; &nbsp; {&nbsp;</div><div>&nbsp; &nbsp; &nbsp; =
NSSize size;</div><div><br></div><div>&nbsp; &nbsp; &nbsp; size =3D =
[curTextContainer containerSize];</div><div>&nbsp; &nbsp; &nbsp; tindent =
=3D size.width + tindent;</div><div>&nbsp; &nbsp; =
}</div><div><br></div><div>&nbsp; return =
NSMakeRect(hindent,</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; curPoint.y,</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tindent - =
hindent,</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; line_height + [curParagraphStyle =
lineSpacing]);</div><div>}</div><div><br></div><div>/*</div><div>&nbsp;* =
Creates the "extra line fragment" used when text ends with a =
newline.</div><div>&nbsp;* This provides a place for the insertion point =
(caret) after the last newline.</div><div>&nbsp;* The fragment has full =
line height but minimal width (1 unit).</div><div>&nbsp;*/</div><div>- =
(void) _addExtraLineFragment</div><div>{</div><div>&nbsp; NSRect r, r2, =
remain;</div><div>&nbsp; CGFloat =
line_height;</div><div><br></div><div>&nbsp; /*</div><div>&nbsp; &nbsp;* =
We need the attributes from the last character to match the =
style.</div><div>&nbsp; &nbsp;* _cacheMoveTo: ensures curParagraphStyle =
and curFont are set.</div><div>&nbsp; &nbsp;*/</div><div>&nbsp; if =
(curGlyphIndex)</div><div>&nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; =
[self _cacheMoveTo: curGlyphIndex - 1];</div><div>&nbsp; &nbsp; =
}</div><div>&nbsp; else</div><div>&nbsp; &nbsp; {</div><div>&nbsp; =
&nbsp; &nbsp; /* No glyphs yet - use typing attributes (default style =
for new text) */</div><div>&nbsp; &nbsp; &nbsp; NSDictionary =
*typingAttributes =3D [curLayoutManager =
typingAttributes];</div><div>&nbsp; &nbsp; &nbsp; curParagraphStyle =3D =
[typingAttributes</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; objectForKey: =
NSParagraphStyleAttributeName];</div><div>&nbsp; &nbsp; &nbsp; if =
(curParagraphStyle =3D=3D nil)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; =
{</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; curParagraphStyle =3D =
[NSParagraphStyle defaultParagraphStyle];</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; }</div><div>&nbsp; &nbsp; &nbsp; curFont =3D [typingAttributes =
objectForKey: NSFontAttributeName];</div><div>&nbsp; &nbsp; =
}</div><div><br></div><div>&nbsp; /* Determine line height from font or =
use default */</div><div>&nbsp; if (curFont)</div><div>&nbsp; &nbsp; =
{</div><div>&nbsp; &nbsp; &nbsp; line_height =3D [curFont =
defaultLineHeightForFont];</div><div>&nbsp; &nbsp; }</div><div>&nbsp; =
else</div><div>&nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; =
line_height =3D 15.0;</div><div>&nbsp; &nbsp; =
}</div><div><br></div><div>&nbsp; r =3D [self _getProposedRectFor: =
YES</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp;withLineHeight: line_height];</div><div>&nbsp; r =3D =
[curTextContainer lineFragmentRectForProposedRect: r</div><div>&nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp;sweepDirection: NSLineSweepRight</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; movementDirection: =
NSLineMovesDown</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; remainingRect: =
&amp;remain];</div><div>&nbsp;&nbsp;</div><div>&nbsp; if =
(!NSIsEmptyRect(r))</div><div>&nbsp; &nbsp; {</div><div>&nbsp; &nbsp; =
&nbsp; r2 =3D r;</div><div>&nbsp; &nbsp; &nbsp; r2.size.width =3D 1; =
&nbsp;// Minimal width for caret positioning</div><div>&nbsp; &nbsp; =
&nbsp; [curLayoutManager setExtraLineFragmentRect: r</div><div>&nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; usedRect: =
r2</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp;textContainer: curTextContainer];</div><div>&nbsp; &nbsp; =
}</div><div>}</div><div><br></div><div>/*</div><div>&nbsp;* Helper for =
line height calculations.</div><div>&nbsp;* Updates *lineHeight to =
newHeight if newHeight is larger (and within max).</div><div>&nbsp;* =
Returns YES if the line height was =
updated.</div><div>&nbsp;*/</div><div>static inline BOOL =
wantNewLineHeight(CGFloat h, CGFloat *lineHeight, CGFloat =
maxLineHeight)</div><div>{</div><div>&nbsp; CGFloat newHeight =3D =
h;</div><div><br></div><div>&nbsp; if (maxLineHeight &gt; 0 &amp;&amp; =
newHeight &gt; maxLineHeight)</div><div>&nbsp; &nbsp; {</div><div>&nbsp; =
&nbsp; &nbsp; newHeight =3D maxLineHeight;</div><div>&nbsp; &nbsp; =
}</div><div><br></div><div>&nbsp; if (newHeight &gt; =
*lineHeight)</div><div>&nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; =
*lineHeight =3D newHeight;</div><div>&nbsp; &nbsp; &nbsp; return =
YES;</div><div>&nbsp; &nbsp; }</div><div>&nbsp; return =
NO;</div><div>}</div><div><br></div><div>/*</div><div>&nbsp;* CORE =
LAYOUT METHOD</div><div>&nbsp;* Lays out a single line of text, handling =
all complexity of glyph positioning,</div><div>&nbsp;* line breaking, =
attachments, and alignment.</div><div>&nbsp;*</div><div>&nbsp;* =
newParagraph: YES if this is the first line of a =
paragraph</div><div>&nbsp;*</div><div>&nbsp;* Return =
values:</div><div>&nbsp;* &nbsp; 0 - Line completed normally, next glyph =
continues this paragraph</div><div>&nbsp;* &nbsp; 1 - No room in text =
container (line fragments exhausted)</div><div>&nbsp;* &nbsp; 2 - All =
glyphs laid out (end of text)</div><div>&nbsp;* &nbsp; 3 - Line ended =
with newline, next glyph starts new paragraph</div><div>&nbsp;* &nbsp; 4 =
- Ambiguous state (must test before next call - from soft =
invalidation)</div><div>&nbsp;*/</div><div>-(int) =
layoutLineNewParagraph: (BOOL)newParagraph</div><div>{</div><div>&nbsp; =
NSRect rect;</div><div><br></div><div>&nbsp; /* LINE METRICS VARIABLES =
*/</div><div>&nbsp; CGFloat line_height; &nbsp; &nbsp; // Current line =
height (ascender + descender)</div><div>&nbsp; CGFloat max_line_height; =
// Maximum allowed (from paragraph style, 0 =3D =
unlimited)</div><div>&nbsp; CGFloat baseline; &nbsp; &nbsp; &nbsp; =
&nbsp;// Distance from top of line to baseline</div><div>&nbsp; CGFloat =
ascender; &nbsp; &nbsp; &nbsp; &nbsp;// Space needed above baseline (max =
of all glyphs)</div><div>&nbsp; CGFloat descender; &nbsp; &nbsp; &nbsp; =
// Space needed below baseline (max of all =
glyphs)</div><div><br></div><div>&nbsp; /*</div><div>&nbsp; &nbsp;* SOFT =
INVALIDATION CHECK</div><div>&nbsp; &nbsp;* Try to reuse previous layout =
if available and appropriate.</div><div>&nbsp; &nbsp;*/</div><div>&nbsp; =
if ([curTextContainer isSimpleRectangularTextContainer] =
&amp;&amp;</div><div>&nbsp; &nbsp; &nbsp; [curLayoutManager =
_softInvalidateFirstGlyphInTextContainer: curTextContainer] =3D=3D =
curGlyphIndex)</div><div>&nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; =
if ([self _reuseSoftInvalidatedLayout])</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; return 4;</div><div>&nbsp; &nbsp; =
}</div><div><br></div><div>&nbsp; /* Initialize cache at current glyph =
position */</div><div>&nbsp; [self _cacheMoveTo: =
curGlyphIndex];</div><div>&nbsp; if (!cache_length)</div><div>&nbsp; =
&nbsp; [self _cacheGlyphs: CACHE_INITIAL];</div><div>&nbsp; if =
(!cache_length &amp;&amp; at_end)</div><div>&nbsp; &nbsp; =
{</div><div>&nbsp; &nbsp; &nbsp; /* No more glyphs to lay out =
*/</div><div>&nbsp; &nbsp; &nbsp; if (newParagraph)</div><div>&nbsp; =
&nbsp; &nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
[self _addExtraLineFragment]; &nbsp;// Text ended with =
newline</div><div>&nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; =
&nbsp; return 2;</div><div>&nbsp; &nbsp; =
}</div><div><br></div><div>&nbsp; /* INITIALIZE LINE METRICS from first =
glyph's font */</div><div>&nbsp; {</div><div>&nbsp; &nbsp; CGFloat min =3D=
 [curParagraphStyle minimumLineHeight];</div><div>&nbsp; &nbsp; =
max_line_height =3D [curParagraphStyle =
maximumLineHeight];</div><div><br></div><div>&nbsp; &nbsp; /* Sanity: =
max must be &gt;=3D min if both are specified */</div><div>&nbsp; &nbsp; =
if (max_line_height &gt; 0 &amp;&amp; max_line_height &lt; =
min)</div><div>&nbsp; &nbsp; &nbsp; max_line_height =3D =
min;</div><div><br></div><div>&nbsp; &nbsp; line_height =3D =
[cache-&gt;font defaultLineHeightForFont];</div><div>&nbsp; &nbsp; =
ascender =3D [cache-&gt;font ascender];</div><div>&nbsp; &nbsp; =
descender =3D -[cache-&gt;font =
descender];</div><div><br></div><div>&nbsp; &nbsp; if (line_height &lt; =
min)</div><div>&nbsp; &nbsp; &nbsp; line_height =3D =
min;</div><div><br></div><div>&nbsp; &nbsp; if (max_line_height &gt; 0 =
&amp;&amp; line_height &gt; max_line_height)</div><div>&nbsp; &nbsp; =
&nbsp; line_height =3D max_line_height;</div><div>&nbsp; =
}</div><div><br></div><div>&nbsp; /*</div><div>&nbsp; &nbsp;* LINE =
FRAGMENT ACQUISITION</div><div>&nbsp; &nbsp;* Get rectangles from text =
container until we have room for at least</div><div>&nbsp; &nbsp;* one =
glyph. If line height increases due to large glyphs, we =
restart</div><div>&nbsp; &nbsp;* this process since the rectangles might =
change.</div><div>&nbsp; &nbsp;*/</div><div>restart: =
;</div><div><br></div><div>&nbsp; do</div><div>&nbsp; &nbsp; =
{</div><div>&nbsp; &nbsp; &nbsp; NSRect =
remain;</div><div><br></div><div>&nbsp; &nbsp; &nbsp; remain =3D [self =
_getProposedRectFor: newParagraph</div><div>&nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
withLineHeight: line_height];</div><div><br></div><div>&nbsp; &nbsp; =
&nbsp; /*</div><div>&nbsp; &nbsp; &nbsp; &nbsp;* Build list of line =
fragment rects for this line.</div><div>&nbsp; &nbsp; &nbsp; &nbsp;* A =
line may have multiple fragments when flowing around =
shapes.</div><div>&nbsp; &nbsp; &nbsp; &nbsp;* TODO: This builds all =
rects in advance which might be inefficient</div><div>&nbsp; &nbsp; =
&nbsp; &nbsp;* for containers with many exclusions (e.g., narrow =
columns).</div><div>&nbsp; &nbsp; &nbsp; &nbsp;*/</div><div>&nbsp; =
&nbsp; &nbsp; line_frags_num =3D 0;</div><div>&nbsp; &nbsp; &nbsp; rect =
=3D [curTextContainer lineFragmentRectForProposedRect: =
remain</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sweepDirection: =
NSLineSweepRight</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;movementDirection: =
NSLineMovesDown</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp;remainingRect: &amp;remain];</div><div>&nbsp; &nbsp; &nbsp; while =
(!NSIsEmptyRect(rect))</div><div>&nbsp; &nbsp; &nbsp; &nbsp; =
{</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
line_frags_num++;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if =
(line_frags_num &gt; line_frags_size)</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; line_frags_size +=3D 2;</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; line_frags =3D realloc(line_frags, =
sizeof(line_frag_t) * line_frags_size);</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
line_frags[line_frags_num - 1].rect =3D =
rect;</div><div><br></div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rect =3D=
 [curTextContainer lineFragmentRectForProposedRect: =
remain</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sweepDirection: =
NSLineSweepRight</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp;movementDirection: NSLineDoesntMove</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp;remainingRect: &amp;remain];</div><div>&nbsp; =
&nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; &nbsp; if (line_frags_num =
=3D=3D 0)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; {</div><div>&nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; /* No fragments available - container might =
be too small */</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if =
(curPoint.y =3D=3D 0.0 &amp;&amp;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; line_height &gt; [curTextContainer =
containerSize].height &amp;&amp;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; [curTextContainer containerSize].height &gt; =
0.0)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
{</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /* =
Emergency: shrink line height to fit at least one line =
*/</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
line_height =3D [curTextContainer =
containerSize].height;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; max_line_height =3D line_height;</div><div>&nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue;</div><div>&nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; return 1; &nbsp;// No room in container</div><div>&nbsp; &nbsp; =
&nbsp; &nbsp; }</div><div>&nbsp; &nbsp; }</div><div>&nbsp; while =
(line_frags_num =3D=3D 0);</div><div><br></div><div>&nbsp; =
/*</div><div>&nbsp; &nbsp;* MAIN GLYPH LAYOUT LOOP</div><div>&nbsp; =
&nbsp;* Positions each glyph in the line fragments, =
handling:</div><div>&nbsp; &nbsp;* &nbsp; - Font changes and metric =
updates</div><div>&nbsp; &nbsp;* &nbsp; - Kerning and baseline =
adjustments</div><div>&nbsp; &nbsp;* &nbsp; - Superscript/subscript =
positioning</div><div>&nbsp; &nbsp;* &nbsp; - Tab stops</div><div>&nbsp; =
&nbsp;* &nbsp; - Text attachments (images, etc.)</div><div>&nbsp; =
&nbsp;* &nbsp; - Line breaking when fragments fill up</div><div>&nbsp; =
&nbsp;*/</div><div>&nbsp; {</div><div>&nbsp; &nbsp; unsigned int i =3D =
0;</div><div>&nbsp; &nbsp; glyph_cache_t =
*g;</div><div><br></div><div>&nbsp; &nbsp; NSPoint p; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// Current glyph position (relative to =
line fragment)</div><div>&nbsp; &nbsp;&nbsp;</div><div>&nbsp; &nbsp; =
NSFont *f =3D cache-&gt;font;</div><div><br></div><div>&nbsp; &nbsp; =
CGFloat f_ascender =3D [f ascender];</div><div>&nbsp; &nbsp; CGFloat =
f_descender =3D -[f descender];</div><div><br></div><div>&nbsp; &nbsp; =
NSGlyph last_glyph =3D NSNullGlyph; &nbsp;// For kerning =
calculations</div><div>&nbsp; &nbsp; NSPoint =
last_p;</div><div><br></div><div>&nbsp; &nbsp; unsigned int =
firstGlyphIndex; &nbsp; &nbsp; &nbsp;// First glyph in current line =
fragment</div><div>&nbsp; &nbsp; line_frag_t *lf =3D line_frags; &nbsp; =
&nbsp; &nbsp;// Current line fragment</div><div>&nbsp; &nbsp; int lfi =3D =
0; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; // Line fragment index</div><div><br></div><div>&nbsp; &nbsp; =
BOOL prev_had_non_nominal_width; &nbsp; // Track if previous glyph had =
custom spacing</div><div><br></div><div><br></div><div>&nbsp; &nbsp; =
last_p =3D p =3D NSMakePoint(0, 0);</div><div><br></div><div>&nbsp; =
&nbsp; g =3D cache;</div><div>&nbsp; &nbsp; firstGlyphIndex =3D =
0;</div><div>&nbsp; &nbsp; prev_had_non_nominal_width =3D =
NO;</div><div><br></div><div>&nbsp; &nbsp; while (1)</div><div>&nbsp; =
&nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; BOOL =
doesGlyphFitInLine =3D YES;</div><div><br></div><div>&nbsp; &nbsp; =
&nbsp; &nbsp; /* Ensure we have cached glyphs to process =
*/</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">	=
</span>if (i &gt;=3D cache_length)</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> =
&nbsp;{</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp;if =
(at_end)</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp; =
&nbsp;{</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span>newParagraph =3D =
NO;</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">		=
</span>break; &nbsp;// End of text</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp; &nbsp;}</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp;[self _cacheGlyphs: =
cache_length + CACHE_STEP];</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp;if (i &gt;=3D =
cache_length)</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp; =
&nbsp;{</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span>newParagraph =3D =
NO;</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">		=
</span>break; &nbsp;// No more glyphs available</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp; &nbsp;}</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp;g =3D cache + =
i;</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">	=
</span> &nbsp;}</div><div><br></div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span>/*</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> * FONT =
CHANGE HANDLING</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> * Update ascender/descender =
tracking when font changes.</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> * Reset last_glyph to disable =
kerning across font boundaries.</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> */</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span>if =
(g-&gt;font !=3D f)</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp;{</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;f =3D g-&gt;font;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp;f_ascender =3D [f =
ascender];</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp;f_descender =3D -[f =
descender];</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp;last_glyph =3D =
NSNullGlyph;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> =
&nbsp;}</div><div><br></div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span>/* Apply explicit kerning if =
specified in attributes */</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span>g-&gt;nominal =3D =
!prev_had_non_nominal_width;</div><div><br></div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span>if =
(g-&gt;attributes.explicit_kern &amp;&amp;</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;g-&gt;attributes.kern !=3D 0)</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> =
&nbsp;{</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp;p.x +=3D =
g-&gt;attributes.kern;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp;g-&gt;nominal =3D =
NO;</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">	=
</span> &nbsp;}</div><div><br></div><div>&nbsp; &nbsp; &nbsp; &nbsp; /* =
Check if glyph fits in current line fragment width */</div><div>&nbsp; =
&nbsp; &nbsp; &nbsp; doesGlyphFitInLine =3D !((i &gt; firstGlyphIndex) =
&amp;&amp; (p.x + g-&gt;size.width &gt; =
lf-&gt;rect.size.width));</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp;&nbsp;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; if =
(doesGlyphFitInLine)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
{</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /* Calculate =
vertical position with baseline adjustments */</div><div>&nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; CGFloat y =3D =
0;</div><div><br></div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /* =
Apply superscript offset (negative =3D up in flipped coords) =
*/</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if =
(g-&gt;attributes.superscript)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; y -=3D g-&gt;attributes.superscript * [f =
xHeight];</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
}</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /* Apply explicit =
baseline offset */</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
if (g-&gt;attributes.baseline_offset)</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; y +=3D =
g-&gt;attributes.baseline_offset;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; }</div><div><br></div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; if (y !=3D p.y)</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; p.y =3D y;</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; g-&gt;nominal =3D NO; &nbsp;// =
Non-standard vertical position</div><div>&nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp;&nbsp;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /* =
Update line metrics based on this glyph */</div><div>&nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; if (f_ascender &gt; =
ascender)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
ascender =3D f_ascender;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; if (f_descender &gt; descender)</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; descender =3D =
f_descender;</div><div><br></div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; /* Adjust for superscript/subscript height requirements =
*/</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (y &lt; 0 =
&amp;&amp; f_ascender - y &gt; ascender)</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; ascender =3D f_ascender - =
y;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (y &gt; 0 =
&amp;&amp; f_descender + y &gt; descender)</div><div>&nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; descender =3D f_descender + =
y;</div><div><br></div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /* =
If metrics changed, check if we need to restart with new line height =
*/</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if =
(wantNewLineHeight(ascender + descender, &amp;line_height, =
max_line_height))</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; goto restart;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
}</div><div><br></div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span>/* CONTROL GLYPH HANDLING =
(newlines, tabs, etc.) */</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span>if (g-&gt;g =3D=3D =
NSControlGlyph)</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp;{</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;unichar ch =3D [[curTextStorage string] characterAtIndex: =
g-&gt;char_index];</div><div><br></div><div><span class=3D"Apple-tab-span"=
 style=3D"white-space:pre">	</span> &nbsp; &nbsp;g-&gt;pos =3D =
p;</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">	=
</span> &nbsp; &nbsp;g-&gt;size.width =3D 0;</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;g-&gt;dont_show =3D YES; &nbsp;// Control glyphs don't =
render</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">=
	</span> &nbsp; &nbsp;g-&gt;nominal =3D =
!prev_had_non_nominal_width;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;i++;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;g++;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp;last_glyph =3D =
NSNullGlyph;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;prev_had_non_nominal_width =3D NO;</div><div><br></div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;/* NEWLINE: End this line, start new paragraph */</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;if (ch =3D=3D 0xa) // new line</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp; &nbsp;{</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span>newParagraph =3D =
YES;</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">	=
	</span>break;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp; =
&nbsp;}</div><div><br></div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp;/* TAB: Advance to =
next tab stop */</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp;if (ch =3D=3D 0x9) =
// horiz. tab</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp; =
&nbsp;{</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span>NSArray *tabs =3D =
[curParagraphStyle tabStops];</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span>NSTextTab *tab =3D =
nil;</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">	=
	</span>CGFloat defaultInterval =3D [curParagraphStyle =
defaultTabInterval];</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span>if (defaultInterval =3D=3D =
0.0)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; defaultInterval =3D 100.0; &nbsp;// Reasonable =
default</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; }</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span>unsigned =
tabIndex;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; unsigned tabCount =3D [tabs count];</div><div>&nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</div><div>&nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /* Find next tab stop after =
current position */</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span>for (tabIndex =3D 0; =
tabIndex &lt; tabCount; tabIndex++)</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">		</span> =
&nbsp;{</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span> &nbsp; &nbsp;tab =3D =
[tabs objectAtIndex: tabIndex];</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span> &nbsp; &nbsp;if ([tab =
location] &gt; p.x + lf-&gt;rect.origin.x)</div><div>&nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
{</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; break;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">		</span> =
&nbsp;}</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span>if (tabIndex =3D=3D =
tabCount)</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span> &nbsp;{</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">		</span> =
&nbsp; &nbsp;/* Past last explicit tab stop - use default interval =
*/</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">		=
</span> &nbsp; &nbsp;p.x =3D (floor(p.x / defaultInterval) + 1.0) * =
defaultInterval;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span> &nbsp;}</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">		=
</span>else</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span> &nbsp;{</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">		</span> =
&nbsp; &nbsp;p.x =3D [tab location] - =
lf-&gt;rect.origin.x;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span> &nbsp;}</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">		=
</span>prev_had_non_nominal_width =3D YES;</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">		=
</span>continue;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp; =
&nbsp;}</div><div><br></div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; /* Unknown control character - log and ignore */</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;NSDebugLLog(@"GSHorizontalTypesetter",</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp; &nbsp;@"ignoring unknown control character %04x\n", =
ch);</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">	=
</span> &nbsp; &nbsp;continue;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> =
&nbsp;}</div><div><br></div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span>/* TEXT ATTACHMENT HANDLING =
(images, files, etc.) */</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span>if (g-&gt;g =3D=3D =
GSAttachmentGlyph)</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp;{</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;NSTextAttachment *attach;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;NSTextAttachmentCell *cell;</div><div><span class=3D"Apple-tab-span"=
 style=3D"white-space:pre">	</span> &nbsp; &nbsp;NSRect =
r;</div><div><br></div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp;attach =3D =
[curTextStorage attribute: NSAttachmentAttributeName</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp; &nbsp;atIndex: g-&gt;char_index</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp; &nbsp;effectiveRange: NULL];</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;cell =3D (NSTextAttachmentCell*)[attach =
attachmentCell];</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp;if =
(!cell)</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp; =
&nbsp;{</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; /* No cell for attachment - treat as zero-width invisible glyph =
*/</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">		=
</span>g-&gt;pos =3D p;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span>g-&gt;size =3D =
NSMakeSize(0, 0);</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span>g-&gt;dont_show =3D =
YES;</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">	=
	</span>g-&gt;nominal =3D YES;</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">		=
</span>i++;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span>g++;</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">		=
</span>last_glyph =3D NSNullGlyph;</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">		=
</span>continue;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp; =
&nbsp;}</div><div><br></div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; /* Calculate baseline position for attachment alignment =
*/</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; baseline =3D =
line_height - descender;</div><div><br></div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;/* Ask attachment cell for its desired frame */</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;r =3D [cell cellFrameForTextContainer: =
curTextContainer</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span> =
&nbsp;proposedLineFragment: lf-&gt;rect</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">		</span> =
&nbsp;glyphPosition: NSMakePoint(p.x,</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">				=
	</span> &nbsp; &nbsp; lf-&gt;rect.size.height - =
baseline)</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span> &nbsp;characterIndex: =
g-&gt;char_index];</div><div><br></div><div>&nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; /* Check if attachment fits in current fragment =
*/</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
doesGlyphFitInLine =3D !((i &gt; firstGlyphIndex) &amp;&amp; (p.x + =
NSMaxX(r) &gt; lf-&gt;rect.size.width));</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; if (doesGlyphFitInLine)</div><div>&nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /* Update line metrics for attachment =
size */</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; if (-NSMinY(r) &gt; descender)</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; descender =3D =
-NSMinY(r);</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; if (NSMaxY(r) &gt; ascender)</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ascender =3D =
NSMaxY(r);</div><div><br></div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; /* Check if attachment forces line height increase =
*/</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if =
(wantNewLineHeight(ascender + descender, &amp;line_height, =
max_line_height))</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; goto restart;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; }</div><div><br></div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; /* Position attachment (note: r is upside-down =
relative to our coords) */</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; g-&gt;size =3D r.size;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; g-&gt;pos.x =3D p.x + r.origin.x;</div><div>&nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; g-&gt;pos.y =3D p.y - =
r.origin.y;</div><div><br></div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; p.x =3D g-&gt;pos.x + g-&gt;size.width;</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;g-&gt;nominal =3D NO; &nbsp;// Attachments always have custom =
positioning</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp;}</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	=
</span>else</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp;{</div><div>&nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; /* STANDARD GLYPH: Just use cached =
advancement */</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /* =
TODO: Kerning is commented out as a bottleneck - needs optimization =
*/</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">	=
</span> &nbsp; &nbsp;last_p =3D g-&gt;pos =3D p;</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;p.x +=3D g-&gt;size.width;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> =
&nbsp;}</div><div><br></div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span>/* LINE BREAKING: Glyph didn't =
fit in current fragment */</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span>if =
(!doesGlyphFitInLine)</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp;{</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;switch ([curParagraphStyle lineBreakMode])</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp; &nbsp;{</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp; =
&nbsp;default:</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp; &nbsp;case =
NSLineBreakByCharWrapping:</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; /* Break immediately before current glyph =
*/</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">		=
</span>lf-&gt;lastGlyphIndex =3D i;</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">		=
</span>break;</div><div><br></div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp; &nbsp;case =
NSLineBreakByWordWrapping:</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; /* Search backward for word boundary =
*/</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">		=
</span>lf-&gt;lastGlyphIndex =3D [self breakLineByWordWrappingBefore: =
cache_base + i] - cache_base;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span>if (lf-&gt;lastGlyphIndex =
&lt;=3D firstGlyphIndex)</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span> &nbsp;{</div><div>&nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // No =
word boundary found - fall back to character wrapping</div><div>&nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
lf-&gt;lastGlyphIndex =3D i;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">		=
</span>break;</div><div><br></div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp; &nbsp;case =
NSLineBreakByTruncatingHead:</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp; &nbsp;case =
NSLineBreakByTruncatingMiddle:</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp; &nbsp;case =
NSLineBreakByTruncatingTail:</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp; &nbsp;case =
NSLineBreakByClipping:</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; /* CLIPPING/TRUNCATING: Hide overflowing glyphs =
*/</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">		=
</span>g-&gt;outside_line_frag =3D YES;</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">		=
</span>while (1)</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span> &nbsp;{</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">		</span> =
&nbsp; &nbsp;i++;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span> &nbsp; =
&nbsp;g++;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span> &nbsp; &nbsp;if (i &gt;=3D=
 cache_length)</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span> &nbsp; &nbsp; =
&nbsp;{</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">			</span>if =
(at_end)</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">			</span> =
&nbsp;{</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">			</span> &nbsp; =
&nbsp;newParagraph =3D NO;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">			</span> &nbsp; =
&nbsp;i--;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">			</span> &nbsp; =
&nbsp;break;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">			</span> =
&nbsp;}</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">			</span>[self =
_cacheGlyphs: cache_length + CACHE_STEP];</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">			=
</span>if (i &gt;=3D cache_length)</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">			=
</span> &nbsp;{</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">			</span> &nbsp; =
&nbsp;newParagraph =3D NO;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">			</span> &nbsp; =
&nbsp;i--;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">			</span> &nbsp; =
&nbsp;break;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">			</span> =
&nbsp;}</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">			</span>g =3D cache + =
i;</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">		=
</span> &nbsp; &nbsp; &nbsp;}</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span> &nbsp; =
&nbsp;g-&gt;dont_show =3D YES;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span> &nbsp; &nbsp;g-&gt;pos =3D=
 p;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; /* Stop at paragraph break */</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">		</span> =
&nbsp; &nbsp;if (g-&gt;g =3D=3D NSControlGlyph</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">			=
</span>&amp;&amp; [[curTextStorage string]</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">			=
</span> &nbsp; &nbsp; &nbsp; characterAtIndex: g-&gt;char_index] =3D=3D =
0xa)</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">	=
	</span> &nbsp; &nbsp; &nbsp;break;</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">		</span> =
&nbsp;}</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span>lf-&gt;lastGlyphIndex =3D =
i + 1;</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">=
		</span>break;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp; =
&nbsp;}</div><div><br></div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; /*</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* =
SAFETY: Ensure at least one glyph per fragment.</div><div>&nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* Prevents infinite loops when =
container is narrower than a single glyph.</div><div>&nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;if (lf-&gt;lastGlyphIndex &lt;=3D firstGlyphIndex)</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp; &nbsp;lf-&gt;lastGlyphIndex =3D i + =
1;</div><div><br></div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /* =
Reset position for next line fragment */</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;last_p =3D p =3D NSMakePoint(0, 0);</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;i =3D lf-&gt;lastGlyphIndex;</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;g =3D cache + i;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp;lf-&gt;last_used =3D =
g[-1].pos.x + g[-1].size.width;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp;last_glyph =3D =
NSNullGlyph;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;prev_had_non_nominal_width =3D NO;</div><div><br></div><div>&nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /* Move to next line fragment or end =
line if exhausted */</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;lf++;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;lfi++;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp;if (lfi =3D=3D =
line_frags_num)</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp; =
&nbsp;{</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span>newParagraph =3D =
NO;</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">		=
</span>break;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp; =
&nbsp;}</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp;firstGlyphIndex =3D =
i;</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">	=
</span> &nbsp;}</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span>else</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> =
&nbsp;{</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /* Glyph fit =
- advance to next */</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp;last_glyph =3D =
g-&gt;g;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp;if (last_glyph =3D=3D=
 GSAttachmentGlyph)</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp; =
&nbsp;{</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span>last_glyph =3D =
NSNullGlyph;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		=
</span>prev_had_non_nominal_width =3D YES;</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp; &nbsp;}</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;else</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp; =
&nbsp;{</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		=
</span>prev_had_non_nominal_width =3D NO;</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp; &nbsp;}</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;i++;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;g++;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp;}</div><div>&nbsp; &nbsp; =
&nbsp; }</div><div>&nbsp; &nbsp; /* END MAIN LAYOUT LOOP =
*/</div><div><br></div><div>&nbsp; &nbsp; /* ALIGNMENT PASS: Apply =
paragraph alignment to positioned glyphs */</div><div>&nbsp; &nbsp; if =
(lfi !=3D line_frags_num)</div><div>&nbsp; &nbsp; &nbsp; =
{</div><div>&nbsp; &nbsp; &nbsp; &nbsp; /* Line filled exactly - apply =
alignment */</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span>lf-&gt;lastGlyphIndex =3D =
i;</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">	=
</span>lf-&gt;last_used =3D p.x;</div><div><br></div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span>if =
([curParagraphStyle alignment] =3D=3D =
NSRightTextAlignment)</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp;[self rightAlignLine: =
line_frags : line_frags_num];</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span>else if ([curParagraphStyle =
alignment] =3D=3D NSCenterTextAlignment)</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> =
&nbsp;[self centerAlignLine: line_frags : =
line_frags_num];</div><div>&nbsp; &nbsp; &nbsp; }</div><div>&nbsp; =
&nbsp; else</div><div>&nbsp; &nbsp; &nbsp; {</div><div>&nbsp; &nbsp; =
&nbsp; &nbsp; /* Line broke early - check for justification or alignment =
*/</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">	=
</span>if ([curParagraphStyle lineBreakMode] =3D=3D =
NSLineBreakByWordWrapping &amp;&amp;</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;[curParagraphStyle alignment] =3D=3D =
NSJustifiedTextAlignment)</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp;[self fullJustifyLine: =
line_frags : line_frags_num];</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span>else if ([curParagraphStyle =
alignment] =3D=3D NSRightTextAlignment)</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> =
&nbsp;[self rightAlignLine: line_frags : =
line_frags_num];</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span>else if ([curParagraphStyle =
alignment] =3D=3D NSCenterTextAlignment)</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> =
&nbsp;[self centerAlignLine: line_frags : =
line_frags_num];</div><div><br></div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span>lfi--;</div><div>&nbsp; &nbsp; =
&nbsp; }</div><div><br></div><div>&nbsp; &nbsp; /* COMMIT LAYOUT TO =
LAYOUT MANAGER */</div><div>&nbsp; &nbsp; [curLayoutManager =
setTextContainer: curTextContainer</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">		</span> =
&nbsp; &nbsp; &nbsp;forGlyphRange: NSMakeRange(cache_base, =
i)];</div><div>&nbsp; &nbsp; curGlyphIndex =3D i + =
cache_base;</div><div>&nbsp; &nbsp;&nbsp;</div><div>&nbsp; &nbsp; =
{</div><div>&nbsp; &nbsp; &nbsp; line_frag_t *lf;</div><div>&nbsp; =
&nbsp; &nbsp; NSPoint p;</div><div>&nbsp; &nbsp; &nbsp; unsigned int =
lineFragCounter, lineFragCounter2;</div><div>&nbsp; &nbsp; &nbsp; =
glyph_cache_t *g;</div><div>&nbsp; &nbsp; &nbsp; NSRect =
used_rect;</div><div><br></div><div>&nbsp; &nbsp; &nbsp; /* Final =
baseline calculation */</div><div>&nbsp; &nbsp; &nbsp; baseline =3D =
line_height - descender;</div><div><br></div><div>&nbsp; &nbsp; &nbsp; =
/* Iterate through line fragments and register each with layout manager =
*/</div><div>&nbsp; &nbsp; &nbsp; for (lf =3D line_frags, =
lineFragCounter =3D 0, g =3D cache; lfi &gt;=3D 0; lfi--, =
lf++)</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">	=
</span>{</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /* Calculate used =
rect (actual ink bounds) vs fragment rect (available space) =
*/</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">	=
</span> &nbsp;used_rect.origin.x =3D g-&gt;pos.x + =
lf-&gt;rect.origin.x;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp;used_rect.size.width =3D =
lf-&gt;last_used - g-&gt;pos.x;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp;used_rect.origin.y =3D =
lf-&gt;rect.origin.y;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp;used_rect.size.height =3D =
lf-&gt;rect.size.height;</div><div><br></div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; /* Register the line fragment */</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> =
&nbsp;[curLayoutManager setLineFragmentRect: lf-&gt;rect</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">			=
</span> &nbsp; &nbsp;forGlyphRange: NSMakeRange(cache_base + =
lineFragCounter, lf-&gt;lastGlyphIndex - =
lineFragCounter)</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">			</span> &nbsp; =
&nbsp;usedRect: used_rect];</div><div>&nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp;&nbsp;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /* Register =
individual glyph positions */</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp;p =3D =
g-&gt;pos;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp;p.y +=3D baseline; =
&nbsp;// Convert from relative to absolute position</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> =
&nbsp;lineFragCounter2 =3D lineFragCounter;</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> =
&nbsp;while (lineFragCounter &lt; lf-&gt;lastGlyphIndex)</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;{</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /* =
Set flags for special glyph handling */</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp; &nbsp;if (g-&gt;outside_line_frag)</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">		=
</span>{</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span> &nbsp;[curLayoutManager =
setDrawsOutsideLineFragment: YES</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span> &nbsp; =
&nbsp;forGlyphAtIndex: cache_base + lineFragCounter];</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">		=
</span>}</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp; &nbsp;if =
(g-&gt;dont_show)</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span>{</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">		</span> =
&nbsp;[curLayoutManager setNotShownAttribute: YES</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">				=
	</span> forGlyphAtIndex: cache_base + =
lineFragCounter];</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span>}</div><div>&nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; /* Register glyph runs with non-nominal =
positioning */</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp; &nbsp;if =
(!g-&gt;nominal &amp;&amp; lineFragCounter !=3D =
lineFragCounter2)</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span>{</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">		</span> =
&nbsp;[curLayoutManager setLocation: p</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">				=
</span> &nbsp; &nbsp;forStartOfGlyphRange: NSMakeRange(cache_base + =
lineFragCounter2, lineFragCounter - lineFragCounter2)];</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">		</span> =
&nbsp;if (g[-1].g =3D=3D GSAttachmentGlyph)</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">		</span> =
&nbsp; &nbsp;{</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span> &nbsp; &nbsp; =
&nbsp;[curLayoutManager setAttachmentSize: g[-1].size</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">			=
</span>forGlyphRange: NSMakeRange(cache_base + lineFragCounter2, =
lineFragCounter - lineFragCounter2)];</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">		</span> =
&nbsp; &nbsp;}</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span> &nbsp;p =3D =
g-&gt;pos;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span> &nbsp;p.y +=3D =
baseline;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span> &nbsp;lineFragCounter2 =3D=
 lineFragCounter;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span>}</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp; &nbsp;lineFragCounter++;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp; =
&nbsp;g++;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp;}</div><div>&nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; /* Register final run in fragment =
*/</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">	=
</span> &nbsp;if (lineFragCounter !=3D lineFragCounter2)</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;{</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp; =
&nbsp;[curLayoutManager setLocation: p</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">				=
</span>forStartOfGlyphRange: NSMakeRange(cache_base + lineFragCounter2, =
lineFragCounter - lineFragCounter2)];</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp; &nbsp;if (g[-1].g =3D=3D GSAttachmentGlyph)</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">		=
</span>{</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span> &nbsp;[curLayoutManager =
setAttachmentSize: g[-1].size</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span> &nbsp; =
&nbsp;forGlyphRange: NSMakeRange(cache_base + lineFragCounter2, =
lineFragCounter - lineFragCounter2)];</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">		=
</span>}</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp;}</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	=
</span>}</div><div>&nbsp; &nbsp; }</div><div>&nbsp; =
}</div><div><br></div><div>&nbsp; /* Advance current point to next line =
*/</div><div>&nbsp; curPoint =3D NSMakePoint(0, =
NSMaxY(line_frags-&gt;rect));</div><div><br></div><div>&nbsp; if =
(newParagraph)</div><div>&nbsp; &nbsp; return 3;</div><div>&nbsp; =
else</div><div>&nbsp; &nbsp; return =
0;</div><div>}</div><div><br></div><div><br></div><div>/*</div><div>&nbsp;=
* MAIN ENTRY POINT</div><div>&nbsp;* Lays out multiple lines of glyphs =
into the text container.</div><div>&nbsp;*</div><div>&nbsp;* =
Parameters:</div><div>&nbsp;* &nbsp; layoutManager: &nbsp; &nbsp; &nbsp; =
&nbsp; The GSLayoutManager managing this text</div><div>&nbsp;* &nbsp; =
textContainer: &nbsp; &nbsp; &nbsp; &nbsp; The container defining layout =
geometry</div><div>&nbsp;* &nbsp; glyphIndex: &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp;Starting glyph index for this layout =
operation</div><div>&nbsp;* &nbsp; previousLineFragRect: &nbsp;Rectangle =
of the previous line (for positioning)</div><div>&nbsp;* &nbsp; =
nextGlyphIndex: &nbsp; &nbsp; &nbsp; &nbsp;OUTPUT - index of first =
unlaid glyph</div><div>&nbsp;* &nbsp; howMany: &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; Maximum number of lines to layout (0 =3D =
unlimited)</div><div>&nbsp;*</div><div>&nbsp;* Return =
values:</div><div>&nbsp;* &nbsp; 0 - Layout completed successfully (more =
glyphs may remain)</div><div>&nbsp;* &nbsp; 1 - Text container is =
full</div><div>&nbsp;* &nbsp; 2 - All glyphs have been laid =
out</div><div>&nbsp;*/</div><div>-(int) layoutGlyphsInLayoutManager: =
(GSLayoutManager *)layoutManager</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span> &nbsp; inTextContainer: =
(NSTextContainer *)textContainer</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp; =
&nbsp;startingAtGlyphIndex: (unsigned int)glyphIndex</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> =
&nbsp;previousLineFragmentRect: =
(NSRect)previousLineFragRect</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span> &nbsp; =
&nbsp;nextGlyphIndex: (unsigned int *)nextGlyphIndex</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp; numberOfLineFragments: (unsigned =
int)howMany</div><div>{</div><div>&nbsp; int ret, =
real_ret;</div><div>&nbsp; BOOL =
newParagraph;</div><div><br></div><div>&nbsp; /* REENTRANCY HANDLING =
*/</div><div>&nbsp; if (![lock tryLock])</div><div>&nbsp; &nbsp; =
{</div><div>&nbsp; &nbsp; &nbsp; /* Already in use - create temporary =
instance to handle nested call */</div><div>&nbsp; &nbsp; &nbsp; =
GSHorizontalTypesetter *temp;</div><div><br></div><div>&nbsp; &nbsp; =
&nbsp; temp =3D [[object_getClass(self) alloc] init];</div><div>&nbsp; =
&nbsp; &nbsp; ret =3D [temp layoutGlyphsInLayoutManager: =
layoutManager</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">			</span> &nbsp; &nbsp; =
&nbsp;inTextContainer: textContainer</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">			=
</span> startingAtGlyphIndex: glyphIndex</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">		</span> =
&nbsp; &nbsp; previousLineFragmentRect: =
previousLineFragRect</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">			</span> &nbsp; &nbsp; =
&nbsp; nextGlyphIndex: nextGlyphIndex</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">			=
</span>numberOfLineFragments: howMany];</div><div>&nbsp; &nbsp; &nbsp; =
DESTROY(temp);</div><div>&nbsp; &nbsp; &nbsp; return =
ret;</div><div>&nbsp; &nbsp; =
}</div><div><br></div><div>NS_DURING</div><div>&nbsp; /* Initialize =
layout context */</div><div>&nbsp; curLayoutManager =3D =
layoutManager;</div><div>&nbsp; curTextContainer =3D =
textContainer;</div><div>&nbsp; curTextStorage =3D [layoutManager =
textStorage];</div><div>&nbsp; curGlyphIndex =3D =
glyphIndex;</div><div><br></div><div>&nbsp; [self =
_cacheClear];</div><div><br></div><div>&nbsp; real_ret =3D 4; &nbsp;// =
Initial state forces paragraph check</div><div>&nbsp; curPoint =3D =
NSMakePoint(0, =
NSMaxY(previousLineFragRect));</div><div>&nbsp;&nbsp;</div><div>&nbsp; =
/* Main layout loop - process lines until limit or completion =
*/</div><div>&nbsp; while (1)</div><div>&nbsp; &nbsp; {</div><div>&nbsp; =
&nbsp; &nbsp; /* Determine if we're starting a new paragraph =
*/</div><div>&nbsp; &nbsp; &nbsp; if (real_ret =3D=3D 4)</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	=
</span>{</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp;if =
(!curGlyphIndex)</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp;{</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp; &nbsp;newParagraph =3D YES; &nbsp;// Very first glyph in =
text</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">	=
</span> &nbsp; &nbsp;}</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp;else</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp;{</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /* =
Check if previous character was a newline */</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp; &nbsp;unsigned int chi;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp; &nbsp;unichar =
ch;</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">	=
</span> &nbsp; &nbsp; &nbsp;chi =3D [curLayoutManager =
characterRangeForGlyphRange: NSMakeRange(curGlyphIndex - 1, =
1)</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">		=
				</span> actualGlyphRange: =
NULL].location;</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp; &nbsp;ch =3D =
[[curTextStorage string] characterAtIndex: chi];</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	=
</span></div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp; &nbsp; &nbsp;if (ch =3D=3D =
'\n')</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">	=
	</span>newParagraph =3D YES;</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> &nbsp; =
&nbsp; &nbsp;else</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">		</span>newParagraph =3D =
NO;</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">	=
</span> &nbsp; &nbsp;}</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span>}</div><div>&nbsp; &nbsp; &nbsp; =
else if (real_ret =3D=3D 3)</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span>{</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> =
&nbsp;newParagraph =3D YES; &nbsp;// Previous line ended with =
newline</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span>}</div><div>&nbsp; &nbsp; &nbsp; =
else</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">	=
</span>{</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span> &nbsp;newParagraph =3D =
NO;</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">	=
</span>}</div><div><br></div><div>&nbsp; &nbsp; &nbsp; /* Layout one =
line */</div><div>&nbsp; &nbsp; &nbsp; ret =3D [self =
layoutLineNewParagraph: newParagraph];</div><div><br></div><div>&nbsp; =
&nbsp; &nbsp; /* Normalize return codes 3 and 4 to 0 for loop control =
*/</div><div>&nbsp; &nbsp; &nbsp; real_ret =3D ret;</div><div>&nbsp; =
&nbsp; &nbsp; if (ret =3D=3D 3 || ret =3D=3D 4)</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span>ret =3D =
0;</div><div><br></div><div>&nbsp; &nbsp; &nbsp; if =
(ret)</div><div><span class=3D"Apple-tab-span" style=3D"white-space:pre">	=
</span>break; &nbsp;// Error or completion (1 or =
2)</div><div><br></div><div>&nbsp; &nbsp; &nbsp; if =
(howMany)</div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span>if (!--howMany)</div><div><span =
class=3D"Apple-tab-span" style=3D"white-space:pre">	</span> =
&nbsp;break; &nbsp;// Reached line limit</div><div>&nbsp; =
&nbsp;}</div><div><br></div><div>&nbsp; *nextGlyphIndex =3D =
curGlyphIndex;</div><div>NS_HANDLER</div><div>&nbsp; /* Exception =
handling: log, unlock, and re-raise */</div><div>&nbsp; =
NSLog(@"GSHorizontalTypesetter - %@", [localException =
reason]);</div><div>&nbsp; [lock unlock];</div><div>&nbsp; =
[localException raise];</div><div>&nbsp; ret=3D0; /* Unreachable, but =
silences compiler warnings =
*/</div><div>NS_ENDHANDLER</div><div>&nbsp;&nbsp;</div><div>&nbsp; [lock =
unlock];</div><div>&nbsp; return =
ret;</div><div>}</div><div><br></div><div>@end</div><div>```</div><div><br=
></div><div>## Summary</div><div><br></div><div>This is a sophisticated =
horizontal typesetter for GNUstep implementing the full Cocoa text =
layout architecture. Key features =
include:</div><div><br></div><div>**Architecture:**</div><div>- =
**Thread-local singleton** pattern for performance without locking =
overhead</div><div>- **Reentrancy support** via temporary instance =
creation when nested calls occur</div><div>- **Two-phase caching**: =
First cache glyph attributes, then compute =
positions</div><div><br></div><div>**Layout Algorithm:**</div><div>1. =
**Soft invalidation** check for incremental layout reuse</div><div>2. =
**Line fragment acquisition** from text container (handles complex =
shapes)</div><div>3. **Glyph-by-glyph positioning** with font changes, =
attachments, tabs</div><div>4. **Line breaking** by character or word =
(with CJK support)</div><div>5. **Alignment/justification** pass to =
distribute space</div><div>6. **Commit** to layout manager with precise =
glyph ranges and positions</div><div><br></div><div>**Performance =
Optimizations:**</div><div>- Aggressive caching of glyph metrics and =
attributes</div><div>- Batch glyph processing with expandable =
buffers</div><div>- Inline helper functions for hot paths</div><div>- =
Memmove for cache window sliding rather than =
refetching</div><div><br></div><div>The code handles complex typography =
including bidirectional text support preparation, text attachments, =
multiple line fragments per line, and all standard paragraph =
alignments.</div><div><br></div><div><br></div><div>=E2=80=94=E2=80=94 =
=E2=80=94=E2=80=94 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94=
 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94 =
=E2=80=94=E2=80=94&nbsp;</div><div><br></div><div><br></div><div>Then I =
asked:</div><div><br></div><div><br></div><div>=E2=80=9EWhat =
refactorings do you propose to make that above code you commented better =
readable and simpler to comprehend without making it =
worse?=E2=80=9C</div><div><br></div><div><br></div><div>=E2=80=94=E2=80=94=
 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94=
 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94 =
=E2=80=94=E2=80=94&nbsp;</div><div><br></div><div><br></div><div><div>I'll=
 analyze this code and propose concrete refactorings to improve =
readability and maintainability. Let me work through this =
systematically.</div><div><br></div><div>## Proposed Refactorings for =
GSHorizontalTypesetter</div><div><br></div><div>### 1. **Extract Magic =
Numbers and Character Constants**</div><div><br></div><div>Replace =
hardcoded hex values with named =
constants:</div><div><br></div><div>```objc</div><div>// Add to top of =
file or private header</div><div>static const unichar kSpaceCharacter =3D =
0x20;</div><div>static const unichar kNewlineCharacter =3D =
0x0a;</div><div>static const unichar kCarriageReturnCharacter =3D =
0x0d;</div><div>static const unichar kTabCharacter =3D =
0x09;</div><div>static const unichar kCJKRangeStart =3D =
0x2ff0;</div><div>static const unichar kCJKRangeEnd =3D =
0x9fff;</div><div>static const CGFloat kDefaultTabInterval =3D =
100.0;</div><div>static const CGFloat kFallbackLineHeight =3D =
15.0;</div><div>static const CGFloat kExtraLineFragmentWidth =3D =
1.0;</div><div>```</div><div><br></div><div>### 2. **Extract Complex =
Conditionals into Meaningful Boolean =
Methods**</div><div><br></div><div>The glyph fitting check appears =
multiple times and is hard to =
read:</div><div><br></div><div>```objc</div><div>// BEFORE: scattered =
throughout code</div><div>doesGlyphFitInLine =3D !((i &gt; =
firstGlyphIndex) &amp;&amp; (p.x + g-&gt;size.width &gt; =
lf-&gt;rect.size.width));</div><div><br></div><div>// AFTER: extracted =
method</div><div>- (BOOL)doesGlyphAtIndex:(unsigned =
int)glyphIndex&nbsp;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp;withWidth:(CGFloat)width&nbsp;</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp;fitInLineFragment:(line_frag_t =
*)fragment</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp;afterFirstGlyph:(unsigned =
int)firstIndex</div><div>{</div><div>&nbsp; &nbsp; if (glyphIndex &lt;=3D =
firstIndex) return YES; &nbsp;// First glyph always =
fits</div><div>&nbsp; &nbsp; return (p.x + width &lt;=3D =
fragment-&gt;rect.size.width);</div><div>}</div><div>```</div><div><br></d=
iv><div>### 3. **Replace `goto restart` with Structured =
Loop**</div><div><br></div><div>The `restart` label for line height =
recalculation is a classic goto =
pattern:</div><div><br></div><div>```objc</div><div>// BEFORE: =
goto-based restart</div><div>restart: ;</div><div>do {</div><div>&nbsp; =
&nbsp; // ... calculate fragments ...</div><div>&nbsp; &nbsp; if =
(line_frags_num =3D=3D 0) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; if (/* =
emergency shrink */) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; line_height =3D ...;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; continue; &nbsp;// or goto restart</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; return =
1;</div><div>&nbsp; &nbsp; }</div><div>} while (line_frags_num =3D=3D =
0);</div><div><br></div><div>// ... later ...</div><div>if =
(wantNewLineHeight(...)) goto restart;</div><div><br></div><div>// =
AFTER: extracted method with explicit loop</div><div>- =
(BOOL)_acquireLineFragmentsWithHeight:(CGFloat)lineHeight</div><div>&nbsp;=
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; maxLineHeight:(CGFloat)maxLineHeight</div><div>&nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; newParagraph:(BOOL)newParagraph</div><div>&nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp;resultingHeight:(CGFloat =
*)outHeight</div><div>{</div><div>&nbsp; &nbsp; CGFloat currentHeight =3D =
lineHeight;</div><div>&nbsp; &nbsp; CGFloat maxHeight =3D =
maxLineHeight;</div><div>&nbsp; &nbsp;&nbsp;</div><div>&nbsp; &nbsp; =
while (YES) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; [self =
_buildLineFragmentsForHeight:currentHeight =
newParagraph:newParagraph];</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp;&nbsp;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; if (line_frags_num =
&gt; 0) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; *outHeight =
=3D currentHeight;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
return YES; &nbsp;// Success</div><div>&nbsp; &nbsp; &nbsp; &nbsp; =
}</div><div>&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</div><div>&nbsp; &nbsp; =
&nbsp; &nbsp; // Emergency: container too small for even one =
line</div><div>&nbsp; &nbsp; &nbsp; &nbsp; if (curPoint.y =3D=3D 0.0 =
&amp;&amp; currentHeight &gt; containerHeight &amp;&amp; containerHeight =
&gt; 0.0) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
currentHeight =3D containerHeight;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; maxHeight =3D currentHeight;</div><div>&nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; continue; &nbsp;// Retry with shrunk =
height</div><div>&nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; =
&nbsp; &nbsp;&nbsp;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; return NO; =
&nbsp;// No room in container</div><div>&nbsp; &nbsp; =
}</div><div>}</div><div><br></div><div>// Then in main layout, replace =
goto with:</div><div>while (![self _updateLineHeightIfNeeded:ascender =
descender:descender&nbsp;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp;currentHeight:&amp;line_height&nbsp;</div><div>&nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp;maxHeight:max_line_height]) {</div><div>&nbsp; &nbsp; // =
Restart layout with new height</div><div>&nbsp; &nbsp; if (![self =
_acquireLineFragmentsWithHeight:line_height ...]) {</div><div>&nbsp; =
&nbsp; &nbsp; &nbsp; return 1;</div><div>&nbsp; &nbsp; =
}</div><div>&nbsp; &nbsp; // Re-initialize cache if =
needed</div><div>}</div><div>```</div><div><br></div><div>### 4. =
**Extract State Machine Transitions**</div><div><br></div><div>The =
`real_ret` state machine in `layoutGlyphsInLayoutManager:` is =
cryptic:</div><div><br></div><div>```objc</div><div>// BEFORE: confusing =
state tracking</div><div>real_ret =3D 4;</div><div>while (1) =
{</div><div>&nbsp; &nbsp; if (real_ret =3D=3D 4) {</div><div>&nbsp; =
&nbsp; &nbsp; &nbsp; // determine newParagraph</div><div>&nbsp; &nbsp; } =
else if (real_ret =3D=3D 3) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; =
newParagraph =3D YES;</div><div>&nbsp; &nbsp; } else {</div><div>&nbsp; =
&nbsp; &nbsp; &nbsp; newParagraph =3D NO;</div><div>&nbsp; &nbsp; =
}</div><div>&nbsp; &nbsp; ret =3D [self =
layoutLineNewParagraph:newParagraph];</div><div>&nbsp; &nbsp; real_ret =3D=
 ret;</div><div>&nbsp; &nbsp; if (ret =3D=3D 3 || ret =3D=3D 4) ret =3D =
0;</div><div>}</div><div><br></div><div>// AFTER: explicit state enum =
and transition method</div><div>typedef NS_ENUM(NSInteger, LayoutState) =
{</div><div>&nbsp; &nbsp; LayoutStateInitial =3D 0, &nbsp; &nbsp; =
&nbsp;// Need to check if new paragraph</div><div>&nbsp; &nbsp; =
LayoutStateNewParagraph, &nbsp; &nbsp; // Previous line ended with =
newline</div><div>&nbsp; &nbsp; LayoutStateContinuing, &nbsp; &nbsp; =
&nbsp; // In middle of paragraph</div><div>&nbsp; &nbsp; =
LayoutStateComplete, &nbsp; &nbsp; &nbsp; &nbsp; // All glyphs laid =
out</div><div>&nbsp; &nbsp; LayoutStateNoRoom &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp;// Container =
full</div><div>};</div><div><br></div><div>- =
(LayoutState)determineNextStateFrom:(LayoutState)previousState&nbsp;</div>=
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; =
lineResult:(int)lineResult</div><div>{</div><div>&nbsp; &nbsp; switch =
(lineResult) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; case 2: return =
LayoutStateComplete; &nbsp; // At end</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; case 1: return LayoutStateNoRoom; &nbsp; &nbsp; // No =
room</div><div>&nbsp; &nbsp; &nbsp; &nbsp; case 3: return =
LayoutStateNewParagraph; // Ended with newline</div><div>&nbsp; &nbsp; =
&nbsp; &nbsp; case 0: return LayoutStateContinuing; &nbsp;// Normal line =
break</div><div>&nbsp; &nbsp; &nbsp; &nbsp; case 4: return =
LayoutStateInitial; &nbsp; // Ambiguous, need check</div><div>&nbsp; =
&nbsp; &nbsp; &nbsp; default: return =
LayoutStateComplete;</div><div>&nbsp; &nbsp; =
}</div><div>}</div><div><br></div><div>- =
(BOOL)isNewParagraphForState:(LayoutState)state&nbsp;</div><div>&nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
atGlyphIndex:(unsigned int)glyphIndex</div><div>{</div><div>&nbsp; =
&nbsp; switch (state) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; case =
LayoutStateInitial:</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
return [self _glyphStartsNewParagraph:glyphIndex];</div><div>&nbsp; =
&nbsp; &nbsp; &nbsp; case LayoutStateNewParagraph:</div><div>&nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return YES;</div><div>&nbsp; &nbsp; =
&nbsp; &nbsp; case LayoutStateContinuing:</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; return NO;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; =
default:</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return =
NO;</div><div>&nbsp; &nbsp; =
}</div><div>}</div><div>```</div><div><br></div><div>### 5. **Break Up =
`layoutLineNewParagraph:` into Phased =
Methods**</div><div><br></div><div>This 400+ line method does too much. =
Extract phases:</div><div><br></div><div>```objc</div><div>- =
(int)layoutLineNewParagraph:(BOOL)newParagraph</div><div>{</div><div>&nbsp=
; &nbsp; // Phase 1: Setup and cache initialization</div><div>&nbsp; =
&nbsp; if (![self _prepareLayoutForNewParagraph:newParagraph]) =
{</div><div>&nbsp; &nbsp; &nbsp; &nbsp; return [self =
_handleEndOfTextOrContainer];</div><div>&nbsp; &nbsp; }</div><div>&nbsp; =
&nbsp;&nbsp;</div><div>&nbsp; &nbsp; // Phase 2: Acquire line fragments =
(may restart if metrics change)</div><div>&nbsp; &nbsp; CGFloat =
finalLineHeight;</div><div>&nbsp; &nbsp; if (![self =
_acquireLineFragmentsWithHeight:initialLineHeight&nbsp;</div><div>&nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
maxLineHeight:maxLineHeight</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp;newParagraph:newParagraph</div><div>&nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
resultingHeight:&amp;finalLineHeight]) {</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; return 1; // No room</div><div>&nbsp; &nbsp; }</div><div>&nbsp; =
&nbsp;&nbsp;</div><div>&nbsp; &nbsp; // Phase 3: Position glyphs in =
fragments</div><div>&nbsp; &nbsp; LayoutLineResult *result =3D [self =
_positionGlyphsInFragments];</div><div>&nbsp; &nbsp; if =
(result.needsRestart) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; // =
Recursively retry with new height (limited depth to prevent infinite =
loop)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; return [self =
layoutLineNewParagraph:newParagraph];</div><div>&nbsp; &nbsp; =
}</div><div>&nbsp; &nbsp;&nbsp;</div><div>&nbsp; &nbsp; // Phase 4: =
Apply alignment</div><div>&nbsp; &nbsp; [self =
_applyAlignmentToLine:result];</div><div>&nbsp; =
&nbsp;&nbsp;</div><div>&nbsp; &nbsp; // Phase 5: Commit to layout =
manager</div><div>&nbsp; &nbsp; [self _commitLayoutResult:result =
lineHeight:finalLineHeight];</div><div>&nbsp; =
&nbsp;&nbsp;</div><div>&nbsp; &nbsp; return result.endedWithNewline ? 3 =
: 0;</div><div>}</div><div>```</div><div><br></div><div>### 6. **Replace =
Anonymous Structs with Named =
Types**</div><div><br></div><div>```objc</div><div>// BEFORE: anonymous =
struct in ivars</div><div>struct {</div><div>&nbsp; &nbsp; BOOL =
explicit_kern;</div><div>&nbsp; &nbsp; float kern;</div><div>&nbsp; =
&nbsp; float baseline_offset;</div><div>&nbsp; &nbsp; int =
superscript;</div><div>} attributes;</div><div><br></div><div>// AFTER: =
named type with documentation</div><div>/**</div><div>&nbsp;* =
GSScriptAttributes collects the typographic attributes that =
affect</div><div>&nbsp;* glyph positioning relative to the =
baseline.</div><div>&nbsp;*/</div><div>typedef struct {</div><div>&nbsp; =
&nbsp; BOOL hasExplicitKern; &nbsp; &nbsp; &nbsp;// YES if =
NSKernAttributeName was specified</div><div>&nbsp; &nbsp; CGFloat =
kernAmount; &nbsp; &nbsp; &nbsp; &nbsp;// Additional spacing between =
glyphs</div><div>&nbsp; &nbsp; CGFloat baselineOffset; &nbsp; &nbsp;// =
Vertical shift from normal baseline (positive =3D up)</div><div>&nbsp; =
&nbsp; NSInteger superscriptLevel; // 0 =3D normal, +1 =3D superscript, =
-1 =3D subscript</div><div>} =
GSScriptAttributes;</div><div><br></div><div>// Usage in class =
becomes:</div><div>GSScriptAttributes =
_currentScriptAttributes;</div><div>```</div><div><br></div><div>### 7. =
**Extract Cache Management to Dedicated =
Class**</div><div><br></div><div>The cache manipulation is complex and =
scattered:</div><div><br></div><div>```objc</div><div>// New class: =
GSGlyphCache</div><div>@interface GSGlyphCache : =
NSObject</div><div>@property (readonly) NSUInteger =
baseIndex;</div><div>@property (readonly) NSUInteger =
count;</div><div>@property (readonly) BOOL =
atEnd;</div><div><br></div><div>- =
(void)repositionToGlyphIndex:(NSUInteger)index&nbsp;</div><div>&nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
layoutManager:(GSLayoutManager *)lm;</div><div>- =
(void)ensureCount:(NSUInteger)count&nbsp;</div><div>&nbsp; &nbsp; =
&nbsp;layoutManager:(GSLayoutManager *)lm;</div><div>- (glyph_cache_t =
*)glyphAtIndex:(NSUInteger)index; &nbsp;// Relative to base</div><div>- =
(void)slideWindowToIndex:(NSUInteger)index;</div><div>@end</div><div>```</=
div><div><br></div><div>### 8. **Replace C Arrays with NSMutableArray or =
Smart Pointer**</div><div><br></div><div>The manual `realloc` management =
is error-prone:</div><div><br></div><div>```objc</div><div>// BEFORE: =
manual C array management</div><div>if (line_frags_num &gt; =
line_frags_size) {</div><div>&nbsp; &nbsp; line_frags_size +=3D =
2;</div><div>&nbsp; &nbsp; line_frags =3D realloc(line_frags, =
sizeof(line_frag_t) * =
line_frags_size);</div><div>}</div><div><br></div><div>// AFTER: use =
NSMutableArray with wrapper object, or at least:</div><div>typedef =
struct {</div><div>&nbsp; &nbsp; line_frag_t *items;</div><div>&nbsp; =
&nbsp; NSUInteger count;</div><div>&nbsp; &nbsp; NSUInteger =
capacity;</div><div>} =
GSLineFragmentArray;</div><div><br></div><div>static inline void =
GSLineFragmentArrayPush(GSLineFragmentArray *array) {</div><div>&nbsp; =
&nbsp; if (array-&gt;count &gt;=3D array-&gt;capacity) =
{</div><div>&nbsp; &nbsp; &nbsp; &nbsp; array-&gt;capacity =3D =
array-&gt;capacity ? array-&gt;capacity * 2 : 4;</div><div>&nbsp; &nbsp; =
&nbsp; &nbsp; array-&gt;items =3D realloc(array-&gt;items, =
sizeof(line_frag_t) * array-&gt;capacity);</div><div>&nbsp; &nbsp; =
}</div><div>&nbsp; &nbsp; =
array-&gt;count++;</div><div>}</div><div>```</div><div><br></div><div>### =
9. **Document Return Value Semantics with =
Enum**</div><div><br></div><div>```objc</div><div>// BEFORE: magic =
integer returns</div><div>-(int) layoutLineNewParagraph: =
(BOOL)newParagraph &nbsp;// Returns 0, 1, 2, 3, or =
4</div><div><br></div><div>// AFTER: explicit result =
type</div><div>typedef NS_ENUM(NSInteger, GSLayoutLineResult) =
{</div><div>&nbsp; &nbsp; GSLayoutLineResultContinuing =3D 0, &nbsp; =
&nbsp; &nbsp;// Line complete, more glyphs in paragraph</div><div>&nbsp; =
&nbsp; GSLayoutLineResultNoRoom =3D 1, &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp;// Text container exhausted</div><div>&nbsp; &nbsp; =
GSLayoutLineResultEndOfText =3D 2, &nbsp; &nbsp; &nbsp; // All glyphs =
consumed</div><div>&nbsp; &nbsp; GSLayoutLineResultNewParagraph =3D 3, =
&nbsp; &nbsp;// Line ended with newline</div><div>&nbsp; &nbsp; =
GSLayoutLineResultNeedParagraphCheck =3D 4 // Ambiguous, caller must =
check</div><div>};</div><div><br></div><div>- =
(GSLayoutLineResult)layoutLineNewParagraph:(BOOL)newParagraph;</div><div>`=
``</div><div><br></div><div>### 10. **Flatten Nested Conditionals in =
Main Loop**</div><div><br></div><div>The main glyph loop has deeply =
nested conditions. Extract =
handlers:</div><div><br></div><div>```objc</div><div>- =
(BOOL)_handleControlGlyph:(glyph_cache_t *)glyph&nbsp;</div><div>&nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp;atPosition:(NSPoint *)position</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; lineFragment:(line_frag_t =
*)fragment</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
newParagraph:(BOOL *)outNewParagraph</div><div>{</div><div>&nbsp; &nbsp; =
unichar ch =3D [[curTextStorage string] =
characterAtIndex:glyph-&gt;char_index];</div><div>&nbsp; =
&nbsp;&nbsp;</div><div>&nbsp; &nbsp; glyph-&gt;pos =3D =
*position;</div><div>&nbsp; &nbsp; glyph-&gt;size.width =3D =
0;</div><div>&nbsp; &nbsp; glyph-&gt;dont_show =3D YES;</div><div>&nbsp; =
&nbsp;&nbsp;</div><div>&nbsp; &nbsp; switch (ch) {</div><div>&nbsp; =
&nbsp; &nbsp; &nbsp; case kNewlineCharacter:</div><div>&nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; *outNewParagraph =3D YES;</div><div>&nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return NO; &nbsp;// Stop processing =
this line</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp;&nbsp;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; case =
kTabCharacter:</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
*position =3D [self _positionAfterTabFrom:*position =
inFragment:fragment];</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; return YES; &nbsp;// Continue to next glyph</div><div>&nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; default:</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
NSDebugLLog(@"GSHorizontalTypesetter", @"Unknown control %04x", =
ch);</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return =
YES;</div><div>&nbsp; &nbsp; }</div><div>}</div><div><br></div><div>- =
(BOOL)_handleAttachmentGlyph:(glyph_cache_t =
*)glyph&nbsp;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; atPosition:(NSPoint *)position</div><div>&nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp;lineFragment:(line_frag_t *)fragment</div><div>&nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp;lineHeight:(CGFloat *)lineHeight</div><div>{</div><div>&nbsp; =
&nbsp; // Extract attachment logic...</div><div>&nbsp; &nbsp; // Return =
YES if fits and metrics updated, NO if needs line =
break</div><div>}</div><div>```</div><div><br></div><div>### 11. =
**Extract Metric Calculation to Dedicated =
Method**</div><div><br></div><div>```objc</div><div>- =
(void)_updateLineMetricsForGlyph:(glyph_cache_t *)glyph</div><div>&nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
currentAscender:(CGFloat *)ascender</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp;currentDescender:(CGFloat *)descender</div><div>&nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp;currentHeight:(CGFloat *)lineHeight</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
maxLineHeight:(CGFloat)maxHeight</div><div>{</div><div>&nbsp; &nbsp; =
NSFont *font =3D glyph-&gt;font;</div><div>&nbsp; &nbsp; CGFloat =
glyphAscender =3D [font ascender];</div><div>&nbsp; &nbsp; CGFloat =
glyphDescender =3D -[font descender];</div><div>&nbsp; =
&nbsp;&nbsp;</div><div>&nbsp; &nbsp; // Apply superscript =
adjustments</div><div>&nbsp; &nbsp; CGFloat yOffset =3D =
0;</div><div>&nbsp; &nbsp; if (glyph-&gt;attributes.superscript) =
{</div><div>&nbsp; &nbsp; &nbsp; &nbsp; yOffset -=3D =
glyph-&gt;attributes.superscript * [font xHeight];</div><div>&nbsp; =
&nbsp; }</div><div>&nbsp; &nbsp; if =
(glyph-&gt;attributes.baseline_offset) {</div><div>&nbsp; &nbsp; &nbsp; =
&nbsp; yOffset +=3D =
glyph-&gt;attributes.baseline_offset;</div><div>&nbsp; &nbsp; =
}</div><div>&nbsp; &nbsp;&nbsp;</div><div>&nbsp; &nbsp; // Update =
metrics</div><div>&nbsp; &nbsp; *ascender =3D MAX(*ascender, =
glyphAscender - MIN(yOffset, 0));</div><div>&nbsp; &nbsp; *descender =3D =
MAX(*descender, glyphDescender + MAX(yOffset, 0));</div><div>&nbsp; =
&nbsp;&nbsp;</div><div>&nbsp; &nbsp; CGFloat newHeight =3D *ascender + =
*descender;</div><div>&nbsp; &nbsp; if (maxHeight &gt; 0) =
{</div><div>&nbsp; &nbsp; &nbsp; &nbsp; newHeight =3D MIN(newHeight, =
maxHeight);</div><div>&nbsp; &nbsp; }</div><div>&nbsp; &nbsp; =
*lineHeight =3D MAX(*lineHeight, =
newHeight);</div><div>}</div><div>```</div><div><br></div><div>### 12. =
**Simplify Soft Invalidation Logic**</div><div><br></div><div>The soft =
invalidation method is hard to follow. Restructure with early returns =
and clear phases:</div><div><br></div><div>```objc</div><div>- =
(BOOL)_reuseSoftInvalidatedLayout</div><div>{</div><div>&nbsp; &nbsp; // =
Quick reject: not at soft invalidation point</div><div>&nbsp; &nbsp; if =
(![self _isAtSoftInvalidationPoint]) return NO;</div><div>&nbsp; =
&nbsp;&nbsp;</div><div>&nbsp; &nbsp; // Quick reject: won't fit =
vertically</div><div>&nbsp; &nbsp; NSRect firstRect =3D [self =
_firstSoftInvalidatedRect];</div><div>&nbsp; &nbsp; if (![self =
_canFitVertically:firstRect]) return NO;</div><div>&nbsp; =
&nbsp;&nbsp;</div><div>&nbsp; &nbsp; // Collect consecutive valid =
fragments</div><div>&nbsp; &nbsp; GSSoftInvalidationBatch *batch =3D =
[self _collectSoftInvalidatedFragments];</div><div>&nbsp; &nbsp; if =
(batch.isEmpty) return NO;</div><div>&nbsp; =
&nbsp;&nbsp;</div><div>&nbsp; &nbsp; // Apply shift and =
commit</div><div>&nbsp; &nbsp; [self =
_applySoftInvalidationBatch:batch];</div><div>&nbsp; &nbsp; =
curGlyphIndex =3D batch.endGlyphIndex;</div><div>&nbsp; &nbsp; return =
YES;</div><div>}</div><div>```</div><div><br></div><div>---</div><div><br>=
</div><div>## Summary Table</div><div><br></div><div>| Refactoring | =
Benefit | Effort =
|</div><div>|-------------|---------|--------|</div><div>| Named =
constants | Eliminates magic numbers, self-documenting | Low =
|</div><div>| Boolean methods | Clarifies intent of complex conditions | =
Low |</div><div>| Replace goto | Structured flow, easier debugging | =
Medium |</div><div>| State machine enum | Explicit transitions, type =
safety | Medium |</div><div>| Method extraction | Single responsibility, =
testable units | Medium |</div><div>| Named structs | Self-documenting, =
IDE completion | Low |</div><div>| Cache class | Encapsulation, easier =
optimization | High |</div><div>| Array abstraction | Memory safety, =
bounds checking | Medium |</div><div>| Return value enum | Compiler =
checking, clear semantics | Low |</div><div>| Glyph handlers | Flatten =
nesting, isolate concerns | Medium |</div><div>| Metric extraction | =
Reusable, testable calculations | Low |</div><div>| Soft invalidation =
phases | Linear flow, early exits | Medium =
|</div><div><br></div><div>These changes maintain performance (critical =
for text layout) while significantly improving maintainability. The most =
impactful would be **#5 (method extraction)** and **#3 (goto =
elimination)**, which together would reduce the cognitive load of =
understanding the layout =
algorithm.</div></div><div><br></div><div><br></div><div>=E2=80=94=E2=80=94=
 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94=
 =E2=80=94=E2=80=94 =E2=80=94=E2=80=94 =
=E2=80=94=E2=80=94&nbsp;</div><div><br></div><div><br></div><div>I see =
you read until here. I know, that was a lot to digest. Now I am asking: =
what do you think about this? Does it make sense? Should we do those =
refactorings?</div><div><br></div><div><br></div><div>Kind =
regards,</div><div><br></div><div><span class=3D"Apple-tab-span" =
style=3D"white-space:pre">	</span>Lars</div></body></html>=

--Apple-Mail=_129DB140-0349-41BF-87CA-B70DD12BB7CE--