1/* 2 * Copyright (C) 2006, 2007, 2008, 2013 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of Apple Inc. ("Apple") nor the names of 14 * its contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include "config.h" 30#include "SimpleFontData.h" 31 32#include "Font.h" 33#include "FontCache.h" 34#include "FloatRect.h" 35#include "FontDescription.h" 36#include "HWndDC.h" 37#include <mlang.h> 38#include <wtf/MathExtras.h> 39#include <wtf/win/GDIObject.h> 40 41namespace WebCore { 42 43const float cSmallCapsFontSizeMultiplier = 0.7f; 44 45static bool g_shouldApplyMacAscentHack; 46 47void SimpleFontData::setShouldApplyMacAscentHack(bool b) 48{ 49 g_shouldApplyMacAscentHack = b; 50} 51 52bool SimpleFontData::shouldApplyMacAscentHack() 53{ 54 return g_shouldApplyMacAscentHack; 55} 56 57float SimpleFontData::ascentConsideringMacAscentHack(const WCHAR* faceName, float ascent, float descent) 58{ 59 if (!shouldApplyMacAscentHack()) 60 return ascent; 61 62 // This code comes from FontDataMac.mm. We only ever do this when running regression tests so that our metrics will match Mac. 63 64 // We need to adjust Times, Helvetica, and Courier to closely match the 65 // vertical metrics of their Microsoft counterparts that are the de facto 66 // web standard. The AppKit adjustment of 20% is too big and is 67 // incorrectly added to line spacing, so we use a 15% adjustment instead 68 // and add it to the ascent. 69 if (!wcscmp(faceName, L"Times") || !wcscmp(faceName, L"Helvetica") || !wcscmp(faceName, L"Courier")) 70 ascent += floorf(((ascent + descent) * 0.15f) + 0.5f); 71 72 return ascent; 73} 74 75void SimpleFontData::initGDIFont() 76{ 77 if (!m_platformData.size()) { 78 m_fontMetrics.reset(); 79 m_avgCharWidth = 0; 80 m_maxCharWidth = 0; 81 return; 82 } 83 84 HWndDC hdc(0); 85 HGDIOBJ oldFont = SelectObject(hdc, m_platformData.hfont()); 86 OUTLINETEXTMETRIC metrics; 87 GetOutlineTextMetrics(hdc, sizeof(metrics), &metrics); 88 TEXTMETRIC& textMetrics = metrics.otmTextMetrics; 89 float ascent = textMetrics.tmAscent; 90 float descent = textMetrics.tmDescent; 91 float lineGap = textMetrics.tmExternalLeading; 92 m_fontMetrics.setAscent(ascent); 93 m_fontMetrics.setDescent(descent); 94 m_fontMetrics.setLineGap(lineGap); 95 m_fontMetrics.setLineSpacing(lroundf(ascent) + lroundf(descent) + lroundf(lineGap)); 96 m_avgCharWidth = textMetrics.tmAveCharWidth; 97 m_maxCharWidth = textMetrics.tmMaxCharWidth; 98 float xHeight = ascent * 0.56f; // Best guess for xHeight if no x glyph is present. 99#if !OS(WINCE) 100 GLYPHMETRICS gm; 101 static const MAT2 identity = { 0, 1, 0, 0, 0, 0, 0, 1 }; 102 DWORD len = GetGlyphOutline(hdc, 'x', GGO_METRICS, &gm, 0, 0, &identity); 103 if (len != GDI_ERROR && gm.gmptGlyphOrigin.y > 0) 104 xHeight = gm.gmptGlyphOrigin.y; 105#endif 106 m_fontMetrics.setXHeight(xHeight); 107 m_fontMetrics.setUnitsPerEm(metrics.otmEMSquare); 108 109 SelectObject(hdc, oldFont); 110} 111 112void SimpleFontData::platformCharWidthInit() 113{ 114 // GDI Fonts init charwidths in initGDIFont. 115 if (!m_platformData.useGDI()) { 116 m_avgCharWidth = 0.f; 117 m_maxCharWidth = 0.f; 118 initCharWidths(); 119 } 120} 121 122void SimpleFontData::platformDestroy() 123{ 124#if !OS(WINCE) 125 ScriptFreeCache(&m_scriptCache); 126 delete m_scriptFontProperties; 127#endif 128} 129 130PassRefPtr<SimpleFontData> SimpleFontData::platformCreateScaledFontData(const FontDescription& fontDescription, float scaleFactor) const 131{ 132 float scaledSize = scaleFactor * m_platformData.size(); 133 if (isCustomFont()) { 134 FontPlatformData scaledFont(m_platformData); 135 scaledFont.setSize(scaledSize); 136 return SimpleFontData::create(scaledFont, true, false); 137 } 138 139 LOGFONT winfont; 140 GetObject(m_platformData.hfont(), sizeof(LOGFONT), &winfont); 141 winfont.lfHeight = -lroundf(scaledSize * (m_platformData.useGDI() ? 1 : 32)); 142 auto hfont = adoptGDIObject(::CreateFontIndirect(&winfont)); 143 return SimpleFontData::create(FontPlatformData(WTF::move(hfont), scaledSize, m_platformData.syntheticBold(), m_platformData.syntheticOblique(), m_platformData.useGDI()), isCustomFont(), false); 144} 145 146bool SimpleFontData::containsCharacters(const UChar* characters, int length) const 147{ 148 // FIXME: Support custom fonts. 149 if (isCustomFont()) 150 return false; 151 152 // FIXME: Microsoft documentation seems to imply that characters can be output using a given font and DC 153 // merely by testing code page intersection. This seems suspect though. Can't a font only partially 154 // cover a given code page? 155 IMLangFontLinkType* langFontLink = fontCache().getFontLinkInterface(); 156 if (!langFontLink) 157 return false; 158 159 HWndDC dc(0); 160 161 DWORD acpCodePages; 162 langFontLink->CodePageToCodePages(CP_ACP, &acpCodePages); 163 164 DWORD fontCodePages; 165 langFontLink->GetFontCodePages(dc, m_platformData.hfont(), &fontCodePages); 166 167 DWORD actualCodePages; 168 long numCharactersProcessed; 169 long offset = 0; 170 while (offset < length) { 171 langFontLink->GetStrCodePages(characters, length, acpCodePages, &actualCodePages, &numCharactersProcessed); 172 if ((actualCodePages & fontCodePages) == 0) 173 return false; 174 offset += numCharactersProcessed; 175 } 176 177 return true; 178} 179 180void SimpleFontData::determinePitch() 181{ 182 if (isCustomFont()) { 183 m_treatAsFixedPitch = false; 184 return; 185 } 186 187 // TEXTMETRICS have this. Set m_treatAsFixedPitch based off that. 188 HWndDC dc(0); 189 SaveDC(dc); 190 SelectObject(dc, m_platformData.hfont()); 191 192 // Yes, this looks backwards, but the fixed pitch bit is actually set if the font 193 // is *not* fixed pitch. Unbelievable but true. 194 TEXTMETRIC tm; 195 GetTextMetrics(dc, &tm); 196 m_treatAsFixedPitch = ((tm.tmPitchAndFamily & TMPF_FIXED_PITCH) == 0); 197 198 RestoreDC(dc, -1); 199} 200 201FloatRect SimpleFontData::boundsForGDIGlyph(Glyph glyph) const 202{ 203#if OS(WINCE) 204 return FloatRect(); 205#else 206 HWndDC hdc(0); 207 SetGraphicsMode(hdc, GM_ADVANCED); 208 HGDIOBJ oldFont = SelectObject(hdc, m_platformData.hfont()); 209 210 GLYPHMETRICS gdiMetrics; 211 static const MAT2 identity = { 0, 1, 0, 0, 0, 0, 0, 1 }; 212 GetGlyphOutline(hdc, glyph, GGO_METRICS | GGO_GLYPH_INDEX, &gdiMetrics, 0, 0, &identity); 213 214 SelectObject(hdc, oldFont); 215 216 return FloatRect(gdiMetrics.gmptGlyphOrigin.x, -gdiMetrics.gmptGlyphOrigin.y, 217 gdiMetrics.gmBlackBoxX + m_syntheticBoldOffset, gdiMetrics.gmBlackBoxY); 218#endif 219} 220 221float SimpleFontData::widthForGDIGlyph(Glyph glyph) const 222{ 223 HWndDC hdc(0); 224#if !OS(WINCE) 225 SetGraphicsMode(hdc, GM_ADVANCED); 226#endif 227 HGDIOBJ oldFont = SelectObject(hdc, m_platformData.hfont()); 228 229#if OS(WINCE) 230 WCHAR c = glyph; 231 SIZE fontSize; 232 GetTextExtentPoint32W(hdc, &c, 1, &fontSize); 233 float result = fontSize.cx * m_platformData.size() / 72.f; 234#else 235 GLYPHMETRICS gdiMetrics; 236 static const MAT2 identity = { 0, 1, 0, 0, 0, 0, 0, 1 }; 237 GetGlyphOutline(hdc, glyph, GGO_METRICS | GGO_GLYPH_INDEX, &gdiMetrics, 0, 0, &identity); 238 float result = gdiMetrics.gmCellIncX + m_syntheticBoldOffset; 239#endif 240 241 SelectObject(hdc, oldFont); 242 243 return result; 244} 245 246#if !OS(WINCE) 247SCRIPT_FONTPROPERTIES* SimpleFontData::scriptFontProperties() const 248{ 249 if (!m_scriptFontProperties) { 250 m_scriptFontProperties = new SCRIPT_FONTPROPERTIES; 251 memset(m_scriptFontProperties, 0, sizeof(SCRIPT_FONTPROPERTIES)); 252 m_scriptFontProperties->cBytes = sizeof(SCRIPT_FONTPROPERTIES); 253 HRESULT result = ScriptGetFontProperties(0, scriptCache(), m_scriptFontProperties); 254 if (result == E_PENDING) { 255 HWndDC dc(0); 256 SaveDC(dc); 257 SelectObject(dc, m_platformData.hfont()); 258 ScriptGetFontProperties(dc, scriptCache(), m_scriptFontProperties); 259 RestoreDC(dc, -1); 260 } 261 } 262 return m_scriptFontProperties; 263} 264#endif 265 266} // namespace WebCore 267