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