win32 font faces

View: New views
2 Messages — Rating Filter:   Alert me  

win32 font faces

by Adrian Johnson :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Bug 24849 reports that Inkscape on win32 is creating PDFs with the same
font embedded multiple times. This is because the win32 font backend is
not using a hash table of font faces to return an existing face when the
same font is used.

Attached is a patch that to fix this. The patch doesn't completely solve
the problem of ensuring each win32 font used is embedded only once. The
function

cairo_win32_font_face_create_for_logfontw_hfont ((LOGFONTW *logfont,
                                                          HFONT font);)

which can specify a HFONT object to use. My patch uses both LOGFONT and
HFONT as the hash table key so creating fonts with the same LOGFONT but
different HFONT objects will return different font faces. For Inkscape
the patch fixes the problem as Inkscape only uses
cairo_win32_font_face_create_for_logfontw ().

Would it be better to filter out duplicate LOGFONTS in
cairo-scaled-font-subsets.c instead to guarantee fonts will only be
embedded once?


From 668fb31a2bbf8cc01baaa6c786b7757066edb4df Mon Sep 17 00:00:00 2001
From: Adrian Johnson <ajohnson@...>
Date: Fri, 6 Nov 2009 23:37:49 +1030
Subject: [PATCH] win32: Use a font_face hash table provide unique font faces

Similar to the freetype and toy font backends, use a hash table
to provide map logfont,hfont to font faces.

Bug 24849 - Inkscape is reembeding the same font.
---
 src/cairo-debug.c              |    4 +
 src/cairo-mutex-list-private.h |    4 +
 src/cairo-win32-font.c         |  163 ++++++++++++++++++++++++++++++++++++++-
 src/cairoint.h                 |    3 +
 4 files changed, 169 insertions(+), 5 deletions(-)

diff --git a/src/cairo-debug.c b/src/cairo-debug.c
index 9160728..0cb416b 100644
--- a/src/cairo-debug.c
+++ b/src/cairo-debug.c
@@ -69,6 +69,10 @@ cairo_debug_reset_static_data (void)
     _cairo_ft_font_reset_static_data ();
 #endif
 
+#if CAIRO_HAS_WIN32_FONT
+    _cairo_win32_font_reset_static_data ();
+#endif
+
     _cairo_intern_string_reset_static_data ();
 
     _cairo_scaled_font_reset_static_data ();
diff --git a/src/cairo-mutex-list-private.h b/src/cairo-mutex-list-private.h
index 2f48316..3af4d71 100644
--- a/src/cairo-mutex-list-private.h
+++ b/src/cairo-mutex-list-private.h
@@ -48,6 +48,10 @@ CAIRO_MUTEX_DECLARE (_cairo_scaled_font_error_mutex)
 CAIRO_MUTEX_DECLARE (_cairo_ft_unscaled_font_map_mutex)
 #endif
 
+#if CAIRO_HAS_WIN32_FONT
+CAIRO_MUTEX_DECLARE (_cairo_win32_font_face_mutex)
+#endif
+
 #if CAIRO_HAS_XLIB_SURFACE
 CAIRO_MUTEX_DECLARE (_cairo_xlib_display_mutex)
 #endif
diff --git a/src/cairo-win32-font.c b/src/cairo-win32-font.c
index 7a86cc8..c13901d 100644
--- a/src/cairo-win32-font.c
+++ b/src/cairo-win32-font.c
@@ -46,6 +46,8 @@
 
 #include "cairo-win32-private.h"
 
+#include <wchar.h>
+
 #ifndef SPI_GETFONTSMOOTHINGTYPE
 #define SPI_GETFONTSMOOTHINGTYPE 0x200a
 #endif
@@ -1903,6 +1905,120 @@ const cairo_font_face_backend_t _cairo_win32_font_face_backend = {
     _cairo_win32_font_face_scaled_font_create
 };
 
+/* We maintain a hash table from LOGFONT,HFONT => #cairo_font_face_t.
+ * The primary purpose of this mapping is to provide unique
+ * #cairo_font_face_t values so that our cache and mapping from
+ * #cairo_font_face_t => #cairo_scaled_font_t works. Once the
+ * corresponding #cairo_font_face_t objects fall out of downstream
+ * caches, we don't need them in this hash table anymore.
+ *
+ * Modifications to this hash table are protected by
+ * _cairo_win32_font_face_mutex.
+ */
+
+static cairo_hash_table_t *cairo_win32_font_face_hash_table = NULL;
+
+static int
+_cairo_win32_font_face_keys_equal (const void *key_a,
+   const void *key_b);
+
+static void
+_cairo_win32_font_face_hash_table_destroy (void)
+{
+    cairo_win32_font_face_t *font_face;
+
+    CAIRO_MUTEX_LOCK (_cairo_win32_font_face_mutex);
+
+    if (cairo_win32_font_face_hash_table) {
+ /* This is rather inefficient, but destroying the hash table
+ * is something we only do during debugging, (during
+ * cairo_debug_reset_static_data), when efficiency is not
+ * relevant. */
+        while (1) {
+    font_face= _cairo_hash_table_random_entry (cairo_win32_font_face_hash_table,
+       NULL);
+    if (font_face == NULL)
+ break;
+    _cairo_hash_table_remove (cairo_win32_font_face_hash_table,
+      &font_face->base.hash_entry);
+
+    cairo_font_face_destroy (&font_face->base);
+ }
+
+ _cairo_hash_table_destroy (cairo_win32_font_face_hash_table);
+
+ cairo_win32_font_face_hash_table = NULL;
+    }
+
+    CAIRO_MUTEX_UNLOCK (_cairo_win32_font_face_mutex);
+}
+
+static cairo_hash_table_t *
+_cairo_win32_font_face_hash_table_lock (void)
+{
+    CAIRO_MUTEX_LOCK (_cairo_win32_font_face_mutex);
+
+    if (cairo_win32_font_face_hash_table == NULL)
+    {
+ cairo_win32_font_face_hash_table =
+ _cairo_hash_table_create (_cairo_win32_font_face_keys_equal);
+
+ if (cairo_win32_font_face_hash_table == NULL) {
+    CAIRO_MUTEX_UNLOCK (_cairo_win32_font_face_mutex);
+    _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+    return NULL;
+ }
+    }
+
+    return cairo_win32_font_face_hash_table;
+}
+
+static void
+_cairo_win32_font_face_hash_table_unlock (void)
+{
+    CAIRO_MUTEX_UNLOCK (_cairo_win32_font_face_mutex);
+}
+
+static void
+_cairo_win32_font_face_init_key (cairo_win32_font_face_t *key,
+ LOGFONTW                *logfont,
+ HFONT                    font)
+{
+    unsigned long hash = _CAIRO_HASH_INIT_VALUE;
+
+    key->logfont = *logfont;
+    key->hfont = font;
+
+    hash = _cairo_hash_bytes (0, logfont->lfFaceName, wcslen(logfont->lfFaceName));
+    hash = _cairo_hash_bytes (hash, &logfont->lfWeight, sizeof(logfont->lfWeight));
+    hash = _cairo_hash_bytes (hash, &logfont->lfItalic, sizeof(logfont->lfItalic));
+    hash = _cairo_hash_bytes (hash, &font, sizeof(font));
+
+    key->base.hash_entry.hash = hash;
+}
+
+static int
+_cairo_win32_font_face_keys_equal (const void *key_a,
+   const void *key_b)
+{
+    const cairo_win32_font_face_t *face_a = key_a;
+    const cairo_win32_font_face_t *face_b = key_b;
+
+    if (face_a->logfont.lfWeight         == face_b->logfont.lfWeight &&
+ face_a->logfont.lfItalic         == face_b->logfont.lfItalic &&
+ face_a->logfont.lfUnderline      == face_b->logfont.lfUnderline &&
+ face_a->logfont.lfStrikeOut      == face_b->logfont.lfStrikeOut &&
+ face_a->logfont.lfCharSet        == face_b->logfont.lfCharSet &&
+ face_a->logfont.lfOutPrecision   == face_b->logfont.lfOutPrecision &&
+ face_a->logfont.lfClipPrecision  == face_b->logfont.lfClipPrecision &&
+ face_a->logfont.lfPitchAndFamily == face_b->logfont.lfPitchAndFamily &&
+ wcscmp (face_a->logfont.lfFaceName, face_a->logfont.lfFaceName) == 0 &&
+ face_a->hfont                    == face_b->hfont)
+ return TRUE;
+    else
+ return FALSE;
+}
+
 /**
  * cairo_win32_font_face_create_for_logfontw_hfont:
  * @logfont: A #LOGFONTW structure specifying the font to use.
@@ -1925,20 +2041,51 @@ const cairo_font_face_backend_t _cairo_win32_font_face_backend = {
 cairo_font_face_t *
 cairo_win32_font_face_create_for_logfontw_hfont (LOGFONTW *logfont, HFONT font)
 {
-    cairo_win32_font_face_t *font_face;
+    cairo_win32_font_face_t *font_face, key;
+    cairo_hash_table_t *hash_table;
+    cairo_status_t status;
+
+    hash_table = _cairo_win32_font_face_hash_table_lock ();
+    if (unlikely (hash_table == NULL)) {
+        _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return (cairo_font_face_t *)&_cairo_font_face_nil;
+    }
+
+    _cairo_win32_font_face_init_key (&key, logfont, font);
+
+    /* Return existing unscaled font if it exists in the hash table. */
+    font_face = _cairo_hash_table_lookup (hash_table,
+ &key.base.hash_entry);
+    if (font_face != NULL) {
+ cairo_font_face_reference (&font_face->base);
+ goto DONE;
+    }
 
+    /* Otherwise create it and insert into hash table. */
     font_face = malloc (sizeof (cairo_win32_font_face_t));
     if (!font_face) {
         _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
-        return (cairo_font_face_t *)&_cairo_font_face_nil;
+ goto FAIL;
     }
 
-    font_face->logfont = *logfont;
-    font_face->hfont = font;
-
+    _cairo_win32_font_face_init_key (font_face, logfont, font);
     _cairo_font_face_init (&font_face->base, &_cairo_win32_font_face_backend);
 
+    assert (font_face->base.hash_entry.hash == key.base.hash_entry.hash);
+    status = _cairo_hash_table_insert (hash_table,
+       &font_face->base.hash_entry);
+    if (unlikely (status))
+ goto FAIL;
+
+DONE:
+    _cairo_win32_font_face_hash_table_unlock ();
+
     return &font_face->base;
+
+FAIL:
+    _cairo_win32_font_face_hash_table_unlock ();
+
+    return (cairo_font_face_t *)&_cairo_font_face_nil;
 }
 
 /**
@@ -2147,3 +2294,9 @@ cairo_win32_scaled_font_get_device_to_logical (cairo_scaled_font_t *scaled_font,
     }
     *device_to_logical = win_font->device_to_logical;
 }
+
+void
+_cairo_win32_font_reset_static_data (void)
+{
+    _cairo_win32_font_face_hash_table_destroy ();
+}
diff --git a/src/cairoint.h b/src/cairoint.h
index 5912173..90059a1 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -433,6 +433,9 @@ _cairo_toy_font_face_reset_static_data (void);
 cairo_private void
 _cairo_ft_font_reset_static_data (void);
 
+cairo_private void
+_cairo_win32_font_reset_static_data (void);
+
 /* the font backend interface */
 
 struct _cairo_unscaled_font_backend {
--
1.6.4.2


_______________________________________________
cairo mailing list
cairo@...
http://lists.cairographics.org/mailman/listinfo/cairo

Re: win32 font faces

by Ian Britten :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Adrian Johnson wrote:
> Bug 24849 reports that Inkscape on win32 is creating PDFs with the same
> font embedded multiple times.

FYI - I encountered a similar problem with custom/user fonts being
embedded multiple times in PDFs.  (Or was it with FT fonts?  Or was
it both?  I don't recall at the moment.)  However, I had to end up
filtering out duplicates myself...

   [ snip ]
> Would it be better to filter out duplicate LOGFONTS in
> cairo-scaled-font-subsets.c instead to guarantee fonts will only be
> embedded once?

Would this alternate fix be limited to just win32/LOGFONTS, or would
it filter out any/all duplicates (Including user fonts)?

I don't particularly _need_ it, since my code is in place, and I have
to do some caching anyways to prevent dangling pointers, but if this
affected my case, I'd probably review/revise my code accordingly.

Ian
_______________________________________________
cairo mailing list
cairo@...
http://lists.cairographics.org/mailman/listinfo/cairo