/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* Cairo - a vector graphics library with display and print output * * Copyright © 2010 Mozilla Foundation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is the Mozilla Foundation * * Contributor(s): * Bas Schouten */ #include "cairoint.h" #include "cairo-win32-private.h" #include "cairo-pattern-private.h" #include "cairo-surface-private.h" #include "cairo-image-surface-private.h" #include "cairo-clip-private.h" #include "cairo-win32-refptr.hpp" #include "cairo-dwrite-private.hpp" #include "cairo-truetype-subset-private.h" #include "cairo-scaled-font-subsets-private.h" #include "cairo-dwrite.h" #include #include #include /** * SECTION:cairo-dwrite-fonts * @Title: DWrite Fonts * @Short_Description: Font support for Microsoft DirectWrite * @See_Also: #cairo_font_face_t * * The Microsoft DirectWrite font backend is primarily used to render text on * Microsoft Windows systems. **/ /** * CAIRO_HAS_DWRITE_FONT: * * Defined if the Microsoft DWrite font backend is available. * This macro can be used to conditionally compile backend-specific code. * * Since: 1.18 **/ typedef HRESULT (WINAPI*D2D1CreateFactoryFunc)( D2D1_FACTORY_TYPE factoryType, REFIID iid, CONST D2D1_FACTORY_OPTIONS *pFactoryOptions, void **factory ); #define CAIRO_INT_STATUS_SUCCESS (cairo_int_status_t)CAIRO_STATUS_SUCCESS // Forward declarations cairo_int_status_t _dwrite_draw_glyphs_to_gdi_surface_d2d(cairo_win32_surface_t *surface, DWRITE_MATRIX *transform, DWRITE_GLYPH_RUN *run, COLORREF color, const RECT &area); static cairo_int_status_t _dwrite_draw_glyphs_to_gdi_surface_gdi(cairo_win32_surface_t *surface, DWRITE_MATRIX *transform, DWRITE_GLYPH_RUN *run, COLORREF color, cairo_dwrite_scaled_font_t *scaled_font, const RECT &area); /** * _cairo_dwrite_error: * @hr HRESULT code * @context: context string to display along with the error * * Helper function to print a human readable form a HRESULT. * * Return value: A cairo status code for the error code **/ static cairo_int_status_t _cairo_dwrite_error (HRESULT hr, const char *context) { void *lpMsgBuf; if (!FormatMessageW (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, NULL, hr, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR) &lpMsgBuf, 0, NULL)) { fprintf (stderr, "%s: Unknown DWrite error HRESULT=0x%08lx\n", context, (unsigned long)hr); } else { fprintf (stderr, "%s: %S\n", context, (wchar_t *)lpMsgBuf); LocalFree (lpMsgBuf); } fflush (stderr); return (cairo_int_status_t)_cairo_error (CAIRO_STATUS_DWRITE_ERROR); } class D2DFactory { public: static RefPtr Instance() { if (!mFactoryInstance) { #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-function-type" #endif HMODULE d2d1 = _cairo_win32_load_library_from_system32 (L"d2d1.dll"); D2D1CreateFactoryFunc createD2DFactory = (D2D1CreateFactoryFunc) GetProcAddress(d2d1, "D2D1CreateFactory"); #ifdef __GNUC__ #pragma GCC diagnostic pop #endif if (createD2DFactory) { D2D1_FACTORY_OPTIONS options; options.debugLevel = D2D1_DEBUG_LEVEL_NONE; createD2DFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory), &options, (void**)&mFactoryInstance); } } return mFactoryInstance; } private: static RefPtr mFactoryInstance; }; class WICImagingFactory { public: static RefPtr Instance() { if (!mFactoryInstance) { CoInitialize(NULL); CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&mFactoryInstance)); } return mFactoryInstance; } private: static RefPtr mFactoryInstance; }; RefPtr WICImagingFactory::mFactoryInstance; cairo_atomic_once_t DWriteFactory::mOnceFactories = CAIRO_ATOMIC_ONCE_INIT; RefPtr DWriteFactory::mFactoryInstance; RefPtr DWriteFactory::mFactoryInstance1; RefPtr DWriteFactory::mFactoryInstance2; RefPtr DWriteFactory::mFactoryInstance3; RefPtr DWriteFactory::mFactoryInstance4; RefPtr DWriteFactory::mFactoryInstance8; cairo_atomic_once_t DWriteFactory::mOnceSystemCollection = CAIRO_ATOMIC_ONCE_INIT; RefPtr DWriteFactory::mSystemCollection; RefPtr D2DFactory::mFactoryInstance; static RefPtr _create_rendering_params(IDWriteRenderingParams *params, const cairo_font_options_t *options, cairo_antialias_t antialias) { RefPtr default_rendering_params; HRESULT hr; if (!params) { hr = DWriteFactory::Instance()->CreateRenderingParams(&default_rendering_params); assert(SUCCEEDED(hr)); params = default_rendering_params.get(); } FLOAT gamma = params->GetGamma(); FLOAT enhanced_contrast = params->GetEnhancedContrast(); FLOAT clear_type_level = params->GetClearTypeLevel(); DWRITE_PIXEL_GEOMETRY pixel_geometry = params->GetPixelGeometry(); DWRITE_RENDERING_MODE rendering_mode = params->GetRenderingMode(); cairo_bool_t modified = FALSE; switch (antialias) { case CAIRO_ANTIALIAS_NONE: if (rendering_mode != DWRITE_RENDERING_MODE_ALIASED) { rendering_mode = DWRITE_RENDERING_MODE_ALIASED; modified = TRUE; } break; case CAIRO_ANTIALIAS_FAST: case CAIRO_ANTIALIAS_GRAY: if (clear_type_level) { clear_type_level = 0; modified = TRUE; } break; default: break; } auto subpixel_order = cairo_font_options_get_subpixel_order (options); switch (subpixel_order) { case CAIRO_SUBPIXEL_ORDER_RGB: if (pixel_geometry != DWRITE_PIXEL_GEOMETRY_RGB) { pixel_geometry = DWRITE_PIXEL_GEOMETRY_RGB; modified = TRUE; } break; case CAIRO_SUBPIXEL_ORDER_BGR: if (pixel_geometry != DWRITE_PIXEL_GEOMETRY_BGR) { pixel_geometry = DWRITE_PIXEL_GEOMETRY_BGR; modified = TRUE; } break; default: break; } if (!modified) return params; RefPtr params1; hr = params->QueryInterface(¶ms1); if (FAILED(hr)) { RefPtr ret; DWriteFactory::Instance()->CreateCustomRenderingParams(gamma, enhanced_contrast, clear_type_level, pixel_geometry, rendering_mode, &ret); return ret; } FLOAT grayscaleEnhancedContrast = params1->GetGrayscaleEnhancedContrast(); RefPtr params2; hr = params->QueryInterface(¶ms2); if (FAILED(hr)) { RefPtr ret; DWriteFactory::Instance1()->CreateCustomRenderingParams(gamma, enhanced_contrast, grayscaleEnhancedContrast, clear_type_level, pixel_geometry, rendering_mode, &ret); return ret; } DWRITE_GRID_FIT_MODE gridFitMode = params2->GetGridFitMode(); RefPtr params3; hr = params->QueryInterface(¶ms3); if (FAILED(hr)) { RefPtr ret; DWriteFactory::Instance2()->CreateCustomRenderingParams(gamma, enhanced_contrast, grayscaleEnhancedContrast, clear_type_level, pixel_geometry, rendering_mode, gridFitMode, &ret); return ret; } DWRITE_RENDERING_MODE1 rendering_mode1 = params3->GetRenderingMode1(); if (antialias == CAIRO_ANTIALIAS_NONE) rendering_mode1 = DWRITE_RENDERING_MODE1_ALIASED; RefPtr ret; DWriteFactory::Instance3()->CreateCustomRenderingParams(gamma, enhanced_contrast, grayscaleEnhancedContrast, clear_type_level, pixel_geometry, rendering_mode1, gridFitMode, &ret); return ret; } /* Functions #cairo_font_face_backend_t */ static cairo_status_t _cairo_dwrite_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, cairo_font_face_t **font_face); static cairo_bool_t _cairo_dwrite_font_face_destroy (void *font_face); static cairo_status_t _cairo_dwrite_font_face_scaled_font_create (void *abstract_face, const cairo_matrix_t *font_matrix, const cairo_matrix_t *ctm, const cairo_font_options_t *options, cairo_scaled_font_t **font); const cairo_font_face_backend_t _cairo_dwrite_font_face_backend = { CAIRO_FONT_TYPE_DWRITE, _cairo_dwrite_font_face_create_for_toy, _cairo_dwrite_font_face_destroy, _cairo_dwrite_font_face_scaled_font_create }; /* Functions #cairo_scaled_font_backend_t */ static void _cairo_dwrite_scaled_font_fini(void *scaled_font); static cairo_warn cairo_int_status_t _cairo_dwrite_scaled_glyph_init(void *scaled_font, cairo_scaled_glyph_t *scaled_glyph, cairo_scaled_glyph_info_t info, const cairo_color_t *foreground_color); static cairo_int_status_t _cairo_dwrite_load_truetype_table(void *scaled_font, unsigned long tag, long offset, unsigned char *buffer, unsigned long *length); static unsigned long _cairo_dwrite_ucs4_to_index(void *scaled_font, uint32_t ucs4); static cairo_int_status_t _cairo_dwrite_is_synthetic(void *scaled_font, cairo_bool_t *is_synthetic); static cairo_bool_t _cairo_dwrite_has_color_glyphs(void *scaled_font); static const cairo_scaled_font_backend_t _cairo_dwrite_scaled_font_backend = { CAIRO_FONT_TYPE_DWRITE, _cairo_dwrite_scaled_font_fini, _cairo_dwrite_scaled_glyph_init, NULL, /* text_to_glyphs */ _cairo_dwrite_ucs4_to_index, _cairo_dwrite_load_truetype_table, NULL, /* index_to_ucs4 */ _cairo_dwrite_is_synthetic, NULL, /* index_to_glyph_name */ NULL, /* load_type1_data */ _cairo_dwrite_has_color_glyphs }; /* Helper conversion functions */ /** * _cairo_dwrite_matrix_from_matrix: * Get a DirectWrite matrix from a cairo matrix. Note that DirectWrite uses row * vectors where cairo uses column vectors. Hence the transposition. * * \param Cairo matrix * \return DirectWrite matrix **/ static DWRITE_MATRIX _cairo_dwrite_matrix_from_matrix(const cairo_matrix_t *matrix) { DWRITE_MATRIX dwmat; dwmat.m11 = (FLOAT)matrix->xx; dwmat.m12 = (FLOAT)matrix->yx; dwmat.m21 = (FLOAT)matrix->xy; dwmat.m22 = (FLOAT)matrix->yy; dwmat.dx = (FLOAT)matrix->x0; dwmat.dy = (FLOAT)matrix->y0; return dwmat; } /* Helper functions for cairo_dwrite_scaled_glyph_init() */ static cairo_int_status_t _cairo_dwrite_scaled_font_init_glyph_metrics (cairo_dwrite_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph); static cairo_int_status_t _cairo_dwrite_scaled_font_init_glyph_color_surface(cairo_dwrite_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph, const cairo_color_t *foreground_color); static cairo_int_status_t _cairo_dwrite_scaled_font_init_glyph_surface (cairo_dwrite_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph); static cairo_int_status_t _cairo_dwrite_scaled_font_init_glyph_path (cairo_dwrite_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph); /* implement the font backend interface */ static cairo_status_t _cairo_dwrite_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, cairo_font_face_t **font_face) { WCHAR *face_name; int face_name_len; if (!DWriteFactory::Instance()) { return (cairo_status_t)CAIRO_INT_STATUS_UNSUPPORTED; } face_name_len = MultiByteToWideChar(CP_UTF8, 0, toy_face->family, -1, NULL, 0); face_name = new WCHAR[face_name_len]; MultiByteToWideChar(CP_UTF8, 0, toy_face->family, -1, face_name, face_name_len); RefPtr family = DWriteFactory::FindSystemFontFamily(face_name); delete[] face_name; if (!family) { /* If the family is not found, use the default that should always exist. */ face_name_len = MultiByteToWideChar(CP_UTF8, 0, CAIRO_FONT_FAMILY_DEFAULT, -1, NULL, 0); face_name = new WCHAR[face_name_len]; MultiByteToWideChar(CP_UTF8, 0, CAIRO_FONT_FAMILY_DEFAULT, -1, face_name, face_name_len); family = DWriteFactory::FindSystemFontFamily(face_name); delete[] face_name; if (!family) { *font_face = (cairo_font_face_t*)&_cairo_font_face_nil; return (cairo_status_t)CAIRO_INT_STATUS_UNSUPPORTED; } } DWRITE_FONT_WEIGHT weight; switch (toy_face->weight) { case CAIRO_FONT_WEIGHT_BOLD: weight = DWRITE_FONT_WEIGHT_BOLD; break; case CAIRO_FONT_WEIGHT_NORMAL: default: weight = DWRITE_FONT_WEIGHT_NORMAL; break; } DWRITE_FONT_STYLE style; switch (toy_face->slant) { case CAIRO_FONT_SLANT_ITALIC: style = DWRITE_FONT_STYLE_ITALIC; break; case CAIRO_FONT_SLANT_OBLIQUE: style = DWRITE_FONT_STYLE_OBLIQUE; break; case CAIRO_FONT_SLANT_NORMAL: default: style = DWRITE_FONT_STYLE_NORMAL; break; } RefPtr font; HRESULT hr = family->GetFirstMatchingFont(weight, DWRITE_FONT_STRETCH_NORMAL, style, &font); if (FAILED(hr)) return (cairo_status_t)_cairo_dwrite_error (hr, "GetFirstMatchingFont failed"); RefPtr dwriteface; hr = font->CreateFontFace(&dwriteface); if (FAILED(hr)) return (cairo_status_t)_cairo_dwrite_error (hr, "CreateFontFace failed"); *font_face = cairo_dwrite_font_face_create_for_dwrite_fontface(dwriteface); return CAIRO_STATUS_SUCCESS; } static cairo_bool_t _cairo_dwrite_font_face_destroy (void *font_face) { cairo_dwrite_font_face_t *dwrite_font_face = static_cast(font_face); if (dwrite_font_face->dwriteface) dwrite_font_face->dwriteface->Release(); if (dwrite_font_face->rendering_params) dwrite_font_face->rendering_params->Release(); return TRUE; } static inline unsigned short read_short(const char *buf) { return be16_to_cpu(*(unsigned short*)buf); } void _cairo_dwrite_glyph_run_from_glyphs(cairo_glyph_t *glyphs, int num_glyphs, cairo_dwrite_scaled_font_t *scaled_font, AutoDWriteGlyphRun *run, cairo_bool_t *transformed) { run->allocate(num_glyphs); UINT16 *indices = const_cast(run->glyphIndices); FLOAT *advances = const_cast(run->glyphAdvances); DWRITE_GLYPH_OFFSET *offsets = const_cast(run->glyphOffsets); run->bidiLevel = 0; run->fontFace = scaled_font->dwriteface; run->glyphCount = num_glyphs; run->isSideways = FALSE; if (scaled_font->mat.xy == 0 && scaled_font->mat.yx == 0 && scaled_font->mat.xx == scaled_font->base.font_matrix.xx && scaled_font->mat.yy == scaled_font->base.font_matrix.yy) { // Fast route, don't actually use a transform but just // set the correct font size. *transformed = 0; run->fontEmSize = (FLOAT)scaled_font->base.font_matrix.yy; for (int i = 0; i < num_glyphs; i++) { indices[i] = (WORD) glyphs[i].index; offsets[i].ascenderOffset = -(FLOAT)(glyphs[i].y); offsets[i].advanceOffset = (FLOAT)(glyphs[i].x); advances[i] = 0.0; } } else { *transformed = 1; // Transforming positions by the inverse matrix, then by the original // matrix later may introduce small errors, especially because the // D2D matrix is single-precision whereas the cairo one is double. // This is a problem when glyph positions were originally at exactly // half-pixel locations, which eventually round to whole pixels for // GDI rendering - the errors introduced here cause them to round in // unpredictable directions, instead of all rounding in a consistent // way, leading to poor glyph spacing (bug 675383). // To mitigate this, nudge the positions by a tiny amount to try and // ensure that after the two transforms, they'll still round in a // consistent direction. const double EPSILON = 0.0001; for (int i = 0; i < num_glyphs; i++) { indices[i] = (WORD) glyphs[i].index; double x = glyphs[i].x + EPSILON; double y = glyphs[i].y; cairo_matrix_transform_point(&scaled_font->mat_inverse, &x, &y); // Since we will multiply by our ctm matrix later for rotation effects // and such, adjust positions by the inverse matrix now. Y-axis is // inverted! Therefor the offset is -y. offsets[i].ascenderOffset = -(FLOAT)y; offsets[i].advanceOffset = (FLOAT)x; advances[i] = 0.0; } // The font matrix takes care of the scaling if we have a transform, // emSize should be 1. run->fontEmSize = 1.0f; } } #define GASP_TAG 0x70736167 #define GASP_DOGRAY 0x2 static cairo_bool_t do_grayscale(IDWriteFontFace *dwface, unsigned int ppem) { void *tableContext; char *tableData; UINT32 tableSize; BOOL exists; dwface->TryGetFontTable(GASP_TAG, (const void**)&tableData, &tableSize, &tableContext, &exists); if (exists) { if (tableSize < 4) { dwface->ReleaseFontTable(tableContext); return true; } struct gaspRange { unsigned short maxPPEM; // Stored big-endian unsigned short behavior; // Stored big-endian }; unsigned short numRanges = read_short(tableData + 2); if (tableSize < (UINT)4 + numRanges * 4) { dwface->ReleaseFontTable(tableContext); return true; } gaspRange *ranges = (gaspRange *)(tableData + 4); for (int i = 0; i < numRanges; i++) { if (be16_to_cpu(ranges[i].maxPPEM) > ppem) { if (!(be16_to_cpu(ranges[i].behavior) & GASP_DOGRAY)) { dwface->ReleaseFontTable(tableContext); return false; } break; } } dwface->ReleaseFontTable(tableContext); } return true; } static void set_dwrite_axes_from_string (const char *string, DWRITE_FONT_AXIS_VALUE *dwrite_axes, UINT32 count) { const char *p = string; while (p && *p) { const char *start; const char *end, *end2; DWRITE_FONT_AXIS_TAG tag; double value; while (_cairo_isspace (*p)) p++; start = p; end = strchr (p, ','); if (end && (end - p < 6)) goto skip; tag = DWRITE_MAKE_FONT_AXIS_TAG (p[0], p[1], p[2], p[3]); p += 4; while (_cairo_isspace (*p)) p++; if (*p == '=') p++; if (p - start < 5) goto skip; value = _cairo_strtod (p, (char **) &end2); while (end2 && _cairo_isspace (*end2)) end2++; if (end2 && (*end2 != ',' && *end2 != '\0')) goto skip; for (UINT32 i = 0; i < count; i++) { if (dwrite_axes[i].axisTag == tag) { dwrite_axes[i].value = value; break; } } skip: p = end ? end + 1 : NULL; } } static cairo_status_t _cairo_dwrite_font_face_scaled_font_create (void *abstract_face, const cairo_matrix_t *font_matrix, const cairo_matrix_t *ctm, const cairo_font_options_t *options, cairo_scaled_font_t **font) { cairo_status_t status; cairo_dwrite_font_face_t *font_face = static_cast(abstract_face); /* Must do malloc and not C++ new, since Cairo frees this. */ cairo_dwrite_scaled_font_t *dwrite_font = (cairo_dwrite_scaled_font_t*)_cairo_calloc( sizeof(cairo_dwrite_scaled_font_t)); if (unlikely(dwrite_font == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); *font = reinterpret_cast(dwrite_font); status = _cairo_scaled_font_init (&dwrite_font->base, &font_face->base, font_matrix, ctm, options, &_cairo_dwrite_scaled_font_backend); if (status) { free(dwrite_font); return status; } if (options->variations) { RefPtr dwriteface5; /* Since Windows 10 20348 */ if (SUCCEEDED (font_face->dwriteface->QueryInterface(&dwriteface5))) { RefPtr dwritefontresource; if (dwriteface5->HasVariations () && SUCCEEDED (dwriteface5->GetFontResource (&dwritefontresource))) { UINT32 count = MIN (dwriteface5->GetFontAxisValueCount (), 500); DWRITE_FONT_AXIS_VALUE *dwrite_axes = new DWRITE_FONT_AXIS_VALUE[count]; UINT32 variables_count = 0; /* Sort variable axes first */ for (UINT32 i = 0; i < count; i++) { if (dwritefontresource->GetFontAxisAttributes (i) & DWRITE_FONT_AXIS_ATTRIBUTES_VARIABLE) { if (variables_count != i) { DWRITE_FONT_AXIS_VALUE swap_aux = dwrite_axes[variables_count]; dwrite_axes[variables_count] = dwrite_axes[i]; dwrite_axes[i] = swap_aux; } variables_count++; } } if (SUCCEEDED (dwriteface5->GetFontAxisValues(dwrite_axes, count))) { RefPtr dwriteface_new5; set_dwrite_axes_from_string (options->variations, dwrite_axes, variables_count); /* Can't use constexpr with mingw-w64 headers */ const DWRITE_FONT_SIMULATIONS all_simulations = DWRITE_FONT_SIMULATIONS_BOLD | DWRITE_FONT_SIMULATIONS_OBLIQUE; if (SUCCEEDED (dwritefontresource->CreateFontFace(all_simulations, dwrite_axes, count, &dwriteface_new5))) { dwrite_font->dwriteface = dwriteface_new5.forget().drop(); } } delete[] dwrite_axes; } } } if (!dwrite_font->dwriteface) { font_face->dwriteface->AddRef (); dwrite_font->dwriteface = font_face->dwriteface; } dwrite_font->mat = dwrite_font->base.ctm; cairo_matrix_multiply(&dwrite_font->mat, &dwrite_font->mat, font_matrix); dwrite_font->mat_inverse = dwrite_font->mat; cairo_matrix_invert (&dwrite_font->mat_inverse); cairo_font_extents_t extents; DWRITE_FONT_METRICS metrics; if (dwrite_font->measuring_mode == DWRITE_MEASURING_MODE_GDI_CLASSIC || dwrite_font->measuring_mode == DWRITE_MEASURING_MODE_GDI_NATURAL) { DWRITE_MATRIX transform = _cairo_dwrite_matrix_from_matrix (&dwrite_font->mat); dwrite_font->dwriteface->GetGdiCompatibleMetrics(1, 1, &transform, &metrics); } else { dwrite_font->dwriteface->GetMetrics(&metrics); } extents.ascent = (FLOAT)metrics.ascent / metrics.designUnitsPerEm; extents.descent = (FLOAT)metrics.descent / metrics.designUnitsPerEm; extents.height = (FLOAT)(metrics.ascent + metrics.descent + metrics.lineGap) / metrics.designUnitsPerEm; extents.max_x_advance = 14.0; extents.max_y_advance = 0.0; cairo_antialias_t default_quality = CAIRO_ANTIALIAS_SUBPIXEL; // The following code detects the system quality at scaled_font creation time, // this means that if cleartype settings are changed but the scaled_fonts // are re-used, they might not adhere to the new system setting until re- // creation. switch (cairo_win32_get_system_text_quality()) { case CLEARTYPE_QUALITY: default_quality = CAIRO_ANTIALIAS_SUBPIXEL; break; case ANTIALIASED_QUALITY: default_quality = CAIRO_ANTIALIAS_GRAY; break; case DEFAULT_QUALITY: // _get_system_quality() seems to think aliased is default! default_quality = CAIRO_ANTIALIAS_NONE; break; } if (default_quality == CAIRO_ANTIALIAS_GRAY) { if (!do_grayscale(dwrite_font->dwriteface, (unsigned int)_cairo_round(font_matrix->yy))) { default_quality = CAIRO_ANTIALIAS_NONE; } } if (options->antialias == CAIRO_ANTIALIAS_DEFAULT) { dwrite_font->antialias_mode = default_quality; } else { dwrite_font->antialias_mode = options->antialias; } dwrite_font->rendering_params = _create_rendering_params(font_face->rendering_params, options, dwrite_font->antialias_mode).forget().drop(); dwrite_font->measuring_mode = font_face->measuring_mode; return _cairo_scaled_font_set_metrics (*font, &extents); } /* Implementation #cairo_dwrite_scaled_font_backend_t */ static void _cairo_dwrite_scaled_font_fini(void *scaled_font) { cairo_dwrite_scaled_font_t *dwrite_font = static_cast(scaled_font); dwrite_font->dwriteface->Release (); if (dwrite_font->rendering_params) dwrite_font->rendering_params->Release(); } static cairo_int_status_t _cairo_dwrite_scaled_glyph_init(void *scaled_font, cairo_scaled_glyph_t *scaled_glyph, cairo_scaled_glyph_info_t info, const cairo_color_t *foreground_color) { cairo_dwrite_scaled_font_t *scaled_dwrite_font = static_cast(scaled_font); cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; if ((info & CAIRO_SCALED_GLYPH_INFO_METRICS) != 0) { status = _cairo_dwrite_scaled_font_init_glyph_metrics (scaled_dwrite_font, scaled_glyph); if (status) return status; } if (info & CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE) { status = _cairo_dwrite_scaled_font_init_glyph_color_surface (scaled_dwrite_font, scaled_glyph, foreground_color); if (status) return status; } if (info & CAIRO_SCALED_GLYPH_INFO_SURFACE) { status = _cairo_dwrite_scaled_font_init_glyph_surface (scaled_dwrite_font, scaled_glyph); if (status) return status; } if ((info & CAIRO_SCALED_GLYPH_INFO_PATH) != 0) { status = _cairo_dwrite_scaled_font_init_glyph_path (scaled_dwrite_font, scaled_glyph); if (status) return status; } return CAIRO_INT_STATUS_SUCCESS; } static unsigned long _cairo_dwrite_ucs4_to_index(void *scaled_font, uint32_t ucs4) { cairo_dwrite_scaled_font_t *dwritesf = static_cast(scaled_font); UINT16 index = 0; dwritesf->dwriteface->GetGlyphIndicesA(&ucs4, 1, &index); return index; } /* cairo_dwrite_scaled_glyph_init() helper function bodies */ static cairo_int_status_t _cairo_dwrite_scaled_font_init_glyph_metrics(cairo_dwrite_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph) { UINT16 charIndex = (UINT16)_cairo_scaled_glyph_index (scaled_glyph); cairo_dwrite_font_face_t *font_face = (cairo_dwrite_font_face_t*)scaled_font->base.font_face; cairo_text_extents_t extents; DWRITE_GLYPH_METRICS metrics; DWRITE_FONT_METRICS fontMetrics; HRESULT hr; if (font_face->measuring_mode == DWRITE_MEASURING_MODE_GDI_CLASSIC || font_face->measuring_mode == DWRITE_MEASURING_MODE_GDI_NATURAL) { DWRITE_MATRIX transform = _cairo_dwrite_matrix_from_matrix (&scaled_font->mat); scaled_font->dwriteface->GetGdiCompatibleMetrics(1, 1, &transform, &fontMetrics); BOOL natural = font_face->measuring_mode == DWRITE_MEASURING_MODE_GDI_NATURAL; hr = scaled_font->dwriteface->GetGdiCompatibleGlyphMetrics (1, 1, &transform, natural, &charIndex, 1, &metrics, FALSE); } else { scaled_font->dwriteface->GetMetrics(&fontMetrics); hr = scaled_font->dwriteface->GetDesignGlyphMetrics(&charIndex, 1, &metrics); } if (FAILED(hr)) { return CAIRO_INT_STATUS_UNSUPPORTED; } // GetGdiCompatibleMetrics may return a glyph metrics that yields a small nagative glyph height. INT32 glyph_width = metrics.advanceWidth - metrics.leftSideBearing - metrics.rightSideBearing; INT32 glyph_height = metrics.advanceHeight - metrics.topSideBearing - metrics.bottomSideBearing; glyph_width = MAX(glyph_width, 0); glyph_height = MAX(glyph_height, 0); // TODO: Treat swap_xy. extents.width = (FLOAT)glyph_width / fontMetrics.designUnitsPerEm; extents.height = (FLOAT)glyph_height / fontMetrics.designUnitsPerEm; extents.x_advance = (FLOAT)metrics.advanceWidth / fontMetrics.designUnitsPerEm; extents.x_bearing = (FLOAT)metrics.leftSideBearing / fontMetrics.designUnitsPerEm; extents.y_advance = 0.0; extents.y_bearing = (FLOAT)(metrics.topSideBearing - metrics.verticalOriginY) / fontMetrics.designUnitsPerEm; // We pad the extents here because GetDesignGlyphMetrics returns "ideal" metrics // for the glyph outline, without accounting for hinting/gridfitting/antialiasing, // and therefore it does not always cover all pixels that will actually be touched. if (extents.width > 0 && extents.height > 0) { double x = 1, y = 1; cairo_matrix_transform_distance (&scaled_font->mat_inverse, &x, &y); x = fabs(x); y = fabs(y); extents.width += x * 2; extents.x_bearing -= x; extents.height += y * 2; extents.y_bearing -= y; } _cairo_scaled_glyph_set_metrics (scaled_glyph, &scaled_font->base, &extents); return CAIRO_INT_STATUS_SUCCESS; } /* * Stack-based helper implementing IDWriteGeometrySink. * Used to determine the path of the glyphs. */ class GeometryRecorder final : public IDWriteGeometrySink { public: GeometryRecorder(cairo_path_fixed_t *aCairoPath, const cairo_matrix_t &matrix) : mCairoPath(aCairoPath) , mMatrix(matrix) {} IFACEMETHOD (QueryInterface)(IID const& iid, OUT void** ppObject) noexcept override { if (iid == __uuidof (IUnknown) || iid == __uuidof (IDWriteGeometrySink)) { AddRef(); *ppObject = this; return S_OK; } *ppObject = nullptr; return E_NOINTERFACE; } IFACEMETHOD_(ULONG, AddRef)() noexcept override { return 1; } IFACEMETHOD_(ULONG, Release)() noexcept override { return 1; } IFACEMETHOD_(void, SetFillMode)(D2D1_FILL_MODE fillMode) noexcept override { return; } IFACEMETHOD (Close)() noexcept override { return S_OK; } IFACEMETHOD_(void, SetSegmentFlags)(D2D1_PATH_SEGMENT vertexFlags) noexcept override { return; } IFACEMETHOD_(void, BeginFigure)( D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN figureBegin) noexcept override { double x = startPoint.x; double y = startPoint.y; cairo_matrix_transform_point(&mMatrix, &x, &y); mStartPointX = _cairo_fixed_from_double(x); mStartPointY = _cairo_fixed_from_double(y); cairo_status_t status = _cairo_path_fixed_move_to(mCairoPath, mStartPointX, mStartPointY); (void)status; /* squelch warning */ } IFACEMETHOD_(void, EndFigure)( D2D1_FIGURE_END figureEnd) noexcept override { if (figureEnd == D2D1_FIGURE_END_CLOSED) { cairo_status_t status = _cairo_path_fixed_line_to(mCairoPath, mStartPointX, mStartPointY); (void)status; /* squelch warning */ } } IFACEMETHOD_(void, AddBeziers)( const D2D1_BEZIER_SEGMENT *beziers, UINT beziersCount) noexcept override { for (unsigned int i = 0; i < beziersCount; i++) { double x1 = beziers[i].point1.x; double y1 = beziers[i].point1.y; double x2 = beziers[i].point2.x; double y2 = beziers[i].point2.y; double x3 = beziers[i].point3.x; double y3 = beziers[i].point3.y; cairo_matrix_transform_point(&mMatrix, &x1, &y1); cairo_matrix_transform_point(&mMatrix, &x2, &y2); cairo_matrix_transform_point(&mMatrix, &x3, &y3); cairo_status_t status = _cairo_path_fixed_curve_to(mCairoPath, _cairo_fixed_from_double(x1), _cairo_fixed_from_double(y1), _cairo_fixed_from_double(x2), _cairo_fixed_from_double(y2), _cairo_fixed_from_double(x3), _cairo_fixed_from_double(y3)); (void)status; /* squelch warning */ } } IFACEMETHOD_(void, AddLines)( const D2D1_POINT_2F *points, UINT pointsCount) noexcept override { for (unsigned int i = 0; i < pointsCount; i++) { double x = points[i].x; double y = points[i].y; cairo_matrix_transform_point(&mMatrix, &x, &y); cairo_status_t status = _cairo_path_fixed_line_to(mCairoPath, _cairo_fixed_from_double(x), _cairo_fixed_from_double(y)); (void)status; /* squelch warning */ } } private: cairo_path_fixed_t *mCairoPath; const cairo_matrix_t &mMatrix; cairo_fixed_t mStartPointX; cairo_fixed_t mStartPointY; }; static cairo_int_status_t _cairo_dwrite_scaled_font_init_glyph_path(cairo_dwrite_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph) { cairo_int_status_t status; cairo_path_fixed_t *path; path = _cairo_path_fixed_create(); GeometryRecorder recorder(path, scaled_font->base.scale); DWRITE_GLYPH_OFFSET offset; offset.advanceOffset = 0; offset.ascenderOffset = 0; UINT16 glyphId = (UINT16)_cairo_scaled_glyph_index(scaled_glyph); FLOAT advance = 0.0; HRESULT hr = scaled_font->dwriteface->GetGlyphRunOutline(1, &glyphId, &advance, &offset, 1, FALSE, FALSE, &recorder); if (!SUCCEEDED(hr)) return _cairo_dwrite_error (hr, "GetGlyphRunOutline failed"); status = (cairo_int_status_t)_cairo_path_fixed_close_path(path); _cairo_scaled_glyph_set_path (scaled_glyph, &scaled_font->base, path); return status; } static cairo_int_status_t _cairo_dwrite_scaled_font_init_glyph_color_surface(cairo_dwrite_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph, const cairo_color_t *foreground_color) { int width, height; double x1, y1, x2, y2; cairo_bool_t uses_foreground_color = FALSE; cairo_dwrite_font_face_t *dwrite_font_face = (cairo_dwrite_font_face_t *)scaled_font->base.font_face; if (!dwrite_font_face->have_color) { scaled_glyph->color_glyph = FALSE; scaled_glyph->color_glyph_set = TRUE; return CAIRO_INT_STATUS_UNSUPPORTED; } x1 = _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x); y1 = _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y); x2 = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x); y2 = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y); width = (int)(x2 - x1); height = (int)(y2 - y1); DWRITE_GLYPH_RUN run; FLOAT advance = 0; UINT16 index = (UINT16)_cairo_scaled_glyph_index (scaled_glyph); DWRITE_GLYPH_OFFSET offset; double x = -x1 + .25 * _cairo_scaled_glyph_xphase (scaled_glyph); double y = -y1 + .25 * _cairo_scaled_glyph_yphase (scaled_glyph); DWRITE_MATRIX matrix; D2D1_POINT_2F origin = {0, 0}; RefPtr run_enumerator; HRESULT hr; /* * We transform by the inverse transformation here. This will put our glyph * locations in the space in which we draw. Which is later transformed by * the transformation matrix that we use. This will transform the * glyph positions back to where they were before when drawing, but the * glyph shapes will be transformed by the transformation matrix. */ cairo_matrix_transform_point(&scaled_font->mat_inverse, &x, &y); offset.advanceOffset = (FLOAT)x; /* Y-axis is inverted */ offset.ascenderOffset = -(FLOAT)y; run.fontFace = scaled_font->dwriteface; run.fontEmSize = 1; run.glyphCount = 1; run.glyphIndices = &index; run.glyphAdvances = &advance; run.glyphOffsets = &offset; run.isSideways = FALSE; run.bidiLevel = 0; matrix = _cairo_dwrite_matrix_from_matrix(&scaled_font->mat); /* The list of glyph image formats this renderer is prepared to support. */ const DWRITE_GLYPH_IMAGE_FORMATS_ base_formats = ( DWRITE_GLYPH_IMAGE_FORMATS_COLR_ | DWRITE_GLYPH_IMAGE_FORMATS_SVG_ | DWRITE_GLYPH_IMAGE_FORMATS_PNG_ | DWRITE_GLYPH_IMAGE_FORMATS_JPEG_ | DWRITE_GLYPH_IMAGE_FORMATS_TIFF_ | DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8_); // Level of support for DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE DWRITE_PAINT_FEATURE_LEVEL dwrite_paint_feature_level = DWRITE_PAINT_FEATURE_LEVEL_COLR_V1; RefPtr fontFace2; UINT32 palette_count = 0; if (SUCCEEDED(scaled_font->dwriteface->QueryInterface(&fontFace2))) palette_count = fontFace2->GetColorPaletteCount(); UINT32 palette_index = CAIRO_COLOR_PALETTE_DEFAULT; if (scaled_font->base.options.palette_index < palette_count) palette_index = scaled_font->base.options.palette_index; if (DWriteFactory::Instance8().get()) { hr = DWriteFactory::Instance8()->TranslateColorGlyphRun( origin, &run, NULL, /* glyphRunDescription */ static_cast (base_formats | DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE_), dwrite_paint_feature_level, dwrite_font_face->measuring_mode, &matrix, palette_index, &run_enumerator); } else if (DWriteFactory::Instance4().get()) { hr = DWriteFactory::Instance4()->TranslateColorGlyphRun( origin, &run, NULL, /* glyphRunDescription */ static_cast (base_formats), dwrite_font_face->measuring_mode, &matrix, palette_index, &run_enumerator); } else { // TODO return CAIRO_INT_STATUS_UNSUPPORTED; } if (hr == DWRITE_E_NOCOLOR) { /* No color glyphs */ scaled_glyph->color_glyph = FALSE; scaled_glyph->color_glyph_set = TRUE; return CAIRO_INT_STATUS_UNSUPPORTED; } if (FAILED(hr)) return _cairo_dwrite_error (hr, "TranslateColorGlyphRun failed"); /* We have a color glyph(s). Use Direct2D to render it to a bitmap */ if (!WICImagingFactory::Instance() || !D2DFactory::Instance()) return _cairo_dwrite_error (hr, "Instance failed"); RefPtr bitmap; hr = WICImagingFactory::Instance()->CreateBitmap ((UINT)width, (UINT)height, GUID_WICPixelFormat32bppPBGRA, WICBitmapCacheOnLoad, &bitmap); if (FAILED(hr)) return _cairo_dwrite_error (hr, "CreateBitmap failed"); D2D1_RENDER_TARGET_PROPERTIES properties = D2D1::RenderTargetProperties( D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat( DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED), 0, 0, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT); RefPtr rt; hr = D2DFactory::Instance()->CreateWicBitmapRenderTarget (bitmap, properties, &rt); if (FAILED(hr)) return _cairo_dwrite_error (hr, "CreateWicBitmapRenderTarget failed"); RefPtr dc4; hr = rt->QueryInterface(&dc4); if (FAILED(hr)) return _cairo_dwrite_error (hr, "QueryInterface(&dc4) failed"); RefPtr foreground_color_brush; dc4->CreateSolidColorBrush( D2D1::ColorF(foreground_color->red, foreground_color->green, foreground_color->blue, foreground_color->alpha), &foreground_color_brush); RefPtr color_brush; dc4->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &color_brush); dc4->SetDpi(96, 96); /* 1 unit = 1 pixel */ rt->SetTransform(D2D1::Matrix3x2F(matrix.m11, matrix.m12, matrix.m21, matrix.m22, matrix.dx, matrix.dy)); dc4->BeginDraw(); dc4->Clear(NULL); /* Transparent black */ while (true) { BOOL have_run; hr = run_enumerator->MoveNext(&have_run); if (FAILED(hr) || !have_run) break; DWRITE_COLOR_GLYPH_RUN1_WORKAROUND const* color_run; hr = run_enumerator->GetCurrentRun(reinterpret_cast(&color_run)); if (FAILED(hr)) return _cairo_dwrite_error (hr, "GetCurrentRun failed"); const auto format = static_cast(color_run->glyphImageFormat); switch (format) { case DWRITE_GLYPH_IMAGE_FORMATS_PNG_: case DWRITE_GLYPH_IMAGE_FORMATS_JPEG_: case DWRITE_GLYPH_IMAGE_FORMATS_TIFF_: case DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8_: /* Bitmap glyphs */ dc4->DrawColorBitmapGlyphRun(color_run->glyphImageFormat, origin, &color_run->glyphRun, dwrite_font_face->measuring_mode, D2D1_COLOR_BITMAP_GLYPH_SNAP_OPTION_DEFAULT); break; case DWRITE_GLYPH_IMAGE_FORMATS_SVG_: /* SVG glyphs */ dc4->DrawSvgGlyphRun(origin, &color_run->glyphRun, foreground_color_brush, nullptr, palette_index, dwrite_font_face->measuring_mode); uses_foreground_color = TRUE; break; case DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE_: case DWRITE_GLYPH_IMAGE_FORMATS_CFF_: case DWRITE_GLYPH_IMAGE_FORMATS_COLR_: /* Outline glyphs */ if (color_run->paletteIndex == 0xFFFF) { D2D1_COLOR_F color = foreground_color_brush->GetColor(); color_brush->SetColor(&color); uses_foreground_color = TRUE; } else { double red, green, blue, alpha; cairo_status_t status; status = cairo_font_options_get_custom_palette_color (&scaled_font->base.options, color_run->paletteIndex, &red, &blue, &green, &alpha); if (status == CAIRO_STATUS_SUCCESS) { color_brush->SetColor(D2D1::ColorF(red, blue, green, alpha)); } else { color_brush->SetColor(color_run->runColor); } } dc4->DrawGlyphRun(origin, &color_run->glyphRun, color_run->glyphRunDescription, color_brush, dwrite_font_face->measuring_mode); break; case DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE_: { RefPtr dc7; hr = rt->QueryInterface(&dc7); if (FAILED(hr)) return _cairo_dwrite_error (hr, "QueryInterface(&dc7) failed"); dc7->DrawPaintGlyphRun (origin, &color_run->glyphRun, foreground_color_brush, palette_index, dwrite_font_face->measuring_mode); break; } case DWRITE_GLYPH_IMAGE_FORMATS_NONE_: break; } } hr = dc4->EndDraw(); if (FAILED(hr)) return _cairo_dwrite_error (hr, "EndDraw failed"); cairo_surface_t *image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); int stride = cairo_image_surface_get_stride (image); WICRect rect = { 0, 0, width, height }; bitmap->CopyPixels(&rect, stride, height * stride, cairo_image_surface_get_data (image)); cairo_surface_mark_dirty (image); cairo_surface_set_device_offset (image, -x1, -y1); _cairo_scaled_glyph_set_color_surface (scaled_glyph, &scaled_font->base, (cairo_image_surface_t *) image, uses_foreground_color ? foreground_color : NULL); scaled_glyph->color_glyph = TRUE; scaled_glyph->color_glyph_set = TRUE; return CAIRO_INT_STATUS_SUCCESS; } // Helper for OS versions up to Windows 8 static cairo_int_status_t init_glyph_surface_fallback_a8 (cairo_dwrite_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph, int width, int height, double x1, double y1, DWRITE_MATRIX *matrix, DWRITE_GLYPH_RUN *run) { RefPtr bitmap; HRESULT hr; hr = WICImagingFactory::Instance()->CreateBitmap ((UINT)width, (UINT)height, GUID_WICPixelFormat8bppAlpha, WICBitmapCacheOnLoad, &bitmap); if (FAILED(hr)) return _cairo_dwrite_error (hr, "CreateBitmap failed"); D2D1_RENDER_TARGET_PROPERTIES properties = D2D1::RenderTargetProperties( D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat( DXGI_FORMAT_A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED), 0, 0, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT); RefPtr rt; hr = D2DFactory::Instance()->CreateWicBitmapRenderTarget (bitmap, properties, &rt); if (FAILED(hr)) return _cairo_dwrite_error (hr, "CreateWicBitmapRenderTarget failed"); RefPtr brush; hr = rt->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black, 1.0), &brush); rt->BeginDraw(); rt->SetTransform(*(D2D1_MATRIX_3X2_F*)matrix); rt->DrawGlyphRun({0, 0}, run, brush, scaled_font->measuring_mode); hr = rt->EndDraw(); if (FAILED(hr)) return _cairo_dwrite_error (hr, "EndDraw failed"); // TODO: rt->Flush()? cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height); if (cairo_surface_status (surface)) return CAIRO_INT_STATUS_UNSUPPORTED; int stride = cairo_image_surface_get_stride (surface); WICRect rect = { 0, 0, width, height }; bitmap->CopyPixels(&rect, stride, height * stride, cairo_image_surface_get_data (surface)); cairo_surface_mark_dirty (surface); cairo_surface_set_device_offset (surface, -x1, -y1); _cairo_scaled_glyph_set_surface (scaled_glyph, &scaled_font->base, (cairo_image_surface_t*)surface); return CAIRO_INT_STATUS_SUCCESS; } static cairo_int_status_t _cairo_dwrite_scaled_font_init_glyph_surface (cairo_dwrite_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph) { HRESULT hr; double x1 = _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x); double y1 = _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y); double x2 = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x); double y2 = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y); int width = (int)(x2 - x1); int height = (int)(y2 - y1); if (width <= 0) width = 1; if (height <= 0) height = 1; FLOAT advance = 0; UINT16 index = (UINT16)_cairo_scaled_glyph_index (scaled_glyph); DWRITE_GLYPH_OFFSET offset; double x = -x1 + .25 * _cairo_scaled_glyph_xphase (scaled_glyph); double y = -y1 + .25 * _cairo_scaled_glyph_yphase (scaled_glyph); /* We transform by the inverse transformation here. This will put our glyph * locations in the space in which we draw. Which is later transformed by * the transformation matrix that we use. This will transform the * glyph positions back to where they were before when drawing, but the * glyph shapes will be transformed by the transformation matrix. */ cairo_matrix_transform_point(&scaled_font->mat_inverse, &x, &y); offset.advanceOffset = (FLOAT)x; offset.ascenderOffset = -(FLOAT)y; /* Y axis is inverted */ DWRITE_MATRIX matrix = _cairo_dwrite_matrix_from_matrix(&scaled_font->mat); DWRITE_GLYPH_RUN run; run.glyphCount = 1; run.glyphAdvances = &advance; run.fontFace = scaled_font->dwriteface; run.fontEmSize = 1.0f; run.bidiLevel = 0; run.glyphIndices = &index; run.isSideways = FALSE; run.glyphOffsets = &offset; // Reduce the many Cairo antialias values to the // three we actually care about: NONE, GRAY, RGB enum { ANTIALIAS_NONE, ANTIALIAS_GRAY, ANTIALIAS_CLEARTYPE, } antialias = ANTIALIAS_CLEARTYPE; switch (scaled_font->antialias_mode) { case CAIRO_ANTIALIAS_NONE: antialias = ANTIALIAS_NONE; break; case CAIRO_ANTIALIAS_FAST: case CAIRO_ANTIALIAS_GRAY: antialias = ANTIALIAS_GRAY; break; case CAIRO_ANTIALIAS_DEFAULT: case CAIRO_ANTIALIAS_GOOD: case CAIRO_ANTIALIAS_BEST: case CAIRO_ANTIALIAS_SUBPIXEL: antialias = ANTIALIAS_CLEARTYPE; break; } // Set DWrite rendering options DWRITE_RENDERING_MODE rendering_mode; DWRITE_RENDERING_MODE1 rendering_mode1; DWRITE_TEXT_ANTIALIAS_MODE text_antialias_mode; DWRITE_TEXTURE_TYPE texture_type; switch (antialias) { case ANTIALIAS_NONE: rendering_mode1 = DWRITE_RENDERING_MODE1_ALIASED; rendering_mode = DWRITE_RENDERING_MODE_ALIASED; text_antialias_mode = DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE; texture_type = DWRITE_TEXTURE_ALIASED_1x1; break; case ANTIALIAS_GRAY: rendering_mode1 = DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC; rendering_mode = DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC; text_antialias_mode = DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE; texture_type = DWRITE_TEXTURE_ALIASED_1x1; break; case ANTIALIAS_CLEARTYPE: rendering_mode1 = DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC; rendering_mode = DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC; text_antialias_mode = DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE; texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1; break; } DWRITE_GRID_FIT_MODE grid_fit_mode = DWRITE_GRID_FIT_MODE_DEFAULT; switch (cairo_font_options_get_hint_style (&scaled_font->base.options)) { case CAIRO_HINT_STYLE_DEFAULT: grid_fit_mode = DWRITE_GRID_FIT_MODE_DEFAULT; break; case CAIRO_HINT_STYLE_NONE: grid_fit_mode = DWRITE_GRID_FIT_MODE_DISABLED; break; case CAIRO_HINT_STYLE_SLIGHT: case CAIRO_HINT_STYLE_MEDIUM: case CAIRO_HINT_STYLE_FULL: grid_fit_mode = DWRITE_GRID_FIT_MODE_ENABLED; break; } cairo_subpixel_order_t subpixel_order; subpixel_order = cairo_font_options_get_subpixel_order (&scaled_font->base.options); bool subpixel_order_is_vertical = false; if (antialias == ANTIALIAS_CLEARTYPE) { switch (subpixel_order) { case CAIRO_SUBPIXEL_ORDER_DEFAULT: case CAIRO_SUBPIXEL_ORDER_RGB: case CAIRO_SUBPIXEL_ORDER_BGR: break; case CAIRO_SUBPIXEL_ORDER_VRGB: case CAIRO_SUBPIXEL_ORDER_VBGR: subpixel_order_is_vertical = true; break; } } if (subpixel_order_is_vertical) { // DirectWrite does not support vertical pixel geometries. // As a workaround, apply a symmetry which swaps x and y // coordinates, then re-swap while copying the back into // the image surface // swap the two rows std::swap (matrix.m11, matrix.m21); std::swap (matrix.m12, matrix.m22); } RefPtr dwrite_glyph_run_analysis; if (DWriteFactory::Instance3()) { hr = DWriteFactory::Instance3()->CreateGlyphRunAnalysis(&run, &matrix, rendering_mode1, scaled_font->measuring_mode, grid_fit_mode, text_antialias_mode, 0, // baselineOriginX, 0, // baselineOriginY, &dwrite_glyph_run_analysis); } else if (DWriteFactory::Instance2()) { hr = DWriteFactory::Instance2()->CreateGlyphRunAnalysis(&run, &matrix, rendering_mode, scaled_font->measuring_mode, grid_fit_mode, text_antialias_mode, 0, // baselineOriginX, 0, // baselineOriginY, &dwrite_glyph_run_analysis); } else { if (antialias == ANTIALIAS_GRAY) { // IDWriteGlyphRunAnalysis supports gray-scale antialiasing only when // created from IDWriteFactory2 or later. If we have IDWriteFactory // only, fallback to rendering with Direct2D on A8 targets. return init_glyph_surface_fallback_a8 (scaled_font, scaled_glyph, width, height, x1, y1, &matrix, &run); } hr = DWriteFactory::Instance()->CreateGlyphRunAnalysis(&run, 1, &matrix, rendering_mode, scaled_font->measuring_mode, 0, // baselineOriginX, 0, // baselineOriginY, &dwrite_glyph_run_analysis); } if (FAILED(hr)) return CAIRO_INT_STATUS_UNSUPPORTED; cairo_format_t surface_format = antialias == ANTIALIAS_NONE ? CAIRO_FORMAT_A1 : antialias == ANTIALIAS_GRAY ? CAIRO_FORMAT_A8 : CAIRO_FORMAT_ARGB32; cairo_surface_t *surface = cairo_image_surface_create (surface_format, width, height); if (cairo_surface_status (surface)) return CAIRO_INT_STATUS_UNSUPPORTED; // Tell pixman that it should use component alpha blending when the surface is // used as a source pixman_image_set_component_alpha (((cairo_image_surface_t*)surface)->pixman_image, TRUE); // That's probably not needed right after creation cairo_surface_flush (surface); unsigned char *surface_data = cairo_image_surface_get_data (surface); int surface_stride = cairo_image_surface_get_stride (surface); UINT32 dwrite_data_size; BYTE *dwrite_data; RECT dwrite_rect = { 0, // left 0, // top width, // right height // bottom }; if (subpixel_order_is_vertical) { std::swap (dwrite_rect.right, dwrite_rect.bottom); } // Whether IDWriteGlyphRunAnalysis::CreateAlphaTexture() can render directly // on the cairo image surface (because the pixel formats match) or a separate // buffer is needed bool render_is_direct; switch (antialias) { case ANTIALIAS_NONE: { dwrite_data_size = width * height; //TODO: check overflow dwrite_data = (BYTE*) _cairo_malloc (dwrite_data_size); if (!dwrite_data) return CAIRO_INT_STATUS_UNSUPPORTED; render_is_direct = false; break; } case ANTIALIAS_GRAY: { // The image surface may have a stride that's bigger than width- // account for that by passing stride as width to DWrite. Note: // stride is a byte-size, but here pixel-size is exactly 1 byte. dwrite_rect.right = cairo_image_surface_get_stride (surface); dwrite_data_size = dwrite_rect.right * height; //TODO: check overflow dwrite_data = static_cast(surface_data); render_is_direct = true; break; } case ANTIALIAS_CLEARTYPE: { dwrite_data_size = 3 * width * height; //TODO: check overflow dwrite_data = (BYTE*) _cairo_malloc (dwrite_data_size); if (!dwrite_data) return CAIRO_INT_STATUS_UNSUPPORTED; render_is_direct = false; break; } } hr = dwrite_glyph_run_analysis->CreateAlphaTexture(texture_type, &dwrite_rect, dwrite_data, dwrite_data_size); if (FAILED (hr)) return CAIRO_INT_STATUS_UNSUPPORTED; // Most of the code here was copied and adapted from cairoft-font.c switch (antialias) { case ANTIALIAS_NONE: { unsigned char *src = static_cast(dwrite_data); unsigned char *dst = surface_data; for (int i = 0; i < height; i++) { unsigned char *d = dst; for (int j = 0; j < width / 8; j++) { *d = (src[0] ? (1 << 0) : 0) + (src[1] ? (1 << 1) : 0) + (src[2] ? (1 << 2) : 0) + (src[3] ? (1 << 3) : 0) + (src[4] ? (1 << 4) : 0) + (src[5] ? (1 << 5) : 0) + (src[6] ? (1 << 6) : 0) + (src[7] ? (1 << 7) : 0); d++; src += 8; } if (width % 8 != 0) { *d = 0; for (int k = 0; k < width % 8; k++) { *d += (src[k] ? (1 << k) : 0); } d++; src += (width % 8); } dst += surface_stride; } break; } case ANTIALIAS_GRAY: { // Nothing to do break; } case ANTIALIAS_CLEARTYPE: { unsigned char *src = static_cast(dwrite_data); unsigned char *dst = surface_data; // The alpha channel is unused for component-alpha blending. // Here we set the alpha channel anyway so that things work // even in case of normal blending (but one likely gets some // color fringing) switch (subpixel_order) { case CAIRO_SUBPIXEL_ORDER_DEFAULT: case CAIRO_SUBPIXEL_ORDER_RGB: { for (int i = 0; i < height; i++) { UINT32 *d = reinterpret_cast(dst); for (int j = 0; j < width; j++) { // CAIRO_FORMAT_ARGB32 is BGRA on little-endian *d = (src[2] << 0) + (src[1] << 8) + (src[0] << 16) + (src[1] << 24); // unused d++; src += 3; } dst += surface_stride; } } break; case CAIRO_SUBPIXEL_ORDER_BGR: { for (int i = 0; i < height; i++) { UINT32 *d = reinterpret_cast(dst); for (int j = 0; j < width; j++) { *d = (src[0] << 0) + (src[1] << 8) + (src[2] << 16) + (src[1] << 24); // unused d++; src += 3; } dst += surface_stride; } } break; case CAIRO_SUBPIXEL_ORDER_VRGB: { size_t p; for (int i = 0; i < height; i++) { UINT32 *d = reinterpret_cast(dst); p = i * 3; for (int j = 0; j < width; j++) { *d = (src[p + 2] << 0) + (src[p + 1] << 8) + (src[p + 0] << 16) + (src[p + 1] << 24); // unused d++; p += 3 * height; } dst += surface_stride; } } break; case CAIRO_SUBPIXEL_ORDER_VBGR: { size_t p; for (int i = 0; i < height; i++) { UINT32 *d = reinterpret_cast(dst); p = i * 3; for (int j = 0; j < width; j++) { *d = (src[p + 0] << 0) + (src[p + 1] << 8) + (src[p + 2] << 16) + (src[p + 1] << 24); // unused d++; p += 3 * height; } dst += surface_stride; } } break; } break; } } // That's probably not needed. cairoft-font.c doesn't do that cairo_surface_mark_dirty (surface); if (!render_is_direct) { free (dwrite_data); } cairo_surface_set_device_offset (surface, -x1, -y1); _cairo_scaled_glyph_set_surface (scaled_glyph, &scaled_font->base, (cairo_image_surface_t*)surface); return CAIRO_INT_STATUS_SUCCESS; } static cairo_int_status_t _cairo_dwrite_load_truetype_table(void *scaled_font, unsigned long tag, long offset, unsigned char *buffer, unsigned long *length) { cairo_dwrite_scaled_font_t *dwritesf = static_cast(scaled_font); const void *data; UINT32 size; void *tableContext; BOOL exists; HRESULT hr; hr = dwritesf->dwriteface->TryGetFontTable (be32_to_cpu (tag), &data, &size, &tableContext, &exists); if (FAILED(hr)) return _cairo_dwrite_error (hr, "TryGetFontTable failed"); if (!exists) { return CAIRO_INT_STATUS_UNSUPPORTED; } if (buffer && *length && (UINT32)offset < size) { size = MIN(size - (UINT32)offset, *length); memcpy(buffer, (const char*)data + offset, size); } *length = size; if (tableContext) { dwritesf->dwriteface->ReleaseFontTable(tableContext); } return (cairo_int_status_t)CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_dwrite_is_synthetic(void *scaled_font, cairo_bool_t *is_synthetic) { cairo_dwrite_scaled_font_t *dwritesf = static_cast(scaled_font); HRESULT hr; cairo_int_status_t status; if (dwritesf->dwriteface->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) { *is_synthetic = FALSE; return CAIRO_INT_STATUS_SUCCESS; } RefPtr fontFace5; if (FAILED(dwritesf->dwriteface->QueryInterface(&fontFace5))) { /* If IDWriteFontFace5 is not available, assume this version of * DirectWrite does not support variations. */ *is_synthetic = FALSE; return CAIRO_INT_STATUS_SUCCESS; } if (!fontFace5->HasVariations()) { *is_synthetic = FALSE; return CAIRO_INT_STATUS_SUCCESS; } RefPtr fontResource; hr = fontFace5->GetFontResource(&fontResource); if (FAILED(hr)) return _cairo_dwrite_error (hr, "GetFontResource failed"); UINT32 axis_count = fontResource->GetFontAxisCount(); DWRITE_FONT_AXIS_VALUE *axis_defaults = new DWRITE_FONT_AXIS_VALUE[axis_count]; DWRITE_FONT_AXIS_VALUE *axis_values = new DWRITE_FONT_AXIS_VALUE[axis_count]; hr = fontResource->GetDefaultFontAxisValues(axis_defaults, axis_count); if (FAILED(hr)) { status = _cairo_dwrite_error (hr, "GetDefaultFontAxisValues failed"); goto cleanup; } hr = fontFace5->GetFontAxisValues(axis_values, axis_count); if (FAILED(hr)) { status = _cairo_dwrite_error (hr, "GetFontAxisValues failed"); goto cleanup; } /* The DirectWrite documentation does not state if the tags of the returned * defaults and values arrays are in the same order. So assume they are not. */ *is_synthetic = FALSE; status = CAIRO_INT_STATUS_SUCCESS; for (UINT32 i = 0; i< axis_count; i++) { for (UINT32 j = 0; j < axis_count; j++) { if (axis_values[i].axisTag == axis_defaults[j].axisTag) { if (axis_values[i].value != axis_defaults[j].value) { *is_synthetic = TRUE; goto cleanup; } break; } } } cleanup: delete[] axis_defaults; delete[] axis_values; return status; } static cairo_bool_t _cairo_dwrite_has_color_glyphs(void *scaled_font) { cairo_dwrite_scaled_font_t *dwritesf = static_cast(scaled_font); return ((cairo_dwrite_font_face_t *)dwritesf->base.font_face)->have_color; } /** * cairo_dwrite_font_face_create_for_dwrite_fontface: * @dwrite_font_face: A pointer to an #IDWriteFontFace specifying the * DWrite font to use. * * Creates a new font for the DWrite font backend based on a * DWrite font face. This font can then be used with * cairo_set_font_face() or cairo_scaled_font_create(). * * Here is an example of how this function might be used: * * #include * * IDWriteFactory* dWriteFactory = NULL; * HRESULT hr = DWriteCreateFactory( * DWRITE_FACTORY_TYPE_SHARED, * __uuidof(IDWriteFactory), * reinterpret_cast(&dWriteFactory)); * * IDWriteFontCollection *systemCollection; * hr = dWriteFactory->GetSystemFontCollection(&systemCollection); * * UINT32 idx; * BOOL found; * systemCollection->FindFamilyName(L"Segoe UI Emoji", &idx, &found); * * IDWriteFontFamily *family; * systemCollection->GetFontFamily(idx, &family); * * IDWriteFont *dwritefont; * DWRITE_FONT_WEIGHT weight = DWRITE_FONT_WEIGHT_NORMAL; * DWRITE_FONT_STYLE style = DWRITE_FONT_STYLE_NORMAL; * hr = family->GetFirstMatchingFont(weight, DWRITE_FONT_STRETCH_NORMAL, style, &dwritefont); * * IDWriteFontFace *dwriteface; * hr = dwritefont->CreateFontFace(&dwriteface); * * cairo_font_face_t *face; * face = cairo_dwrite_font_face_create_for_dwrite_fontface(dwriteface); * cairo_set_font_face(cr, face); * cairo_set_font_size(cr, 70); * cairo_move_to(cr, 100, 100); * cairo_show_text(cr, "😃"); * ]]> * * Note: When printing a DWrite font to a * #CAIRO_SURFACE_TYPE_WIN32_PRINTING surface, the printing surface * will substitute each DWrite font with a Win32 font created from the same * underlying font file. If the matching font file can not be found, * the #CAIRO_SURFACE_TYPE_WIN32_PRINTING surface will convert each * glyph to a filled path. If a DWrite font was not created from a system * font, it is recommended that the font used to create the DWrite * font be made available to GDI to avoid the undesirable fallback * to emitting paths. This can be achieved using the GDI font loading functions * such as AddFontMemResourceEx(). * * Return value: a newly created #cairo_font_face_t. Free with * cairo_font_face_destroy() when you are done using it. * * Since: 1.18 **/ cairo_font_face_t * cairo_dwrite_font_face_create_for_dwrite_fontface (IDWriteFontFace *dwrite_font_face) { IDWriteFontFace *dwriteface = static_cast(dwrite_font_face); // Must do malloc and not C++ new, since Cairo frees this. cairo_dwrite_font_face_t *face = (cairo_dwrite_font_face_t *)_cairo_malloc(sizeof(cairo_dwrite_font_face_t)); if (unlikely (face == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_font_face_t*)&_cairo_font_face_nil; } dwriteface->AddRef(); face->dwriteface = dwriteface; face->have_color = false; face->rendering_params = NULL; face->measuring_mode = DWRITE_MEASURING_MODE_NATURAL; /* Ensure IDWriteFactory4 is available before enabling color fonts */ if (DWriteFactory::Instance4()) { RefPtr fontFace2; if (SUCCEEDED(dwriteface->QueryInterface(&fontFace2))) { if (fontFace2->IsColorFont()) face->have_color = true; } } cairo_font_face_t *font_face; font_face = (cairo_font_face_t*)face; _cairo_font_face_init (&((cairo_dwrite_font_face_t*)font_face)->base, &_cairo_dwrite_font_face_backend); return font_face; } /** * cairo_dwrite_font_face_get_rendering_params: * @font_face: The #cairo_dwrite_font_face_t object to query * * Gets the #IDWriteRenderingParams object of @font_face. * * Return value: the #IDWriteRenderingParams object or %NULL if none. * * Since: 1.18 **/ IDWriteRenderingParams * cairo_dwrite_font_face_get_rendering_params (cairo_font_face_t *font_face) { cairo_dwrite_font_face_t *dwface = reinterpret_cast(font_face); return dwface->rendering_params; } /** * cairo_dwrite_font_face_set_rendering_params: * @font_face: The #cairo_dwrite_font_face_t object to modify * @params: The #IDWriteRenderingParams object * * Sets the #IDWriteRenderingParams object to @font_face. * This #IDWriteRenderingParams is used to render glyphs if default values of font options are used. * If non-defalut values of font options are specified when creating a #cairo_scaled_font_t, * cairo creates a new #IDWriteRenderingParams object for the #cairo_scaled_font_t object by overwriting the corresponding parameters. * * Since: 1.18 **/ void cairo_dwrite_font_face_set_rendering_params (cairo_font_face_t *font_face, IDWriteRenderingParams *params) { cairo_dwrite_font_face_t *dwface = reinterpret_cast(font_face); if (dwface->rendering_params) dwface->rendering_params->Release(); dwface->rendering_params = params; if (dwface->rendering_params) dwface->rendering_params->AddRef(); } /** * cairo_dwrite_font_face_get_measuring_mode: * @font_face: The #cairo_dwrite_font_face_t object to query * * Gets the #DWRITE_MEASURING_MODE enum of @font_face. * * Return value: The #DWRITE_MEASURING_MODE enum of @font_face. * * Since: 1.18 **/ DWRITE_MEASURING_MODE cairo_dwrite_font_face_get_measuring_mode (cairo_font_face_t *font_face) { cairo_dwrite_font_face_t *dwface = reinterpret_cast(font_face); return dwface->measuring_mode; } /** * cairo_dwrite_font_face_set_measuring_mode: * @font_face: The #cairo_dwrite_font_face_t object to modify * @mode: The #DWRITE_MEASURING_MODE enum. * * Sets the #DWRITE_MEASURING_MODE enum to @font_face. * * Since: 1.18 **/ void cairo_dwrite_font_face_set_measuring_mode (cairo_font_face_t *font_face, DWRITE_MEASURING_MODE mode) { cairo_dwrite_font_face_t *dwface = reinterpret_cast(font_face); dwface->measuring_mode = mode; } static cairo_int_status_t _dwrite_draw_glyphs_to_gdi_surface_gdi(cairo_win32_surface_t *surface, DWRITE_MATRIX *transform, DWRITE_GLYPH_RUN *run, COLORREF color, cairo_dwrite_scaled_font_t *scaled_font, const RECT &area) { RefPtr gdiInterop; DWriteFactory::Instance()->GetGdiInterop(&gdiInterop); RefPtr rt; HRESULT hr; hr = gdiInterop->CreateBitmapRenderTarget(surface->dc, area.right - area.left, area.bottom - area.top, &rt); if (FAILED(hr)) { if (hr == E_OUTOFMEMORY) { return (cairo_int_status_t)CAIRO_STATUS_NO_MEMORY; } else { return CAIRO_INT_STATUS_UNSUPPORTED; } } /* * We set the number of pixels per DIP to 1.0. This is because we always want * to draw in device pixels, and not device independent pixels. On high DPI * systems this value will be higher than 1.0 and automatically upscale * fonts, we don't want this since we do our own upscaling for various reasons. */ rt->SetPixelsPerDip(1.0); float x = 0, y = 0; if (transform) { DWRITE_MATRIX matrix = *transform; matrix.dx -= area.left; matrix.dy -= area.top; rt->SetCurrentTransform(&matrix); } else { x = (float) -area.left; y = (float) -area.top; } BitBlt(rt->GetMemoryDC(), 0, 0, area.right - area.left, area.bottom - area.top, surface->dc, area.left, area.top, SRCCOPY | NOMIRRORBITMAP); rt->DrawGlyphRun(x, y, scaled_font->measuring_mode, run, scaled_font->rendering_params, color); BitBlt(surface->dc, area.left, area.top, area.right - area.left, area.bottom - area.top, rt->GetMemoryDC(), 0, 0, SRCCOPY | NOMIRRORBITMAP); return CAIRO_INT_STATUS_SUCCESS; } /* Surface helper function */ cairo_int_status_t _cairo_dwrite_show_glyphs_on_surface(void *surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, cairo_clip_t *clip) { // TODO: Check font & surface for types. cairo_dwrite_scaled_font_t *dwritesf = reinterpret_cast(scaled_font); cairo_win32_surface_t *dst = reinterpret_cast(surface); cairo_int_status_t status; /* We can only handle dwrite fonts */ if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_DWRITE) return CAIRO_INT_STATUS_UNSUPPORTED; /* We can only handle opaque solid color sources */ if (!_cairo_pattern_is_opaque_solid(source)) return CAIRO_INT_STATUS_UNSUPPORTED; /* We can only handle operator SOURCE or OVER with the destination * having no alpha */ if (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_OVER) return CAIRO_INT_STATUS_UNSUPPORTED; /* It is vital that dx values for dxy_buf are calculated from the delta of * _logical_ x coordinates (not user x coordinates) or else the sum of all * previous dx values may start to diverge from the current glyph's x * coordinate due to accumulated rounding error. As a result strings could * be painted shorter or longer than expected. */ AutoDWriteGlyphRun run; run.allocate(num_glyphs); BOOL transform = FALSE; _cairo_dwrite_glyph_run_from_glyphs(glyphs, num_glyphs, dwritesf, &run, &transform); cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)source; COLORREF color = RGB(((int)solid_pattern->color.red_short) >> 8, ((int)solid_pattern->color.green_short) >> 8, ((int)solid_pattern->color.blue_short) >> 8); DWRITE_MATRIX matrix = _cairo_dwrite_matrix_from_matrix(&dwritesf->mat); DWRITE_MATRIX *mat; if (transform) { mat = &matrix; } else { mat = NULL; } RefPtr runAnalysis; HRESULT hr = DWriteFactory::Instance()-> CreateGlyphRunAnalysis(&run, 1, mat, DWRITE_RENDERING_MODE_ALIASED, dwritesf->measuring_mode, 0, // baselineOriginX, 0, // baselineOriginY, &runAnalysis); if (FAILED(hr)) return CAIRO_INT_STATUS_UNSUPPORTED; RECT fontArea; hr = runAnalysis->GetAlphaTextureBounds(DWRITE_TEXTURE_ALIASED_1x1, &fontArea); if (FAILED(hr)) return CAIRO_INT_STATUS_UNSUPPORTED; InflateRect(&fontArea, 1, 1); /* Needed to calculate bounding box for efficient blitting */ RECT copyArea, dstArea = { 0, 0, dst->extents.width, dst->extents.height }; IntersectRect(©Area, &fontArea, &dstArea); status = _dwrite_draw_glyphs_to_gdi_surface_gdi(dst, mat, &run, color, dwritesf, copyArea); return status; } /* Check if a specific font table in a DWrite font and a scaled font is identical */ static cairo_int_status_t compare_font_tables (cairo_dwrite_font_face_t *dwface, cairo_scaled_font_t *scaled_font, unsigned long tag, cairo_bool_t *match) { unsigned long size; cairo_int_status_t status; unsigned char *buffer = NULL; const void *dw_data; UINT32 dw_size; void *dw_tableContext = NULL; BOOL dw_exists = FALSE; HRESULT hr; hr = dwface->dwriteface->TryGetFontTable(be32_to_cpu (tag), &dw_data, &dw_size, &dw_tableContext, &dw_exists); if (FAILED(hr)) return _cairo_dwrite_error (hr, "TryGetFontTable failed"); if (!dw_exists) { *match = FALSE; status = CAIRO_INT_STATUS_SUCCESS; goto cleanup; } status = scaled_font->backend->load_truetype_table (scaled_font, tag, 0, NULL, &size); if (unlikely(status)) goto cleanup; if (size != dw_size) { *match = FALSE; status = CAIRO_INT_STATUS_SUCCESS; goto cleanup; } buffer = (unsigned char *) _cairo_malloc (size); if (unlikely (buffer == NULL)) { status = (cairo_int_status_t) _cairo_error (CAIRO_STATUS_NO_MEMORY); goto cleanup; } status = scaled_font->backend->load_truetype_table (scaled_font, tag, 0, buffer, &size); if (unlikely(status)) goto cleanup; *match = memcmp (dw_data, buffer, size) == 0; status = CAIRO_INT_STATUS_SUCCESS; cleanup: free (buffer); if (dw_tableContext) dwface->dwriteface->ReleaseFontTable(dw_tableContext); return status; } /* Check if a DWrite font and a scaled font areis identical * * DWrite does not allow accessing the entire font data using tag=0 so we compare * two of the font tables: * - 'name' table * - 'head' table since this contains the checksum for the entire font */ static cairo_int_status_t font_tables_match (cairo_dwrite_font_face_t *dwface, cairo_scaled_font_t *scaled_font, cairo_bool_t *match) { cairo_int_status_t status; status = compare_font_tables (dwface, scaled_font, TT_TAG_name, match); if (unlikely(status)) return status; if (!*match) return CAIRO_INT_STATUS_SUCCESS; status = compare_font_tables (dwface, scaled_font, TT_TAG_head, match); if (unlikely(status)) return status; return CAIRO_INT_STATUS_SUCCESS; } /* * Helper for _cairo_win32_printing_surface_show_glyphs to create a win32 equivalent * of a dwrite scaled_font so that we can print using ExtTextOut instead of drawing * paths or blitting glyph bitmaps. */ cairo_int_status_t _cairo_dwrite_scaled_font_create_win32_scaled_font (cairo_scaled_font_t *scaled_font, cairo_scaled_font_t **new_font) { if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_DWRITE) { return CAIRO_INT_STATUS_UNSUPPORTED; } cairo_font_face_t *face = cairo_scaled_font_get_font_face (scaled_font); if (cairo_font_face_status (face) == CAIRO_STATUS_SUCCESS && cairo_font_face_get_type (face) == CAIRO_FONT_TYPE_TOY) { face = ((cairo_toy_font_face_t *)face)->impl_face; } if (face == NULL || cairo_font_face_get_type (face) != CAIRO_FONT_TYPE_DWRITE) { return CAIRO_INT_STATUS_UNSUPPORTED; } cairo_dwrite_font_face_t *dwface = reinterpret_cast(face); RefPtr gdiInterop; DWriteFactory::Instance()->GetGdiInterop(&gdiInterop); if (!gdiInterop) { return CAIRO_INT_STATUS_UNSUPPORTED; } /* TODO: use scaled font? */ LOGFONTW logfont; if (FAILED(gdiInterop->ConvertFontFaceToLOGFONT (dwface->dwriteface, &logfont))) { return CAIRO_INT_STATUS_UNSUPPORTED; } /* DWrite must have been using an outline font, so we want GDI to use the same, * even if there's also a bitmap face available */ logfont.lfOutPrecision = OUT_OUTLINE_PRECIS; cairo_font_face_t *win32_face = cairo_win32_font_face_create_for_logfontw (&logfont); if (cairo_font_face_status (win32_face)) { cairo_font_face_destroy (win32_face); return CAIRO_INT_STATUS_UNSUPPORTED; } cairo_matrix_t font_matrix; cairo_scaled_font_get_font_matrix (scaled_font, &font_matrix); cairo_matrix_t ctm; cairo_scaled_font_get_ctm (scaled_font, &ctm); cairo_font_options_t options; _cairo_font_options_init_default (&options); cairo_scaled_font_get_font_options (scaled_font, &options); cairo_scaled_font_t *font = cairo_scaled_font_create (win32_face, &font_matrix, &ctm, &options); cairo_font_face_destroy (win32_face); if (cairo_scaled_font_status(font)) { cairo_scaled_font_destroy (font); return CAIRO_INT_STATUS_UNSUPPORTED; } cairo_bool_t match; cairo_int_status_t status; status = font_tables_match (dwface, font, &match); if (status) { cairo_scaled_font_destroy (font); return status; } /* If the font tables aren't equal, then GDI may have failed to * find the right font and substituted a different font. */ if (!match) { #if 0 char *ps_name; char *font_name; status = _cairo_truetype_read_font_name (scaled_font, &ps_name, &font_name); printf("dwrite fontname: %s PS name: %s\n", font_name, ps_name); free (font_name); free (ps_name); status = _cairo_truetype_read_font_name (font, &ps_name, &font_name); printf("win32 fontname: %s PS name: %s\n", font_name, ps_name); free (font_name); free (ps_name); #endif cairo_scaled_font_destroy (font); return CAIRO_INT_STATUS_UNSUPPORTED; } *new_font = font; return CAIRO_INT_STATUS_SUCCESS; }