https://opensource.apple.com/source/WebCore/WebCore-955.66/platform/wx/wxcode/win/fontprops.cpp.auto.html
Windows的方案:
GetTextExtentPoint32
GetTextExtentPoint
GetTextExtentExPoint
DrawText
DrawTextEx
GetTextExtent
GetTextMetrics
GetCharWidth
LpkUseGDIWidthCache
GetTabbedTextExtent
LpkGetTextExtentExPoint
GetTextExtentExPointWPri
GetCharABCWidths
GetCharABCWidthsFloat
GetTextExtentPoint
GetTextExtentExPoint
DrawText
DrawTextEx
GetTextExtent
GetTextMetrics
GetCharWidth
LpkUseGDIWidthCache
GetTabbedTextExtent
LpkGetTextExtentExPoint
GetTextExtentExPointWPri
GetCharABCWidths
GetCharABCWidthsFloat
GetOutlineTextMetrics
void GetTextExtent( const wxFont& font, const wxString& str, wxCoord *width, wxCoord *height, wxCoord *descent, wxCoord *externalLeading ) { HDC dc = GetDC(0); WXHFONT hFont = font.GetHFONT(); ::SelectObject(dc, hFont); HFONT hfontOld; if ( font != wxNullFont ) { wxASSERT_MSG( font.Ok(), _T("invalid font in wxDC::GetTextExtent") ); hfontOld = (HFONT)::SelectObject(dc, hFont); } else // don't change the font { hfontOld = 0; } SIZE sizeRect; const size_t len = str.length(); if ( !::GetTextExtentPoint32(dc, str, len, &sizeRect) ) { wxLogLastError(_T("GetTextExtentPoint32()")); } #if !defined(_WIN32_WCE) || (_WIN32_WCE >= 400) // the result computed by GetTextExtentPoint32() may be too small as it // accounts for under/overhang of the first/last character while we want // just the bounding rect for this string so adjust the width as needed // (using API not available in 2002 SDKs of WinCE) if ( len > 1 ) { ABC width; const wxChar chFirst = *str.begin(); if ( ::GetCharABCWidths(dc, chFirst, chFirst, &width) ) { if ( width.abcA < 0 ) sizeRect.cx -= width.abcA; if ( len > 1 ) { const wxChar chLast = *str.rbegin(); ::GetCharABCWidths(dc, chLast, chLast, &width); } //else: we already have the width of the last character if ( width.abcC < 0 ) sizeRect.cx -= width.abcC; } //else: GetCharABCWidths() failed, not a TrueType font? } #endif // !defined(_WIN32_WCE) || (_WIN32_WCE >= 400) TEXTMETRIC tm; ::GetTextMetrics(dc, &tm); if (width) *width = sizeRect.cx; if (height) *height = sizeRect.cy; if (descent) *descent = tm.tmDescent; if (externalLeading) *externalLeading = tm.tmExternalLeading; if ( hfontOld ) { ::SelectObject(dc, hfontOld); } ReleaseDC(0, dc); }
https://opensource.apple.com/source/WebCore/WebCore-955.66/platform/wx/wxcode/mac/carbon/fontprops.mm.auto.html
Mac平台使用了wxWidgets方案
void GetTextExtent( const wxFont& font, const wxString& str, wxCoord *width, wxCoord *height, wxCoord *descent, wxCoord *externalLeading ) { wxGraphicsContext * const gc = wxGraphicsContext::Create(); gc->SetFont(font, *wxBLACK); // colour doesn't matter but must be specified struct GCTextExtent { wxDouble width, height, descent, externalLeading; } e; gc->GetTextExtent(str, &e.width, &e.height, &e.descent, &e.externalLeading); if ( width ) *width = wxCoord(e.width + .5); if ( height ) *height = wxCoord(e.height + .5); if ( descent ) *descent = wxCoord(e.descent + .5); if ( externalLeading ) *externalLeading = wxCoord(e.externalLeading + .5); delete gc; }
void wxMacCoreGraphicsContext::GetTextExtent( const wxString &str, wxDouble *width, wxDouble *height, wxDouble *descent, wxDouble *externalLeading ) const { wxCHECK_RET( !m_font.IsNull(), wxT("wxMacCoreGraphicsContext::GetTextExtent - no valid font set") ); if ( width ) *width = 0; if ( height ) *height = 0; if ( descent ) *descent = 0; if ( externalLeading ) *externalLeading = 0; // In wxWidgets (MSW-inspired) API it is possible to call GetTextExtent() // with an empty string to get just the descent and the leading of the // font, so support this (mis)use. wxString strToMeasure(str); if (str.empty()) strToMeasure = wxS(" "); wxMacCoreGraphicsFontData* fref = (wxMacCoreGraphicsFontData*)m_font.GetRefData(); wxCFStringRef text(strToMeasure, wxLocale::GetSystemEncoding() ); wxCFRef<CFAttributedStringRef> attrtext( CFAttributedStringCreate(kCFAllocatorDefault, text, fref->OSXGetCTFontAttributes() ) ); wxCFRef<CTLineRef> line( CTLineCreateWithAttributedString(attrtext) ); CGFloat a, d, l, w; w = CTLineGetTypographicBounds(line, &a, &d, &l); if ( !str.empty() ) { if ( width ) *width = w; if ( height ) *height = a+d+l; } if ( descent ) *descent = d; if ( externalLeading ) *externalLeading = l; CheckInvariants(); } void wxMacCoreGraphicsContext::GetPartialTextExtents(const wxString& text, wxArrayDouble& widths) const { widths.clear(); wxCHECK_RET( !m_font.IsNull(), wxT("wxMacCoreGraphicsContext::DrawText - no valid font set") ); if (text.empty()) return; wxMacCoreGraphicsFontData* fref = (wxMacCoreGraphicsFontData*)m_font.GetRefData(); wxCFStringRef t(text, wxLocale::GetSystemEncoding() ); wxCFRef<CFAttributedStringRef> attrtext( CFAttributedStringCreate(kCFAllocatorDefault, t, fref->OSXGetCTFontAttributes()) ); wxCFRef<CTLineRef> line( CTLineCreateWithAttributedString(attrtext) ); widths.reserve(text.length()); CFIndex u16index = 1; for ( wxString::const_iterator iter = text.begin(); iter != text.end(); ++iter, ++u16index ) { // Take care of surrogate pairs: they take two, not one, of UTF-16 code // units used by CoreText. if ( *iter >= 0x10000 ) { ++u16index; } widths.push_back( CTLineGetOffsetForStringIndex( line, u16index, NULL ) ); } CheckInvariants(); }
因为wxWidgets也是跨平台的:
https://opensource.apple.com/source/WebCore/WebCore-955.66/platform/wx/wxcode/gtk/fontprops.cpp.auto.html
Linux平台使用了Pango:
void GetTextExtent( const wxFont& font, const wxString& str, wxCoord *width, wxCoord *height, wxCoord *descent, wxCoord *externalLeading ) { if ( width ) *width = 0; if ( height ) *height = 0; if ( descent ) *descent = 0; if ( externalLeading ) *externalLeading = 0; if (str.empty()) return; // FIXME: Doesn't support height, descent or external leading, though we don't need this for WebKit // it will need to be implemented before merging into wx unless we craft a new API. #if USE(WXGC) PangoFont* pangoFont = WebCore::createPangoFontForFont(&font); PangoContext* pangoContext = pango_cairo_font_map_create_context(PANGO_CAIRO_FONT_MAP(WebCore::pangoFontMap())); PangoGlyph pangoGlyph = WebCore::pango_font_get_glyph(pangoFont, pangoContext, (gunichar)g_utf8_get_char(str.ToUTF8())); cairo_glyph_t cglyph = { pangoGlyph, 0, 0 }; cairo_text_extents_t extents; cairo_scaled_font_t* scaled_font = WebCore::createScaledFontForFont(&font); cairo_scaled_font_glyph_extents(scaled_font, &cglyph, 1, &extents); if (cairo_scaled_font_status(scaled_font) == CAIRO_STATUS_SUCCESS && extents.x_advance != 0) *width = (wxCoord)extents.x_advance; cairo_scaled_font_destroy(scaled_font); g_object_unref(pangoContext); g_object_unref(pangoFont); #else PangoContext* context = gdk_pango_context_get_for_screen( gdk_screen_get_default() ); PangoLayout* m_layout = pango_layout_new(context); // and use it if it's valid if ( font && font->IsOk() ) { pango_layout_set_font_description ( m_layout, font->GetNativeFontInfo()->description ); } // Set layout's text const wxCharBuffer dataUTF8 = wxConvUTF8.cWX2MB(str); if ( !dataUTF8 ) { // hardly ideal, but what else can we do if conversion failed? return; } pango_layout_set_text( m_layout, dataUTF8, strlen(dataUTF8) ); int h = 0; pango_layout_get_pixel_size( m_layout, width, &h ); if (descent) { PangoLayoutIter *iter = pango_layout_get_iter(m_layout); int baseline = pango_layout_iter_get_baseline(iter); pango_layout_iter_free(iter); *descent = h - PANGO_PIXELS(baseline); } if (height) *height = (wxCoord) h; #endif }