1 /* 2 * Copyright (c) 2008, 2009 Google 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 are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 #include <windows.h> 33 #include <vector> 34 35 #include "ChromiumBridge.h" 36 #include "Font.h" 37 #include "GlyphPageTreeNode.h" 38 #include "SimpleFontData.h" 39 #include "UniscribeHelperTextRun.h" 40 #include "WindowsVersion.h" 41 42 namespace WebCore { 43 44 // Fills one page of font data pointers with 0 to indicate that there 45 // are no glyphs for the characters. 46 static void fillEmptyGlyphs(GlyphPage* page) 47 { 48 for (int i = 0; i < GlyphPage::size; ++i) 49 page->setGlyphDataForIndex(i, 0, 0); 50 } 51 52 // Lazily initializes space glyph 53 static Glyph initSpaceGlyph(HDC dc, Glyph* spaceGlyph) 54 { 55 if (*spaceGlyph) 56 return *spaceGlyph; 57 58 static wchar_t space = ' '; 59 GetGlyphIndices(dc, &space, 1, spaceGlyph, 0); 60 return *spaceGlyph; 61 } 62 63 // Fills |length| glyphs starting at |offset| in a |page| in the Basic 64 // Multilingual Plane (<= U+FFFF). The input buffer size should be the 65 // same as |length|. We can use the standard Windows GDI functions here. 66 // Returns true if any glyphs were found. 67 static bool fillBMPGlyphs(unsigned offset, 68 unsigned length, 69 UChar* buffer, 70 GlyphPage* page, 71 const SimpleFontData* fontData, 72 bool recurse) 73 { 74 HDC dc = GetDC((HWND)0); 75 HGDIOBJ oldFont = SelectObject(dc, fontData->platformData().hfont()); 76 77 TEXTMETRIC tm = {0}; 78 if (!GetTextMetrics(dc, &tm)) { 79 SelectObject(dc, oldFont); 80 ReleaseDC(0, dc); 81 82 if (recurse) { 83 if (ChromiumBridge::ensureFontLoaded(fontData->platformData().hfont())) 84 return fillBMPGlyphs(offset, length, buffer, page, fontData, false); 85 else { 86 fillEmptyGlyphs(page); 87 return false; 88 } 89 } else { 90 // FIXME: Handle gracefully the error if this call also fails. 91 // See http://crbug.com/6401 92 LOG_ERROR("Unable to get the text metrics after second attempt"); 93 fillEmptyGlyphs(page); 94 return false; 95 } 96 } 97 98 // FIXME: GetGlyphIndices() sets each item of localGlyphBuffer[] 99 // with the one of the values listed below. 100 // * With the GGI_MARK_NONEXISTING_GLYPHS flag 101 // + If the font has a glyph available for the character, 102 // localGlyphBuffer[i] > 0x0. 103 // + If the font does not have glyphs available for the character, 104 // localGlyphBuffer[i] = 0x1F (TrueType Collection?) or 105 // 0xFFFF (OpenType?). 106 // * Without the GGI_MARK_NONEXISTING_GLYPHS flag 107 // + If the font has a glyph available for the character, 108 // localGlyphBuffer[i] > 0x0. 109 // + If the font does not have glyphs available for the character, 110 // localGlyphBuffer[i] = 0x80. 111 // (Windows automatically assigns the glyph for a box character to 112 // prevent ExtTextOut() from returning errors.) 113 // To avoid from hurting the rendering performance, this code just 114 // tells WebKit whether or not the all glyph indices for the given 115 // characters are 0x80 (i.e. a possibly-invalid glyph) and let it 116 // use alternative fonts for the characters. 117 // Although this may cause a problem, it seems to work fine as far as I 118 // have tested. (Obviously, I need more tests.) 119 WORD localGlyphBuffer[GlyphPage::size]; 120 121 // FIXME: I find some Chinese characters can not be correctly displayed 122 // when call GetGlyphIndices without flag GGI_MARK_NONEXISTING_GLYPHS, 123 // because the corresponding glyph index is set as 0x20 when current font 124 // does not have glyphs available for the character. According a blog post 125 // http://blogs.msdn.com/michkap/archive/2006/06/28/649791.aspx 126 // I think we should switch to the way about calling GetGlyphIndices with 127 // flag GGI_MARK_NONEXISTING_GLYPHS, it should be OK according the 128 // description of MSDN. 129 // Also according to Jungshik and Hironori's suggestion and modification 130 // we treat turetype and raster Font as different way when windows version 131 // is less than Vista. 132 GetGlyphIndices(dc, buffer, length, localGlyphBuffer, GGI_MARK_NONEXISTING_GLYPHS); 133 134 // Copy the output to the GlyphPage 135 bool haveGlyphs = false; 136 int invalidGlyph = 0xFFFF; 137 if (!isVistaOrNewer() && !(tm.tmPitchAndFamily & TMPF_TRUETYPE)) 138 invalidGlyph = 0x1F; 139 140 Glyph spaceGlyph = 0; // Glyph for a space. Lazily filled. 141 142 for (unsigned i = 0; i < length; i++) { 143 UChar c = buffer[i]; 144 Glyph glyph = localGlyphBuffer[i]; 145 const SimpleFontData* glyphFontData = fontData; 146 // When this character should be a space, we ignore whatever the font 147 // says and use a space. Otherwise, if fonts don't map one of these 148 // space or zero width glyphs, we will get a box. 149 if (Font::treatAsSpace(c)) { 150 // Hard code the glyph indices for characters that should be 151 // treated like spaces. 152 glyph = initSpaceGlyph(dc, &spaceGlyph); 153 } else if (glyph == invalidGlyph) { 154 // WebKit expects both the glyph index and FontData 155 // pointer to be 0 if the glyph is not present 156 glyph = 0; 157 glyphFontData = 0; 158 } else 159 haveGlyphs = true; 160 page->setGlyphDataForCharacter(offset + i, glyph, glyphFontData); 161 } 162 163 SelectObject(dc, oldFont); 164 ReleaseDC(0, dc); 165 return haveGlyphs; 166 } 167 168 // For non-BMP characters, each is two words (UTF-16) and the input buffer 169 // size is 2 * |length|. Since GDI doesn't know how to handle non-BMP 170 // characters, we must use Uniscribe to tell us the glyph indices. 171 // 172 // We don't want to call this in the case of "regular" characters since some 173 // fonts may not have the correct combining rules for accents. See the notes 174 // at the bottom of ScriptGetCMap. We can't use ScriptGetCMap, though, since 175 // it doesn't seem to support UTF-16, despite what this blog post says: 176 // http://blogs.msdn.com/michkap/archive/2006/06/29/650680.aspx 177 // 178 // So we fire up the full Uniscribe doohicky, give it our string, and it will 179 // correctly handle the UTF-16 for us. The hard part is taking this and getting 180 // the glyph indices back out that correspond to the correct input characters, 181 // since they may be missing. 182 // 183 // Returns true if any glyphs were found. 184 static bool fillNonBMPGlyphs(unsigned offset, 185 unsigned length, 186 UChar* buffer, 187 GlyphPage* page, 188 const SimpleFontData* fontData) 189 { 190 bool haveGlyphs = false; 191 192 UniscribeHelperTextRun state(buffer, length * 2, false, 193 fontData->platformData().hfont(), 194 fontData->platformData().scriptCache(), 195 fontData->platformData().scriptFontProperties()); 196 state.setInhibitLigate(true); 197 state.setDisableFontFallback(true); 198 state.init(); 199 200 for (unsigned i = 0; i < length; i++) { 201 // Each character in this input buffer is a surrogate pair, which 202 // consists of two UChars. So, the offset for its i-th character is 203 // (i * 2). 204 WORD glyph = state.firstGlyphForCharacter(i * 2); 205 if (glyph) { 206 haveGlyphs = true; 207 page->setGlyphDataForIndex(offset + i, glyph, fontData); 208 } else 209 // Clear both glyph and fontData fields. 210 page->setGlyphDataForIndex(offset + i, 0, 0); 211 } 212 return haveGlyphs; 213 } 214 215 // We're supposed to return true if there are any glyphs in the range 216 // specified by |offset| and |length| in our font, 217 // false if there are none. 218 bool GlyphPage::fill(unsigned offset, unsigned length, UChar* characterBuffer, 219 unsigned bufferLength, const SimpleFontData* fontData) 220 { 221 // We have to handle BMP and non-BMP characters differently. 222 // FIXME: Add assertions to make sure that buffer is entirely in BMP 223 // or entirely in non-BMP. 224 if (bufferLength == length) 225 return fillBMPGlyphs(offset, length, characterBuffer, this, fontData, true); 226 227 if (bufferLength == 2 * length) { 228 // A non-BMP input buffer will be twice as long as output glyph buffer 229 // because each character in the non-BMP input buffer will be 230 // represented by a surrogate pair (two UChar's). 231 return fillNonBMPGlyphs(offset, length, characterBuffer, this, fontData); 232 } 233 234 ASSERT_NOT_REACHED(); 235 return false; 236 } 237 238 } // namespace WebCore 239