static void _cairo_atsui_font_fini(void *abstract_font) { cairo_atsui_font_t *font = abstract_font; if (font == NULL) return; if (font->style) ATSUDisposeStyle(font->style); if (font->unscaled_style) ATSUDisposeStyle(font->unscaled_style); }
MacCarbFont::~MacCarbFont() { // apple docs say we should dispose the layout first. ATSUDisposeTextLayout(mLayout); ATSUDisposeStyle(mStyle); CGColorSpaceRelease(mColorSpace); }
QFontEngineMacMulti::~QFontEngineMacMulti() { ATSUDisposeTextLayout(textLayout); ATSUDisposeStyle(style); for (int i = 0; i < engines.count(); ++i) { QFontEngineMac *fe = const_cast<QFontEngineMac *>(static_cast<const QFontEngineMac *>(engines.at(i))); fe->multiEngine = 0; if (!fe->ref.deref()) delete fe; } engines.clear(); }
static OSStatus CreateStyleObjectWithFontName( char *fontName, Fixed *fontSize, RGBColor *fontColor, ATSUStyle *newStyle ) { OSStatus err; ATSUFontID fontID; ATSUAttributeTag tags[3]; ByteCount tagValueSizes[3]; ATSUAttributeValuePtr tagValuePtrs[3]; // try to find the font based on the font name err = ATSUFindFontFromName( fontName, strlen( fontName ), kFontFullName, kFontNoPlatform, kFontNoScript, kFontNoLanguage, &fontID ); require_noerr( err, CreateStyleObjectWithFontName_err ); // first, create the new style object err = ATSUCreateStyle( newStyle ); // set up the three tags that we are setting in the style tags[0] = kATSUFontTag; tagValueSizes[0] = sizeof( ATSUFontID ); tagValuePtrs[0] = &fontID; tags[1] = kATSUSizeTag; tagValueSizes[1] = sizeof( Fixed ); tagValuePtrs[1] = fontSize; tags[2] = kATSUColorTag; tagValueSizes[2] = sizeof( RGBColor ); tagValuePtrs[2] = fontColor; // set the attributes in the style object err = ATSUSetAttributes( *newStyle, 3, tags, tagValueSizes, tagValuePtrs ); check_noerr( err ); // if there was an error, then dispose of the style if ( err != noErr ) { ATSUDisposeStyle( *newStyle ); } CreateStyleObjectWithFontName_err: return err; }
static cairo_status_t _cairo_atsui_font_glyph_extents(void *abstract_font, cairo_glyph_t * glyphs, int num_glyphs, cairo_text_extents_t * extents) { cairo_atsui_font_t *font = abstract_font; OSStatus err; assert(num_glyphs == 1); GlyphID theGlyph = glyphs[0].index; ATSGlyphIdealMetrics metricsH, metricsV; ATSUStyle style; ATSUCreateAndCopyStyle(font->unscaled_style, &style); err = ATSUGlyphGetIdealMetrics(style, 1, &theGlyph, 0, &metricsH); ATSUVerticalCharacterType verticalType = kATSUStronglyVertical; const ATSUAttributeTag theTag[] = { kATSUVerticalCharacterTag }; const ByteCount theSizes[] = { sizeof(verticalType) }; ATSUAttributeValuePtr theValues[] = { &verticalType }; err = ATSUSetAttributes(style, 1, theTag, theSizes, theValues); err = ATSUGlyphGetIdealMetrics(style, 1, &theGlyph, 0, &metricsV); extents->x_bearing = metricsH.sideBearing.x; extents->y_bearing = metricsV.advance.y; extents->width = metricsH.advance.x - metricsH.sideBearing.x - metricsH.otherSideBearing.x; extents->height = -metricsV.advance.y - metricsV.sideBearing.y - metricsV.otherSideBearing.y; extents->x_advance = metricsH.advance.x; extents->y_advance = 0; ATSUDisposeStyle(style); return CAIRO_STATUS_SUCCESS; }
void QFontEngineMac::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics) { ATSUStyle unscaledStyle; ATSUCreateAndCopyStyle(style, &unscaledStyle); int emSquare = properties().emSquare.toInt(); const int maxAttributeCount = 4; ATSUAttributeTag tags[maxAttributeCount + 1]; ByteCount sizes[maxAttributeCount + 1]; ATSUAttributeValuePtr values[maxAttributeCount + 1]; int attributeCount = 0; Fixed size = FixRatio(emSquare, 1); tags[attributeCount] = kATSUSizeTag; sizes[attributeCount] = sizeof(size); values[attributeCount] = &size; ++attributeCount; Q_ASSERT(attributeCount < maxAttributeCount + 1); OSStatus err = ATSUSetAttributes(unscaledStyle, attributeCount, tags, sizes, values); Q_ASSERT(err == noErr); Q_UNUSED(err); GlyphID atsuGlyph = glyph; ATSGlyphScreenMetrics atsuMetrics; ATSUGlyphGetScreenMetrics(unscaledStyle, 1, &atsuGlyph, 0, /* iForcingAntiAlias =*/ false, /* iAntiAliasSwitch =*/true, &atsuMetrics); metrics->width = int(atsuMetrics.width); metrics->height = int(atsuMetrics.height); metrics->x = QFixed::fromReal(atsuMetrics.topLeft.x); metrics->y = -QFixed::fromReal(atsuMetrics.topLeft.y); metrics->xoff = QFixed::fromReal(atsuMetrics.deviceAdvance.x); metrics->yoff = QFixed::fromReal(atsuMetrics.deviceAdvance.y); QFixedPoint p; ::addGlyphsToPath(unscaledStyle, &glyph, &p, 1, path); ATSUDisposeStyle(unscaledStyle); }
OSStatus CreateATSUIStyleFromFontFullNameAndSize (const char *fontName, int fontSize, ATSUStyle *theStyle) { OSStatus err; ATSUStyle localStyle; ATSUFontID atsuFont; Fixed atsuSize; ATSUAttributeTag theTags[] = { kATSUFontTag, kATSUSizeTag }; ByteCount theSizes[] = { sizeof(ATSUFontID), sizeof(Fixed) }; ATSUAttributeValuePtr theValues[] = { NULL, NULL }; atsuFont = 0; atsuSize = FixRatio(fontSize, 1); localStyle = NULL; theValues[0] = &atsuFont; theValues[1] = &atsuSize; err = ATSUFindFontFromName(fontName, strlen(fontName), kFontFullName, kFontNoPlatform, kFontNoScript, kFontNoLanguage, &atsuFont); if (err == noErr) { err = ATSUCreateStyle(&localStyle); if (err == noErr) { err = ATSUSetAttributes(localStyle, sizeof(theTags) / sizeof(theTags[0]), theTags, theSizes, theValues); if (err == noErr) { *theStyle = localStyle; return (noErr); } } } if (localStyle != NULL) err = ATSUDisposeStyle(localStyle); return (err); }
// 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); }
void QFontDatabase::load(const QFontPrivate *d, int script) { // sanity checks if(!qApp) qWarning("QFont: Must construct a QApplication before a QFont"); Q_ASSERT(script >= 0 && script < QUnicodeTables::ScriptCount); Q_UNUSED(script); QFontDef req = d->request; req.pixelSize = qt_mac_pixelsize(req, d->dpi); // set the point size to 0 to get better caching req.pointSize = 0; QFontCache::Key key = QFontCache::Key(req, QUnicodeTables::Common, d->screen); if(!(d->engineData = QFontCache::instance()->findEngineData(key))) { d->engineData = new QFontEngineData; QFontCache::instance()->insertEngineData(key, d->engineData); } else { d->engineData->ref.ref(); } if(d->engineData->engine) // already loaded return; // set it to the actual pointsize, so QFontInfo will do the right thing req.pointSize = qRound(qt_mac_pointsize(d->request, d->dpi)); QFontEngine *e = QFontCache::instance()->findEngine(key); if(!e && qt_enable_test_font && req.family == QLatin1String("__Qt__Box__Engine__")) { e = new QTestFontEngine(req.pixelSize); e->fontDef = req; } if(e) { e->ref.ref(); d->engineData->engine = e; return; // the font info and fontdef should already be filled } //find the font QStringList family_list = familyList(req); const char *stylehint = styleHint(req); if (stylehint) family_list << QLatin1String(stylehint); // add QFont::defaultFamily() to the list, for compatibility with // previous versions family_list << QApplication::font().defaultFamily(); ATSFontFamilyRef familyRef = 0; ATSFontRef fontRef = 0; QMutexLocker locker(fontDatabaseMutex()); QFontDatabasePrivate *db = privateDb(); if (!db->count) initializeDb(); for(int i = 0; i < family_list.size(); ++i) { for (int k = 0; k < db->count; ++k) { if (db->families[k]->name.compare(family_list.at(i), Qt::CaseInsensitive) == 0) { QByteArray family_name = db->families[k]->name.toUtf8(); familyRef = ATSFontFamilyFindFromName(QCFString(db->families[k]->name), kATSOptionFlagsDefault); if (familyRef) { fontRef = ATSFontFindFromName(QCFString(db->families[k]->name), kATSOptionFlagsDefault); goto FamilyFound; } else { #if defined(QT_MAC_USE_COCOA) // ATS and CT disagrees on what the family name should be, // use CT to look up the font if ATS fails. QCFString familyName = QString::fromAscii(family_name); QCFType<CTFontRef> CTfontRef = CTFontCreateWithName(familyName, 12, NULL); QCFType<CTFontDescriptorRef> fontDescriptor = CTFontCopyFontDescriptor(CTfontRef); QCFString displayName = (CFStringRef)CTFontDescriptorCopyAttribute(fontDescriptor, kCTFontDisplayNameAttribute); familyRef = ATSFontFamilyFindFromName(displayName, kATSOptionFlagsDefault); if (familyRef) { fontRef = ATSFontFindFromName(displayName, kATSOptionFlagsDefault); goto FamilyFound; } #endif } } } } FamilyFound: //fill in the engine's font definition QFontDef fontDef = d->request; //copy.. if(fontDef.pointSize < 0) fontDef.pointSize = qt_mac_pointsize(fontDef, d->dpi); else fontDef.pixelSize = qt_mac_pixelsize(fontDef, d->dpi); #if 0 ItemCount name_count; if(ATSUCountFontNames(fontID, &name_count) == noErr && name_count) { ItemCount actualName_size; if(ATSUGetIndFontName(fontID, 0, 0, 0, &actualName_size, 0, 0, 0, 0) == noErr && actualName_size) { QByteArray actualName(actualName_size); if(ATSUGetIndFontName(fontID, 0, actualName_size, actualName.data(), &actualName_size, 0, 0, 0, 0) == noErr && actualName_size) fontDef.family = QString::fromUtf8(actualName); } } #else { QCFString actualName; if(ATSFontFamilyGetName(familyRef, kATSOptionFlagsDefault, &actualName) == noErr) fontDef.family = actualName; } #endif #ifdef QT_MAC_USE_COCOA QFontEngine *engine = new QCoreTextFontEngineMulti(familyRef, fontRef, fontDef, d->kerning); #elif 1 QFontEngine *engine = new QFontEngineMacMulti(familyRef, fontRef, fontDef, d->kerning); #else ATSFontFamilyRef atsFamily = familyRef; ATSFontFamilyRef atsFontRef = fontRef; FMFont fontID; FMFontFamily fmFamily; FMFontStyle fntStyle = 0; fmFamily = FMGetFontFamilyFromATSFontFamilyRef(atsFamily); if (fmFamily == kInvalidFontFamily) { // Use the ATSFont then... fontID = FMGetFontFromATSFontRef(atsFontRef); } else { if (fontDef.weight >= QFont::Bold) fntStyle |= ::bold; if (fontDef.style != QFont::StyleNormal) fntStyle |= ::italic; FMFontStyle intrinsicStyle; FMFont fnt = 0; if (FMGetFontFromFontFamilyInstance(fmFamily, fntStyle, &fnt, &intrinsicStyle) == noErr) fontID = FMGetATSFontRefFromFont(fnt); } OSStatus status; const int maxAttributeCount = 5; ATSUAttributeTag tags[maxAttributeCount + 1]; ByteCount sizes[maxAttributeCount + 1]; ATSUAttributeValuePtr values[maxAttributeCount + 1]; int attributeCount = 0; Fixed size = FixRatio(fontDef.pixelSize, 1); tags[attributeCount] = kATSUSizeTag; sizes[attributeCount] = sizeof(size); values[attributeCount] = &size; ++attributeCount; tags[attributeCount] = kATSUFontTag; sizes[attributeCount] = sizeof(fontID); values[attributeCount] = &fontID; ++attributeCount; CGAffineTransform transform = CGAffineTransformIdentity; if (fontDef.stretch != 100) { transform = CGAffineTransformMakeScale(float(fontDef.stretch) / float(100), 1); tags[attributeCount] = kATSUFontMatrixTag; sizes[attributeCount] = sizeof(transform); values[attributeCount] = &transform; ++attributeCount; } ATSUStyle style; status = ATSUCreateStyle(&style); Q_ASSERT(status == noErr); Q_ASSERT(attributeCount < maxAttributeCount + 1); status = ATSUSetAttributes(style, attributeCount, tags, sizes, values); Q_ASSERT(status == noErr); QFontEngine *engine = new QFontEngineMac(style, fontID, fontDef, /*multiEngine*/ 0); ATSUDisposeStyle(style); #endif d->engineData->engine = engine; engine->ref.ref(); //a ref for the engineData->engine QFontCache::instance()->insertEngine(key, engine); }
// Disposes of the ATSUI data // void DisposeATSUIStuff(void) { verify_noerr( ATSUDisposeStyle(gStyle) ); free(gText); }
bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback) { const char *str; bool result = false; callback->FindMissingGlyphs(&str); #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) if (MacOSVersionIsAtLeast(10, 5, 0)) { /* Determine fallback font using CoreText. This uses the language isocode * to find a suitable font. CoreText is available from 10.5 onwards. */ char lang[16]; if (strcmp(language_isocode, "zh_TW") == 0) { /* Traditional Chinese */ strecpy(lang, "zh-Hant", lastof(lang)); } else if (strcmp(language_isocode, "zh_CN") == 0) { /* Simplified Chinese */ strecpy(lang, "zh-Hans", lastof(lang)); } else if (strncmp(language_isocode, "ur", 2) == 0) { /* The urdu alphabet is variant of persian. As OS X has no default * font that advertises an urdu language code, search for persian * support instead. */ strecpy(lang, "fa", lastof(lang)); } else { /* Just copy the first part of the isocode. */ strecpy(lang, language_isocode, lastof(lang)); char *sep = strchr(lang, '_'); if (sep != NULL) *sep = '\0'; } CFStringRef lang_code; lang_code = CFStringCreateWithCString(kCFAllocatorDefault, lang, kCFStringEncodingUTF8); /* Create a font iterator and iterate over all fonts that * are available to the application. */ ATSFontIterator itr; ATSFontRef font; ATSFontIteratorCreate(kATSFontContextLocal, NULL, NULL, kATSOptionFlagsUnRestrictedScope, &itr); while (!result && ATSFontIteratorNext(itr, &font) == noErr) { /* Get CoreText font handle. */ CTFontRef font_ref = CTFontCreateWithPlatformFont(font, 0.0, NULL, NULL); CFArrayRef langs = CTFontCopySupportedLanguages(font_ref); if (langs != NULL) { /* Font has a list of supported languages. */ for (CFIndex i = 0; i < CFArrayGetCount(langs); i++) { CFStringRef lang = (CFStringRef)CFArrayGetValueAtIndex(langs, i); if (CFStringCompare(lang, lang_code, kCFCompareAnchored) == kCFCompareEqualTo) { /* Lang code is supported by font, get full font name. */ CFStringRef font_name = CTFontCopyFullName(font_ref); char name[128]; CFStringGetCString(font_name, name, lengthof(name), kCFStringEncodingUTF8); CFRelease(font_name); /* Skip some inappropriate or ugly looking fonts that have better alternatives. */ if (strncmp(name, "Courier", 7) == 0 || strncmp(name, "Apple Symbols", 13) == 0 || strncmp(name, ".Aqua", 5) == 0 || strncmp(name, "LastResort", 10) == 0 || strncmp(name, "GB18030 Bitmap", 14) == 0) continue; /* Save result. */ callback->SetFontNames(settings, name); DEBUG(freetype, 2, "CT-Font for %s: %s", language_isocode, name); result = true; break; } } CFRelease(langs); } CFRelease(font_ref); } ATSFontIteratorRelease(&itr); CFRelease(lang_code); } else #endif { #if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) && !__LP64__ /* Determine fallback font using ATSUI. This uses a string sample with * missing characters. This is not failure-proof, but a better way like * using the isocode as in the CoreText code path is not available. * ATSUI was deprecated with 10.6 and is only partially available in * 64-bit mode. */ /* Remove all control characters in the range from SCC_CONTROL_START to * SCC_CONTROL_END as well as all ASCII < 0x20 from the string as it will * mess with the automatic font detection */ char buff[256]; // This length is enough to find a suitable replacement font strecpy(buff, str, lastof(buff)); str_validate(buff, lastof(buff), SVS_ALLOW_NEWLINE); /* Extract a UniChar representation of the sample string. */ CFStringRef cf_str = CFStringCreateWithCString(kCFAllocatorDefault, buff, kCFStringEncodingUTF8); if (cf_str == NULL) { /* Something went wrong. Corrupt/invalid sample string? */ return false; } CFIndex str_len = CFStringGetLength(cf_str); UniChar string[str_len]; CFStringGetCharacters(cf_str, CFRangeMake(0, str_len), string); /* Create a default text style with the default font. */ ATSUStyle style; ATSUCreateStyle(&style); /* Create a text layout object from the sample string using the text style. */ UniCharCount run_len = kATSUToTextEnd; ATSUTextLayout text_layout; ATSUCreateTextLayoutWithTextPtr(string, kATSUFromTextBeginning, kATSUToTextEnd, str_len, 1, &run_len, &style, &text_layout); /* Try to match a font for the sample text. ATSUMatchFontsToText stops after * it finds the first continuous character run not renderable with the currently * selected font starting at offset. The matching needs to be repeated until * the end of the string is reached to make sure the fallback font matches for * all characters in the string and not only the first run. */ UniCharArrayOffset offset = kATSUFromTextBeginning; OSStatus os_err; do { ATSUFontID font; UniCharCount run_len; os_err = ATSUMatchFontsToText(text_layout, offset, kATSUToTextEnd, &font, &offset, &run_len); if (os_err == kATSUFontsMatched) { /* Found a better fallback font. Update the text layout * object with the new font. */ ATSUAttributeTag tag = kATSUFontTag; ByteCount size = sizeof(font); ATSUAttributeValuePtr val = &font; ATSUSetAttributes(style, 1, &tag, &size, &val); offset += run_len; } /* Exit if the end of the string is reached or some other error occurred. */ } while (os_err == kATSUFontsMatched && offset < (UniCharArrayOffset)str_len); if (os_err == noErr || os_err == kATSUFontsMatched) { /* ATSUMatchFontsToText exited normally. Extract font * out of the text layout object. */ ATSUFontID font; ByteCount act_len; ATSUGetAttribute(style, kATSUFontTag, sizeof(font), &font, &act_len); /* Get unique font name. The result is not a c-string, we have * to leave space for a \0 and terminate it ourselves. */ char name[128]; ATSUFindFontName(font, kFontUniqueName, kFontNoPlatformCode, kFontNoScriptCode, kFontNoLanguageCode, 127, name, &act_len, NULL); name[act_len > 127 ? 127 : act_len] = '\0'; /* Save Result. */ callback->SetFontNames(settings, name); DEBUG(freetype, 2, "ATSUI-Font for %s: %s", language_isocode, name); result = true; } ATSUDisposeTextLayout(text_layout); ATSUDisposeStyle(style); CFRelease(cf_str); #endif } if (result && strncmp(settings->medium.font, "Geeza Pro", 9) == 0) { /* The font 'Geeza Pro' is often found for arabic characters, but * it has the 'tiny' problem of not having any latin characters. * 'Arial Unicode MS' on the other hand has arabic and latin glyphs, * but seems to 'forget' to inform the OS about this fact. Manually * substitute the latter for the former if it is loadable. */ bool ft_init = _library != NULL; FT_Face face; /* Init FreeType if needed. */ if ((ft_init || FT_Init_FreeType(&_library) == FT_Err_Ok) && GetFontByFaceName("Arial Unicode MS", &face) == FT_Err_Ok) { FT_Done_Face(face); callback->SetFontNames(settings, "Arial Unicode MS"); DEBUG(freetype, 1, "Replacing font 'Geeza Pro' with 'Arial Unicode MS'"); } if (!ft_init) { /* Uninit FreeType if we did the init. */ FT_Done_FreeType(_library); _library = NULL; } } callback->FindMissingGlyphs(NULL); return result; }
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); }
QFontEngineMac::~QFontEngineMac() { ATSUDisposeStyle(style); }
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); }
static OSStatus RoundTripFlatten( CGContextRef cgContext, Fixed currentXPos, Fixed *currentYPos, ATSUStyle styleArray[], ItemCount styleArraySize, ATSUStyleRunInfo styleRunArray[], ItemCount styleRunArraySize, UniChar *stringPtr, UniCharCount stringLen ) { OSStatus err; ItemCount i; ByteCount suggestedStreamBufferSize; ItemCount suggestedNumberOfRunInfo; ItemCount suggestedNumberOfStyleObjects; ByteCount actualStreamBufferSize; ItemCount actualNumberOfRunInfo; ItemCount actualNumberOfStyleObjects; void *streamBuffer = NULL; ATSUStyle *unflattenedStyleArray = NULL; ATSUStyleRunInfo *unflattenedStyleRunArray = NULL; // get the size of the outgoing stream err = ATSUFlattenStyleRunsToStream( kATSUDataStreamUnicodeStyledText, kATSUFlattenOptionNoOptionsMask, styleRunArraySize, styleRunArray, styleArraySize, styleArray, 0, NULL, &suggestedStreamBufferSize ); require_noerr( err, RoundTripFlatteningSample_err ); // allocate a buffer big enough to hold the stream streamBuffer = (void *) malloc( suggestedStreamBufferSize ); require_action( streamBuffer != NULL, RoundTripFlatteningSample_err, err = memFullErr ); // now, flatten the stream err = ATSUFlattenStyleRunsToStream( kATSUDataStreamUnicodeStyledText, kATSUFlattenOptionNoOptionsMask, styleRunArraySize, styleRunArray, styleArraySize, styleArray, suggestedStreamBufferSize, streamBuffer, &actualStreamBufferSize ); require_noerr( err, RoundTripFlatteningSample_err ); // next, try to unflatten the stream that was just flattened. First, try // to get counts of the stuff in the stream, so that we can allocate // accordingly. err = ATSUUnflattenStyleRunsFromStream( kATSUDataStreamUnicodeStyledText, kATSUUnFlattenOptionNoOptionsMask, actualStreamBufferSize, streamBuffer, 0, 0, NULL, NULL, &suggestedNumberOfRunInfo, &suggestedNumberOfStyleObjects ); require_noerr( err, RoundTripFlatteningSample_err ); // allocate some buffer space for the style unflattenedStyleArray = (ATSUStyle *) malloc( sizeof( ATSUStyle ) * suggestedNumberOfStyleObjects ); require_action( unflattenedStyleArray != NULL, RoundTripFlatteningSample_err, err = memFullErr ); // allocate some buffer space for the style runs unflattenedStyleRunArray = (ATSUStyleRunInfo *) malloc( sizeof( ATSUStyleRunInfo ) * suggestedNumberOfRunInfo ); require_action( unflattenedStyleRunArray != NULL, RoundTripFlatteningSample_err, err = memFullErr ); // try to unflatten the stream for real now. err = ATSUUnflattenStyleRunsFromStream( kATSUDataStreamUnicodeStyledText, kATSUUnFlattenOptionNoOptionsMask, actualStreamBufferSize, streamBuffer, suggestedNumberOfRunInfo, suggestedNumberOfStyleObjects, unflattenedStyleRunArray, unflattenedStyleArray, &actualNumberOfRunInfo, &actualNumberOfStyleObjects ); require_noerr( err, RoundTripFlatteningSample_err ); // try to draw the text with the unflattened stream err = DrawLayoutForStyleAndRunInfo( unflattenedStyleArray, actualNumberOfStyleObjects, unflattenedStyleRunArray, actualNumberOfRunInfo, stringPtr, stringLen, cgContext, currentXPos, currentYPos ); require_noerr( err, RoundTripFlatteningSample_err ); // now, we need to get rid of the text layouts that were produced by the // ATSUUnflattenStyleRunsFromStream for ( i = 0; i < actualNumberOfStyleObjects; i++ ) { ATSUDisposeStyle( unflattenedStyleArray[i] ); } // finally, we can dispose of the buffers that we had allocated free( streamBuffer ); free( unflattenedStyleArray ); free( unflattenedStyleRunArray ); RoundTripFlatteningSample_err: // flush the context CGContextFlush( cgContext ); // return noErr so that our error and debugging messages printout return noErr; }