1 /* 2 * Copyright (c) 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 33 #include "FontPlatformData.h" 34 #include "wtf/OwnArrayPtr.h" 35 36 #include "SkFontHost.h" 37 #include "SkPaint.h" 38 #include "SkPath.h" 39 #include "SkPoint.h" 40 #include "SkRect.h" 41 #include "SkUtils.h" 42 43 extern "C" { 44 #include "harfbuzz-shaper.h" 45 } 46 47 // This file implements the callbacks which Harfbuzz requires by using Skia 48 // calls. See the Harfbuzz source for references about what these callbacks do. 49 50 namespace WebCore { 51 52 static HB_Fixed SkiaScalarToHarfbuzzFixed(SkScalar value) 53 { 54 // HB_Fixed is a 26.6 fixed point format. 55 return value * 64; 56 } 57 58 static HB_Bool stringToGlyphs(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length, HB_Glyph* glyphs, hb_uint32* glyphsSize, HB_Bool isRTL) 59 { 60 FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData); 61 SkPaint paint; 62 63 font->setupPaint(&paint); 64 paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); 65 66 unsigned codepoints = 0; 67 for (hb_uint32 i = 0; i < length; i++) { 68 if (!SkUTF16_IsHighSurrogate(characters[i])) 69 codepoints++; 70 if (codepoints > *glyphsSize) 71 return 0; 72 } 73 74 int numGlyphs = paint.textToGlyphs(characters, length * sizeof(uint16_t), reinterpret_cast<uint16_t*>(glyphs)); 75 76 // HB_Glyph is 32-bit, but Skia outputs only 16-bit numbers. So our 77 // |glyphs| array needs to be converted. 78 for (int i = numGlyphs - 1; i >= 0; --i) { 79 uint16_t value; 80 // We use a memcpy to avoid breaking strict aliasing rules. 81 memcpy(&value, reinterpret_cast<char*>(glyphs) + sizeof(uint16_t) * i, sizeof(uint16_t)); 82 glyphs[i] = value; 83 } 84 85 *glyphsSize = numGlyphs; 86 return 1; 87 } 88 89 static void glyphsToAdvances(HB_Font hbFont, const HB_Glyph* glyphs, hb_uint32 numGlyphs, HB_Fixed* advances, int flags) 90 { 91 FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData); 92 SkPaint paint; 93 94 font->setupPaint(&paint); 95 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 96 97 OwnArrayPtr<uint16_t> glyphs16 = adoptArrayPtr(new uint16_t[numGlyphs]); 98 if (!glyphs16.get()) 99 return; 100 for (unsigned i = 0; i < numGlyphs; ++i) 101 glyphs16[i] = glyphs[i]; 102 paint.getTextWidths(glyphs16.get(), numGlyphs * sizeof(uint16_t), reinterpret_cast<SkScalar*>(advances)); 103 104 // The |advances| values which Skia outputs are SkScalars, which are floats 105 // in Chromium. However, Harfbuzz wants them in 26.6 fixed point format. 106 // These two formats are both 32-bits long. 107 for (unsigned i = 0; i < numGlyphs; ++i) { 108 float value; 109 // We use a memcpy to avoid breaking strict aliasing rules. 110 memcpy(&value, reinterpret_cast<char*>(advances) + sizeof(float) * i, sizeof(float)); 111 advances[i] = SkiaScalarToHarfbuzzFixed(value); 112 } 113 } 114 115 static HB_Bool canRender(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length) 116 { 117 FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData); 118 SkPaint paint; 119 120 font->setupPaint(&paint); 121 paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); 122 123 OwnArrayPtr<uint16_t> glyphs16 = adoptArrayPtr(new uint16_t[length]); 124 if (!glyphs16.get()) 125 return 0; 126 int numGlyphs = paint.textToGlyphs(characters, length * sizeof(uint16_t), glyphs16.get()); 127 128 bool canRender = true; 129 for (int i = 0; i < numGlyphs; ++i) { 130 if (!glyphs16[i]) { 131 canRender = false; 132 break; 133 } 134 } 135 136 return canRender; 137 } 138 139 static HB_Error getOutlinePoint(HB_Font hbFont, HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed* xPos, HB_Fixed* yPos, hb_uint32* resultingNumPoints) 140 { 141 FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData); 142 SkPaint paint; 143 144 if (flags & HB_ShaperFlag_UseDesignMetrics) 145 return HB_Err_Invalid_Argument; // This is requesting pre-hinted positions. We can't support this. 146 147 font->setupPaint(&paint); 148 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 149 uint16_t glyph16 = glyph; 150 SkPath path; 151 paint.getTextPath(&glyph16, sizeof(glyph16), 0, 0, &path); 152 int numPoints = path.getPoints(0, 0); 153 if (point >= static_cast<unsigned>(numPoints)) 154 return HB_Err_Invalid_SubTable; 155 SkPoint* points = reinterpret_cast<SkPoint*>(fastMalloc(sizeof(SkPoint) * (point + 1))); 156 if (!points) 157 return HB_Err_Invalid_SubTable; 158 // Skia does let us get a single point from the path. 159 path.getPoints(points, point + 1); 160 *xPos = SkiaScalarToHarfbuzzFixed(points[point].fX); 161 *yPos = SkiaScalarToHarfbuzzFixed(points[point].fY); 162 *resultingNumPoints = numPoints; 163 fastFree(points); 164 165 return HB_Err_Ok; 166 } 167 168 static void getGlyphMetrics(HB_Font hbFont, HB_Glyph glyph, HB_GlyphMetrics* metrics) 169 { 170 FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData); 171 SkPaint paint; 172 173 font->setupPaint(&paint); 174 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 175 uint16_t glyph16 = glyph; 176 SkScalar width; 177 SkRect bounds; 178 paint.getTextWidths(&glyph16, sizeof(glyph16), &width, &bounds); 179 180 metrics->x = SkiaScalarToHarfbuzzFixed(bounds.fLeft); 181 metrics->y = SkiaScalarToHarfbuzzFixed(bounds.fTop); 182 metrics->width = SkiaScalarToHarfbuzzFixed(bounds.width()); 183 metrics->height = SkiaScalarToHarfbuzzFixed(bounds.height()); 184 185 metrics->xOffset = SkiaScalarToHarfbuzzFixed(width); 186 // We can't actually get the |y| correct because Skia doesn't export 187 // the vertical advance. However, nor we do ever render vertical text at 188 // the moment so it's unimportant. 189 metrics->yOffset = 0; 190 } 191 192 static HB_Fixed getFontMetric(HB_Font hbFont, HB_FontMetric metric) 193 { 194 FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData); 195 SkPaint paint; 196 197 font->setupPaint(&paint); 198 SkPaint::FontMetrics skiaMetrics; 199 paint.getFontMetrics(&skiaMetrics); 200 201 switch (metric) { 202 case HB_FontAscent: 203 return SkiaScalarToHarfbuzzFixed(-skiaMetrics.fAscent); 204 // We don't support getting the rest of the metrics and Harfbuzz doesn't seem to need them. 205 default: 206 return 0; 207 } 208 } 209 210 HB_FontClass harfbuzzSkiaClass = { 211 stringToGlyphs, 212 glyphsToAdvances, 213 canRender, 214 getOutlinePoint, 215 getGlyphMetrics, 216 getFontMetric, 217 }; 218 219 HB_Error harfbuzzSkiaGetTable(void* voidface, const HB_Tag tag, HB_Byte* buffer, HB_UInt* len) 220 { 221 FontPlatformData* font = reinterpret_cast<FontPlatformData*>(voidface); 222 223 const size_t tableSize = SkFontHost::GetTableSize(font->uniqueID(), tag); 224 if (!tableSize) 225 return HB_Err_Invalid_Argument; 226 // If Harfbuzz specified a NULL buffer then it's asking for the size of the table. 227 if (!buffer) { 228 *len = tableSize; 229 return HB_Err_Ok; 230 } 231 232 if (*len < tableSize) 233 return HB_Err_Invalid_Argument; 234 SkFontHost::GetTableData(font->uniqueID(), tag, 0, tableSize, buffer); 235 return HB_Err_Ok; 236 } 237 238 } // namespace WebCore 239