ATSUTextLayout ATSUCreateTextLayoutWithTextPtr_wrap( ATSUStyle style, u_char* text, int len, int32 textOffset, int32 textLength, void* FH ) { ATSUTextLayout layout; ResourceMark rm; UniChar* unis = NEW_C_HEAP_ARRAY(UniChar, len); for (uint i = 0; i < len; ++i) unis[i] = UniChar(text[i]); // hack to unicode OSStatus e = ATSUCreateTextLayoutWithTextPtr( unis, (uint32)textOffset, (int32)textLength, len, 1, (const UniCharCount*)&textLength, &style, &layout); return e != noErr ? (ATSUTextLayout)reportOSError(e, "ATSUCreateTextLayoutWithTextPtr", FH) : layout; }
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; }
void drawPlugin(NPP instance, NPCocoaEvent* event) { if (!browserUAString) return; PluginInstance* currentInstance = (PluginInstance*)(instance->pdata); CGContextRef cgContext = event->data.draw.context; if (!cgContext) return; float windowWidth = currentInstance->window.width; float windowHeight = currentInstance->window.height; // save the cgcontext gstate CGContextSaveGState(cgContext); // we get a flipped context CGContextTranslateCTM(cgContext, 0.0, windowHeight); CGContextScaleCTM(cgContext, 1.0, -1.0); // draw a gray background for the plugin CGContextAddRect(cgContext, CGRectMake(0, 0, windowWidth, windowHeight)); CGContextSetGrayFillColor(cgContext, 0.5, 1.0); CGContextDrawPath(cgContext, kCGPathFill); // draw a black frame around the plugin CGContextAddRect(cgContext, CGRectMake(0, 0, windowWidth, windowHeight)); CGContextSetGrayStrokeColor(cgContext, 0.0, 1.0); CGContextSetLineWidth(cgContext, 6.0); CGContextStrokePath(cgContext); // draw the UA string using ATSUI CGContextSetGrayFillColor(cgContext, 0.0, 1.0); ATSUStyle atsuStyle; ATSUCreateStyle(&atsuStyle); CFIndex stringLength = CFStringGetLength(browserUAString); UniChar* unicharBuffer = (UniChar*)malloc((stringLength + 1) * sizeof(UniChar)); CFStringGetCharacters(browserUAString, CFRangeMake(0, stringLength), unicharBuffer); UniCharCount runLengths = kATSUToTextEnd; ATSUTextLayout atsuLayout; ATSUCreateTextLayoutWithTextPtr(unicharBuffer, kATSUFromTextBeginning, kATSUToTextEnd, stringLength, 1, &runLengths, &atsuStyle, &atsuLayout); ATSUAttributeTag contextTag = kATSUCGContextTag; ByteCount byteSize = sizeof(CGContextRef); ATSUAttributeValuePtr contextATSUPtr = &cgContext; ATSUSetLayoutControls(atsuLayout, 1, &contextTag, &byteSize, &contextATSUPtr); ATSUTextMeasurement lineAscent, lineDescent; ATSUGetLineControl(atsuLayout, kATSUFromTextBeginning, kATSULineAscentTag, sizeof(ATSUTextMeasurement), &lineAscent, &byteSize); ATSUGetLineControl(atsuLayout, kATSUFromTextBeginning, kATSULineDescentTag, sizeof(ATSUTextMeasurement), &lineDescent, &byteSize); float lineHeight = FixedToFloat(lineAscent) + FixedToFloat(lineDescent); ItemCount softBreakCount; ATSUBatchBreakLines(atsuLayout, kATSUFromTextBeginning, stringLength, FloatToFixed(windowWidth - 10.0), &softBreakCount); ATSUGetSoftLineBreaks(atsuLayout, kATSUFromTextBeginning, kATSUToTextEnd, 0, NULL, &softBreakCount); UniCharArrayOffset* softBreaks = (UniCharArrayOffset*)malloc(softBreakCount * sizeof(UniCharArrayOffset)); ATSUGetSoftLineBreaks(atsuLayout, kATSUFromTextBeginning, kATSUToTextEnd, softBreakCount, softBreaks, &softBreakCount); UniCharArrayOffset currentDrawOffset = kATSUFromTextBeginning; int i = 0; while (i < softBreakCount) { ATSUDrawText(atsuLayout, currentDrawOffset, softBreaks[i], FloatToFixed(5.0), FloatToFixed(windowHeight - 5.0 - (lineHeight * (i + 1.0)))); currentDrawOffset = softBreaks[i]; i++; } ATSUDrawText(atsuLayout, currentDrawOffset, kATSUToTextEnd, FloatToFixed(5.0), FloatToFixed(windowHeight - 5.0 - (lineHeight * (i + 1.0)))); free(unicharBuffer); free(softBreaks); // restore the cgcontext gstate CGContextRestoreGState(cgContext); }
void Text::calculate_position_and_advance_cursor(TextWriter &tw, int *out_x, int *out_y) const { #ifdef WIN32 const long options = DT_LEFT | DT_NOPREFIX; Context c = tw.renderer.m_context; int previous_map_mode = SetMapMode(c, MM_TEXT); HFONT font = font_handle_lookup[tw.size]; // Create the font we want to use, and swap it out with // whatever is currently in there, along with our color HFONT previous_font = (HFONT)SelectObject(c, font); // Call DrawText to find out how large our text is RECT drawing_rect = { tw.x, tw.y, 0, 0 }; tw.last_line_height = DrawText(c, m_text.c_str(), int(m_text.length()), &drawing_rect, options | DT_CALCRECT); // Return the hdc settings to their previous setting SelectObject(c, previous_font); SetMapMode(c, previous_map_mode); #else // Convert passed-in text to Unicode CFStringRef cftext = MacStringFromWide(m_text, true).get(); CFDataRef unitext = CFStringCreateExternalRepresentation(kCFAllocatorDefault, cftext, kCFStringEncodingUnicode, 0); if (!unitext) throw PianoGameError(WSTRING(L"Couldn't convert string to unicode: '" << m_text << L"'")); CFRelease(cftext); // Create an ATSU layout ATSUTextLayout layout; const UniCharCount run_length = kATSUToTextEnd; OSStatus status = ATSUCreateTextLayoutWithTextPtr((ConstUniCharArrayPtr)CFDataGetBytePtr(unitext), kATSUFromTextBeginning, kATSUToTextEnd, CFDataGetLength(unitext) / 2, 1, &run_length, &atsu_style_lookup[tw.size], &layout); if (status != noErr) throw PianoGameError(WSTRING(L"Couldn't create ATSU text layout for string: '" << m_text << L"', Error code: " << static_cast<int>(status))); // Measure the size of the resulting text Rect drawing_rect = { 0, 0, 0, 0 }; ATSUTextMeasurement before = 0; ATSUTextMeasurement after = 0; ATSUTextMeasurement ascent = 0; ATSUTextMeasurement descent = 0; status = ATSUGetUnjustifiedBounds(layout, 0, kATSUToTextEnd, &before, &after, &ascent, &descent); if (status != noErr) throw PianoGameError(WSTRING(L"Couldn't get unjustified bounds for text layout for string: '" << m_text << L"', Error code: " << static_cast<int>(status))); // NOTE: the +1 here is completely arbitrary and seemed to place the text better. // It may just be a difference between the Windows and Mac text placement systems. drawing_rect.top += tw.y + 1; drawing_rect.left += tw.x + FixRound(before); drawing_rect.right += tw.x + FixRound(after); // Not used. drawing_rect.bottom = 0; // Clean-up ATSUDisposeTextLayout(layout); CFRelease(unitext); #endif // Update the text-writer with post-draw coordinates if (tw.centered) drawing_rect.left -= (drawing_rect.right - drawing_rect.left) / 2; if (!tw.centered) tw.x += drawing_rect.right - drawing_rect.left; // Tell the draw function where to put the text *out_x = drawing_rect.left; *out_y = drawing_rect.top; }
static bool osx_draw_text_to_cgcontext_at_location(const void *p_text, uindex_t p_length, MCGPoint p_location, const MCGFont &p_font, CGContextRef p_cgcontext, MCGIntRectangle &r_bounds) { OSStatus t_err; t_err = noErr; ATSUFontID t_font_id; Fixed t_font_size; t_font_size = p_font . size << 16; ATSUAttributeTag t_tags[] = { kATSUFontTag, kATSUSizeTag, }; ByteCount t_sizes[] = { sizeof(ATSUFontID), sizeof(Fixed), }; ATSUAttributeValuePtr t_attrs[] = { &t_font_id, &t_font_size, }; ATSLineLayoutOptions t_layout_options; ATSUAttributeTag t_layout_tags[] = { kATSULineLayoutOptionsTag, kATSUCGContextTag, }; ByteCount t_layout_sizes[] = { sizeof(ATSLineLayoutOptions), sizeof(CGContextRef), }; ATSUAttributeValuePtr t_layout_attrs[] = { &t_layout_options, &p_cgcontext, }; if (t_err == noErr) { // if the specified fon't can't be found, just use the default if (ATSUFONDtoFontID((short)(intptr_t)p_font . fid, p_font . style, &t_font_id) != noErr) t_err = ATSUFONDtoFontID(0, p_font . style, &t_font_id); } ATSUStyle t_style; t_style = NULL; if (t_err == noErr) t_err = ATSUCreateStyle(&t_style); if (t_err == noErr) t_err = ATSUSetAttributes(t_style, sizeof(t_tags) / sizeof(ATSUAttributeTag), t_tags, t_sizes, t_attrs); ATSUTextLayout t_layout; t_layout = NULL; if (t_err == noErr) { UniCharCount t_run; t_run = p_length / 2; t_err = ATSUCreateTextLayoutWithTextPtr((const UniChar *)p_text, 0, p_length / 2, p_length / 2, 1, &t_run, &t_style, &t_layout); } if (t_err == noErr) t_err = ATSUSetTransientFontMatching(t_layout, true); if (t_err == noErr) { t_layout_options = kATSLineUseDeviceMetrics | kATSLineFractDisable; t_err = ATSUSetLayoutControls(t_layout, sizeof(t_layout_tags) / sizeof(ATSUAttributeTag), t_layout_tags, t_layout_sizes, t_layout_attrs); } MCGIntRectangle t_bounds; if (p_cgcontext == NULL) { ATSUTextMeasurement t_before, t_after, t_ascent, t_descent; if (t_err == noErr) t_err = ATSUGetUnjustifiedBounds(t_layout, 0, p_length / 2, &t_before, &t_after, &t_ascent, &t_descent); if (t_err == noErr) { t_ascent = (t_ascent + 0xffff) >> 16; t_descent = (t_descent + 0xffff) >> 16; t_after = (t_after + 0xffff) >> 16; t_bounds . x = p_location . x; t_bounds . y = p_location . y - p_font . ascent; t_bounds . width = t_after; t_bounds . height = p_font . descent + p_font . ascent; r_bounds = t_bounds; }
static OSStatus AddNewTextLayoutToContext( DrawContextStruct *context ) { OSStatus err = noErr; ATSUAttributeTag tag; ByteCount valueSize; ATSUAttributeValuePtr valuePtr; UniCharCount runLength = kATSUToTextEnd; if (context->cgContext == NULL) return err; // create a text layout object err = ATSUCreateTextLayoutWithTextPtr( context->textBuffer, kATSUFromTextBeginning, kATSUToTextEnd, context->characterCount, 1, &runLength, &gGlobalStyle, &context->layoutObject ); require_noerr( err, AddNewTextLayoutToContext_err ); // add the cgContext to the text layout tag = kATSUCGContextTag; valueSize = sizeof( CGContextRef ); valuePtr = &context->cgContext; err = ATSUSetLayoutControls( context->layoutObject, 1, &tag, &valueSize, &valuePtr ); require_noerr( err, AddNewTextLayoutToContext_err ); // set font substitution for the new layout err = ATSUSetTransientFontMatching( context->layoutObject, true ); require_noerr( err, AddNewTextLayoutToContext_err ); // now, check the menu and see if we need to install a callback switch ( gDemoMenuSelection ) { case kDemoMenuItemStretch: err = InstallStrechyGlyphCallback( context->layoutObject ); require_noerr( err, AddNewTextLayoutToContext_err ); break; case kDemoMenuItemShrink: err = InstallShrinkyGlyphCallback( context->layoutObject ); require_noerr( err, AddNewTextLayoutToContext_err ); break; case kDemoMenuItemWhitespaceReplace: err = InstallGlyphReplacementCallback( context->layoutObject ); require_noerr( err, AddNewTextLayoutToContext_err ); break; case kDemoMenuItemSineWave: err = InstallGlyphWaveCallback( context->layoutObject ); require_noerr( err, AddNewTextLayoutToContext_err ); break; case kDemoMenuItemNone: default: break; } AddNewTextLayoutToContext_err: return err; }
static OSStatus DrawLayoutForStyleAndRunInfo( ATSUStyle styleArray[], ItemCount styleArraySize, ATSUStyleRunInfo styleRunArray[], ItemCount styleRunArraySize, UniChar *stringPtr, UniCharCount stringLen, CGContextRef cgContext, Fixed currentXPos, Fixed *currentYPos ) { OSStatus err; ATSUStyle *seperateStyles = NULL; UniCharCount *seperateRunLengths = NULL; ItemCount numberOfRuns = 0; ATSUAttributeTag attrTag; ByteCount attrSize; ATSUAttributeValuePtr attrPtr; ATSUTextLayout newLayout; // check to see if we have a style array if ( styleArray != NULL ) { // if the styleRunArray is NULL, then there is only one run if ( styleRunArray == NULL ) { numberOfRuns = 1; seperateStyles = styleArray; seperateRunLengths = &stringLen; } else { ItemCount i; // the number of runs is equal to the number of runs passed in numberOfRuns = styleRunArraySize; // allocate a seperateStyles and seperateRuns array seperateStyles = (ATSUStyle *) malloc( sizeof( ATSUStyle ) * numberOfRuns ); require_action( seperateStyles != NULL, CreateLayoutForStyleAndRunInfo_err, err = memFullErr ); seperateRunLengths = (UniCharCount *) malloc( sizeof( UniCharCount ) * numberOfRuns ); require_action( seperateRunLengths != NULL, CreateLayoutForStyleAndRunInfo_err, err = memFullErr ); // loop through and assign the runs to the seperate arrays. I'm not // sure that this is the best way to do this. Perhaps it's best to // simply create the layout and assign the style runs to it individually. for ( i = 0; i < numberOfRuns; i++ ) { seperateStyles[i] = styleArray[styleRunArray[i].styleObjectIndex]; seperateRunLengths[i] = styleRunArray[i].runLength; } } } // first of all, create the layout with the text information passed in err = ATSUCreateTextLayoutWithTextPtr( stringPtr, kATSUFromTextBeginning, kATSUToTextEnd, stringLen, numberOfRuns, seperateRunLengths, seperateStyles, &newLayout ); require_noerr( err, CreateLayoutForStyleAndRunInfo_err ); // if we've got a layout, then assign the CGContext to it attrTag = kATSUCGContextTag; attrSize = sizeof( CGContextRef ); attrPtr = &cgContext; err = ATSUSetLayoutControls( newLayout, 1, &attrTag, &attrSize, &attrPtr ); require_noerr( err, CreateLayoutForStyleAndRunInfoLayout_err ); // set the Y position before we draw err = SetYPositionForLineHeight( newLayout, currentYPos ); require_noerr( err, CreateLayoutForStyleAndRunInfoLayout_err ); // do some drawin' err = ATSUDrawText( newLayout, kATSUFromTextBeginning, kATSUToTextEnd, currentXPos, *currentYPos ); require_noerr( err, CreateLayoutForStyleAndRunInfoLayout_err ); CreateLayoutForStyleAndRunInfoLayout_err: ATSUDisposeTextLayout( newLayout ); CreateLayoutForStyleAndRunInfo_err: if ( ( styleRunArray != NULL ) && ( styleArray != NULL ) ) { if ( seperateStyles != NULL ) { free( seperateStyles ); } if ( seperateRunLengths != NULL ); { free( seperateRunLengths ); } } return err; }