From 72307b0d50a5d1dc568881fdc135444afce24fde Mon Sep 17 00:00:00 2001
From: Jonathan Kew <jfkthame@gmail.com>
Date: Sun, 12 Jul 2009 18:04:43 +0100
Subject: [PATCH] implement --disable-fontconfig and --enable-mac-fonts options

--disable-fontconfig removes the dependency on Fontconfig; if no alternative is provided,
then non-embedded fonts cannot be rendered.

--enable-mac-fonts allows for the use of Mac OS X system fonts for non-embedded fonts in the PDF,
without requiring X11 or fontconfig to be installed.
---
 configure.ac               |   29 +++++-
 m4/mac-framework.m4        |   30 +++++
 poppler/GlobalParams.cc    |   20 +++-
 poppler/GlobalParams.h     |   15 +++
 poppler/GlobalParamsMac.cc |  263 ++++++++++++++++++++++++++++++++++++++++++++
 poppler/Makefile.am        |    7 +
 6 files changed, 362 insertions(+), 2 deletions(-)
 create mode 100644 m4/mac-framework.m4
 create mode 100644 poppler/GlobalParamsMac.cc

diff --git a/configure.ac b/configure.ac
index e7db018..03fa83a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -232,7 +232,34 @@ fi
 AC_SUBST(FREETYPE_CFLAGS)
 AC_SUBST(FREETYPE_LIBS)
 
-PKG_CHECK_MODULES(FONTCONFIG, fontconfig >= 2.0.0)
+AC_ARG_ENABLE(fontconfig,
+              AC_HELP_STRING([--disable-fontconfig],
+                             [Disable FontConfig support.]),
+              enable_fontconfig=$enableval,
+              enable_fontconfig="yes")
+AH_TEMPLATE([DISABLE_FONTCONFIG], [Disable FontConfig support.])
+if test x$enable_fontconfig = xyes; then
+    PKG_CHECK_MODULES(FONTCONFIG, fontconfig >= 2.0.0)
+else
+    AC_DEFINE(DISABLE_FONTCONFIG)
+fi
+
+AC_ARG_ENABLE(mac-fonts,
+              AC_HELP_STRING([--enable-mac-fonts],
+                             [Use Mac system fonts when fonts are not embedded in the PDF.]),
+              enable_mac_fonts=$enableval,
+              enable_mac_fonts="no")
+AH_TEMPLATE([ENABLE_MAC_FONTS], [Enable Mac font support.])
+if test x$enable_mac_fonts = xyes; then
+    KPSE_CHECK_FRAMEWORK([ApplicationServices], [ATSFontRef fontRef])
+    if test x$kpse_cv_have_ApplicationServices = xyes; then
+        AC_DEFINE(ENABLE_MAC_FONTS, 1, [Use Mac system fonts])
+    else
+        AC_MSG_WARN("Mac system font support is not available on this platform.")
+        enable_mac_fonts="no"
+    fi
+fi
+AM_CONDITIONAL(ENABLE_MAC_FONTS, test x$enable_mac_fonts = xyes)
 
 AC_ARG_ENABLE(splash-output,
               AC_HELP_STRING([--disable-splash-output],
diff --git a/m4/mac-framework.m4 b/m4/mac-framework.m4
new file mode 100644
index 0000000..c2aca13
--- /dev/null
+++ b/m4/mac-framework.m4
@@ -0,0 +1,30 @@
+# based on kpse-macos-framework.m4 from the TeX Live source tree
+# -------------------------------------------------------------------
+# Original copyright notice:
+#
+# Public macros for the TeX Live (TL) tree.
+# Copyright (C) 2005 - 2008 Jonathan Kew <...@...>
+# Copyright (C) 2009 Peter Breitenlohner <tex-live@tug.org>
+#
+# This file is free software; the copyright holders
+# give unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+# -------------------------------------------------------------------
+#
+# serial 0
+
+# KPSE_CHECK_FRAMEWORK(FRAMEWORK, BODY)
+# -------------------------------------
+# Check for mthe Mac OS X framework FRAMEWORK (using BODY) and if found,
+# set kpse_cv_have_FRAMEWORK to yes and define HAVE_FRAMEWORK.
+AC_DEFUN([KPSE_CHECK_FRAMEWORK],
+[AC_CACHE_CHECK([for Mac OS X $1 framework],
+                [kpse_cv_have_$1],
+                [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <$1/$1.h>]],
+                                                    [[$2]])],
+                                   [kpse_cv_have_$1=yes],
+                                   [kpse_cv_have_$1=no])])
+AS_IF([test "x$kpse_cv_have_$1" = xyes],
+      [AC_DEFINE([HAVE_]AS_TR_CPP([$1]), 1,
+                 [Define to 1 if you have the Mac OS X $1 framework.])])[]dnl
+]) # KPSE_CHECK_FRAMEWORK
diff --git a/poppler/GlobalParams.cc b/poppler/GlobalParams.cc
index 177bc8a..5bd0a10 100644
--- a/poppler/GlobalParams.cc
+++ b/poppler/GlobalParams.cc
@@ -555,10 +555,17 @@ GlobalParams::GlobalParams(const char *customPopplerDataDir)
   UnicodeMap *map;
   int i;
 
-#ifndef _MSC_VER  
+#ifndef _MSC_VER
+#ifndef DISABLE_FONTCONFIG
   FcInit();
   FCcfg = FcConfigGetCurrent();
 #endif
+#endif
+
+#ifdef ENABLE_MAC_FONTS
+  tempFontFiles = NULL;
+  numTempFontFiles = 0;
+#endif
 
 #if MULTITHREADED
   gInitMutex(&mutex);
@@ -832,6 +839,13 @@ GlobalParams::~GlobalParams() {
   deleteGooList(plugins, Plugin);
 #endif
 
+#ifdef ENABLE_MAC_FONTS
+  while (numTempFontFiles > 0) {
+    --numTempFontFiles;
+    unlink(tempFontFiles[numTempFontFiles]);
+  }
+#endif
+
 #if MULTITHREADED
   gDestroyMutex(&mutex);
   gDestroyMutex(&unicodeMapCacheMutex);
@@ -960,6 +974,7 @@ static GBool findModifier(const char *name, const char *modifier, const char **s
 }
 
 #ifndef _MSC_VER
+#ifndef DISABLE_FONTCONFIG
 static FcPattern *buildFcPattern(GfxFont *font)
 {
   int weight = -1,
@@ -1103,11 +1118,13 @@ static FcPattern *buildFcPattern(GfxFont *font)
   return p;
 }
 #endif
+#endif
 
 /* if you can't or don't want to use Fontconfig, you need to implement
    this function for your platform. For Windows, it's in GlobalParamsWin.cc
 */
 #ifndef _MSC_VER
+#ifndef DISABLE_FONTCONFIG
 DisplayFontParam *GlobalParams::getDisplayFont(GfxFont *font) {
   DisplayFontParam *dfp;
   FcPattern *p=0;
@@ -1167,6 +1184,7 @@ fin:
   return dfp;
 }
 #endif
+#endif
 
 GBool GlobalParams::getPSExpandSmaller() {
   GBool f;
diff --git a/poppler/GlobalParams.h b/poppler/GlobalParams.h
index 5a724af..7cb2ffc 100644
--- a/poppler/GlobalParams.h
+++ b/poppler/GlobalParams.h
@@ -33,12 +33,16 @@
 #pragma interface
 #endif
 
+#include "config.h"
+
 #include <assert.h>
 #include "poppler-config.h"
 #include <stdio.h>
 #ifndef _MSC_VER
+#ifndef DISABLE_FONTCONFIG
 #include <fontconfig/fontconfig.h>
 #endif
+#endif
 #include "goo/gtypes.h"
 #include "CharTypes.h"
 
@@ -172,6 +176,10 @@ public:
   void setupBaseFonts(char *dir);
 #endif
 
+#ifdef ENABLE_MAC_FONTS
+  GBool loadPlatformFont(const char * fontName);
+#endif
+
   //----- accessors
 
   CharCode getMacRomanCharCode(char *charName);
@@ -351,8 +359,15 @@ private:
   CMapCache *cMapCache;
   
 #ifndef _MSC_VER
+#ifndef DISABLE_FONTCONFIG
   FcConfig *FCcfg;
 #endif
+#endif
+
+#ifdef ENABLE_MAC_FONTS
+  char **tempFontFiles;
+  int numTempFontFiles;
+#endif
 
 #ifdef ENABLE_PLUGINS
   GList *plugins;		// list of plugins [Plugin]
diff --git a/poppler/GlobalParamsMac.cc b/poppler/GlobalParamsMac.cc
new file mode 100644
index 0000000..35c36f5
--- /dev/null
+++ b/poppler/GlobalParamsMac.cc
@@ -0,0 +1,263 @@
+//========================================================================
+//
+// GlobalParamsMac.cc
+//
+//========================================================================
+
+//========================================================================
+//
+// Contributed to the Poppler project - http://poppler.freedesktop.org
+//
+// Copyright (c) 2009 Jonathan Kew
+//
+//========================================================================
+
+#include <config.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <string.h>
+#include <stdio.h>
+
+#include "goo/gmem.h"
+#include "goo/GooString.h"
+#include "goo/GooList.h"
+#include "goo/GooHash.h"
+#include "goo/gfile.h"
+#include "Error.h"
+
+#include "GlobalParams.h"
+#include "GfxFont.h"
+
+#if MULTITHREADED
+#  define lockGlobalParams            gLockMutex(&mutex)
+#  define lockUnicodeMapCache         gLockMutex(&unicodeMapCacheMutex)
+#  define lockCMapCache               gLockMutex(&cMapCacheMutex)
+#  define unlockGlobalParams          gUnlockMutex(&mutex)
+#  define unlockUnicodeMapCache       gUnlockMutex(&unicodeMapCacheMutex)
+#  define unlockCMapCache             gUnlockMutex(&cMapCacheMutex)
+#else
+#  define lockGlobalParams
+#  define lockUnicodeMapCache
+#  define lockCMapCache
+#  define unlockGlobalParams
+#  define unlockUnicodeMapCache
+#  define unlockCMapCache
+#endif
+
+/* Mac implementation of external font matching code */
+
+#include <ApplicationServices/ApplicationServices.h>
+
+GBool GlobalParams::loadPlatformFont(const char * fontName) {
+
+  CFStringRef psName = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault,
+							fontName,
+							kCFStringEncodingASCII,
+							kCFAllocatorNull);
+  ATSFontRef fontRef = ATSFontFindFromPostScriptName(psName, kATSOptionFlagsDefault);
+  CFRelease(psName);
+  if (fontRef == kATSUInvalidFontID)
+    return gFalse;
+
+  // Currently support only TrueType fonts: check for presence of 'glyf' table
+  // TODO: what about OpenType/CFF? DisplayFontParam doesn't seem to allow for this
+#define TAG(a,b,c,d) (UInt32)((a)<<24) | (UInt32)((b)<<16) | (UInt32)((c)<<8) | (UInt32)(d)
+
+  ByteCount tableSize;
+  if (ATSFontGetTable(fontRef, TAG('g','l','y','f'), 0, 0, NULL, &tableSize) != noErr ||
+      tableSize == 0)
+    return gFalse;
+
+  do { // if the font comes from a .ttf file, we can use that directly
+    FSRef fsRef;
+    if (ATSFontGetFileReference(fontRef, &fsRef) != noErr)
+      break;
+
+    UInt8 fontPath[PATH_MAX + 1];
+    if (FSRefMakePath(&fsRef, fontPath, PATH_MAX) != noErr)
+      break;
+
+    int pathLen = strlen((const char *) fontPath);
+    if (pathLen > 4 && fontPath[pathLen - 4] == '.') {
+      const char * ext = (const char *) fontPath + pathLen - 3;
+
+      // accept either .ttf or .otf extension; .otf could contain TrueType-format glyphs
+      if (strcmp(ext, "ttf") == 0 || strcmp(ext, "TTF") == 0 ||
+          strcmp(ext, "otf") == 0 || strcmp(ext, "OTF") == 0) {
+        DisplayFontParam *dfp = new DisplayFontParam(new GooString(fontName), displayFontTT);
+        dfp->setFileName(new GooString((const char *) fontPath));
+        displayFonts->add(dfp->name, dfp);
+        return gTrue;
+      }
+    }
+  } while (0);
+
+  // for .dfont or suitcase files, FoFiTrueType can't handle them, so we extract
+  // the required font to a temporary .ttf file and then use that
+
+  struct sfntHeader {
+    UInt32 version;
+    UInt16 numTables;
+    UInt16 searchRange;
+    UInt16 entrySelector;
+    UInt16 rangeShift;
+    struct {
+      UInt32 tag;
+      UInt32 checkSum;
+      UInt32 offset;
+      UInt32 length;
+    } dir[1];
+  };
+
+  ByteCount headerSize;
+  if (ATSFontGetTableDirectory(fontRef, 0, NULL, &headerSize) != noErr)
+    return gFalse;
+  struct sfntHeader * header = (struct sfntHeader *) new Byte[headerSize];
+  ATSFontGetTableDirectory(fontRef, headerSize, (Byte *) header, &headerSize);
+
+#define READ16(x) (UInt16)(((UInt8*)&(x))[0]<<8) + (UInt16)((UInt8*)&(x))[1]
+#define READ32(x) (UInt32)(((UInt8*)&(x))[0]<<24) + (UInt32)(((UInt8*)&(x))[1]<<16) + (UInt32)(((UInt8*)&(x))[2]<<8) + (UInt32)((UInt8*)&(x))[3]
+
+  UInt32 version = READ32(header->version);
+  if (version != 0x00010000 &&
+// TODO: figure out whether we can support OpenType/CFF fonts here
+//      version != TAG('O','T','T','0') &&
+      version != TAG('t','r','u','e')) {
+    delete [] (Byte *) header;
+    return gFalse;
+  }
+
+  UInt16 numTables = READ16(header->numTables);
+  UInt32 maxOffset = 0;
+  for (UInt16 i = 0; i < numTables; ++i) {
+    UInt32 end = READ32(header->dir[i].offset) + READ32(header->dir[i].length);
+    if (end > maxOffset)
+      maxOffset = end;
+  }
+
+  char * ttfData = new char[maxOffset];
+  struct sfntHeader * newFont = (struct sfntHeader *) ttfData;
+
+  newFont->version = header->version;
+
+  UInt16 realTables = 0, tableIndex;
+  for (tableIndex = 0; tableIndex < numTables; ++tableIndex) {
+    ByteCount tableLoc = READ32(header->dir[tableIndex].offset);
+    if (tableLoc == 0) // ATS synthetic table, do not copy
+      continue;
+    tableSize = READ32(header->dir[tableIndex].length);
+    if (ATSFontGetTable(fontRef, READ32(header->dir[tableIndex].tag),
+                        0, tableSize, ttfData + tableLoc, &tableSize) != noErr)
+      break;
+    newFont->dir[realTables] = header->dir[tableIndex];
+    realTables++;
+  }
+  delete [] (Byte*) header;
+  if (tableIndex < numTables) {
+    delete [] ttfData;
+    return gFalse;
+  }
+
+  newFont->numTables = READ16(realTables);
+  UInt16 searchRange = realTables;
+  searchRange |= searchRange >> 1;
+  searchRange |= searchRange >> 2;
+  searchRange |= searchRange >> 4;
+  searchRange |= searchRange >> 8;
+  searchRange &= ~searchRange >> 1;
+  searchRange *= 16;
+  newFont->searchRange = READ16(searchRange);
+  UInt16 rangeShift = realTables * 16 - searchRange;
+  UInt16 entrySelector = 0;
+  while (searchRange > 16) {
+    ++entrySelector;
+    searchRange >>= 1;
+  }
+  newFont->entrySelector = READ16(entrySelector);
+  newFont->rangeShift = READ16(rangeShift);
+
+  char * fontPath = copyString("/tmp/XXXXXXXX.ttf");
+  if (mkstemps(fontPath, 4) == -1) {
+    delete [] ttfData;
+    gfree(fontPath);
+    return gFalse;
+  }
+
+  GBool writtenOk = gFalse;
+  FILE * ttfFile = fopen(fontPath, "wb");
+  if (ttfFile) {
+    writtenOk = (fwrite(ttfData, 1, maxOffset, ttfFile) == maxOffset);
+    fclose(ttfFile);
+  }
+  delete [] ttfData;
+  if (!writtenOk) {
+    unlink(fontPath);
+    gfree(fontPath);
+    return gFalse;
+  }
+
+  void * p = realloc(tempFontFiles, (numTempFontFiles + 1) * sizeof(char *));
+  if (!p) {
+    unlink(fontPath);
+    gfree(fontPath);
+    return gFalse;
+  }
+  tempFontFiles = (char **) p;
+  tempFontFiles[numTempFontFiles] = fontPath;
+  ++numTempFontFiles;
+
+  DisplayFontParam *dfp = new DisplayFontParam(new GooString(fontName), displayFontTT);
+  dfp->setFileName(new GooString(fontPath));
+  displayFonts->add(dfp->name, dfp);
+
+  return gTrue;
+}
+
+static const char *
+findSubstituteName(char * fontName) {
+  GBool bold = (strstr(fontName, "Bold") != NULL ||
+                strstr(fontName, "bold") != NULL || // to catch "Semibold", "Demibold", etc
+                strstr(fontName, "Ultra") != NULL ||
+                strstr(fontName, "Heavy") != NULL ||
+                strstr(fontName, "Black") != NULL);
+  GBool ital = (strstr(fontName, "Italic") != NULL ||
+                strstr(fontName, "Oblique") != NULL);
+  if (bold) {
+    return ital ? "Helvetica-BoldOblique" : "Helvetica-Bold";
+  } else {
+    return ital ? "Helvetica-Oblique" : "Helvetica";
+  }
+}
+
+DisplayFontParam *GlobalParams::getDisplayFont(GfxFont *font) {
+  DisplayFontParam *  dfp;
+  GooString *         fontName = font->getName();
+  char *              substFontName = NULL;
+
+  if (!fontName) return NULL;
+  lockGlobalParams;
+
+  dfp = (DisplayFontParam *)displayFonts->lookup(fontName);
+  if (!dfp) {
+    if (loadPlatformFont(fontName->getCString()))
+      dfp = (DisplayFontParam *)displayFonts->lookup(fontName);
+    if (!dfp) {
+      substFontName = (char *) findSubstituteName(fontName->getCString());
+      error(-1, "Couldn't find a font for '%s', subst is '%s'", fontName->getCString(), substFontName);
+      dfp = (DisplayFontParam *)displayFonts->lookup(substFontName);
+      if (!dfp) {
+        if (loadPlatformFont(substFontName))
+          dfp = (DisplayFontParam *)displayFonts->lookup(substFontName);
+      }
+    }
+  }
+  // this isn't supposed to fail, because the substitutes are system fonts
+  // that should always be available
+  assert(dfp);
+
+  unlockGlobalParams;
+  return dfp;
+}
diff --git a/poppler/Makefile.am b/poppler/Makefile.am
index 35be92d..b697bbe 100644
--- a/poppler/Makefile.am
+++ b/poppler/Makefile.am
@@ -272,5 +272,12 @@ libpoppler_la_SOURCES =		\
 	Sound.cc		\
 	XpdfPluginAPI.cc
 
+if ENABLE_MAC_FONTS
+
+libpoppler_la_SOURCES += \
+	GlobalParamsMac.cc
+
+endif
+
 EXTRA_DIST = gen-unicode-tables.py	\
              GlobalParamsWin.cc
-- 
1.5.4.3

