[Pkg-cups-devel] r817 - in cupsys/trunk/debian: . local/filters/pdf-filters local/filters/pdf-filters/conf local/filters/pdf-filters/data local/filters/pdf-filters/filter local/filters/pdf-filters/filter/fontembed

Till Kamppeter till-guest at alioth.debian.org
Thu Aug 14 17:18:53 UTC 2008


Author: till-guest
Date: Thu Aug 14 17:18:52 2008
New Revision: 817

Log:
Added texttopdf filter.


Added:
   cupsys/trunk/debian/local/filters/pdf-filters/conf/texttopdf.convs
   cupsys/trunk/debian/local/filters/pdf-filters/data/
   cupsys/trunk/debian/local/filters/pdf-filters/data/pdf.utf-8.heavy
   cupsys/trunk/debian/local/filters/pdf-filters/data/pdf.utf-8.simple
   cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/
   cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/Makefile
   cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/README
   cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/bitset.h
   cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/dynstring.c
   cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/dynstring.h
   cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/embed.c
   cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/embed.h
   cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/embed_sfnt.c
   cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/main.c
   cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/sfnt.c
   cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/sfnt.h
   cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/sfnt_int.h
   cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/test_analyze.c
   cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/test_pdf.c
   cupsys/trunk/debian/local/filters/pdf-filters/filter/pdfutils.c
   cupsys/trunk/debian/local/filters/pdf-filters/filter/pdfutils.h
   cupsys/trunk/debian/local/filters/pdf-filters/filter/test.sh   (contents, props changed)
   cupsys/trunk/debian/local/filters/pdf-filters/filter/test_pdf1.c
   cupsys/trunk/debian/local/filters/pdf-filters/filter/test_pdf2.c
   cupsys/trunk/debian/local/filters/pdf-filters/filter/texttopdf.c
Modified:
   cupsys/trunk/debian/changelog
   cupsys/trunk/debian/control
   cupsys/trunk/debian/local/filters/pdf-filters/AUTHORS
   cupsys/trunk/debian/local/filters/pdf-filters/README
   cupsys/trunk/debian/local/filters/pdf-filters/addtocups
   cupsys/trunk/debian/local/filters/pdf-filters/removefromcups
   cupsys/trunk/debian/rules

Modified: cupsys/trunk/debian/changelog
==============================================================================
--- cupsys/trunk/debian/changelog	(original)
+++ cupsys/trunk/debian/changelog	Thu Aug 14 17:18:52 2008
@@ -1,3 +1,27 @@
+cups (1.3.8-4) UNRELEASED; urgency=low
+
+  [ Till Kamppeter ]
+  * debian/control, debian/rules,
+    debian/local/filters/pdf-filters/filter/pdfutils.h,
+    debian/local/filters/pdf-filters/filter/texttopdf.c,
+    debian/local/filters/pdf-filters/filter/fontembed,
+    debian/local/filters/pdf-filters/filter/test.sh,
+    debian/local/filters/pdf-filters/filter/test_pdf1.c,
+    debian/local/filters/pdf-filters/filter/test_pdf2.c,
+    debian/local/filters/pdf-filters/filter/pdfutils.c,
+    debian/local/filters/pdf-filters/conf/texttopdf.convs,
+    debian/local/filters/pdf-filters/AUTHORS,
+    debian/local/filters/pdf-filters/addtocups,
+    debian/local/filters/pdf-filters/data,
+    debian/local/filters/pdf-filters/data/pdf.utf-8.simple,
+    debian/local/filters/pdf-filters/data/pdf.utf-8.heavy,
+    debian/local/filters/pdf-filters/removefromcups,
+    debian/local/filters/pdf-filters/README: Added texttopdf filter.
+    Added "Depends: ttf-freefont" for the cups package, as the
+    texttopdf filter needs these fonts.
+
+ -- Martin Pitt <mpitt at debian.org>  Thu, 14 Aug 2008 17:40:59 +0200
+
 cups (1.3.8-3) experimental; urgency=low
 
   [ Till Kamppeter ]

Modified: cupsys/trunk/debian/control
==============================================================================
--- cupsys/trunk/debian/control	(original)
+++ cupsys/trunk/debian/control	Thu Aug 14 17:18:52 2008
@@ -8,7 +8,8 @@
  debhelper (>= 5.0), po-debconf, cdbs (>= 0.4.27), sharutils, 
  dpatch (>= 1.11), libdbus-1-dev, libkrb5-dev | heimdal-dev,
  libavahi-compat-libdnssd-dev, libpoppler-dev, poppler-utils | xpdf-utils,
- lsb-release, po4a (>= 0.31), autotools-dev, autoconf, automake, libtool
+ lsb-release, po4a (>= 0.31), autotools-dev, autoconf, automake, libtool,
+ ttf-freefont
 Uploaders: Kenshi Muto <kmuto at debian.org>, 
  Martin Pitt <mpitt at debian.org>, Roger Leigh <rleigh at debian.org>, 
  Martin-Éric Racine <q-funk at iki.fi>, Masayuki Hatta (mhatta) <mhatta at debian.org>,
@@ -62,7 +63,7 @@
 Depends: ${shlibs:Depends}, debconf (>= 1.2.9) | debconf-2.0,
  poppler-utils | xpdf-utils, perl-modules, procps, 
  ghostscript, lsb-base (>= 3), cups-common,  
- ssl-cert (>= 1.0.11), adduser
+ ssl-cert (>= 1.0.11), adduser, ttf-freefont
 Recommends: cups-client, smbclient (>= 3.0.9), foomatic-filters, avahi-utils
 Suggests: cups-bsd, cups-driver-gutenprint, foomatic-db-engine, foomatic-db,
  hplip, xpdf-korean | xpdf-japanese | xpdf-chinese-traditional | xpdf-chinese-simplified, 

Modified: cupsys/trunk/debian/local/filters/pdf-filters/AUTHORS
==============================================================================
--- cupsys/trunk/debian/local/filters/pdf-filters/AUTHORS	(original)
+++ cupsys/trunk/debian/local/filters/pdf-filters/AUTHORS	Thu Aug 14 17:18:52 2008
@@ -1 +1,2 @@
 Koji Otani
+Tobias Hoffmann

Modified: cupsys/trunk/debian/local/filters/pdf-filters/README
==============================================================================
--- cupsys/trunk/debian/local/filters/pdf-filters/README	(original)
+++ cupsys/trunk/debian/local/filters/pdf-filters/README	Thu Aug 14 17:18:52 2008
@@ -26,8 +26,7 @@
 Below you find the original documentation of the three filters, from
 the original README files.
 
-The COPYING and AUTHORS files of all the three filters are the same.
-They are also included in the root directory of this tarball.
+The COPYING and AUTHORS files are merged from the original packages
 
 
 ===========================================================================
@@ -715,3 +714,48 @@
 "pdftoraster" creates temporally files if needed. Temporary files are created
 in the location specified by TMPDIR environment variable. Default location
 is "/tmp".
+
+===========================================================================
+
+TEXTTOPDF
+
+This implements a texttopdf filter, and is derived from cups' texttops.
+
+To configure:
+-------------
+
+- texttopdf uses a CUPS_DATADIR/charset/pdf.* (e.g. pdf.utf-8) for
+  font configuration. All the fonts named here MUST also be present
+  under CUPS_DATADIR/fonts/ as TrueType fonts for texttopdf to work.
+  For TrueType Collections (.TTC) you'll have to append '/' and the 
+  number of the font in the collection to the filename charsets/pdf.utf-8
+  (resp. charsets/pdf.* ), for example to use the second font of uming.ttc 
+  use the filename uming.ttc/1
+
+- There are examples of pdf.utf-8 in the cups/data directory,
+  you may use one of these (don't forget to copy / symlink the fonts).
+
+To use:
+-------
+
+The filter is called just like any other cups filter. Have a
+look at test.sh for example. 
+
+Known Issues
+------------
+(Release 0.0.1)
+
+ - text extraction does not work (at least for pdftotext from xpdf)
+   for the resulting pdfs.
+
+ - text wrapping in pretty-printing mode does not respect double-wide
+   characters (CJK), and thus produce wrong results (wrap too late)
+   for lines where they occur.  The fix is not trivial, since all the
+   pretty-printing processing is done without knowledge of / prior to
+   the font configuration (which is where single or double width
+   code-ranges are specified).
+
+ - The hebrew example in test5.pdf shows one of our limitations:
+   Compose glyphs are not composed with the primary glyph but printed
+   as separate glyphs.
+

Modified: cupsys/trunk/debian/local/filters/pdf-filters/addtocups
==============================================================================
--- cupsys/trunk/debian/local/filters/pdf-filters/addtocups	(original)
+++ cupsys/trunk/debian/local/filters/pdf-filters/addtocups	Thu Aug 14 17:18:52 2008
@@ -6,11 +6,20 @@
 # Copy files
 cp filter/imagetopdf.c $1/filter/
 cp filter/pdftoraster.cxx $1/filter/
+cp filter/texttopdf.c $1/filter/
+cp filter/pdfutils.h $1/filter/
+cp filter/pdfutils.c $1/filter/
+cp -r filter/fontembed $1/filter/
+cp filter/test_pdf1.c $1/filter/
+cp filter/test_pdf2.c $1/filter/
 cp conf/imagetopdf.convs $1/conf/
 cp conf/imagetopdf.types $1/conf/
 cp conf/pdftopdf.convs $1/conf/
 cp conf/pdftoraster.convs $1/conf/
 cp conf/pdf.types $1/conf/
+cp conf/texttopdf.convs $1/conf/
+cp data/pdf.utf-8.heavy $1/data/
+cp data/pdf.utf-8.simple $1/data/
 cp config-scripts/cups-pdf-filters.m4 $1/config-scripts/
 
 # Copy directories
@@ -70,11 +79,13 @@
 # Edit Makefiles
 cp conf/Makefile conf/Makefile.pdf-filters
 cp filter/Makefile filter/Makefile.pdf-filters
+cp data/Makefile data/Makefile.pdf-filters
 cp Makefile Makefile.pdf-filters
 cp Makedefs.in Makedefs.in.pdf-filters
-perl -p -i -e 's/^(\s*REPLACE\s*=.*)$/$1 imagetopdf.convs imagetopdf.types pdftopdf.convs pdftoraster.convs pdf.types/' conf/Makefile
-perl -p -i -e 's/^(\s*FILTERS\s*=\s+)/$1imagetopdf pdftoraster /' filter/Makefile
-perl -p -i -e 's/^(\s*OBJS\s*=\s+)/$1imagetopdf.o pdftoraster.o /' filter/Makefile
+perl -p -i -e 's/^(\s*REPLACE\s*=.*)$/$1 imagetopdf.convs imagetopdf.types pdftopdf.convs pdftoraster.convs pdf.types texttopdf.convs/' conf/Makefile
+perl -p -i -e 's/^(\s*FILTERS\s*=\s+)/$1imagetopdf pdftoraster texttopdf /' filter/Makefile
+perl -p -i -e 's/^(\s*OBJS\s*=\s+)/$1imagetopdf.o pdftoraster.o texttopdf.o pdfutils.o /' filter/Makefile
+perl -p -i -e 's/^(\s*CHARSETS\s*=\s+)/$1pdf.utf-8.heavy pdf.utf-8.simple /' data/Makefile
 cat >> filter/Makefile <<EOF
 
 #
@@ -98,6 +109,19 @@
 	\$(CC) \$(LDFLAGS) -o \$@ pdftoraster.o common.o \$(LINKCUPSIMAGE) \\
 		\$(POPPLER_LIBS)
 
+
+#
+# texttopdf
+#
+
+fontembed/libfontembed.a:
+	\$(MAKE) -C fontembed
+
+texttopdf:	texttopdf.o textcommon.o common.o pdfutils.o fontembed/libfontembed.a \\
+		../cups/\$(LIBCUPS)
+	echo Linking \$@...
+	\$(CC) \$(LDFLAGS) -o \$@ texttopdf.o textcommon.o common.o pdfutils.o -Lfontembed -lfontembed \$(LIBS)
+
 EOF
 perl -p -i -e 's/^(\s*DIRS\s*=.*\s+filter\s+)/$1pdftopdf /' Makefile
 perl -p -i -e 's/^(\s*LIBS\s*=.*$)/$1\nPOPPLER_LIBS\t=\t\@POPPLER_LIBS\@ \$(LIBS)/' Makedefs.in

Added: cupsys/trunk/debian/local/filters/pdf-filters/conf/texttopdf.convs
==============================================================================
--- (empty file)
+++ cupsys/trunk/debian/local/filters/pdf-filters/conf/texttopdf.convs	Thu Aug 14 17:18:52 2008
@@ -0,0 +1,6 @@
+application/x-cshell	application/pdf		0	texttopdf
+application/x-csource	application/pdf		0	texttopdf
+application/x-perl	application/pdf		0	texttopdf
+application/x-shell	application/pdf		0	texttopdf
+text/plain		application/pdf		0	texttopdf
+text/html		application/pdf		0	texttopdf

Added: cupsys/trunk/debian/local/filters/pdf-filters/data/pdf.utf-8.heavy
==============================================================================
--- (empty file)
+++ cupsys/trunk/debian/local/filters/pdf-filters/data/pdf.utf-8.heavy	Thu Aug 14 17:18:52 2008
@@ -0,0 +1,40 @@
+charset utf8
+
+#
+# This file defines the font mappings used for Unicode/UTF-8 text printing
+# through PDF.
+#
+# Each line consists of:
+#
+#   first last direction width normal bold italic bold-italic
+#
+# First and last are the first and last glyphs in the font mapping
+# that correspond to that font; contrary to PostScript printing
+# they only select the font. To find the glyph the complete unicode
+# character will be looked up in the (3,1) resp. (3,0) cmap of the 
+# TrueType font. The glyph values are hexadecimal.
+#
+# Direction is the string "ltor" or "rtol", indicating left-to-right or
+# right-to-left text.
+#
+# Width is the string "single" or "double"; double means that the glyphs
+# are twice as wide as ASCII characters in the Courier typeface.
+#
+# "Normal", "bold", "italic", and "bold-italic" are the typefaces to use
+# for each presentation.  If characters are only available in a single
+# style then only one typeface should be listed (e.g. "Symbol")
+#
+# Each font that is listed will be used (and downloaded if needed) when
+# printing.
+#
+
+0000 00FF ltor single COUR.TTF COURBD.TTF COURI.TTF COURBI.TTF
+0100 02FF ltor single DejaVuSansMono.ttf DejaVuSansMono-Bold.ttf DejaVuSansMono-Oblique.ttf DejaVuSansMono-BoldOblique.ttf
+0300 03FF ltor single DejaVuSansMono.ttf
+0400 04FF ltor single DejaVuSansMono.ttf DejaVuSansMono-Bold.ttf DejaVuSansMono-Oblique.ttf DejaVuSansMono-BoldOblique.ttf
+0500 05FF rtol single FreeMono.ttf
+1E00 1EFF ltor single COUR.TTF COURBD.TTF COURI.TTF COURBI.TTF
+2000 21FF ltor single DejaVuSansMono.ttf DejaVuSansMono-Bold.ttf DejaVuSansMono-Oblique.ttf DejaVuSansMono-BoldOblique.ttf
+2200 23FF ltor single SYMBOL.TTF
+3000 9FFF ltor double uming.ttc/0
+#0400 04FF ltor single FreeMono.ttf FreeMonoBold.ttf FreeMonoOblique.ttf FreeMonoBoldOblique.ttf

Added: cupsys/trunk/debian/local/filters/pdf-filters/data/pdf.utf-8.simple
==============================================================================
--- (empty file)
+++ cupsys/trunk/debian/local/filters/pdf-filters/data/pdf.utf-8.simple	Thu Aug 14 17:18:52 2008
@@ -0,0 +1,32 @@
+charset utf8
+
+#
+# This file defines the font mappings used for Unicode/UTF-8 text printing
+# through PDF.
+#
+# Each line consists of:
+#
+#   first last direction width normal bold italic bold-italic
+#
+# First and last are the first and last glyphs in the font mapping
+# that correspond to that font; contrary to PostScript printing
+# they only select the font. To find the glyph the complete unicode
+# character will be looked up in the (3,1) resp. (3,0) cmap of the 
+# TrueType font. The glyph values are hexadecimal.
+#
+# Direction is the string "ltor" or "rtol", indicating left-to-right or
+# right-to-left text.
+#
+# Width is the string "single" or "double"; double means that the glyphs
+# are twice as wide as ASCII characters in the Courier typeface.
+#
+# "Normal", "bold", "italic", and "bold-italic" are the typefaces to use
+# for each presentation.  If characters are only available in a single
+# style then only one typeface should be listed (e.g. "Symbol")
+#
+# Each font that is listed will be used (and downloaded if needed) when
+# printing.
+#
+
+0000 04FF ltor single FreeMono.ttf FreeMonoBold.ttf FreeMonoOblique.ttf FreeMonoBoldOblique.ttf
+0500 05FF rtol single FreeMono.ttf

Added: cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/Makefile
==============================================================================
--- (empty file)
+++ cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/Makefile	Thu Aug 14 17:18:52 2008
@@ -0,0 +1,47 @@
+SOURCES=main.c sfnt.c embed.c embed_sfnt.c test_analyse.c dynstring.c test_pdf.c
+LIBOBJ=sfnt.o embed.o embed_sfnt.o dynstring.o
+EXEC=ttfread test_analyze test_pdf
+LIB=libfontembed.a
+
+#CPPFLAGS=-O3 -march=pentium -funroll-all-loops -finline-functions -Wall -g
+CPPFLAGS=-march=pentium -Wall -g
+LDFLAGS=
+
+OBJECTS=$(patsubst %.c,$(PREFIX)%$(SUFFIX).o,\
+        $(patsubst %.cpp,$(PREFIX)%$(SUFFIX).o,\
+$(SOURCES)))
+DEPENDS=$(patsubst %.c,$(PREFIX)%$(SUFFIX).d,\
+        $(patsubst %.cpp,$(PREFIX)%$(SUFFIX).d,\
+        $(filter-out %.o,""\
+$(SOURCES))))
+
+all: $(LIB)
+test: $(EXEC)
+ifneq "$(MAKECMDGOALS)" "clean"
+  -include $(DEPENDS)
+endif 
+
+clean:
+	rm -f $(EXEC) $(LIB) $(OBJECTS) $(DEPENDS) 
+
+%.d: %.c
+	@$(SHELL) -ec '$(CXX) -MM $(CPPFLAGS) $< \
+                      | sed '\''s/\($*\)\.o[ :]*/\1.o $@ : /g'\'' > $@;\
+                      [ -s $@ ] || rm -f $@'
+
+%.d: %.cpp
+	@$(SHELL) -ec '$(CXX) -MM $(CPPFLAGS) $< \
+                      | sed '\''s/\($*\)\.o[ :]*/\1.o $@ : /g'\'' > $@;\
+                      [ -s $@ ] || rm -f $@'
+
+$(LIB): $(LIBOBJ)
+	$(AR) rcu $@ $^ 
+
+ttfread: main.o $(LIB)
+	$(CXX) -o $@ $^ $(LDFLAGS)
+
+test_analyze: test_analyze.o $(LIB)
+	$(CXX) -o $@ $^ $(LDFLAGS)
+
+test_pdf: test_pdf.o $(LIB)
+	$(CXX) -o $@ $^ $(LDFLAGS)

Added: cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/README
==============================================================================
--- (empty file)
+++ cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/README	Thu Aug 14 17:18:52 2008
@@ -0,0 +1,40 @@
+libfontembed - font embedding and subsetting library
+----------------------------------------------------
+
+This library implements all the stuff required to
+embed and subset TrueType fonts, as for example 
+required in PDF files. It is completely self-contained,
+although a FreeType binding might come sometime in the future.
+
+Currently only glyf-flavored TrueType is supported,
+but OTF, i.e. CFF-flavored TrueType/OpenType is on the way.
+Also reencoding and conversion of Type1 to CFF is planned.
+PostScript embedding is another goal of the project.
+
+Usage
+-----
+(TODO)... see test_pdf.c ...
+
+
+License (MIT)
+-------------
+Copyright (c) 2008 by Tobias Hoffmann.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Added: cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/bitset.h
==============================================================================
--- (empty file)
+++ cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/bitset.h	Thu Aug 14 17:18:52 2008
@@ -0,0 +1,24 @@
+#ifndef _BITSET_H
+#define _BITSET_H
+
+#include <stdlib.h>
+
+typedef int * BITSET;
+
+static inline void bit_set(BITSET bs,int num)
+{
+  bs[num/(8*sizeof(int))]|=1<<(num%(8*sizeof(int)));
+}
+
+static inline int bit_check(BITSET bs,int num)
+{
+  return bs[num/(8*sizeof(int))]&1<<(num%(8*sizeof(int)));
+}
+
+// use free() when done. returns NULL on bad_alloc
+static inline BITSET bitset_new(int size)
+{
+  return (BITSET)calloc(1,((size+31)&~31)/8);
+}
+
+#endif

Added: cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/dynstring.c
==============================================================================
--- (empty file)
+++ cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/dynstring.c	Thu Aug 14 17:18:52 2008
@@ -0,0 +1,104 @@
+#include "dynstring.h"
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+int dyn_init(DYN_STRING *ds,int reserve_size) // {{{
+{
+  assert(ds);
+  assert(reserve_size>0);
+
+  ds->len=0;
+  ds->alloc=reserve_size;
+  ds->buf=malloc(ds->alloc+1);
+  if (!ds->buf) {
+    fprintf(stderr,"Bad alloc: %m\n");
+    assert(0);
+    ds->len=-1;
+    return -1;
+  }
+  return 0;
+}
+// }}}
+
+void dyn_free(DYN_STRING *ds) // {{{
+{
+  assert(ds);
+
+  ds->len=-1;
+  ds->alloc=0;
+  free(ds->buf);
+  ds->buf=NULL;
+}
+// }}}
+
+int dyn_ensure(DYN_STRING *ds,int free_space) // {{{
+{
+  assert(ds);
+  assert(free_space);
+
+  if (ds->len<0) {
+    return -1;
+  }
+  if (ds->alloc - ds->len >= free_space) {
+    return 0; // done
+  }
+  ds->alloc+=free_space;
+  char *tmp=realloc(ds->buf,ds->alloc+1);
+  if (!tmp) {
+    ds->len=-1;
+    fprintf(stderr,"Bad alloc: %m\n");
+    assert(0);
+    return -1;
+  }
+  ds->buf=tmp;
+  return 0;
+}
+// }}}
+
+inline int dyn_vprintf(DYN_STRING *ds,const char *fmt,va_list ap) // {{{
+{
+  assert(ds);
+
+  int need,len=strlen(fmt)+100;
+  va_list va;
+
+  if (dyn_ensure(ds,len)==-1) {
+    return -1;
+  }
+
+  while (1) {
+    va_copy(va,ap);
+    need=vsnprintf(ds->buf+ds->len,ds->alloc-ds->len+1,fmt,va);
+    va_end(va);
+    if (need==-1) {
+      len+=100;
+    } else if (need>=len) {
+      len=need;
+    } else {
+      ds->len+=need;
+      break;
+    }
+    if (dyn_ensure(ds,len)==-1) {
+      return -1;
+    }
+  }
+  return 0;
+}
+// }}}
+
+int dyn_printf(DYN_STRING *ds,const char *fmt,...) // {{{
+{
+  va_list va;
+  int ret;
+
+  va_start(va,fmt);
+  ret=dyn_vprintf(ds,fmt,va);
+  va_end(va);
+ 
+  return ret;
+}
+// }}}
+

Added: cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/dynstring.h
==============================================================================
--- (empty file)
+++ cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/dynstring.h	Thu Aug 14 17:18:52 2008
@@ -0,0 +1,15 @@
+#ifndef _DYNSTRING_H
+#define _DYNSTRING_H
+
+typedef struct {
+  int len,alloc;
+  char *buf;
+} DYN_STRING;
+
+int dyn_init(DYN_STRING *ds,int reserve_size); // -1 on error
+void dyn_free(DYN_STRING *ds);
+int dyn_ensure(DYN_STRING *ds,int free_space);
+int dyn_printf(DYN_STRING *ds,const char *fmt,...); // appends
+
+#endif
+

Added: cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/embed.c
==============================================================================
--- (empty file)
+++ cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/embed.c	Thu Aug 14 17:18:52 2008
@@ -0,0 +1,602 @@
+#include "embed.h"
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "dynstring.h"
+
+#include "sfnt.h"
+
+// from embed_sfnt.c
+EMB_RIGHT_TYPE emb_otf_get_rights(OTF_FILE *otf);
+const char *emb_otf_get_fontname(OTF_FILE *otf);
+EMB_PDF_FONTWIDTHS *emb_otf_get_pdf_widths(OTF_FILE *otf,const unsigned short *encoding,int len,const BITSET glyphs);
+EMB_PDF_FONTWIDTHS *emb_otf_get_pdf_cidwidths(OTF_FILE *otf,const BITSET glyph);
+
+void emb_otf_get_pdf_fontdescr(OTF_FILE *otf,EMB_PDF_FONTDESCR *ret);
+
+struct _FONTFILE {
+  OTF_FILE *sfnt;
+  // ??? *cff;
+};
+void fontfile_close(FONTFILE *ff) 
+{
+  if (ff) {
+    otf_close(ff->sfnt);
+    // ??? cff_close(ff->cff);
+    free(ff);
+  }
+}
+
+static inline int copy_file(FILE *f,OUTPUT_FN output,void *context) // {{{
+{
+  assert(f);
+  assert(output);
+
+  char buf[4096];
+  int iA,ret=0;
+
+  ret=0;
+  rewind(f);
+  do {
+    iA=fread(buf,1,4096,f);
+    (*output)(buf,iA,context);
+    ret+=iA;
+  } while (iA>0);
+  return ret;
+}
+// }}}
+
+EMB_PARAMS *emb_new(FONTFILE *font,EMB_DESTINATION dest,EMB_CONSTRAINTS mode) // {{{
+{
+  assert(font);
+
+  EMB_PARAMS *ret=calloc(1,sizeof(EMB_PARAMS));
+  if (!ret) {
+    fprintf(stderr,"Bad alloc: %m\n");
+    if (mode&EMB_C_TAKE_FONTFILE) {
+      fontfile_close(font);
+    }
+    return NULL;
+  }
+  ret->dest=dest;
+  ret->font=font;
+  if (mode&EMB_C_TAKE_FONTFILE) {
+    ret->plan|=EMB_A_CLOSE_FONTFILE;
+  }
+
+  // check parameters
+  if ( (mode&EMB_C_KEEP_T1)&&(mode&EMB_C_FORCE_MULTIBYTE) ) {
+    fprintf(stderr,"Incompatible mode: KEEP_T1 and FORCE_MULTIBYTE\n");
+    emb_close(ret);
+    return NULL;
+  }
+  if ((mode&0x07)>5) {
+    fprintf(stderr,"Bad subset specification\n");
+    emb_close(ret);
+    return NULL;
+  }
+
+  // determine intype
+  // if (font->
+  ret->intype=EMB_INPUT_TTF; // for now
+  ret->rights=emb_otf_get_rights(ret->font->sfnt);
+  const int numGlyphs=ret->font->sfnt->numGlyphs; // TODO
+/*
+  if ( (ret->intype==EMB_INPUT_CFF)&&
+       (ret->cffFont.is_cid()) ) {
+    ret->plan|=EMB_A_MULTIBYTE;
+  }
+*/
+
+  // determine outtype
+  if ( (ret->intype==EMB_INPUT_T1)&&(mode&EMB_C_KEEP_T1) ) {
+    ret->outtype=EMB_OUTPUT_T1;
+  } else if (ret->intype==EMB_INPUT_TTF) {
+    if (mode&EMB_C_PDF_OT) {
+      ret->outtype=EMB_OUTPUT_SFNT;
+    } else {
+      ret->outtype=EMB_OUTPUT_TTF;
+    }
+  } else { // T1, OTF, CFF
+    if (ret->intype==EMB_INPUT_T1) {
+      ret->plan|=EMB_A_CONVERT_CFF;
+    }
+    if (mode&EMB_C_PDF_OT) {
+      ret->outtype=EMB_OUTPUT_SFNT;
+      ret->plan|=EMB_A_WRAP_SFNT;
+    } else {
+      ret->outtype=EMB_OUTPUT_CFF;
+    }
+  }
+
+  if (mode&EMB_C_FORCE_MULTIBYTE) {
+    ret->plan|=EMB_A_MULTIBYTE;
+  }
+
+  // check rights
+  if (  (ret->rights&EMB_RIGHT_NONE)||
+        (ret->rights&EMB_RIGHT_BITMAPONLY)||
+        ( (ret->rights&EMB_RIGHT_READONLY)&&(mode&EMB_C_EDITABLE_SUBSET) )||
+        ( (ret->rights&EMB_RIGHT_NO_SUBSET)&&(mode&EMB_C_MUST_SUBSET) )  ) {
+    fprintf(stderr,"The font does not permit the requested embedding\n");
+    emb_close(ret);
+    return NULL;
+  } else if ( (!(ret->rights&EMB_RIGHT_NO_SUBSET))&&
+              (!(mode&EMB_C_NEVER_SUBSET)) ) {
+    ret->plan|=EMB_A_SUBSET;
+  }
+
+  // alloc subset
+  if (ret->plan&EMB_A_SUBSET) {
+    ret->subset=bitset_new(numGlyphs);
+    if (!ret->subset) {
+      fprintf(stderr,"Bad alloc: %m\n");
+      emb_close(ret);
+      return NULL;
+    }
+  }
+
+  return ret;
+}
+// }}}
+
+int emb_embed(EMB_PARAMS *emb,OUTPUT_FN output,void *context) // {{{
+{
+  assert(emb);
+
+  if (emb->intype==EMB_INPUT_TTF) {
+    assert(emb->font->sfnt);
+    if (emb->plan&EMB_A_SUBSET) {
+      return otf_subset(emb->font->sfnt,emb->subset,output,context);
+    } else if (emb->font->sfnt->numTTC) { // 
+      return otf_ttc_extract(emb->font->sfnt,output,context);
+    } else {
+      // copy verbatim
+      return copy_file(emb->font->sfnt->f,output,context);
+    }
+  } else {
+    fprintf(stderr,"NOT IMPLEMENTED\n");
+    assert(0);
+    return -1;
+  }
+}
+// }}}
+
+void emb_close(EMB_PARAMS *emb) // {{{
+{
+  if (emb) {
+    free(emb->subset);
+    if (emb->plan&EMB_A_CLOSE_FONTFILE) {
+      fontfile_close(emb->font);
+    }
+    free(emb);
+  }
+}
+// }}}
+
+/*** PDF out stuff ***/
+static const int emb_pdf_font_format[]={0,1,0,0}; // {{{ (input_format) }}}
+
+static const char *emb_pdf_font_subtype[][2]={ // {{{ (format,multibyte)
+        {"Type1","CIDFontType0"},
+        {"TrueType","CIDFontType2"}};
+// }}}
+
+static const char *emb_pdf_fontfile_key[]={ // {{{ (output_format)
+        "FontFile1","FontFile2","FontFile3","Fontfile3"};
+// }}}
+
+static const char *emb_pdf_fontfile_subtype[][2]={ // {{{ (output_format,multibyte)
+        {NULL,NULL},
+        {NULL,NULL},
+        {"Type1C","CIDFontType0C"},
+        {"OpenType","OpenType"}};
+// }}}
+
+static inline int emb_multibyte(EMB_PARAMS *emb) // {{{
+{
+  return (emb->plan&EMB_A_MULTIBYTE)?1:0;
+}
+// }}}
+
+static const char *emb_pdf_escape_name(const char *name,int len) // {{{ // - statically allocated buffer
+{
+  assert(name);
+  if (len==-1) {
+    len=strlen(name);
+  }
+  assert(len<=127); // pdf implementation limit
+
+  static char buf[128*3];
+  int iA,iB;
+  const char hex[]="0123456789abcdef";
+
+  for (iA=0,iB=0;iA<len;iA++,iB++) {
+    if ( ((unsigned char)name[iA]<33)||((unsigned char)name[iA]>126)||
+         (strchr("#()<>[]{}/%",name[iA])) ) {
+      buf[iB]='#';
+      buf[++iB]=hex[(name[iA]>>4)&0x0f];
+      buf[++iB]=hex[name[iA]&0xf];
+    } else {
+      buf[iB]=name[iA];
+    }
+  }
+  buf[iB]=0;
+  return buf;
+}
+// }}}
+
+const char *emb_pdf_get_font_subtype(EMB_PARAMS *emb) // {{{
+{
+  assert(emb);
+  return emb_pdf_font_subtype[emb_pdf_font_format[emb->intype]][emb_multibyte(emb)];
+}
+// }}}
+
+const char *emb_pdf_get_fontfile_key(EMB_PARAMS *emb) // {{{
+{
+  assert(emb);
+  return emb_pdf_fontfile_key[emb->outtype];
+}
+// }}}
+
+const char *emb_pdf_get_fontfile_subtype(EMB_PARAMS *emb) // {{{
+{
+  assert(emb);
+  return emb_pdf_fontfile_subtype[emb->outtype][emb_multibyte(emb)];
+}
+// }}}
+
+// {{{ EMB_PDF_FONTDESCR *emb_pdf_fd_new(fontname,subset_tag,cid_registry,cid_ordering,cid_supplement,panose)
+EMB_PDF_FONTDESCR *emb_pdf_fd_new(const char *fontname,
+                                  const char *subset_tag,
+                                  const char *cid_registry, // or supplement==-1
+                                  const char *cid_ordering, // or supplement==-1
+                                  int cid_supplement) // -1 for non-cid
+{
+  assert(fontname);
+  EMB_PDF_FONTDESCR *ret;
+
+  int len=sizeof(EMB_PDF_FONTDESCR);
+  if (subset_tag) {
+    assert(strlen(subset_tag)==6);
+    len+=7;
+  }
+  len+=strlen(fontname)+1;
+  if (cid_supplement>=0) { // cid font
+    len+=12; // space for panose
+    assert(cid_registry);
+    assert(cid_ordering);
+    len+=strlen(cid_registry)+1;
+    len+=strlen(cid_ordering)+1;
+  }
+  ret=calloc(1,len);
+  if (!ret) {
+    fprintf(stderr,"Bad alloc: %m\n");
+    assert(0);
+    return NULL;
+  }
+
+  // now fill the struct
+  len=0;
+  if (cid_supplement>=0) { // free space for panose is at beginning
+    len+=12;
+  }
+  ret->fontname=ret->data+len;
+  len+=strlen(fontname)+1;
+  if (subset_tag) {
+    strncpy(ret->fontname,subset_tag,6);
+    ret->fontname[6]='+';
+    strcpy(ret->fontname+7,fontname);
+    len+=7;
+  } else {
+    strcpy(ret->fontname,fontname);
+  }
+  ret->italicAngle=90;
+  if (cid_supplement>=0) {
+    ret->registry=ret->data+len;
+    strcpy(ret->registry,cid_registry);
+    len+=strlen(cid_registry)+1;
+
+    ret->ordering=ret->data+len;
+    strcpy(ret->ordering,cid_ordering);
+    len+=strlen(cid_registry)+1;
+  }
+  ret->supplement=cid_supplement;
+
+  return ret;
+}
+// }}}
+
+EMB_PDF_FONTDESCR *emb_pdf_fontdescr(EMB_PARAMS *emb) // {{{ -  to be freed by user
+{
+  assert(emb);
+
+  const char *subset_tag=NULL;
+  // {{{ generate pdf subtag
+  static unsigned int rands=0;
+  if (!rands) {
+    rands=time(NULL);
+  }
+  
+  char subtag[7];
+  subtag[6]=0;
+  if (emb->plan&EMB_A_SUBSET) {
+    int iA;
+    for (iA=0;iA<6;iA++) {
+      const int x=(int)(26.0*(rand_r(&rands)/(RAND_MAX+1.0)));
+      subtag[iA]='A'+x;
+    }
+    subset_tag=subtag;
+  }
+  // }}}
+
+  const char *fontname=NULL;
+  if (emb->intype==EMB_INPUT_TTF) {
+    assert(emb->font->sfnt);
+    fontname=emb_otf_get_fontname(emb->font->sfnt);
+  } else {
+    fprintf(stderr,"NOT IMPLEMENTED\n");
+    assert(0);
+    return NULL;
+  }
+
+  EMB_PDF_FONTDESCR *ret;
+  if (emb->plan&EMB_A_MULTIBYTE) { // multibyte
+    ret=emb_pdf_fd_new(fontname,subset_tag,"Adobe","Identity",0); // TODO /ROS
+  } else {
+    ret=emb_pdf_fd_new(fontname,subset_tag,NULL,NULL,-1);
+  }
+  if (!ret) {
+    return NULL;
+  }
+
+  if (emb->intype==EMB_INPUT_TTF) {
+    emb_otf_get_pdf_fontdescr(emb->font->sfnt,ret);
+  } else {
+    assert(0); 
+  }
+  return ret;
+}
+// }}}
+
+// TODO: encoding into EMB_PARAMS
+EMB_PDF_FONTWIDTHS *emb_pdf_fw_new(int datasize); 
+
+EMB_PDF_FONTWIDTHS *emb_pdf_fw_new(int datasize) // {{{
+{
+  assert(datasize>=0);
+  EMB_PDF_FONTWIDTHS *ret=calloc(1,sizeof(EMB_PDF_FONTWIDTHS)+datasize*sizeof(int));
+  if (!ret) {
+    fprintf(stderr,"Bad alloc: %m\n");
+    assert(0);
+    return NULL;
+  }
+  return ret;
+}
+// }}}
+
+EMB_PDF_FONTWIDTHS *emb_pdf_fontwidths(EMB_PARAMS *emb) // {{{
+{
+  assert(emb);
+
+  if (emb->intype==EMB_INPUT_TTF) {
+    assert(emb->font->sfnt);
+    if (emb->plan&EMB_A_MULTIBYTE) {
+      return emb_otf_get_pdf_cidwidths(emb->font->sfnt,emb->subset);
+    } else {
+      return emb_otf_get_pdf_widths(emb->font->sfnt,NULL,emb->font->sfnt->numGlyphs,emb->subset); // TODO: encoding
+    }
+  } else {
+    fprintf(stderr,"NOT IMPLEMENTED\n");
+    assert(0);
+    return NULL;
+  }
+}
+// }}}
+
+#define NEXT /* {{{ */ \
+  if ( (len<0)||(len>=size) ) { \
+    assert(0); \
+    free(ret); \
+    return NULL; \
+  } \
+  pos+=len; \
+  size-=len; /* }}} */ 
+
+char *emb_pdf_simple_fontdescr(EMB_PARAMS *emb,EMB_PDF_FONTDESCR *fdes,int fontfile_obj_ref) // {{{ - to be freed by user
+{
+  assert(emb);
+  assert(fdes);
+
+  char *ret=NULL,*pos;
+  int len,size;
+
+  size=300;
+  pos=ret=malloc(size);
+  if (!ret) {
+    fprintf(stderr,"Bad alloc: %m\n");
+    return NULL;
+  }
+
+  len=snprintf(pos,size,
+               "<</Type /FontDescriptor\n"
+               "  /FontName /%s\n" // TODO? handle quoting in struct?
+               "  /Flags %d\n"
+               "  /ItalicAngle %d\n",
+               emb_pdf_escape_name(fdes->fontname,-1),
+               fdes->flags,
+               fdes->italicAngle);
+  NEXT;
+
+  if (1) { // TODO type!=EMB_PDF_TYPE3
+    len=snprintf(pos,size,
+                 "  /FontBBox [%d %d %d %d]\n"
+                 "  /Ascend %d\n"
+                 "  /Descend %d\n"
+                 "  /CapHeight %d\n" // if font has Latin chars
+                 "  /StemV %d\n",
+                 fdes->bbxmin,fdes->bbymin,fdes->bbxmax,fdes->bbymax,
+                 fdes->ascend,
+                 fdes->descend,
+                 fdes->capHeight,
+                 fdes->stemV);
+    NEXT;
+  }
+  if (fdes->xHeight) {
+    len=snprintf(pos,size,"  /XHeight %d\n",fdes->xHeight);
+    NEXT;
+  }
+  if (fdes->avgWidth) {
+    len=snprintf(pos,size,"  /AvgWidth %d\n",fdes->avgWidth);
+    NEXT;
+  }
+  if (fdes->panose) {
+    int iA;
+    len=snprintf(pos,size,"  /Style << /Panose <");
+    NEXT;
+    if (size<30) {
+      assert(0);
+      free(ret);
+      return NULL;
+    }
+    for (iA=0;iA<12;iA++) {
+      snprintf(pos+iA*2,size-iA*2,"%02x",fdes->panose[iA]);
+    }
+    size-=24;
+    pos+=24;
+    len=snprintf(pos,size,"> >>\n");
+    NEXT;
+  }
+  len=snprintf(pos,size,
+               "  /%s %d 0 R\n"
+               ">>\n",
+               emb_pdf_get_fontfile_key(emb),
+               fontfile_obj_ref);
+  NEXT;
+
+  return ret;
+}
+// }}}
+
+char *emb_pdf_simple_font(EMB_PARAMS *emb,EMB_PDF_FONTDESCR *fdes,EMB_PDF_FONTWIDTHS *fwid,int fontdescr_obj_ref) // {{{ - to be freed by user
+{
+  assert(emb);
+  assert(fdes);
+  assert(fwid);
+
+  int iA,iB;
+  DYN_STRING ret;
+
+  if (dyn_init(&ret,500)==-1) {
+    return NULL;
+  }
+
+  dyn_printf(&ret,"<</Type /Font\n"
+                  "  /Subtype /%s\n"
+                  "  /BaseFont /%s\n"
+                  "  /FontDescriptor %d 0 R\n",
+                  emb_pdf_get_font_subtype(emb),
+                  emb_pdf_escape_name(fdes->fontname,-1),
+                  fontdescr_obj_ref);
+
+  if (emb->plan&EMB_A_MULTIBYTE) { // multibyte
+    assert(fwid->warray);
+    dyn_printf(&ret,"  /CIDSystemInfo <<\n"
+                    "    /Registry (%s)\n"
+                    "    /Ordering (%s)\n"
+                    "    /Supplement %d\n"
+                    "  >>\n"
+                    "  /DW %d\n",
+//                    "  /CIDToGIDMap /Id...\n" // TrueType only, default /Identity
+                    fdes->registry,
+                    fdes->ordering,
+                    fdes->supplement,
+                    fwid->default_width);
+
+    if (fwid->warray[0]) {
+      dyn_printf(&ret,"  /W [");
+      for (iA=0;fwid->warray[iA];) {
+        if (fwid->warray[iA]<0) { // c1 (c1-len) w
+          dyn_printf(&ret," %d %d %d",
+                          fwid->warray[iA+1],
+                          fwid->warray[iA+1]-fwid->warray[iA],
+                          fwid->warray[iA+2]);
+          iA+=3;
+        } else { // c [w ... w]
+          iB=fwid->warray[iA++]; // len
+          dyn_printf(&ret," %d [",fwid->warray[iA++]); // c
+          for (;iB>0;iB--) {
+            dyn_printf(&ret," %d",fwid->warray[iA++]);
+          }
+          dyn_printf(&ret,"]");
+        }
+      }
+      dyn_printf(&ret,"]\n");
+    }
+  } else { // "not std14"
+    assert(fwid->widths);
+    dyn_printf(&ret,
+                    "  /Encoding /MacRomanEncoding\n"  // optional; TODO!!!!!
+//                    "  /ToUnicode ?\n"  // optional
+                    "  /FirstChar %d\n"
+                    "  /LastChar %d\n"
+                    "  /Widths [",
+                    fwid->first,
+                    fwid->last);
+    for (iA=0,iB=fwid->first;iB<=fwid->last;iA++,iB++) {
+      dyn_printf(&ret," %d",fwid->widths[iA]);
+    }
+    dyn_printf(&ret,"]\n");
+  }
+  dyn_printf(&ret,">>\n");
+  if (ret.len==-1) {
+    dyn_free(&ret);
+    assert(0);
+    return NULL;
+  }
+
+  return ret.buf;
+}
+// }}}
+
+char *emb_pdf_simple_cidfont(EMB_PARAMS *emb,const char *fontname,int descendant_obj_ref) // {{{ - to be freed by user
+{
+  assert(emb);
+  assert(fontname);
+
+  char *ret=NULL,*pos;
+  int len,size;
+
+  size=200;
+  pos=ret=malloc(size);
+  if (!ret) {
+    fprintf(stderr,"Bad alloc: %m\n");
+    return NULL;
+  }
+  
+  len=snprintf(pos,size,
+               "<</Type /Font\n"
+               "  /Subtype /Type0\n"
+               "  /BaseFont /%s\n"  // TODO? "-CMap-name"(/Encoding) for CidType0
+               "  /Encoding /Identity-H\n"
+         // for CFF: one of:
+         // UniGB-UCS2-H, UniCNS-UCS2-H, UniJIS-UCS2-H, UniKS-UCS2-H
+               "  /DescendantFonts [%d 0 R]\n",
+//               "  /ToUnicode ?\n" // TODO
+               emb_pdf_escape_name(fontname,-1),
+               descendant_obj_ref);
+  NEXT;
+
+  len=snprintf(pos,size,">>\n");
+  NEXT;
+
+  return ret;
+}
+// }}}
+#undef NEXT
+
+//...
+

Added: cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/embed.h
==============================================================================
--- (empty file)
+++ cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/embed.h	Thu Aug 14 17:18:52 2008
@@ -0,0 +1,118 @@
+#ifndef EMBED_H
+#define EMBED_H
+
+#include "bitset.h"
+
+typedef enum { EMB_INPUT_T1,     // type1-lib, with AFM/PFM,PFA/PFB
+               EMB_INPUT_TTF,    // sfnt-lib, for TTF(glyf)
+               EMB_INPUT_OTF,    // sfnt-lib + cff-lib, for OTF
+               EMB_INPUT_CFF     // cff-lib, for raw CFF
+               } EMB_INPUT_FORMAT;
+typedef enum { EMB_OUTPUT_T1,    // original type1
+               EMB_OUTPUT_TTF,   // ttf(glyf)
+               EMB_OUTPUT_CFF,   // raw cff
+               EMB_OUTPUT_SFNT   // OpenType (cff or glyf)
+               } EMB_OUTPUT_FORMAT;
+typedef enum { EMB_DEST_NATIVE,  // just subsetting/conversion
+//  TODO       EMB_DEST_PS, 
+               EMB_DEST_PDF16
+               } EMB_DESTINATION;
+
+typedef enum { EMB_RIGHT_FULL=0, EMB_RIGHT_NONE=0x02,
+               EMB_RIGHT_READONLY=0x04, 
+               EMB_RIGHT_NO_SUBSET=0x0100,
+               EMB_RIGHT_BITMAPONLY=0x0200 } EMB_RIGHT_TYPE;
+
+typedef enum { EMB_A_MULTIBYTE=0x01,    // embedd as multibyte font?
+               EMB_A_SUBSET=0x02,       // do subsetting?
+               EMB_A_CONVERT_CFF=0x04,  // convert Type1 to CFF?
+               EMB_A_WRAP_SFNT=0x08,    // wrap in sfnt?
+
+               EMB_A_CLOSE_FONTFILE=0x8000
+               } EMB_ACTIONS;
+
+typedef struct _FONTFILE FONTFILE;
+typedef struct _EMB_PARAMS {
+  EMB_INPUT_FORMAT intype;
+  EMB_OUTPUT_FORMAT outtype;
+  EMB_DESTINATION dest;
+
+  EMB_ACTIONS plan;
+
+  // font infos
+  FONTFILE *font;
+  EMB_RIGHT_TYPE rights;
+// public:
+  BITSET subset;
+
+} EMB_PARAMS;
+
+typedef enum { EMB_C_MUST_SUBSET=0x01,     // (fail, when not possible)
+               EMB_C_EDITABLE_SUBSET=0x02, // (...)
+               EMB_C_NEVER_SUBSET=0x04,    // (...)
+
+               EMB_C_FORCE_MULTIBYTE=0x08, // always use multibyte fonts
+
+               EMB_C_PDF_OT=0x10, // output CFF and even TTF as OpenType (for PDF)
+               EMB_C_KEEP_T1=0x20, // don't convert T1 to CFF
+
+               EMB_C_TAKE_FONTFILE=0x8000 // take ownership of fontfile
+               } EMB_CONSTRAINTS;
+
+#ifndef OUTPUT_FN_DECLARED
+#define OUTPUT_FN_DECLARED
+typedef void (*OUTPUT_FN)(const char *buf,int len,void *context); // as in sfnt.h 
+#endif
+EMB_PARAMS *emb_new(FONTFILE *font,EMB_DESTINATION dest,EMB_CONSTRAINTS mode);
+int emb_embed(EMB_PARAMS *emb,OUTPUT_FN output,void *context); // returns number of bytes written
+void emb_close(EMB_PARAMS *emb);
+
+/*** PDF out stuff ***/
+// all the necessary information for pdf font embedding
+typedef struct {
+  char *fontname;
+  unsigned int flags;
+
+  // for the following: 0=not set/invalid
+  int bbxmin,bbymin,bbxmax,bbymax;
+  int italicAngle;    // >=90: not set/invalid
+  int ascend;
+  int descend;
+  int capHeight;
+  int stemV;
+  // optional, default=0:
+  int xHeight;
+  int avgWidth;
+
+  // CID-additions:
+  char *panose; // 12 bytes
+  char *registry,*ordering;
+  int supplement;
+
+  char data[]; // used for storing e.g. >fontname
+} EMB_PDF_FONTDESCR;
+
+typedef struct {
+  // normal font
+  int first,last;
+  int *widths;
+
+  // multibyte font
+  int default_width;
+  int *warray; // format: len c w ... w   if (len<0) { c1 (c2=c1+(-len)) w } else { c w[len] }, terminated by len==0
+
+  int data[];
+} EMB_PDF_FONTWIDTHS;
+
+const char *emb_pdf_get_font_subtype(EMB_PARAMS *emb);
+const char *emb_pdf_get_fontfile_key(EMB_PARAMS *emb);
+const char *emb_pdf_get_fontfile_subtype(EMB_PARAMS *emb);
+
+EMB_PDF_FONTDESCR *emb_pdf_fontdescr(EMB_PARAMS *emb);
+EMB_PDF_FONTWIDTHS *emb_pdf_fontwidths(EMB_PARAMS *emb);
+
+char *emb_pdf_simple_fontdescr(EMB_PARAMS *emb,EMB_PDF_FONTDESCR *fdes,int fontfile_obj_ref);
+char *emb_pdf_simple_font(EMB_PARAMS *emb,EMB_PDF_FONTDESCR *fdes,EMB_PDF_FONTWIDTHS *fwid,int fontdescr_obj_ref);
+char *emb_pdf_simple_cidfont(EMB_PARAMS *emb,const char *fontname,int descendant_obj_ref);
+
+#endif

Added: cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/embed_sfnt.c
==============================================================================
--- (empty file)
+++ cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/embed_sfnt.c	Thu Aug 14 17:18:52 2008
@@ -0,0 +1,582 @@
+#include "embed.h"
+#include "sfnt.h"
+#include "sfnt_int.h"
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// from embed.c
+EMB_PDF_FONTWIDTHS *emb_pdf_fw_new(int datasize);
+
+EMB_RIGHT_TYPE emb_otf_get_rights(OTF_FILE *otf) // {{{
+{
+  EMB_RIGHT_TYPE ret=EMB_RIGHT_FULL;
+
+  int len;
+  char *os2=otf_get_table(otf,OTF_TAG('O','S','/','2'),&len);
+  if (os2) {
+    const unsigned short os2_version=get_USHORT(os2);
+    // check len
+    assert( (os2_version!=0x0000)||(len==78) );
+    assert( (os2_version!=0x0001)||(len==86) );
+    assert( (os2_version<0x0002)||(os2_version>0x0004)||(len==96) );
+    if (os2_version<=0x0004) {
+      // get rights
+      unsigned short fsType=get_USHORT(os2+8);
+      ret=fsType&0x0200;
+      if ((fsType&0x0f)==0x0002) {
+        ret|=EMB_RIGHT_NONE;
+      } else if ((fsType&0x0f)==0x0004) {
+        ret|=EMB_RIGHT_READONLY;
+      } 
+    }
+    free(os2);
+  }
+  return ret;
+}
+// }}}
+
+// NOTE: statically allocated buffer
+const char *emb_otf_get_fontname(OTF_FILE *otf) // {{{ 
+{
+  static char fontname[64];
+
+  int len;
+  const char *fname=otf_get_name(otf,3,1,0x409,6,&len); // microsoft
+  if (fname) {
+    int iA,iB=0;
+    for (iA=0;(iA<63)&&(iA*2<len);iA++) {
+      if ( (fname[2*iA]==0)&&
+           (fname[2*iA+1]>=33)&&(fname[2*iA+1]<=126)&&
+           (!strchr("[](){}<>/%",fname[iA*2+1])) ) {
+        fontname[iB++]=fname[iA*2+1];
+      }
+    }
+    fontname[iB]=0;
+  } else if ((fname=otf_get_name(otf,1,0,0,6,&len))) { // mac
+    int iA,iB=0;
+    for (iA=0;(iA<63)&&(iA<len);iA++) {
+      if ( (fname[iA]>=33)&&(fname[iA]<=126)&&
+           (!strchr("[](){}<>/%",fname[iA])) ) {
+        fontname[iB++]=fname[iA];
+      }
+    }
+    fontname[iB]=0;
+  } else {
+    fontname[0]=0;
+  }
+  if (!*fontname) {
+    // TODO construct a fontname, eg from */*/*/4
+    fprintf(stderr,"WARNING: no fontName\n");
+  }
+  return fontname;
+}
+// }}}
+
+void emb_otf_get_pdf_fontdescr(OTF_FILE *otf,EMB_PDF_FONTDESCR *ret) // {{{
+{
+  int len;
+
+//  TODO
+//  ... fill in struct
+  char *head=otf_get_table(otf,OTF_TAG('h','e','a','d'),&len);
+  assert(head); // version is 1.0 from otf_load
+  ret->bbxmin=get_SHORT(head+36)*1000/otf->unitsPerEm;
+  ret->bbymin=get_SHORT(head+38)*1000/otf->unitsPerEm;
+  ret->bbxmax=get_SHORT(head+40)*1000/otf->unitsPerEm;
+  ret->bbymax=get_SHORT(head+42)*1000/otf->unitsPerEm;
+  const int macStyle=get_USHORT(head+44);
+  free(head);
+
+  char *post=otf_get_table(otf,OTF_TAG('p','o','s','t'),&len);
+  assert(post);
+  const unsigned int post_version=get_ULONG(post);
+  // check length
+  assert( (post_version!=0x00010000)||(len==32) );
+  assert( (post_version!=0x00020000)||(len>=34+2*otf->numGlyphs) );
+  assert( (post_version!=0x00025000)||(len==35+otf->numGlyphs) );
+  assert( (post_version!=0x00030000)||(len==32) );
+  assert( (post_version!=0x00020000)||(get_USHORT(post+32)==otf->numGlyphs) );
+//  assert( (post_version==0x00030000)==(!!(otf->flags&OTF_F_FMT_CFF)) ); // ghostscript embedding does this..
+  // TODO: v4 (apple) :  uint16 reencoding[numGlyphs]
+  if ( (post_version==0x00010000)||
+       (post_version==0x00020000)||
+       (post_version==0x00025000)||
+       (post_version==0x00030000) ) {
+    ret->italicAngle=get_LONG(post+4)>>16;
+    if (get_ULONG(post+12)>0) { // monospaced
+      ret->flags|=1;
+    }
+  } else {
+    fprintf(stderr,"WARNING: no italicAngle, no monospaced flag\n");
+  }
+  free(post);
+
+  char *os2=otf_get_table(otf,OTF_TAG('O','S','/','2'),&len);
+  if (os2) {
+    const unsigned short os2_version=get_USHORT(os2);
+    // check len
+    assert( (os2_version!=0x0000)||(len==78) );
+    assert( (os2_version!=0x0001)||(len==86) );
+    assert( (os2_version<0x0002)||(os2_version>0x0004)||(len==96) );
+    if (os2_version<=0x0004) {
+
+      // from PDF14Deltas.pdf, pg 113
+      const int weightClass=get_USHORT(os2+4);
+      ret->stemV=50+weightClass*weightClass/(65*65); // TODO, really bad
+//printf("a %d\n",weightClass);
+
+      if (ret->supplement>=0) { // cid
+        ret->panose=ret->data;
+        memcpy(ret->panose,os2+30,12); // sFamilyClass + panose
+      }
+      const unsigned short fsSelection=get_USHORT(os2+62);
+      if (fsSelection&0x01) { // italic
+        ret->flags|=0x0040;
+      }
+      if ( (fsSelection&0x10)&&(weightClass>600) ) { // force bold
+        ret->flags|=0x0400;
+      }
+      const unsigned char family_class=get_USHORT(os2+30)>>8;
+      if (family_class==10) { // script
+        ret->flags|=0x0008;
+      }
+      if (family_class!=8) { // not sans-serif
+        ret->flags|=0x0002;
+      }
+
+      ret->avgWidth=get_SHORT(os2+2);
+      ret->ascend=get_SHORT(os2+68)*1000/otf->unitsPerEm;
+      ret->descend=get_SHORT(os2+70)*1000/otf->unitsPerEm;
+      if (os2_version>=0x0002) {
+        ret->xHeight=get_SHORT(os2+86)*1000/otf->unitsPerEm;
+        ret->capHeight=get_SHORT(os2+88)*1000/otf->unitsPerEm;
+      } // else capHeight fixed later
+    } else {
+      free(os2);
+      os2=NULL;
+    }
+  } else { 
+    fprintf(stderr,"WARNING: no OS/2 table\n");
+    // e.g. subsetted font from ghostscript // e.g. CFF
+  }
+  if (os2) {
+    free(os2);
+  } else { // TODO (if(CFF))
+    fprintf(stderr,"WARNING: no ascend/descend, capHeight, stemV, flags\n");
+    if (macStyle&0x01) { // force bold - just do it on bold
+      ret->flags|=0x0400;
+    }
+    if (macStyle&0x02) { // italic
+      ret->flags|=0x0004;
+    }
+    //  ... flags TODO? (Serif, Script, Italic, AllCap,SmallCap, ForceBold)
+  }
+
+// ? maybe get ascend,descend,capHeight,xHeight,stemV directly from cff
+  // Fallbacks
+  if ( (!ret->ascend)||(!ret->descend) ) {
+    char *hhea=otf_get_table(otf,OTF_TAG('h','h','e','a'),&len);
+    if (hhea) {
+      ret->ascend=get_SHORT(hhea+4)*1000/otf->unitsPerEm;
+      ret->descend=get_SHORT(hhea+6)*1000/otf->unitsPerEm;
+    }
+    free(hhea);
+  }
+  if (!ret->stemV) { // TODO? use name
+    const unsigned short d_gid=otf_from_unicode(otf,'.');
+    if (d_gid) { // stemV=bbox['.'].width;
+      len=otf_get_glyph(otf,d_gid);
+      assert(len>=10);
+      ret->stemV=(get_SHORT(otf->gly+6)-get_SHORT(otf->gly+2))*1000/otf->unitsPerEm;
+    } else {
+      if (macStyle&1) { // bold
+        ret->stemV=165;
+      } else {
+        ret->stemV=109; // TODO... unserious values...
+      }
+    }
+  }
+  if (!ret->capHeight) { // TODO? only reqd. for fonts with latin...
+    ret->capHeight=ret->ascend;
+  }
+  if (0) { // TODO? uses only adobe latin standard? ?? e.g. Type1
+    ret->flags|=0x0020;
+  } else {
+    ret->flags|=0x0004;
+  }
+  // TODO SmallCap by font name(?)  
+
+// TODO ;   ? cid ?
+}
+// }}}
+
+EMB_PDF_FONTWIDTHS *emb_otf_get_pdf_widths(OTF_FILE *otf,const unsigned short *encoding,int len,const BITSET glyphs) // {{{ glyphs==NULL -> all from 0 to len
+{
+  assert(otf);
+
+  int first=len,last=0;
+  int iA;
+
+  if (glyphs) {
+    for (iA=0;iA<len;iA++) {
+      const int gid=(encoding)?encoding[iA]:otf_from_unicode(otf,iA); // TODO
+      if (bit_check(glyphs,gid)) {
+        if (first>iA) {
+          first=iA;
+        }
+        if (last<iA) {
+          last=iA;
+        }
+      }
+    }
+  } else {
+    first=0;
+    last=len;
+  }
+  if (last<first) {
+    // empty
+    fprintf(stderr,"WARNING: empty embedding range\n");
+    return NULL;
+  }
+
+  // ensure hmtx is there
+  if (!otf->hmtx) {
+    if (otf_load_more(otf)!=0) {
+      assert(0);
+      return NULL;
+    }
+  }
+
+  // now create the array
+  EMB_PDF_FONTWIDTHS *ret=emb_pdf_fw_new(last-first+1);
+  if (!ret) {
+    return NULL;
+  }
+  ret->first=first;
+  ret->last=last;
+  ret->widths=ret->data;
+  for (iA=0;first<=last;iA++,first++) {
+    const int gid=(encoding)?encoding[first]:otf_from_unicode(otf,first); // TODO
+    if (gid>=otf->numGlyphs) {
+      fprintf(stderr,"Bad glyphid\n");
+      assert(0);
+      free(ret);
+      return NULL;
+    }
+    if ( (!glyphs)||(bit_check(glyphs,gid)) ) {
+      ret->widths[iA]=get_width_fast(otf,gid)*1000/otf->unitsPerEm;
+    } // else 0 from calloc
+  }
+
+  return ret;
+}
+// }}}
+
+// TODO: split into general part and otf specific part
+EMB_PDF_FONTWIDTHS *emb_otf_get_pdf_cidwidths(OTF_FILE *otf,const BITSET glyphs) // {{{ // glyphs==NULL -> output all
+{
+  assert(otf);
+
+  int iA,b,c;
+  int dw=otf_get_width(otf,0)*1000/otf->unitsPerEm,size=0; // also ensures otf->hmtx
+  assert(dw>=0);
+  // TODO? dw from  hmtx(otf->numberOfHMetrics);
+
+  int in_array=0; // current number of elements in array mode
+
+  // first pass
+  for (iA=0,b=0,c=1;iA<otf->numGlyphs;iA++,c<<=1) {
+    if (!c) {
+      b++;
+      c=1;
+    }
+    if ( (!glyphs)||(glyphs[b]&c) ) {
+      if (in_array) {
+        in_array++;
+      } else {
+        size+=2; // len c
+        in_array=1;
+      }
+    } else {
+      size+=in_array;
+      in_array=0;
+    }
+  }
+  size+=in_array;
+
+  // now create the array
+  EMB_PDF_FONTWIDTHS *ret=emb_pdf_fw_new(size+1);
+  if (!ret) {
+    return NULL;
+  }
+  ret->default_width=dw;
+  ret->warray=ret->data;
+
+  // second pass
+  in_array=0;
+  size=0;
+  for (iA=0,b=0,c=1;iA<otf->numGlyphs;iA++,c<<=1) {
+    if (!c) {
+      b++;
+      c=1;
+    }
+    if ( (!glyphs)||(glyphs[b]&c) ) {
+      const int w=get_width_fast(otf,iA)*1000/otf->unitsPerEm;
+      if ( (in_array<0)&&(ret->warray[size-1]==w) ) {
+        in_array--; // just add
+        ret->warray[size-3]=in_array; // fix len;
+        continue;
+      }
+      if (in_array>0) {
+        if ( (w==dw)&&(ret->warray[size-1]==dw) ) { // omit this and prev
+          size--;
+          in_array--; // !=0, as it does not start with >dw
+          ret->warray[size-in_array-2]=in_array; // fix len
+        } else if ( (in_array>=2)&&
+                    (ret->warray[size-1]==w)&&
+                    (ret->warray[size-2]==w) ) {
+          // three in a row.  c1 c2 w is equally short
+          if (in_array==2) { // completely replace
+            size-=4; 
+          } else {
+            size-=2;
+            ret->warray[size-in_array-2]=in_array; // fix len
+            in_array=-2;
+          }
+          in_array=-2;
+          ret->warray[size++]=in_array;
+          ret->warray[size++]=iA-2;
+          ret->warray[size++]=w;
+        } else { // just add
+          in_array++;
+          ret->warray[size++]=w;
+          ret->warray[size-in_array-2]=in_array; // fix len
+        }
+      } else if (w!=dw) {
+        in_array=1;
+        ret->warray[size++]=in_array; // len
+        ret->warray[size++]=iA; // c
+        ret->warray[size++]=w;
+      } else { // especially for in_array<0
+        in_array=0;
+      }
+    } else {
+      in_array=0;
+    }
+  }
+  ret->warray[size]=0; // terminator
+  return ret;
+}
+// }}}
+
+/*** PS stuff ***/
+
+#include "dynstring.h"
+
+// NOTE: statically allocated string
+const char *get_glyphname(const char *post,unsigned short *to_unicode,unsigned short gid)
+{
+  /*
+  ... TODO: consult post table, if there.
+  ... otherwise consult fallback table
+  ... otherwise generate "uni...".
+  ... otherwise unique name c01...
+  */
+  return "";
+}
+
+struct OUTFILTER_PS {
+  OUTPUT_FN out;
+  void *ctx;
+  int len;
+};
+
+static void outfilter_ascii_ps(const char *buf,int len,void *context)  // {{{
+{
+  struct OUTFILTER_PS *of=context;
+  OUTPUT_FN out=of->out;
+  int iA;
+
+  if (len*2+of->len > 64000) {
+    (*out)("00>\n",4,of->ctx);
+    (*out)("<",1,of->ctx);
+    of->len+=5;
+  }
+  char tmp[256];
+  while (len>0) {
+    for (iA=0;(iA<40)&&(len>0);iA++,len--) {
+      sprintf(tmp+2*iA,"%02x",buf[iA]);
+    }
+    tmp[iA]='\n';
+    (*out)(tmp,iA*2+1,of->ctx);
+    of->len+=iA*2+1;
+  }
+}
+// }}}
+
+static void outfilter_binary_ps(const char *buf,int len,void *context)  // {{{
+{
+  struct OUTFILTER_PS *of=context;
+  OUTPUT_FN out=of->out;
+
+  char tmp[100];
+  const int l=sprintf(tmp,"%d RD ",len);
+  (*out)(tmp,l,of->ctx);
+  of->len+=l;
+
+  (*out)(buf,len,of->ctx);
+  (*out)("\n",1,of->ctx);
+  of->len+=len+1;
+}
+// }}}
+
+/*
+  encoding:  character-code -> glyph id  ["required", NULL: identity(?)[or: from_unicode()]] // TODO: respect subsetting
+  to_unicode:  character-code -> unicode  [NULL: no char names]
+*/
+int emb_otf_ps(OTF_FILE *otf,unsigned short *encoding,int len,unsigned short *to_unicode,OUTPUT_FN output,void *context) // {{{
+{
+  const int binary=0; // binary format? // TODO
+  if (len>256) {
+    fprintf(stderr,"Encoding too big(%d) for Type42\n",len);
+    return -1;
+  }
+  int iA,ret=0;
+
+  DYN_STRING ds;
+  if (dyn_init(&ds,1024)==-1) {
+    return -1;
+  }
+
+  int rlen=0;
+  char *head=otf_get_table(otf,OTF_TAG('h','e','a','d'),&rlen);
+  if (!head) {
+    free(ds.buf);
+    return -1;
+  }
+  dyn_printf(&ds,"%!PS-TrueTypeFont-%d-%d\n",
+                 otf->version,get_ULONG(head+4));
+  const int bbxmin=get_SHORT(head+36)*1000/otf->unitsPerEm,
+            bbymin=get_SHORT(head+38)*1000/otf->unitsPerEm,
+            bbxmax=get_SHORT(head+40)*1000/otf->unitsPerEm,
+            bbymax=get_SHORT(head+42)*1000/otf->unitsPerEm;
+  free(head);
+
+  char *post=otf_get_table(otf,OTF_TAG('p','o','s','t'),&rlen);
+  if ( (!post)&&(rlen!=-1) ) { // other error than "not found"
+    free(ds.buf);
+    return -1;
+  }
+  if (post) {
+    const unsigned int minMem=get_ULONG(post+16),maxMem=get_ULONG(post+20);
+    if (minMem) {
+      dyn_printf(&ds,"%%VMusage: %d %d\n",minMem,maxMem);
+    }
+  }
+
+  dyn_printf(&ds,"11 dict begin\n"
+                 "/FontName /%s def\n"
+                 "/Encoding 256 array\n"
+                 "0 1 255 { 1 index exch /.notdef put } for\n",
+                 emb_otf_get_fontname(otf));
+  for (iA=0;iA<len;iA++) {
+    const int gid=(encoding)?encoding[iA]:iA;
+    dyn_printf(&ds,"dup %d /%s put\n",
+                   iA,get_glyphname(post,to_unicode,gid));
+  }
+  dyn_printf(&ds,"readonly def\n");
+
+  dyn_printf(&ds,"/PaintType 0 def\n"
+                 "/FontMatrix [1 0 0 1 0 0] def\n"
+                 "/FontBBox [%d %d %d %d] def\n"
+                 "/FontType 42 def\n",
+//                 "/XUID\n"  // TODO?!?
+                 bbxmin,bbymin,bbxmax,bbymax);
+  if (post) {
+    dyn_printf(&ds,"/FontInfo 4 dict dup begin\n"
+                   "  /ItalicAngle %d def\n"
+                   "  /isFixedPitch %d def\n"
+                   "  /UnderlinePosition %d def\n"
+                   "  /UnderlineThickness %d def\n"
+                   "end readonly def\n",
+                   get_LONG(post+4)>>16,
+                   get_ULONG(post+12),
+                   (get_SHORT(post+8)-get_SHORT(post+10)/2)*1000/otf->unitsPerEm,
+                   get_SHORT(post+10)*1000/otf->unitsPerEm);
+  }
+  if (binary) {
+    dyn_printf(&ds,"/RD { string currentfile exch readstring pop } executeonly def\n");
+  }
+
+  dyn_printf(&ds,"/sfnts[");
+  if (ds.len<0) {
+    free(post);
+    free(ds.buf);
+    return -1;
+  }
+  (*output)(ds.buf,ds.len,context);
+  ret+=ds.len;
+  ds.len=0;
+
+  // {{{ copy tables verbatim
+  struct _OTF_WRITE *otw;
+  otw=malloc(sizeof(struct _OTF_WRITE)*otf->numTables);
+  if (!otw) {
+    fprintf(stderr,"Bad alloc: %m\n");
+    free(post);
+    free(ds.buf);
+    return -1;
+  }
+  // just copy everything
+  for (iA=0;iA<otf->numTables;iA++) {
+    otw[iA].tag=otf->tables[iA].tag;
+    otw[iA].action=otf_action_copy;
+    otw[iA].param=otf;
+    otw[iA].length=iA;
+  }
+
+  struct OUTFILTER_PS of;
+  of.out=output;
+  of.ctx=context;
+  of.len=0;
+  if (binary) {
+    iA=otf_write_sfnt(otw,otf->version,otf->numTables,outfilter_binary_ps,&of);
+  } else {
+    iA=otf_write_sfnt(otw,otf->version,otf->numTables,outfilter_ascii_ps,&of);
+  }
+  free(otw);
+  if (iA==-1) {
+    free(post);
+    free(ds.buf);
+    return -1;
+  }
+  ret+=of.len;
+
+  dyn_printf(&ds,"] def\n");
+  // }}} done copying
+
+  const int num_chars=0;
+  dyn_printf(&ds,"/CharStrings %d dict dup begin\n",num_chars);
+  for (iA=0;iA<num_chars;iA++) {
+    const int gid=(encoding)?encoding[iA]:iA;
+    dyn_printf(&ds,"/%s %d def\n",get_glyphname(post,to_unicode,gid),gid);
+// ... from cmap [respecting subsetting...]
+  }
+  dyn_printf(&ds,"Fontname currentdict end definefont pop\n");
+  free(post);
+
+  if (ds.len<0) {
+    free(ds.buf);
+    return -1;
+  }
+  (*output)(ds.buf,ds.len,context);
+  ret+=ds.len;
+  ds.len=0;
+
+  free(ds.buf);
+  return ret; 
+}
+// }}}
+

Added: cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/main.c
==============================================================================
--- (empty file)
+++ cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/main.c	Thu Aug 14 17:18:52 2008
@@ -0,0 +1,166 @@
+#include "sfnt.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "embed.h"
+
+struct _FONTFILE {
+  OTF_FILE *sfnt;
+  // CFF *;
+};
+
+#if 0
+enum { TTF_OTF, TYPE1 } inputFile;
+if (TTF_OTF) {
+  assert(!TTC);
+  if (CFF/OTF) {
+    // or EMB_PDF_FONTFILE3_OTF [unstripped]
+    if (CIDfont) {
+      asset(multiBYTE);
+      strip_sfnt() // "CIDFontType0"  EMB_PDF_FONTFILE3_CID0C
+    } else {
+      ... strip_sfnt();
+    }
+  } else {
+    ...
+  }
+} else if (TYPE1) {
+  assert(!MMType1);
+  assert(!OCF);
+  assert(!WrappedCID_CFF);
+  ... convert_to_cff()
+}
+// not supported: MMType1 Type3
+#endif
+
+#include <string.h>
+
+static void example_outfn(const char *buf,int len,void *context) // {{{
+{
+  FILE *f=(FILE *)context;
+  if (fwrite(buf,1,len,f)!=len) {
+    fprintf(stderr,"Short write: %m\n");
+    assert(0);
+    return;
+  }
+}
+// }}}
+
+void example_write_fontdescr(OTF_FILE *otf,const char *outfile) // {{{
+{
+  struct _FONTFILE ff; ff.sfnt=otf; // TODO
+  EMB_PARAMS *emb=emb_new(&ff,
+                          EMB_DEST_PDF16,
+//                          EMB_C_KEEP_T1 
+                          EMB_C_FORCE_MULTIBYTE
+
+                          );
+  EMB_PDF_FONTDESCR *fdes=emb_pdf_fontdescr(emb);
+  assert(fdes);
+  assert(emb->subset);
+
+  bit_set(emb->subset,otf_from_unicode(otf,'a'));
+  bit_set(emb->subset,otf_from_unicode(otf,0x400));
+
+  EMB_PDF_FONTWIDTHS *fwid=emb_pdf_fontwidths(emb);
+  assert(fwid);
+
+  printf("0 0 obj\n");
+  char *res=emb_pdf_simple_fontdescr(emb,fdes,1);
+  assert(res);
+  fputs(res,stdout);
+  free(res);
+  printf("endobj\n");
+
+  printf("1 0 obj\n"
+         "<<\n");
+  if (emb_pdf_get_fontfile_subtype(emb)) {
+    printf("  /SubType /%s\n",
+           emb_pdf_get_fontfile_subtype(emb));
+  }
+  if (emb->outtype&EMB_OUTPUT_T1) {
+    printf("  /Length1 ?\n"
+           "  /Length2 ?\n"
+           "  /Length3 ?\n");
+  } else {
+    printf("  /Length1 2 0 R\n");
+  }
+  printf("  /Length 2 0 R\n" // maybe compress it...
+         ">>\n"
+         "stream\n");
+  int outlen=0; // TODO
+// TODO
+  if (outfile) {
+    FILE *f=fopen(outfile,"w");
+    if (!f) {
+      fprintf(stderr,"Opening \"%s\" for writing failed: %m\n",outfile);
+      assert(0);
+      return;
+    }
+    outlen=emb_embed(emb,example_outfn,f);
+//    outlen=otf_ttc_extract(emb->font->sfnt,example_outfn,f);
+    fclose(f);
+  }
+puts("...");
+  printf("endstream\n"
+         "endobj\n");
+  printf("2 0 obj\n"
+         "%d\n" 
+         "endobj\n",
+         outlen
+         );
+
+  printf("3 0 obj\n");
+  res=emb_pdf_simple_font(emb,fdes,fwid,0);
+  assert(res);
+  fputs(res,stdout);
+  free(res);
+  printf("endobj\n");
+
+  if (emb->plan&EMB_A_MULTIBYTE) {
+    printf("4 0 obj\n");
+    res=emb_pdf_simple_cidfont(emb,fdes->fontname,3);
+    assert(res);
+    fputs(res,stdout);
+    free(res);
+    printf("endobj\n");
+  }
+
+  free(fdes);
+  free(fwid);
+}
+// }}}
+
+// TODO? reencode?
+int main(int argc,char **argv) 
+{
+  const char *fn="/usr/share/fonts/truetype/microsoft/ARIALN.TTF";
+  if (argc==2) {
+    fn=argv[1];
+  }
+  OTF_FILE *otf=otf_load(fn);
+  assert(otf);
+  printf("width(4): %d\n",otf_get_width(otf,4));
+
+
+  if (strcmp(fn,"test.ttf")!=0) {
+    example_write_fontdescr(otf,"test.ttf");
+  } else {
+    example_write_fontdescr(otf,NULL);
+  }
+
+  // show_post(otf);
+
+  // show_name(otf);
+
+  // show_cmap(otf);
+  // printf("%d %d\n",otf_from_unicode(otf,'A'),0);
+
+  // ... name 6 -> FontName  /20(cid)
+  // ? StemV Flags(?) from FontName 
+
+  otf_close(otf);
+
+  return 0;
+}

Added: cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/sfnt.c
==============================================================================
--- (empty file)
+++ cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/sfnt.c	Thu Aug 14 17:18:52 2008
@@ -0,0 +1,1317 @@
+#include "sfnt.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "sfnt_int.h"
+
+// TODO?
+// get_SHORT(head+48) // fontDirectionHint
+/* reqd. Tables: cmap, head, hhea, hmtx, maxp, name, OS/2, post     .
+ OTF: glyf,loca [cvt,fpgm,prep]
+ */
+
+static void otf_bsearch_params(int num, // {{{
+                               int recordSize,
+                               int *searchRange,
+                               int *entrySelector,
+                               int *rangeShift)
+{
+  assert(num>0);
+  assert(searchRange);
+  assert(entrySelector);
+  assert(rangeShift);
+
+  int iA,iB;
+  for (iA=1,iB=0;iA<=num;iA<<=1,iB++) {}
+
+  *searchRange=iA*recordSize/2;
+  *entrySelector=iB-1;
+  *rangeShift=num*recordSize-*searchRange;
+}
+// }}}
+
+static char *otf_bsearch(char *table, // {{{
+                         const char *target,int len,
+                         int searchRange,
+                         int entrySelector,
+                         int rangeShift,
+                         int lower_bound) // return lower_bound, if !=0
+{
+  char *ret=table+rangeShift;
+  if (memcmp(target,ret,len)<0) {
+    ret=table;
+  }
+
+  for (;entrySelector>0;entrySelector--) {
+    searchRange>>=1;
+    ret+=searchRange;
+    if (memcmp(target,ret,len)<0) {
+      ret-=searchRange;
+    }
+  }
+  const int result=memcmp(target,ret,len);
+  if (result==0) {
+    return ret;
+  } else if (lower_bound) {
+    if (result>0) {
+      return ret+searchRange;
+    }
+    return ret;
+  }
+  return NULL; // not found;
+}
+// }}}
+
+static OTF_FILE *otf_new(FILE *f) // {{{
+{
+  assert(f);
+
+  OTF_FILE *ret;
+  ret=calloc(1,sizeof(OTF_FILE));
+  if (ret) {
+    ret->f=f;
+  }
+  ret->version=0x00010000;
+
+  return ret;
+}
+// }}}
+
+static char *otf_read(OTF_FILE *otf,char *buf,long pos,int length) // {{{ -  will alloc, if >buf ==NULL, returns >buf, or NULL on error
+{
+  char *ours=NULL;
+
+  if (length==0) {
+    return buf;
+  } else if (length<0) {
+    assert(0);
+    return NULL;
+  }
+
+  int res=fseek(otf->f,pos,SEEK_SET);
+  if (res==-1) {
+    fprintf(stderr,"Seek failed: %m\n");
+    return NULL;
+  }
+
+  // (+3)&~3 for checksum...
+  const int pad_len=(length+3)&~3;
+  if (!buf) {
+    ours=buf=malloc(sizeof(char)*pad_len);
+    if (!buf) {
+      fprintf(stderr,"Bad alloc: %m\n");
+      return NULL;
+    }
+  }
+
+  res=fread(buf,1,pad_len,otf->f);
+  if (res!=pad_len) {
+    if (res==length) { // file size not multiple of 4, pad with zero
+      memset(buf+res,0,pad_len-length);
+    } else {
+      fprintf(stderr,"Short read\n");
+      free(ours);
+      return NULL;
+    }
+  }
+ 
+  return buf;
+}
+// }}}
+
+static unsigned int otf_checksum(const char *buf, unsigned int len) // {{{
+{
+  unsigned int ret=0;
+
+  for (len=(len+3)/4;len>0;len--,buf+=4) {
+    ret+=get_ULONG(buf);
+  }
+
+  return ret;
+}
+// }}}
+
+
+static int otf_get_ttc_start(OTF_FILE *otf,int use_ttc) // {{{
+{
+  char buf[4];
+
+  if (!otf->numTTC) { // >0 if TTC...
+    return 0;
+  }
+
+  int pos=0;
+  if ( (use_ttc<0)||(use_ttc>=otf->numTTC)||
+       (!otf_read(otf,buf,pos+12+4*use_ttc,4)) ) {
+    fprintf(stderr,"Bad TTC subfont number\n");
+    return -1;
+  }
+  return get_ULONG(buf);
+}
+// }}}
+
+OTF_FILE *otf_do_load(OTF_FILE *otf,int pos) // {{{
+{
+  int iA;
+  char buf[16];
+
+  // {{{ read offset table
+  if (otf_read(otf,buf,pos,12)) {
+    otf->version=get_ULONG(buf);
+    if (otf->version==0x00010000) { // 1.0 truetype
+    } else if (otf->version==OTF_TAG('O','T','T','O')) { // OTF(CFF)
+      otf->flags|=OTF_F_FMT_CFF;
+    } else if (otf->version==OTF_TAG('t','r','u','e')) { // (old mac)
+    } else if (otf->version==OTF_TAG('t','y','p','1')) { // sfnt wrapped type1
+      // TODO: unsupported
+    } else {
+      otf_close(otf);
+      otf=NULL;
+    }
+    pos+=12;
+  } else {
+    otf_close(otf);
+    otf=NULL;
+  }
+  if (!otf) {
+    fprintf(stderr,"Not a ttf font\n");
+    return NULL;
+  }
+  otf->numTables=get_USHORT(buf+4);
+  // }}}
+
+  // {{{ read directory
+  otf->tables=malloc(sizeof(OTF_DIRENT)*otf->numTables);
+  if (!otf->tables) {
+    fprintf(stderr,"Bad alloc: %m\n");
+    otf_close(otf);
+    return NULL;
+  }
+  for (iA=0;iA<otf->numTables;iA++) {
+    if (!otf_read(otf,buf,pos,16)) {
+      otf_close(otf);
+      return NULL;
+    }
+    otf->tables[iA].tag=get_ULONG(buf);
+    otf->tables[iA].checkSum=get_ULONG(buf+4);
+    otf->tables[iA].offset=get_ULONG(buf+8);
+    otf->tables[iA].length=get_ULONG(buf+12);
+    if ( (otf->tables[iA].tag==OTF_TAG('C','F','F',' '))&&
+         ((otf->flags&OTF_F_FMT_CFF)==0) ) {
+      fprintf(stderr,"Wrong magic\n");
+      otf_close(otf);
+      return NULL;
+    } else if ( (otf->tables[iA].tag==OTF_TAG('g','l','y','p'))&&
+                (otf->flags&OTF_F_FMT_CFF) ) {
+      fprintf(stderr,"Wrong magic\n");
+      otf_close(otf);
+      return NULL;
+    }
+    pos+=16;
+  }
+  // }}}
+  
+//  otf->flags|=OTF_F_DO_CHECKSUM;
+  // {{{ check head table
+  int len=0;
+  char *head=otf_get_table(otf,OTF_TAG('h','e','a','d'),&len);
+  if ( (!head)||
+       (get_ULONG(head+0)!=0x00010000)||  // version
+       (len!=54)||
+       (get_ULONG(head+12)!=0x5F0F3CF5)|| // magic
+       (get_SHORT(head+52)!=0x0000) ) {   // glyphDataFormat
+    fprintf(stderr,"Unsupported OTF font / head table \n");
+    free(head);
+    otf_close(otf);
+    return NULL;
+  }
+  // }}}
+  otf->unitsPerEm=get_USHORT(head+18);
+  otf->indexToLocFormat=get_SHORT(head+50);
+
+  // {{{ checksum whole file
+  if (otf->flags&OTF_F_DO_CHECKSUM) {
+    unsigned int csum=0;
+    char tmp[1024];
+    rewind(otf->f);
+    while (!feof(otf->f)) {
+      len=fread(tmp,1,1024,otf->f);
+      if (len&3) { // zero padding reqd.
+        memset(tmp+len,0,4-(len&3));
+      }
+      csum+=otf_checksum(tmp,len);
+    }
+    if (csum!=0xb1b0afba) {
+      fprintf(stderr,"Wrong global checksum\n");
+      free(head);
+      otf_close(otf);
+      return NULL;
+    }
+  }
+  // }}}
+  free(head);
+
+  // {{{ read maxp table / numGlyphs
+  char *maxp=otf_get_table(otf,OTF_TAG('m','a','x','p'),&len);
+  if (maxp) {
+    const unsigned int maxp_version=get_ULONG(maxp);
+    if ( (maxp_version==0x00005000)&&(len==6) ) { // version 0.5
+      otf->numGlyphs=get_USHORT(maxp+4);
+      if ( (otf->flags&OTF_F_FMT_CFF)==0) { // only CFF
+        free(maxp);
+        maxp=NULL;
+      }
+    } else if ( (maxp_version==0x00010000)&&(len==32) ) { // version 1.0
+      otf->numGlyphs=get_USHORT(maxp+4);
+      if (otf->flags&OTF_F_FMT_CFF) { // only TTF
+        free(maxp);
+        maxp=NULL;
+      }
+    } else {
+      free(maxp);
+      maxp=NULL;
+    }
+  }
+  if (!maxp) {
+    fprintf(stderr,"Unsupported OTF font / maxp table \n");
+    free(maxp);
+    otf_close(otf);
+    return NULL;
+  }
+  free(maxp);
+  // }}}
+
+  return otf;
+}
+// }}}
+
+OTF_FILE *otf_load(const char *file) // {{{
+{
+  FILE *f;
+  OTF_FILE *otf;
+ 
+  int use_ttc=-1;
+  if ((f=fopen(file,"rb"))==NULL) {
+    // check for TTC
+    char *tmp=strrchr(file,'/'),*end;
+    if (tmp) {
+      use_ttc=strtoul(tmp+1,&end,10);
+      if (!*end) {
+        end=malloc((tmp-file+1)*sizeof(char));
+        if (!end) {
+          fprintf(stderr,"Bad alloc: %m\n");
+          return NULL;
+        }
+        strncpy(end,file,tmp-file);
+        end[tmp-file]=0;
+        f=fopen(end,"rb");
+        free(end);
+      }
+    }
+    if (!f) {
+      fprintf(stderr,"Could not open \"%s\": %m\n",file);
+      return NULL;
+    }
+  }
+  otf=otf_new(f);
+  if (!otf) {
+    fprintf(stderr,"Bad alloc: %m\n");
+    fclose(f);
+    return NULL;
+  }
+
+  char buf[12];
+  int pos=0;
+  // {{{ check for TTC
+  if (otf_read(otf,buf,pos,12)) {
+    const unsigned int version=get_ULONG(buf);
+    if (version==OTF_TAG('t','t','c','f')) {
+      const unsigned int ttc_version=get_ULONG(buf+4);
+      if ( (ttc_version!=0x00010000)&&(ttc_version!=0x00020000) ) {
+        fprintf(stderr,"Unsupported TTC version\n");
+        otf_close(otf);
+        return NULL;
+      }
+      otf->numTTC=get_ULONG(buf+8);
+      otf->useTTC=use_ttc;
+      pos=otf_get_ttc_start(otf,use_ttc);
+      if (pos==-1) {
+        otf_close(otf);
+        return NULL;
+      }
+    }
+  } else {
+    fprintf(stderr,"Not a ttf font\n");
+    otf_close(otf);
+    return NULL;
+  }
+  // }}}
+
+  return otf_do_load(otf,pos);
+}
+// }}}
+
+void otf_close(OTF_FILE *otf) // {{{
+{
+  assert(otf);
+  if (otf) {
+    free(otf->gly);
+    free(otf->cmap);
+    free(otf->name);
+    free(otf->hmtx);
+    free(otf->glyphOffsets);
+    fclose(otf->f);
+    free(otf->tables);
+    free(otf);
+  }
+}
+// }}}
+
+static int otf_find_table(OTF_FILE *otf,unsigned int tag) // {{{ -1 on error
+{
+#if 0
+  // binary search would require raw table
+  int pos=0;
+  char buf[12];
+  if (!otf_read(otf,buf,pos,12)) {
+    return -1;
+  }
+  pos=12;
+  const unsigned int numTables=get_USHORT(buf+4);
+  char *tables=malloc(16*numTables);
+  if (!tables) {
+    return -1;
+  }
+  if (!otf_read(otf,tables,pos,16*numTables)) {
+    free(tables);
+    return -1;
+  }
+  char target[]={(tag>>24),(tag>>16),(tag>>8),tag};
+  //  assert(get_USHORT(buf+6)+get_USHORT(buf+10)==16*numTables);
+  char *result=otf_bsearch(tables,target,4,
+                           get_USHORT(buf+6),
+                           get_USHORT(buf+8),
+                           get_USHORT(buf+10),0);
+  free(tables);
+  if (result) {
+    return (result-tables)/16;
+  }
+#else
+  int iA;
+  for (iA=0;iA<otf->numTables;iA++) {
+    if (otf->tables[iA].tag==tag) {
+      return iA;
+    }
+  }
+#endif
+  return -1;
+}
+// }}}
+
+char *otf_get_table(OTF_FILE *otf,unsigned int tag,int *ret_len) // {{{
+{
+  assert(otf);
+  assert(ret_len);
+
+  const int idx=otf_find_table(otf,tag);
+  if (idx==-1) {
+    *ret_len=-1;
+    return NULL;
+  }
+  const OTF_DIRENT *table=otf->tables+idx;
+
+  char *ret=otf_read(otf,NULL,table->offset,table->length);
+  if (!ret) {
+    return NULL;
+  }
+  if (otf->flags&OTF_F_DO_CHECKSUM) {
+    unsigned int csum=otf_checksum(ret,table->length);
+    if (tag==OTF_TAG('h','e','a','d')) { // special case
+      csum-=get_ULONG(ret+8);
+    }
+    if (csum!=table->checkSum) {
+      fprintf(stderr,"Wrong checksum for %c%c%c%c\n",OTF_UNTAG(tag));
+      free(ret);
+      return NULL;
+    }
+  }
+  *ret_len=table->length;
+  return ret;
+}
+// }}}
+
+int otf_load_glyf(OTF_FILE *otf) // {{{ - 0 on success
+{
+  assert((otf->flags&OTF_F_FMT_CFF)==0); // not for CFF
+  int iA,len;
+  // {{{ find glyf table
+  iA=otf_find_table(otf,OTF_TAG('g','l','y','f'));
+  if (iA==-1) {
+    fprintf(stderr,"Unsupported OTF font / glyf table \n");
+    return -1;
+  }
+  otf->glyfTable=otf->tables+iA;
+  // }}}
+
+  // {{{ read loca table
+  char *loca=otf_get_table(otf,OTF_TAG('l','o','c','a'),&len);
+  if ( (!loca)||
+       (otf->indexToLocFormat>=2)||
+       (((len+3)&~3)!=((((otf->numGlyphs+1)*(otf->indexToLocFormat+1)*2)+3)&~3)) ) {
+    fprintf(stderr,"Unsupported OTF font / loca table \n");
+    return -1;
+  }
+  if (otf->glyphOffsets) {
+    free(otf->glyphOffsets);
+    assert(0);
+  }
+  otf->glyphOffsets=malloc((otf->numGlyphs+1)*sizeof(unsigned int));
+  if (!otf->glyphOffsets) {
+    fprintf(stderr,"Bad alloc: %m\n");
+    return -1;
+  }
+  if (otf->indexToLocFormat==0) {
+    for (iA=0;iA<=otf->numGlyphs;iA++) {
+      otf->glyphOffsets[iA]=get_USHORT(loca+iA*2)*2;
+    }
+  } else { // indexToLocFormat==1
+    for (iA=0;iA<=otf->numGlyphs;iA++) {
+      otf->glyphOffsets[iA]=get_ULONG(loca+iA*4);
+    }
+  }
+  free(loca);
+  if (otf->glyphOffsets[otf->numGlyphs]>otf->glyfTable->length) {
+    fprintf(stderr,"Bad loca table \n");
+    return -1;
+  }
+  // }}}
+  
+  // {{{ allocate otf->gly slot
+  int maxGlyfLen=0;  // no single glyf takes more space
+  for (iA=1;iA<=otf->numGlyphs;iA++) {
+    const int glyfLen=otf->glyphOffsets[iA]-otf->glyphOffsets[iA-1];
+    if (glyfLen<0) {
+      fprintf(stderr,"Bad loca table: glyph len %d\n",glyfLen);
+      return -1;
+    }
+    if (maxGlyfLen<glyfLen) {
+      maxGlyfLen=glyfLen;
+    }
+  }
+  if (otf->gly) {
+    free(otf->gly);
+    assert(0);
+  }
+  otf->gly=malloc(maxGlyfLen*sizeof(char));
+  if (!otf->gly) {
+    fprintf(stderr,"Bad alloc: %m\n");
+    return -1;
+  }
+  // }}}
+
+  return 0;
+}
+// }}}
+
+int otf_load_more(OTF_FILE *otf) // {{{ -  0 on success
+{
+  int iA;
+
+  int len;
+  if ((otf->flags&OTF_F_FMT_CFF)==0) { // not for CFF
+    if (otf_load_glyf(otf)==-1) {
+      return -1;
+    }
+  }
+
+  // {{{ read hhea table
+  char *hhea=otf_get_table(otf,OTF_TAG('h','h','e','a'),&len);
+  if ( (!hhea)||
+       (get_ULONG(hhea)!=0x00010000)|| // version
+       (len!=36)||
+       (get_SHORT(hhea+32)!=0) ) { // metric format
+    fprintf(stderr,"Unsupported OTF font / hhea table \n");
+    return -1;
+  }
+  otf->numberOfHMetrics=get_USHORT(hhea+34);
+  free(hhea);
+  // }}}
+
+  // {{{ read hmtx table
+  char *hmtx=otf_get_table(otf,OTF_TAG('h','m','t','x'),&len);
+  if ( (!hmtx)||
+       (len!=otf->numberOfHMetrics*2+otf->numGlyphs*2) ) {
+    fprintf(stderr,"Unsupported OTF font / hmtx table \n");
+    return -1;
+  }
+  if (otf->hmtx) {
+    free(otf->hmtx);
+    assert(0);
+  }
+  otf->hmtx=hmtx;
+  // }}}
+
+  // {{{ read name table
+  char *name=otf_get_table(otf,OTF_TAG('n','a','m','e'),&len);
+  if ( (!name)||
+       (get_USHORT(name)!=0x0000)|| // version
+       (len<get_USHORT(name+2)*12+6)||
+       (len<=get_USHORT(name+4)) ) {
+    fprintf(stderr,"Unsupported OTF font / name table \n");
+    return -1;
+  }
+  // check bounds
+  int name_count=get_USHORT(name+2);
+  const char *nstore=name+get_USHORT(name+4);
+  for (iA=0;iA<name_count;iA++) {
+    const char *nrec=name+6+12*iA;
+    if (nstore-name+get_USHORT(nrec+10)+get_USHORT(nrec+8)>len) {
+      fprintf(stderr,"Bad name table \n");
+      free(name);
+      return -1;
+    }
+  }
+  if (otf->name) {
+    free(otf->name);
+    assert(0);
+  }
+  otf->name=name;
+  // }}}
+
+  return 0;
+}
+// }}}
+
+int otf_load_cmap(OTF_FILE *otf) // {{{ -  0 on success
+{
+  int iA;
+  int len;
+
+  char *cmap=otf_get_table(otf,OTF_TAG('c','m','a','p'),&len);
+  if ( (!cmap)||
+       (get_USHORT(cmap)!=0x0000)|| // version
+       (len<get_USHORT(cmap+2)*8+4) ) {
+    fprintf(stderr,"Unsupported OTF font / cmap table \n");
+    assert(0);
+    return -1;
+  }
+  // check bounds, find (3,0) or (3,1) [TODO?]
+  const int numTables=get_USHORT(cmap+2);
+  for (iA=0;iA<numTables;iA++) {
+    const char *nrec=cmap+4+8*iA;
+    const unsigned int offset=get_ULONG(nrec+4);
+    const char *ndata=cmap+offset;
+    if ( (ndata<cmap+4+8*numTables)||
+         (offset>=len)||
+         (offset+get_USHORT(ndata+2)>len) ) {
+      fprintf(stderr,"Bad cmap table \n");
+      free(cmap);
+      assert(0);
+      return -1;
+    }
+    if ( (get_USHORT(nrec)==3)&&
+         (get_USHORT(nrec+2)<=1)&&
+         (get_USHORT(ndata)==4)&&
+         (get_USHORT(ndata+4)==0) ) {
+      otf->unimap=ndata;
+    }
+  }
+  if (otf->cmap) {
+    free(otf->cmap);
+    assert(0);
+  }
+  otf->cmap=cmap;
+
+  return 0;
+}
+// }}}
+
+int otf_get_width(OTF_FILE *otf,unsigned short gid) // {{{  -1 on error
+{
+  assert(otf);
+
+  if (gid>=otf->numGlyphs) {
+    return -1;
+  }
+
+  // ensure hmtx is there
+  if (!otf->hmtx) {
+    if (otf_load_more(otf)!=0) {
+      assert(0);
+      return -1;
+    }
+  }
+
+  return get_width_fast(otf,gid);
+#if 0
+  if (gid>=otf->numberOfHMetrics) {
+    return get_USHORT(otf->hmtx+(otf->numberOfHMetrics-1)*2);
+    // TODO? lsb=get_SHORT(otf->hmtx+otf->numberOfHMetrics*2+gid*2);
+  }
+  return get_USHORT(otf->hmtx+gid*4);
+  // TODO? lsb=get_SHORT(otf->hmtx+gid*4+2);
+#endif
+}
+// }}}
+
+static int otf_name_compare(const void *a,const void *b) // {{{
+{
+  return memcmp(a,b,8);
+}
+// }}}
+
+const char *otf_get_name(OTF_FILE *otf,int platformID,int encodingID,int languageID,int nameID,int *ret_len) // {{{
+{
+  assert(otf);
+  assert(ret_len);
+
+  // ensure name is there
+  if (!otf->name) {
+    if (otf_load_more(otf)!=0) {
+      *ret_len=-1;
+      assert(0);
+      return NULL;
+    }
+  }
+
+  char key[8];
+  set_USHORT(key,platformID);
+  set_USHORT(key+2,encodingID);
+  set_USHORT(key+4,languageID);
+  set_USHORT(key+6,nameID);
+
+  char *res=bsearch(key,otf->name+6,get_USHORT(otf->name+2),12,otf_name_compare);
+  if (res) {
+    *ret_len=get_USHORT(res+8);
+    int npos=get_USHORT(res+10);
+    const char *nstore=otf->name+get_USHORT(otf->name+4);
+    return nstore+npos;
+  }
+  *ret_len=0;
+  return NULL;
+}
+// }}}
+
+int otf_get_glyph(OTF_FILE *otf,unsigned short gid) // {{{ result in >otf->gly, returns length, -1 on error
+{
+  assert(otf);
+  assert((otf->flags&OTF_F_FMT_CFF)==0); // not for CFF
+
+  if (gid>=otf->numGlyphs) {
+    return -1;
+  }
+
+  // ensure >glyphOffsets and >gly is there
+  if ( (!otf->gly)||(!otf->glyphOffsets) ) {
+    if (otf_load_more(otf)!=0) {
+      assert(0);
+      return -1;
+    }
+  }
+
+  const int len=otf->glyphOffsets[gid+1]-otf->glyphOffsets[gid];
+  if (len==0) {
+    return 0;
+  }
+
+  assert(otf->glyfTable->length>=otf->glyphOffsets[gid+1]);
+  if (!otf_read(otf,otf->gly,
+                otf->glyfTable->offset+otf->glyphOffsets[gid],len)) {
+    return -1;
+  }
+
+  return len;
+}
+// }}}
+
+unsigned short otf_from_unicode(OTF_FILE *otf,int unicode) // {{{ 0 = missing
+{
+  assert(otf);
+  assert( (unicode>=0)&&(unicode<65536) );
+//  assert((otf->flags&OTF_F_FMT_CFF)==0); // not for CFF, other method!
+
+  // ensure >cmap and >unimap is there
+  if (!otf->cmap) {
+    if (otf_load_cmap(otf)!=0) {
+      assert(0);
+      return 0; // TODO?
+    }
+  }
+  if (!otf->unimap) {
+    fprintf(stderr,"Unicode (3,1) cmap in format 4 not found\n");
+    return 0;
+  }
+
+#if 0
+  // linear search is cache friendly and should be quite fast
+#else
+  const unsigned short segCountX2=get_USHORT(otf->unimap+6);
+  char target[]={unicode>>8,unicode}; // set_USHORT(target,unicode);
+  char *result=otf_bsearch((char *)otf->unimap+14,target,2,
+                           get_USHORT(otf->unimap+8),
+                           get_USHORT(otf->unimap+10),
+                           get_USHORT(otf->unimap+12),1);
+  if (result>=otf->unimap+14+segCountX2) {
+    assert(0); // bad font, no 0xffff
+    return 0;
+  }
+
+  result+=2+segCountX2;
+  const unsigned short startCode=get_USHORT(result);
+  if (startCode>unicode) {
+    return 0;
+  }
+  result+=2*segCountX2;
+  const unsigned short rangeOffset=get_USHORT(result);
+  if (rangeOffset) {
+    return get_USHORT(result+rangeOffset+2*(unicode-startCode));
+  } else {
+    const short delta=get_SHORT(result-segCountX2);
+    return (delta+unicode)&0xffff;
+  }
+#endif
+}
+// }}}
+
+#include "bitset.h"
+
+static int otf_subset_glyf(OTF_FILE *otf,int gid,BITSET glyphs) // {{{  include components of compund glyphs, returns additional space requirements
+{
+  int ret=0;
+  if (get_SHORT(otf->gly)>=0) { // not composite
+    return ret; // done
+  }
+
+  char *cur=otf->gly+10;
+
+  unsigned short flags;
+  do {
+    flags=get_USHORT(cur);
+    const unsigned short sub_gid=get_USHORT(cur+2);
+    assert(sub_gid<otf->numGlyphs);
+    if (!bit_check(glyphs,sub_gid)) {
+      // bad: temporarily load sub glyph
+      const int len=otf_get_glyph(otf,sub_gid);
+      assert(len>0);
+      bit_set(glyphs,sub_gid);
+      if (sub_gid<gid) {
+        ret+=len;
+        ret+=otf_subset_glyf(otf,sub_gid,glyphs); // composite of composites?, e.g. in DejaVu
+      }
+      const int res=otf_get_glyph(otf,gid); // reload current glyph
+      assert(res);
+    }
+    
+    // skip parameters
+    cur+=6;
+    if (flags&0x01) {
+      cur+=2;
+    }
+    if (flags&0x08) {
+      cur+=2;
+    } else if (flags&0x40) {
+      cur+=4;
+    } else if (flags&0x80) {
+      cur+=8;
+    }
+  } while (flags&0x20); // more components
+
+  return ret;
+}
+// }}}
+
+int otf_subset2(OTF_FILE *otf,BITSET glyphs,OUTPUT_FN output,void *context) // {{{ - returns number of bytes written
+{
+  assert(otf);
+  assert(glyphs);
+  assert(output);
+
+  int iA,b,c;
+  int ret=0;
+
+  // first pass
+  bit_set(glyphs,0); // .notdef always required
+  int glyfSize=0;
+  for (iA=0,b=0,c=1;iA<otf->numGlyphs;iA++,c<<=1) {
+    if (!c) {
+      b++;
+      c=1;
+    }
+    if (glyphs[b]&c) {
+      int len=otf_get_glyph(otf,iA);
+      if (len<0) {
+        assert(0);
+        return -1;
+      } else if (len>0) {
+        glyfSize+=len;
+        len=otf_subset_glyf(otf,iA,glyphs);
+        if (len<0) {
+          assert(0);
+          return -1;
+        }
+        glyfSize+=len;
+      }
+    }
+  }
+
+  int locaSize=((otf->numGlyphs+1)*(otf->indexToLocFormat+1)*2+3)&~3;
+  glyfSize=(glyfSize+3)&~3;
+  // second pass
+  char *new_loca=malloc(locaSize);
+  char *new_glyf=malloc(glyfSize);
+  if ( (!new_loca)||(!new_glyf) ) {
+    fprintf(stderr,"Bad alloc: %m\n");
+    assert(0);
+    free(new_loca);
+    free(new_glyf);
+    return -1;
+  }
+
+  int offset=0;
+  for (iA=0,b=0,c=1;iA<otf->numGlyphs;iA++,c<<=1) {
+    if (!c) {
+      b++;
+      c=1;
+    }
+
+    assert(offset%2==0);
+    // TODO? change format? if glyfSize<0x20000 
+    if (otf->indexToLocFormat==0) {
+      set_USHORT(new_loca+iA*2,offset/2);
+    } else { // ==1
+      set_ULONG(new_loca+iA*4,offset);
+    }
+
+    if (glyphs[b]&c) {
+      const int len=otf_get_glyph(otf,iA);
+      assert(len>=0);
+      memcpy(new_glyf+offset,otf->gly,len);
+      offset+=len;
+    }
+  }
+  // last entry
+  if (otf->indexToLocFormat==0) {
+    set_USHORT(new_loca+otf->numGlyphs*2,offset/2);
+  } else { // ==1
+    set_ULONG(new_loca+otf->numGlyphs*4,offset);
+  }
+  // zero padding
+  assert(glyfSize-offset<4);
+  for (iA=offset;iA<glyfSize;iA++) {
+    new_glyf[iA]=0;
+  }
+  assert(offset<=glyfSize);
+//assert(offset==glyfSize); // else TODO 0 padding
+
+  // copy some tables
+#define MAX_TABLES 11
+  int iB,new_numTables=MAX_TABLES;
+  OTF_DIRENT new_tables[MAX_TABLES]={
+      {OTF_TAG('c','m','a','p'),},
+      {OTF_TAG('c','v','t',' '),},
+      {OTF_TAG('f','p','g','m'),},
+      {OTF_TAG('g','l','y','f'),},
+      {OTF_TAG('h','e','a','d'),},
+      {OTF_TAG('h','h','e','a'),},
+      {OTF_TAG('h','m','t','x'),},
+      {OTF_TAG('l','o','c','a'),},
+      {OTF_TAG('m','a','x','p'),},
+      {OTF_TAG('n','a','m','e'),},
+      {OTF_TAG('p','r','e','p'),}};
+  for (iA=0,iB=0;(iA<otf->numTables)&&(iB<MAX_TABLES);) {
+    if (otf->tables[iA].tag==new_tables[iB].tag) {
+      new_tables[iB].checkSum=otf->tables[iA].checkSum;
+      new_tables[iB].offset=iA;
+      new_tables[iB].length=otf->tables[iA].length;
+      iA++;
+      iB++;
+    } else if (otf->tables[iA].tag<new_tables[iB].tag) {
+      iA++;
+    } else {
+      new_tables[iB].tag=0; // don't output
+      iB++;
+      new_numTables--;
+    }
+  }
+
+  // now we have to know all the checksums and lengths
+  //TODO ? reduce cmap [to (1,0) ;-)]
+  // glyf
+  new_tables[3].checkSum=otf_checksum(new_glyf,glyfSize);
+  new_tables[3].length=offset;
+  // loca
+  new_tables[7].checkSum=otf_checksum(new_loca,locaSize);
+  new_tables[7].length=(otf->numGlyphs+1)*(otf->indexToLocFormat+1)*2;
+
+  // offset table + directory
+  char new_start[12+MAX_TABLES*16];
+  set_ULONG(new_start,0x00010000); // TODO ? true / original value
+  set_USHORT(new_start+4,new_numTables);
+  otf_bsearch_params(new_numTables,16,&iA,&iB,&c); 
+  set_USHORT(new_start+6,iA);
+  set_USHORT(new_start+8,iB);
+  set_USHORT(new_start+10,c);
+
+  unsigned int csum=0;
+  offset=12+16*new_numTables;
+  iB=12;
+  for (iA=0;iA<MAX_TABLES;iA++) {
+    if (!new_tables[iA].tag) {
+      continue;
+    }
+    set_ULONG(new_start+iB,new_tables[iA].tag);
+    set_ULONG(new_start+iB+4,new_tables[iA].checkSum);
+    set_ULONG(new_start+iB+8,offset);
+    set_ULONG(new_start+iB+12,new_tables[iA].length);
+    iB+=16;
+    offset+=(new_tables[iA].length+3)&~3;
+    csum+=new_tables[iA].checkSum;
+  }
+  assert(iB==12+16*new_numTables);
+  (*output)(new_start,iB,context);
+  ret+=iB;
+  csum+=otf_checksum(new_start,iB);
+
+  // now output the tables / copy them
+  iB=12+8;
+  for (iA=0;iA<MAX_TABLES;iA++) {
+    if (!new_tables[iA].tag) {
+      continue;
+    }
+    if (iA==3) { // glyf
+      (*output)(new_glyf,glyfSize,context);
+      ret+=glyfSize;
+    } else if (iA==7) { // loca
+      (*output)(new_loca,locaSize,context);
+      ret+=locaSize;
+    } else { // just copy
+      const OTF_DIRENT *table=otf->tables+new_tables[iA].offset;
+      char *data=otf_read(otf,NULL,table->offset,table->length);
+      assert(data);
+      if (iA==4) { // head. fix global checksum
+        set_ULONG(data+8,0xb1b0afba-csum);
+      }
+      assert(ret==get_ULONG(new_start+iB));
+      (*output)(data,(table->length+3)&~3,context);
+      ret+=(table->length+3)&~3;
+      free(data);
+    }
+    iB+=16;
+  }
+
+  // TODO? suggested ordering
+  // head, hhea, maxp, hmtx, cmap, fpgm, prep, cvt, loca, glyf, name
+
+  // copy some tables [cvt,fpgm,(glyf),head!,hhea,hmtx,(loca),maxp,name(?),prep]
+
+  //TODO (cmap for non-cid)
+
+  free(new_loca);
+  free(new_glyf);
+  return ret;
+#undef MAX_TABLES
+}
+// }}}
+
+int otf_action_copy(void *param,int table_no,OUTPUT_FN output,void *context) // {{{
+{
+  OTF_FILE *otf=param;
+  const OTF_DIRENT *table=otf->tables+table_no;
+
+  if (!output) { // get checksum and unpadded length
+    *(unsigned int *)context=table->checkSum;
+    return table->length;
+  }
+
+  char *data=otf_read(otf,NULL,table->offset,table->length);
+  if (!data) {
+    return -1;
+  }
+  int ret=(table->length+3)&~3;
+  (*output)(data,ret,context);
+  free(data);
+  return ret; // padded length
+}
+// }}}
+
+// TODO? >modified time-stamp?
+int otf_action_copy_head(void *param,int csum,OUTPUT_FN output,void *context) // {{{
+{
+  OTF_FILE *otf=param;
+  const int table_no=otf_find_table(otf,OTF_TAG('h','e','a','d'));
+  assert(table_no!=-1);
+  const OTF_DIRENT *table=otf->tables+table_no;
+
+  if (!output) { // get checksum and unpadded length
+    *(unsigned int *)context=table->checkSum;
+    return table->length;
+  }
+
+  char *data=otf_read(otf,NULL,table->offset,table->length);
+  if (!data) {
+    return -1;
+  }
+  set_ULONG(data+8,0xb1b0afba-csum); // head. fix global checksum
+  int ret=(table->length+3)&~3;
+  (*output)(data,ret,context);
+  free(data);
+  return ret; // padded length
+}
+// }}}
+
+int otf_action_replace(void *param,int length,OUTPUT_FN output,void *context) // {{{
+{
+  char *data=param;
+  char pad[4]={0,0,0,0};
+
+  int ret=(length+3)&~3;
+  if (!output) { // get checksum and unpadded length
+    if (ret!=length) {
+      unsigned int csum=otf_checksum(data,ret-4);
+      memcpy(pad,data+ret-4,ret-length);
+      csum+=get_ULONG(pad);
+      *(unsigned int *)context=csum;
+    } else {
+      *(unsigned int *)context=otf_checksum(data,length);
+    }
+    return length;
+  }
+
+  (*output)(data,length,context);
+  if (ret!=length) {
+    (*output)(pad,ret-length,context);
+  }
+
+  return ret; // padded length
+}
+// }}}
+
+int otf_write_sfnt(struct _OTF_WRITE *otw,unsigned int version,int numTables,OUTPUT_FN output,void *context) // {{{
+{
+  int iA;
+  int ret;
+
+  // find head
+  int headAt=-1;
+  for (iA=0;iA<numTables;iA++) {
+    if ( (otw[iA].tag==OTF_TAG('h','e','a','d'))&&
+         (otw[iA].action==otf_action_copy) ) {
+      // only this supported for now
+      headAt=iA;
+    }
+  }
+
+  // TODO? sort tables 
+
+  char *start=malloc(12+16*numTables);
+  if (!start) {
+    fprintf(stderr,"Bad alloc: %m\n");
+    return -1;
+  }
+
+  // the header
+  set_ULONG(start,version);
+  set_USHORT(start+4,numTables);
+  int a,b,c;
+  otf_bsearch_params(numTables,16,&a,&b,&c); 
+  set_USHORT(start+6,a);
+  set_USHORT(start+8,b);
+  set_USHORT(start+10,c);
+
+  // first pass: calculate table directory / offsets and checksums
+  unsigned int globalSum=0,csum;
+  int pos,res;
+  int offset=12+16*numTables;
+  pos=12;
+  for (iA=0;iA<numTables;iA++) {
+    set_ULONG(start+pos,otw[iA].tag);
+    res=(*otw[iA].action)(otw[iA].param,otw[iA].length,NULL,&csum);
+    assert(res>=0);
+    set_ULONG(start+pos+4,csum);
+    set_ULONG(start+pos+8,offset);
+    set_ULONG(start+pos+12,res);
+    pos+=16;
+    offset+=(res+3)&~3; // padding
+    globalSum+=csum;
+  }
+
+  // second pass: write actual data
+  // write header + directory
+  (*output)(start,pos,context);
+  ret=pos;
+  globalSum+=otf_checksum(start,pos);
+  if (headAt!=-1) {
+    otw[headAt].action=otf_action_copy_head;
+    otw[headAt].length=globalSum;
+  }
+
+  // write tables
+  for (iA=0;iA<numTables;iA++) {
+    assert(ret==get_ULONG(start+12+16*iA+8)); // table directory correct?
+    res=(*otw[iA].action)(otw[iA].param,otw[iA].length,output,context);
+    if (res<0) {
+      free(start);
+      return -1;
+    }
+    assert(((res+3)&~3)==res); // correctly padded?
+    ret+=(res+3)&~3;
+  }
+  assert(offset==ret);
+  free(start);
+
+  return ret;
+}
+// }}}
+
+int otf_ttc_extract(OTF_FILE *otf,OUTPUT_FN output,void *context) // {{{
+{
+  assert(otf);
+  assert(output);
+  assert(otf->numTTC);
+  int iA;
+
+  struct _OTF_WRITE *otw;
+  otw=malloc(sizeof(struct _OTF_WRITE)*otf->numTables);
+  if (!otw) {
+    fprintf(stderr,"Bad alloc: %m\n");
+    return -1;
+  }
+
+  // just copy everything
+  for (iA=0;iA<otf->numTables;iA++) {
+    otw[iA].tag=otf->tables[iA].tag;
+    otw[iA].action=otf_action_copy;
+    otw[iA].param=otf;
+    otw[iA].length=iA;
+  }
+  iA=otf_write_sfnt(otw,otf->version,otf->numTables,output,context);
+  free(otw);
+
+  return iA;
+}
+// }}}
+
+int otf_subset(OTF_FILE *otf,BITSET glyphs,OUTPUT_FN output,void *context) // {{{ - returns number of bytes written
+{
+  assert(otf);
+  assert(glyphs);
+  assert(output);
+
+  int iA,iB,b,c;
+  int ret=0;
+
+  // first pass: include all required glyphs
+  bit_set(glyphs,0); // .notdef always required
+  int glyfSize=0;
+  for (iA=0,b=0,c=1;iA<otf->numGlyphs;iA++,c<<=1) {
+    if (!c) {
+      b++;
+      c=1;
+    }
+    if (glyphs[b]&c) {
+      int len=otf_get_glyph(otf,iA);
+      if (len<0) {
+        assert(0);
+        return -1;
+      } else if (len>0) {
+        glyfSize+=len;
+        len=otf_subset_glyf(otf,iA,glyphs);
+        if (len<0) {
+          assert(0);
+          return -1;
+        }
+        glyfSize+=len;
+      }
+    }
+  }
+
+  // second pass: calculate new glyf and loca
+  int locaSize=(otf->numGlyphs+1)*(otf->indexToLocFormat+1)*2;
+
+  char *new_loca=malloc(locaSize);
+  char *new_glyf=malloc(glyfSize);
+  if ( (!new_loca)||(!new_glyf) ) {
+    fprintf(stderr,"Bad alloc: %m\n");
+    assert(0);
+    free(new_loca);
+    free(new_glyf);
+    return -1;
+  }
+
+  int offset=0;
+  for (iA=0,b=0,c=1;iA<otf->numGlyphs;iA++,c<<=1) {
+    if (!c) {
+      b++;
+      c=1;
+    }
+
+    assert(offset%2==0);
+    // TODO? change format? if glyfSize<0x20000 
+    if (otf->indexToLocFormat==0) {
+      set_USHORT(new_loca+iA*2,offset/2);
+    } else { // ==1
+      set_ULONG(new_loca+iA*4,offset);
+    }
+
+    if (glyphs[b]&c) {
+      const int len=otf_get_glyph(otf,iA);
+      assert(len>=0);
+      memcpy(new_glyf+offset,otf->gly,len);
+      offset+=len;
+    }
+  }
+  // last entry
+  if (otf->indexToLocFormat==0) {
+    set_USHORT(new_loca+otf->numGlyphs*2,offset/2);
+  } else { // ==1
+    set_ULONG(new_loca+otf->numGlyphs*4,offset);
+  }
+  assert(offset==glyfSize);
+
+  // determine new tables.
+#define MAX_TABLES 12
+  int numTables=0;
+  struct _OTF_WRITE otw[MAX_TABLES]={
+      {OTF_TAG('c','m','a','p'),otf_action_copy,otf,},
+      {OTF_TAG('c','v','t',' '),otf_action_copy,otf,},
+      {OTF_TAG('f','p','g','m'),otf_action_copy,otf,},
+      {OTF_TAG('g','l','y','f'),otf_action_replace,new_glyf,glyfSize},
+      {OTF_TAG('h','e','a','d'),otf_action_copy,otf,}, // _copy_head
+      {OTF_TAG('h','h','e','a'),otf_action_copy,otf,},
+      {OTF_TAG('h','m','t','x'),otf_action_copy,otf,},
+      {OTF_TAG('l','o','c','a'),otf_action_replace,new_loca,locaSize},
+      {OTF_TAG('m','a','x','p'),otf_action_copy,otf,},
+      {OTF_TAG('n','a','m','e'),otf_action_copy,otf,},
+      {OTF_TAG('p','r','e','p'),otf_action_copy,otf,},
+      {0,0,0,0}};
+
+  for (iA=0;(iA<otf->numTables)&&(otw[numTables].tag);) {
+    if (otf->tables[iA].tag==otw[numTables].tag) {
+      if (otw[numTables].param==otf) {
+        otw[numTables].length=iA;
+      }
+      iA++;
+      numTables++;
+    } else if (otf->tables[iA].tag<otw[numTables].tag) {
+      iA++;
+    } else {
+      memmove(otw+numTables,otw+numTables+1,sizeof(struct _OTF_WRITE)*(MAX_TABLES-numTables-1)); // don't output
+    } 
+  }
+  // write them
+  ret=otf_write_sfnt(otw,otf->version,numTables,output,context);
+
+  free(new_loca);
+  free(new_glyf);
+  return ret;
+
+  // TODO? suggested ordering
+  // head, hhea, maxp, hmtx, cmap, fpgm, prep, cvt, loca, glyf, name
+  // copy some tables [cvt,fpgm,(glyf),head!,hhea,hmtx,(loca),maxp,name(?),prep]
+
+  //TODO ? reduce cmap [to (1,0) ;-)]
+  //TODO (cmap for non-cid)
+}
+// }}}
+

Added: cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/sfnt.h
==============================================================================
--- (empty file)
+++ cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/sfnt.h	Thu Aug 14 17:18:52 2008
@@ -0,0 +1,64 @@
+#ifndef _SFNT_H
+#define _SFNT_H
+
+#include "bitset.h"
+
+typedef struct {
+  unsigned int tag;
+  unsigned int checkSum;
+  unsigned int offset;
+  unsigned int length;
+} OTF_DIRENT;
+
+typedef struct _IO_FILE FILE;
+
+typedef struct {
+  FILE *f;
+  unsigned int numTTC,useTTC;
+  unsigned int version;
+
+  unsigned short numTables;
+  OTF_DIRENT *tables;
+
+  int flags;
+  unsigned short unitsPerEm;
+  unsigned short indexToLocFormat; // 0=short, 1=long
+  unsigned short numGlyphs;
+
+  // optionally loaded data
+  unsigned int *glyphOffsets;
+  unsigned short numberOfHMetrics;
+  char *hmtx,*name,*cmap;
+  const char *unimap; // ptr to (3,1) or (3,0) cmap start
+  
+  // single glyf buffer, allocated large enough by otf_load_more() 
+  char *gly;
+  OTF_DIRENT *glyfTable;
+
+} OTF_FILE;
+#define OTF_F_FMT_CFF      0x10000
+#define OTF_F_DO_CHECKSUM  0x40000
+
+// to load TTC collections: append e.g. "/3" for the third font in the file.
+OTF_FILE *otf_load(const char *file);
+void otf_close(OTF_FILE *otf);
+
+#define OTF_TAG(a,b,c,d)  (unsigned int)( ((a)<<24)|((b)<<16)|((c)<<8)|(d) )
+#define OTF_UNTAG(a)  (((unsigned int)(a)>>24)&0xff),(((unsigned int)(a)>>16)&0xff),\
+                      (((unsigned int)(a)>>8)&0xff),(((unsigned int)(a))&0xff)
+
+char *otf_get_table(OTF_FILE *otf,unsigned int tag,int *ret_len);
+
+int otf_get_width(OTF_FILE *otf,unsigned short gid);
+const char *otf_get_name(OTF_FILE *otf,int platformID,int encodingID,int languageID,int nameID,int *ret_len);
+int otf_get_glyph(OTF_FILE *otf,unsigned short gid);
+unsigned short otf_from_unicode(OTF_FILE *otf,int unicode);
+
+#ifndef OUTPUT_FN_DECLARED
+#define OUTPUT_FN_DECLARED
+typedef void (*OUTPUT_FN)(const char *buf,int len,void *context);
+#endif
+int otf_subset(OTF_FILE *otf,BITSET glyphs,OUTPUT_FN output,void *context);
+int otf_ttc_extract(OTF_FILE *otf,OUTPUT_FN output,void *context);
+
+#endif

Added: cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/sfnt_int.h
==============================================================================
--- (empty file)
+++ cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/sfnt_int.h	Thu Aug 14 17:18:52 2008
@@ -0,0 +1,79 @@
+#ifndef _SFNT_INT_H
+#define _SFNT_INT_H
+
+static inline unsigned short get_USHORT(const char *buf) // {{{
+{
+  return ((unsigned char)buf[0]<<8)|((unsigned char)buf[1]);
+}
+// }}}
+static inline short get_SHORT(const char *buf) // {{{
+{
+  return (buf[0]<<8)|((unsigned char)buf[1]);
+}
+// }}}
+static inline unsigned int get_UINT24(const char *buf) // {{{
+{
+  return ((unsigned char)buf[0]<<16)|
+         ((unsigned char)buf[1]<<8)|
+         ((unsigned char)buf[2]);
+}
+// }}}
+static inline unsigned int get_ULONG(const char *buf) // {{{
+{
+  return ((unsigned char)buf[0]<<24)|
+         ((unsigned char)buf[1]<<16)|
+         ((unsigned char)buf[2]<<8)|
+         ((unsigned char)buf[3]);
+}
+// }}}
+static inline int get_LONG(const char *buf) // {{{
+{
+  return (buf[0]<<24)|
+         ((unsigned char)buf[1]<<16)|
+         ((unsigned char)buf[2]<<8)|
+         ((unsigned char)buf[3]);
+}
+// }}}
+
+static inline void set_USHORT(char *buf,unsigned short val) // {{{
+{
+  buf[0]=val>>8;
+  buf[1]=val&0xff;
+}
+// }}}
+static inline void set_ULONG(char *buf,unsigned int val) // {{{
+{
+  buf[0]=val>>24;
+  buf[1]=(val>>16)&0xff;
+  buf[2]=(val>>8)&0xff;
+  buf[3]=val&0xff;
+}
+// }}}
+
+static inline int get_width_fast(OTF_FILE *otf,int gid) // {{{
+{
+  if (gid>=otf->numberOfHMetrics) {
+    return get_USHORT(otf->hmtx+(otf->numberOfHMetrics-1)*4);
+  } else {
+    return get_USHORT(otf->hmtx+gid*4);
+  }
+}
+// }}}
+
+int otf_load_glyf(OTF_FILE *otf); //  - 0 on success
+int otf_load_more(OTF_FILE *otf); //  - 0 on success
+
+int otf_action_copy(void *param,int csum,OUTPUT_FN output,void *context);
+int otf_action_copy_head(void *param,int csum,OUTPUT_FN output,void *context);
+int otf_action_replace(void *param,int csum,OUTPUT_FN output,void *context);
+
+struct _OTF_WRITE {
+  unsigned long tag;
+  int (*action)(void *param,int length,OUTPUT_FN output,void *context); // -1 on error, num_bytes_written on success; if >output==NULL return checksum in (unsigned int *)context  instead.
+  void *param;
+  int length;
+};
+
+int otf_write_sfnt(struct _OTF_WRITE *otw,unsigned int version,int numTables,OUTPUT_FN output,void *context);
+
+#endif

Added: cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/test_analyze.c
==============================================================================
--- (empty file)
+++ cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/test_analyze.c	Thu Aug 14 17:18:52 2008
@@ -0,0 +1,205 @@
+#include "sfnt.h"
+#include "sfnt_int.h"
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+enum { WEIGHT_THIN=100,
+       WEIGHT_EXTRALIGHT=200, WEIGHT_ULTRALIGHT=200,
+       WEIGHT_LIGHT=300,
+       WEIGHT_NORMAL=400, WEIGHT_REGULAR=400,
+       WEIGHT_MEDIUM=500,
+       WEIGHT_SEMIBOLD=600, // DEMI
+       WEIGHT_BOLD=700,
+       WEIGHT_EXTRABOLD=800, WEIGHT_ULTRABOLD=800,
+       WEIGHT_BLACK=900, WEIGHT_HEAVY=900 };
+
+void show_post(OTF_FILE *otf) // {{{
+{
+  assert(otf);
+  int len=0;
+  char *buf;
+
+  buf=otf_get_table(otf,OTF_TAG('p','o','s','t'),&len);
+  if (!buf) {
+    assert(len==-1);
+    printf("No post table\n");
+    return;
+  }
+  // TODO: check len
+  printf("POST: (%d bytes)\n"
+         "  version: %08x\n"
+         "  italicAngle: %d.%d\n"
+         "  underlinePosition: %d\n"
+         "  underlineThickness: %d\n"
+         "  isFixedPitch: %d\n"
+         "  vmType42: %d %d\n"
+         "  vmType1: %d %d\n",len,
+         get_ULONG(buf),
+         get_LONG(buf+4)>>16,get_ULONG(buf+4)&0xffff,
+         get_SHORT(buf+8),
+         get_SHORT(buf+10),
+         get_ULONG(buf+12),
+         get_ULONG(buf+16),get_ULONG(buf+20),
+         get_ULONG(buf+24),get_ULONG(buf+38));
+  free(buf);
+}
+// }}}
+
+void show_name(OTF_FILE *otf) // {{{
+{
+  assert(otf);
+  int iA,len=0;
+  char *buf;
+
+  buf=otf_get_table(otf,OTF_TAG('n','a','m','e'),&len);
+  if (!buf) {
+    assert(len==-1);
+    printf("No name table\n");
+    return;
+  }
+  printf("NAME:\n");
+  int name_count=get_USHORT(buf+2);
+  const char *nstore=buf+get_USHORT(buf+4);
+  for (iA=0;iA<name_count;iA++) {
+    const char *nrec=buf+6+12*iA;
+    printf("  { platformID/encodingID/languageID/nameID: %d/%d/%d/%d\n"
+           "    length: %d, offset: %d, data                       :", 
+           get_USHORT(nrec),
+           get_USHORT(nrec+2),
+           get_USHORT(nrec+4),
+           get_USHORT(nrec+6),
+           get_USHORT(nrec+8),
+           get_USHORT(nrec+10));
+    if (  (get_USHORT(nrec)==0)||
+          ( (get_USHORT(nrec)==3) )  ) { // WCHAR
+      int nlen=get_USHORT(nrec+8);
+      int npos=get_USHORT(nrec+10);
+      for (;nlen>0;nlen-=2,npos+=2) {
+        if (nstore[npos]!=0x00) {
+          printf("?");
+        } else {
+          printf("%c",nstore[npos+1]);
+        }
+      }
+      printf(" }\n");
+    } else {
+      printf("%.*s }\n",
+             get_USHORT(nrec+8),nstore+get_USHORT(nrec+10));
+    }
+  }
+  free(buf);
+}
+// }}}
+
+void show_cmap(OTF_FILE *otf) // {{{
+{
+  assert(otf);
+  int iA,len=0;
+
+  char *cmap=otf_get_table(otf,OTF_TAG('c','m','a','p'),&len);
+  if (!cmap) {
+    assert(len==-1);
+    printf("No cmap table\n");
+    return;
+  }
+  printf("cmap:\n");
+  assert(get_USHORT(cmap)==0x0000); // version
+  const int numTables=get_USHORT(cmap+2);
+  printf("  numTables: %d\n",numTables);
+  for (iA=0;iA<numTables;iA++) {
+    const char *nrec=cmap+4+8*iA;
+    const char *ndata=cmap+get_ULONG(nrec+4);
+    assert(ndata>=cmap+4+8*numTables);
+    printf("  platformID/encodingID: %d/%d\n"
+           "  offset: %d  data (format: %d, length: %d, language: %d);\n",
+           get_USHORT(nrec),get_USHORT(nrec+2),
+           get_ULONG(nrec+4),
+           get_USHORT(ndata),get_USHORT(ndata+2),get_USHORT(ndata+4));
+  }
+  free(cmap);
+}
+// }}}
+
+void show_glyf(OTF_FILE *otf,int full) // {{{ 
+{
+  assert(otf);
+
+  // ensure >glyphOffsets and >gly is there
+  if ( (!otf->gly)||(!otf->glyphOffsets) ) {
+    if (otf_load_glyf(otf)!=0) {
+      assert(0);
+      return;
+    }
+  }
+
+  int iA;
+  int compGlyf=0,zeroGlyf=0;
+
+  // {{{ glyf
+  assert(otf->gly);
+  for (iA=0;iA<otf->numGlyphs;iA++) {
+    int len=otf_get_glyph(otf,iA);
+    if (len==0) {
+      zeroGlyf++;
+    } else if (get_SHORT(otf->gly)==-1) {
+      compGlyf++;
+    }
+    if (full) {
+      printf("%d(%d) ",get_SHORT(otf->gly),len);
+    }
+  }
+  if (full) {
+    printf("\n");
+  }
+  printf("numGlyf(nonnull): %d(%d), composites: %d\n",otf->numGlyphs,otf->numGlyphs-zeroGlyf,compGlyf);
+  // }}}
+}
+// }}}
+
+int main(int argc,char **argv)
+{
+  const char *fn="/usr/share/fonts/truetype/microsoft/ARIALN.TTF";
+  if (argc==2) {
+    fn=argv[1];
+  }
+  OTF_FILE *otf=otf_load(fn);
+  assert(otf);
+  if (otf->numTTC) {
+    printf("TTC has %d fonts, using %d\n",otf->numTTC,otf->useTTC);
+  }
+  if (otf->version==0x00010000) {
+    printf("Got TTF 1.0\n");
+  } else if (otf->version==OTF_TAG('O','T','T','O')) {
+    printf("Got OTF(CFF)\n");
+  } else if (otf->version==OTF_TAG('t','r','u','e')) {
+    printf("Got TTF (true)\n");
+  } else if (otf->version==OTF_TAG('t','y','p','1')) {
+    printf("Got SFNT(Type1)\n");
+  }
+
+  printf("Has %d tables\n",otf->numTables);
+
+  int iA;
+  for (iA=0;iA<otf->numTables;iA++) {
+    printf("%c%c%c%c %d\n",OTF_UNTAG(otf->tables[iA].tag),otf->tables[iA].length);
+  }
+  printf("unitsPerEm: %d, indexToLocFormat: %d\n",
+         otf->unitsPerEm,otf->indexToLocFormat);
+  printf("num glyphs: %d\n",otf->numGlyphs);
+  otf_get_width(otf,0); // load table.
+  printf("numberOfHMetrics: %d\n",otf->numberOfHMetrics);
+
+  show_post(otf);
+
+  show_name(otf);
+
+  show_cmap(otf);
+  // printf("%d %d\n",otf_from_unicode(otf,'A'),0);
+
+  show_glyf(otf,1);
+
+  otf_close(otf);
+
+  return 0;
+}

Added: cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/test_pdf.c
==============================================================================
--- (empty file)
+++ cupsys/trunk/debian/local/filters/pdf-filters/filter/fontembed/test_pdf.c	Thu Aug 14 17:18:52 2008
@@ -0,0 +1,222 @@
+#include "embed.h"
+#include "sfnt.h"
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+ 
+struct _FONTFILE { // TODO
+  OTF_FILE *sfnt;
+};
+
+static void example_outfn(const char *buf,int len,void *context) // {{{
+{
+  FILE *f=(FILE *)context;
+  if (fwrite(buf,1,len,f)!=len) {
+    fprintf(stderr,"Short write: %m\n");
+    assert(0);
+    return;
+  }
+}
+// }}}
+
+#define OBJ \
+    xref[xrefpos++]=ftell(f); \
+    fprintf(f,"%d 0 obj\n",xrefpos);
+
+#define ENDOBJ \
+    fprintf(f,"endobj\n");
+
+#define STREAMDICT \
+    OBJ; \
+    fprintf(f,"<<\n" \
+              "  /Length %d 0 R\n",xrefpos+1);
+
+#define STREAMDATA \
+    fprintf(f,">>\n" \
+              "stream\n"); \
+  stream_len=-ftell(f);
+
+#define STREAM \
+  STREAMDICT \
+  STREAMDATA
+
+#define ENDSTREAM \
+  stream_len+=ftell(f); \
+  fprintf(f,"endstream\n" \
+            "endobj\n"); \
+  OBJ; \
+  fprintf(f,"%d\n",stream_len); \
+  ENDOBJ;
+
+static inline void write_string(FILE *f,EMB_PARAMS *emb,const char *str) // {{{
+{
+  assert(f);
+  assert(emb);
+  OTF_FILE *otf=emb->font->sfnt;
+  assert(otf);
+  int iA;
+
+  if (emb->plan&EMB_A_MULTIBYTE) {
+    putc('<',f); 
+    for (iA=0;str[iA];iA++) {
+      const unsigned short gid=otf_from_unicode(otf,(unsigned char)str[iA]);
+      fprintf(f,"%04x",gid);
+      bit_set(emb->subset,gid);
+    }
+    putc('>',f); 
+  } else {
+    putc('(',f); 
+    for (iA=0;str[iA];iA++) {
+      bit_set(emb->subset,otf_from_unicode(otf,(unsigned char)str[iA])); // TODO: emb_set(...) encoding/unicode->gid
+    }
+    fprintf(f,"%s",str); // TODO
+    putc(')',f); 
+  }
+}
+// }}}
+
+int main(int argc,char **argv)
+{
+  const char *fn="/usr/share/fonts/truetype/microsoft/ARIALN.TTF";
+  if (argc==2) {
+    fn=argv[1];
+  }
+  OTF_FILE *otf=otf_load(fn);
+  assert(otf);
+  struct _FONTFILE ff; ff.sfnt=otf; // TODO
+  EMB_PARAMS *emb=emb_new(&ff,
+                          EMB_DEST_PDF16,
+                          EMB_C_FORCE_MULTIBYTE
+);
+
+  FILE *f=fopen("test.pdf","w");
+  assert(f);
+  int xref[100],xrefpos=3;
+  int stream_len;
+
+  assert(emb->subset);
+
+  fprintf(f,"%%PDF-1.3\n");
+  // content
+  STREAM;
+  fprintf(f,"BT\n" // content
+            "  100 100 Td\n"
+            "  /F1 10 Tf\n");
+  write_string(f,emb,"Hallo");
+  fprintf(f," Tj\n"
+            "ET\n");
+  ENDSTREAM;
+
+  bit_set(emb->subset,otf_from_unicode(otf,'a'));
+
+  // {{{ do font
+  EMB_PDF_FONTDESCR *fdes=emb_pdf_fontdescr(emb);
+  assert(fdes);
+  EMB_PDF_FONTWIDTHS *fwid=emb_pdf_fontwidths(emb);
+  assert(fwid);
+
+  STREAMDICT;
+  int ff_ref=xrefpos;
+  if (emb_pdf_get_fontfile_subtype(emb)) {
+    fprintf(f,"  /SubType /%s\n",
+              emb_pdf_get_fontfile_subtype(emb));
+  }
+  if (emb->outtype&EMB_OUTPUT_T1) {
+    fprintf(f,"  /Length1 ?\n"
+              "  /Length2 ?\n"
+              "  /Length3 ?\n");
+  } else {
+    fprintf(f,"  /Length1 %d 0 R\n",xrefpos+2);
+  }
+  STREAMDATA;
+  const int outlen=emb_embed(emb,example_outfn,f);
+  ENDSTREAM;
+  OBJ;
+  fprintf(f,"%d\n",outlen);
+  ENDOBJ;
+
+  OBJ;
+  const int fd_ref=xrefpos;
+  char *res=emb_pdf_simple_fontdescr(emb,fdes,ff_ref);
+  assert(res);
+  fputs(res,f);
+  free(res);
+  ENDOBJ;
+
+  OBJ;
+  int f_ref=xrefpos;
+  res=emb_pdf_simple_font(emb,fdes,fwid,fd_ref);
+  assert(res);
+  fputs(res,f);
+  free(res);
+  ENDOBJ;
+
+  if (emb->plan&EMB_A_MULTIBYTE) {
+    OBJ;
+    res=emb_pdf_simple_cidfont(emb,fdes->fontname,f_ref);
+    f_ref=xrefpos;
+    assert(res);
+    fputs(res,f);
+    free(res);
+    ENDOBJ;
+  }
+
+  free(fdes);
+  free(fwid);
+  // }}}
+
+  int iA;
+
+  xref[2]=ftell(f);
+  fprintf(f,"3 0 obj\n"
+            "<</Type/Page\n"
+            "  /Parent 2 0 R\n"
+            "  /MediaBox [0 0 595 842]\n"
+            "  /Contents 4 0 R\n"
+            "  /Resources <<\n"
+            "    /Font <<\n"
+            "      /F1 %d 0 R\n"
+            "    >>\n"
+            "  >>\n"
+            ">>\n"
+            "endobj\n",
+            f_ref);
+  xref[1]=ftell(f);
+  fprintf(f,"2 0 obj\n"
+            "<</Type/Pages\n"
+            "  /Count 1\n"
+            "  /Kids [3 0 R]"
+            ">>\n"
+            "endobj\n");
+  xref[0]=ftell(f);
+  fprintf(f,"1 0 obj\n"
+            "<</Type/Catalog\n"
+            "  /Pages 2 0 R\n"
+            ">>\n"
+            "endobj\n");
+  // {{{ pdf trailer 
+  int xref_start=ftell(f);
+  fprintf(f,"xref\n"
+            "0 %d\n"
+            "%010d 65535 f \n",
+            xrefpos+1,0);
+  for (iA=0;iA<xrefpos;iA++) {
+    fprintf(f,"%010d 00000 n \n",xref[iA]);
+  }
+  fprintf(f,"trailer\n"
+          "<<\n"
+          "  /Size %d\n"
+          "  /Root 1 0 R\n"
+          ">>\n"
+          "startxref\n"
+          "%d\n"
+          "%%%%EOF\n",
+          xrefpos+1,xref_start);
+  // }}}
+  fclose(f);
+
+  emb_close(emb);
+  otf_close(otf);
+
+  return 0;
+}

Added: cupsys/trunk/debian/local/filters/pdf-filters/filter/pdfutils.c
==============================================================================
--- (empty file)
+++ cupsys/trunk/debian/local/filters/pdf-filters/filter/pdfutils.c	Thu Aug 14 17:18:52 2008
@@ -0,0 +1,411 @@
+/*
+ *   PDF file output routines.
+ *
+ *   Copyright 2008 by Tobias Hoffmann.
+ *
+ *   This file is licensed as noted in "LICENSE.txt" 
+ *   which should have been included with this file.  If this file is
+ *   file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ */
+#include <stdio.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <memory.h>
+#include <stdlib.h>
+#include "pdfutils.h"
+#include "fontembed/embed.h"
+
+void pdfOut_printf(pdfOut *pdf,const char *fmt,...) // {{{
+{
+  assert(pdf);
+  int len;
+  va_list ap;
+
+  va_start(ap,fmt);
+  len=vprintf(fmt,ap);
+  va_end(ap);
+  pdf->filepos+=len;
+}
+// }}}
+
+void pdfOut_putString(pdfOut *pdf,const char *str,int len) // {{{ - >len==-1: strlen()
+{
+  assert(pdf);
+  assert(str);
+  if (len==-1) {
+    len=strlen(str);
+  }
+  putc('(',stdout);
+  // escape special chars: \0 \\ \)
+  int iA=0;
+  for (;len>0;iA++,len--) {
+    if ( (str[iA]<32)||(str[iA]>126) ) {
+      fwrite(str,1,iA,stdout);
+      fprintf(stdout,"\\%03o",str[iA]);
+      pdf->filepos+=iA+4;
+      str+=iA+1;
+      iA=-1;
+    } else if ( (str[iA]==')')||(str[iA]=='\\') ) {
+      fwrite(str,1,iA,stdout);
+      fprintf(stdout,"\\%c",str[iA]);
+      pdf->filepos+=iA+2;
+      str+=iA+1;
+      iA=-1;
+    }
+  }
+  pdf->filepos+=iA+2;
+  fwrite(str,1,iA,stdout);
+  putc(')',stdout);
+}
+// }}}
+
+void pdfOut_putHexString(pdfOut *pdf,const char *str,int len) // {{{ - >len==-1: strlen()
+{
+  assert(pdf);
+  assert(str);
+  if (len==-1) {
+    len=strlen(str);
+  }
+  pdf->filepos+=2*len+2;
+  putc('<',stdout);
+  for (;len>0;str++,len--) {
+    fprintf(stdout,"%02x",(unsigned char)*str);
+  }
+  putc('>',stdout);
+}
+// }}}
+
+pdfOut *pdfOut_new() // {{{ -  NULL on error 
+{
+  pdfOut *ret=malloc(sizeof(pdfOut));
+  if (ret) {
+    memset(ret,0,sizeof(pdfOut));
+  }
+
+  return ret;
+}
+// }}}
+
+// NOTE: uses statically allocated buffer
+const char *pdfOut_to_pdfdate(struct tm *curtm) // {{{
+{
+  static char curdate[250];
+  if (!curtm) {
+    time_t curtime;
+    curtime = time(NULL);
+    curtm   = localtime(&curtime);
+  }
+  strftime(curdate, sizeof(curdate), "D:%Y%m%d%H%M%S%z", curtm);
+  curdate[23]=0;
+  curdate[22]='\'';
+  curdate[21]=curdate[18];
+  curdate[20]=curdate[17];
+  curdate[19]='\'';
+  return curdate;
+}
+// }}}
+
+int pdfOut_add_xref(pdfOut *pdf) // {{{  -  returns obj_no
+{
+  assert(pdf);
+  assert(pdf->xrefsize<=pdf->xrefalloc);
+
+  if (pdf->xrefsize==pdf->xrefalloc) {
+    long *tmp;
+    pdf->xrefalloc+=50;
+    tmp=realloc(pdf->xref,sizeof(long)*pdf->xrefalloc);
+    if (!tmp) {
+      pdf->xrefalloc=-1;
+      return -1;
+    }
+    pdf->xref=tmp;
+  }
+  pdf->xref[pdf->xrefsize++]=pdf->filepos;
+  return pdf->xrefsize; // xrefsize+1
+}
+// }}}
+
+int pdfOut_add_page(pdfOut *pdf,int obj) // {{{ -  returns false on error
+{
+  assert(pdf);
+  assert(obj>0);
+  assert(pdf->pagessize<=pdf->pagesalloc);
+
+  if (pdf->pagessize==pdf->pagesalloc) {
+    int *tmp;
+    pdf->pagesalloc+=10;
+    tmp=realloc(pdf->pages,sizeof(int)*pdf->pagesalloc);
+    if (!tmp) {
+      pdf->pagesalloc=-1;
+      return 0;
+    }
+    pdf->pages=tmp;
+  }
+  pdf->pages[pdf->pagessize++]=obj;
+  return 1;
+}
+// }}}
+
+int pdfOut_add_kv(pdfOut *pdf,const char *key,const char *val) // {{{ -  returns false on error
+{
+  assert(pdf);
+  assert(pdf->kvsize<=pdf->kvalloc);
+
+  if (pdf->kvsize==pdf->kvalloc) {
+    struct keyval_t *tmp;
+    pdf->kvalloc+=10;
+    tmp=realloc(pdf->kv,sizeof(struct keyval_t)*pdf->kvalloc);
+    if (!tmp) {
+      pdf->kvalloc=-1;
+      return 0;
+    }
+    pdf->kv=tmp;
+  }
+  pdf->kv[pdf->kvsize].key=strdup(key);
+  pdf->kv[pdf->kvsize].value=strdup(val);
+  if ( (!pdf->kv[pdf->kvsize].key)||(!pdf->kv[pdf->kvsize].value) ) {
+    return 0;
+  }
+  pdf->kvsize++;
+  return 1;
+}
+// }}}
+
+int pdfOut_begin_pdf(pdfOut *pdf) // ,...output_device?...) // {{{ - false on error
+{
+  assert(pdf);
+  assert(pdf->kvsize==0); // otherwise: finish_pdf has not been called
+  int pages_obj;
+
+  pdf->xrefsize=pdf->pagessize=0;
+  pdf->filepos=0;
+  pages_obj=pdfOut_add_xref(pdf); // fixed later
+  if (pages_obj!=1) {
+    return 0;
+  }
+  pdfOut_printf(pdf,"%%PDF-1.3\n");
+  return 1;
+}
+// }}}
+
+void pdfOut_finish_pdf(pdfOut *pdf) // {{{
+{
+  int iA;
+  int root_obj,info_obj=0,xref_start;
+  assert( (pdf)&&(pdf->filepos!=-1) );
+
+  // pages 
+  const int pages_obj=1;
+  pdf->xref[0]=pdf->filepos; // now fix it
+  pdfOut_printf(pdf,"%d 0 obj\n"
+                    "<</Type/Pages\n"
+                    "  /Count %d\n"
+                    "  /Kids [",
+                    pages_obj,pdf->pagessize);
+  for (iA=0;iA<pdf->pagessize;iA++) {
+    pdfOut_printf(pdf,"%d 0 R ",pdf->pages[iA]);
+  }
+  pdfOut_printf(pdf,"]\n"
+                    ">>\n"
+                    "endobj\n");
+
+  // rootdict
+  root_obj=pdfOut_add_xref(pdf);
+  pdfOut_printf(pdf,"%d 0 obj\n"
+                    "<</Type/Catalog\n"
+                    "  /Pages %d 0 R\n"
+                    ">>\n"
+                    "endobj\n",
+                    root_obj,pages_obj);
+
+  // info 
+  if (pdf->kvsize) {
+    info_obj=pdfOut_add_xref(pdf);
+    pdfOut_printf(pdf,"%d 0 obj\n"
+                      "<<\n",
+                      info_obj);
+    for (iA=0;iA<pdf->kvsize;iA++) {
+      pdfOut_printf(pdf,"  /%s ",pdf->kv[iA].key);
+      pdfOut_putString(pdf,pdf->kv[iA].value,-1);
+      pdfOut_printf(pdf,"\n");
+    }
+    pdfOut_printf(pdf,">>\n"
+                      "endobj\n");
+  }
+  // TODO: some return-value checking (??)
+ 
+  // write xref
+  xref_start=pdf->filepos;
+  pdfOut_printf(pdf,"xref\n"
+                    "%d %d\n"
+                    "%010d 65535 f \n",
+                    0,pdf->xrefsize+1,0);
+  for (iA=0;iA<pdf->xrefsize;iA++) {
+    pdfOut_printf(pdf,"%010d 00000 n \n",
+                      pdf->xref[iA]);
+  }
+  pdfOut_printf(pdf,"trailer\n"
+                    "<<\n"
+                    "  /Size %d\n"
+                    "  /Root %d 0 R\n",
+                    pdf->xrefsize+1,
+                    root_obj);
+  if (info_obj) {
+    pdfOut_printf(pdf,"  /Info %d 0 R\n",info_obj);
+  }
+  pdfOut_printf(pdf,">>\n"
+                    "startxref\n"
+                    "%d\n"
+                    "%%%%EOF\n",
+                    xref_start);
+
+  // set to done
+  pdf->filepos=-1;
+  for (iA=0;iA<pdf->kvsize;iA++) {
+    free(pdf->kv[iA].key);
+    free(pdf->kv[iA].value);
+  }
+  pdf->kvsize=0;
+}
+// }}}
+
+void pdfOut_free(pdfOut *pdf) // {{{
+{
+  if (pdf) {
+    assert(pdf->kvsize==0); // otherwise: finish_pdf has not been called
+    free(pdf->kv);
+    free(pdf->pages);
+    free(pdf->xref);
+    free(pdf);
+  }
+}
+// }}}
+
+static void pdfOut_outfn(const char *buf,int len,void *context) // {{{
+{
+  pdfOut *pdf=(pdfOut *)context;
+
+  if (fwrite(buf,1,len,stdout)!=len) {
+    fprintf(stderr,"Short write: %m\n");
+    assert(0);
+    return;
+  }
+  pdf->filepos+=len;
+}
+// }}}
+
+int pdfOut_write_font(pdfOut *pdf,EMB_PARAMS *emb) // {{{ 
+{
+  assert(pdf);
+  assert(emb);
+
+  EMB_PDF_FONTDESCR *fdes=emb_pdf_fontdescr(emb);
+  if (!fdes) {
+    return 0;
+  }
+
+  const int ff_obj=pdfOut_add_xref(pdf);
+  pdfOut_printf(pdf,"%d 0 obj\n"
+                    "<</Length %d 0 R\n"
+                    "  /Length1 %d 0 R\n"
+                    ,ff_obj,ff_obj+1,ff_obj+2);
+  if (emb_pdf_get_fontfile_subtype(emb)) {
+    pdfOut_printf(pdf,"  /SubType /%s\n",
+                      emb_pdf_get_fontfile_subtype(emb));
+  }
+  if (emb->outtype&EMB_OUTPUT_T1) { // TODO
+    pdfOut_printf(pdf,"  /Length2 ?\n"
+                      "  /Length3 ?\n");
+  }
+  pdfOut_printf(pdf,">>\n"
+                    "stream\n");
+  long streamsize=-pdf->filepos;
+  const int outlen=emb_embed(emb,pdfOut_outfn,pdf);
+  streamsize+=pdf->filepos;
+  pdfOut_printf(pdf,"\nendstream\n"
+                    "endobj\n");
+
+  const int l0_obj=pdfOut_add_xref(pdf);
+  assert(l0_obj==ff_obj+1);
+  pdfOut_printf(pdf,"%d 0 obj\n"
+                    "%d\n"
+                    "endobj\n"
+                    ,l0_obj,streamsize);
+
+  const int l1_obj=pdfOut_add_xref(pdf);
+  assert(l1_obj==ff_obj+2);
+  pdfOut_printf(pdf,"%d 0 obj\n"
+                    "%d\n"
+                    "endobj\n"
+                    ,l1_obj,outlen);
+
+  const int fd_obj=pdfOut_add_xref(pdf);
+  char *res=emb_pdf_simple_fontdescr(emb,fdes,ff_obj);
+  if (!res) {
+    free(fdes);
+    return 0;
+  }
+  pdfOut_printf(pdf,"%d 0 obj\n"
+                    "%s"
+                    "endobj\n"
+                    ,fd_obj,res);
+  free(res);
+
+  EMB_PDF_FONTWIDTHS *fwid=emb_pdf_fontwidths(emb);
+  if (!fwid) {
+    free(fdes);
+    return 0;
+  }
+  const int f_obj=pdfOut_add_xref(pdf);
+  res=emb_pdf_simple_font(emb,fdes,fwid,fd_obj);
+  if (!res) {
+    free(fwid);
+    free(fdes);
+    return 0;
+  }
+  pdfOut_printf(pdf,"%d 0 obj\n"
+                    "%s"
+                    "endobj\n"
+                    ,f_obj,res);
+  free(res);
+  free(fwid);
+
+  if (emb->plan&EMB_A_MULTIBYTE) {
+    res=emb_pdf_simple_cidfont(emb,fdes->fontname,f_obj);
+    if (!res) {
+      free(fdes);
+      return 0;
+    }
+    const int cf_obj=pdfOut_add_xref(pdf);
+    pdfOut_printf(pdf,"%d 0 obj\n"
+                      "%s"
+                      "endobj\n"
+                      ,cf_obj,res);
+    free(res);
+    free(fdes);
+    return cf_obj;
+  }
+
+  free(fdes);
+  return f_obj;
+}
+// }}}
+
+#if 0
+one_page(...parent,resources,mediabox,contents);
+{
+//                    "  /Resources %d 0 R\n"
+  pdfOut_printf(pdf,"%d 0 obj\n"
+                    "<</Type/Page\n"
+                    "  /Parent 1 0 R\n"
+                    "  /MediaBox [0 0 %d %d]\n"
+                    "  /Contents %d 0 R\n"
+                    ">>\n"
+                    "endobj\n"
+                    ,,,PageWidth,PageLength // TODO: into pdf->
+  ...
+}
+
+... pfb_embedder ... pfa?
+#endif

Added: cupsys/trunk/debian/local/filters/pdf-filters/filter/pdfutils.h
==============================================================================
--- (empty file)
+++ cupsys/trunk/debian/local/filters/pdf-filters/filter/pdfutils.h	Thu Aug 14 17:18:52 2008
@@ -0,0 +1,80 @@
+/*
+ *   PDF file output routines.
+ *
+ *   Copyright 2008 by Tobias Hoffmann.
+ *
+ *   This file is licensed as noted in "LICENSE.txt" 
+ *   which should have been included with this file.  If this file is
+ *   file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ */
+#include <time.h>
+
+struct keyval_t {
+  char *key,*value;
+};
+
+typedef struct {
+  long filepos;
+
+  int pagessize,pagesalloc;
+  int *pages;
+
+  int xrefsize,xrefalloc;
+  long *xref;
+
+  int kvsize,kvalloc;
+  struct keyval_t *kv;
+} pdfOut;
+
+/* allocates a new pdfOut structure
+ * returns NULL on error
+ */
+pdfOut *pdfOut_new();
+void pdfOut_free(pdfOut *pdf);
+
+/* start outputting a pdf
+ * returns false on error
+ */
+int pdfOut_begin_pdf(pdfOut *pdf);
+void pdfOut_finish_pdf(pdfOut *pdf);
+
+/* General output routine for our pdf.
+ * Keeps track of characters actually written out
+ */
+void pdfOut_printf(pdfOut *pdf,const char *fmt,...);
+
+/* write out an escaped pdf string: e.g.  (Text \(Test\)\n)
+ * >len==-1: use strlen(str) 
+ */
+void pdfOut_putString(pdfOut *pdf,const char *str,int len);
+void pdfOut_putHexString(pdfOut *pdf,const char *str,int len);
+
+/* Format the broken up timestamp according to
+ * pdf requirements for /CreationDate
+ * NOTE: uses statically allocated buffer 
+ */
+const char *pdfOut_to_pdfdate(struct tm *curtm);
+
+/* begin a new object at current point of the 
+ * output stream and add it to the xref table.
+ * returns the obj number.
+ */
+int pdfOut_add_xref(pdfOut *pdf);
+
+/* adds page dictionary >obj to the global Pages tree
+ * returns false on error
+ */
+int pdfOut_add_page(pdfOut *pdf,int obj);
+
+/* add a >key,>val pair to the document's Info dictionary
+ * returns false on error
+ */
+int pdfOut_add_kv(pdfOut *pdf,const char *key,const char *val);
+
+/* Writes the font >emb including descriptor to the pdf 
+ * and returns the object number.
+ * On error 0 is returned.
+ */
+struct _EMB_PARAMS;
+int pdfOut_write_font(pdfOut *pdf,struct _EMB_PARAMS *emb);

Added: cupsys/trunk/debian/local/filters/pdf-filters/filter/test.sh
==============================================================================
--- (empty file)
+++ cupsys/trunk/debian/local/filters/pdf-filters/filter/test.sh	Thu Aug 14 17:18:52 2008
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+# just for comparison
+/usr/lib/cups/filter/texttops 1 hi_user there_title 1 "" Makefile > test1.ps
+
+# for the next to work, you'll have to make a subdirectory fonts/ here, containing the fonts
+# and a subdirectory charsets/ with a file pdf.utf-8
+export CUPS_DATADIR=`pwd`/
+
+./texttopdf 1 hi_user there_title 1 "" Makefile > test1.pdf
+./texttopdf 1 hi_user there_title 1 "PrettyPrint=1" Makefile > test2.pdf
+(export CONTENT_TYPE=application/x-csource; ./texttopdf 1 hi_user there_title 1 "PrettyPrint=1" test_pdf1.c > test3.pdf)
+(export CHARSET=utf-8; ./texttopdf 1 hi_user there_title 1 "PrettyPrint=1" Makefile > test4.pdf)
+(export CHARSET=utf-8; ./texttopdf 1 hi_user there_title 1 "" testin > test5.pdf)

Added: cupsys/trunk/debian/local/filters/pdf-filters/filter/test_pdf1.c
==============================================================================
--- (empty file)
+++ cupsys/trunk/debian/local/filters/pdf-filters/filter/test_pdf1.c	Thu Aug 14 17:18:52 2008
@@ -0,0 +1,51 @@
+#include "pdfutils.h"
+#include <assert.h>
+
+int main()
+{
+  pdfOut *pdf;
+
+  pdf=pdfOut_new();
+  assert(pdf);
+
+  pdfOut_begin_pdf(pdf);
+
+  // bad font
+  int font_obj=pdfOut_add_xref(pdf);
+  pdfOut_printf(pdf,"%d 0 obj\n"
+                    "<</Type/Font\n"
+                    "  /Subtype /Type1\n" // /TrueType,/Type3
+                    "  /BaseFont /%s\n"
+                    ">>\n"
+                    "endobj\n"
+                    ,font_obj,"Courier");
+  // test
+  const int PageWidth=595,PageLength=842;
+  int cobj=pdfOut_add_xref(pdf);
+  const char buf[]="BT /a 10 Tf (abc) Tj ET";
+  pdfOut_printf(pdf,"%d 0 obj\n"
+                    "<</Length %d\n"
+                    ">>\n"
+                    "stream\n"
+                    "%s\n"
+                    "endstream\n"
+                    "endobj\n"
+                    ,cobj,strlen(buf),buf);
+
+  int obj=pdfOut_add_xref(pdf);
+  pdfOut_printf(pdf,"%d 0 obj\n"
+                    "<</Type/Page\n"
+                    "  /Parent 1 0 R\n"
+                    "  /MediaBox [0 0 %d %d]\n"
+                    "  /Contents %d 0 R\n"
+                    "  /Resources << /Font << /a %d 0 R >> >>\n"
+                    ">>\n"
+                    "endobj\n"
+                    ,obj,PageWidth,PageLength,cobj,font_obj); // TODO: into pdf->
+  pdfOut_add_page(pdf,obj);
+  pdfOut_finish_pdf(pdf);
+
+  pdfOut_free(pdf);
+
+  return 0;
+}

Added: cupsys/trunk/debian/local/filters/pdf-filters/filter/test_pdf2.c
==============================================================================
--- (empty file)
+++ cupsys/trunk/debian/local/filters/pdf-filters/filter/test_pdf2.c	Thu Aug 14 17:18:52 2008
@@ -0,0 +1,108 @@
+#include "pdfutils.h"
+#include <assert.h>
+#include "fontembed/embed.h"
+#include "fontembed/sfnt.h"
+
+#include <stdio.h>
+
+struct _FONTFILE { // TODO
+  OTF_FILE *sfnt;
+};
+
+static inline void write_string(pdfOut *pdf,EMB_PARAMS *emb,const char *str) // {{{
+{
+  assert(pdf);
+  assert(emb);
+  OTF_FILE *otf=emb->font->sfnt;
+  assert(otf);
+  int iA;
+
+  if (emb->plan&EMB_A_MULTIBYTE) {
+    putc('<',stdout); 
+    for (iA=0;str[iA];iA++) {
+      const unsigned short gid=otf_from_unicode(otf,(unsigned char)str[iA]);
+      fprintf(stdout,"%04x",gid);
+      if (emb->subset) {
+        bit_set(emb->subset,gid);
+      }
+    }
+    putc('>',stdout); 
+    pdf->filepos+=4*iA+2;
+  } else { 
+    if (emb->subset) {
+      for (iA=0;str[iA];iA++) {
+        bit_set(emb->subset,otf_from_unicode(otf,(unsigned char)str[iA])); // TODO: emb_set(...) encoding/unicode->gid  // TODO: pdf: otf_from_pdf_default_encoding
+      }
+    }
+    pdfOut_putString(pdf,str,-1);
+  }
+}
+// }}}
+
+int main()
+{
+  pdfOut *pdf;
+
+  pdf=pdfOut_new();
+  assert(pdf);
+
+  pdfOut_begin_pdf(pdf);
+
+  // font, pt.1 
+  const char *fn="/usr/share/fonts/truetype/ttf-bitstream-vera/Vera.ttf";
+/*
+  if (argc==2) {
+    fn=argv[1];
+  }
+*/
+  OTF_FILE *otf=otf_load(fn);
+  assert(otf);
+  struct _FONTFILE ff; ff.sfnt=otf; // TODO
+  EMB_PARAMS *emb=emb_new(&ff,
+                          EMB_DEST_PDF16,
+                          EMB_C_FORCE_MULTIBYTE);
+
+  // test
+  const int PageWidth=595,PageLength=842;
+  const int cobj=pdfOut_add_xref(pdf);
+  pdfOut_printf(pdf,"%d 0 obj\n"
+                    "<</Length %d 0 R\n"
+                    ">>\n"
+                    "stream\n"
+                    ,cobj,cobj+1);
+  long streamlen=-pdf->filepos;
+  pdfOut_printf(pdf,"BT /a 10 Tf ");
+  write_string(pdf,emb,"Test");
+  pdfOut_printf(pdf," Tj ET");
+
+  streamlen+=pdf->filepos;
+  pdfOut_printf(pdf,"\nendstream\n"
+                    "endobj\n");
+  const int clobj=pdfOut_add_xref(pdf);
+  assert(clobj==cobj+1);
+  pdfOut_printf(pdf,"%d 0 obj\n"
+                    "%d\n"
+                    "endobj\n"
+                    ,clobj,streamlen);
+
+  // font
+  int font_obj=pdfOut_write_font(pdf,emb);
+  assert(font_obj);
+
+  int obj=pdfOut_add_xref(pdf);
+  pdfOut_printf(pdf,"%d 0 obj\n"
+                    "<</Type/Page\n"
+                    "  /Parent 1 0 R\n"
+                    "  /MediaBox [0 0 %d %d]\n"
+                    "  /Contents %d 0 R\n"
+                    "  /Resources << /Font << /a %d 0 R >> >>\n"
+                    ">>\n"
+                    "endobj\n"
+                    ,obj,PageWidth,PageLength,cobj,font_obj); // TODO: into pdf->
+  pdfOut_add_page(pdf,obj);
+  pdfOut_finish_pdf(pdf);
+
+  pdfOut_free(pdf);
+
+  return 0;
+}

Added: cupsys/trunk/debian/local/filters/pdf-filters/filter/texttopdf.c
==============================================================================
--- (empty file)
+++ cupsys/trunk/debian/local/filters/pdf-filters/filter/texttopdf.c	Thu Aug 14 17:18:52 2008
@@ -0,0 +1,1148 @@
+/*
+ *   Text to PDF filter for the Common UNIX Printing System (CUPS).
+ *
+ *   Copyright 2008 by Tobias Hoffmann.
+ *   Copyright 2007 by Apple Inc.
+ *   Copyright 1993-2007 by Easy Software Products.
+ *
+ *   These coded instructions, statements, and computer programs are the
+ *   property of Apple Inc. and are protected by Federal copyright
+ *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+ *   which should have been included with this file.  If this file is
+ *   file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ *   This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ *   main()          - Main entry for text to PDF filter.
+ *   WriteEpilogue() - Write the PDF file epilogue.
+ *   WritePage()     - Write a page of text.
+ *   WriteProlog()   - Write the PDF file prolog with options.
+ *   write_line()    - Write a row of text.
+ *   write_string()  - Write a string of text.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "textcommon.h"
+#include <cups/i18n.h>
+#include "pdfutils.h"
+#include "fontembed/embed.h"
+#include <assert.h>
+#include "fontembed/sfnt.h"
+
+// TODO:
+struct _FONTFILE { // TODO
+  OTF_FILE *sfnt;
+  int fobj; // TODO
+};
+EMB_PARAMS *font_load(const char *datadir,const char *font);
+
+static int bits_used(BITSET bits,int len) // {{{
+{
+  len=((len+31)&~31)/8;
+  while (len>0) {
+    if (*bits) {
+      return 1;
+    }
+    bits++;
+    len--;
+  }
+  return 0;
+}
+// }}}
+
+EMB_PARAMS *font_load(const char *datadir,const char *font)
+{
+  char		filename[1024];	/* Glyph filenames */
+  snprintf(filename, sizeof(filename), "%s/fonts/%s", datadir, font);
+
+  OTF_FILE *otf=otf_load(filename);
+  assert(otf);
+  struct _FONTFILE *ff=malloc(sizeof(struct _FONTFILE));
+  assert(ff);
+  ff->sfnt=otf; // TODO
+  ff->fobj=0; // TODO
+  EMB_PARAMS *emb=emb_new(ff,
+                          EMB_DEST_PDF16,
+                          EMB_C_FORCE_MULTIBYTE|
+                          EMB_C_TAKE_FONTFILE);
+  assert(emb);
+  assert(emb->plan&EMB_A_MULTIBYTE);
+  return emb;
+}
+
+/*
+ * Globals...
+ */
+
+int		NumFonts;	/* Number of fonts to use */
+EMB_PARAMS	*Fonts[256][4];	/* Fonts to use */
+unsigned short	Chars[256];	/* Input char to unicode */
+unsigned char	Codes[65536];	/* Unicode glyph mapping to font */
+int		Widths[256];	/* Widths of each font */
+int		Directions[256];/* Text directions for each font */
+pdfOut *pdf;
+int    FontResource;   /* Object number of font resource dictionary */
+float  FontScaleX,FontScaleY;  /* The font matrix */
+lchar_t *Title,*Date;   /* The title and date strings */
+
+/*
+ * Local functions...
+ */
+
+static void	write_line(int row, lchar_t *line);
+static void	write_string(int col, int row, int len, lchar_t *s);
+static lchar_t *make_wide(const char *buf);
+static void     write_font_str(float x,float y,int fontid, lchar_t *str, int len);
+static void     write_pretty_header();
+
+
+/*
+ * 'main()' - Main entry for text to PDF filter.
+ */
+
+int			/* O - Exit status */
+main(int  argc,		/* I - Number of command-line arguments */
+     char *argv[])	/* I - Command-line arguments */
+{
+  return (TextMain("texttopdf", argc, argv));
+}
+
+
+/*
+ * 'WriteEpilogue()' - Write the PDF file epilogue.
+ */
+
+void
+WriteEpilogue(void)
+{
+  static char	*names[] =	/* Font names */
+		{ "FN","FB","FI" };
+  int i,j;
+
+  free(Page[0]);
+  free(Page);
+
+  // embed fonts
+  for (i = PrettyPrint ? 2 : 1; i >= 0; i --) {
+    for (j = 0; j < NumFonts; j ++) 
+    {
+      EMB_PARAMS *emb=Fonts[j][i];
+      if (emb->font->fobj) { // already embedded
+        continue;
+      }
+      if ( (!emb->subset)||(bits_used(emb->subset,emb->font->sfnt->numGlyphs)) ) {
+        emb->font->fobj=pdfOut_write_font(pdf,emb);
+        assert(emb->font->fobj);
+      }
+    }
+  }
+
+  /*
+   * Create the global fontdict
+   */
+
+  // now fix FontResource
+  pdf->xref[FontResource-1]=pdf->filepos;
+  pdfOut_printf(pdf,"%d 0 obj\n"
+                    "<<\n",
+                    FontResource);
+
+  for (i = PrettyPrint ? 2 : 1; i >= 0; i --) {
+    for (j = 0; j < NumFonts; j ++) {
+      EMB_PARAMS *emb=Fonts[j][i];
+      if (emb->font->fobj) { // used
+        pdfOut_printf(pdf,"  /%s%02x %d 0 R\n",names[i],j,emb->font->fobj);
+      }
+    }
+  }
+
+  pdfOut_printf(pdf,">>\n"
+                    "endobj\n");
+
+  pdfOut_finish_pdf(pdf);
+
+  pdfOut_free(pdf);
+}
+
+/*
+ * {{{ 'WritePage()' - Write a page of text.
+ */
+
+void
+WritePage(void)
+{
+  int	line;			/* Current line */
+
+  int content=pdfOut_add_xref(pdf);
+  pdfOut_printf(pdf,"%d 0 obj\n"
+                    "<</Length %d 0 R\n"
+                    ">>\n"
+                    "stream\n"
+                    "q\n"
+                    ,content,content+1);
+  long size=-(pdf->filepos-2);
+
+  NumPages ++;
+  if (PrettyPrint)
+    write_pretty_header(pdf);
+
+  for (line = 0; line < SizeLines; line ++)
+    write_line(line, Page[line]);
+
+  size+=pdf->filepos+2;
+  pdfOut_printf(pdf,"Q\n"
+                    "endstream\n"
+                    "endobj\n");
+  
+  int len_obj=pdfOut_add_xref(pdf);
+  assert(len_obj==content+1);
+  pdfOut_printf(pdf,"%d 0 obj\n"
+                    "%d\n"
+                    "endobj\n",
+                    len_obj,size);
+
+  int obj=pdfOut_add_xref(pdf);
+  pdfOut_printf(pdf,"%d 0 obj\n"
+                    "<</Type/Page\n"
+                    "  /Parent 1 0 R\n"
+                    "  /MediaBox [0 0 %.0f %.0f]\n"
+                    "  /Contents %d 0 R\n"
+                    "  /Resources << /Font %d 0 R >>\n"
+                    ">>\n"
+                    "endobj\n",
+                    obj,PageWidth,PageLength,content,FontResource);
+  pdfOut_add_page(pdf,obj);
+
+  memset(Page[0], 0, sizeof(lchar_t) * SizeColumns * SizeLines);
+}
+// }}}
+
+/* 
+ * {{{'WriteProlog()' - Write the PDF file prolog with options.
+ */
+
+void
+WriteProlog(const char *title,		/* I - Title of job */
+	    const char *user,		/* I - Username */
+            const char *classification,	/* I - Classification */
+	    const char *label,		/* I - Page label */
+            ppd_file_t *ppd)		/* I - PPD file info */
+{
+  int		i, j, k;	/* Looping vars */
+  char		*charset;	/* Character set string */
+  char		filename[1024];	/* Glyph filenames */
+  FILE		*fp;		/* Glyph files */
+  const char	*datadir;	/* CUPS_DATADIR environment variable */
+  char		line[1024],	/* Line from file */
+		*lineptr,	/* Pointer into line */
+		*valptr;	/* Pointer to value in line */
+  int		ch, unicode;	/* Character values */
+  int		start, end;	/* Start and end values for range */
+  time_t	curtime;	/* Current time */
+  struct tm	*curtm;		/* Current date */
+  char		curdate[255];	/* Current date (text format) */
+  int		num_fonts=0;	/* Number of unique fonts */
+  EMB_PARAMS	*fonts[1024];	/* Unique fonts */
+  char		*fontnames[1024];	/* Unique fonts */
+#if 0
+  static char	*names[] =	/* Font names */
+		{
+                  "FN","FB","FI"
+                /*
+		  "cupsNormal",
+		  "cupsBold",
+		  "cupsItalic"
+                */
+		};
+#endif
+
+
+ /*
+  * Get the data directory...
+  */
+
+  if ((datadir = getenv("CUPS_DATADIR")) == NULL)
+    datadir = CUPS_DATADIR;
+
+ /*
+  * Adjust margins as necessary...
+  */
+
+  if (classification || label)
+  {
+   /*
+    * Leave room for labels...
+    */
+
+    PageBottom += 36;
+    PageTop    -= 36;
+  }
+
+ /*
+  * Allocate memory for the page...
+  */
+
+  SizeColumns = (PageRight - PageLeft) / 72.0 * CharsPerInch;
+  SizeLines   = (PageTop - PageBottom) / 72.0 * LinesPerInch;
+
+  Page    = calloc(sizeof(lchar_t *), SizeLines);
+  Page[0] = calloc(sizeof(lchar_t), SizeColumns * SizeLines);
+  for (i = 1; i < SizeLines; i ++)
+    Page[i] = Page[0] + i * SizeColumns;
+
+  if (PageColumns > 1)
+  {
+    ColumnGutter = CharsPerInch / 2;
+    ColumnWidth  = (SizeColumns - ColumnGutter * (PageColumns - 1)) /
+                   PageColumns;
+  }
+  else
+    ColumnWidth = SizeColumns;
+
+ /*
+  * {{{ Output the PDF header...
+  */
+
+  assert(!pdf);
+  pdf=pdfOut_new();
+  assert(pdf);
+
+  pdfOut_begin_pdf(pdf);
+  pdfOut_printf(pdf,"%%cupsRotation: %d\n", (Orientation & 3) * 90); // TODO?
+
+  pdfOut_add_kv(pdf,"Creator","texttopdf/" CUPS_SVERSION);
+
+  curtime = time(NULL);
+  curtm   = localtime(&curtime);
+  strftime(curdate, sizeof(curdate), "%c", curtm);
+
+  pdfOut_add_kv(pdf,"CreationDate",pdfOut_to_pdfdate(curtm));
+  pdfOut_add_kv(pdf,"Title",title);
+  pdfOut_add_kv(pdf,"Author",user); // was(PostScript): /For
+  // }}}
+
+ /*
+  * {{{ Initialize globals...
+  */
+
+  NumFonts = 0;
+  memset(Fonts, 0, sizeof(Fonts));
+  memset(Chars, 0, sizeof(Chars));
+  memset(Codes, 0, sizeof(Codes));
+  // }}}
+
+ /*
+  * Get the output character set...
+  */
+
+  charset = getenv("CHARSET");
+  if (charset != NULL && strcmp(charset, "us-ascii") != 0) // {{{
+  {
+    snprintf(filename, sizeof(filename), "%s/charsets/pdf.%s", datadir, charset);
+
+    if ((fp = fopen(filename, "r")) == NULL)
+    {
+     /*
+      * Can't open charset file!
+      */
+
+      fprintf(stderr, _("ERROR: Unable to open %s: %s\n"), filename,
+              strerror(errno));
+      exit(1);
+    }
+
+   /*
+    * Opened charset file; now see if this is really a charset file...
+    */
+
+    if (fgets(line, sizeof(line), fp) == NULL)
+    {
+     /*
+      * Bad/empty charset file!
+      */
+
+      fclose(fp);
+      fprintf(stderr, _("ERROR: Bad charset file %s\n"), filename);
+      exit(1);
+    }
+
+    if (strncmp(line, "charset", 7) != 0)
+    {
+     /*
+      * Bad format/not a charset file!
+      */
+
+      fclose(fp);
+      fprintf(stderr, _("ERROR: Bad charset file %s\n"), filename);
+      exit(1);
+    }
+
+   /*
+    * See if this is an 8-bit or UTF-8 character set file...
+    */
+
+    line[strlen(line) - 1] = '\0'; /* Drop \n */
+    for (lineptr = line + 7; isspace(*lineptr & 255); lineptr ++); /* Skip whitespace */
+
+    if (strcmp(lineptr, "8bit") == 0) // {{{
+    {
+     /*
+      * 8-bit text...
+      */
+
+      UTF8     = 0;
+      NumFonts = 0;
+
+     /*
+      * Read the font description(s)...
+      */
+
+      while (fgets(line, sizeof(line), fp) != NULL)
+      {
+       /*
+        * Skip comment and blank lines...
+	*/
+
+        if (line[0] == '#' || line[0] == '\n')
+	  continue;
+
+       /*
+	* Read the font descriptions that should look like:
+	*
+	*   first last direction width normal [bold italic bold-italic]
+	*/
+
+	lineptr = line;
+
+        start = strtol(lineptr, &lineptr, 16);
+	end   = strtol(lineptr, &lineptr, 16);
+
+	while (isspace(*lineptr & 255))
+	  lineptr ++;
+
+        if (!*lineptr)
+	  break;	/* Must be a font mapping */
+
+	valptr = lineptr;
+
+	while (!isspace(*lineptr & 255) && *lineptr)
+	  lineptr ++;
+
+	if (!*lineptr)
+	{
+	 /*
+	  * Can't have a font without all required values...
+	  */
+
+	  fprintf(stderr, _("ERROR: Bad font description line: %s\n"), valptr);
+	  fclose(fp);
+	  exit(1);
+	}
+
+	*lineptr++ = '\0';
+
+	if (strcmp(valptr, "ltor") == 0)
+	  Directions[NumFonts] = 1;
+	else if (strcmp(valptr, "rtol") == 0)
+	  Directions[NumFonts] = -1;
+	else
+	{
+	  fprintf(stderr, _("ERROR: Bad text direction %s\n"), valptr);
+	  fclose(fp);
+	  exit(1);
+	}
+
+       /*
+	* Got the direction, now get the width...
+	*/
+
+	while (isspace(*lineptr & 255))
+	  lineptr ++;
+
+	valptr = lineptr;
+
+	while (!isspace(*lineptr & 255) && *lineptr)
+	  lineptr ++;
+
+	if (!*lineptr)
+	{
+	 /*
+	  * Can't have a font without all required values...
+	  */
+
+	  fprintf(stderr, _("ERROR: Bad font description line: %s\n"), valptr);
+	  fclose(fp);
+	  exit(1);
+	}
+
+	*lineptr++ = '\0';
+
+	if (strcmp(valptr, "single") == 0)
+          Widths[NumFonts] = 1;
+	else if (strcmp(valptr, "double") == 0)
+          Widths[NumFonts] = 2;
+	else 
+	{
+	  fprintf(stderr, _("ERROR: Bad text width %s\n"), valptr);
+	  fclose(fp);
+	  exit(1);
+	}
+
+       /*
+	* Get the fonts...
+	*/
+
+	for (i = 0; *lineptr && i < 4; i ++)
+	{
+	  while (isspace(*lineptr & 255))
+	    lineptr ++;
+
+	  valptr = lineptr;
+
+	  while (!isspace(*lineptr & 255) && *lineptr)
+	    lineptr ++;
+
+          if (*lineptr)
+	    *lineptr++ = '\0';
+
+          if (lineptr > valptr) {
+            // search for duplicates
+            for (k = 0; k < num_fonts; k ++)
+              if (strcmp(valptr, fontnames[k]) == 0) {
+	        Fonts[NumFonts][i] = fonts[k];
+                break;
+              }
+
+            if (k==num_fonts) {  // not found
+	      fonts[num_fonts] = Fonts[NumFonts][i] = font_load(datadir,valptr);
+              fontnames[num_fonts++] = strdup(valptr);
+            }
+          }
+	}
+
+       /*
+	* Fill in remaining fonts as needed...
+	*/
+
+	for (j = i; j < 4; j ++)
+	  Fonts[NumFonts][j] = Fonts[NumFonts][0];
+
+       /*
+        * Define the character mappings...
+	*/
+
+	for (i = start; i <= end; i ++)
+	  Codes[i] = NumFonts;
+
+        NumFonts ++;
+      }
+
+     /*
+      * Read encoding lines...
+      */
+
+      do
+      {
+       /*
+        * Skip comment and blank lines...
+	*/
+
+        if (line[0] == '#' || line[0] == '\n')
+	  continue;
+
+       /*
+        * Grab the character and unicode glyph number.
+	*/
+
+	if (sscanf(line, "%x%x", &ch, &unicode) == 2 && ch < 256)
+          Chars[ch] = unicode;
+      }
+      while (fgets(line, sizeof(line), fp) != NULL);
+
+      fclose(fp);
+    } // }}}
+    else if (strcmp(lineptr, "utf8") == 0) // {{{
+    {
+     /*
+      * UTF-8 (Unicode) text...
+      */
+
+      UTF8 = 1;
+
+     /*
+      * Read the font descriptions...
+      */
+
+      NumFonts = 0;
+
+      while (fgets(line, sizeof(line), fp) != NULL)
+      {
+       /*
+        * Skip comment and blank lines...
+	*/
+
+        if (line[0] == '#' || line[0] == '\n')
+	  continue;
+
+       /*
+	* Read the font descriptions that should look like:
+	*
+	*   start end direction width normal [bold italic bold-italic]
+	*/
+
+	lineptr = line;
+
+        start = strtol(lineptr, &lineptr, 16);
+	end   = strtol(lineptr, &lineptr, 16);
+
+	while (isspace(*lineptr & 255))
+	  lineptr ++;
+
+	valptr = lineptr;
+
+	while (!isspace(*lineptr & 255) && *lineptr)
+	  lineptr ++;
+
+	if (!*lineptr)
+	{
+	 /*
+	  * Can't have a font without all required values...
+	  */
+
+	  fprintf(stderr, _("ERROR: Bad font description line: %s\n"), valptr);
+	  fclose(fp);
+	  exit(1);
+	}
+
+	*lineptr++ = '\0';
+
+	if (strcmp(valptr, "ltor") == 0)
+	  Directions[NumFonts] = 1;
+	else if (strcmp(valptr, "rtol") == 0)
+	  Directions[NumFonts] = -1;
+	else
+	{
+	  fprintf(stderr, _("ERROR: Bad text direction %s\n"), valptr);
+	  fclose(fp);
+	  exit(1);
+	}
+
+       /*
+	* Got the direction, now get the width...
+	*/
+
+	while (isspace(*lineptr & 255))
+	  lineptr ++;
+
+	valptr = lineptr;
+
+	while (!isspace(*lineptr & 255) && *lineptr)
+	  lineptr ++;
+
+	if (!*lineptr)
+	{
+	 /*
+	  * Can't have a font without all required values...
+	  */
+
+	  fprintf(stderr, _("ERROR: Bad font description line: %s\n"), valptr);
+	  fclose(fp);
+	  exit(1);
+	}
+
+	*lineptr++ = '\0';
+
+	if (strcmp(valptr, "single") == 0)
+          Widths[NumFonts] = 1;
+	else if (strcmp(valptr, "double") == 0)
+          Widths[NumFonts] = 2;
+	else 
+	{
+	  fprintf(stderr, _("ERROR: Bad text width %s\n"), valptr);
+	  fclose(fp);
+	  exit(1);
+	}
+
+       /*
+	* Get the fonts...
+	*/
+
+	for (i = 0; *lineptr && i < 4; i ++)
+	{
+	  while (isspace(*lineptr & 255))
+	    lineptr ++;
+
+	  valptr = lineptr;
+
+	  while (!isspace(*lineptr & 255) && *lineptr)
+	    lineptr ++;
+
+          if (*lineptr)
+	    *lineptr++ = '\0';
+
+          if (lineptr > valptr) {
+            // search for duplicates
+            for (k = 0; k < num_fonts; k ++)
+              if (strcmp(valptr, fontnames[k]) == 0) {
+	        Fonts[NumFonts][i] = fonts[k];
+                break;
+              }
+
+            if (k==num_fonts) {  // not found
+	      fonts[num_fonts] = Fonts[NumFonts][i] = font_load(datadir,valptr);
+              fontnames[num_fonts++] = strdup(valptr);
+            }
+          }
+	}
+
+       /*
+	* Fill in remaining fonts as needed...
+	*/
+
+	for (j = i; j < 4; j ++)
+	  Fonts[NumFonts][j] = Fonts[NumFonts][0];
+
+       /*
+        * Define the character mappings...
+	*/
+
+	for (i = start; i <= end; i ++)
+	{
+          Codes[i] = NumFonts;
+	}
+
+       /*
+        * Move to the next font, stopping if needed...
+	*/
+
+        NumFonts ++;
+	if (NumFonts >= 256)
+	  break;
+      }
+
+      fclose(fp);
+    } // }}}
+    else // {{{
+    {
+      fprintf(stderr, _("ERROR: Bad charset type %s\n"), lineptr);
+      fclose(fp);
+      exit(1);
+    } // }}}
+  } // }}}
+  else // {{{ Standard ASCII
+  {
+   /*
+    * Standard ASCII output just uses Courier, Courier-Bold, and
+    * possibly Courier-Oblique.
+    */
+
+    NumFonts = 1;
+
+    Fonts[0][ATTR_NORMAL]     = font_load(datadir,"Courier");
+    Fonts[0][ATTR_BOLD]       = font_load(datadir,"Courier-Bold");
+    Fonts[0][ATTR_ITALIC]     = font_load(datadir,"Courier-Oblique");
+    Fonts[0][ATTR_BOLDITALIC] = font_load(datadir,"Courier-BoldOblique");
+
+    Widths[0]     = 1;
+    Directions[0] = 1;
+
+   /*
+    * Define US-ASCII characters...
+    */
+
+    for (i = 32; i < 127; i ++)
+    {
+      Chars[i] = i;
+      Codes[i] = NumFonts-1;
+    }
+  }
+  // }}}
+
+  FontScaleX=120.0 / CharsPerInch;
+  FontScaleY=68.0 / LinesPerInch;
+
+  // allocate now, for pages to use. will be fixed in epilogue
+  FontResource=pdfOut_add_xref(pdf);
+
+  if (PrettyPrint)
+  {
+    Date=make_wide(curdate);
+    Title=make_wide(title);
+  }
+}
+// }}}
+
+/*
+ * {{{ 'write_line()' - Write a row of text.
+ */
+
+static void
+write_line(int     row,		/* I - Row number (0 to N) */
+           lchar_t *line)	/* I - Line to print */
+{
+  int		i;		/* Looping var */
+  int		col,xcol,xwid;		/* Current column */
+  int		attr;		/* Current attribute */
+  int		font,		/* Font to use */
+		lastfont,	/* Last font */
+		mono;		/* Monospaced? */
+  lchar_t	*start;		/* First character in sequence */
+
+
+  xcol=0;
+  for (col = 0, start = line; col < SizeColumns;)
+  {
+    while (col < SizeColumns && (line->ch == ' ' || line->ch == 0))
+    {
+      col ++;
+      xcol ++;
+      line ++;
+    }
+
+    if (col >= SizeColumns)
+      break;
+
+    if (NumFonts == 1)
+    {
+     /*
+      * All characters in a single font - assume monospaced and single width...
+      */
+
+      attr  = line->attr;
+      start = line;
+
+      while (col < SizeColumns && line->ch != 0 && attr == line->attr)
+      {
+	col ++;
+	line ++;
+      }
+
+      write_string(col - (line - start), row, line - start, start);
+    }
+    else
+    {
+     /*
+      * Multiple fonts; break up based on the font...
+      */
+
+      attr     = line->attr;
+      start    = line;
+      xwid     = 0;
+      if (UTF8) {
+        lastfont = Codes[line->ch];
+      } else {
+        lastfont = Codes[Chars[line->ch]];
+      }
+//      mono     = strncmp(Fonts[lastfont][0], "Courier", 7) == 0;
+mono=1; // TODO
+
+      col ++;
+      xwid += Widths[lastfont];
+      line ++;
+
+      if (mono)
+      {
+	while (col < SizeColumns && line->ch != 0 && attr == line->attr)
+	{
+          if (UTF8) {
+            font = Codes[line->ch];
+          } else {
+            font = Codes[Chars[line->ch]];
+          }
+          if (/*strncmp(Fonts[font][0], "Courier", 7) != 0 ||*/ // TODO
+	      font != lastfont)
+	    break;
+
+	  col ++;
+          xwid += Widths[lastfont];
+	  line ++;
+	}
+      }
+
+      if (Directions[lastfont] > 0) {
+        write_string(xcol, row, line - start, start);
+        xcol += xwid;
+      }
+      else
+      {
+       /*
+        * Do right-to-left text... ; assume no font change without direction change
+	*/
+
+	while (col < SizeColumns && line->ch != 0 && attr == line->attr)
+	{
+          if (UTF8) {
+            font = Codes[line->ch];
+          } else {
+            font = Codes[Chars[line->ch]];
+          }
+          if (Directions[font] > 0 &&
+	      !ispunct(line->ch & 255) && !isspace(line->ch & 255))
+	    break;
+
+	  col ++;
+          xwid += Widths[lastfont];
+	  line ++;
+	}
+
+        for (i = 1; start < line; i ++, start ++)
+	  if (!isspace(start->ch & 255)) {
+            xwid-=Widths[lastfont];
+	    write_string(xcol + xwid, row, 1, start);
+          } else {
+            xwid--;
+          }
+      }
+    }
+  }
+}
+// }}}
+
+static lchar_t *make_wide(const char *buf)  // {{{ - convert to lchar_t
+{
+  const unsigned char	*utf8;	/* UTF8 text */
+  lchar_t *ret,*out;
+  
+  // this is enough, utf8 chars will only require less space
+  out=ret=malloc((strlen(buf)+1)*sizeof(lchar_t)); 
+
+  utf8 = (const unsigned char *)buf;
+  while (*utf8)
+  {
+    out->attr=0;
+
+    if (*utf8 < 0xc0 || !UTF8)
+      out->ch = *utf8 ++;
+    else if ((*utf8 & 0xe0) == 0xc0)
+    {
+     /*
+      * Two byte character...
+      */
+
+      out->ch = ((utf8[0] & 0x1f) << 6) | (utf8[1] & 0x3f);
+      utf8 += 2;
+    }
+    else
+    {
+     /*
+      * Three byte character...
+      */
+
+      out->ch = ((((utf8[0] & 0x1f) << 6) | (utf8[1] & 0x3f)) << 6) |
+                (utf8[2] & 0x3f);
+      utf8 += 3;
+    }
+
+    out++;
+  }
+  out->ch=out->attr=0;
+  return ret;
+}
+// }}}
+
+/*
+ * {{{ 'write_string()' - Write a string of text.
+ */
+
+static void
+write_string(int     col,	/* I - Start column */
+             int     row,	/* I - Row */
+             int     len,	/* I - Number of characters */
+             lchar_t *s)	/* I - String to print */
+{
+  float		x, y;		/* Position of text */
+  unsigned	attr;		/* Character attributes */
+
+
+ /*
+  * Position the text and set the font...
+  */
+
+  if (Duplex && (NumPages & 1) == 0)
+  {
+    x = PageWidth - PageRight;
+    y = PageTop;
+  }
+  else
+  {
+    x = PageLeft;
+    y = PageTop;
+  }
+
+  x += (float)col * 72.0f / (float)CharsPerInch;
+  y -= (float)(row + 0.843) * 72.0f / (float)LinesPerInch;
+
+  attr = s->attr;
+
+  if (attr & ATTR_RAISED)
+    y += 36.0 / (float)LinesPerInch;
+  else if (attr & ATTR_LOWERED)
+    y -= 36.0 / (float)LinesPerInch;
+
+  if (attr & ATTR_UNDERLINE)
+    pdfOut_printf(pdf,"q 0.5 w 0 g %.3f %.3f m %.3f %.3f l S Q ",
+                      x, y - 6.8 / LinesPerInch,
+                      x + (float)len * 72.0 / (float)CharsPerInch,
+                      y - 6.8 / LinesPerInch);
+
+  if (PrettyPrint)
+  {
+    if (ColorDevice) {
+      if (attr & ATTR_RED)
+        pdfOut_printf(pdf,"0.5 0 0 rg\n");
+      else if (attr & ATTR_GREEN)
+        pdfOut_printf(pdf,"0 0.5 0 rg\n");
+      else if (attr & ATTR_BLUE)
+        pdfOut_printf(pdf,"0 0 0.5 rg\n");
+      else
+        pdfOut_printf(pdf,"0 g\n");
+    } else {
+      if ( (attr & ATTR_RED)||(attr & ATTR_GREEN)||(attr & ATTR_BLUE) )
+        pdfOut_printf(pdf,"0.2 g\n");
+      else
+        pdfOut_printf(pdf,"0 g\n");
+    }
+  }
+  else
+    pdfOut_printf(pdf,"0 g\n");
+  
+  write_font_str(x,y,attr & ATTR_FONT,s,len);
+}
+// }}}
+
+// {{{ show >len characters from >str, using the right font(s) at >x,>y
+static void write_font_str(float x,float y,int fontid, lchar_t *str, int len)
+{
+  unsigned short		ch;		/* Current character */
+  static char	*names[] =	/* Font names */
+		{ "FN","FB","FI" };
+
+  if (len==-1) {
+    for (len=0;str[len].ch;len++);
+  }
+  pdfOut_printf(pdf,"BT\n");
+
+  if (x == (int)x)
+    pdfOut_printf(pdf,"  %.0f ", x);
+  else
+    pdfOut_printf(pdf,"  %.3f ", x);
+
+  if (y == (int)y)
+    pdfOut_printf(pdf,"%.0f Td\n", y);
+  else
+    pdfOut_printf(pdf,"%.3f Td\n", y);
+
+  int lastfont,font;
+
+  // split on font boundary
+  while (len > 0) 
+  {
+   /*
+    * Write a hex string...
+    */
+    if (UTF8) {
+      lastfont=Codes[str->ch];
+    } else {
+      lastfont=Codes[Chars[str->ch]];
+    }
+    EMB_PARAMS *emb=Fonts[lastfont][fontid];
+    OTF_FILE *otf=emb->font->sfnt;
+
+    pdfOut_printf(pdf,"  %.3f Tz\n",
+                      FontScaleX*600.0/(otf_get_width(otf,0)*1000.0/otf->unitsPerEm)*100.0/FontScaleY); // TODO?
+
+    pdfOut_printf(pdf,"  /%s%02x %.3f Tf <",
+                      names[fontid],lastfont,FontScaleY);
+
+    while (len > 0)
+    {
+      if (UTF8) {
+        ch=str->ch;
+      } else {
+        ch=Chars[str->ch];
+      }
+      const unsigned short gid=otf_from_unicode(otf,ch);
+
+      font = Codes[ch];
+      if (lastfont != font) {
+        assert(0); // should never happen; TODO
+        break;
+      }
+      pdfOut_printf(pdf,"%04x", gid);
+ 
+      if (emb->subset) {
+        bit_set(emb->subset,gid);
+      }
+
+      len --;
+      str ++;
+    }
+
+    pdfOut_printf(pdf,"> Tj\n");
+  }
+  pdfOut_printf(pdf,"ET\n");
+}
+// }}}
+
+static float stringwidth_x(lchar_t *str)
+{
+  int len;
+
+  for (len=0;str[len].ch;len++);
+
+  return  (float)len * 72.0 / (float)CharsPerInch;
+}
+
+static void write_pretty_header() // {{{
+{
+  float x,y;
+  pdfOut_printf(pdf,"q\n"
+                    "0.9 g\n");
+
+  if (Duplex && (NumPages & 1) == 0) {
+    x = PageWidth - PageRight;
+    y = PageTop + 72.0f / LinesPerInch;
+  } else {
+    x = PageLeft;
+    y = PageTop + 72.0f / LinesPerInch;
+  }
+
+  pdfOut_printf(pdf,"1 0 0 1 %.3f %.3f cm\n",x,y); // translate
+  pdfOut_printf(pdf,"0 0 %.3f %.3f re f\n",
+                    PageRight - PageLeft, 144.0f / LinesPerInch);
+  pdfOut_printf(pdf,"0 g 0 G\n");
+
+  if (Duplex && (NumPages & 1) == 0) {
+      x = PageRight - PageLeft - 36.0f / LinesPerInch - stringwidth_x(Title);
+      y = (0.5f + 0.157f) * 72.0f / LinesPerInch;
+  } else {
+      x = 36.0f / LinesPerInch;
+      y = (0.5f + 0.157f) * 72.0f / LinesPerInch;
+  }
+  write_font_str(x,y,ATTR_BOLD,Title,-1);
+
+  x = (-stringwidth_x(Date) + PageRight - PageLeft) * 0.5;
+  write_font_str(x,y,ATTR_BOLD,Date,-1);
+
+  // convert pagenumber to string
+  char tmp[20];
+  tmp[19]=0;
+  snprintf(tmp,19,"%d",NumPages);
+  lchar_t *pagestr=make_wide(tmp);
+
+  if (Duplex && (NumPages & 1) == 0) {
+      x = 36.0f / LinesPerInch;
+  } else {
+      x = PageRight - PageLeft - 36.0f / LinesPerInch - stringwidth_x(pagestr);
+  }
+  write_font_str(x,y,ATTR_BOLD,pagestr,-1);
+
+  pdfOut_printf(pdf,"Q\n");
+}
+// }}}
+

Modified: cupsys/trunk/debian/local/filters/pdf-filters/removefromcups
==============================================================================
--- cupsys/trunk/debian/local/filters/pdf-filters/removefromcups	(original)
+++ cupsys/trunk/debian/local/filters/pdf-filters/removefromcups	Thu Aug 14 17:18:52 2008
@@ -6,11 +6,20 @@
 # Remove files
 rm -f $1/filter/imagetopdf.c
 rm -f $1/filter/pdftoraster.cxx
+rm -f $1/filter/texttopdf.c
+rm -f $1/filter/pdfutils.h
+rm -f $1/filter/pdfutils.c
+rm -rf $1/filter/fontembed
+rm -f $1/filter/test_pdf1.c
+rm -f $1/filter/test_pdf2.c
 rm -f $1/conf/imagetopdf.convs
 rm -f $1/conf/imagetopdf.types
 rm -f $1/conf/pdftopdf.convs
 rm -f $1/conf/pdftoraster.convs
 rm -f $1/conf/pdf.types
+rm -f $1/conf/texttopdf.convs
+rm -f $1/data/pdf.utf-8.heavy
+rm -f $1/data/pdf.utf-8.simple
 rm -f $1/config-scripts/cups-pdf-filters.m4
 
 # Remove directories
@@ -26,5 +35,6 @@
 # Restore Makefiles
 mv -f conf/Makefile.pdf-filters conf/Makefile
 mv -f filter/Makefile.pdf-filters filter/Makefile
+mv -f data/Makefile.pdf-filters data/Makefile
 mv -f Makefile.pdf-filters Makefile
 mv -f Makedefs.in.pdf-filters Makedefs.in

Modified: cupsys/trunk/debian/rules
==============================================================================
--- cupsys/trunk/debian/rules	(original)
+++ cupsys/trunk/debian/rules	Thu Aug 14 17:18:52 2008
@@ -83,10 +83,13 @@
 
 	# Install documentation of the PDF CUPS filters
 	install -m 644 debian/local/filters/pdf-filters/README $(DEB_DESTDIR)/../cups/usr/share/doc/cups/README.pdf-filters
+	install -m 644 debian/local/filters/pdf-filters/filter/test.sh $(DEB_DESTDIR)/../cups/usr/share/doc/cups/examples/texttopdf-text.sh
 	# Move file detection and conversion rules to /usr/share/cups/mime/ so
 	# that the package manager does not consider them conffiles
 	install -d $(DEB_DESTDIR)/../cups/usr/share/cups/mime
-	( cd $(DEB_DESTDIR)/../cups/etc/cups; mv imagetopdf.* pdftopdf.* pdftoraster.* pdf.* $(DEB_DESTDIR)/../cups/usr/share/cups/mime/ )
+	( cd $(DEB_DESTDIR)/../cups/etc/cups; mv imagetopdf.* pdftopdf.* pdftoraster.* pdf.* texttopdf.* $(DEB_DESTDIR)/../cups/usr/share/cups/mime/ )
+	# Configure fonts for texttopdf
+	for f in /usr/share/fonts/truetype/freefont/FreeMono*.ttf; do ln -s $$f $(DEB_DESTDIR)/../cups/usr/share/cups/fonts/; done
 	# Simple Ghostscript-based PostScript-to-PDF filter
 	install -m 0755 debian/filters/pstopdf $(DEB_DESTDIR)/../cups/usr/lib/cups/filter
 
@@ -102,3 +105,7 @@
 binary-post-install/cups-client::
 	rm -r debian/cups-client/usr/share/doc/cups-client
 	ln -s libcups2 debian/cups-client/usr/share/doc/cups-client
+
+binary-post-install/cups-common::
+	# Configure fonts for texttopdf
+	ln -s /usr/share/cups/charsets/pdf.utf-8.simple $(DEB_DESTDIR)/../cups-common/usr/share/cups/charsets/pdf.utf-8



More information about the Pkg-cups-devel mailing list