17a43d320845cc8eaed09007cacae77c48842798
[WebKit-https.git] / WebCore / platform / graphics / win / FontDataWin.cpp
1 /*
2  * Copyright (C) 2006, 2007 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 <winsock2.h>
31 #include "Font.h"
32 #include "FontCache.h"
33 #include "FontData.h"
34 #include "FloatRect.h"
35 #include "FontDescription.h"
36 #include <wtf/MathExtras.h>
37 #include <unicode/uchar.h>
38 #include <unicode/unorm.h>
39 #include <ApplicationServices/ApplicationServices.h>
40 #include <mlang.h>
41 #include <tchar.h>
42 #include <WebKitSystemInterface/WebKitSystemInterface.h>
43
44 namespace WebCore {
45
46 using std::max;
47
48 const float cSmallCapsFontSizeMultiplier = 0.7f;
49
50 static bool shouldApplyMacAscentHack;
51
52 static inline float scaleEmToUnits(float x, unsigned unitsPerEm) { return unitsPerEm ? x / (float)unitsPerEm : x; }
53
54 void FontData::setShouldApplyMacAscentHack(bool b)
55 {
56     shouldApplyMacAscentHack = b;
57 }
58
59 void FontData::platformInit()
60 {    
61     m_syntheticBoldOffset = m_font.syntheticBold() ? 1.0f : 0.f;
62
63     CGFontRef font = m_font.cgFont();
64     int iAscent = CGFontGetAscent(font);
65     int iDescent = CGFontGetDescent(font);
66     int iLineGap = CGFontGetLeading(font);
67     m_unitsPerEm = CGFontGetUnitsPerEm(font);
68     float pointSize = m_font.size();
69     float fAscent = scaleEmToUnits(iAscent, m_unitsPerEm) * pointSize;
70     float fDescent = -scaleEmToUnits(iDescent, m_unitsPerEm) * pointSize;
71     float fLineGap = scaleEmToUnits(iLineGap, m_unitsPerEm) * pointSize;
72
73     m_isSystemFont = false;
74     if (!isCustomFont()) {
75         HDC dc = GetDC(0);
76         HGDIOBJ oldFont = SelectObject(dc, m_font.hfont());
77         int faceLength = GetTextFace(dc, 0, 0);
78         Vector<TCHAR> faceName(faceLength);
79         GetTextFace(dc, faceLength, faceName.data());
80         m_isSystemFont = !_tcscmp(faceName.data(), _T("Lucida Grande"));
81         SelectObject(dc, oldFont);
82         ReleaseDC(0, dc);
83
84         if (shouldApplyMacAscentHack) {
85             // This code comes from FontDataMac.mm. We only ever do this when running regression tests so that our metrics will match Mac.
86
87             // We need to adjust Times, Helvetica, and Courier to closely match the
88             // vertical metrics of their Microsoft counterparts that are the de facto
89             // web standard. The AppKit adjustment of 20% is too big and is
90             // incorrectly added to line spacing, so we use a 15% adjustment instead
91             // and add it to the ascent.
92             if (!_tcscmp(faceName.data(), _T("Times")) || !_tcscmp(faceName.data(), _T("Helvetica")) || !_tcscmp(faceName.data(), _T("Courier")))
93                 fAscent += floorf(((fAscent + fDescent) * 0.15f) + 0.5f);
94         }
95     }
96
97     m_ascent = lroundf(fAscent);
98     m_descent = lroundf(fDescent);
99     m_lineGap = lroundf(fLineGap);
100     m_lineSpacing = m_ascent + m_descent + m_lineGap;
101
102     // Measure the actual character "x", because AppKit synthesizes X height rather than getting it from the font.
103     // Unfortunately, NSFont will round this for us so we don't quite get the right value.
104     GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page();
105     Glyph xGlyph = glyphPageZero ? glyphPageZero->glyphDataForCharacter('x').glyph : 0;
106     if (xGlyph) {
107         CGRect xBox;
108         CGFontGetGlyphBBoxes(font, &xGlyph, 1, &xBox);
109         // Use the maximum of either width or height because "x" is nearly square
110         // and web pages that foolishly use this metric for width will be laid out
111         // poorly if we return an accurate height. Classic case is Times 13 point,
112         // which has an "x" that is 7x6 pixels.
113         m_xHeight = scaleEmToUnits(max(CGRectGetMaxX(xBox), CGRectGetMaxY(xBox)), m_unitsPerEm) * pointSize;
114     } else {
115         int iXHeight = CGFontGetXHeight(font);
116         m_xHeight = scaleEmToUnits(iXHeight, m_unitsPerEm) * pointSize;
117     }
118
119     m_scriptCache = 0;
120     m_scriptFontProperties = 0;
121 }
122
123 void FontData::platformDestroy()
124 {
125     if (!isCustomFont()) {
126         DeleteObject(m_font.hfont());
127         CGFontRelease(m_font.cgFont());
128     }
129
130     // We don't hash this on Win32, so it's effectively owned by us.
131     delete m_smallCapsFontData;
132
133     ScriptFreeCache(&m_scriptCache);
134     delete m_scriptFontProperties;
135 }
136
137 FontData* FontData::smallCapsFontData(const FontDescription& fontDescription) const
138 {
139     if (!m_smallCapsFontData) {
140         int smallCapsHeight = lroundf(cSmallCapsFontSizeMultiplier * m_font.size());
141         if (isCustomFont()) {
142             FontPlatformData smallCapsFontData(m_font);
143             smallCapsFontData.setSize(smallCapsHeight);
144             m_smallCapsFontData = new FontData(smallCapsFontData, true, false);
145         } else {
146             LOGFONT winfont;
147             GetObject(m_font.hfont(), sizeof(LOGFONT), &winfont);
148             winfont.lfHeight = -smallCapsHeight * 32;
149             HFONT hfont = CreateFontIndirect(&winfont);
150             m_smallCapsFontData = new FontData(FontPlatformData(hfont, smallCapsHeight, fontDescription.bold(), fontDescription.italic()));
151         }
152     }
153     return m_smallCapsFontData;
154 }
155
156 bool FontData::containsCharacters(const UChar* characters, int length) const
157 {
158     // FIXME: Support custom fonts.
159     if (isCustomFont())
160         return false;
161
162     // FIXME: Microsoft documentation seems to imply that characters can be output using a given font and DC
163     // merely by testing code page intersection.  This seems suspect though.  Can't a font only partially
164     // cover a given code page?
165     IMLangFontLink2* langFontLink = FontCache::getFontLinkInterface();
166     if (!langFontLink)
167         return false;
168
169     HDC dc = GetDC((HWND)0);
170     
171     DWORD acpCodePages;
172     langFontLink->CodePageToCodePages(CP_ACP, &acpCodePages);
173
174     DWORD fontCodePages;
175     langFontLink->GetFontCodePages(dc, m_font.hfont(), &fontCodePages);
176
177     DWORD actualCodePages;
178     long numCharactersProcessed;
179     long offset = 0;
180     while (offset < length) {
181         langFontLink->GetStrCodePages(characters, length, acpCodePages, &actualCodePages, &numCharactersProcessed);
182         if ((actualCodePages & fontCodePages) == 0)
183             return false;
184         offset += numCharactersProcessed;
185     }
186
187     ReleaseDC(0, dc);
188
189     return true;
190 }
191
192 void FontData::determinePitch()
193 {
194     if (isCustomFont()) {
195         m_treatAsFixedPitch = false;
196         return;
197     }
198
199     // TEXTMETRICS have this.  Set m_treatAsFixedPitch based off that.
200     HDC dc = GetDC((HWND)0);
201     SaveDC(dc);
202     SelectObject(dc, m_font.hfont());
203
204     // Yes, this looks backwards, but the fixed pitch bit is actually set if the font
205     // is *not* fixed pitch.  Unbelievable but true.
206     TEXTMETRIC tm;
207     GetTextMetrics(dc, &tm);
208     m_treatAsFixedPitch = ((tm.tmPitchAndFamily & TMPF_FIXED_PITCH) == 0);
209
210     RestoreDC(dc, -1);
211     ReleaseDC(0, dc);
212 }
213
214 float FontData::platformWidthForGlyph(Glyph glyph) const
215 {
216     CGFontRef font = m_font.cgFont();
217     float pointSize = m_font.size();
218     CGSize advance;
219     CGAffineTransform m = CGAffineTransformMakeScale(pointSize, pointSize);
220     // FIXME: Need to add real support for printer fonts.
221     bool isPrinterFont = false;
222     wkGetGlyphAdvances(font, m, m_isSystemFont, isPrinterFont, glyph, advance);
223     return advance.width + m_syntheticBoldOffset;
224 }
225
226 SCRIPT_FONTPROPERTIES* FontData::scriptFontProperties() const
227 {
228     if (!m_scriptFontProperties) {
229         m_scriptFontProperties = new SCRIPT_FONTPROPERTIES;
230         memset(m_scriptFontProperties, 0, sizeof(SCRIPT_FONTPROPERTIES));
231         m_scriptFontProperties->cBytes = sizeof(SCRIPT_FONTPROPERTIES);
232         HRESULT result = ScriptGetFontProperties(0, scriptCache(), m_scriptFontProperties);
233         if (result == E_PENDING) {
234             HDC dc = GetDC(0);
235             SaveDC(dc);
236             SelectObject(dc, m_font.hfont());
237             ScriptGetFontProperties(dc, scriptCache(), m_scriptFontProperties);
238             RestoreDC(dc, -1);
239             ReleaseDC(0, dc);
240         }
241     }
242     return m_scriptFontProperties;
243 }
244
245 }