summaryrefslogtreecommitdiffstats
path: root/libass
diff options
context:
space:
mode:
authorGrigori Goronzy <greg@chown.ath.cx>2015-09-07 11:40:54 +0200
committerGrigori Goronzy <greg@chown.ath.cx>2015-09-07 11:40:54 +0200
commita2f12d6f79bfdc6bc7c1cb6ddd3a42c97ab2de4f (patch)
tree2600406db3d71a17d4389f34ba4bf66f7c098247 /libass
parent23de9995103c16ceceba19cae87e328be12fa8e4 (diff)
parent0268c64b8ec7bef9287a212759152bf7d15e64d8 (diff)
downloadlibass-a2f12d6f79bfdc6bc7c1cb6ddd3a42c97ab2de4f.tar.bz2
libass-a2f12d6f79bfdc6bc7c1cb6ddd3a42c97ab2de4f.tar.xz
Merge branch 'fonts'
Diffstat (limited to 'libass')
-rw-r--r--libass/Makefile.am19
-rw-r--r--libass/ass.c2
-rw-r--r--libass/ass.h47
-rw-r--r--libass/ass_cache.c4
-rw-r--r--libass/ass_coretext.c295
-rw-r--r--libass/ass_coretext.h35
-rw-r--r--libass/ass_directwrite.c715
-rw-r--r--libass/ass_directwrite.h31
-rw-r--r--libass/ass_font.c131
-rw-r--r--libass/ass_font.h28
-rw-r--r--libass/ass_fontconfig.c705
-rw-r--r--libass/ass_fontconfig.h32
-rw-r--r--libass/ass_fontselect.c1043
-rw-r--r--libass/ass_fontselect.h260
-rw-r--r--libass/ass_library.h2
-rw-r--r--libass/ass_parse.c11
-rw-r--r--libass/ass_render.c13
-rw-r--r--libass/ass_render.h4
-rw-r--r--libass/ass_render_api.c25
-rw-r--r--libass/ass_shaper.c2
-rw-r--r--libass/ass_types.h13
-rw-r--r--libass/ass_utils.c26
-rw-r--r--libass/ass_utils.h6
-rw-r--r--libass/dwrite_c.h673
-rw-r--r--libass/libass.sym1
25 files changed, 3521 insertions, 602 deletions
diff --git a/libass/Makefile.am b/libass/Makefile.am
index 4bf9584..2cf6354 100644
--- a/libass/Makefile.am
+++ b/libass/Makefile.am
@@ -11,7 +11,7 @@ yasm_verbose_ = $(yasm_verbose_$(AM_DEFAULT_VERBOSITY))
yasm_verbose_0 = @echo " YASM " $@;
.asm.lo:
- $(yasm_verbose)$(LIBTOOL) $(AM_V_lt) --mode=compile $(AS) $(ASFLAGS) -o $@ $< -prefer-non-pic
+ $(yasm_verbose)$(LIBTOOL) $(AM_V_lt) --tag=CC --mode=compile $(AS) $(ASFLAGS) -o $@ $< -prefer-non-pic
SRC_INTEL = x86/blend_bitmaps.asm x86/blur.asm x86/cpuid.asm x86/cpuid.h
SRC_INTEL64 = x86/be_blur.asm
@@ -19,18 +19,29 @@ SRC_INTEL_RASTERIZER = x86/rasterizer.asm
SRC_RASTERIZER = ass_rasterizer.h ass_rasterizer.c ass_rasterizer_c.c
+SRC_DIRECTWRITE = ass_directwrite.c ass_directwrite.h dwrite_c.h
+SRC_CORETEXT = ass_coretext.c ass_coretext.h
+
lib_LTLIBRARIES = libass.la
-libass_la_SOURCES = ass.c ass_cache.c ass_font.c ass_fontconfig.c ass_render.c \
+libass_la_SOURCES = ass.c ass_cache.c ass_font.c ass_fontselect.c ass_render.c \
ass_utils.c ass_bitmap.c ass_blur.c ass_library.c ass_bitmap.h \
- ass_cache.h ass_fontconfig.h ass_font.h ass.h \
+ ass_cache.h ass_fontselect.h ass_font.h ass.h \
ass_library.h ass_types.h ass_utils.h ass_drawing.c \
ass_drawing.h ass_cache_template.h ass_render.h \
ass_parse.c ass_parse.h ass_render_api.c ass_shaper.c \
- ass_shaper.h ass_strtod.c ass_func_template.h
+ ass_shaper.h ass_strtod.c ass_fontconfig.c ass_fontconfig.h
libass_la_LDFLAGS = -no-undefined -version-info $(LIBASS_LT_CURRENT):$(LIBASS_LT_REVISION):$(LIBASS_LT_AGE)
libass_la_LDFLAGS += -export-symbols $(srcdir)/libass.sym
+if DIRECTWRITE
+libass_la_SOURCES += $(SRC_DIRECTWRITE)
+endif
+
+if CORETEXT
+libass_la_SOURCES += $(SRC_CORETEXT)
+endif
+
if RASTERIZER
libass_la_SOURCES += $(SRC_RASTERIZER)
endif
diff --git a/libass/ass.c b/libass/ass.c
index 01dd2eb..b51cf2e 100644
--- a/libass/ass.c
+++ b/libass/ass.c
@@ -1031,7 +1031,7 @@ out:
* \param bufsize out: file size
* \return pointer to file contents. Caller is responsible for its deallocation.
*/
-static char *read_file(ASS_Library *library, char *fname, size_t *bufsize)
+char *read_file(ASS_Library *library, char *fname, size_t *bufsize)
{
int res;
long sz;
diff --git a/libass/ass.h b/libass/ass.h
index 774f344..5fdee10 100644
--- a/libass/ass.h
+++ b/libass/ass.h
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
+ * Copyright (C) 2011 Grigori Goronzy <greg@chown.ath.cx>
*
* This file is part of libass.
*
@@ -175,6 +176,24 @@ typedef enum {
int ass_library_version(void);
/**
+ * \brief Default Font provider to load fonts in libass' database
+ *
+ * NONE don't use any default font provider for font lookup
+ * AUTODETECT use the first available font provider
+ * CORETEXT force a CoreText based font provider (OS X only)
+ * FONTCONFIG force a Fontconfig based font provider
+ *
+ * libass uses the best shaper available by default.
+ */
+typedef enum {
+ ASS_FONTPROVIDER_NONE = 0,
+ ASS_FONTPROVIDER_AUTODETECT = 1,
+ ASS_FONTPROVIDER_CORETEXT,
+ ASS_FONTPROVIDER_FONTCONFIG,
+ ASS_FONTPROVIDER_DIRECTWRITE,
+} ASS_DefaultFontProvider;
+
+/**
* \brief Initialize the library.
* \return library handle or NULL if failed
*/
@@ -383,11 +402,29 @@ void ass_set_line_spacing(ASS_Renderer *priv, double line_spacing);
void ass_set_line_position(ASS_Renderer *priv, double line_position);
/**
+ * \brief Get the list of available font providers. The output array
+ * is allocated with malloc and can be released with free(). If an
+ * allocation error occurs, size is set to (size_t)-1.
+ * \param priv library handle
+ * \param providers output, list of default providers (malloc'ed array)
+ * \param size output, number of providers
+ * \return list of available font providers (user owns the returned array)
+ */
+void ass_get_available_font_providers(ASS_Library *priv,
+ ASS_DefaultFontProvider **providers,
+ size_t *size);
+
+/**
* \brief Set font lookup defaults.
* \param default_font path to default font to use. Must be supplied if
* fontconfig is disabled or unavailable.
* \param default_family fallback font family for fontconfig, or NULL
- * \param fc whether to use fontconfig
+ * \param dfp which font provider to use (one of ASS_DefaultFontProvider). In
+ * older libass version, this could be 0 or 1, where 1 enabled fontconfig.
+ * Newer relases also accept 0 (ASS_FONTPROVIDER_NONE) and 1
+ * (ASS_FONTPROVIDER_AUTODETECT), which is almost backward-compatible.
+ * If the requested fontprovider does not exist or fails to initialize, the
+ * behavior is the same as when ASS_FONTPROVIDER_NONE was passed.
* \param config path to fontconfig configuration file, or NULL. Only relevant
* if fontconfig is used.
* \param update whether fontconfig cache should be built/updated now. Only
@@ -396,8 +433,8 @@ void ass_set_line_position(ASS_Renderer *priv, double line_position);
* NOTE: font lookup must be configured before an ASS_Renderer can be used.
*/
void ass_set_fonts(ASS_Renderer *priv, const char *default_font,
- const char *default_family, int fc, const char *config,
- int update);
+ const char *default_family, int dfp,
+ const char *config, int update);
/**
* \brief Set selective style override mode.
@@ -426,8 +463,8 @@ void ass_set_selective_style_override_enabled(ASS_Renderer *priv, int bits);
void ass_set_selective_style_override(ASS_Renderer *priv, ASS_Style *style);
/**
- * \brief Update/build font cache. This needs to be called if it was
- * disabled when ass_set_fonts was set.
+ * \brief This is a stub and does nothing. Old documentation: Update/build font
+ * cache. This needs to be called if it was disabled when ass_set_fonts was set.
*
* \param priv renderer handle
* \return success
diff --git a/libass/ass_cache.c b/libass/ass_cache.c
index 2381e88..d1aaec0 100644
--- a/libass/ass_cache.c
+++ b/libass/ass_cache.c
@@ -43,8 +43,6 @@ static unsigned font_hash(void *buf, size_t len)
hval = fnv_32a_str(desc->family, FNV1_32A_INIT);
hval = fnv_32a_buf(&desc->bold, sizeof(desc->bold), hval);
hval = fnv_32a_buf(&desc->italic, sizeof(desc->italic), hval);
- hval = fnv_32a_buf(&desc->treat_family_as_pattern,
- sizeof(desc->treat_family_as_pattern), hval);
hval = fnv_32a_buf(&desc->vertical, sizeof(desc->vertical), hval);
return hval;
}
@@ -59,8 +57,6 @@ static unsigned font_compare(void *key1, void *key2, size_t key_size)
return 0;
if (a->italic != b->italic)
return 0;
- if (a->treat_family_as_pattern != b->treat_family_as_pattern)
- return 0;
if (a->vertical != b->vertical)
return 0;
return 1;
diff --git a/libass/ass_coretext.c b/libass/ass_coretext.c
new file mode 100644
index 0000000..877018d
--- /dev/null
+++ b/libass/ass_coretext.c
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2013 Stefano Pigozzi <stefano.pigozzi@gmail.com>
+ *
+ * This file is part of libass.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreText/CoreText.h>
+
+#include "ass_coretext.h"
+
+#define SAFE_CFRelease(x) do { if (x) CFRelease(x); } while(0)
+
+static const ASS_FontMapping font_substitutions[] = {
+ {"sans-serif", "Helvetica"},
+ {"serif", "Times"},
+ {"monospace", "Courier"}
+};
+
+static char *cfstr2buf(CFStringRef string)
+{
+ const int encoding = kCFStringEncodingUTF8;
+ const char *buf_ptr = CFStringGetCStringPtr(string, encoding);
+ if (buf_ptr) {
+ return strdup(buf_ptr);
+ } else {
+ size_t len = CFStringGetLength(string);
+ CFIndex buf_len = CFStringGetMaximumSizeForEncoding(len, encoding);
+ char *buf = malloc(buf_len);
+ CFStringGetCString(string, buf, buf_len, encoding);
+ return buf;
+ }
+}
+
+static void destroy_font(void *priv)
+{
+ CFCharacterSetRef set = priv;
+ SAFE_CFRelease(set);
+}
+
+static int check_glyph(void *priv, uint32_t code)
+{
+ CFCharacterSetRef set = priv;
+
+ if (!set)
+ return 1;
+
+ if (code == 0)
+ return 1;
+
+ return CFCharacterSetIsLongCharacterMember(set, code);
+}
+
+static char *get_font_file(CTFontDescriptorRef fontd)
+{
+ CFURLRef url = CTFontDescriptorCopyAttribute(fontd, kCTFontURLAttribute);
+ CFStringRef path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
+ char *buffer = cfstr2buf(path);
+ SAFE_CFRelease(path);
+ SAFE_CFRelease(url);
+ return buffer;
+}
+
+static void get_name(CTFontDescriptorRef fontd, CFStringRef attr,
+ char **array, int *idx)
+{
+
+ CFStringRef name = CTFontDescriptorCopyAttribute(fontd, attr);
+ if (name) {
+ array[*idx] = cfstr2buf(name);
+ SAFE_CFRelease(name);
+ *idx += 1;
+ }
+}
+
+static void get_trait(CFDictionaryRef traits, CFStringRef attribute,
+ float *trait)
+{
+ CFNumberRef cftrait = CFDictionaryGetValue(traits, attribute);
+ if (!CFNumberGetValue(cftrait, kCFNumberFloatType, trait))
+ *trait = 0.0;
+}
+
+static void get_font_traits(CTFontDescriptorRef fontd,
+ ASS_FontProviderMetaData *meta)
+{
+ float weight, slant, width;
+
+ CFDictionaryRef traits =
+ CTFontDescriptorCopyAttribute(fontd, kCTFontTraitsAttribute);
+
+ get_trait(traits, kCTFontWeightTrait, &weight);
+ get_trait(traits, kCTFontSlantTrait, &slant);
+ get_trait(traits, kCTFontWidthTrait, &width);
+
+ SAFE_CFRelease(traits);
+
+ // Printed all of my system fonts (see if'deffed code below). Here is how
+ // CoreText 'normalized' weights maps to CSS/libass:
+
+ // opentype: 0 100 200 300 400 500 600 700 800 900
+ // css: LIGHT REG MED SBOLD BOLD BLACK EXTRABL
+ // libass: LIGHT MEDIUM BOLD
+ // coretext: -0.4 0.0 0.23 0.3 0.4 0.62
+
+ if (weight >= 0.62)
+ meta->weight = 800;
+ else if (weight >= 0.4)
+ meta->weight = 700;
+ else if (weight >= 0.3)
+ meta->weight = 600;
+ else if (weight >= 0.23)
+ meta->weight = 500;
+ else if (weight >= -0.4)
+ meta->weight = 400;
+ else
+ meta->weight = 200;
+
+ if (slant > 0.03)
+ meta->slant = FONT_SLANT_ITALIC;
+ else
+ meta->slant = FONT_SLANT_NONE;
+
+ if (width <= -0.2)
+ meta->width = FONT_WIDTH_CONDENSED;
+ else if (width >= 0.2)
+ meta->width = FONT_WIDTH_EXPANDED;
+ else
+ meta->width = FONT_WIDTH_NORMAL;
+
+#if 0
+ char *name[1];
+ int idx = 0;
+ get_name(fontd, kCTFontDisplayNameAttribute, name, &idx);
+ char *file = get_font_file(fontd);
+ printf(
+ "Font traits for: %-40s [%-50s] "
+ "<slant: %f, %03d>, <weight: (%f, %03d)>, <width: %f, %03d>\n",
+ name[0], file,
+ slant, meta->slant, weight, meta->weight, width, meta->width);
+ free(name[0]);
+ free(file);
+#endif
+}
+
+static void process_descriptors(ASS_FontProvider *provider, CFArrayRef fontsd)
+{
+ ASS_FontProviderMetaData meta;
+ char *families[1];
+ char *identifiers[1];
+ char *fullnames[1];
+
+ if (!fontsd)
+ return;
+
+ for (int i = 0; i < CFArrayGetCount(fontsd); i++) {
+ CTFontDescriptorRef fontd = CFArrayGetValueAtIndex(fontsd, i);
+ int index = -1;
+
+ char *path = get_font_file(fontd);
+ if (strcmp("", path) == 0) {
+ // skip the font if the URL field in the font descriptor is empty
+ free(path);
+ continue;
+ }
+
+ memset(&meta, 0, sizeof(meta));
+ get_font_traits(fontd, &meta);
+
+ get_name(fontd, kCTFontFamilyNameAttribute, families, &meta.n_family);
+ meta.families = families;
+
+ int zero = 0;
+ get_name(fontd, kCTFontNameAttribute, identifiers, &zero);
+ get_name(fontd, kCTFontDisplayNameAttribute, fullnames, &meta.n_fullname);
+ meta.fullnames = fullnames;
+
+ CFCharacterSetRef chset =
+ CTFontDescriptorCopyAttribute(fontd, kCTFontCharacterSetAttribute);
+ ass_font_provider_add_font(provider, &meta, path, index,
+ identifiers[0], (void*)chset);
+
+ for (int j = 0; j < meta.n_family; j++)
+ free(meta.families[j]);
+
+ for (int j = 0; j < meta.n_fullname; j++)
+ free(meta.fullnames[j]);
+
+ free(identifiers[0]);
+
+ free(path);
+ }
+}
+
+static void match_fonts(ASS_Library *lib, ASS_FontProvider *provider,
+ char *name)
+{
+ const size_t attributes_n = 3;
+ CTFontDescriptorRef ctdescrs[attributes_n];
+ CFMutableDictionaryRef cfattrs[attributes_n];
+ CFStringRef attributes[attributes_n] = {
+ kCTFontFamilyNameAttribute,
+ kCTFontDisplayNameAttribute,
+ kCTFontNameAttribute,
+ };
+
+ CFStringRef cfname =
+ CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8);
+
+ for (int i = 0; i < attributes_n; i++) {
+ cfattrs[i] = CFDictionaryCreateMutable(NULL, 0, 0, 0);
+ CFDictionaryAddValue(cfattrs[i], attributes[i], cfname);
+ ctdescrs[i] = CTFontDescriptorCreateWithAttributes(cfattrs[i]);
+ }
+
+ CFArrayRef descriptors =
+ CFArrayCreate(NULL, (const void **)&ctdescrs, attributes_n, NULL);
+
+ CTFontCollectionRef ctcoll =
+ CTFontCollectionCreateWithFontDescriptors(descriptors, 0);
+
+ CFArrayRef fontsd =
+ CTFontCollectionCreateMatchingFontDescriptors(ctcoll);
+
+ process_descriptors(provider, fontsd);
+
+ SAFE_CFRelease(fontsd);
+ SAFE_CFRelease(ctcoll);
+
+ for (int i = 0; i < attributes_n; i++) {
+ SAFE_CFRelease(cfattrs[i]);
+ SAFE_CFRelease(ctdescrs[i]);
+ }
+
+ SAFE_CFRelease(descriptors);
+ SAFE_CFRelease(cfname);
+}
+
+static char *get_fallback(void *priv, const char *family, uint32_t codepoint)
+{
+ CFStringRef name = CFStringCreateWithBytes(
+ 0, (UInt8 *)family, strlen(family), kCFStringEncodingUTF8, false);
+ CTFontRef font = CTFontCreateWithName(name, 0, NULL);
+ uint32_t codepointle = OSSwapHostToLittleInt32(codepoint);
+ CFStringRef r = CFStringCreateWithBytes(
+ 0, (UInt8*)&codepointle, sizeof(codepointle),
+ kCFStringEncodingUTF32LE, false);
+ CTFontRef fb = CTFontCreateForString(font, r, CFRangeMake(0, 1));
+ CFStringRef cffamily = CTFontCopyFamilyName(fb);
+ char *res_family = cfstr2buf(cffamily);
+
+ SAFE_CFRelease(name);
+ SAFE_CFRelease(font);
+ SAFE_CFRelease(r);
+ SAFE_CFRelease(fb);
+ SAFE_CFRelease(cffamily);
+
+ return res_family;
+}
+
+static void get_substitutions(void *priv, const char *name,
+ ASS_FontProviderMetaData *meta)
+{
+ const int n = sizeof(font_substitutions) / sizeof(font_substitutions[0]);
+ ass_map_font(font_substitutions, n, name, meta);
+}
+
+static ASS_FontProviderFuncs coretext_callbacks = {
+ .check_glyph = check_glyph,
+ .destroy_font = destroy_font,
+ .match_fonts = match_fonts,
+ .get_substitutions = get_substitutions,
+ .get_fallback = get_fallback,
+};
+
+ASS_FontProvider *
+ass_coretext_add_provider(ASS_Library *lib, ASS_FontSelector *selector,
+ const char *config)
+{
+ return ass_font_provider_new(selector, &coretext_callbacks, NULL);
+}
diff --git a/libass/ass_coretext.h b/libass/ass_coretext.h
new file mode 100644
index 0000000..07a49fa
--- /dev/null
+++ b/libass/ass_coretext.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2013 Stefano Pigozzi <stefano.pigozzi@gmail.com>
+ *
+ * This file is part of libass.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "ass_types.h"
+#include "ass_fontselect.h"
+
+#ifndef ASS_CORETEXT_H
+#define ASS_CORETEXT_H
+
+#ifdef CONFIG_CORETEXT
+
+ASS_FontProvider *
+ass_coretext_add_provider(ASS_Library *lib, ASS_FontSelector *selector,
+ const char *config);
+
+#endif
+
+#endif
diff --git a/libass/ass_directwrite.c b/libass/ass_directwrite.c
new file mode 100644
index 0000000..f11deb6
--- /dev/null
+++ b/libass/ass_directwrite.c
@@ -0,0 +1,715 @@
+/*
+ * Copyright (C) 2015 Stephan Vedder <stephan.vedder@gmail.com>
+ *
+ * This file is part of libass.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#define COBJMACROS
+
+#include "config.h"
+
+#include <initguid.h>
+#include <ole2.h>
+#include <shobjidl.h>
+
+#include "dwrite_c.h"
+
+#include "ass_directwrite.h"
+#include "ass_utils.h"
+
+#define NAME_MAX_LENGTH 256
+#define FALLBACK_DEFAULT_FONT L"Arial"
+
+static const ASS_FontMapping font_substitutions[] = {
+ {"sans-serif", "Arial"},
+ {"serif", "Times New Roman"},
+ {"monospace", "Courier New"}
+};
+
+/*
+ * The private data stored for every font, detected by this backend.
+ */
+typedef struct {
+ IDWriteFont *font;
+ IDWriteFontFileStream *stream;
+} FontPrivate;
+
+typedef struct {
+ HMODULE directwrite_lib;
+ IDWriteFactory *factory;
+} ProviderPrivate;
+
+/**
+ * Custom text renderer class for logging the fonts used. It does not
+ * actually render anything or do anything apart from that.
+ */
+
+typedef struct FallbackLogTextRenderer {
+ IDWriteTextRenderer iface;
+ IDWriteTextRendererVtbl vtbl;
+ IDWriteFactory *dw_factory;
+ LONG ref_count;
+} FallbackLogTextRenderer;
+
+static HRESULT STDMETHODCALLTYPE FallbackLogTextRenderer_IsPixelSnappingDisabled(
+ IDWriteTextRenderer *This,
+ _In_opt_ void* clientDrawingContext,
+ _Out_ BOOL* isDisabled
+ )
+{
+ *isDisabled = true;
+ return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE FallbackLogTextRenderer_GetCurrentTransform(
+ IDWriteTextRenderer *This,
+ _In_opt_ void* clientDrawingContext,
+ _Out_ DWRITE_MATRIX* transform
+ )
+{
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE FallbackLogTextRenderer_GetPixelsPerDip(
+ IDWriteTextRenderer *This,
+ _In_opt_ void* clientDrawingContext,
+ _Out_ FLOAT* pixelsPerDip
+ )
+{
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE FallbackLogTextRenderer_DrawGlyphRun(
+ IDWriteTextRenderer *This,
+ _In_opt_ void* clientDrawingContext,
+ FLOAT baselineOriginX,
+ FLOAT baselineOriginY,
+ DWRITE_MEASURING_MODE measuringMode,
+ _In_ DWRITE_GLYPH_RUN const* glyphRun,
+ _In_ DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
+ IUnknown* clientDrawingEffect
+ )
+{
+ FallbackLogTextRenderer *this = (FallbackLogTextRenderer *)This;
+ HRESULT hr;
+ IDWriteFontCollection *font_coll = NULL;
+ IDWriteFont **font = (IDWriteFont **)clientDrawingContext;
+
+ hr = IDWriteFactory_GetSystemFontCollection(this->dw_factory, &font_coll, FALSE);
+ if (FAILED(hr))
+ return E_FAIL;
+
+ hr = IDWriteFontCollection_GetFontFromFontFace(font_coll, glyphRun->fontFace,
+ font);
+ if (FAILED(hr))
+ return E_FAIL;
+
+ return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE FallbackLogTextRenderer_DrawUnderline(
+ IDWriteTextRenderer *This,
+ _In_opt_ void* clientDrawingContext,
+ FLOAT baselineOriginX,
+ FLOAT baselineOriginY,
+ _In_ DWRITE_UNDERLINE const* underline,
+ IUnknown* clientDrawingEffect
+ )
+{
+ return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE FallbackLogTextRenderer_DrawStrikethrough(
+ IDWriteTextRenderer *This,
+ _In_opt_ void* clientDrawingContext,
+ FLOAT baselineOriginX,
+ FLOAT baselineOriginY,
+ _In_ DWRITE_STRIKETHROUGH const* strikethrough,
+ IUnknown* clientDrawingEffect
+ )
+{
+ return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE FallbackLogTextRenderer_DrawInlineObject(
+ IDWriteTextRenderer *This,
+ void *clientDrawingContext,
+ FLOAT originX,
+ FLOAT originY,
+ IDWriteInlineObject *inlineObject,
+ WINBOOL isSideways,
+ WINBOOL isRightToLeft,
+ IUnknown *clientDrawingEffect
+ )
+{
+ return S_OK;
+}
+
+// IUnknown methods
+
+static ULONG STDMETHODCALLTYPE FallbackLogTextRenderer_AddRef(
+ IDWriteTextRenderer *This
+ )
+{
+ FallbackLogTextRenderer *this = (FallbackLogTextRenderer *)This;
+ return InterlockedIncrement(&this->ref_count);
+}
+
+static ULONG STDMETHODCALLTYPE FallbackLogTextRenderer_Release(
+ IDWriteTextRenderer *This
+ )
+{
+ FallbackLogTextRenderer *this = (FallbackLogTextRenderer *)This;
+ unsigned long new_count = InterlockedDecrement(&this->ref_count);
+ if (new_count == 0) {
+ free(this);
+ return 0;
+ }
+
+ return new_count;
+}
+
+static HRESULT STDMETHODCALLTYPE FallbackLogTextRenderer_QueryInterface(
+ IDWriteTextRenderer *This,
+ REFIID riid,
+ void **ppvObject
+ )
+{
+ if (IsEqualGUID(riid, &IID_IDWriteTextRenderer)
+ || IsEqualGUID(riid, &IID_IDWritePixelSnapping)
+ || IsEqualGUID(riid, &IID_IUnknown)) {
+ *ppvObject = This;
+ } else {
+ *ppvObject = NULL;
+ return E_FAIL;
+ }
+
+ This->lpVtbl->AddRef(This);
+ return S_OK;
+}
+
+static void init_FallbackLogTextRenderer(FallbackLogTextRenderer *r,
+ IDWriteFactory *factory)
+{
+ *r = (FallbackLogTextRenderer){
+ .iface = {
+ .lpVtbl = &r->vtbl,
+ },
+ .vtbl = {
+ FallbackLogTextRenderer_QueryInterface,
+ FallbackLogTextRenderer_AddRef,
+ FallbackLogTextRenderer_Release,
+ FallbackLogTextRenderer_IsPixelSnappingDisabled,
+ FallbackLogTextRenderer_GetCurrentTransform,
+ FallbackLogTextRenderer_GetPixelsPerDip,
+ FallbackLogTextRenderer_DrawGlyphRun,
+ FallbackLogTextRenderer_DrawUnderline,
+ FallbackLogTextRenderer_DrawStrikethrough,
+ FallbackLogTextRenderer_DrawInlineObject,
+ },
+ .dw_factory = factory,
+ };
+}
+
+/*
+ * This function is called whenever a font is used for the first
+ * time. It will create a FontStream for memory reading, which
+ * will be stored within the private data.
+ */
+static bool init_font_private(FontPrivate *priv)
+{
+ HRESULT hr = S_OK;
+ IDWriteFont *font = priv->font;
+ IDWriteFontFace *face = NULL;
+ IDWriteFontFile *file = NULL;
+ IDWriteFontFileStream *stream = NULL;
+ IDWriteFontFileLoader *loader = NULL;
+ UINT32 n_files = 1;
+ const void *refKey = NULL;
+ UINT32 keySize = 0;
+
+ if (priv->stream != NULL)
+ return true;
+
+ hr = IDWriteFont_CreateFontFace(font, &face);
+ if (FAILED(hr) || !face)
+ return false;
+
+ /* DirectWrite only supports one file per face */
+ hr = IDWriteFontFace_GetFiles(face, &n_files, &file);
+ if (FAILED(hr) || !file) {
+ IDWriteFontFace_Release(face);
+ return false;
+ }
+
+ hr = IDWriteFontFile_GetReferenceKey(file, &refKey, &keySize);
+ if (FAILED(hr)) {
+ IDWriteFontFile_Release(file);
+ IDWriteFontFace_Release(face);
+ return false;
+ }
+
+ hr = IDWriteFontFile_GetLoader(file, &loader);
+ if (FAILED(hr) || !loader) {
+ IDWriteFontFile_Release(file);
+ IDWriteFontFace_Release(face);
+ return false;
+ }
+
+ hr = IDWriteFontFileLoader_CreateStreamFromKey(loader, refKey, keySize, &stream);
+ if (FAILED(hr) || !stream) {
+ IDWriteFontFile_Release(file);
+ IDWriteFontFace_Release(face);
+ return false;
+ }
+
+ priv->stream = stream;
+ IDWriteFontFile_Release(file);
+ IDWriteFontFace_Release(face);
+
+ return true;
+}
+
+/*
+ * Read a specified part of a fontfile into memory.
+ * If the font wasn't used before first creates a
+ * FontStream and save it into the private data for later usage.
+ * If the parameter "buf" is NULL libass wants to know the
+ * size of the Fontfile
+ */
+static size_t get_data(void *data, unsigned char *buf, size_t offset,
+ size_t length)
+{
+ HRESULT hr = S_OK;
+ FontPrivate *priv = (FontPrivate *) data;
+ const void *fileBuf = NULL;
+ void *fragContext = NULL;
+
+ if (!init_font_private(priv))
+ return 0;
+
+ if (buf == NULL) {
+ UINT64 fileSize;
+ hr = IDWriteFontFileStream_GetFileSize(priv->stream, &fileSize);
+ if (FAILED(hr))
+ return 0;
+
+ return fileSize;
+ }
+
+ hr = IDWriteFontFileStream_ReadFileFragment(priv->stream, &fileBuf, offset,
+ length, &fragContext);
+
+ if (FAILED(hr) || !fileBuf)
+ return 0;
+
+ memcpy(buf, fileBuf, length);
+
+ IDWriteFontFileStream_ReleaseFileFragment(priv->stream, fragContext);
+
+ return length;
+}
+
+/*
+ * Checks if the passed font has a specific unicode
+ * character. Returns 0 for failure and 1 for success
+ */
+static int check_glyph(void *data, uint32_t code)
+{
+ HRESULT hr = S_OK;
+ FontPrivate *priv = (FontPrivate *) data;
+ BOOL exists = FALSE;
+
+ if (code == 0)
+ return 1;
+
+ IDWriteFont_HasCharacter(priv->font, code, &exists);
+ if (FAILED(hr))
+ return 0;
+
+ return exists;
+}
+
+/*
+ * This will release the directwrite backend
+ */
+static void destroy_provider(void *priv)
+{
+ ProviderPrivate *provider_priv = (ProviderPrivate *)priv;
+ provider_priv->factory->lpVtbl->Release(provider_priv->factory);
+ FreeLibrary(provider_priv->directwrite_lib);
+ free(provider_priv);
+}
+
+/*
+ * This will destroy a specific font and it's
+ * Fontstream (in case it does exist)
+ */
+
+static void destroy_font(void *data)
+{
+ FontPrivate *priv = (FontPrivate *) data;
+
+ IDWriteFont_Release(priv->font);
+ if (pr