| /* |
| * Copyright (c) 2012 Google Inc. All rights reserved. |
| * Copyright (c) 2012 Intel Corporation |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "HarfBuzzFace.h" |
| |
| #include "CairoUtilities.h" |
| #include "Font.h" |
| #include "FontCascade.h" |
| #include "FontPlatformData.h" |
| #include "GlyphBuffer.h" |
| #include "TextEncoding.h" |
| #include <cairo-ft.h> |
| #include <cairo.h> |
| #include <ft2build.h> |
| #include FT_FREETYPE_H |
| #include FT_TRUETYPE_TABLES_H |
| #include <hb.h> |
| #include <wtf/text/CString.h> |
| #include <wtf/text/StringView.h> |
| |
| namespace WebCore { |
| |
| struct HarfBuzzFontData { |
| WTF::HashMap<uint32_t, uint16_t>& glyphCacheForFaceCacheEntry; |
| RefPtr<cairo_scaled_font_t> cairoScaledFont; |
| }; |
| |
| static hb_position_t floatToHarfBuzzPosition(float value) |
| { |
| return static_cast<hb_position_t>(value * (1 << 16)); |
| } |
| |
| static hb_position_t doubleToHarfBuzzPosition(double value) |
| { |
| return static_cast<hb_position_t>(value * (1 << 16)); |
| } |
| |
| static void CairoGetGlyphWidthAndExtents(cairo_scaled_font_t* scaledFont, hb_codepoint_t codepoint, hb_position_t* advance, hb_glyph_extents_t* extents) |
| { |
| cairo_text_extents_t glyphExtents; |
| cairo_glyph_t glyph; |
| glyph.index = codepoint; |
| glyph.x = 0; |
| glyph.y = 0; |
| cairo_scaled_font_glyph_extents(scaledFont, &glyph, 1, &glyphExtents); |
| |
| bool hasVerticalGlyphs = glyphExtents.y_advance; |
| if (advance) |
| *advance = doubleToHarfBuzzPosition(hasVerticalGlyphs ? -glyphExtents.y_advance : glyphExtents.x_advance); |
| |
| if (extents) { |
| extents->x_bearing = doubleToHarfBuzzPosition(glyphExtents.x_bearing); |
| extents->y_bearing = doubleToHarfBuzzPosition(hasVerticalGlyphs ? -glyphExtents.y_bearing : glyphExtents.y_bearing); |
| extents->width = doubleToHarfBuzzPosition(hasVerticalGlyphs ? -glyphExtents.height : glyphExtents.width); |
| extents->height = doubleToHarfBuzzPosition(hasVerticalGlyphs ? glyphExtents.width : glyphExtents.height); |
| } |
| } |
| |
| static hb_bool_t harfBuzzGetGlyph(hb_font_t*, void* fontData, hb_codepoint_t unicode, hb_codepoint_t, hb_codepoint_t* glyph, void*) |
| { |
| auto& hbFontData = *static_cast<HarfBuzzFontData*>(fontData); |
| auto* scaledFont = hbFontData.cairoScaledFont.get(); |
| ASSERT(scaledFont); |
| |
| auto result = hbFontData.glyphCacheForFaceCacheEntry.add(unicode, 0); |
| if (result.isNewEntry) { |
| cairo_glyph_t* glyphs = 0; |
| int numGlyphs = 0; |
| char buffer[U8_MAX_LENGTH]; |
| size_t bufferLength = 0; |
| if (FontCascade::treatAsSpace(unicode) && unicode != '\t') |
| unicode = ' '; |
| else if (FontCascade::treatAsZeroWidthSpaceInComplexScript(unicode)) |
| unicode = zeroWidthSpace; |
| U8_APPEND_UNSAFE(buffer, bufferLength, unicode); |
| if (cairo_scaled_font_text_to_glyphs(scaledFont, 0, 0, buffer, bufferLength, &glyphs, &numGlyphs, nullptr, nullptr, nullptr) != CAIRO_STATUS_SUCCESS || !numGlyphs) |
| return false; |
| result.iterator->value = glyphs[0].index; |
| cairo_glyph_free(glyphs); |
| } |
| *glyph = result.iterator->value; |
| return !!*glyph; |
| } |
| |
| static hb_position_t harfBuzzGetGlyphHorizontalAdvance(hb_font_t*, void* fontData, hb_codepoint_t glyph, void*) |
| { |
| auto& hbFontData = *static_cast<HarfBuzzFontData*>(fontData); |
| auto* scaledFont = hbFontData.cairoScaledFont.get(); |
| ASSERT(scaledFont); |
| |
| hb_position_t advance = 0; |
| CairoGetGlyphWidthAndExtents(scaledFont, glyph, &advance, 0); |
| return advance; |
| } |
| |
| static hb_bool_t harfBuzzGetGlyphHorizontalOrigin(hb_font_t*, void*, hb_codepoint_t, hb_position_t*, hb_position_t*, void*) |
| { |
| // Just return true, following the way that Harfbuzz-FreeType |
| // implementation does. |
| return true; |
| } |
| |
| static hb_bool_t harfBuzzGetGlyphExtents(hb_font_t*, void* fontData, hb_codepoint_t glyph, hb_glyph_extents_t* extents, void*) |
| { |
| auto& hbFontData = *static_cast<HarfBuzzFontData*>(fontData); |
| auto* scaledFont = hbFontData.cairoScaledFont.get(); |
| ASSERT(scaledFont); |
| |
| CairoGetGlyphWidthAndExtents(scaledFont, glyph, 0, extents); |
| return true; |
| } |
| |
| static hb_font_funcs_t* harfBuzzCairoTextGetFontFuncs() |
| { |
| static hb_font_funcs_t* harfBuzzCairoFontFuncs = 0; |
| |
| // We don't set callback functions which we can't support. |
| // Harfbuzz will use the fallback implementation if they aren't set. |
| if (!harfBuzzCairoFontFuncs) { |
| harfBuzzCairoFontFuncs = hb_font_funcs_create(); |
| hb_font_funcs_set_glyph_func(harfBuzzCairoFontFuncs, harfBuzzGetGlyph, 0, 0); |
| hb_font_funcs_set_glyph_h_advance_func(harfBuzzCairoFontFuncs, harfBuzzGetGlyphHorizontalAdvance, 0, 0); |
| hb_font_funcs_set_glyph_h_origin_func(harfBuzzCairoFontFuncs, harfBuzzGetGlyphHorizontalOrigin, 0, 0); |
| hb_font_funcs_set_glyph_extents_func(harfBuzzCairoFontFuncs, harfBuzzGetGlyphExtents, 0, 0); |
| hb_font_funcs_make_immutable(harfBuzzCairoFontFuncs); |
| } |
| return harfBuzzCairoFontFuncs; |
| } |
| |
| static hb_blob_t* harfBuzzCairoGetTable(hb_face_t*, hb_tag_t tag, void* userData) |
| { |
| auto* scaledFont = static_cast<cairo_scaled_font_t*>(userData); |
| if (!scaledFont) |
| return 0; |
| |
| CairoFtFaceLocker cairoFtFaceLocker(scaledFont); |
| FT_Face ftFont = cairoFtFaceLocker.ftFace(); |
| if (!ftFont) |
| return nullptr; |
| |
| FT_ULong tableSize = 0; |
| FT_Error error = FT_Load_Sfnt_Table(ftFont, tag, 0, 0, &tableSize); |
| if (error) |
| return 0; |
| |
| FT_Byte* buffer = reinterpret_cast<FT_Byte*>(fastMalloc(tableSize)); |
| if (!buffer) |
| return 0; |
| FT_ULong expectedTableSize = tableSize; |
| error = FT_Load_Sfnt_Table(ftFont, tag, 0, buffer, &tableSize); |
| if (error || tableSize != expectedTableSize) { |
| fastFree(buffer); |
| return 0; |
| } |
| |
| return hb_blob_create(reinterpret_cast<const char*>(buffer), tableSize, HB_MEMORY_MODE_WRITABLE, buffer, fastFree); |
| } |
| |
| hb_face_t* HarfBuzzFace::createFace() |
| { |
| auto* scaledFont = m_platformData.scaledFont(); |
| cairo_scaled_font_reference(scaledFont); |
| |
| hb_face_t* face = hb_face_create_for_tables(harfBuzzCairoGetTable, scaledFont, |
| [](void* data) |
| { |
| cairo_scaled_font_destroy(static_cast<cairo_scaled_font_t*>(data)); |
| }); |
| ASSERT(face); |
| return face; |
| } |
| |
| hb_font_t* HarfBuzzFace::createFont() |
| { |
| hb_font_t* font = hb_font_create(m_cacheEntry->face()); |
| hb_font_set_funcs(font, harfBuzzCairoTextGetFontFuncs(), new HarfBuzzFontData { m_cacheEntry->glyphCache(), m_platformData.scaledFont() }, |
| [](void* data) |
| { |
| delete static_cast<HarfBuzzFontData*>(data); |
| }); |
| |
| const float size = m_platformData.size(); |
| if (floorf(size) == size) |
| hb_font_set_ppem(font, size, size); |
| int scale = floatToHarfBuzzPosition(size); |
| hb_font_set_scale(font, scale, scale); |
| hb_font_make_immutable(font); |
| return font; |
| } |
| |
| } // namespace WebCore |