//------------------------------------------------------------------------ // GetBoundingMetrics // //------------------------------------------------------------------------ nsresult nsATSUIToolkit::GetBoundingMetrics( const PRUnichar *aCharPt, PRUint32 aLen, nsBoundingMetrics &oBoundingMetrics, short aSize, short aFontNum, PRBool aBold, PRBool aItalic, nscolor aColor) { if(!nsATSUIUtils::IsAvailable()) return NS_ERROR_NOT_INITIALIZED; StPortSetter setter(mPort); ATSUTextLayout aTxtLayout; StartDraw(aCharPt, aLen, aSize, aFontNum, aBold, aItalic, aColor, aTxtLayout); if(nsnull == aTxtLayout) return NS_ERROR_FAILURE; OSStatus err = noErr; Rect rect; ATSUTextMeasurement width; if((err = ATSUMeasureTextImage(aTxtLayout, kATSUFromTextBeginning, kATSUToTextEnd, 0, 0, &rect)) != noErr) { NS_WARNING("ATSUMeasureTextImage failed"); return NS_ERROR_FAILURE; } // return the values in points, the caller will convert them into twips oBoundingMetrics.leftBearing = rect.left; oBoundingMetrics.rightBearing = rect.right; oBoundingMetrics.ascent = -rect.top; oBoundingMetrics.descent = rect.bottom; err = ::ATSUGetUnjustifiedBounds(aTxtLayout, kATSUFromTextBeginning, kATSUToTextEnd, NULL, &width, NULL, NULL); if (err != noErr) { oBoundingMetrics.width = oBoundingMetrics.rightBearing; } else oBoundingMetrics.width = FixRound(width); return NS_OK; }
oop ATSUMeasureTextImage_wrap( ATSUTextLayout iTextLayout, int iLineStart, // uint32 int iLineLength, // uint32 void* FH ) { ATSUTextMeasurement oTextBefore, oTextAfter, oAscent, oDescent; Rect rect; OSStatus e = ATSUMeasureTextImage( iTextLayout, (uint32)iLineStart, (uint32)iLineLength, 0, 0, &rect); if (e != noErr) { return (oop)reportOSError(e, "ATSUMeasureTextImage", FH); } objVectorOop r = Memory->objVectorObj->cloneSize(4); r->obj_at_put(0, as_smiOop(rect.left), false); r->obj_at_put(1, as_smiOop(rect.top), false); r->obj_at_put(2, as_smiOop(rect.right), false); r->obj_at_put(3, as_smiOop(rect.bottom), false); return r; }
// MM-2013-10-23: [[ RefactorGraphics ]] Move over to using (a fudged) ATSUMeasureTextImage when calculating the bounds. // Using the ATSUGetUnjustifiedBounds for the width and the fonts ascent/descent for the height was causing clipping issues for certain fonts. static bool osx_measure_text_substring_bounds(uindex_t p_length, MCGIntRectangle &r_bounds) { if (s_layout == NULL || s_style == NULL) return false; OSStatus t_err; t_err = noErr; Rect t_bounds; if (t_err == noErr) t_err = ATSUMeasureTextImage(s_layout, 0, p_length / 2, 0, 0, &t_bounds); if (t_err == noErr) { r_bounds . x = t_bounds . left - kMCGMeasureTextFudge; r_bounds . y = t_bounds . top - kMCGMeasureTextFudge; r_bounds . width = t_bounds . right - t_bounds . left + 2 * kMCGMeasureTextFudge; r_bounds . height = t_bounds . bottom - t_bounds . top + 2 * kMCGMeasureTextFudge; } return t_err == noErr; }
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; }
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; }