Exemplo n.º 1
0
int MapCharToGlyph_AAT(ATSUStyle style, UInt32 ch)
{
	UniChar	txt[2];
	int		len = 1;
	
	if (sTextLayout == 0)
		InitializeLayout();

	if (ch > 0xffff) {
		ch -= 0x10000;
		txt[0] = 0xd800 + ch / 1024;
		txt[1] = 0xdc00 + ch % 1024;
		len = 2;
	}
	else
		txt[0] = ch;

	OSStatus	status = ATSUSetTextPointerLocation(sTextLayout, &txt[0], 0, len, len);
	if (status != noErr)
		return 0;
	status = ATSUSetRunStyle(sTextLayout, style, 0, len);
	if (status != noErr)
		return 0;
	
	ByteCount	bufferSize = sizeof(ATSUGlyphInfoArray);
	ATSUGlyphInfoArray	info;
	status = ATSUGetGlyphInfo(sTextLayout, 0, len, &bufferSize, &info);
	if (status == noErr) {
		if (bufferSize > 0 && info.numGlyphs > 0)
			return info.glyphs[0].glyphID;
	}
	return 0;
}
Exemplo n.º 2
0
bool QFontEngineMacMulti::canRender(const QChar *string, int len)
{
    ATSUSetTextPointerLocation(textLayout, reinterpret_cast<const UniChar *>(string), 0, len, len);
    ATSUSetRunStyle(textLayout, style, 0, len);

    OSStatus e = noErr;
    int pos = 0;
    do {
        FMFont substFont = 0;
        UniCharArrayOffset changedOffset = 0;
        UniCharCount changeCount = 0;

        e = ATSUMatchFontsToText(textLayout, pos, len - pos,
                                 &substFont, &changedOffset,
                                 &changeCount);
        if (e == kATSUFontsMatched) {
            pos = changedOffset + changeCount;
        } else if (e == kATSUFontsNotMatched) {
            break;
        }
    } while (pos < len && e != noErr);

    return e == noErr || e == kATSUFontsMatched;
}
Exemplo n.º 3
0
// This example is almost identical to the helloworld example, except that
// in this case, there are two styles instead of just one. ATSUSetRunStyle
// is used to apply a style to different parts of the text.
//
void DrawMultipleStylesContents(WindowRef window)
{
	CFStringRef					string;
	UniChar						*text;
	UniCharCount				length;
    UniCharArrayOffset			currentStart, currentEnd;
	ATSUStyle					style1, style2;
	ATSUTextLayout				layout;
	ATSUFontID					font;
	Fixed						pointSize;
    ATSUAttributeTag			tags[2];
    ByteCount					sizes[2];
    ATSUAttributeValuePtr		values[2];
	Fixed						lineWidth, ascent, descent;
	CGContextRef				cgContext;
	float						x, y, cgY, windowHeight;
    ItemCount					numSoftBreaks;
    UniCharArrayOffset			*theSoftBreaks;
    int							i;
    GrafPtr						port, savedPort;
	Rect						portBounds;

    // Set up the graphics port
	port = GetWindowPort(window);
    GetPort(&savedPort);
    SetPort(port);
    GetPortBounds(port, &portBounds);
    EraseRect(&portBounds);

	// Create a style object. This is one of two objects necessary to draw using ATSUI.
	// (The layout is the other.)
	verify_noerr( ATSUCreateStyle(&style1) );

    // Look up the font we are going to use, and set it in the style object, using
	// the aforementioned "triple" (tag, size, value) semantics. This is how almost
	// all settings in ATSUI are applied.
	verify_noerr( ATSUFindFontFromName(kMultipleStylesFontName, strlen(kMultipleStylesFontName), kFontFullName, kFontNoPlatform, kFontNoScript, kFontNoLanguage, &font) );
    tags[0] = kATSUFontTag;
    sizes[0] = sizeof(ATSUFontID);
    values[0] = &font;
    verify_noerr( ATSUSetAttributes(style1, 1, tags, sizes, values) );

    // Set the point size, also using a triple. You can actually set multiple triples at once,
	// since the tag, size, and value parameters are arrays. Other examples do this, such as
	// the vertical text example.
	// 
	pointSize = Long2Fix(kMultipleStylesFontSize);
	tags[0] = kATSUSizeTag;
    sizes[0] = sizeof(Fixed);
    values[0] = &pointSize;
    verify_noerr( ATSUSetAttributes(style1, 1, tags, sizes, values) );
	
	// Now we create the second of two objects necessary to draw text using ATSUI, the layout.
	// You can specify a pointer to the text buffer at layout creation time, or later using
	// the routine ATSUSetTextPointerLocation(). Below, we do it after layout creation time.
	verify_noerr( ATSUCreateTextLayout(&layout) );

	// Before assigning text to the layout, we must first convert the string we plan to draw
	// from a CFStringRef into an array of UniChar.
	string = CFStringCreateWithCString(NULL, "In this example, various parts of the text have different styles applied. The same style is used more than once.", kCFStringEncodingASCII);

    // Extract the raw Unicode from the CFString, then dispose of the CFString
    length = CFStringGetLength(string);
    text = (UniChar *)malloc(length * sizeof(UniChar));
    CFStringGetCharacters(string, CFRangeMake(0, length), text);
	CFRelease(string);

    // Attach the resulting UTF-16 Unicode text to the layout
    verify_noerr( ATSUSetTextPointerLocation(layout, text, kATSUFromTextBeginning, kATSUToTextEnd, length) );
	
	// Now we tie the two necessary objects, the layout and the style, together
	verify_noerr( ATSUSetRunStyle(layout, style1, kATSUFromTextBeginning, kATSUToTextEnd) );

	// Now, for this example we create a second style, and assign it to various runs within
	// the text. For our example, the run offsets are hard-coded for simplicity's sake. In
	// a real application, style runs are often assigned from external sources, such as user
	// selection.
	verify_noerr( ATSUCreateAndCopyStyle(style1, &style2) );

	// Change the font for the second style
	verify_noerr( ATSUFindFontFromName(kMultipleStylesFontName2, strlen(kMultipleStylesFontName2), kFontFullName, kFontNoPlatform, kFontNoScript, kFontNoLanguage, &font) );
    tags[0] = kATSUFontTag;
    sizes[0] = sizeof(ATSUFontID);
    values[0] = &font;
    verify_noerr( ATSUSetAttributes(style2, 1, tags, sizes, values) );

	// Apply the new style to the text in various places
	verify_noerr( ATSUSetRunStyle(layout, style2, 8, 7) ); // The word "example"
	verify_noerr( ATSUSetRunStyle(layout, style2, 65, 7) ); // The word "applied"
	verify_noerr( ATSUSetRunStyle(layout, style2, 83, 5) ); // The word "style"
	verify_noerr( ATSUSetRunStyle(layout, style2, 107, 4) ); // The word "once"

	// In this example, we are breaking text into lines.
	// Therefore, we need to know the width of the line.
    lineWidth = X2Fix(portBounds.right - portBounds.left - 2.0*kMultipleStylesMargin);
    tags[0] = kATSULineWidthTag;
    sizes[0] = sizeof(Fixed);
    values[0] = &lineWidth;
	verify_noerr( ATSUSetLayoutControls(layout, 1, tags, sizes, values) );

	// Prepare the CGContext for drawing
	QDBeginCGContext(port, &cgContext);
	tags[0] = kATSUCGContextTag;
	sizes[0] = sizeof(CGContextRef);
	values[0] = &cgContext;
	verify_noerr( ATSUSetLayoutControls(layout, 1, tags, sizes, values) );
	
	// Prepare the coordinates for drawing. In our example, "x" and "y" are the coordinates
	// in QD space. "cgY" contains the y coordinate in CG space.
	//
	windowHeight = portBounds.bottom - portBounds.top;
	x = kMultipleStylesMargin; // leave a small left margin
	y = kMultipleStylesMargin; // leave a small top margin
	cgY = windowHeight - y; // Subtract the y coordinate from the height of the
							// window to get the coordinate in CG-aware space.

	// Break the text into lines
	verify_noerr( ATSUBatchBreakLines(layout, kATSUFromTextBeginning, length, lineWidth, &numSoftBreaks) );
    verify_noerr( ATSUGetSoftLineBreaks(layout, kATSUFromTextBeginning, kATSUToTextEnd, 0, NULL, &numSoftBreaks) );
    theSoftBreaks = (UniCharArrayOffset *) malloc(numSoftBreaks * sizeof(UniCharArrayOffset));
    verify_noerr( ATSUGetSoftLineBreaks(layout, kATSUFromTextBeginning, kATSUToTextEnd, numSoftBreaks, theSoftBreaks, &numSoftBreaks) );
	
    // Loop over all the lines and draw them
    currentStart = 0;
    for (i=0; i <= numSoftBreaks; i++) {
        currentEnd = ((numSoftBreaks > 0 ) && (numSoftBreaks > i)) ? theSoftBreaks[i] : length;

        // This is the height of a line, the ascent and descent. Getting the values this way is the preferred method.
		ATSUGetLineControl(layout, currentStart, kATSULineAscentTag, sizeof(ATSUTextMeasurement), &ascent, NULL);
        ATSUGetLineControl(layout, currentStart, kATSULineDescentTag, sizeof(ATSUTextMeasurement), &descent, NULL);

        // Make room for the area above the baseline
		y += Fix2X(ascent);
		cgY = windowHeight - y;

		// Draw the text
        verify_noerr( ATSUDrawText(layout, currentStart, currentEnd - currentStart, X2Fix(x), X2Fix(cgY)) );

		// Make room for the area beloww the baseline
        y += Fix2X(descent);
		
		// Prepare for next line
        currentStart = currentEnd;
    }

	// This is a one-shot window, so we are now ready to dispose of all our objects.
	// Normally, we would want to keep everything around in case we needed to redraw or change
	// the text at some point.
	
    // Tear down the CGContext
	CGContextFlush(cgContext);
	QDEndCGContext(port, &cgContext);

	// Deallocate string storage
	free(text);

	// Layout and styles also need to be disposed
	verify_noerr( ATSUDisposeStyle(style1) );
	verify_noerr( ATSUDisposeStyle(style2) );
	verify_noerr( ATSUDisposeTextLayout(layout) );

    // Restore the graphics port
    SetPort(savedPort);
}
Exemplo n.º 4
0
PlatformFont::CharInfo& MacCarbFont::getCharInfo(const UTF16 ch) const
{
   // We use some static data here to avoid re allocating the same variable in a loop.
   // this func is primarily called by GFont::loadCharInfo(),
   Rect                 imageRect;
   CGContextRef         imageCtx;
   U32                  bitmapDataSize;
   ATSUTextMeasurement  tbefore, tafter, tascent, tdescent;
   OSStatus             err;

   // 16 bit character buffer for the ATUSI calls.
   // -- hey... could we cache this at the class level, set style and loc *once*, 
   //    then just write to this buffer and clear the layout cache, to speed up drawing?
   static UniChar chUniChar[1];
   chUniChar[0] = ch;

   // Declare and clear out the CharInfo that will be returned.
   static PlatformFont::CharInfo c;
   dMemset(&c, 0, sizeof(c));
   
   // prep values for GFont::addBitmap()
   c.bitmapIndex = 0;
   c.xOffset = 0;
   c.yOffset = 0;

   // put the text in the layout.
   // we've hardcoded a string length of 1 here, but this could work for longer strings... (hint hint)
   // note: ATSUSetTextPointerLocation() also clears the previous cached layout information.
   ATSUSetTextPointerLocation( mLayout, chUniChar, 0, 1, 1);
   ATSUSetRunStyle( mLayout, mStyle, 0,1);
   
   // get the typographic bounds. this tells us how characters are placed relative to other characters.
   ATSUGetUnjustifiedBounds( mLayout, 0, 1, &tbefore, &tafter, &tascent, &tdescent);
   c.xIncrement =  FixedToInt(tafter);
   
   // find out how big of a bitmap we'll need.
   // as a bonus, we also get the origin where we should draw, encoded in the Rect.
   ATSUMeasureTextImage( mLayout, 0, 1, 0, 0, &imageRect);
   U32 xFudge = 2;
   U32 yFudge = 1;
   c.width  = imageRect.right - imageRect.left + xFudge; // add 2 because small fonts don't always have enough room
   c.height = imageRect.bottom - imageRect.top + yFudge;
   c.xOrigin = imageRect.left; // dist x0 -> center line
   c.yOrigin = -imageRect.top; // dist y0 -> base line
   
   // kick out early if the character is undrawable
   if( c.width == xFudge || c.height == yFudge)
      return c;
   
   // allocate a greyscale bitmap and clear it.
   bitmapDataSize = c.width * c.height;
   c.bitmapData = new U8[bitmapDataSize];
   dMemset(c.bitmapData,0x00,bitmapDataSize);
   
   // get a graphics context on the bitmap
   imageCtx = CGBitmapContextCreate( c.bitmapData, c.width, c.height, 8, c.width, mColorSpace, kCGImageAlphaNone);
   if(!imageCtx) {
      Con::errorf("Error: failed to create a graphics context on the CharInfo bitmap! Drawing a blank block.");
      c.xIncrement = c.width;
      dMemset(c.bitmapData,0x0F,bitmapDataSize);
      return c;
   }

   // Turn off antialiasing for monospaced console fonts. yes, this is cheating.
   if(mSize < 12  && ( dStrstr(mName,"Monaco")!=NULL || dStrstr(mName,"Courier")!=NULL ))
      CGContextSetShouldAntialias(imageCtx, false);

   // Set up drawing options for the context.
   // Since we're not going straight to the screen, we need to adjust accordingly
   CGContextSetShouldSmoothFonts(imageCtx, false);
   CGContextSetRenderingIntent(imageCtx, kCGRenderingIntentAbsoluteColorimetric);
   CGContextSetInterpolationQuality( imageCtx, kCGInterpolationNone);
   CGContextSetGrayFillColor( imageCtx, 1.0, 1.0);
   CGContextSetTextDrawingMode( imageCtx,  kCGTextFill);
   
   // tell ATSUI to substitute fonts as needed for missing glyphs
   ATSUSetTransientFontMatching(mLayout, true); 

   // set up three parrallel arrays for setting up attributes. 
   // this is how most options in ATSUI are set, by passing arrays of options.
   ATSUAttributeTag theTags[] = { kATSUCGContextTag };
   ByteCount theSizes[] = { sizeof(CGContextRef) };
   ATSUAttributeValuePtr theValues[] = { &imageCtx };
   
   // bind the layout to the context.
   ATSUSetLayoutControls( mLayout, 1, theTags, theSizes, theValues );

   // Draw the character!
   int yoff = c.height < 3 ? 1 : 0; // kludge for 1 pixel high characters, such as '-' and '_'
   int xoff = 1;
   err = ATSUDrawText( mLayout, 0, 1, IntToFixed(-imageRect.left + xoff), IntToFixed(imageRect.bottom + yoff ) );
   CGContextRelease(imageCtx);
   
   if(err != noErr) {
      Con::errorf("Error: could not draw the character! Drawing a blank box.");
      dMemset(c.bitmapData,0x0F,bitmapDataSize);
   }


#if TORQUE_DEBUG
//   Con::printf("Font Metrics: Rect = %2i %2i %2i %2i  Char= %C, 0x%x  Size= %i, Baseline= %i, Height= %i",imageRect.top, imageRect.bottom, imageRect.left, imageRect.right,ch,ch, mSize,mBaseline, mHeight);
//   Con::printf("Font Bounds:  left= %2i right= %2i  Char= %C, 0x%x  Size= %i",FixedToInt(tbefore), FixedToInt(tafter), ch,ch, mSize);
#endif
      
   return c;
}
Exemplo n.º 5
0
static bool osx_prepare_text(const void *p_text, uindex_t p_length, const MCGFont &p_font)
{
    OSStatus t_err;
	t_err = noErr;
    
	if (t_err == noErr)
		if (s_layout == NULL)
			t_err = ATSUCreateTextLayout(&s_layout);
	
	if (t_err == noErr)
		if (s_style == NULL)
			t_err = ATSUCreateStyle(&s_style);
    
    if (t_err == noErr)
        if (s_colour_space == NULL)
            s_colour_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
    
    if (t_err == noErr)
        if (s_colour == NULL)
        {
            // Components are grey and alpha
            const float t_colour_components[] = {1.0, 1.0};
            s_colour = CGColorCreate(s_colour_space, t_colour_components);
        }
	
	ATSUFontID t_font_id;
	Fixed t_font_size;
	Boolean t_font_is_italic;
	ATSUTextMeasurement t_imposed_width;
    if (t_err == noErr)
    {
        t_font_size = p_font . size << 16;
		
		// MM-2013-09-16: [[ Bug 11283 ]] It appears that ATSUI doesn't like italic being passed as a style parameter to ATSUFONDtoFontID.
		//   Instead, set italic as an attribute tag.
		uint8_t t_style;
		t_style = p_font . style & ~italic;
		t_font_is_italic = p_font . style & italic;
		
		// MW-2013-12-05: [[ Bug 11535 ]] Set the imposed width to the fixed advance width
		//   if non-zero. Otherwise use the glyph advance.
		if (p_font . fixed_advance != 0)
			t_imposed_width = p_font . fixed_advance << 16;
		else
			t_imposed_width = kATSUseGlyphAdvance;
			
		// if the specified font can't be found, just use the default
		// MM-2013-09-16: [[ Bug 11283 ]] Do the same for font styles - if the font/style paring cannot be found, try font with no style.
		t_err = ATSUFONDtoFontID((short)(intptr_t)p_font . fid, t_style, &t_font_id);
		if (t_err != noErr)
			t_err = ATSUFONDtoFontID((short)(intptr_t)p_font . fid, 0, &t_font_id);
		if (t_err != noErr)
			t_err = ATSUFONDtoFontID(0, t_style, &t_font_id);
		if (t_err != noErr)
			t_err = ATSUFONDtoFontID(0, 0, &t_font_id);
    }
	ATSUAttributeTag t_tags[] =
	{
		kATSUFontTag,
		kATSUSizeTag,
		kATSUQDItalicTag,
		kATSUImposeWidthTag,
	};
	ByteCount t_sizes[] =
	{
		sizeof(ATSUFontID),
		sizeof(Fixed),
		sizeof(Boolean),
		sizeof(ATSUTextMeasurement),
	};
	ATSUAttributeValuePtr t_attrs[] =
	{
		&t_font_id,
		&t_font_size,
		&t_font_is_italic,
		&t_imposed_width,
	};
	if (t_err == noErr)
		t_err = ATSUSetAttributes(s_style, sizeof(t_tags) / sizeof(ATSUAttributeTag), t_tags, t_sizes, t_attrs);
	if (t_err == noErr)
		t_err = ATSUSetTextPointerLocation(s_layout, (const UniChar *) p_text, 0, p_length / 2, p_length / 2);
	if (t_err == noErr)
		t_err = ATSUSetRunStyle(s_layout, s_style, 0, p_length / 2);
	if (t_err == noErr)
		t_err = ATSUSetTransientFontMatching(s_layout, true);
	
	return t_err == noErr;
}
Exemplo n.º 6
0
static cairo_status_t
_cairo_atsui_font_text_to_glyphs(void		*abstract_font,
                                 const char     *utf8,
                                 cairo_glyph_t **glyphs,
				 int		*num_glyphs)
{
    cairo_atsui_font_t *font = abstract_font;
    size_t i;
    OSStatus err;
    ATSUTextLayout textLayout;
    ATSLayoutRecord *layoutRecords;
    ItemCount glyphCount;
    int charCount;
    UniChar *theText;
    cairo_status_t status;

    // liberal estimate of size
    charCount = strlen(utf8);

    if (charCount == 0) {
       *glyphs = NULL;
       *num_glyphs = 0;
       return CAIRO_STATUS_SUCCESS;
    }

    status = _cairo_utf8_to_utf16 (utf8, -1, &theText, &charCount);
    if (status)
	return status;

    err = ATSUCreateTextLayout(&textLayout);

    err = ATSUSetTextPointerLocation(textLayout,
                                     theText, 0, charCount, charCount);


    // Set the style for all of the text
    err = ATSUSetRunStyle(textLayout,
                          font->unscaled_style, kATSUFromTextBeginning, kATSUToTextEnd);

    // Get the glyphs from the text layout object
    err = ATSUDirectGetLayoutDataArrayPtrFromTextLayout(textLayout,
                                                        0,
                                                        kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,
                                                        (void *)
                                                        &layoutRecords,
                                                        &glyphCount);

    *num_glyphs = glyphCount - 1;


    *glyphs =
        (cairo_glyph_t *) malloc(*num_glyphs * (sizeof(cairo_glyph_t)));
    if (*glyphs == NULL) {
        return CAIRO_STATUS_NO_MEMORY;
    }

    for (i = 0; i < *num_glyphs; i++) {
        (*glyphs)[i].index = layoutRecords[i].glyphID;
        (*glyphs)[i].x = FixedToFloat(layoutRecords[i].realPos);
        (*glyphs)[i].y = 0;
    }


    free(theText);

    ATSUDirectReleaseLayoutDataArrayPtr(NULL,
                                        kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,
                                        (void *) &layoutRecords);

    ATSUDisposeTextLayout(textLayout);

    return CAIRO_STATUS_SUCCESS;
}
PsychError SCREENTextBounds(void) 
{
	//for debugging
	TextEncodingBase		textEncodingBase;
	TextEncodingVariant		textEncodingVariant;
	TextEncodingFormat		textEncodingFormat;
	
	///////
        PsychWindowRecordType           *winRec;
	char				*textCString;
	Str255				textPString;
	UniChar				*textUniString;
	OSStatus			callError;
	PsychRectType			resultPsychRect, resultPsychNormRect;
	ATSUTextLayout			textLayout;				//layout is a pointer to an opaque struct.
	int				stringLengthChars;
	int				uniCharBufferLengthElements, uniCharBufferLengthChars, uniCharBufferLengthBytes;
	ByteCount			uniCharStringLengthBytes;
	TextToUnicodeInfo		textToUnicodeInfo;
	TextEncoding			textEncoding;
	ATSUStyle			atsuStyle;
	Boolean				foundFont;
	
	//for ATSU  style attributes
	PsychFontStructPtrType  psychFontRecord;

    			
        //all subfunctions should have these two lines.  
        PsychPushHelp(useString, synopsisString, seeAlsoString);
        if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
    
        //check for correct the number of arguments before getting involved
        PsychErrorExit(PsychCapNumInputArgs(2));   	
        PsychErrorExit(PsychRequireNumInputArgs(2)); 	
        PsychErrorExit(PsychCapNumOutputArgs(2));
	
	//get the window pointer and the text string and check that the window record has a font set
        PsychAllocInWindowRecordArg(1, kPsychArgRequired, &winRec);
	foundFont=PsychGetFontRecordFromFontNumber(winRec->textAttributes.textFontNumber, &psychFontRecord);
	if(!foundFont)
		PsychErrorExitMsg(PsychError_user, "Attempt to determine the bounds of text with no font or invalid font number");
		//it would be better to both prevent the user from setting invalid font numbers and init to the OS 9  default font.
	
	//read in the string and get its length and convert it to a unicode string.
	PsychAllocInCharArg(2, kPsychArgRequired, &textCString);
	stringLengthChars=strlen(textCString);
	if(stringLengthChars > 255)
		PsychErrorExitMsg(PsychError_unimplemented, "Cut corners and TextBounds will not accept a string longer than 255 characters");
	CopyCStringToPascal(textCString, textPString);
	uniCharBufferLengthChars= stringLengthChars * CHAR_TO_UNICODE_LENGTH_FACTOR;
	uniCharBufferLengthElements= uniCharBufferLengthChars + 1;		
	uniCharBufferLengthBytes= sizeof(UniChar) * uniCharBufferLengthElements;
	textUniString=(UniChar*)malloc(uniCharBufferLengthBytes);
	//Using a TextEncoding type describe the encoding of the text to be converteed.  
	textEncoding=CreateTextEncoding(kTextEncodingMacRoman, kMacRomanDefaultVariant, kTextEncodingDefaultFormat);
	//Take apart the encoding we just made to check it:
        textEncodingBase=GetTextEncodingBase(textEncoding);
        textEncodingVariant=GetTextEncodingVariant(textEncoding);
        textEncodingFormat=GetTextEncodingFormat(textEncoding);
	//Create a structure holding conversion information from the text encoding type we just created.
	callError=CreateTextToUnicodeInfoByEncoding(textEncoding,&textToUnicodeInfo);
	//Convert the text to a unicode string
	callError=ConvertFromPStringToUnicode(textToUnicodeInfo, textPString, (ByteCount)uniCharBufferLengthBytes,	&uniCharStringLengthBytes,	textUniString);
	//create the text layout object
	callError=ATSUCreateTextLayout(&textLayout);			
	//associate our unicode text string with the text layout object
	callError=ATSUSetTextPointerLocation(textLayout, textUniString, kATSUFromTextBeginning, kATSUToTextEnd, (UniCharCount)stringLengthChars);
	
	//create an ATSU style object
	callError=ATSUCreateStyle(&atsuStyle);
	callError=ATSUClearStyle(atsuStyle);
	
	//Not that we have a style object we have to set style charactersitics.  These include but are more general than Font Manager styles.  
	//ATSU Style objects have three sets of characteristics:  attributes, variations, and features.
	//attributes are things we need to set to match OS 9 behavior, such as the font ID, size, boldness, and italicization.
	//features are esoteric settings which we don't need for reproducing OS 9 behavior.  Whatever clearstyle sets should be fine.
	//font variations are axes of variation through the space of font characteristics.  The font definition includes available axes of variation.  Something else we can ignore for now.  
	PsychSetATSUStyleAttributesFromPsychWindowRecord(atsuStyle, winRec);
	//don't bother to set the variations of the style.
	//don't bother to set the features of the style.
	
	//associate the style with our layout object. This call assigns a style to every character of the string to be displayed.  
	callError=ATSUSetRunStyle(textLayout, atsuStyle, (UniCharArrayOffset)0, (UniCharCount)stringLengthChars);

        //Get the bounds for our text so that and create a texture of sufficient size to containt it. 
        ATSTrapezoid trapezoid;
        ItemCount oActualNumberOfBounds = 0;
        callError=ATSUGetGlyphBounds(textLayout, 0, 0, kATSUFromTextBeginning, kATSUToTextEnd, kATSUseDeviceOrigins, 0, NULL, &oActualNumberOfBounds);
        if (callError || oActualNumberOfBounds!=1) {
            PsychErrorExitMsg(PsychError_internal, "Failed to compute bounding box in call 1 to ATSUGetGlyphBounds() (nrbounds!=1)\n");    
        }
        callError=ATSUGetGlyphBounds(textLayout, 0, 0, kATSUFromTextBeginning, kATSUToTextEnd, kATSUseDeviceOrigins, 1, &trapezoid, &oActualNumberOfBounds);
        if (callError || oActualNumberOfBounds!=1) {
            PsychErrorExitMsg(PsychError_internal, "Failed to retrieve bounding box in call 2 to ATSUGetGlyphBounds() (nrbounds!=1)\n");    
        }
        
        resultPsychRect[kPsychLeft]=(Fix2X(trapezoid.upperLeft.x) < Fix2X(trapezoid.lowerLeft.x)) ? Fix2X(trapezoid.upperLeft.x) : Fix2X(trapezoid.lowerLeft.x);
        resultPsychRect[kPsychRight]=(Fix2X(trapezoid.upperRight.x) > Fix2X(trapezoid.lowerRight.x)) ? Fix2X(trapezoid.upperRight.x) : Fix2X(trapezoid.lowerRight.x);
        resultPsychRect[kPsychTop]=(Fix2X(trapezoid.upperLeft.y) < Fix2X(trapezoid.upperRight.y)) ? Fix2X(trapezoid.upperLeft.y) : Fix2X(trapezoid.upperRight.y);
        resultPsychRect[kPsychBottom]=(Fix2X(trapezoid.lowerLeft.y) > Fix2X(trapezoid.lowerRight.y)) ? Fix2X(trapezoid.lowerLeft.y) : Fix2X(trapezoid.lowerRight.y);

	PsychNormalizeRect(resultPsychRect, resultPsychNormRect);

	PsychCopyOutRectArg(1, FALSE, resultPsychNormRect);
	PsychCopyOutRectArg(2, FALSE, resultPsychRect);


	//release resources
	free((void*)textUniString);
	callError=ATSUDisposeStyle(atsuStyle);

    return(PsychError_none);
}
Exemplo n.º 8
0
static
OSStatus HandleKeyEvent(
	EventHandlerCallRef inHandlerCallRef,
	EventRef 			inEvent, 
	void 				*inUserData )
{
#pragma unused( inHandlerCallRef )

	OSStatus			err;
	EventRef			keyboardEvent;
	ByteCount			dataSize;
	UniCharCount		numNewCharacters;
	DrawContextStruct	*context = inUserData;
	
	// get the keyboard event
	err = GetEventParameter( inEvent, kEventParamTextInputSendKeyboardEvent,
		typeEventRef, NULL, sizeof( EventRef ), NULL, &keyboardEvent );
	require_noerr( err, HandleKeyEvent_err );
	
	// get the data size of the event
	err = GetEventParameter( keyboardEvent, kEventParamKeyUnicodes,
		typeUnicodeText, NULL, 0, &dataSize, NULL );
	require_noerr( err, HandleKeyEvent_err );
	
	// calculate the number of characters based on the data size
	numNewCharacters = dataSize / sizeof( UniChar );
	
	// check to see if the buffer is large enough to fit the key
	if ( context->textBufferSize < ( context->characterCount + 
		numNewCharacters ) )
	{
		ByteCount newSize;
	
		// nope. We need to resize the buffer. First, we need to pick a
		// size.
		if ( context->textBufferSize == 0 )
		{
			// the buffer has never been allocated. So, set it to a default
			// size plus whatever we got
			newSize = dataSize + kDefaultUnicodeBufferSize;
		}
		else
		{
			// the buffer has been allocated, so just double it's current
			// size. This is actually one of the most efficient ways to
			// use realloc
			newSize = dataSize + (context->textBufferSize * 2);
		}
		
		// allocate the new buffer
		context->textBuffer = realloc( context->textBuffer, newSize );
		require_action( context->textBuffer != NULL, HandleKeyEvent_err,
			err = memFullErr );
			
		// reset the text pointer, if there is one
		if ( context->layoutObject != NULL ) {
			err = ATSUSetTextPointerLocation( context->layoutObject, 
				context->textBuffer, kATSUFromTextBeginning, 
				kATSUToTextEnd, context->characterCount );
			require_noerr( err, HandleKeyEvent_err );
		}
			
		// set the new size in the buffer size
		context->textBufferSize = newSize / sizeof( UniChar );
	}
	
	// add the keys to the buffer from the event
	err = GetEventParameter( keyboardEvent, kEventParamKeyUnicodes,
		typeUnicodeText, NULL, dataSize, NULL,
		(void*) ( context->textBuffer + context->characterCount ) );
	require_noerr( err, HandleKeyEvent_err );
		
	// if the layout's already been allocated, then add the characters
	// to it. Otherwise, do nothing and let the draw function handle
	// everything
	if ( context->layoutObject != NULL ) {
		
		// insert the text into the layout
		err = ATSUTextInserted( context->layoutObject,
			context->characterCount, numNewCharacters );
		require_noerr( err, HandleKeyEvent_err );
	}
	
	// increment the character count stored in the context
	context->characterCount += numNewCharacters;

	// send message to draw the context
	HIViewSetNeedsDisplay( context->viewRef, true);


HandleKeyEvent_err:

	return err;

}
Exemplo n.º 9
0
bool QFontEngineMacMulti::stringToCMapInternal(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs,
                                               QTextEngine::ShaperFlags flags, QShaperItem *shaperItem) const
{
    //qDebug() << "stringToCMap" << QString(str, len);

    OSStatus e = noErr;

    e = ATSUSetTextPointerLocation(textLayout, (UniChar *)(str), 0, len, len);
    if (e != noErr) {
        qWarning("Qt: internal: %ld: Error ATSUSetTextPointerLocation %s: %d", long(e), __FILE__, __LINE__);
        return false;
    }

    QGlyphLayoutInfo nfo;
    nfo.glyphs = glyphs;
    nfo.numGlyphs = nglyphs;
    nfo.callbackCalled = false;
    nfo.flags = flags;
    nfo.shaperItem = shaperItem;

    QVarLengthArray<int> mappedFonts(len);
    for (int i = 0; i < len; ++i)
        mappedFonts[i] = 0;
    nfo.mappedFonts = mappedFonts.data();

    Q_ASSERT(sizeof(void *) <= sizeof(URefCon));
    e = ATSUSetTextLayoutRefCon(textLayout, (URefCon)&nfo);
    if (e != noErr) {
        qWarning("Qt: internal: %ld: Error ATSUSetTextLayoutRefCon %s: %d", long(e), __FILE__, __LINE__);
        return false;
    }

    {
        const int maxAttributeCount = 3;
        ATSUAttributeTag tags[maxAttributeCount + 1];
        ByteCount sizes[maxAttributeCount + 1];
        ATSUAttributeValuePtr values[maxAttributeCount + 1];
        int attributeCount = 0;

        tags[attributeCount] = kATSULineLayoutOptionsTag;
        ATSLineLayoutOptions layopts = kATSLineHasNoOpticalAlignment
                                       | kATSLineIgnoreFontLeading
                                       | kATSLineNoSpecialJustification // we do kashidas ourselves
                                       | kATSLineDisableAllJustification
                                       ;

        if (!(flags & QTextEngine::DesignMetrics)) {
            layopts |= kATSLineFractDisable | kATSLineUseDeviceMetrics
                       | kATSLineDisableAutoAdjustDisplayPos;
        }

        if (fontDef.styleStrategy & QFont::NoAntialias)
            layopts |= kATSLineNoAntiAliasing;

        if (!kerning)
            layopts |= kATSLineDisableAllKerningAdjustments;

        values[attributeCount] = &layopts;
        sizes[attributeCount] = sizeof(layopts);
        ++attributeCount;

        tags[attributeCount] = kATSULayoutOperationOverrideTag;
        ATSULayoutOperationOverrideSpecifier spec;
        spec.operationSelector = kATSULayoutOperationPostLayoutAdjustment;
        spec.overrideUPP = atsuPostLayoutCallback;
        values[attributeCount] = &spec;
        sizes[attributeCount] = sizeof(spec);
        ++attributeCount;

        Boolean direction;
        if (flags & QTextEngine::RightToLeft)
            direction = kATSURightToLeftBaseDirection;
        else
            direction = kATSULeftToRightBaseDirection;
        tags[attributeCount] = kATSULineDirectionTag;
        values[attributeCount] = &direction;
        sizes[attributeCount] = sizeof(direction);
        ++attributeCount;

        Q_ASSERT(attributeCount < maxAttributeCount + 1);
        e = ATSUSetLayoutControls(textLayout, attributeCount, tags, sizes, values);
        if (e != noErr) {
            qWarning("Qt: internal: %ld: Error ATSUSetLayoutControls %s: %d", long(e), __FILE__, __LINE__);
            return false;
        }

    }

    e = ATSUSetRunStyle(textLayout, style, 0, len);
    if (e != noErr) {
        qWarning("Qt: internal: %ld: Error ATSUSetRunStyle %s: %d", long(e), __FILE__, __LINE__);
        return false;
    }

    if (!(fontDef.styleStrategy & QFont::NoFontMerging)) {
        int pos = 0;
        do {
            ATSUFontID substFont = 0;
            UniCharArrayOffset changedOffset = 0;
            UniCharCount changeCount = 0;

            e = ATSUMatchFontsToText(textLayout, pos, len - pos,
                                     &substFont, &changedOffset,
                                     &changeCount);
            if (e == kATSUFontsMatched) {
                int fontIdx = fontIndexForFontID(substFont);
                for (uint i = 0; i < changeCount; ++i)
                    mappedFonts[changedOffset + i] = fontIdx;
                pos = changedOffset + changeCount;
                ATSUSetRunStyle(textLayout, engineAt(fontIdx)->style, changedOffset, changeCount);
            } else if (e == kATSUFontsNotMatched) {
                pos = changedOffset + changeCount;
            }
        } while (pos < len && e != noErr);
    }
    {    // trigger the a layout
        Rect rect;
        e = ATSUMeasureTextImage(textLayout, kATSUFromTextBeginning, kATSUToTextEnd,
                                 /*iLocationX =*/ 0, /*iLocationY =*/ 0,
                                 &rect);
        if (e != noErr) {
            qWarning("Qt: internal: %ld: Error ATSUMeasureTextImage %s: %d", long(e), __FILE__, __LINE__);
            return false;
        }
    }

    if (!nfo.callbackCalled) {
            qWarning("Qt: internal: %ld: Error ATSUMeasureTextImage did not trigger callback %s: %d", long(e), __FILE__, __LINE__);
            return false;
    }

    ATSUClearLayoutCache(textLayout, kATSUFromTextBeginning);
    return true;
}
Exemplo n.º 10
0
PsychError SCREENTextBounds(void) 
{
	//for debugging
	TextEncodingBase		textEncodingBase;
	TextEncodingVariant		textEncodingVariant;
	TextEncodingFormat		textEncodingFormat;
	
	///////
        PsychWindowRecordType           *winRec;
	char				*textCString;
	Str255				textPString;
	UniChar				*textUniString;
	OSStatus			callError;
	PsychRectType			resultPsychRect, resultPsychNormRect;
	ATSUTextLayout			textLayout;				//layout is a pointer to an opaque struct.
	int				stringLengthChars;
	int				uniCharBufferLengthElements, uniCharBufferLengthChars, uniCharBufferLengthBytes, yPositionIsBaseline;
	double			textHeightToBaseline;
	ByteCount			uniCharStringLengthBytes;
	TextToUnicodeInfo		textToUnicodeInfo;
	TextEncoding			textEncoding;
	ATSUStyle			atsuStyle;
	Boolean				foundFont;
	
	//for ATSU  style attributes
	PsychFontStructPtrType  psychFontRecord;

    			
        //all subfunctions should have these two lines.  
        PsychPushHelp(useString, synopsisString, seeAlsoString);
        if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
    
        //check for correct the number of arguments before getting involved
        PsychErrorExit(PsychCapNumInputArgs(5));   	
        PsychErrorExit(PsychRequireNumInputArgs(2)); 	
        PsychErrorExit(PsychCapNumOutputArgs(2));
	
	//get the window pointer and the text string and check that the window record has a font set
        PsychAllocInWindowRecordArg(1, kPsychArgRequired, &winRec);
	foundFont=PsychGetFontRecordFromFontNumber(winRec->textAttributes.textFontNumber, &psychFontRecord);
	if(!foundFont)
		PsychErrorExitMsg(PsychError_user, "Attempt to determine the bounds of text with no font or invalid font number");
		//it would be better to both prevent the user from setting invalid font numbers and init to the OS 9  default font.
	
	//read in the string and get its length and convert it to a unicode string.
	PsychAllocInCharArg(2, kPsychArgRequired, &textCString);
	stringLengthChars=strlen(textCString);
	if(stringLengthChars < 1) PsychErrorExitMsg(PsychError_user, "You asked me to compute the bounding box of an empty text string?!? Sorry, that's a no no...");
	if(stringLengthChars > 255) PsychErrorExitMsg(PsychError_unimplemented, "Cut corners and TextBounds will not accept a string longer than 255 characters");
	CopyCStringToPascal(textCString, textPString);
	uniCharBufferLengthChars= stringLengthChars * CHAR_TO_UNICODE_LENGTH_FACTOR;
	uniCharBufferLengthElements= uniCharBufferLengthChars + 1;		
	uniCharBufferLengthBytes= sizeof(UniChar) * uniCharBufferLengthElements;
	textUniString=(UniChar*)malloc(uniCharBufferLengthBytes);

    PsychCopyInDoubleArg(3, kPsychArgOptional, &(winRec->textAttributes.textPositionX));
    PsychCopyInDoubleArg(4, kPsychArgOptional, &(winRec->textAttributes.textPositionY));

	//Using a TextEncoding type describe the encoding of the text to be converteed.  
	textEncoding=CreateTextEncoding(kTextEncodingMacRoman, kMacRomanDefaultVariant, kTextEncodingDefaultFormat);
	//Take apart the encoding we just made to check it:
        textEncodingBase=GetTextEncodingBase(textEncoding);
        textEncodingVariant=GetTextEncodingVariant(textEncoding);
        textEncodingFormat=GetTextEncodingFormat(textEncoding);
	//Create a structure holding conversion information from the text encoding type we just created.
	callError=CreateTextToUnicodeInfoByEncoding(textEncoding,&textToUnicodeInfo);
	//Convert the text to a unicode string
	callError=ConvertFromPStringToUnicode(textToUnicodeInfo, textPString, (ByteCount)uniCharBufferLengthBytes,	&uniCharStringLengthBytes,	textUniString);
	//create the text layout object
	callError=ATSUCreateTextLayout(&textLayout);			
	//associate our unicode text string with the text layout object
	callError=ATSUSetTextPointerLocation(textLayout, textUniString, kATSUFromTextBeginning, kATSUToTextEnd, (UniCharCount)stringLengthChars);
	
	//create an ATSU style object
	callError=ATSUCreateStyle(&atsuStyle);
	callError=ATSUClearStyle(atsuStyle);
	
	//Not that we have a style object we have to set style charactersitics.  These include but are more general than Font Manager styles.  
	//ATSU Style objects have three sets of characteristics:  attributes, variations, and features.
	//attributes are things we need to set to match OS 9 behavior, such as the font ID, size, boldness, and italicization.
	//features are esoteric settings which we don't need for reproducing OS 9 behavior.  Whatever clearstyle sets should be fine.
	//font variations are axes of variation through the space of font characteristics.  The font definition includes available axes of variation.  Something else we can ignore for now.  
	PsychSetATSUStyleAttributesFromPsychWindowRecord(atsuStyle, winRec);
	//don't bother to set the variations of the style.
	//don't bother to set the features of the style.
	
	//associate the style with our layout object. This call assigns a style to every character of the string to be displayed.  
	callError=ATSUSetRunStyle(textLayout, atsuStyle, (UniCharArrayOffset)0, (UniCharCount)stringLengthChars);

	// Define the meaning of the y position of the specified drawing cursor.
	// We get the global setting from the Screen preference, but allow to override
	// it on a per-invocation basis via the optional 7th argument to 'DrawText':
	yPositionIsBaseline = PsychPrefStateGet_TextYPositionIsBaseline();
	PsychCopyInIntegerArg(5, kPsychArgOptional, &yPositionIsBaseline);
	 
	if (yPositionIsBaseline) {
		// Y position of drawing cursor defines distance between top of text and
		// baseline of text, i.e. the textheight excluding descenders of letters:

		// Need to compute offset via ATSU:
		ATSUTextMeasurement mleft, mright, mtop, mbottom;
        callError=ATSUGetUnjustifiedBounds(textLayout, kATSUFromTextBeginning, kATSUToTextEnd, &mleft, &mright, &mbottom, &mtop);
		if (callError) {
			PsychErrorExitMsg(PsychError_internal, "Failed to compute unjustified text height to baseline in call to ATSUGetUnjustifiedBounds().\n");    
		}

		// Only take height including ascenders into account, not the descenders.
		// MK: Honestly, i have no clue why this is the correct calculation (or if it is
		// the correct calculation), but visually it seems to provide the correct results
		// and i'm not a typographic expert and don't intend to become one...
		textHeightToBaseline = fabs(Fix2X(mbottom));
	}
	else {
		// Y position of drawing cursor defines top of text, therefore no offset (==0) needed:
		textHeightToBaseline = 0;
	}

	//Get the bounds for our text so that and create a texture of sufficient size to containt it. 
	ATSTrapezoid trapezoid;
	ItemCount oActualNumberOfBounds = 0;
	callError=ATSUGetGlyphBounds(textLayout, 0, 0, kATSUFromTextBeginning, kATSUToTextEnd, kATSUseDeviceOrigins, 0, NULL, &oActualNumberOfBounds);
	if (callError || oActualNumberOfBounds!=1) {
		PsychErrorExitMsg(PsychError_internal, "Failed to compute bounding box in call 1 to ATSUGetGlyphBounds() (nrbounds!=1)\n");    
	}
	callError=ATSUGetGlyphBounds(textLayout, 0, 0, kATSUFromTextBeginning, kATSUToTextEnd, kATSUseDeviceOrigins, 1, &trapezoid, &oActualNumberOfBounds);
	if (callError || oActualNumberOfBounds!=1) {
		PsychErrorExitMsg(PsychError_internal, "Failed to retrieve bounding box in call 2 to ATSUGetGlyphBounds() (nrbounds!=1)\n");    
	}
	
	resultPsychRect[kPsychLeft]=(Fix2X(trapezoid.upperLeft.x) < Fix2X(trapezoid.lowerLeft.x)) ? Fix2X(trapezoid.upperLeft.x) : Fix2X(trapezoid.lowerLeft.x);
	resultPsychRect[kPsychRight]=(Fix2X(trapezoid.upperRight.x) > Fix2X(trapezoid.lowerRight.x)) ? Fix2X(trapezoid.upperRight.x) : Fix2X(trapezoid.lowerRight.x);
	resultPsychRect[kPsychTop]=(Fix2X(trapezoid.upperLeft.y) < Fix2X(trapezoid.upperRight.y)) ? Fix2X(trapezoid.upperLeft.y) : Fix2X(trapezoid.upperRight.y);
	resultPsychRect[kPsychBottom]=(Fix2X(trapezoid.lowerLeft.y) > Fix2X(trapezoid.lowerRight.y)) ? Fix2X(trapezoid.lowerLeft.y) : Fix2X(trapezoid.lowerRight.y);
	
	PsychNormalizeRect(resultPsychRect, resultPsychNormRect);
	resultPsychRect[kPsychLeft]=resultPsychNormRect[kPsychLeft] + winRec->textAttributes.textPositionX;
	resultPsychRect[kPsychRight]=resultPsychNormRect[kPsychRight] + winRec->textAttributes.textPositionX;
	resultPsychRect[kPsychTop]=resultPsychNormRect[kPsychTop] + winRec->textAttributes.textPositionY - textHeightToBaseline;
	resultPsychRect[kPsychBottom]=resultPsychNormRect[kPsychBottom] + winRec->textAttributes.textPositionY - textHeightToBaseline;

	PsychCopyOutRectArg(1, FALSE, resultPsychNormRect);
	PsychCopyOutRectArg(2, FALSE, resultPsychRect);


	//release resources
	free((void*)textUniString);
	callError=ATSUDisposeStyle(atsuStyle);

    return(PsychError_none);
}
Exemplo n.º 11
0
void
DoAtsuiLayout(void* p, int justify)
{
	memoryword*	node = (memoryword*)p;

	unsigned	f = native_font(node);
	if (fontarea[f] != AAT_FONT_FLAG) {
		fprintf(stderr, "internal error: do_atsui_layout called for non-ATSUI font\n");
		exit(1);
	}

	if (sTextLayout == 0)
		InitializeLayout();

	OSStatus	status = noErr;
	
	long		txtLen = native_length(node);
	const UniChar*	txtPtr = (UniChar*)(node + native_node_size);

	status = ATSUSetTextPointerLocation(sTextLayout, txtPtr, 0, txtLen, txtLen);
	
	// we're using this font in AAT mode, so fontlayoutengine[f] is actually an ATSUStyle
	ATSUStyle	style = (ATSUStyle)(fontlayoutengine[native_font(node)]);
	status = ATSUSetRunStyle(sTextLayout, style, 0, txtLen);

	ATSUAttributeTag		tags[] = { kATSULineWidthTag, kATSULineJustificationFactorTag };
	ItemCount				numTags = sizeof(tags) / sizeof(ATSUAttributeTag);
	if (justify) {
		ByteCount				valSizes[] = { sizeof(Fixed), sizeof(Fract) };
		Fixed					wid = FixedTeXtoPSPoints(node_width(node));
		Fract					just = fract1;
		ATSUAttributeValuePtr	valPtrs[] = { &wid, &just };
		status = ATSUSetLayoutControls(sTextLayout, numTags, tags, valSizes, valPtrs);
	}
	
	ItemCount	count;
	ATSLayoutRecord*	layoutRec = NULL;
	status = ATSUDirectGetLayoutDataArrayPtrFromTextLayout(sTextLayout, 0,
		kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, (void*)&layoutRec, &count);

	int i;
	int	realGlyphCount = 0;
	int lastRealGlyph = 0;
	for (i = 0; i < count; ++i)
		if (layoutRec[i].glyphID < 0xfffe) {
			lastRealGlyph = i;
			++realGlyphCount;
		}

	void*		glyph_info = xmalloc(realGlyphCount * native_glyph_info_size);
	FixedPoint*	locations = (FixedPoint*)glyph_info;
	UInt16*		glyphIDs = (UInt16*)(locations + realGlyphCount);

	Fixed		lsUnit = justify ? 0 : fontletterspace[f];
	Fixed		lsDelta = 0;

	realGlyphCount = 0;
	for (i = 0; i < count; ++i) {
		if (layoutRec[i].glyphID < 0xfffe) {
			if ((layoutRec[i].flags & kATSGlyphInfoIsAttachment) && (lsDelta != 0))
				lsDelta -= lsUnit;
			glyphIDs[realGlyphCount] = layoutRec[i].glyphID;
			locations[realGlyphCount].y = 0;	/* FIXME: won't handle baseline offsets */
			locations[realGlyphCount].x = FixedPStoTeXPoints(layoutRec[i].realPos) + lsDelta;
			lsDelta += lsUnit;
			++realGlyphCount;
		}
	}
	if (lsDelta != 0)
		lsDelta -= lsUnit;

	native_glyph_count(node) = realGlyphCount;
	native_glyph_info_ptr(node) = glyph_info;
	
	if (!justify)
		node_width(node) = FixedPStoTeXPoints(layoutRec[count-1].realPos) + lsDelta;

	ATSUDirectReleaseLayoutDataArrayPtr(NULL, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, (void*)&layoutRec);

	if (justify)
		ATSUClearLayoutControls(sTextLayout, numTags, tags);
}