1 /*
2 * Copyright (C) 2006, 2007, 2008 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 Computer, 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 <mlang.h>
37 #include <unicode/uchar.h>
38 #include <unicode/unorm.h>
39 #include <winsock2.h>
40 #include <wtf/MathExtras.h>
41
42 #if USE(CG)
43 #include <ApplicationServices/ApplicationServices.h>
44 #include <WebKitSystemInterface/WebKitSystemInterface.h>
45 #endif
46
47 namespace WebCore {
48
49 using std::max;
50
51 const float cSmallCapsFontSizeMultiplier = 0.7f;
52
53 static bool g_shouldApplyMacAscentHack;
54
setShouldApplyMacAscentHack(bool b)55 void SimpleFontData::setShouldApplyMacAscentHack(bool b)
56 {
57 g_shouldApplyMacAscentHack = b;
58 }
59
shouldApplyMacAscentHack()60 bool SimpleFontData::shouldApplyMacAscentHack()
61 {
62 return g_shouldApplyMacAscentHack;
63 }
64
initGDIFont()65 void SimpleFontData::initGDIFont()
66 {
67 if (!m_platformData.size()) {
68 m_fontMetrics.reset();
69 m_avgCharWidth = 0;
70 m_maxCharWidth = 0;
71 return;
72 }
73
74 HDC hdc = GetDC(0);
75 HGDIOBJ oldFont = SelectObject(hdc, m_platformData.hfont());
76 OUTLINETEXTMETRIC metrics;
77 GetOutlineTextMetrics(hdc, sizeof(metrics), &metrics);
78 TEXTMETRIC& textMetrics = metrics.otmTextMetrics;
79 float ascent = textMetrics.tmAscent;
80 float descent = textMetrics.tmDescent;
81 float lineGap = textMetrics.tmExternalLeading;
82 m_fontMetrics.setAscent(ascent);
83 m_fontMetrics.setDescent(descent);
84 m_fontMetrics.setLineGap(lineGap);
85 m_fontMetrics.setLineSpacing(lroundf(ascent) + lroundf(descent) + lroundf(lineGap));
86 m_avgCharWidth = textMetrics.tmAveCharWidth;
87 m_maxCharWidth = textMetrics.tmMaxCharWidth;
88 float xHeight = ascent * 0.56f; // Best guess for xHeight if no x glyph is present.
89
90 GLYPHMETRICS gm;
91 MAT2 mat = { 1, 0, 0, 1 };
92 DWORD len = GetGlyphOutline(hdc, 'x', GGO_METRICS, &gm, 0, 0, &mat);
93 if (len != GDI_ERROR && gm.gmptGlyphOrigin.y > 0)
94 xHeight = gm.gmptGlyphOrigin.y;
95
96 m_fontMetrics.setXHeight(xHeight);
97 m_fontMetrics.setUnitsPerEm(metrics.otmEMSquare);
98
99 SelectObject(hdc, oldFont);
100 ReleaseDC(0, hdc);
101
102 return;
103 }
104
platformDestroy()105 void SimpleFontData::platformDestroy()
106 {
107 ScriptFreeCache(&m_scriptCache);
108 delete m_scriptFontProperties;
109 }
110
scaledFontData(const FontDescription & fontDescription,float scaleFactor) const111 SimpleFontData* SimpleFontData::scaledFontData(const FontDescription& fontDescription, float scaleFactor) const
112 {
113 float scaledSize = scaleFactor * m_platformData.size();
114 if (isCustomFont()) {
115 FontPlatformData scaledFont(m_platformData);
116 scaledFont.setSize(scaledSize);
117 return new SimpleFontData(scaledFont, true, false);
118 }
119
120 LOGFONT winfont;
121 GetObject(m_platformData.hfont(), sizeof(LOGFONT), &winfont);
122 winfont.lfHeight = -lroundf(scaledSize * (m_platformData.useGDI() ? 1 : 32));
123 HFONT hfont = CreateFontIndirect(&winfont);
124 return new SimpleFontData(FontPlatformData(hfont, scaledSize, m_platformData.syntheticBold(), m_platformData.syntheticOblique(), m_platformData.useGDI()), isCustomFont(), false);
125 }
126
smallCapsFontData(const FontDescription & fontDescription) const127 SimpleFontData* SimpleFontData::smallCapsFontData(const FontDescription& fontDescription) const
128 {
129 if (!m_derivedFontData)
130 m_derivedFontData = DerivedFontData::create(isCustomFont());
131 if (!m_derivedFontData->smallCaps)
132 m_derivedFontData->smallCaps = scaledFontData(fontDescription, cSmallCapsFontSizeMultiplier);
133
134 return m_derivedFontData->smallCaps.get();
135 }
136
emphasisMarkFontData(const FontDescription & fontDescription) const137 SimpleFontData* SimpleFontData::emphasisMarkFontData(const FontDescription& fontDescription) const
138 {
139 if (!m_derivedFontData)
140 m_derivedFontData = DerivedFontData::create(isCustomFont());
141 if (!m_derivedFontData->emphasisMark)
142 m_derivedFontData->emphasisMark = scaledFontData(fontDescription, .5);
143
144 return m_derivedFontData->emphasisMark.get();
145 }
146
containsCharacters(const UChar * characters,int length) const147 bool SimpleFontData::containsCharacters(const UChar* characters, int length) const
148 {
149 // FIXME: Support custom fonts.
150 if (isCustomFont())
151 return false;
152
153 // FIXME: Microsoft documentation seems to imply that characters can be output using a given font and DC
154 // merely by testing code page intersection. This seems suspect though. Can't a font only partially
155 // cover a given code page?
156 IMLangFontLink2* langFontLink = fontCache()->getFontLinkInterface();
157 if (!langFontLink)
158 return false;
159
160 HDC dc = GetDC(0);
161
162 DWORD acpCodePages;
163 langFontLink->CodePageToCodePages(CP_ACP, &acpCodePages);
164
165 DWORD fontCodePages;
166 langFontLink->GetFontCodePages(dc, m_platformData.hfont(), &fontCodePages);
167
168 DWORD actualCodePages;
169 long numCharactersProcessed;
170 long offset = 0;
171 while (offset < length) {
172 langFontLink->GetStrCodePages(characters, length, acpCodePages, &actualCodePages, &numCharactersProcessed);
173 if ((actualCodePages & fontCodePages) == 0)
174 return false;
175 offset += numCharactersProcessed;
176 }
177
178 ReleaseDC(0, dc);
179
180 return true;
181 }
182
determinePitch()183 void SimpleFontData::determinePitch()
184 {
185 if (isCustomFont()) {
186 m_treatAsFixedPitch = false;
187 return;
188 }
189
190 // TEXTMETRICS have this. Set m_treatAsFixedPitch based off that.
191 HDC dc = GetDC(0);
192 SaveDC(dc);
193 SelectObject(dc, m_platformData.hfont());
194
195 // Yes, this looks backwards, but the fixed pitch bit is actually set if the font
196 // is *not* fixed pitch. Unbelievable but true.
197 TEXTMETRIC tm;
198 GetTextMetrics(dc, &tm);
199 m_treatAsFixedPitch = ((tm.tmPitchAndFamily & TMPF_FIXED_PITCH) == 0);
200
201 RestoreDC(dc, -1);
202 ReleaseDC(0, dc);
203 }
204
boundsForGDIGlyph(Glyph glyph) const205 FloatRect SimpleFontData::boundsForGDIGlyph(Glyph glyph) const
206 {
207 HDC hdc = GetDC(0);
208 SetGraphicsMode(hdc, GM_ADVANCED);
209 HGDIOBJ oldFont = SelectObject(hdc, m_platformData.hfont());
210
211 GLYPHMETRICS gdiMetrics;
212 static const MAT2 identity = { 0, 1, 0, 0, 0, 0, 0, 1 };
213 GetGlyphOutline(hdc, glyph, GGO_METRICS | GGO_GLYPH_INDEX, &gdiMetrics, 0, 0, &identity);
214
215 SelectObject(hdc, oldFont);
216 ReleaseDC(0, hdc);
217
218 return FloatRect(gdiMetrics.gmptGlyphOrigin.x, -gdiMetrics.gmptGlyphOrigin.y,
219 gdiMetrics.gmBlackBoxX + m_syntheticBoldOffset, gdiMetrics.gmBlackBoxY);
220 }
221
widthForGDIGlyph(Glyph glyph) const222 float SimpleFontData::widthForGDIGlyph(Glyph glyph) const
223 {
224 HDC hdc = GetDC(0);
225 SetGraphicsMode(hdc, GM_ADVANCED);
226 HGDIOBJ oldFont = SelectObject(hdc, m_platformData.hfont());
227
228 GLYPHMETRICS gdiMetrics;
229 static const MAT2 identity = { 0, 1, 0, 0, 0, 0, 0, 1 };
230 GetGlyphOutline(hdc, glyph, GGO_METRICS | GGO_GLYPH_INDEX, &gdiMetrics, 0, 0, &identity);
231
232 SelectObject(hdc, oldFont);
233 ReleaseDC(0, hdc);
234
235 return gdiMetrics.gmCellIncX + m_syntheticBoldOffset;
236 }
237
scriptFontProperties() const238 SCRIPT_FONTPROPERTIES* SimpleFontData::scriptFontProperties() const
239 {
240 if (!m_scriptFontProperties) {
241 m_scriptFontProperties = new SCRIPT_FONTPROPERTIES;
242 memset(m_scriptFontProperties, 0, sizeof(SCRIPT_FONTPROPERTIES));
243 m_scriptFontProperties->cBytes = sizeof(SCRIPT_FONTPROPERTIES);
244 HRESULT result = ScriptGetFontProperties(0, scriptCache(), m_scriptFontProperties);
245 if (result == E_PENDING) {
246 HDC dc = GetDC(0);
247 SaveDC(dc);
248 SelectObject(dc, m_platformData.hfont());
249 ScriptGetFontProperties(dc, scriptCache(), m_scriptFontProperties);
250 RestoreDC(dc, -1);
251 ReleaseDC(0, dc);
252 }
253 }
254 return m_scriptFontProperties;
255 }
256
257 }
258