]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.2.0898: printing support is lacking v9.2.0898
authorFoxe Chen <chen.foxe@gmail.com>
Sun, 2 Aug 2026 18:27:01 +0000 (18:27 +0000)
committerChristian Brabandt <cb@256bit.org>
Sun, 2 Aug 2026 18:33:37 +0000 (18:33 +0000)
Problem:  printing support is lacking
Solution: Improve printing support by making use of pango and cairo
          libraries, use a graphical dialog for :hardcopy in the GTK4 ui
          (Foxe Chen).

fixes:  #8935
closes: #20648

Signed-off-by: Foxe Chen <chen.foxe@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
32 files changed:
Filelist
runtime/doc/options.txt
runtime/doc/print.txt
runtime/doc/tags
runtime/doc/various.txt
runtime/doc/version9.txt
src/Makefile
src/auto/configure
src/config.h.in
src/config.mk.in
src/configure.ac
src/errors.h
src/evalfunc.c
src/evalvars.c
src/feature.h
src/globals.h
src/gui_gtk4.c
src/hardcopy.c
src/hardcopy.h [new file with mode: 0644]
src/hardcopy_pango.c [new file with mode: 0644]
src/hardcopy_pango.h [new file with mode: 0644]
src/hardcopy_postscript.c [new file with mode: 0644]
src/hardcopy_postscript.h [new file with mode: 0644]
src/option.c
src/option.h
src/optiondefs.h
src/proto/gui_gtk4.pro
src/proto/hardcopy.pro
src/structs.h
src/testdir/test_hardcopy.vim
src/version.c
src/vim.h

index 07853f4c690783100cf935140173c795f70a8912..7a2f1219baf1daf23679d9cda8a8aa5e0bd8c189 100644 (file)
--- a/Filelist
+++ b/Filelist
@@ -93,6 +93,11 @@ SRC_ALL =    \
                src/gui.h \
                src/gui_beval.c \
                src/hardcopy.c \
+               src/hardcopy.h \
+               src/hardcopy_pango.c \
+               src/hardcopy_pango.h \
+               src/hardcopy_postscript.c \
+               src/hardcopy_postscript.h \
                src/hashtab.c \
                src/help.c \
                src/highlight.c \
index 647e2a622cdf1681835937a6ac04a258eaf098d9..a9d8f06f534565e8f5053886e467a7f7c524828c 100644 (file)
@@ -1,4 +1,4 @@
-*options.txt*  For Vim version 9.2.  Last change: 2026 Jul 29
+*options.txt*  For Vim version 9.2.  Last change: 2026 Aug 02
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -6900,7 +6900,7 @@ A jump table for the options with a short description can be found at |Q_op|.
 'printexpr' 'pexpr'    string  (default: see below)
                        global
                        {only available when compiled with the |+printer|
-                       and |+postscript| features}
+                       and |+postscript| or |+pango| features}
        Expression used to print the PostScript produced with |:hardcopy|.
        See |pexpr-option|.
        This option cannot be set from a |modeline| or in the |sandbox|, for
index 2e8466482c0e2a8f6d37fbaff21eba5714e7a8c3..392ad268781e5d5084378910ac84b3fe544d5537 100644 (file)
@@ -1,4 +1,4 @@
-*print.txt*    For Vim version 9.2.  Last change: 2026 Feb 14
+*print.txt*    For Vim version 9.2.  Last change: 2026 Aug 02
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -13,7 +13,8 @@ Printing                                              *printing*
 5. PostScript CJK Printing             |postscript-cjk-printing|
 6. PostScript Printing Troubleshooting |postscript-print-trouble|
 7. PostScript Utilities                        |postscript-print-util|
-8. Formfeed Characters                 |printing-formfeed|
+8. Pango Printing                      |pango-printing|
+9. Formfeed Characters                 |printing-formfeed|
 
 {only available when compiled with the |+printer| feature}
 
@@ -33,20 +34,23 @@ Note: If you have problems printing with |:hardcopy|, an alternative is to use
                        Send [range] lines (default whole file) to the
                        printer.
 
-                       On MS-Windows a dialog is displayed to allow selection
-                       of printer, paper size etc.  To skip the dialog, use
-                       the [!].  In this case the printer defined by
-                       'printdevice' is used, or, if 'printdevice' is empty,
-                       the system default printer.
+                       On MS-Windows or GTK4 GUI, a dialog is displayed to
+                       allow selection of printer, paper size etc.  To skip
+                       the dialog, use the [!].  In this case the printer
+                       defined by 'printdevice' is used, or, if 'printdevice'
+                       is empty, the system default printer.  Note that the
+                       GTK4 GUI print dialog requires at least GTK 4.14.
 
-                       For systems other than MS-Windows, PostScript is
-                       written in a temp file and 'printexpr' is used to
+                       For systems other than MS-Windows or Pango, PostScript
+                       is written in a temp file and 'printexpr' is used to
                        actually print it.  Then [arguments] can be used by
                        'printexpr' through |v:cmdarg|.  Otherwise [arguments]
                        is ignored.  'printoptions' can be used to specify
                        paper size, duplex, etc.
                        Note: If you want PDF, there are tools such as
-                       "ps2pdf" that can convert the PostScript to PDF.
+                       "ps2pdf" that can convert the PostScript to PDF.  If
+                       the |+pango| feature is enabled, then emit PDF by
+                       setting "format" to "pdf" in 'printoptions'.
 
 :[range]ha[rdcopy][!] >{filename}
                        As above, but write the resulting PostScript in file
@@ -54,7 +58,7 @@ Note: If you have problems printing with |:hardcopy|, an alternative is to use
                        Things like "%" are expanded |cmdline-special|
                        Careful: An existing file is silently overwritten.
                        {only available when compiled with the |+postscript|
-                       feature}
+                       or |+pango| feature}
                        On MS-Windows use the "print to file" feature of the
                        printer driver.
 
@@ -129,7 +133,7 @@ MS-Windows platform.
                                                        *pexpr-option*
 'printexpr' 'pexpr'    String  (default: see below)
                        global
-Expression that is evaluated to print the PostScript produced with
+Expression that is evaluated to print the PostScript or PDF produced with
 |:hardcopy|.
 The file name to be printed is in |v:fname_in|.
 The arguments to the ":hardcopy" command are in |v:cmdarg|.
@@ -195,7 +199,8 @@ output.  It has the same format as the 'guifont' option, except that only one
 font may be named, and the special "guifont=*" syntax is not available.
 
 In the Win32 GUI version this specifies a font name with its extra attributes,
-as with the 'guifont' option.
+as with the 'guifont' option.  When |+pango| is enabled, the format is the
+same as 'guifont' for the GTK GUI (e.g. "Courier New 10").
 
 For other systems, only ":h11" is recognized, where "11" is the point size of
 the font.  When omitted, the point size is 10.
@@ -397,6 +402,13 @@ of |:hardcopy|:
   formfeed:y           When a form feed character is encountered, continue
                        printing of the current line at the beginning of the
                        first line on a new page.
+  format:postscript (default)
+                       Output format: Postscript
+  format:{name}                Output format from this table (only used when compiled
+                       with |+pango| feature):
+                       {name}          meaning ~
+                       postscript      Generate Postscript
+                       pdf             Generate Adobe PDF
 
 The item indicated with (default) is used when the item is not present.  The
 values are not always used, especially when using a dialog to select the
@@ -436,6 +448,9 @@ There are currently a number of limitations with PostScript printing:
   unknown characters.  Printing will fail if Vim cannot convert the multibyte
   to the 8-bit encoding.
 
+When using the Pango backend for printing, these limitations are solved.  See
+|pango-printing| for more information.
+
 ==============================================================================
 4. Custom 8-bit Print Character Encodings      *postscript-print-encoding*
                                                                *E618* *E619*
@@ -717,9 +732,26 @@ There are a couple of points to bear in mind:
    desktop ink jets than for small office laser printers where the paper path
    is hidden from view.
 
+==============================================================================
+8. Pango Printing                                      *pango-printing*
+
+If Vim is compiled with |+pango|, then it will render pages using Pango (a
+text handling library), and Cairo (a graphics library).  The benefit of this
+is that Unicode is fully supported.  Postscript files will still be generated
+using Cairo.  Additionally, PDF files can be emitted as well, see the
+"format" key in |popt-option|
+
+When using the GTK GUI, |+pango| is always enabled and used.  On terminal
+builds of Vim, "--enable-hardcopy-pango" must be passed to the configure
+options. The linked Cairo library must have the Postscript and PDF backends
+enabled.
+
+                                                       *gtk4-print*
+When using the GTK4 GUI, |:hardcopy| will use a native graphical print dialog,
+similar to MS-Windows.  See |:hardcopy| for more information.
 
 ==============================================================================
-8. Formfeed Characters                                 *printing-formfeed*
+9. Formfeed Characters                                 *printing-formfeed*
 
 By default Vim does not do any special processing of formfeed control
 characters.  Setting the 'printoptions' formfeed item will make Vim recognize
index 9f59289a8ce79fd222b5763ef70038de26456aa6..e83116386d7d5f8b32b9525c66090890cf493361 100644 (file)
@@ -1514,6 +1514,7 @@ $quote    eval.txt        /*$quote*
 +num64 various.txt     /*+num64*
 +ole   various.txt     /*+ole*
 +packages      various.txt     /*+packages*
++pango various.txt     /*+pango*
 +path_extra    various.txt     /*+path_extra*
 +perl  various.txt     /*+perl*
 +perl/dyn      various.txt     /*+perl\/dyn*
@@ -8262,6 +8263,7 @@ gtk-css   gui_x11.txt     /*gtk-css*
 gtk-tooltip-colors     gui_x11.txt     /*gtk-tooltip-colors*
 gtk3-slow      gui_x11.txt     /*gtk3-slow*
 gtk4-menu-navigation   gui_x11.txt     /*gtk4-menu-navigation*
+gtk4-print     print.txt       /*gtk4-print*
 gtk4-slow      gui_x11.txt     /*gtk4-slow*
 gu     change.txt      /*gu*
 gugu   change.txt      /*gugu*
@@ -9717,6 +9719,7 @@ page-up   intro.txt       /*page-up*
 page_down      intro.txt       /*page_down*
 page_up        intro.txt       /*page_up*
 pager  message.txt     /*pager*
+pango-printing print.txt       /*pango-printing*
 papp.vim       syntax.txt      /*papp.vim*
 paragraph      motion.txt      /*paragraph*
 partial        builtin.txt     /*partial*
index 0a5ff259ca6289edbce3f52f001519d078a00522..f0e9acaaee933f8398b35ebbd1b9d20170b5ef5a 100644 (file)
@@ -1,4 +1,4 @@
-*various.txt*  For Vim version 9.2.  Last change: 2026 Jul 29
+*various.txt*  For Vim version 9.2.  Last change: 2026 Aug 02
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -482,6 +482,7 @@ m  *+perl/dyn*              Perl interface |perl-dynamic| |/dyn|
 N  *+persistent_undo*  Persistent undo |undo-persistence|
 N  *+popupwin*         Popup windows |popup-window|
    *+postscript*       |:hardcopy| writes a PostScript file
+   *+pango*            |:hardcopy| uses Pango and Cairo to render pages
 N  *+printer*          |:hardcopy| command
 H  *+profile*          |:profile| command
 m  *+python*           Python 2 interface |python|
index 738e95bdbec312ba3928d66d47ac270d4b74f8d9..4db123621e6473d3f9a09c89aa709f13bb135297 100644 (file)
@@ -1,4 +1,4 @@
-*version9.txt* For Vim version 9.2.  Last change: 2026 Jul 27
+*version9.txt* For Vim version 9.2.  Last change: 2026 Aug 02
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -52590,6 +52590,8 @@ GUI ~
 - Added support for the GTK 4 GUI (via patch 9.2.0501).  Enable with
   `--enable-gui=gtk4` configure switch at build time.
 - Add support for fullscreen mode in the GTK UI |'go-s'|.
+- Enable native printing diaglog for the GTK4 UI using pango library, see
+  |pango-printing|.
 
 Popups ~
 ------
index a3878d0c852c76931a2cf8a01246cd282db49749..a8a046da7f4adf5f8cabf2476ca534fa4f209a2e 100644 (file)
@@ -1417,6 +1417,8 @@ ALL_LIBS = \
           $(X_PRE_LIBS) \
           $(X_LIBS) \
           $(WAYLAND_LIBS) \
+          $(CAIRO_LIBS) \
+          $(PANGO_LIBS) \
           $(X_EXTRA_LIBS) \
           $(MZSCHEME_LIBS) \
           $(LIBS) \
@@ -1543,6 +1545,8 @@ BASIC_SRC = \
        gc.c \
        gui_xim.c \
        hardcopy.c \
+       hardcopy_pango.c \
+       hardcopy_postscript.c \
        hashtab.c \
        help.c \
        highlight.c \
@@ -1729,6 +1733,8 @@ OBJ_COMMON = \
        objects/gc.o \
        objects/gui_xim.o \
        objects/hardcopy.o \
+       objects/hardcopy_pango.o \
+       objects/hardcopy_postscript.o \
        objects/hashtab.o \
        objects/help.o \
        objects/highlight.o \
@@ -3381,6 +3387,12 @@ objects/gc.o: gc.c
 objects/hardcopy.o: hardcopy.c
        $(CCC) -o $@ hardcopy.c
 
+objects/hardcopy_pango.o: hardcopy_pango.c
+       $(CCC) $(CAIRO_CFLAGS) $(CAIRO_CPPFLAGS) $(PANGO_CFLAGS) $(PANGO_CPPFLAGS) -o $@ hardcopy_pango.c
+
+objects/hardcopy_postscript.o: hardcopy_postscript.c
+       $(CCC) -o $@ hardcopy_postscript.c
+
 objects/hashtab.o: hashtab.c
        $(CCC) -o $@ hashtab.c
 
@@ -4097,7 +4109,17 @@ objects/hardcopy.o: auto/osdef.h hardcopy.c vim.h protodef.h auto/config.h featu
  ascii.h keymap.h termdefs.h macros.h option.h beval.h \
  structs.h regexp.h gui.h libvterm/include/vterm.h \
  libvterm/include/vterm_keycodes.h alloc.h ex_cmds.h spell.h proto.h \
- globals.h errors.h version.h
+ globals.h errors.h version.h hardcopy.h hardcopy_pango.h hardcopy_postscript.h
+objects/hardcopy_pango.o: auto/osdef.h hardcopy_pango.c vim.h protodef.h auto/config.h feature.h os_unix.h \
+ ascii.h keymap.h termdefs.h macros.h option.h beval.h \
+ structs.h regexp.h gui.h libvterm/include/vterm.h \
+ libvterm/include/vterm_keycodes.h alloc.h ex_cmds.h spell.h proto.h \
+ globals.h errors.h hardcopy.h hardcopy_pango.h
+objects/hardcopy_postscript.o: auto/osdef.h hardcopy_postscript.c vim.h protodef.h auto/config.h feature.h os_unix.h \
+ ascii.h keymap.h termdefs.h macros.h option.h beval.h \
+ structs.h regexp.h gui.h libvterm/include/vterm.h \
+ libvterm/include/vterm_keycodes.h alloc.h ex_cmds.h spell.h proto.h \
+ globals.h errors.h version.h hardcopy.h hardcopy_postscript.h
 objects/hashtab.o: auto/osdef.h hashtab.c vim.h protodef.h auto/config.h feature.h os_unix.h \
  ascii.h keymap.h termdefs.h macros.h option.h beval.h \
  structs.h regexp.h gui.h libvterm/include/vterm.h \
index 8fd4ba3b7e4ab46bf0ddffbbd091d617b3cf4504..a47cb58d2a44d96c1e63e5a1f7b40ad7b097e027 100755 (executable)
@@ -657,6 +657,12 @@ GUI_LIB_LOC
 GUI_INC_LOC
 NARROW_PROTO
 MOTIF_LIBNAME
+CAIRO_CPPFLAGS
+CAIRO_CFLAGS
+CAIRO_LIBS
+PANGO_CPPFLAGS
+PANGO_CFLAGS
+PANGO_LIBS
 GRESOURCE_OBJ
 GRESOURCE_SRC
 UPDATE_DESKTOP_DATABASE
@@ -870,6 +876,7 @@ with_gnome_libs
 with_gnome
 enable_icon_cache_update
 enable_desktop_database_update
+enable_hardcopy_pango
 with_motif_lib
 with_tlib
 enable_largefile
@@ -1546,6 +1553,7 @@ Optional Features:
   --disable-gtktest       Do not try to compile and run a test GTK program
   --disable-icon-cache-update        update disabled
   --disable-desktop-database-update  update disabled
+  --enable-hardcopy-pango     Use Pango and Cairo for improved hardcopy printing. default=yes when GTK GUI enabled
   --disable-largefile     omit support for large files
   --disable-canberra      Do not use libcanberra.
   --disable-libsodium      Do not use libsodium.
@@ -9241,6 +9249,43 @@ else
 printf "%s\n" "no" >&6; }
 fi
 
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for cairo" >&5
+printf %s "checking for cairo... " >&6; }
+have_cairo="no"
+if "$PKG_CONFIG" --exists 'cairo-ps'; then
+  if "$PKG_CONFIG" --exists 'cairo-pdf'; then
+    have_cairo="yes"
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+
+    CAIRO_CFLAGS=`$PKG_CONFIG --cflags-only-other cairo-ps cairo-pdf`
+    CAIRO_CPPFLAGS=`$PKG_CONFIG --cflags-only-I cairo-ps cairo-pdf`
+    CAIRO_LIBS=`$PKG_CONFIG --libs cairo-ps cairo-pdf`
+  else
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+  fi
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for pango" >&5
+printf %s "checking for pango... " >&6; }
+have_pango="no"
+if "$PKG_CONFIG" --exists 'pango' && "$PKG_CONFIG" --exists 'pangocairo'; then
+  have_pango="yes"
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+
+  PANGO_CFLAGS=`$PKG_CONFIG --cflags-only-other pango pangocairo`
+  PANGO_CPPFLAGS=`$PKG_CONFIG --cflags-only-I pango pangocairo`
+  PANGO_LIBS=`$PKG_CONFIG --libs pango pangocairo`
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
 if test "x$enable_gui" = "xgtk4"; then
   with_x=no
 fi
 
 
 
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking --enable-hardcopy-pango argument" >&5
+printf %s "checking --enable-hardcopy-pango argument... " >&6; }
+# Check whether --enable-hardcopy-pango was given.
+if test ${enable_hardcopy_pango+y}
+then :
+  enableval=$enable_hardcopy_pango;
+fi
+
+
+if test "x$have_pango" = "xno" || test "x$have_cairo" = "xno"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no, Pango or Cairo not found" >&5
+printf "%s\n" "no, Pango or Cairo not found" >&6; }
+else
+  if test "x$GUITYPE" = "xGTK" || test "x$GUITYPE" = "xGTK4"; then
+    enable_hardcopy_pango="yes"
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes, GTK GUI enabled" >&5
+printf "%s\n" "yes, GTK GUI enabled" >&6; }
+  else
+    if test "$enable_hardcopy_pango" = "yes"; then
+      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+    else
+      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+    fi
+  fi
+fi
+
+if test "$enable_hardcopy_pango" = "yes"; then
+  cppflags_save=$CPPFLAGS
+  cflags_save=$CFLAGS
+
+  printf "%s\n" "#define FEAT_PRINT_PANGO 1" >>confdefs.h
+
+
+  CPPFLAGS="$CPPFLAGS $PANGO_CPPFLAGS"
+  CFLAGS="$CFLAGS $PANGO_CFLAGS"
+
+
+
+
+  CPPFLAGS="$CPPFLAGS $CAIRO_CPPFLAGS"
+  CFLAGS="$CFLAGS $CAIRO_CFLAGS"
+
+
+
+
+  CPPFLAGS=$cppflags_save
+  CFLAGS=$cflags_save
+fi
+
+
 
 if test -z "$SKIP_MOTIF"; then
   gui_XXX="/usr/XXX/Motif* /usr/Motif*/XXX /usr/XXX /usr/shlib /usr/X11*/XXX /usr/XXX/X11* /usr/dt/XXX /local/Motif*/XXX /local/XXX/Motif* /usr/local/Motif*/XXX /usr/local/XXX/Motif* /usr/local/XXX /usr/local/X11*/XXX /usr/local/LessTif/Motif*/XXX $MOTIFHOME/XXX"
index d26bd2078e43aff9bd51dfcfcd1ec0d76d79b9b6..2dc3b2decfe701ea7e6c80135e4c144fdce2a977 100644 (file)
 /* Define if GTK GUI is to be linked against GTK 4 */
 #undef USE_GTK4
 
+/* Define to use Pango and Cairo for hardcopy printing */
+#undef FEAT_PRINT_PANGO
+
 /* Define if we have isinf() */
 #undef HAVE_ISINF
 
index a46a140a7143c7124e6f1d92bedd2adb3f10e0ef..06a77aed696e6350b27d413e387450935ac2e734 100644 (file)
@@ -43,6 +43,14 @@ WAYLAND_OBJ          = @WAYLAND_OBJ@
 WAYLAND_CFLAGS     = @WAYLAND_CFLAGS@
 WAYLAND_CPPFLAGS    = @WAYLAND_CPPFLAGS@
 
+CAIRO_LIBS     = @CAIRO_LIBS@
+CAIRO_CFLAGS   = @CAIRO_CFLAGS@
+CAIRO_CPPFLAGS  = @CAIRO_CPPFLAGS@
+
+PANGO_LIBS     = @PANGO_LIBS@
+PANGO_CFLAGS   = @PANGO_CFLAGS@
+PANGO_CPPFLAGS  = @PANGO_CPPFLAGS@
+
 XDIFF_OBJS_USED        = @XDIFF_OBJS_USED@
 
 LUA_LIBS       = @LUA_LIBS@
index 654959874d4e610e42d1878f01868f495f7c2b17..2fdc516372f6352fb272c269aeb9d63a51f4e5be 100644 (file)
@@ -2473,6 +2473,36 @@ else
   AC_MSG_RESULT(no)
 fi
 
+AC_MSG_CHECKING(for cairo)
+have_cairo="no"
+if "$PKG_CONFIG" --exists 'cairo-ps'; then
+  if "$PKG_CONFIG" --exists 'cairo-pdf'; then
+    have_cairo="yes"
+    AC_MSG_RESULT([yes])
+
+    CAIRO_CFLAGS=`$PKG_CONFIG --cflags-only-other cairo-ps cairo-pdf`
+    CAIRO_CPPFLAGS=`$PKG_CONFIG --cflags-only-I cairo-ps cairo-pdf`
+    CAIRO_LIBS=`$PKG_CONFIG --libs cairo-ps cairo-pdf`
+  else
+    AC_MSG_RESULT(no, pdf backend not enabled)
+  fi
+else
+  AC_MSG_RESULT(no, postscript backend not enabled)
+fi
+
+AC_MSG_CHECKING(for pango)
+have_pango="no"
+if "$PKG_CONFIG" --exists 'pango' && "$PKG_CONFIG" --exists 'pangocairo'; then
+  have_pango="yes"
+  AC_MSG_RESULT([yes])
+
+  PANGO_CFLAGS=`$PKG_CONFIG --cflags-only-other pango pangocairo`
+  PANGO_CPPFLAGS=`$PKG_CONFIG --cflags-only-I pango pangocairo`
+  PANGO_LIBS=`$PKG_CONFIG --libs pango pangocairo`
+else
+  AC_MSG_RESULT(no)
+fi
+
 dnl GTK4 does not use X11 APIs directly, so skip X11 detection entirely.
 dnl This avoids pulling libICE/libSM/libX11/libXt into build dependencies
 dnl for GTK4-only builds, even when --with-x=yes is passed (e.g. by a
@@ -3121,6 +3151,49 @@ AC_SUBST(GRESOURCE_OBJ)
 AC_SUBST(GTK_UPDATE_ICON_CACHE)
 AC_SUBST(UPDATE_DESKTOP_DATABASE)
 
+AC_MSG_CHECKING(--enable-hardcopy-pango argument)
+AC_ARG_ENABLE(hardcopy-pango,
+       [  --enable-hardcopy-pango     Use Pango and Cairo for improved hardcopy printing. [default=yes when GTK GUI enabled]],
+       ,, enable_hardcopy_pango="no")
+
+if test "x$have_pango" = "xno" || test "x$have_cairo" = "xno"; then
+  AC_MSG_RESULT([no, Pango or Cairo not found])
+else
+  if test "x$GUITYPE" = "xGTK" || test "x$GUITYPE" = "xGTK4"; then
+    enable_hardcopy_pango="yes"
+    AC_MSG_RESULT([yes, GTK GUI enabled])
+  else
+    if test "$enable_hardcopy_pango" = "yes"; then
+      AC_MSG_RESULT([yes])
+    else
+      AC_MSG_RESULT([no])
+    fi
+  fi
+fi
+
+if test "$enable_hardcopy_pango" = "yes"; then
+  cppflags_save=$CPPFLAGS
+  cflags_save=$CFLAGS
+
+  AC_DEFINE(FEAT_PRINT_PANGO)
+
+  CPPFLAGS="$CPPFLAGS $PANGO_CPPFLAGS"
+  CFLAGS="$CFLAGS $PANGO_CFLAGS"
+  AC_SUBST(PANGO_LIBS)
+  AC_SUBST(PANGO_CFLAGS)
+  AC_SUBST(PANGO_CPPFLAGS)
+
+  CPPFLAGS="$CPPFLAGS $CAIRO_CPPFLAGS"
+  CFLAGS="$CFLAGS $CAIRO_CFLAGS"
+  AC_SUBST(CAIRO_LIBS)
+  AC_SUBST(CAIRO_CFLAGS)
+  AC_SUBST(CAIRO_CPPFLAGS)
+
+  CPPFLAGS=$cppflags_save
+  CFLAGS=$cflags_save
+fi
+
+
 dnl Check for Motif include files location.
 dnl The LAST one found is used, this makes the highest version to be used,
 dnl e.g. when Motif1.2 and Motif2.0 are both present.
index 143e7d7e6cc190a656cff97b2d3bd030fa1f42e8..350ffe704db768259d0723114b02552c6cef4959 100644 (file)
@@ -569,7 +569,7 @@ EXTERN char e_font_str_is_not_fixed_width[]
        INIT(= N_("E236: Font \"%s\" is not fixed-width"));
 # endif
 #endif
-#ifdef MSWIN
+#if defined(MSWIN) || defined(FEAT_PRINT_PANGO)
 EXTERN char e_printer_selection_failed[]
        INIT(= N_("E237: Printer selection failed"));
 EXTERN char e_print_error_str[]
@@ -815,7 +815,7 @@ EXTERN char e_line_number_out_of_range_nr_past_the_end[]
        INIT(= "E322: Line number out of range: %ld past the end");
 EXTERN char e_line_count_wrong_in_block_nr[]
        INIT(= "E323: Line count wrong in block %ld");
-#ifdef FEAT_POSTSCRIPT
+#if defined(FEAT_POSTSCRIPT) || defined(FEAT_PRINT_PANGO)
 EXTERN char e_cant_open_postscript_output_file[]
        INIT(= N_("E324: Can't open PostScript output file"));
 #endif
@@ -915,7 +915,7 @@ EXTERN char e_pattern_uses_more_memory_than_maxmempattern[]
 EXTERN char e_library_call_failed_for_str[]
        INIT(= N_("E364: Library call failed for \"%s()\""));
 #endif
-#ifdef FEAT_POSTSCRIPT
+#if defined(FEAT_POSTSCRIPT) || defined(FEAT_PRINT_PANGO)
 EXTERN char e_failed_to_print_postscript_file[]
        INIT(= N_("E365: Failed to print PostScript file"));
 #endif
@@ -1568,7 +1568,7 @@ EXTERN char e_using_special_as_number[]
 EXTERN char e_too_many_signs_defined[]
        INIT(= N_("E612: Too many signs defined"));
 #endif
-#if defined(MSWIN) && defined(FEAT_PRINTER)
+#if defined(FEAT_PRINTER) && (defined(MSWIN) || defined(FEAT_PRINT_PANGO))
 EXTERN char e_unknown_printer_font_str[]
        INIT(= N_("E613: Unknown printer font: %s"));
 #endif
index bad28de7cc555908e37821df2a5d04c0bb081c69..74ca2716ec8be640f528961f0377db3696877d4d 100644 (file)
@@ -7560,6 +7560,13 @@ f_has(typval_T *argvars, typval_T *rettv)
                1
 #else
                0
+#endif
+               },
+       {"pango",
+#ifdef FEAT_PRINT_PANGO
+               1
+#else
+               0
 #endif
                },
        {"printer",
index 854cb1fc00fac52ba5ad0928bef481d6166ccf50..c08a9d6e08397707d5d2e18f8c07beb31090db53 100644 (file)
@@ -412,7 +412,7 @@ eval_charconvert(
     return OK;
 }
 
-#if defined(FEAT_POSTSCRIPT)
+#if defined(FEAT_POSTSCRIPT) || defined(FEAT_PRINT_PANGO)
     int
 eval_printexpr(char_u *fname, char_u *args)
 {
index 5d5512615b9aa57403ab03129e903284a6dd0ac1..ee7475276cd3d6f40a43eb7bba010c8cc725c97b 100644 (file)
        && !defined(AMIGA)
 # define FEAT_PRINTER
 #endif
-#if defined(FEAT_PRINTER) && ((defined(MSWIN) && defined(MSWINPS)) \
-       || (!defined(MSWIN) && defined(FEAT_EVAL)))
+#if defined(FEAT_PRINTER) && !defined(FEAT_PRINT_PANGO) \
+    && ((defined(MSWIN) && defined(MSWINPS)) \
+           || (!defined(MSWIN) && defined(FEAT_EVAL)))
 # define FEAT_POSTSCRIPT
 #endif
-
-/*
- * +gtk_print          Native GTK print dialog for :hardcopy (GTK4).
- *                     Uses GtkPrintOperation + Pango/Cairo instead of PostScript.
- */
-#if defined(FEAT_PRINTER) && defined(FEAT_GUI_GTK) && defined(USE_GTK4)
-# define FEAT_GUI_GTK_PRINT
+#if !defined(FEAT_PRINTER) && defined(FEAT_PRINT_PANGO)
+# undef FEAT_PRINT_PANGO
 #endif
 
 /*
index 8e0a37494e2fb297e5e7d67d2fd8bc8d937b5331..305725c3e544a4fa6b8c7719bcbdd9d22fb90e41 100644 (file)
@@ -1832,8 +1832,9 @@ extern cursorentry_T shape_table[SHAPE_IDX_COUNT];
 # define OPT_PRINT_COLLATE     11
 # define OPT_PRINT_JOBSPLIT    12
 # define OPT_PRINT_FORMFEED    13
+# define OPT_PRINT_FORMAT      14
 
-# define OPT_PRINT_NUM_OPTIONS 14
+# define OPT_PRINT_NUM_OPTIONS 15
 
 EXTERN option_table_T printer_opts[OPT_PRINT_NUM_OPTIONS]
 # ifdef DO_INIT
@@ -1852,6 +1853,7 @@ EXTERN option_table_T printer_opts[OPT_PRINT_NUM_OPTIONS]
        {"collate",     FALSE, 0, NULL, 0, FALSE},
        {"jobsplit", FALSE, 0, NULL, 0, FALSE},
        {"formfeed", FALSE, 0, NULL, 0, FALSE},
+       {"format", FALSE, 0, NULL, 0, FALSE},
     }
 # endif
     ;
index f5fb4487dfce6d6a80a764121254250b4c616f54..40fa89d896796c71a1cf83b51f232b6ad61e8bfe 100644 (file)
@@ -5043,322 +5043,188 @@ ex_helpfind(exarg_T *eap UNUSED)
     do_cmdline_cmd((char_u *)"emenu ToolBar.FindHelp");
 }
 
-
 /*
  * ============================================================
- * Printing with GtkPrintOperation
+ * Native print dialog
  * ============================================================
  */
-#ifdef FEAT_GUI_GTK_PRINT
 
-typedef struct
-{
-    linenr_T   first_line;     // first line to print (from range)
-    linenr_T   last_line;      // last line to print (from range)
-    int                n_pages;        // total number of pages
-    int                lines_per_page; // lines that fit on one page
-    PangoFontDescription *font_desc;
-    int                do_syntax;      // whether to use syntax highlighting
-    double     line_height;    // line height in points
-    double     char_width;     // character width in points
-} print_data_T;
+#ifdef USE_GTK4_PRINT_DIALOG
+static GtkPrintDialog *print_dialog = NULL;
+static GtkPrintSettings *print_settings = NULL;
+static GtkPageSetup *page_setup = NULL;
+static GtkPrintSetup *print_setup = NULL;
+static GFile *print_file;
 
-/*
- * "begin-print" signal handler.
- * Calculate pagination based on page size and font metrics.
- */
     static void
-print_begin_cb(
-    GtkPrintOperation  *op,
-    GtkPrintContext    *context,
-    gpointer           user_data)
+print_dialog_cb(GtkPrintDialog *dialog, GAsyncResult *result, gboolean *done)
 {
-    print_data_T    *pd = (print_data_T *)user_data;
-    PangoLayout            *layout;
-    PangoFontMetrics *metrics;
-    double         page_height;
-    int                    total_lines;
-
-    page_height = gtk_print_context_get_height(context);
-
-    // Create a PangoLayout to measure font metrics on the print surface.
-    layout = gtk_print_context_create_pango_layout(context);
-    pango_layout_set_font_description(layout, pd->font_desc);
-
-    metrics = pango_context_get_metrics(
-           pango_layout_get_context(layout),
-           pd->font_desc, NULL);
-
-    pd->line_height = (double)(pango_font_metrics_get_ascent(metrics)
-           + pango_font_metrics_get_descent(metrics)) / PANGO_SCALE;
-    pd->char_width = (double)pango_font_metrics_get_approximate_char_width(
-           metrics) / PANGO_SCALE;
-
-    pango_font_metrics_unref(metrics);
-    g_object_unref(layout);
-
-    if (pd->line_height <= 0)
-       pd->line_height = 12.0;
+    GError *error = NULL;
+    print_setup = gtk_print_dialog_setup_finish(dialog, result, &error);
 
-    pd->lines_per_page = (int)(page_height / pd->line_height);
-    if (pd->lines_per_page <= 0)
-       pd->lines_per_page = 1;
-
-    total_lines = (int)(pd->last_line - pd->first_line + 1);
-    pd->n_pages = (total_lines + pd->lines_per_page - 1) / pd->lines_per_page;
-    if (pd->n_pages <= 0)
-       pd->n_pages = 1;
-
-    gtk_print_operation_set_n_pages(op, pd->n_pages);
+    if (print_setup == NULL)
+    {
+       semsg(_(e_print_error_str), error->message);
+       g_error_free(error);
+    }
+    *done = TRUE;
 }
 
 /*
- * "draw-page" signal handler.
- * Render one page of buffer text with optional syntax highlighting.
+ * Return file to write to on success (allocated string), or NULL on failure
+ * (e.g. user cancelled)
  */
-    static void
-print_draw_page_cb(
-    GtkPrintOperation  *op UNUSED,
-    GtkPrintContext    *context,
-    int                        page_nr,
-    gpointer           user_data)
+    char_u *
+gui_gtk4_print_dialog(
+       prt_settings_T  *psettings,
+       char_u          *jobname,
+       double          *page_width,
+       double          *page_height)
 {
-    print_data_T    *pd = (print_data_T *)user_data;
-    cairo_t        *cr;
-    linenr_T       lnum;
-    linenr_T       first;
-    linenr_T       last;
-    double         y;
-
-    cr = gtk_print_context_get_cairo_context(context);
+    GtkPrintDialog  *dialog = gtk_print_dialog_new();
+    gboolean       done = FALSE;
+    char_u         *temp;
 
-    first = pd->first_line + (linenr_T)page_nr * pd->lines_per_page;
-    last = first + pd->lines_per_page - 1;
-    if (last > pd->last_line)
-       last = pd->last_line;
+    gtk_print_dialog_set_title(dialog, (const char *)jobname);
+    gtk_print_dialog_set_modal(dialog, TRUE);
 
-    y = 0;
+    print_dialog = NULL;
+    print_setup = NULL;
 
-    for (lnum = first; lnum <= last; ++lnum)
+    if (print_settings == NULL)
     {
-       char_u          *line;
-       PangoLayout     *layout;
-       PangoAttrList   *attr_list;
+       option_table_T *opt;
 
-       line = ml_get(lnum);
-       layout = gtk_print_context_create_pango_layout(context);
-       pango_layout_set_font_description(layout, pd->font_desc);
+       print_settings = gtk_print_settings_new();
 
-       attr_list = pango_attr_list_new();
-
-# ifdef FEAT_SYN_HL
-       if (pd->do_syntax && syntax_present(curwin))
+       opt = &printer_opts[OPT_PRINT_DUPLEX];
+       if (opt->present)
        {
-           colnr_T     col;
-           int         prev_syn_id = -1;
-           int         attr_start = 0;
-           long_u      prev_fg = 0;
-           int         prev_bold = FALSE;
-           int         prev_italic = FALSE;
-           int         len = (int)STRLEN(line);
-
-           for (col = 0; col < len; )
-           {
-               int         id;
-               int         outputlen;
-               long_u      fg_color;
-               int         is_bold;
-               int         is_italic;
-
-               if (has_mbyte)
-               {
-                   outputlen = (*mb_ptr2len)(line + col);
-                   if (outputlen < 1)
-                       outputlen = 1;
-               }
-               else
-                   outputlen = 1;
-
-               id = syn_get_id(curwin, lnum, col, 1, NULL, FALSE);
-               if (id > 0)
-                   id = syn_get_final_id(id);
-               else
-                   id = 0;
-               // syn_get_id may invalidate the line pointer.
-               line = ml_get(lnum);
-
-               fg_color = highlight_gui_color_rgb(id, TRUE);
-               is_bold = (highlight_has_attr(id, HL_BOLD, 'g') != NULL);
-               is_italic = (highlight_has_attr(id, HL_ITALIC, 'g') != NULL);
-
-               // When attributes change, flush the previous run.
-               if (id != prev_syn_id && col > 0)
-               {
-                   if (prev_fg != 0 && prev_fg != (long_u)0xffffffL)
-                   {
-                       PangoAttribute *a = pango_attr_foreground_new(
-                               (guint16)(((prev_fg >> 16) & 0xff) * 257),
-                               (guint16)(((prev_fg >> 8) & 0xff) * 257),
-                               (guint16)((prev_fg & 0xff) * 257));
-                       a->start_index = attr_start;
-                       a->end_index = col;
-                       pango_attr_list_insert(attr_list, a);
-                   }
-                   if (prev_bold)
-                   {
-                       PangoAttribute *a = pango_attr_weight_new(
-                               PANGO_WEIGHT_BOLD);
-                       a->start_index = attr_start;
-                       a->end_index = col;
-                       pango_attr_list_insert(attr_list, a);
-                   }
-                   if (prev_italic)
-                   {
-                       PangoAttribute *a = pango_attr_style_new(
-                               PANGO_STYLE_ITALIC);
-                       a->start_index = attr_start;
-                       a->end_index = col;
-                       pango_attr_list_insert(attr_list, a);
-                   }
-                   attr_start = col;
-               }
-
-               prev_syn_id = id;
-               prev_fg = fg_color;
-               prev_bold = is_bold;
-               prev_italic = is_italic;
-
-               col += outputlen;
-           }
+           if (STRNICMP(opt->string, "off", 3) == 0)
+               gtk_print_settings_set_duplex(print_settings,
+                       GTK_PRINT_DUPLEX_SIMPLEX);
+           else if (STRNICMP(opt->string, "short", 5) == 0)
+               gtk_print_settings_set_duplex(print_settings,
+                       GTK_PRINT_DUPLEX_VERTICAL);
+           else
+               gtk_print_settings_set_duplex(print_settings,
+                       GTK_PRINT_DUPLEX_HORIZONTAL);
+       }
 
-           // Flush the last run.
-           if (attr_start < len)
-           {
-               if (prev_fg != 0 && prev_fg != (long_u)0xffffffL)
-               {
-                   PangoAttribute *a = pango_attr_foreground_new(
-                           (guint16)(((prev_fg >> 16) & 0xff) * 257),
-                           (guint16)(((prev_fg >> 8) & 0xff) * 257),
-                           (guint16)((prev_fg & 0xff) * 257));
-                   a->start_index = attr_start;
-                   a->end_index = len;
-                   pango_attr_list_insert(attr_list, a);
-               }
-               if (prev_bold)
-               {
-                   PangoAttribute *a = pango_attr_weight_new(
-                           PANGO_WEIGHT_BOLD);
-                   a->start_index = attr_start;
-                   a->end_index = len;
-                   pango_attr_list_insert(attr_list, a);
-               }
-               if (prev_italic)
-               {
-                   PangoAttribute *a = pango_attr_style_new(
-                           PANGO_STYLE_ITALIC);
-                   a->start_index = attr_start;
-                   a->end_index = len;
-                   pango_attr_list_insert(attr_list, a);
-               }
-           }
+       opt = &printer_opts[OPT_PRINT_PORTRAIT];
+       if (opt->present)
+       {
+           if (*opt->string == 'y')
+               gtk_print_settings_set_orientation(print_settings,
+                       GTK_PAGE_ORIENTATION_PORTRAIT);
+           else
+               gtk_print_settings_set_orientation(print_settings,
+                       GTK_PAGE_ORIENTATION_LANDSCAPE);
        }
-# endif // FEAT_SYN_HL
 
-       pango_layout_set_attributes(layout, attr_list);
+       opt = &printer_opts[OPT_PRINT_PAPER];
+       if (opt->present)
+       {
+           gtk_print_settings_set_paper_size(print_settings,
+                   gtk_paper_size_new((const char *)opt->string));
+       }
 
-       // Expand tabs. Use a tab array matching Vim's tabstop.
+       opt = &printer_opts[OPT_PRINT_COLLATE];
+       if (opt->present)
        {
-           PangoTabArray *tabs;
-           int tab_width = (int)(curbuf->b_p_ts * pd->char_width);
-
-           if (tab_width <= 0)
-               tab_width = (int)(8 * pd->char_width);
-           tabs = pango_tab_array_new(1, TRUE);
-           pango_tab_array_set_tab(tabs, 0, PANGO_TAB_LEFT, tab_width);
-           pango_layout_set_tabs(layout, tabs);
-           pango_tab_array_free(tabs);
+           if (*opt->string == 'y')
+               gtk_print_settings_set_collate(print_settings, TRUE);
+           else
+               gtk_print_settings_set_collate(print_settings, FALSE);
        }
+    }
 
-       pango_layout_set_text(layout, (const char *)line, -1);
+    if (page_setup == NULL)
+       page_setup = gtk_page_setup_new();
 
-       cairo_move_to(cr, 0, y);
-       pango_cairo_show_layout(cr, layout);
+    gtk_print_dialog_set_print_settings(dialog, print_settings);
+    gtk_print_dialog_set_page_setup(dialog, page_setup);
 
-       pango_attr_list_unref(attr_list);
-       g_object_unref(layout);
+    gtk_print_dialog_setup(dialog, GTK_WINDOW(gui.mainwin), NULL,
+           (GAsyncReadyCallback)print_dialog_cb, &done);
 
-       y += pd->line_height;
+    while (!done)
+       g_main_context_iteration(NULL, TRUE);
+
+    if (print_setup == NULL)
+    {
+       g_object_unref(dialog);
+       g_clear_pointer(&print_setup, gtk_print_setup_unref);
+
+       psettings->user_abort_msg = FALSE;
+       return NULL;
     }
-}
 
-/*
- * Main entry point for GTK4 native printing.
- * Called from ex_hardcopy() when running in a GTK4 GUI.
- */
-    void
-gui_gtk4_hardcopy(exarg_T *eap)
-{
-    GtkPrintOperation  *op;
-    GtkPrintOperationResult res;
-    print_data_T       pd;
-    char_u             *font_name;
+    g_clear_object(&print_settings);
+    g_clear_object(&page_setup);
+    print_settings = g_object_ref(
+           gtk_print_setup_get_print_settings(print_setup));
+    page_setup = g_object_ref(gtk_print_setup_get_page_setup(print_setup));
 
-    static GtkPrintSettings *settings = NULL;
+    {
+       GtkPrintDuplex duplex = gtk_print_settings_get_duplex(print_settings);
+       psettings->duplex = duplex != GTK_PRINT_DUPLEX_SIMPLEX;
+    }
 
-    CLEAR_FIELD(pd);
-    pd.first_line = eap->line1;
-    pd.last_line = eap->line2;
+    psettings->has_color = gtk_print_settings_get_use_color(print_settings);
 
-    // Use 'printfont' if set, otherwise fall back to 'guifont'.
-    font_name = *p_pfn != NUL ? p_pfn : p_guifont;
-    if (font_name == NULL || *font_name == NUL)
-       font_name = (char_u *)"Monospace 10";
+    *page_width = gtk_page_setup_get_paper_width(page_setup,
+           GTK_UNIT_POINTS);
+    *page_height = gtk_page_setup_get_paper_height(page_setup,
+           GTK_UNIT_POINTS);
 
-    pd.font_desc = pango_font_description_from_string((const char *)font_name);
-    if (pd.font_desc == NULL)
+    temp  = vim_tempname('p', FALSE);
+    if (temp == NULL)
     {
-       semsg(_(e_unknown_font_str), font_name);
-       return;
-    }
+       emsg(_(e_cant_find_temp_file_for_writing));
+       g_clear_object(&dialog);
+       g_clear_pointer(&print_setup, gtk_print_setup_unref);
 
-    // Ensure the font description has a size (default 10pt if missing).
-    if (pango_font_description_get_size(pd.font_desc) == 0)
-       pango_font_description_set_size(pd.font_desc, 10 * PANGO_SCALE);
+       psettings->user_abort_msg = FALSE;
+       return NULL;
+    }
+    print_file = g_file_new_for_path((char *)temp);
 
-# ifdef FEAT_SYN_HL
-    pd.do_syntax = syntax_present(curwin);
-# endif
+    print_dialog = dialog;
 
-    op = gtk_print_operation_new();
+    return temp;
+}
 
-    if (settings != NULL)
-       gtk_print_operation_set_print_settings(op, settings);
+    static void
+print_file_cb(
+       GtkPrintDialog  *dialog,
+       GAsyncResult    *result,
+       GFile           *file)
+{
+    char *path = g_file_get_path(file);
 
-    gtk_print_operation_set_job_name(op,
-           curbuf->b_fname != NULL
-               ? (const char *)curbuf->b_fname : "Vim");
-    gtk_print_operation_set_show_progress(op, TRUE);
-    gtk_print_operation_set_unit(op, GTK_UNIT_POINTS);
+    (void)gtk_print_dialog_print_file_finish(dialog, result, NULL);
+    g_object_unref(dialog);
 
-    g_signal_connect(op, "begin-print", G_CALLBACK(print_begin_cb), &pd);
-    g_signal_connect(op, "draw-page", G_CALLBACK(print_draw_page_cb), &pd);
+    // Don't use g_file_delete() to be consistent
+    mch_remove(path);
+    g_free(path);
+}
 
-    res = gtk_print_operation_run(op,
-           GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
-           GTK_WINDOW(gui.mainwin), NULL);
 
-    if (res == GTK_PRINT_OPERATION_RESULT_APPLY)
+    void
+gui_gtk4_print_finish(void)
+{
+    if (print_dialog != NULL)
     {
-       if (settings != NULL)
-           g_object_unref(settings);
-       settings = g_object_ref(
-               gtk_print_operation_get_print_settings(op));
+       gtk_print_dialog_print_file(
+               print_dialog, GTK_WINDOW(gui.mainwin),
+               print_setup, print_file, NULL,
+               (GAsyncReadyCallback)print_file_cb, print_file);
+       print_dialog = NULL;
+       g_clear_pointer(&print_setup, gtk_print_setup_unref);
     }
-
-    g_object_unref(op);
-    pango_font_description_free(pd.font_desc);
 }
-
-#endif // FEAT_GUI_GTK_PRINT
+#endif // USE_GTK4_PRINT_DIALOG
 
 #endif // FEAT_GUI_GTK
index 10c3beb8f1444893cd8417bc75dfeee4ab833d0b..d7f2298950056e2a74837ae2cacb295273ae5651 100644 (file)
 #include "version.h"
 
 #if defined(FEAT_PRINTER)
+
+#include "hardcopy.h"
+#ifdef FEAT_POSTSCRIPT
+# include "hardcopy_postscript.h"
+#endif
+#ifdef FEAT_PRINT_PANGO
+# include "hardcopy_pango.h"
+#endif
+
 /*
  * To implement printing on a platform, the following functions must be
  * defined:
@@ -92,35 +101,12 @@ static const long_u  cterm_color_16[16] =
 static int             current_syn_id;
 #endif
 
-#define PRCOLOR_BLACK  (long_u)0
-#define PRCOLOR_WHITE  (long_u)0xFFFFFFL
-
-static int     curr_italic;
-static int     curr_bold;
-static int     curr_underline;
-static long_u  curr_bg;
-static long_u  curr_fg;
-static int     page_count;
-
-#if defined(FEAT_POSTSCRIPT)
-# define OPT_MBFONT_USECOURIER  0
-# define OPT_MBFONT_ASCII       1
-# define OPT_MBFONT_REGULAR     2
-# define OPT_MBFONT_BOLD       3
-# define OPT_MBFONT_OBLIQUE     4
-# define OPT_MBFONT_BOLDOBLIQUE 5
-# define OPT_MBFONT_NUM_OPTIONS 6
-
-static option_table_T mbfont_opts[OPT_MBFONT_NUM_OPTIONS] =
-{
-    {"c",      FALSE, 0, NULL, 0, FALSE},
-    {"a",      FALSE, 0, NULL, 0, FALSE},
-    {"r",      FALSE, 0, NULL, 0, FALSE},
-    {"b",      FALSE, 0, NULL, 0, FALSE},
-    {"i",      FALSE, 0, NULL, 0, FALSE},
-    {"o",      FALSE, 0, NULL, 0, FALSE},
-};
-#endif
+int    prt_curr_italic;
+int    prt_curr_bold;
+int    prt_curr_underline;
+long_u prt_curr_bg;
+long_u prt_curr_fg;
+int    prt_page_count;
 
 /*
  * These values determine the print position on a page.
@@ -135,6 +121,25 @@ typedef struct
     int                ff;                 // seen form feed character
 } prt_pos_T;
 
+#if defined(FEAT_POSTSCRIPT) || defined(FEAT_PRINT_PANGO)
+prt_mediasize_T prt_mediasize[PRT_MEDIASIZE_LEN] = {
+    {"A4",             595.0,  842.0},
+    {"letter", 612.0,  792.0},
+    {"10x14",  720.0, 1008.0},
+    {"A3",             842.0, 1191.0},
+    {"A5",             420.0,  595.0},
+    {"B4",             729.0, 1032.0},
+    {"B5",             516.0,  729.0},
+    {"executive",      522.0,  756.0},
+    {"folio",  595.0,  935.0},
+    {"ledger", 1224.0,  792.0},
+    {"legal",  612.0, 1008.0},
+    {"quarto", 610.0,  780.0},
+    {"statement",      396.0,  612.0},
+    {"tabloid",        792.0, 1224.0}
+};
+#endif
+
 static char *parse_list_options(char_u *option_str, option_table_T *table, int table_size);
 
 static colnr_T hardcopy_line(prt_settings_T *psettings, int page_line, prt_pos_T *ppos);
@@ -334,34 +339,34 @@ prt_get_attr(
     static void
 prt_set_fg(long_u fg)
 {
-    if (fg == curr_fg)
+    if (fg == prt_curr_fg)
        return;
 
-    curr_fg = fg;
+    prt_curr_fg = fg;
     mch_print_set_fg(fg);
 }
 
     static void
 prt_set_bg(long_u bg)
 {
-    if (bg == curr_bg)
+    if (bg == prt_curr_bg)
        return;
 
-    curr_bg = bg;
+    prt_curr_bg = bg;
     mch_print_set_bg(bg);
 }
 
     static void
 prt_set_font(int bold, int italic, int underline)
 {
-    if (curr_bold == bold
-           && curr_italic == italic
-           && curr_underline == underline)
+    if (prt_curr_bold == bold
+           && prt_curr_italic == italic
+           && prt_curr_underline == underline)
        return;
 
-    curr_underline = underline;
-    curr_italic = italic;
-    curr_bold = bold;
+    prt_curr_underline = underline;
+    prt_curr_italic = italic;
+    prt_curr_bold = bold;
     mch_print_set_font(bold, italic, underline);
 }
 
@@ -539,7 +544,7 @@ prt_header(
 /*
  * Display a print status message.
  */
-    static void
+    void
 prt_message(char_u *s)
 {
     screen_fill((int)Rows - 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0);
@@ -559,18 +564,11 @@ ex_hardcopy(exarg_T *eap)
 
     CLEAR_FIELD(settings);
     settings.has_color = TRUE;
-
-#ifdef FEAT_GUI_GTK_PRINT
-    // Use the native GTK print dialog only for interactive printing;
-    // ":hardcopy >file" must fall through to the PostScript writer.
-    if (gui.in_use && *eap->arg != '>')
-    {
-       gui_gtk4_hardcopy(eap);
-       return;
-    }
+#ifdef FEAT_PRINT_PANGO
+    settings.user_abort_msg = TRUE;
 #endif
 
-#ifdef FEAT_POSTSCRIPT
+#if defined(FEAT_POSTSCRIPT) || defined(FEAT_PRINT_PANGO)
     if (*eap->arg == '>')
     {
        char    *errormsg = NULL;
@@ -659,11 +657,11 @@ ex_hardcopy(exarg_T *eap)
     }
 
     // Set colors and font to normal.
-    curr_bg = (long_u)0xffffffffL;
-    curr_fg = (long_u)0xffffffffL;
-    curr_italic = MAYBE;
-    curr_bold = MAYBE;
-    curr_underline = MAYBE;
+    prt_curr_bg = (long_u)0xffffffffL;
+    prt_curr_fg = (long_u)0xffffffffL;
+    prt_curr_italic = MAYBE;
+    prt_curr_bold = MAYBE;
+    prt_curr_underline = MAYBE;
 
     prt_set_fg(PRCOLOR_BLACK);
     prt_set_bg(PRCOLOR_WHITE);
@@ -681,7 +679,7 @@ ex_hardcopy(exarg_T *eap)
     /*
      * Loop over collated copies: 1 2 3, 1 2 3, ...
      */
-    page_count = 0;
+    prt_page_count = 0;
     for (collated_copies = 0;
            collated_copies < settings.n_collated_copies;
            collated_copies++)
@@ -705,7 +703,7 @@ ex_hardcopy(exarg_T *eap)
        /*
         * Loop over all pages in the print job: 1 2 3 ...
         */
-       for (page_count = 0; prtpos.file_line <= eap->line2; ++page_count)
+       for (prt_page_count = 0; prtpos.file_line <= eap->line2; ++prt_page_count)
        {
            /*
             * Loop over uncollated copies: 1 1 1, 2 2 2, 3 3 3, ...
@@ -733,7 +731,7 @@ ex_hardcopy(exarg_T *eap)
                        goto print_fail;
 
                    sprintf((char *)IObuff, _("Printing page %d (%d%%)"),
-                           page_count + 1 + side,
+                           prt_page_count + 1 + side,
                            prtpos.bytes_printed > 1000000
                                ? (int)(prtpos.bytes_printed /
                                                       (bytes_to_print / 100))
@@ -753,7 +751,7 @@ ex_hardcopy(exarg_T *eap)
                     * Output header if required
                     */
                    if (prt_header_height() > 0)
-                       prt_header(&settings, page_count + 1 + side,
+                       prt_header(&settings, prt_page_count + 1 + side,
                                                        prtpos.file_line);
 
                    for (page_line = 0; page_line < settings.lines_per_page;
@@ -796,7 +794,7 @@ ex_hardcopy(exarg_T *eap)
                }
            }
            if (settings.duplex && prtpos.file_line <= eap->line2)
-               ++page_count;
+               ++prt_page_count;
 
            // Remember the position where the next page starts.
            page_prtpos = prtpos;
@@ -808,7 +806,11 @@ ex_hardcopy(exarg_T *eap)
     }
 
 print_fail:
-    if (got_int || settings.user_abort)
+    if (got_int || (settings.user_abort
+#ifdef FEAT_PRINT_PANGO
+               && settings.user_abort_msg
+#endif
+               ))
     {
        sprintf((char *)IObuff, "%s", _("Printing aborted"));
        prt_message(IObuff);
@@ -950,2519 +952,4 @@ hardcopy_line(
     return col;
 }
 
-#if defined(FEAT_POSTSCRIPT)
-
-/*
- * PS printer stuff.
- *
- * Sources of information to help maintain the PS printing code:
- *
- * 1. PostScript Language Reference, 3rd Edition,
- *      Addison-Wesley, 1999, ISBN 0-201-37922-8
- * 2. PostScript Language Program Design,
- *      Addison-Wesley, 1988, ISBN 0-201-14396-8
- * 3. PostScript Tutorial and Cookbook,
- *      Addison Wesley, 1985, ISBN 0-201-10179-3
- * 4. PostScript Language Document Structuring Conventions Specification,
- *    version 3.0,
- *      Adobe Technote 5001, 25th September 1992
- * 5. PostScript Printer Description File Format Specification, Version 4.3,
- *      Adobe technote 5003, 9th February 1996
- * 6. Adobe Font Metrics File Format Specification, Version 4.1,
- *      Adobe Technote 5007, 7th October 1998
- * 7. Adobe CMap and CIDFont Files Specification, Version 1.0,
- *      Adobe Technote 5014, 8th October 1996
- * 8. Adobe CJKV Character Collections and CMaps for CID-Keyed Fonts,
- *      Adoboe Technote 5094, 8th September, 2001
- * 9. CJKV Information Processing, 2nd Edition,
- *      O'Reilly, 2002, ISBN 1-56592-224-7
- *
- * Some of these documents can be found in PDF form on Adobe's web site -
- * http://www.adobe.com
- */
-
-# define PRT_PS_DEFAULT_DPI        (72)    // Default user space resolution
-# define PRT_PS_DEFAULT_FONTSIZE     (10)
-# define PRT_PS_DEFAULT_BUFFER_SIZE  (80)
-
-struct prt_mediasize_S
-{
-    char       *name;
-    float      width;          // width and height in points for portrait
-    float      height;
-};
-
-# define PRT_MEDIASIZE_LEN  ARRAY_LENGTH(prt_mediasize)
-
-static struct prt_mediasize_S prt_mediasize[] =
-{
-    {"A4",             595.0,  842.0},
-    {"letter",         612.0,  792.0},
-    {"10x14",          720.0, 1008.0},
-    {"A3",             842.0, 1191.0},
-    {"A5",             420.0,  595.0},
-    {"B4",             729.0, 1032.0},
-    {"B5",             516.0,  729.0},
-    {"executive",      522.0,  756.0},
-    {"folio",          595.0,  935.0},
-    {"ledger",        1224.0,  792.0},   // Yes, it is wider than taller!
-    {"legal",          612.0, 1008.0},
-    {"quarto",         610.0,  780.0},
-    {"statement",      396.0,  612.0},
-    {"tabloid",                792.0, 1224.0}
-};
-
-// PS font names, must be in Roman, Bold, Italic, Bold-Italic order
-struct prt_ps_font_S
-{
-    int                wx;
-    int                uline_offset;
-    int                uline_width;
-    int                bbox_min_y;
-    int                bbox_max_y;
-    char       *(ps_fontname[4]);
-};
-
-# define PRT_PS_FONT_ROMAN     (0)
-# define PRT_PS_FONT_BOLD      (1)
-# define PRT_PS_FONT_OBLIQUE   (2)
-# define PRT_PS_FONT_BOLDOBLIQUE (3)
-
-// Standard font metrics for Courier family
-static struct prt_ps_font_S prt_ps_courier_font =
-{
-    600,
-    -100, 50,
-    -250, 805,
-    {"Courier", "Courier-Bold", "Courier-Oblique", "Courier-BoldOblique"}
-};
-
-// Generic font metrics for multi-byte fonts
-static struct prt_ps_font_S prt_ps_mb_font =
-{
-    1000,
-    -100, 50,
-    -250, 805,
-    {NULL, NULL, NULL, NULL}
-};
-
-// Pointer to current font set being used
-static struct prt_ps_font_S* prt_ps_font;
-
-// Structures to map user named encoding and mapping to PS equivalents for
-// building CID font name
-struct prt_ps_encoding_S
-{
-    char       *encoding;
-    char       *cmap_encoding;
-    int                needs_charset;
-};
-
-struct prt_ps_charset_S
-{
-    char       *charset;
-    char       *cmap_charset;
-    int                has_charset;
-};
-
-
-# define CS_JIS_C_1978   (0x01)
-# define CS_JIS_X_1983   (0x02)
-# define CS_JIS_X_1990   (0x04)
-# define CS_NEC                (0x08)
-# define CS_MSWINDOWS  (0x10)
-# define CS_CP932      (0x20)
-# define CS_KANJITALK6 (0x40)
-# define CS_KANJITALK7   (0x80)
-
-// Japanese encodings and charsets
-static struct prt_ps_encoding_S j_encodings[] =
-{
-    {"iso-2022-jp", NULL,       (CS_JIS_C_1978|CS_JIS_X_1983|CS_JIS_X_1990|
-                                                                   CS_NEC)},
-    {"euc-jp",     "EUC",      (CS_JIS_C_1978|CS_JIS_X_1983|CS_JIS_X_1990)},
-    {"sjis",       "RKSJ",     (CS_JIS_C_1978|CS_JIS_X_1983|CS_MSWINDOWS|
-                                               CS_KANJITALK6|CS_KANJITALK7)},
-    {"cp932",       "RKSJ",     CS_JIS_X_1983},
-    {"ucs-2",       "UCS2",     CS_JIS_X_1990},
-    {"utf-8",       "UTF8" ,    CS_JIS_X_1990}
-};
-static struct prt_ps_charset_S j_charsets[] =
-{
-    {"JIS_C_1978",  "78",       CS_JIS_C_1978},
-    {"JIS_X_1983",  NULL,       CS_JIS_X_1983},
-    {"JIS_X_1990",  "Hojo",     CS_JIS_X_1990},
-    {"NEC",        "Ext",      CS_NEC},
-    {"MSWINDOWS",   "90ms",     CS_MSWINDOWS},
-    {"CP932",       "90ms",     CS_JIS_X_1983},
-    {"KANJITALK6",  "83pv",     CS_KANJITALK6},
-    {"KANJITALK7",  "90pv",     CS_KANJITALK7}
-};
-
-# define CS_GB_2312_80       (0x01)
-# define CS_GBT_12345_90     (0x02)
-# define CS_GBK2K          (0x04)
-# define CS_SC_MAC         (0x08)
-# define CS_GBT_90_MAC     (0x10)
-# define CS_GBK                    (0x20)
-# define CS_SC_ISO10646      (0x40)
-
-// Simplified Chinese encodings and charsets
-static struct prt_ps_encoding_S sc_encodings[] =
-{
-    {"iso-2022",    NULL,       (CS_GB_2312_80|CS_GBT_12345_90)},
-    {"gb18030",     NULL,       CS_GBK2K},
-    {"euc-cn",      "EUC",      (CS_GB_2312_80|CS_GBT_12345_90|CS_SC_MAC|
-                                                               CS_GBT_90_MAC)},
-    {"gbk",        "EUC",      CS_GBK},
-    {"ucs-2",       "UCS2",     CS_SC_ISO10646},
-    {"utf-8",       "UTF8",     CS_SC_ISO10646}
-};
-static struct prt_ps_charset_S sc_charsets[] =
-{
-    {"GB_2312-80",  "GB",       CS_GB_2312_80},
-    {"GBT_12345-90","GBT",      CS_GBT_12345_90},
-    {"MAC",        "GBpc",     CS_SC_MAC},
-    {"GBT-90_MAC",  "GBTpc",   CS_GBT_90_MAC},
-    {"GBK",        "GBK",      CS_GBK},
-    {"GB18030",     "GBK2K",    CS_GBK2K},
-    {"ISO10646",    "UniGB",    CS_SC_ISO10646}
-};
-
-# define CS_CNS_PLANE_1      (0x01)
-# define CS_CNS_PLANE_2      (0x02)
-# define CS_CNS_PLANE_1_2    (0x04)
-# define CS_B5             (0x08)
-# define CS_ETEN                   (0x10)
-# define CS_HK_GCCS        (0x20)
-# define CS_HK_SCS         (0x40)
-# define CS_HK_SCS_ETEN            (0x80)
-# define CS_MTHKL          (0x100)
-# define CS_MTHKS          (0x200)
-# define CS_DLHKL          (0x400)
-# define CS_DLHKS          (0x800)
-# define CS_TC_ISO10646            (0x1000)
-
-// Traditional Chinese encodings and charsets
-static struct prt_ps_encoding_S tc_encodings[] =
-{
-    {"iso-2022",    NULL,       (CS_CNS_PLANE_1|CS_CNS_PLANE_2)},
-    {"euc-tw",      "EUC",      CS_CNS_PLANE_1_2},
-    {"big5",       "B5",       (CS_B5|CS_ETEN|CS_HK_GCCS|CS_HK_SCS|
-                                   CS_HK_SCS_ETEN|CS_MTHKL|CS_MTHKS|CS_DLHKL|
-                                                                   CS_DLHKS)},
-    {"cp950",       "B5",       CS_B5},
-    {"ucs-2",       "UCS2",     CS_TC_ISO10646},
-    {"utf-8",       "UTF8",     CS_TC_ISO10646},
-    {"utf-16",      "UTF16",    CS_TC_ISO10646},
-    {"utf-32",      "UTF32",    CS_TC_ISO10646}
-};
-static struct prt_ps_charset_S tc_charsets[] =
-{
-    {"CNS_1992_1",  "CNS1",     CS_CNS_PLANE_1},
-    {"CNS_1992_2",  "CNS2",     CS_CNS_PLANE_2},
-    {"CNS_1993",    "CNS",      CS_CNS_PLANE_1_2},
-    {"BIG5",       NULL,       CS_B5},
-    {"CP950",      NULL,       CS_B5},
-    {"ETEN",       "ETen",     CS_ETEN},
-    {"HK_GCCS",     "HKgccs",  CS_HK_GCCS},
-    {"SCS",        "HKscs",    CS_HK_SCS},
-    {"SCS_ETEN",    "ETHK",     CS_HK_SCS_ETEN},
-    {"MTHKL",       "HKm471",   CS_MTHKL},
-    {"MTHKS",       "HKm314",   CS_MTHKS},
-    {"DLHKL",       "HKdla",    CS_DLHKL},
-    {"DLHKS",       "HKdlb",    CS_DLHKS},
-    {"ISO10646",    "UniCNS",   CS_TC_ISO10646}
-};
-
-# define CS_KR_X_1992      (0x01)
-# define CS_KR_MAC         (0x02)
-# define CS_KR_X_1992_MS     (0x04)
-# define CS_KR_ISO10646      (0x08)
-
-// Korean encodings and charsets
-static struct prt_ps_encoding_S k_encodings[] =
-{
-    {"iso-2022-kr", NULL,       CS_KR_X_1992},
-    {"euc-kr",      "EUC",      (CS_KR_X_1992|CS_KR_MAC)},
-    {"johab",       "Johab",    CS_KR_X_1992},
-    {"cp1361",      "Johab",    CS_KR_X_1992},
-    {"uhc",        "UHC",      CS_KR_X_1992_MS},
-    {"cp949",       "UHC",      CS_KR_X_1992_MS},
-    {"ucs-2",       "UCS2",     CS_KR_ISO10646},
-    {"utf-8",       "UTF8",     CS_KR_ISO10646}
-};
-static struct prt_ps_charset_S k_charsets[] =
-{
-    {"KS_X_1992",   "KSC",      CS_KR_X_1992},
-    {"CP1361",      "KSC",      CS_KR_X_1992},
-    {"MAC",        "KSCpc",    CS_KR_MAC},
-    {"MSWINDOWS",   "KSCms",    CS_KR_X_1992_MS},
-    {"CP949",       "KSCms",    CS_KR_X_1992_MS},
-    {"WANSUNG",     "KSCms",    CS_KR_X_1992_MS},
-    {"ISO10646",    "UniKS",    CS_KR_ISO10646}
-};
-
-// Collections of encodings and charsets for multi-byte printing
-struct prt_ps_mbfont_S
-{
-    int                                num_encodings;
-    struct prt_ps_encoding_S   *encodings;
-    int                                num_charsets;
-    struct prt_ps_charset_S    *charsets;
-    char                       *ascii_enc;
-    char                       *defcs;
-};
-
-static struct prt_ps_mbfont_S prt_ps_mbfonts[] =
-{
-    {
-       ARRAY_LENGTH(j_encodings),
-       j_encodings,
-       ARRAY_LENGTH(j_charsets),
-       j_charsets,
-       "jis_roman",
-       "JIS_X_1983"
-    },
-    {
-       ARRAY_LENGTH(sc_encodings),
-       sc_encodings,
-       ARRAY_LENGTH(sc_charsets),
-       sc_charsets,
-       "gb_roman",
-       "GB_2312-80"
-    },
-    {
-       ARRAY_LENGTH(tc_encodings),
-       tc_encodings,
-       ARRAY_LENGTH(tc_charsets),
-       tc_charsets,
-       "cns_roman",
-       "BIG5"
-    },
-    {
-       ARRAY_LENGTH(k_encodings),
-       k_encodings,
-       ARRAY_LENGTH(k_charsets),
-       k_charsets,
-       "ks_roman",
-       "KS_X_1992"
-    }
-};
-
-struct prt_ps_resource_S
-{
-    char_u  name[64];
-    char_u  filename[MAXPATHL + 1];
-    int     type;
-    char_u  title[256];
-    char_u  version[256];
-};
-
-// Types of PS resource file currently used
-# define PRT_RESOURCE_TYPE_PROCSET   (0)
-# define PRT_RESOURCE_TYPE_ENCODING  (1)
-# define PRT_RESOURCE_TYPE_CMAP      (2)
-
-// The PS prolog file version number has to match - if the prolog file is
-// updated, increment the number in the file and here.  Version checking was
-// added as of VIM 6.2.
-// The CID prolog file version number behaves as per PS prolog.
-// Table of VIM and prolog versions:
-//
-// VIM      Prolog  CIDProlog
-// 6.2      1.3
-// 7.0      1.4            1.0
-# define PRT_PROLOG_VERSION  ((char_u *)"1.4")
-# define PRT_CID_PROLOG_VERSION  ((char_u *)"1.0")
-
-// String versions of PS resource types - indexed by constants above so don't
-// re-order!
-static char *prt_resource_types[] =
-{
-    "procset",
-    "encoding",
-    "cmap"
-};
-
-// Strings to look for in a PS resource file
-# define PRT_RESOURCE_HEADER       "%!PS-Adobe-"
-# define PRT_RESOURCE_RESOURCE     "Resource-"
-# define PRT_RESOURCE_PROCSET      "ProcSet"
-# define PRT_RESOURCE_ENCODING     "Encoding"
-# define PRT_RESOURCE_CMAP         "CMap"
-
-
-// Data for table based DSC comment recognition, easy to extend if VIM needs to
-// read more comments.
-# define PRT_DSC_MISC_TYPE         (-1)
-# define PRT_DSC_TITLE_TYPE        (1)
-# define PRT_DSC_VERSION_TYPE      (2)
-# define PRT_DSC_ENDCOMMENTS_TYPE    (3)
-
-# define PRT_DSC_TITLE             "%%Title:"
-# define PRT_DSC_VERSION                   "%%Version:"
-# define PRT_DSC_ENDCOMMENTS       "%%EndComments:"
-
-struct prt_dsc_comment_S
-{
-    char       *string;
-    int                len;
-    int                type;
-};
-
-struct prt_dsc_line_S
-{
-    int                type;
-    char_u     *string;
-    int                len;
-};
-
-
-# define SIZEOF_CSTR(s)      (sizeof(s) - 1)
-static struct prt_dsc_comment_S prt_dsc_table[] =
-{
-    {PRT_DSC_TITLE,       SIZEOF_CSTR(PRT_DSC_TITLE),     PRT_DSC_TITLE_TYPE},
-    {PRT_DSC_VERSION,     SIZEOF_CSTR(PRT_DSC_VERSION),
-                                                       PRT_DSC_VERSION_TYPE},
-    {PRT_DSC_ENDCOMMENTS, SIZEOF_CSTR(PRT_DSC_ENDCOMMENTS),
-                                                    PRT_DSC_ENDCOMMENTS_TYPE}
-};
-
-static void prt_write_file_len(char_u *buffer, int bytes);
-static int prt_next_dsc(struct prt_dsc_line_S *p_dsc_line);
-
-/*
- * Variables for the output PostScript file.
- */
-static FILE *prt_ps_fd;
-static int prt_file_error;
-static char_u *prt_ps_file_name = NULL;
-
-/*
- * Various offsets and dimensions in default PostScript user space (points).
- * Used for text positioning calculations
- */
-static float prt_page_width;
-static float prt_page_height;
-static float prt_left_margin;
-static float prt_right_margin;
-static float prt_top_margin;
-static float prt_bottom_margin;
-static float prt_line_height;
-static float prt_first_line_height;
-static float prt_char_width;
-static float prt_number_width;
-static float prt_bgcol_offset;
-static float prt_pos_x_moveto = 0.0;
-static float prt_pos_y_moveto = 0.0;
-
-/*
- * Various control variables used to decide when and how to change the
- * PostScript graphics state.
- */
-static int prt_need_moveto;
-static int prt_do_moveto;
-static int prt_need_font;
-static int prt_font;
-static int prt_need_underline;
-static int prt_underline;
-static int prt_do_underline;
-static int prt_need_fgcol;
-static int prt_fgcol;
-static int prt_need_bgcol;
-static int prt_do_bgcol;
-static int prt_bgcol;
-static int prt_new_bgcol;
-static int prt_attribute_change;
-static float prt_text_run;
-static int prt_page_num;
-static int prt_bufsiz;
-
-/*
- * Variables controlling physical printing.
- */
-static int prt_media;
-static int prt_portrait;
-static int prt_num_copies;
-static int prt_duplex;
-static int prt_tumble;
-static int prt_collate;
-
-/*
- * Buffers used when generating PostScript output
- */
-static char_u prt_line_buffer[257];
-static garray_T prt_ps_buffer;
-
-static int prt_do_conv;
-static vimconv_T prt_conv;
-
-static int prt_out_mbyte;
-static int prt_custom_cmap;
-static char prt_cmap[80];
-static int prt_use_courier;
-static int prt_in_ascii;
-static int prt_half_width;
-static char *prt_ascii_encoding;
-static const char_u prt_hexchar[] = "0123456789abcdef";
-
-    static void
-prt_write_file_raw_len(char_u *buffer, int bytes)
-{
-    if (!prt_file_error
-           && fwrite(buffer, sizeof(char_u), bytes, prt_ps_fd)
-                                                            != (size_t)bytes)
-    {
-       emsg(_(e_error_writing_to_postscript_output_file));
-       prt_file_error = TRUE;
-    }
-}
-
-    static void
-prt_write_file(char_u *buffer)
-{
-    prt_write_file_len(buffer, (int)STRLEN(buffer));
-}
-
-    static void
-prt_write_file_len(char_u *buffer, int bytes)
-{
-    prt_write_file_raw_len(buffer, bytes);
-}
-
-/*
- * Write a string.
- */
-    static void
-prt_write_string(char *s)
-{
-    vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer), "%s", s);
-    prt_write_file(prt_line_buffer);
-}
-
-/*
- * Write an int and a space.
- */
-    static void
-prt_write_int(int i)
-{
-    sprintf((char *)prt_line_buffer, "%d ", i);
-    prt_write_file(prt_line_buffer);
-}
-
-/*
- * Write a boolean and a space.
- */
-    static void
-prt_write_boolean(int b)
-{
-    sprintf((char *)prt_line_buffer, "%s ", (b ? "T" : "F"));
-    prt_write_file(prt_line_buffer);
-}
-
-/*
- * Write PostScript to re-encode and define the font.
- */
-    static void
-prt_def_font(
-    char       *new_name,
-    char       *encoding,
-    int                height,
-    char       *font)
-{
-    vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
-                         "/_%s /VIM-%s /%s ref\n", new_name, encoding, font);
-    prt_write_file(prt_line_buffer);
-    if (prt_out_mbyte)
-       sprintf((char *)prt_line_buffer, "/%s %d %f /_%s sffs\n",
-                      new_name, height, 500./prt_ps_courier_font.wx, new_name);
-    else
-       vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
-                            "/%s %d /_%s ffs\n", new_name, height, new_name);
-    prt_write_file(prt_line_buffer);
-}
-
-/*
- * Write a line to define the CID font.
- */
-    static void
-prt_def_cidfont(char *new_name, int height, char *cidfont)
-{
-    vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
-             "/_%s /%s[/%s] vim_composefont\n", new_name, prt_cmap, cidfont);
-    prt_write_file(prt_line_buffer);
-    vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
-                            "/%s %d /_%s ffs\n", new_name, height, new_name);
-    prt_write_file(prt_line_buffer);
-}
-
-/*
- * Write a line to define a duplicate of a CID font
- */
-    static void
-prt_dup_cidfont(char *original_name, char *new_name)
-{
-    vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
-                                      "/%s %s d\n", new_name, original_name);
-    prt_write_file(prt_line_buffer);
-}
-
-/*
- * Convert a real value into an integer and fractional part as integers, with
- * the fractional part being in the range [0,10^precision).  The fractional part
- * is also rounded based on the precision + 1'th fractional digit.
- */
-    static void
-prt_real_bits(
-    double      real,
-    int                precision,
-    int                *pinteger,
-    int                *pfraction)
-{
-    int     i;
-    int     integer;
-    float   fraction;
-
-    integer = (int)real;
-    fraction = (float)(real - integer);
-    if (real < (double)integer)
-       fraction = -fraction;
-    for (i = 0; i < precision; i++)
-       fraction *= 10.0;
-
-    *pinteger = integer;
-    *pfraction = (int)(fraction + 0.5);
-}
-
-/*
- * Write a real and a space.  Save bytes if real value has no fractional part!
- * We use prt_real_bits() as %f in sprintf uses the locale setting to decide
- * what decimal point character to use, but PS always requires a '.'.
- */
-    static void
-prt_write_real(double val, int prec)
-{
-    int     integer;
-    int     fraction;
-
-    prt_real_bits(val, prec, &integer, &fraction);
-    // Emit integer part
-    sprintf((char *)prt_line_buffer, "%d", integer);
-    prt_write_file(prt_line_buffer);
-    // Only emit fraction if necessary
-    if (fraction != 0)
-    {
-       // Remove any trailing zeros
-       while ((fraction % 10) == 0)
-       {
-           prec--;
-           fraction /= 10;
-       }
-       // Emit fraction left padded with zeros
-       sprintf((char *)prt_line_buffer, ".%0*d", prec, fraction);
-       prt_write_file(prt_line_buffer);
-    }
-    sprintf((char *)prt_line_buffer, " ");
-    prt_write_file(prt_line_buffer);
-}
-
-/*
- * Write a line to define a numeric variable.
- */
-    static void
-prt_def_var(char *name, double value, int prec)
-{
-    vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
-                                                               "/%s ", name);
-    prt_write_file(prt_line_buffer);
-    prt_write_real(value, prec);
-    sprintf((char *)prt_line_buffer, "d\n");
-    prt_write_file(prt_line_buffer);
-}
-
-// Convert size from font space to user space at current font scale
-# define PRT_PS_FONT_TO_USER(scale, size)    ((size) * ((scale)/1000.0))
-
-    static void
-prt_flush_buffer(void)
-{
-    if (prt_ps_buffer.ga_len <= 0)
-       return;
-
-    // Any background color must be drawn first
-    if (prt_do_bgcol && (prt_new_bgcol != PRCOLOR_WHITE))
-    {
-       int     r, g, b;
-
-       if (prt_do_moveto)
-       {
-           prt_write_real(prt_pos_x_moveto, 2);
-           prt_write_real(prt_pos_y_moveto, 2);
-           prt_write_string("m\n");
-           prt_do_moveto = FALSE;
-       }
-
-       // Size of rect of background color on which text is printed
-       prt_write_real(prt_text_run, 2);
-       prt_write_real(prt_line_height, 2);
-
-       // Lastly add the color of the background
-       r = ((unsigned)prt_new_bgcol & 0xff0000) >> 16;
-       g = ((unsigned)prt_new_bgcol & 0xff00) >> 8;
-       b = prt_new_bgcol & 0xff;
-       prt_write_real(r / 255.0, 3);
-       prt_write_real(g / 255.0, 3);
-       prt_write_real(b / 255.0, 3);
-       prt_write_string("bg\n");
-    }
-    // Draw underlines before the text as it makes it slightly easier to
-    // find the starting point.
-    if (prt_do_underline)
-    {
-       if (prt_do_moveto)
-       {
-           prt_write_real(prt_pos_x_moveto, 2);
-           prt_write_real(prt_pos_y_moveto, 2);
-           prt_write_string("m\n");
-           prt_do_moveto = FALSE;
-       }
-
-       // Underline length of text run
-       prt_write_real(prt_text_run, 2);
-       prt_write_string("ul\n");
-    }
-    // Draw the text
-    if (prt_out_mbyte)
-       prt_write_string("<");
-    else
-       prt_write_string("(");
-    prt_write_file_raw_len(prt_ps_buffer.ga_data, prt_ps_buffer.ga_len);
-    if (prt_out_mbyte)
-       prt_write_string(">");
-    else
-       prt_write_string(")");
-    // Add a moveto if need be and use the appropriate show procedure
-    if (prt_do_moveto)
-    {
-       prt_write_real(prt_pos_x_moveto, 2);
-       prt_write_real(prt_pos_y_moveto, 2);
-       // moveto and a show
-       prt_write_string("ms\n");
-       prt_do_moveto = FALSE;
-    }
-    else // Simple show
-       prt_write_string("s\n");
-
-    ga_clear(&prt_ps_buffer);
-    ga_init2(&prt_ps_buffer, sizeof(char), prt_bufsiz);
-}
-
-
-    static void
-prt_resource_name(char_u *filename, void *cookie)
-{
-    char_u *resource_filename = cookie;
-
-    if (STRLEN(filename) >= MAXPATHL)
-       *resource_filename = NUL;
-    else
-       STRCPY(resource_filename, filename);
-}
-
-    static int
-prt_find_resource(char *name, struct prt_ps_resource_S *resource)
-{
-    char_u     *buffer;
-    int                retval;
-
-    buffer = alloc(MAXPATHL + 1);
-    if (buffer == NULL)
-       return FALSE;
-
-    vim_strncpy(resource->name, (char_u *)name, 63);
-    // Look for named resource file in runtimepath
-    STRCPY(buffer, "print");
-    add_pathsep(buffer);
-    vim_strcat(buffer, (char_u *)name, MAXPATHL);
-    vim_strcat(buffer, (char_u *)".ps", MAXPATHL);
-    resource->filename[0] = NUL;
-    retval = (do_in_runtimepath(buffer, 0, prt_resource_name,
-                                                          resource->filename)
-           && resource->filename[0] != NUL);
-    vim_free(buffer);
-    return retval;
-}
-
-// PS CR and LF characters have platform independent values
-# define PSLF  (0x0a)
-# define PSCR  (0x0d)
-
-// Static buffer to read initial comments in a resource file, some can have a
-// couple of KB of comments!
-# define PRT_FILE_BUFFER_LEN (2048)
-struct prt_resfile_buffer_S
-{
-    char_u  buffer[PRT_FILE_BUFFER_LEN];
-    int     len;
-    int     line_start;
-    int     line_end;
-};
-
-static struct prt_resfile_buffer_S prt_resfile;
-
-    static int
-prt_resfile_next_line(void)
-{
-    int     idx;
-
-    // Move to start of next line and then find end of line
-    idx = prt_resfile.line_end + 1;
-    while (idx < prt_resfile.len)
-    {
-       if (prt_resfile.buffer[idx] != PSLF && prt_resfile.buffer[idx] != PSCR)
-           break;
-       idx++;
-    }
-    prt_resfile.line_start = idx;
-
-    while (idx < prt_resfile.len)
-    {
-       if (prt_resfile.buffer[idx] == PSLF || prt_resfile.buffer[idx] == PSCR)
-           break;
-       idx++;
-    }
-    prt_resfile.line_end = idx;
-
-    return (idx < prt_resfile.len);
-}
-
-    static int
-prt_resfile_strncmp(int offset, char *string, int len)
-{
-    // Force not equal if string is longer than remainder of line
-    if (len > (prt_resfile.line_end - (prt_resfile.line_start + offset)))
-       return 1;
-
-    return STRNCMP(&prt_resfile.buffer[prt_resfile.line_start + offset],
-                                                               string, len);
-}
-
-    static int
-prt_resfile_skip_nonws(int offset)
-{
-    int     idx;
-
-    idx = prt_resfile.line_start + offset;
-    while (idx < prt_resfile.line_end)
-    {
-       if (SAFE_isspace(prt_resfile.buffer[idx]))
-           return idx - prt_resfile.line_start;
-       idx++;
-    }
-    return -1;
-}
-
-    static int
-prt_resfile_skip_ws(int offset)
-{
-    int     idx;
-
-    idx = prt_resfile.line_start + offset;
-    while (idx < prt_resfile.line_end)
-    {
-       if (!SAFE_isspace(prt_resfile.buffer[idx]))
-           return idx - prt_resfile.line_start;
-       idx++;
-    }
-    return -1;
-}
-
-// prt_next_dsc() - returns detail on next DSC comment line found.  Returns true
-// if a DSC comment is found, else false
-    static int
-prt_next_dsc(struct prt_dsc_line_S *p_dsc_line)
-{
-    int     comment;
-    int     offset;
-
-    // Move to start of next line
-    if (!prt_resfile_next_line())
-       return FALSE;
-
-    // DSC comments always start %%
-    if (prt_resfile_strncmp(0, "%%", 2) != 0)
-       return FALSE;
-
-    // Find type of DSC comment
-    for (comment = 0; comment < (int)ARRAY_LENGTH(prt_dsc_table); comment++)
-       if (prt_resfile_strncmp(0, prt_dsc_table[comment].string,
-                                           prt_dsc_table[comment].len) == 0)
-           break;
-
-    if (comment != ARRAY_LENGTH(prt_dsc_table))
-    {
-       // Return type of comment
-       p_dsc_line->type = prt_dsc_table[comment].type;
-       offset = prt_dsc_table[comment].len;
-    }
-    else
-    {
-       // Unrecognised DSC comment, skip to ws after comment leader
-       p_dsc_line->type = PRT_DSC_MISC_TYPE;
-       offset = prt_resfile_skip_nonws(0);
-       if (offset == -1)
-           return FALSE;
-    }
-
-    // Skip ws to comment value
-    offset = prt_resfile_skip_ws(offset);
-    if (offset == -1)
-       return FALSE;
-
-    p_dsc_line->string = &prt_resfile.buffer[prt_resfile.line_start + offset];
-    p_dsc_line->len = prt_resfile.line_end - (prt_resfile.line_start + offset);
-
-    return TRUE;
-}
-
-/*
- * Improved hand crafted parser to get the type, title, and version number of a
- * PS resource file so the file details can be added to the DSC header comments.
- */
-    static int
-prt_open_resource(struct prt_ps_resource_S *resource)
-{
-    int                offset;
-    int                seen_all;
-    int                seen_title;
-    int                seen_version;
-    FILE       *fd_resource;
-    struct prt_dsc_line_S dsc_line;
-
-    fd_resource = mch_fopen((char *)resource->filename, READBIN);
-    if (fd_resource == NULL)
-    {
-       semsg(_(e_cant_open_file_str_3), resource->filename);
-       return FALSE;
-    }
-    CLEAR_FIELD(prt_resfile.buffer);
-
-    // Parse first line to ensure valid resource file
-    prt_resfile.len = (int)fread((char *)prt_resfile.buffer, sizeof(char_u),
-                                           PRT_FILE_BUFFER_LEN, fd_resource);
-    if (ferror(fd_resource))
-    {
-       semsg(_(e_cant_read_postscript_resource_file_str),
-               resource->filename);
-       fclose(fd_resource);
-       return FALSE;
-    }
-    fclose(fd_resource);
-
-    prt_resfile.line_end = -1;
-    prt_resfile.line_start = 0;
-    if (!prt_resfile_next_line())
-       return FALSE;
-
-    offset = 0;
-
-    if (prt_resfile_strncmp(offset, PRT_RESOURCE_HEADER,
-                              (int)STRLEN_LITERAL(PRT_RESOURCE_HEADER)) != 0)
-    {
-       semsg(_(e_file_str_is_not_postscript_resource_file),
-                                                          resource->filename);
-       return FALSE;
-    }
-
-    // Skip over any version numbers and following ws
-    offset += (int)STRLEN_LITERAL(PRT_RESOURCE_HEADER);
-    offset = prt_resfile_skip_nonws(offset);
-    if (offset == -1)
-       return FALSE;
-    offset = prt_resfile_skip_ws(offset);
-    if (offset == -1)
-       return FALSE;
-
-    if (prt_resfile_strncmp(offset, PRT_RESOURCE_RESOURCE,
-                            (int)STRLEN_LITERAL(PRT_RESOURCE_RESOURCE)) != 0)
-    {
-       semsg(_(e_file_str_is_not_supported_postscript_resource_file),
-                                                          resource->filename);
-       return FALSE;
-    }
-    offset += (int)STRLEN_LITERAL(PRT_RESOURCE_RESOURCE);
-
-    // Decide type of resource in the file
-    if (prt_resfile_strncmp(offset, PRT_RESOURCE_PROCSET,
-                             (int)STRLEN_LITERAL(PRT_RESOURCE_PROCSET)) == 0)
-       resource->type = PRT_RESOURCE_TYPE_PROCSET;
-    else if (prt_resfile_strncmp(offset, PRT_RESOURCE_ENCODING,
-                            (int)STRLEN_LITERAL(PRT_RESOURCE_ENCODING)) == 0)
-       resource->type = PRT_RESOURCE_TYPE_ENCODING;
-    else if (prt_resfile_strncmp(offset, PRT_RESOURCE_CMAP,
-                                (int)STRLEN_LITERAL(PRT_RESOURCE_CMAP)) == 0)
-       resource->type = PRT_RESOURCE_TYPE_CMAP;
-    else
-    {
-       semsg(_(e_file_str_is_not_supported_postscript_resource_file),
-                                                          resource->filename);
-       return FALSE;
-    }
-
-    // Look for title and version of resource
-    resource->title[0] = '\0';
-    resource->version[0] = '\0';
-    seen_title = FALSE;
-    seen_version = FALSE;
-    seen_all = FALSE;
-    while (!seen_all && prt_next_dsc(&dsc_line))
-    {
-       switch (dsc_line.type)
-       {
-       case PRT_DSC_TITLE_TYPE:
-           vim_strncpy(resource->title, dsc_line.string, dsc_line.len);
-           seen_title = TRUE;
-           if (seen_version)
-               seen_all = TRUE;
-           break;
-
-       case PRT_DSC_VERSION_TYPE:
-           vim_strncpy(resource->version, dsc_line.string, dsc_line.len);
-           seen_version = TRUE;
-           if (seen_title)
-               seen_all = TRUE;
-           break;
-
-       case PRT_DSC_ENDCOMMENTS_TYPE:
-           // Won't find title or resource after this comment, stop searching
-           seen_all = TRUE;
-           break;
-
-       case PRT_DSC_MISC_TYPE:
-           // Not interested in whatever comment this line had
-           break;
-       }
-    }
-
-    if (!seen_title || !seen_version)
-    {
-       semsg(_(e_file_str_is_not_supported_postscript_resource_file),
-                                                          resource->filename);
-       return FALSE;
-    }
-
-    return TRUE;
-}
-
-    static int
-prt_check_resource(struct prt_ps_resource_S *resource, char_u *version)
-{
-    // Version number m.n should match, the revision number does not matter
-    if (STRNCMP(resource->version, version, STRLEN(version)) != 0)
-    {
-       semsg(_(e_str_resource_file_has_wrong_version),
-               resource->name);
-       return FALSE;
-    }
-
-    // Other checks to be added as needed
-    return TRUE;
-}
-
-    static void
-prt_dsc_start(void)
-{
-    prt_write_string("%!PS-Adobe-3.0\n");
-}
-
-    static void
-prt_dsc_noarg(char *comment)
-{
-    vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
-                                                        "%%%%%s\n", comment);
-    prt_write_file(prt_line_buffer);
-}
-
-    static void
-prt_dsc_textline(char *comment, char *text)
-{
-    vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
-                                              "%%%%%s: %s\n", comment, text);
-    prt_write_file(prt_line_buffer);
-}
-
-    static void
-prt_dsc_text(char *comment, char *text)
-{
-    // TODO - should scan 'text' for any chars needing escaping!
-    vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
-                                            "%%%%%s: (%s)\n", comment, text);
-    prt_write_file(prt_line_buffer);
-}
-
-# define prt_dsc_atend(c)      prt_dsc_text((c), "atend")
-
-    static void
-prt_dsc_ints(char *comment, int count, int *ints)
-{
-    int                i;
-
-    vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
-                                                         "%%%%%s:", comment);
-    prt_write_file(prt_line_buffer);
-
-    for (i = 0; i < count; i++)
-    {
-       sprintf((char *)prt_line_buffer, " %d", ints[i]);
-       prt_write_file(prt_line_buffer);
-    }
-
-    prt_write_string("\n");
-}
-
-    static void
-prt_dsc_resources(
-    char       *comment,       // if NULL add to previous
-    char       *type,
-    char       *string)
-{
-    if (comment != NULL)
-       vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
-                                                "%%%%%s: %s", comment, type);
-    else
-       vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
-                                                           "%%%%+ %s", type);
-    prt_write_file(prt_line_buffer);
-
-    vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
-                                                            " %s\n", string);
-    prt_write_file(prt_line_buffer);
-}
-
-    static void
-prt_dsc_font_resource(char *resource, struct prt_ps_font_S *ps_font)
-{
-    int     i;
-
-    prt_dsc_resources(resource, "font",
-                                   ps_font->ps_fontname[PRT_PS_FONT_ROMAN]);
-    for (i = PRT_PS_FONT_BOLD ; i <= PRT_PS_FONT_BOLDOBLIQUE ; i++)
-       if (ps_font->ps_fontname[i] != NULL)
-           prt_dsc_resources(NULL, "font", ps_font->ps_fontname[i]);
-}
-
-    static void
-prt_dsc_requirements(
-    int                duplex,
-    int                tumble,
-    int                collate,
-    int                color,
-    int                num_copies)
-{
-    // Only output the comment if we need to.
-    // Note: tumble is ignored if we are not duplexing
-    if (!(duplex || collate || color || (num_copies > 1)))
-       return;
-
-    sprintf((char *)prt_line_buffer, "%%%%Requirements:");
-    prt_write_file(prt_line_buffer);
-
-    if (duplex)
-    {
-       prt_write_string(" duplex");
-       if (tumble)
-           prt_write_string("(tumble)");
-    }
-    if (collate)
-       prt_write_string(" collate");
-    if (color)
-       prt_write_string(" color");
-    if (num_copies > 1)
-    {
-       prt_write_string(" numcopies(");
-       // Note: no space wanted so don't use prt_write_int()
-       sprintf((char *)prt_line_buffer, "%d", num_copies);
-       prt_write_file(prt_line_buffer);
-       prt_write_string(")");
-    }
-    prt_write_string("\n");
-}
-
-    static void
-prt_dsc_docmedia(
-    char       *paper_name,
-    double     width,
-    double     height,
-    double     weight,
-    char       *colour,
-    char       *type)
-{
-    vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
-                                       "%%%%DocumentMedia: %s ", paper_name);
-    prt_write_file(prt_line_buffer);
-    prt_write_real(width, 2);
-    prt_write_real(height, 2);
-    prt_write_real(weight, 2);
-    if (colour == NULL)
-       prt_write_string("()");
-    else
-       prt_write_string(colour);
-    prt_write_string(" ");
-    if (type == NULL)
-       prt_write_string("()");
-    else
-       prt_write_string(type);
-    prt_write_string("\n");
-}
-
-    void
-mch_print_cleanup(void)
-{
-    if (prt_out_mbyte)
-    {
-       int     i;
-
-       // Free off all CID font names created, but first clear duplicate
-       // pointers to the same string (when the same font is used for more than
-       // one style).
-       for (i = PRT_PS_FONT_ROMAN; i <= PRT_PS_FONT_BOLDOBLIQUE; i++)
-       {
-           if (prt_ps_mb_font.ps_fontname[i] != NULL)
-               VIM_CLEAR(prt_ps_mb_font.ps_fontname[i]);
-       }
-    }
-
-    if (prt_do_conv)
-    {
-       convert_setup(&prt_conv, NULL, NULL);
-       prt_do_conv = FALSE;
-    }
-    if (prt_ps_fd != NULL)
-    {
-       fclose(prt_ps_fd);
-       prt_ps_fd = NULL;
-       prt_file_error = FALSE;
-    }
-    if (prt_ps_file_name != NULL)
-       VIM_CLEAR(prt_ps_file_name);
-}
-
-    static float
-to_device_units(int idx, double physsize, int def_number)
-{
-    float      ret;
-    int                u;
-    int                nr;
-
-    u = prt_get_unit(idx);
-    if (u == PRT_UNIT_NONE)
-    {
-       u = PRT_UNIT_PERC;
-       nr = def_number;
-    }
-    else
-       nr = printer_opts[idx].number;
-
-    switch (u)
-    {
-       case PRT_UNIT_INCH:
-           ret = (float)(nr * PRT_PS_DEFAULT_DPI);
-           break;
-       case PRT_UNIT_MM:
-           ret = (float)(nr * PRT_PS_DEFAULT_DPI) / (float)25.4;
-           break;
-       case PRT_UNIT_POINT:
-           ret = (float)nr;
-           break;
-       case PRT_UNIT_PERC:
-       default:
-           ret = (float)(physsize * nr) / 100;
-           break;
-    }
-
-    return ret;
-}
-
-/*
- * Calculate margins for given width and height from printoptions settings.
- */
-    static void
-prt_page_margins(
-    double     width,
-    double     height,
-    double     *left,
-    double     *right,
-    double     *top,
-    double     *bottom)
-{
-    *left   = to_device_units(OPT_PRINT_LEFT, width, 10);
-    *right  = width - to_device_units(OPT_PRINT_RIGHT, width, 5);
-    *top    = height - to_device_units(OPT_PRINT_TOP, height, 5);
-    *bottom = to_device_units(OPT_PRINT_BOT, height, 5);
-}
-
-    static void
-prt_font_metrics(int font_scale)
-{
-    prt_line_height = (float)font_scale;
-    prt_char_width = (float)PRT_PS_FONT_TO_USER(font_scale, prt_ps_font->wx);
-}
-
-
-    static int
-prt_get_cpl(void)
-{
-    if (prt_use_number())
-    {
-       prt_number_width = PRINT_NUMBER_WIDTH * prt_char_width;
-       // If we are outputting multi-byte characters then line numbers will be
-       // printed with half width characters
-       if (prt_out_mbyte)
-           prt_number_width /= 2;
-       prt_left_margin += prt_number_width;
-    }
-    else
-       prt_number_width = 0.0;
-
-    return (int)((prt_right_margin - prt_left_margin) / prt_char_width);
-}
-
-    static int
-prt_build_cid_fontname(int font, char_u *name, int name_len)
-{
-    char    *fontname;
-
-    fontname = alloc(name_len + 1);
-    if (fontname == NULL)
-       return FALSE;
-    vim_strncpy((char_u *)fontname, name, name_len);
-    prt_ps_mb_font.ps_fontname[font] = fontname;
-
-    return TRUE;
-}
-
-/*
- * Get number of lines of text that fit on a page (excluding the header).
- */
-    static int
-prt_get_lpp(void)
-{
-    int lpp;
-
-    /*
-     * Calculate offset to lower left corner of background rect based on actual
-     * font height (based on its bounding box) and the line height, handling the
-     * case where the font height can exceed the line height.
-     */
-    prt_bgcol_offset = (float)PRT_PS_FONT_TO_USER(prt_line_height,
-                                          prt_ps_font->bbox_min_y);
-    if ((prt_ps_font->bbox_max_y - prt_ps_font->bbox_min_y) < 1000.0)
-    {
-       prt_bgcol_offset -= (float)PRT_PS_FONT_TO_USER(prt_line_height,
-                               (1000.0 - (prt_ps_font->bbox_max_y -
-                                           prt_ps_font->bbox_min_y)) / 2);
-    }
-
-    // Get height for topmost line based on background rect offset.
-    prt_first_line_height = prt_line_height + prt_bgcol_offset;
-
-    // Calculate lpp
-    lpp = (int)((prt_top_margin - prt_bottom_margin) / prt_line_height);
-
-    // Adjust top margin if there is a header
-    prt_top_margin -= prt_line_height * prt_header_height();
-
-    return lpp - prt_header_height();
-}
-
-    static int
-prt_match_encoding(
-    char                       *p_encoding,
-    struct prt_ps_mbfont_S     *p_cmap,
-    struct prt_ps_encoding_S   **pp_mbenc)
-{
-    int                                mbenc;
-    int                                enc_len;
-    struct prt_ps_encoding_S   *p_mbenc;
-
-    *pp_mbenc = NULL;
-    // Look for recognised encoding
-    enc_len = (int)STRLEN(p_encoding);
-    p_mbenc = p_cmap->encodings;
-    for (mbenc = 0; mbenc < p_cmap->num_encodings; mbenc++)
-    {
-       if (STRNICMP(p_mbenc->encoding, p_encoding, enc_len) == 0)
-       {
-           *pp_mbenc = p_mbenc;
-           return TRUE;
-       }
-       p_mbenc++;
-    }
-    return FALSE;
-}
-
-    static int
-prt_match_charset(
-    char                   *p_charset,
-    struct prt_ps_mbfont_S  *p_cmap,
-    struct prt_ps_charset_S **pp_mbchar)
-{
-    int                            mbchar;
-    int                            char_len;
-    struct prt_ps_charset_S *p_mbchar;
-
-    // Look for recognised character set, using default if one is not given
-    if (*p_charset == NUL)
-       p_charset = p_cmap->defcs;
-    char_len = (int)STRLEN(p_charset);
-    p_mbchar = p_cmap->charsets;
-    for (mbchar = 0; mbchar < p_cmap->num_charsets; mbchar++)
-    {
-       if (STRNICMP(p_mbchar->charset, p_charset, char_len) == 0)
-       {
-           *pp_mbchar = p_mbchar;
-           return TRUE;
-       }
-       p_mbchar++;
-    }
-    return FALSE;
-}
-
-    int
-mch_print_init(
-    prt_settings_T *psettings,
-    char_u     *jobname,
-    int                forceit UNUSED)
-{
-    int                i;
-    char       *paper_name;
-    int                paper_strlen;
-    int                fontsize;
-    char_u     *p;
-    double      left;
-    double      right;
-    double      top;
-    double      bottom;
-    int                props;
-    int                cmap = 0;
-    char_u     *p_encoding;
-    struct prt_ps_encoding_S *p_mbenc;
-    struct prt_ps_encoding_S *p_mbenc_first;
-    struct prt_ps_charset_S  *p_mbchar = NULL;
-
-# if 0
-    /*
-     * TODO:
-     * If "forceit" is false: pop up a dialog to select:
-     * - printer name
-     * - copies
-     * - collated/uncollated
-     * - duplex off/long side/short side
-     * - paper size
-     * - portrait/landscape
-     * - font size
-     *
-     * If "forceit" is true: use the default printer and settings
-     */
-    if (forceit)
-       s_pd.Flags |= PD_RETURNDEFAULT;
-# endif
-
-    /*
-     * Set up font and encoding.
-     */
-    p_encoding = enc_skip(p_penc);
-    if (*p_encoding == NUL)
-       p_encoding = enc_skip(p_enc);
-
-    // Look for a multi-byte font that matches the encoding and character set.
-    // Only look if multi-byte character set is defined, or using multi-byte
-    // encoding other than Unicode.  This is because a Unicode encoding does not
-    // uniquely identify a CJK character set to use.
-    p_mbenc = NULL;
-    props = enc_canon_props(p_encoding);
-    if (!(props & ENC_8BIT) && ((*p_pmcs != NUL) || !(props & ENC_UNICODE)))
-    {
-       int cmap_first = 0;
-
-       p_mbenc_first = NULL;
-       for (cmap = 0; cmap < (int)ARRAY_LENGTH(prt_ps_mbfonts); cmap++)
-           if (prt_match_encoding((char *)p_encoding, &prt_ps_mbfonts[cmap],
-                                                                   &p_mbenc))
-           {
-               if (p_mbenc_first == NULL)
-               {
-                   p_mbenc_first = p_mbenc;
-                   cmap_first = cmap;
-               }
-               if (prt_match_charset((char *)p_pmcs, &prt_ps_mbfonts[cmap],
-                                                                  &p_mbchar))
-                   break;
-           }
-
-       // Use first encoding matched if no charset matched
-       if (p_mbchar == NULL && p_mbenc_first != NULL)
-       {
-           p_mbenc = p_mbenc_first;
-           cmap = cmap_first;
-       }
-    }
-
-    prt_out_mbyte = (p_mbenc != NULL);
-    if (prt_out_mbyte)
-    {
-       // Build CMap name - will be same for all multi-byte fonts used
-       prt_cmap[0] = NUL;
-
-       prt_custom_cmap = (p_mbchar == NULL);
-       if (!prt_custom_cmap)
-       {
-           // Check encoding and character set are compatible
-           if ((p_mbenc->needs_charset & p_mbchar->has_charset) == 0)
-           {
-               emsg(_(e_incompatible_multi_byte_encoding_and_character_set));
-               return FALSE;
-           }
-
-           // Add charset name if not empty
-           if (p_mbchar->cmap_charset != NULL)
-           {
-               vim_strncpy((char_u *)prt_cmap,
-                     (char_u *)p_mbchar->cmap_charset, sizeof(prt_cmap) - 3);
-               STRCAT(prt_cmap, "-");
-           }
-       }
-       else
-       {
-           // Add custom CMap character set name
-           if (*p_pmcs == NUL)
-           {
-               emsg(_(e_printmbcharset_cannot_be_empty_with_multi_byte_encoding));
-               return FALSE;
-           }
-           vim_strncpy((char_u *)prt_cmap, p_pmcs, sizeof(prt_cmap) - 3);
-           STRCAT(prt_cmap, "-");
-       }
-
-       // CMap name ends with (optional) encoding name and -H for horizontal
-       if (p_mbenc->cmap_encoding != NULL && STRLEN(prt_cmap)
-                     + STRLEN(p_mbenc->cmap_encoding) + 3 < sizeof(prt_cmap))
-       {
-           STRCAT(prt_cmap, p_mbenc->cmap_encoding);
-           STRCAT(prt_cmap, "-");
-       }
-       STRCAT(prt_cmap, "H");
-
-       if (!mbfont_opts[OPT_MBFONT_REGULAR].present)
-       {
-           emsg(_(e_no_default_font_specified_for_multi_byte_printing));
-           return FALSE;
-       }
-
-       // Derive CID font names with fallbacks if not defined
-       if (!prt_build_cid_fontname(PRT_PS_FONT_ROMAN,
-                                   mbfont_opts[OPT_MBFONT_REGULAR].string,
-                                   mbfont_opts[OPT_MBFONT_REGULAR].strlen))
-           return FALSE;
-       if (mbfont_opts[OPT_MBFONT_BOLD].present)
-           if (!prt_build_cid_fontname(PRT_PS_FONT_BOLD,
-                                       mbfont_opts[OPT_MBFONT_BOLD].string,
-                                       mbfont_opts[OPT_MBFONT_BOLD].strlen))
-               return FALSE;
-       if (mbfont_opts[OPT_MBFONT_OBLIQUE].present)
-           if (!prt_build_cid_fontname(PRT_PS_FONT_OBLIQUE,
-                                       mbfont_opts[OPT_MBFONT_OBLIQUE].string,
-                                       mbfont_opts[OPT_MBFONT_OBLIQUE].strlen))
-               return FALSE;
-       if (mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].present)
-           if (!prt_build_cid_fontname(PRT_PS_FONT_BOLDOBLIQUE,
-                                  mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].string,
-                                 mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].strlen))
-               return FALSE;
-
-       // Check if need to use Courier for ASCII code range, and if so pick up
-       // the encoding to use
-       prt_use_courier = mbfont_opts[OPT_MBFONT_USECOURIER].present &&
-           (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_USECOURIER].string[0]) == 'y');
-       if (prt_use_courier)
-       {
-           // Use national ASCII variant unless ASCII wanted
-           if (mbfont_opts[OPT_MBFONT_ASCII].present &&
-               (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_ASCII].string[0]) == 'y'))
-               prt_ascii_encoding = "ascii";
-           else
-               prt_ascii_encoding = prt_ps_mbfonts[cmap].ascii_enc;
-       }
-
-       prt_ps_font = &prt_ps_mb_font;
-    }
-    else
-    {
-       prt_use_courier = FALSE;
-       prt_ps_font = &prt_ps_courier_font;
-    }
-
-    /*
-     * Find the size of the paper and set the margins.
-     */
-    prt_portrait = (!printer_opts[OPT_PRINT_PORTRAIT].present
-          || TOLOWER_ASC(printer_opts[OPT_PRINT_PORTRAIT].string[0]) == 'y');
-    if (printer_opts[OPT_PRINT_PAPER].present)
-    {
-       paper_name = (char *)printer_opts[OPT_PRINT_PAPER].string;
-       paper_strlen = printer_opts[OPT_PRINT_PAPER].strlen;
-    }
-    else
-    {
-       paper_name = "A4";
-       paper_strlen = 2;
-    }
-    for (i = 0; i < (int)PRT_MEDIASIZE_LEN; ++i)
-       if (STRLEN(prt_mediasize[i].name) == (unsigned)paper_strlen
-               && STRNICMP(prt_mediasize[i].name, paper_name,
-                                                          paper_strlen) == 0)
-           break;
-    if (i == PRT_MEDIASIZE_LEN)
-       i = 0;
-    prt_media = i;
-
-    /*
-     * Set PS pagesize based on media dimensions and print orientation.
-     * Note: Media and page sizes have defined meanings in PostScript and should
-     * be kept distinct.  Media is the paper (or transparency, or ...) that is
-     * printed on, whereas the page size is the area that the PostScript
-     * interpreter renders into.
-     */
-    if (prt_portrait)
-    {
-       prt_page_width = prt_mediasize[i].width;
-       prt_page_height = prt_mediasize[i].height;
-    }
-    else
-    {
-       prt_page_width = prt_mediasize[i].height;
-       prt_page_height = prt_mediasize[i].width;
-    }
-
-    /*
-     * Set PS page margins based on the PS pagesize, not the mediasize - this
-     * needs to be done before the cpl and lpp are calculated.
-     */
-    prt_page_margins(prt_page_width, prt_page_height, &left, &right, &top,
-                                                                   &bottom);
-    prt_left_margin = (float)left;
-    prt_right_margin = (float)right;
-    prt_top_margin = (float)top;
-    prt_bottom_margin = (float)bottom;
-
-    /*
-     * Set up the font size.
-     */
-    fontsize = PRT_PS_DEFAULT_FONTSIZE;
-    for (p = p_pfn; (p = vim_strchr(p, ':')) != NULL; ++p)
-       if (p[1] == 'h' && VIM_ISDIGIT(p[2]))
-           fontsize = atoi((char *)p + 2);
-    prt_font_metrics(fontsize);
-
-    /*
-     * Return the number of characters per line, and lines per page for the
-     * generic print code.
-     */
-    psettings->chars_per_line = prt_get_cpl();
-    psettings->lines_per_page = prt_get_lpp();
-
-    // Catch margin settings that leave no space for output!
-    if (psettings->chars_per_line <= 0 || psettings->lines_per_page <= 0)
-       return FAIL;
-
-    /*
-     * Sort out the number of copies to be printed.  PS by default will do
-     * uncollated copies for you, so once we know how many uncollated copies are
-     * wanted cache it away and lie to the generic code that we only want one
-     * uncollated copy.
-     */
-    psettings->n_collated_copies = 1;
-    psettings->n_uncollated_copies = 1;
-    prt_num_copies = 1;
-    prt_collate = (!printer_opts[OPT_PRINT_COLLATE].present
-           || TOLOWER_ASC(printer_opts[OPT_PRINT_COLLATE].string[0]) == 'y');
-    if (prt_collate)
-    {
-       // TODO: Get number of collated copies wanted.
-       psettings->n_collated_copies = 1;
-    }
-    else
-    {
-       // TODO: Get number of uncollated copies wanted and update the cached
-       // count.
-       prt_num_copies = 1;
-    }
-
-    psettings->jobname = jobname;
-
-    /*
-     * Set up printer duplex and tumble based on Duplex option setting - default
-     * is long sided duplex printing (i.e. no tumble).
-     */
-    prt_duplex = TRUE;
-    prt_tumble = FALSE;
-    psettings->duplex = 1;
-    if (printer_opts[OPT_PRINT_DUPLEX].present)
-    {
-       if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "off", 3) == 0)
-       {
-           prt_duplex = FALSE;
-           psettings->duplex = 0;
-       }
-       else if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "short", 5)
-                                                                        == 0)
-           prt_tumble = TRUE;
-    }
-
-    // For now user abort not supported
-    psettings->user_abort = 0;
-
-    // If the user didn't specify a file name, use a temp file.
-    if (psettings->outfile == NULL)
-    {
-       prt_ps_file_name = vim_tempname('p', TRUE);
-       if (prt_ps_file_name == NULL)
-       {
-           emsg(_(e_cant_get_temp_file_name));
-           return FAIL;
-       }
-       prt_ps_fd = mch_fopen((char *)prt_ps_file_name, WRITEBIN);
-    }
-    else
-    {
-       p = expand_env_save(psettings->outfile);
-       if (p != NULL)
-       {
-           prt_ps_fd = mch_fopen((char *)p, WRITEBIN);
-           vim_free(p);
-       }
-    }
-    if (prt_ps_fd == NULL)
-    {
-       emsg(_(e_cant_open_postscript_output_file));
-       mch_print_cleanup();
-       return FAIL;
-    }
-
-    prt_bufsiz = psettings->chars_per_line;
-    if (prt_out_mbyte)
-       prt_bufsiz *= 2;
-    ga_init2(&prt_ps_buffer, sizeof(char), prt_bufsiz);
-
-    prt_page_num = 0;
-
-    prt_attribute_change = FALSE;
-    prt_need_moveto = FALSE;
-    prt_need_font = FALSE;
-    prt_need_fgcol = FALSE;
-    prt_need_bgcol = FALSE;
-    prt_need_underline = FALSE;
-
-    prt_file_error = FALSE;
-
-    return OK;
-}
-
-    static int
-prt_add_resource(struct prt_ps_resource_S *resource)
-{
-    FILE*      fd_resource;
-    char_u     resource_buffer[512];
-    size_t     bytes_read;
-
-    fd_resource = mch_fopen((char *)resource->filename, READBIN);
-    if (fd_resource == NULL)
-    {
-       semsg(_(e_cant_open_file_str_2), resource->filename);
-       return FALSE;
-    }
-    prt_dsc_resources("BeginResource", prt_resource_types[resource->type],
-                                                    (char *)resource->title);
-
-    prt_dsc_textline("BeginDocument", (char *)resource->filename);
-
-    for (;;)
-    {
-       bytes_read = fread((char *)resource_buffer, sizeof(char_u),
-                          sizeof(resource_buffer), fd_resource);
-       if (ferror(fd_resource))
-       {
-           semsg(_(e_cant_read_postscript_resource_file_str),
-                                                           resource->filename);
-           fclose(fd_resource);
-           return FALSE;
-       }
-       if (bytes_read == 0)
-           break;
-       prt_write_file_raw_len(resource_buffer, (int)bytes_read);
-       if (prt_file_error)
-       {
-           fclose(fd_resource);
-           return FALSE;
-       }
-    }
-    fclose(fd_resource);
-
-    prt_dsc_noarg("EndDocument");
-
-    prt_dsc_noarg("EndResource");
-
-    return TRUE;
-}
-
-    int
-mch_print_begin(prt_settings_T *psettings)
-{
-    int                bbox[4];
-    double      left;
-    double      right;
-    double      top;
-    double      bottom;
-    struct prt_ps_resource_S *res_prolog;
-    struct prt_ps_resource_S *res_encoding;
-    char       buffer[256];
-    char_u      *p_encoding;
-    struct prt_ps_resource_S *res_cidfont;
-    struct prt_ps_resource_S *res_cmap;
-    int                retval = FALSE;
-
-    res_prolog = ALLOC_ONE(struct prt_ps_resource_S);
-    res_encoding = ALLOC_ONE(struct prt_ps_resource_S);
-    res_cidfont = ALLOC_ONE(struct prt_ps_resource_S);
-    res_cmap = ALLOC_ONE(struct prt_ps_resource_S);
-    if (res_prolog == NULL || res_encoding == NULL
-           || res_cidfont == NULL || res_cmap == NULL)
-       goto theend;
-
-    /*
-     * PS DSC Header comments - no PS code!
-     */
-    prt_dsc_start();
-    prt_dsc_textline("Title", (char *)psettings->jobname);
-    if (!get_user_name((char_u *)buffer, 256))
-       STRCPY(buffer, "Unknown");
-    prt_dsc_textline("For", buffer);
-    prt_dsc_textline("Creator", VIM_VERSION_LONG);
-    // Note: to ensure Clean8bit I don't think we can use LC_TIME
-
-    prt_dsc_textline("CreationDate", get_ctime(time(NULL), FALSE));
-    prt_dsc_textline("DocumentData", "Clean8Bit");
-    prt_dsc_textline("Orientation", prt_portrait ? "Portrait" : "Landscape");
-    prt_dsc_atend("Pages");
-    prt_dsc_textline("PageOrder", "Ascend");
-    // The bbox does not change with orientation - it is always in the default
-    // user coordinate system!  We have to recalculate right and bottom
-    // coordinates based on the font metrics for the bbox to be accurate.
-    prt_page_margins(prt_mediasize[prt_media].width,
-                                           prt_mediasize[prt_media].height,
-                                           &left, &right, &top, &bottom);
-    bbox[0] = (int)left;
-    if (prt_portrait)
-    {
-       // In portrait printing the fixed point is the top left corner so we
-       // derive the bbox from that point.  We have the expected cpl chars
-       // across the media and lpp lines down the media.
-       bbox[1] = (int)(top - (psettings->lines_per_page + prt_header_height())
-                                                   * (double)prt_line_height);
-       bbox[2] = (int)(left + psettings->chars_per_line
-                                              * (double)prt_char_width + 0.5);
-       bbox[3] = (int)(top + 0.5);
-    }
-    else
-    {
-       // In landscape printing the fixed point is the bottom left corner so we
-       // derive the bbox from that point.  We have lpp chars across the media
-       // and cpl lines up the media.
-       bbox[1] = (int)bottom;
-       bbox[2] = (int)(left + ((psettings->lines_per_page
-                             + prt_header_height()) * prt_line_height) + 0.5);
-       bbox[3] = (int)(bottom + psettings->chars_per_line
-                                              * (double)prt_char_width + 0.5);
-    }
-    prt_dsc_ints("BoundingBox", 4, bbox);
-    // The media width and height does not change with landscape printing!
-    prt_dsc_docmedia(prt_mediasize[prt_media].name,
-                               prt_mediasize[prt_media].width,
-                               prt_mediasize[prt_media].height,
-                               (double)0, NULL, NULL);
-    // Define fonts needed
-    if (!prt_out_mbyte || prt_use_courier)
-       prt_dsc_font_resource("DocumentNeededResources", &prt_ps_courier_font);
-    if (prt_out_mbyte)
-    {
-       prt_dsc_font_resource((prt_use_courier ? NULL
-                               : "DocumentNeededResources"), &prt_ps_mb_font);
-       if (!prt_custom_cmap)
-           prt_dsc_resources(NULL, "cmap", prt_cmap);
-    }
-
-    // Search for external resources VIM supplies
-    if (!prt_find_resource("prolog", res_prolog))
-    {
-       semsg(_(e_cant_find_postscript_resource_file_str_ps), "prolog");
-       goto theend;
-    }
-    if (!prt_open_resource(res_prolog))
-       goto theend;
-    if (!prt_check_resource(res_prolog, PRT_PROLOG_VERSION))
-       goto theend;
-    if (prt_out_mbyte)
-    {
-       // Look for required version of multi-byte printing procset
-       if (!prt_find_resource("cidfont", res_cidfont))
-       {
-           semsg(_(e_cant_find_postscript_resource_file_str_ps), "cidfont");
-           goto theend;
-       }
-       if (!prt_open_resource(res_cidfont))
-           goto theend;
-       if (!prt_check_resource(res_cidfont, PRT_CID_PROLOG_VERSION))
-           goto theend;
-    }
-
-    // Find an encoding to use for printing.
-    // Check 'printencoding'. If not set or not found, then use 'encoding'. If
-    // that cannot be found then default to "latin1".
-    // Note: VIM specific encoding header is always skipped.
-    if (!prt_out_mbyte)
-    {
-       p_encoding = enc_skip(p_penc);
-       if (*p_encoding == NUL
-               || !prt_find_resource((char *)p_encoding, res_encoding))
-       {
-           // 'printencoding' not set or not supported - find alternate
-           int         props;
-
-           p_encoding = enc_skip(p_enc);
-           props = enc_canon_props(p_encoding);
-           if (!(props & ENC_8BIT)
-                   || !prt_find_resource((char *)p_encoding, res_encoding))
-               // 8-bit 'encoding' is not supported
-           {
-               // Use latin1 as default printing encoding
-               p_encoding = (char_u *)"latin1";
-               if (!prt_find_resource((char *)p_encoding, res_encoding))
-               {
-                   semsg(_(e_cant_find_postscript_resource_file_str_ps),
-                           p_encoding);
-                   goto theend;
-               }
-           }
-       }
-       if (!prt_open_resource(res_encoding))
-           goto theend;
-       // For the moment there are no checks on encoding resource files to
-       // perform
-    }
-    else
-    {
-       p_encoding = enc_skip(p_penc);
-       if (*p_encoding == NUL)
-           p_encoding = enc_skip(p_enc);
-       if (prt_use_courier)
-       {
-           // Include ASCII range encoding vector
-           if (!prt_find_resource(prt_ascii_encoding, res_encoding))
-           {
-               semsg(_(e_cant_find_postscript_resource_file_str_ps),
-                                                         prt_ascii_encoding);
-               goto theend;
-           }
-           if (!prt_open_resource(res_encoding))
-               goto theend;
-           // For the moment there are no checks on encoding resource files to
-           // perform
-       }
-    }
-
-    prt_conv.vc_type = CONV_NONE;
-    if (!(enc_canon_props(p_enc) & enc_canon_props(p_encoding) & ENC_8BIT))
-    {
-       // Set up encoding conversion if required
-       if (FAIL == convert_setup(&prt_conv, p_enc, p_encoding))
-       {
-           semsg(_(e_unable_to_convert_to_print_encoding_str), p_encoding);
-           goto theend;
-       }
-       prt_do_conv = TRUE;
-    }
-    prt_do_conv = prt_conv.vc_type != CONV_NONE;
-
-    if (prt_out_mbyte && prt_custom_cmap)
-    {
-       // Find user supplied CMap
-       if (!prt_find_resource(prt_cmap, res_cmap))
-       {
-           semsg(_(e_cant_find_postscript_resource_file_str_ps), prt_cmap);
-           goto theend;
-       }
-       if (!prt_open_resource(res_cmap))
-           goto theend;
-    }
-
-    // List resources supplied
-    STRCPY(buffer, res_prolog->title);
-    STRCAT(buffer, " ");
-    STRCAT(buffer, res_prolog->version);
-    prt_dsc_resources("DocumentSuppliedResources", "procset", buffer);
-    if (prt_out_mbyte)
-    {
-       STRCPY(buffer, res_cidfont->title);
-       STRCAT(buffer, " ");
-       STRCAT(buffer, res_cidfont->version);
-       prt_dsc_resources(NULL, "procset", buffer);
-
-       if (prt_custom_cmap)
-       {
-           STRCPY(buffer, res_cmap->title);
-           STRCAT(buffer, " ");
-           STRCAT(buffer, res_cmap->version);
-           prt_dsc_resources(NULL, "cmap", buffer);
-       }
-    }
-    if (!prt_out_mbyte || prt_use_courier)
-    {
-       STRCPY(buffer, res_encoding->title);
-       STRCAT(buffer, " ");
-       STRCAT(buffer, res_encoding->version);
-       prt_dsc_resources(NULL, "encoding", buffer);
-    }
-    prt_dsc_requirements(prt_duplex, prt_tumble, prt_collate,
-# ifdef FEAT_SYN_HL
-                                       psettings->do_syntax
-# else
-                                       0
-# endif
-                                       , prt_num_copies);
-    prt_dsc_noarg("EndComments");
-
-    /*
-     * PS Document page defaults
-     */
-    prt_dsc_noarg("BeginDefaults");
-
-    // List font resources most likely common to all pages
-    if (!prt_out_mbyte || prt_use_courier)
-       prt_dsc_font_resource("PageResources", &prt_ps_courier_font);
-    if (prt_out_mbyte)
-    {
-       prt_dsc_font_resource((prt_use_courier ? NULL : "PageResources"),
-                                                           &prt_ps_mb_font);
-       if (!prt_custom_cmap)
-           prt_dsc_resources(NULL, "cmap", prt_cmap);
-    }
-
-    // Paper will be used for all pages
-    prt_dsc_textline("PageMedia", prt_mediasize[prt_media].name);
-
-    prt_dsc_noarg("EndDefaults");
-
-    /*
-     * PS Document prolog inclusion - all required procsets.
-     */
-    prt_dsc_noarg("BeginProlog");
-
-    // Add required procsets - NOTE: order is important!
-    if (!prt_add_resource(res_prolog))
-       goto theend;
-    if (prt_out_mbyte)
-    {
-       // Add CID font procset, and any user supplied CMap
-       if (!prt_add_resource(res_cidfont))
-           goto theend;
-       if (prt_custom_cmap && !prt_add_resource(res_cmap))
-           goto theend;
-    }
-
-    if (!prt_out_mbyte || prt_use_courier)
-       // There will be only one Roman font encoding to be included in the PS
-       // file.
-       if (!prt_add_resource(res_encoding))
-           goto theend;
-
-    prt_dsc_noarg("EndProlog");
-
-    /*
-     * PS Document setup - must appear after the prolog
-     */
-    prt_dsc_noarg("BeginSetup");
-
-    // Device setup - page size and number of uncollated copies
-    prt_write_int((int)prt_mediasize[prt_media].width);
-    prt_write_int((int)prt_mediasize[prt_media].height);
-    prt_write_int(0);
-    prt_write_string("sps\n");
-    prt_write_int(prt_num_copies);
-    prt_write_string("nc\n");
-    prt_write_boolean(prt_duplex);
-    prt_write_boolean(prt_tumble);
-    prt_write_string("dt\n");
-    prt_write_boolean(prt_collate);
-    prt_write_string("c\n");
-
-    // Font resource inclusion and definition
-    if (!prt_out_mbyte || prt_use_courier)
-    {
-       // When using Courier for ASCII range when printing multi-byte, need to
-       // pick up ASCII encoding to use with it.
-       if (prt_use_courier)
-           p_encoding = (char_u *)prt_ascii_encoding;
-       prt_dsc_resources("IncludeResource", "font",
-                         prt_ps_courier_font.ps_fontname[PRT_PS_FONT_ROMAN]);
-       prt_def_font("F0", (char *)p_encoding, (int)prt_line_height,
-                    prt_ps_courier_font.ps_fontname[PRT_PS_FONT_ROMAN]);
-       prt_dsc_resources("IncludeResource", "font",
-                         prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLD]);
-       prt_def_font("F1", (char *)p_encoding, (int)prt_line_height,
-                    prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLD]);
-       prt_dsc_resources("IncludeResource", "font",
-                         prt_ps_courier_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
-       prt_def_font("F2", (char *)p_encoding, (int)prt_line_height,
-                    prt_ps_courier_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
-       prt_dsc_resources("IncludeResource", "font",
-                         prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
-       prt_def_font("F3", (char *)p_encoding, (int)prt_line_height,
-                    prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
-    }
-    if (prt_out_mbyte)
-    {
-       // Define the CID fonts to be used in the job.  Typically CJKV fonts do
-       // not have an italic form being a western style, so where no font is
-       // defined for these faces VIM falls back to an existing face.
-       // Note: if using Courier for the ASCII range then the printout will
-       // have bold/italic/bolditalic regardless of the setting of printmbfont.
-       prt_dsc_resources("IncludeResource", "font",
-                         prt_ps_mb_font.ps_fontname[PRT_PS_FONT_ROMAN]);
-       if (!prt_custom_cmap)
-           prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
-       prt_def_cidfont("CF0", (int)prt_line_height,
-                       prt_ps_mb_font.ps_fontname[PRT_PS_FONT_ROMAN]);
-
-       if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD] != NULL)
-       {
-           prt_dsc_resources("IncludeResource", "font",
-                             prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD]);
-           if (!prt_custom_cmap)
-               prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
-           prt_def_cidfont("CF1", (int)prt_line_height,
-                           prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD]);
-       }
-       else
-           // Use ROMAN for BOLD
-           prt_dup_cidfont("CF0", "CF1");
-
-       if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE] != NULL)
-       {
-           prt_dsc_resources("IncludeResource", "font",
-                             prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
-           if (!prt_custom_cmap)
-               prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
-           prt_def_cidfont("CF2", (int)prt_line_height,
-                           prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
-       }
-       else
-           // Use ROMAN for OBLIQUE
-           prt_dup_cidfont("CF0", "CF2");
-
-       if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE] != NULL)
-       {
-           prt_dsc_resources("IncludeResource", "font",
-                             prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
-           if (!prt_custom_cmap)
-               prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
-           prt_def_cidfont("CF3", (int)prt_line_height,
-                           prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
-       }
-       else
-           // Use BOLD for BOLDOBLIQUE
-           prt_dup_cidfont("CF1", "CF3");
-    }
-
-    // Misc constant vars used for underlining and background rects
-    prt_def_var("UO", PRT_PS_FONT_TO_USER(prt_line_height,
-                                               prt_ps_font->uline_offset), 2);
-    prt_def_var("UW", PRT_PS_FONT_TO_USER(prt_line_height,
-                                                prt_ps_font->uline_width), 2);
-    prt_def_var("BO", prt_bgcol_offset, 2);
-
-    prt_dsc_noarg("EndSetup");
-
-    // Fail if any problems writing out to the PS file
-    retval = !prt_file_error;
-
-theend:
-    vim_free(res_prolog);
-    vim_free(res_encoding);
-    vim_free(res_cidfont);
-    vim_free(res_cmap);
-
-    return retval;
-}
-
-    void
-mch_print_end(prt_settings_T *psettings)
-{
-    prt_dsc_noarg("Trailer");
-
-    /*
-     * Output any info we don't know in toto until we finish
-     */
-    prt_dsc_ints("Pages", 1, &prt_page_num);
-
-    prt_dsc_noarg("EOF");
-
-    // Write CTRL-D to close serial communication link if used.
-    // NOTHING MUST BE WRITTEN AFTER THIS!
-    prt_write_file((char_u *)"\004");
-
-    if (!prt_file_error && psettings->outfile == NULL
-                                       && !got_int && !psettings->user_abort)
-    {
-       // Close the file first.
-       if (prt_ps_fd != NULL)
-       {
-           fclose(prt_ps_fd);
-           prt_ps_fd = NULL;
-       }
-       prt_message((char_u *)_("Sending to printer..."));
-
-       // Not printing to a file: use 'printexpr' to print the file.
-       if (eval_printexpr(prt_ps_file_name, psettings->arguments) == FAIL)
-           emsg(_(e_failed_to_print_postscript_file));
-       else
-           prt_message((char_u *)_("Print job sent."));
-    }
-
-    mch_print_cleanup();
-}
-
-    int
-mch_print_end_page(void)
-{
-    prt_flush_buffer();
-
-    prt_write_string("re sp\n");
-
-    prt_dsc_noarg("PageTrailer");
-
-    return !prt_file_error;
-}
-
-    int
-mch_print_begin_page(char_u *str UNUSED)
-{
-    int                page_num[2];
-
-    prt_page_num++;
-
-    page_num[0] = page_num[1] = prt_page_num;
-    prt_dsc_ints("Page", 2, page_num);
-
-    prt_dsc_noarg("BeginPageSetup");
-
-    prt_write_string("sv\n0 g\n");
-    prt_in_ascii = !prt_out_mbyte;
-    if (prt_out_mbyte)
-       prt_write_string("CF0 sf\n");
-    else
-       prt_write_string("F0 sf\n");
-    prt_fgcol = PRCOLOR_BLACK;
-    prt_bgcol = PRCOLOR_WHITE;
-    prt_font = PRT_PS_FONT_ROMAN;
-
-    // Set up page transformation for landscape printing.
-    if (!prt_portrait)
-    {
-       prt_write_int(-((int)prt_mediasize[prt_media].width));
-       prt_write_string("sl\n");
-    }
-
-    prt_dsc_noarg("EndPageSetup");
-
-    // We have reset the font attributes, force setting them again.
-    curr_bg = (long_u)0xffffffff;
-    curr_fg = (long_u)0xffffffff;
-    curr_bold = MAYBE;
-
-    return !prt_file_error;
-}
-
-    int
-mch_print_blank_page(void)
-{
-    return (mch_print_begin_page(NULL) ? (mch_print_end_page()) : FALSE);
-}
-
-static float prt_pos_x = 0;
-static float prt_pos_y = 0;
-
-    void
-mch_print_start_line(int margin, int page_line)
-{
-    prt_pos_x = prt_left_margin;
-    if (margin)
-       prt_pos_x -= prt_number_width;
-
-    prt_pos_y = prt_top_margin - prt_first_line_height -
-                                       page_line * prt_line_height;
-
-    prt_attribute_change = TRUE;
-    prt_need_moveto = TRUE;
-    prt_half_width = FALSE;
-}
-
-    int
-mch_print_text_out(char_u *textp, int len UNUSED)
-{
-    char_u     *p = textp;
-    int                need_break;
-    char_u     ch;
-    char_u      ch_buff[8];
-    float       char_width;
-    float       next_pos;
-    int                in_ascii;
-    int                half_width;
-    char_u     *tofree = NULL;
-
-    char_width = prt_char_width;
-
-    // Ideally VIM would create a rearranged CID font to combine a Roman and
-    // CJKV font to do what VIM is doing here - use a Roman font for characters
-    // in the ASCII range, and the original CID font for everything else.
-    // The problem is that GhostScript still (as of 8.13) does not support
-    // rearranged fonts even though they have been documented by Adobe for 7
-    // years!  If they ever do, a lot of this code will disappear.
-    if (prt_use_courier)
-    {
-       in_ascii = (len == 1 && *p < 0x80);
-       if (prt_in_ascii)
-       {
-           if (!in_ascii)
-           {
-               // No longer in ASCII range - need to switch font
-               prt_in_ascii = FALSE;
-               prt_need_font = TRUE;
-               prt_attribute_change = TRUE;
-           }
-       }
-       else if (in_ascii)
-       {
-           // Now in ASCII range - need to switch font
-           prt_in_ascii = TRUE;
-           prt_need_font = TRUE;
-           prt_attribute_change = TRUE;
-       }
-    }
-    if (prt_out_mbyte)
-    {
-       half_width = ((*mb_ptr2cells)(p) == 1);
-       if (half_width)
-           char_width /= 2;
-       if (prt_half_width)
-       {
-           if (!half_width)
-           {
-               prt_half_width = FALSE;
-               prt_pos_x += prt_char_width/4;
-               prt_need_moveto = TRUE;
-               prt_attribute_change = TRUE;
-           }
-       }
-       else if (half_width)
-       {
-           prt_half_width = TRUE;
-           prt_pos_x += prt_char_width/4;
-           prt_need_moveto = TRUE;
-           prt_attribute_change = TRUE;
-       }
-    }
-
-    // Output any required changes to the graphics state, after flushing any
-    // text buffered so far.
-    if (prt_attribute_change)
-    {
-       prt_flush_buffer();
-       // Reset count of number of chars that will be printed
-       prt_text_run = 0;
-
-       if (prt_need_moveto)
-       {
-           prt_pos_x_moveto = prt_pos_x;
-           prt_pos_y_moveto = prt_pos_y;
-           prt_do_moveto = TRUE;
-
-           prt_need_moveto = FALSE;
-       }
-       if (prt_need_font)
-       {
-           if (!prt_in_ascii)
-               prt_write_string("CF");
-           else
-               prt_write_string("F");
-           prt_write_int(prt_font);
-           prt_write_string("sf\n");
-           prt_need_font = FALSE;
-       }
-       if (prt_need_fgcol)
-       {
-           int     r, g, b;
-           r = ((unsigned)prt_fgcol & 0xff0000) >> 16;
-           g = ((unsigned)prt_fgcol & 0xff00) >> 8;
-           b = prt_fgcol & 0xff;
-
-           prt_write_real(r / 255.0, 3);
-           if (r == g && g == b)
-               prt_write_string("g\n");
-           else
-           {
-               prt_write_real(g / 255.0, 3);
-               prt_write_real(b / 255.0, 3);
-               prt_write_string("r\n");
-           }
-           prt_need_fgcol = FALSE;
-       }
-
-       if (prt_bgcol != PRCOLOR_WHITE)
-       {
-           prt_new_bgcol = prt_bgcol;
-           if (prt_need_bgcol)
-               prt_do_bgcol = TRUE;
-       }
-       else
-           prt_do_bgcol = FALSE;
-       prt_need_bgcol = FALSE;
-
-       if (prt_need_underline)
-           prt_do_underline = prt_underline;
-       prt_need_underline = FALSE;
-
-       prt_attribute_change = FALSE;
-    }
-
-    if (prt_do_conv)
-    {
-       // Convert from multi-byte to 8-bit encoding
-       tofree = p = string_convert(&prt_conv, p, &len);
-       if (p == NULL)
-       {
-           p = (char_u *)"";
-           len = 0;
-       }
-    }
-
-    if (prt_out_mbyte)
-    {
-       // Multi-byte character strings are represented more efficiently as hex
-       // strings when outputting clean 8 bit PS.
-       while (len-- > 0)
-       {
-           ch = prt_hexchar[(unsigned)(*p) >> 4];
-           ga_append(&prt_ps_buffer, ch);
-           ch = prt_hexchar[(*p) & 0xf];
-           ga_append(&prt_ps_buffer, ch);
-           p++;
-       }
-    }
-    else
-    {
-       // Add next character to buffer of characters to output.
-       // Note: One printed character may require several PS characters to
-       // represent it, but we only count them as one printed character.
-       ch = *p;
-       if (ch < 32 || ch == '(' || ch == ')' || ch == '\\')
-       {
-           // Convert non-printing characters to either their escape or octal
-           // sequence, ensures PS sent over a serial line does not interfere
-           // with the comms protocol.
-           ga_append(&prt_ps_buffer, '\\');
-           switch (ch)
-           {
-               case BS:   ga_append(&prt_ps_buffer, 'b'); break;
-               case TAB:  ga_append(&prt_ps_buffer, 't'); break;
-               case NL:   ga_append(&prt_ps_buffer, 'n'); break;
-               case FF:   ga_append(&prt_ps_buffer, 'f'); break;
-               case CAR:  ga_append(&prt_ps_buffer, 'r'); break;
-               case '(':  ga_append(&prt_ps_buffer, '('); break;
-               case ')':  ga_append(&prt_ps_buffer, ')'); break;
-               case '\\': ga_append(&prt_ps_buffer, '\\'); break;
-
-               default:
-                          sprintf((char *)ch_buff, "%03o", (unsigned int)ch);
-                          ga_append(&prt_ps_buffer, ch_buff[0]);
-                          ga_append(&prt_ps_buffer, ch_buff[1]);
-                          ga_append(&prt_ps_buffer, ch_buff[2]);
-                          break;
-           }
-       }
-       else
-           ga_append(&prt_ps_buffer, ch);
-    }
-
-    // Need to free any translated characters
-    vim_free(tofree);
-
-    prt_text_run += char_width;
-    prt_pos_x += char_width;
-
-    // The downside of fp - use relative error on right margin check
-    next_pos = prt_pos_x + prt_char_width;
-    need_break = (next_pos > prt_right_margin) &&
-                   ((next_pos - prt_right_margin) > (prt_right_margin*1e-5));
-
-    if (need_break)
-       prt_flush_buffer();
-
-    return need_break;
-}
-
-    void
-mch_print_set_font(int iBold, int iItalic, int iUnderline)
-{
-    int                font = 0;
-
-    if (iBold)
-       font |= 0x01;
-    if (iItalic)
-       font |= 0x02;
-
-    if (font != prt_font)
-    {
-       prt_font = font;
-       prt_attribute_change = TRUE;
-       prt_need_font = TRUE;
-    }
-    if (prt_underline != iUnderline)
-    {
-       prt_underline = iUnderline;
-       prt_attribute_change = TRUE;
-       prt_need_underline = TRUE;
-    }
-}
-
-    void
-mch_print_set_bg(long_u bgcol)
-{
-    prt_bgcol = (int)bgcol;
-    prt_attribute_change = TRUE;
-    prt_need_bgcol = TRUE;
-}
-
-    void
-mch_print_set_fg(long_u fgcol)
-{
-    if (fgcol == (long_u)prt_fgcol)
-       return;
-
-    prt_fgcol = (int)fgcol;
-    prt_attribute_change = TRUE;
-    prt_need_fgcol = TRUE;
-}
-
-#endif //FEAT_POSTSCRIPT
 #endif //FEAT_PRINTER
diff --git a/src/hardcopy.h b/src/hardcopy.h
new file mode 100644 (file)
index 0000000..0d8d6e5
--- /dev/null
@@ -0,0 +1,46 @@
+/* vi:set ts=8 sts=4 sw=4 noet:
+ *
+ * VIM - Vi IMproved   by Bram Moolenaar
+ *
+ * Do ":help uganda"  in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ * See README.txt for an overview of the Vim source code.
+ */
+
+/*
+ * hardcopy.h: common stuff for hardcopy feature
+ */
+
+#ifndef HARDCOPY_H
+#define HARDCOPY_H
+
+#include "vim.h"
+
+#if defined(FEAT_PRINTER)
+
+# define PRCOLOR_BLACK (long_u)0
+# define PRCOLOR_WHITE (long_u)0xFFFFFFL
+
+extern int     prt_curr_italic;
+extern int     prt_curr_bold;
+extern int     prt_curr_underline;
+extern long_u  prt_curr_bg;
+extern long_u  prt_curr_fg;
+extern int     prt_page_count;
+
+# if defined(FEAT_POSTSCRIPT) || defined(FEAT_PRINT_PANGO)
+typedef struct
+{
+    char       *name;
+    double     width; // Width and height in points for portrait
+    double     height;
+} prt_mediasize_T;
+
+#  define PRT_MEDIASIZE_LEN  14
+
+extern prt_mediasize_T prt_mediasize[PRT_MEDIASIZE_LEN];
+# endif
+
+#endif
+
+#endif
diff --git a/src/hardcopy_pango.c b/src/hardcopy_pango.c
new file mode 100644 (file)
index 0000000..e3d46a5
--- /dev/null
@@ -0,0 +1,783 @@
+/* vi:set ts=8 sts=4 sw=4 noet:
+ *
+ * VIM - Vi IMproved   by Bram Moolenaar
+ *
+ * Do ":help uganda"  in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ * See README.txt for an overview of the Vim source code.
+ */
+
+/*
+ * See https://www.cairographics.org/documentation/using_the_postscript_surface/
+ * for information about Cairo's postscript backend.
+ *
+ * Related:
+ * https://www.cairographics.org/manual/cairo-PDF-Surfaces.html#cairo-pdf-surface-set-size
+ */
+
+#include "vim.h"
+#include "version.h"
+
+#ifdef FEAT_PRINT_PANGO
+
+#include "hardcopy.h"
+#include "hardcopy_pango.h"
+
+#include <cairo/cairo-ps.h>
+#include <cairo/cairo-pdf.h>
+#include <pango/pango.h>
+#include <pango/pangocairo.h>
+
+#define PRINT_DPI 72.0
+
+#define PRT_DRAW_BOLD 1
+#define PRT_DRAW_ITALIC 2
+#define PRT_DRAW_UNDERLINE 4
+
+#define GET_Y(l) (pctx.top_margin + (l) * pctx.char_height)
+#define GET_TEXT_Y(l) (GET_Y(l) + pctx.char_ascent)
+
+typedef enum
+{
+    PRT_FORMAT_PS,  // Postscript
+    PRT_FORMAT_PDF, // PDF
+} prt_format_T;
+
+/*
+ * Global state for printing
+ */
+static struct
+{
+    // Cairo surface to draw on. Note that surface is scaled in points (1/72 of
+    // an inch), for all formats.
+    cairo_surface_t *surface;
+    cairo_t        *cr;
+    PangoContext    *text_context;
+
+    PangoFontDescription *font;
+
+    char_u *filename;
+
+    int media;
+    bool portrait;
+    bool collate;
+    bool duplex;
+    bool tumble; // Short edge duplex
+
+    prt_format_T format;
+
+    double char_width;
+    double char_ascent;
+    double char_height;
+    double line_spacing;
+    double ul_pos;
+    double ul_thickness;
+
+    double page_width;
+    double page_height;
+
+    double left_margin;
+    double right_margin;
+    double top_margin;
+    double bottom_margin;
+
+    double number_width;
+
+    // Current position to draw from
+    double cur_x;
+    int cur_line;
+    long_u cur_fg;
+    long_u cur_bg;
+
+    char_u draw_flags;
+
+#ifdef USE_GTK4_PRINT_DIALOG
+    bool dialog;
+#endif
+} pctx;
+
+static const char *print_formats[] = {
+    [PRT_FORMAT_PS] = "postscript",
+    [PRT_FORMAT_PDF] = "pdf",
+};
+
+/*
+ * Convert the margin specified in "idx" to device units. "physsize" is the size
+ * (width or height).
+ */
+    static double
+to_device_units(int idx, double physsize, int def_number)
+{
+    float      ret;
+    int                u;
+    int                nr;
+
+    u = prt_get_unit(idx);
+    if (u == PRT_UNIT_NONE)
+    {
+       u = PRT_UNIT_PERC;
+       nr = def_number;
+    }
+    else
+       nr = printer_opts[idx].number;
+
+    switch (u)
+    {
+       case PRT_UNIT_INCH:
+           ret = (nr * PRINT_DPI);
+           break;
+       case PRT_UNIT_MM:
+           ret = (nr * PRINT_DPI) / 25.4;
+           break;
+       case PRT_UNIT_POINT:
+           ret = (double)nr;
+           break;
+       case PRT_UNIT_PERC:
+       default:
+           ret = physsize * (nr / 100.0);
+           break;
+    }
+
+    return ret;
+}
+
+/*
+ * Calculate margins for given width and height from printoptions settings.
+ */
+    static void
+get_page_margins(
+       double  width,
+       double  height,
+       double  *left,
+       double  *right,
+       double  *top,
+       double  *bottom)
+{
+    *left = to_device_units(OPT_PRINT_LEFT, width, 10);
+    *right = width - to_device_units(OPT_PRINT_RIGHT, width, 5);
+
+    *top = to_device_units(OPT_PRINT_TOP, height, 5);
+    *bottom = height - to_device_units(OPT_PRINT_BOT, height, 5);
+}
+
+/*
+ * Get characters per line (excluding margins).
+ */
+    static int
+get_cpl(void)
+{
+    if (prt_use_number())
+    {
+       pctx.number_width = PRINT_NUMBER_WIDTH * pctx.char_width;
+       pctx.left_margin += pctx.number_width;
+    }
+    else
+       pctx.number_width = 0.0;
+
+    return (int)((pctx.right_margin - pctx.left_margin) / pctx.char_width);
+}
+
+/*
+ * Get number of lines of text that fit on a page (excluding the header).
+ */
+    static int
+get_lpp(void)
+{
+    int lpp;
+
+    // Calculate lpp
+    lpp = (int)((pctx.bottom_margin - pctx.top_margin) / pctx.char_height);
+
+    // Adjust top margin if there is a header
+    pctx.top_margin += pctx.char_height * prt_header_height();
+
+    return lpp - prt_header_height();
+}
+
+/*
+ * Get the font descrption from the given name. Returns NULL on failure.
+ */
+    static PangoFontDescription *
+get_font(char_u *name)
+{
+    PangoFontDescription *font;
+
+    if (name == NULL)
+       return NULL;
+
+    font = pango_font_description_from_string((const char *)name);
+    if (font == NULL)
+    {
+       semsg(_(e_unknown_printer_font_str), name);
+       return NULL;
+    }
+
+    // Ensure a size is set
+    if (pango_font_description_get_size(font) <= 0)
+       pango_font_description_set_size(font, 10 * PANGO_SCALE);
+
+    return font;
+}
+
+    int
+mch_print_init(
+       prt_settings_T  *psettings,
+       char_u          *jobname,
+       int             forceit UNUSED)
+{
+    char_u     *paper_name;
+    int                paper_strlen;
+    bool       portrait;
+    int                media;
+    char_u     *filename = NULL;
+#ifdef USE_GTK4_PRINT_DIALOG
+    bool       dialog = false;
+
+    if (gui.in_use && !forceit && psettings->outfile == NULL)
+    {
+       filename = gui_gtk4_print_dialog(psettings, jobname,
+               &pctx.page_width, &pctx.page_height);
+
+       if (filename == NULL)
+           return FAIL;
+       dialog = true;
+    }
+#endif
+
+    pctx.format = PRT_FORMAT_PS;
+
+    // Let GTK handle stuff when using dialog. Not sure if our postscript DSC
+    // comments can intefere with dialog settings, but don't add them...
+#ifdef USE_GTK4_PRINT_DIALOG
+    if (!dialog)
+#endif
+    {
+       // Get paper type to use
+       portrait = (!printer_opts[OPT_PRINT_PORTRAIT].present ||
+               TOLOWER_ASC(printer_opts[OPT_PRINT_PORTRAIT].string[0]) == 'y');
+       pctx.portrait = portrait;
+       if (printer_opts[OPT_PRINT_PAPER].present)
+       {
+           paper_name = printer_opts[OPT_PRINT_PAPER].string;
+           paper_strlen = printer_opts[OPT_PRINT_PAPER].strlen;
+       }
+       else
+       {
+           paper_name = (char_u *)"A4";
+           paper_strlen = 2;
+       }
+
+       // Set width and height using given paper type
+       for (media = 0; media < (int)PRT_MEDIASIZE_LEN; ++media)
+           if ((int)STRLEN(prt_mediasize[media].name) == paper_strlen
+                   && STRNICMP(prt_mediasize[media].name,
+                       paper_name, paper_strlen) == 0)
+               break;
+       if (media == PRT_MEDIASIZE_LEN)
+           media = 0;
+       pctx.media = media;
+
+       if (portrait)
+       {
+           pctx.page_width = prt_mediasize[media].width;
+           pctx.page_height = prt_mediasize[media].height;
+       }
+       else
+       {
+           pctx.page_width = prt_mediasize[media].height;
+           pctx.page_height = prt_mediasize[media].width;
+       }
+
+       if (printer_opts[OPT_PRINT_FORMAT].present)
+       {
+           for (int i = 0; i < (int)ARRAY_LENGTH(print_formats); i++)
+               if (STRNCMP(printer_opts[OPT_PRINT_FORMAT].string,
+                           print_formats[i],
+                           printer_opts[OPT_PRINT_FORMAT].strlen) == 0)
+               {
+                   pctx.format = i;
+                   break;
+               }
+       }
+    }
+    psettings->user_abort = FALSE;
+
+#ifdef USE_GTK4_PRINT_DIALOG
+    if (dialog)
+       // Always use PDF for dialog
+       pctx.format = PRT_FORMAT_PDF;
+#endif
+
+    if (filename == NULL)
+    {
+       // If the user didn't specify a file name, use a temp file.
+       if (psettings->outfile == NULL)
+       {
+           filename = vim_tempname('p', TRUE);
+           if (filename == NULL)
+           {
+               emsg(_(e_cant_get_temp_file_name));
+               return FAIL;
+           }
+       }
+       else
+           filename = expand_env_save(psettings->outfile);
+    }
+
+    // If using dialog, then always use PDF
+    if (pctx.format == PRT_FORMAT_PS)
+       pctx.surface = cairo_ps_surface_create((const char *)filename,
+               pctx.page_width, pctx.page_height);
+    else
+       pctx.surface = cairo_pdf_surface_create((const char *)filename,
+               pctx.page_width, pctx.page_height);
+
+    pctx.filename = (char_u *)filename;
+
+    if (cairo_surface_status(pctx.surface) != CAIRO_STATUS_SUCCESS)
+    {
+       emsg(_(e_cant_open_postscript_output_file));
+       mch_print_cleanup();
+       return FAIL;
+    }
+
+    pctx.cr = cairo_create(pctx.surface);
+    pctx.text_context = pango_cairo_create_context(pctx.cr);
+    pctx.font = get_font(p_pfn);
+
+    if (pctx.font == NULL)
+    {
+       mch_print_cleanup();
+       return FAIL;
+    }
+
+    pango_context_set_font_description(pctx.text_context, pctx.font);
+    // Pango DPI is 96, must adjust
+    pango_cairo_context_set_resolution(pctx.text_context, 72.0);
+
+    // Get line height and char width
+    {
+       PangoLayout         *layout;
+       PangoFontMetrics    *metrics;
+       int                 ascent;
+       int                 descent;
+
+       layout = pango_layout_new(pctx.text_context);
+       pango_layout_set_font_description(layout, pctx.font);
+
+       metrics = pango_context_get_metrics(
+               pango_layout_get_context(layout), pctx.font, NULL);
+
+       pctx.char_width =
+           (double)pango_font_metrics_get_approximate_char_width(metrics)
+           / PANGO_SCALE;
+
+       ascent = pango_font_metrics_get_ascent(metrics);
+       descent = pango_font_metrics_get_descent(metrics);
+
+       pctx.char_ascent = (double)ascent / PANGO_SCALE;
+       pctx.char_height = (double)(ascent + descent
+               + (PANGO_SCALE * 15.0) / 16.0) / PANGO_SCALE;
+
+       pctx.line_spacing = (double)
+           (pango_font_metrics_get_height(metrics) - (ascent + descent))
+           / PANGO_SCALE;
+
+       pctx.ul_pos =
+           (double)pango_font_metrics_get_underline_position(metrics)
+           / PANGO_SCALE;
+       pctx.ul_thickness =
+           (double)pango_font_metrics_get_underline_thickness(metrics)
+           / PANGO_SCALE;
+
+       pango_font_metrics_unref(metrics);
+       g_object_unref(layout);
+    }
+
+    // Calculate margins
+    {
+       double  left;
+       double  right;
+       double  top;
+       double  bottom;
+
+       get_page_margins(pctx.page_width, pctx.page_height, &left, &right, &top,
+               &bottom);
+       pctx.left_margin = left;
+       pctx.right_margin = right;
+       pctx.top_margin = top;
+       pctx.bottom_margin = bottom;
+    }
+
+    psettings->chars_per_line = get_cpl();
+    psettings->lines_per_page = get_lpp();
+
+    // Postscript handles collating pages, don't let Vim handle it. This means
+    // we don't need to worry about the "jobsplit" value in 'printoptions'
+    psettings->n_collated_copies = 1;
+    psettings->n_uncollated_copies = 1;
+
+    psettings->jobname = jobname;
+
+#ifdef USE_GTK4_PRINT_DIALOG
+    if (!dialog)
+#endif
+    {
+       pctx.collate = (!printer_opts[OPT_PRINT_COLLATE].present ||
+               TOLOWER_ASC(printer_opts[OPT_PRINT_COLLATE].string[0]) == 'y');
+
+       pctx.duplex = TRUE;
+       pctx.tumble = FALSE;
+       psettings->duplex = TRUE;
+       if (printer_opts[OPT_PRINT_DUPLEX].present)
+       {
+           if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "off", 3) == 0)
+           {
+               pctx.duplex = FALSE;
+               psettings->duplex = 0;
+           }
+           else if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "short",
+                       5) == 0)
+               pctx.tumble = TRUE;
+       }
+    }
+
+#ifdef USE_GTK4_PRINT_DIALOG
+    pctx.dialog = dialog;
+#endif
+
+    return OK;
+}
+
+/*
+ * Emit a DSC comment, "fmt" may be NULL to indicate no arguments.
+ */
+    static void
+emit_dsc_comment(char *comment, char *fmt, ...)
+{
+    static char buf[257];
+
+    va_list ap;
+    int            len = sizeof(buf);
+    int            o;
+    char    *s = buf;
+
+    if (fmt == NULL)
+    {
+       o = vim_snprintf(s, len, "%%%%%s", comment);
+       goto exit;
+    }
+    else
+       o = vim_snprintf(s, len, "%%%%%s: ", comment);
+
+    len -= o;
+    s += o;
+    if (len <= 0)
+       return;
+
+    va_start(ap, fmt);
+    vim_vsnprintf(s, len, fmt, ap);
+    va_end(ap);
+
+exit:
+    cairo_ps_surface_dsc_comment(pctx.surface, buf);
+}
+
+    int
+mch_print_begin(prt_settings_T *psettings)
+{
+    char user[256];
+
+    pctx.cur_x = 0.0;
+    pctx.cur_line = 0;
+    pctx.draw_flags = 0;
+
+#ifdef USE_GTK4_PRINT_DIALOG
+    if (pctx.dialog)
+       return TRUE;
+#endif
+
+    if (!get_user_name((char_u *)user, sizeof(user)))
+       STRCPY(user, "Unknown");
+
+    if (pctx.format == PRT_FORMAT_PS)
+    {
+       emit_dsc_comment("Title", (char *)psettings->jobname);
+       emit_dsc_comment("For", user);
+       emit_dsc_comment("Orientation",
+               pctx.portrait ? "Portrait" : "Landscape");
+       emit_dsc_comment("PageOrder", "Ascend");
+
+       emit_dsc_comment("DocumentMedia",
+               "%s %.2lf %.2lf 0 () ()",
+               prt_mediasize[pctx.media].name,
+               prt_mediasize[pctx.media].width,
+               prt_mediasize[pctx.media].height);
+
+       emit_dsc_comment("Requirements", "%s%s %s %s",
+               pctx.duplex ? "duplex" : "",
+               pctx.tumble ? "(tumble)" : "",
+               pctx.collate ? "collate" : "",
+               psettings->do_syntax ? "color" : "");
+
+       // In defaults section (defaults for every page)
+       emit_dsc_comment("BeginDefaults", NULL);
+       emit_dsc_comment("PageMedia", prt_mediasize[pctx.media].name);
+       emit_dsc_comment("EndDefaults", NULL);
+    }
+    else if (pctx.format == PRT_FORMAT_PDF)
+    {
+       char_u *str = (char_u *)user;
+
+       if (output_conv.vc_type != CONV_NONE)
+           str = string_convert(&output_conv, str, NULL);
+
+       cairo_pdf_surface_set_metadata(pctx.surface,
+               CAIRO_PDF_METADATA_AUTHOR, (char *)str);
+       if (output_conv.vc_type != CONV_NONE)
+           vim_free(str);
+
+       str = psettings->jobname;
+       if (output_conv.vc_type != CONV_NONE)
+           str = string_convert(&output_conv, str, NULL);
+       cairo_pdf_surface_set_metadata(pctx.surface,
+               CAIRO_PDF_METADATA_TITLE, (char *)str);
+       if (output_conv.vc_type != CONV_NONE)
+           vim_free(str);
+
+       cairo_pdf_surface_set_metadata(pctx.surface,
+               CAIRO_PDF_METADATA_CREATOR, VIM_VERSION_LONG);
+    }
+
+    return TRUE;
+}
+
+    void
+mch_print_end(prt_settings_T *psettings)
+{
+    // Make sure surface flushes to file or stream
+    cairo_surface_finish(pctx.surface);
+
+    if (psettings->outfile == NULL && !got_int && !psettings->user_abort
+           && pctx.filename != NULL
+#ifdef USE_GTK4_PRINT_DIALOG
+           && !pctx.dialog
+#endif
+       )
+    {
+       msg(_("Sending to printer..."));
+
+       // Not printing to a file: use 'printexpr' to print the file.
+       if (eval_printexpr(pctx.filename, psettings->arguments) == FAIL)
+           emsg(_(e_failed_to_print_postscript_file));
+       else
+           msg(_("Print job sent."));
+    }
+
+    mch_print_cleanup();
+}
+
+    int
+mch_print_end_page(void)
+{
+    cairo_show_page(pctx.cr);
+    return TRUE;
+}
+
+/*
+ * Do nothing because we create a new page for the cairo surface at the end of
+ * every page.
+ */
+    int
+mch_print_begin_page(char_u *str UNUSED)
+{
+    return TRUE;
+}
+
+    int
+mch_print_blank_page(void)
+{
+    mch_print_end_page();
+    return TRUE;
+}
+
+    void
+mch_print_start_line(int margin, int page_line)
+{
+    pctx.cur_x = pctx.left_margin;
+    if (margin)
+       pctx.cur_x -= pctx.number_width;
+
+    pctx.cur_line = page_line;
+}
+
+    static void
+long_to_rgb(long_u c, double *r, double *g, double *b)
+{
+    *r = ((c >> 16) & 0xFF) / 255.0;
+    *g = ((c >> 8) & 0xFF) / 255.0;
+    *b = ((c >> 0) & 0xFF) / 255.0;
+}
+
+/*
+ * Don't use PangoLayout for rendering the text, so that we can handle the
+ * positioning and sizing ourselves.
+ */
+    int
+mch_print_text_out(char_u *textp, int len)
+{
+    PangoAttrList      *attr_list;
+    GList              *item_list;
+    PangoGlyphString   *glyphs;
+    char_u             *str;
+    double             r, g, b;
+    double             width = 0;
+    double             next_x;
+
+    // Always convert to UTF-8, Pango expects that
+    if (output_conv.vc_type == CONV_NONE)
+       str = textp;
+    else
+       str = string_convert(&output_conv, textp, &len);
+
+    if (str == NULL)
+       return TRUE;
+
+    attr_list = pango_attr_list_new();
+
+#define INSERT_PANGO_ATTR(Attribute, AttrList, Start, End)  \
+    G_STMT_START{                                          \
+       PangoAttribute *tmp_attr_;                          \
+       tmp_attr_ = (Attribute);                            \
+       tmp_attr_->start_index = (Start);                   \
+       tmp_attr_->end_index = (End);                       \
+       pango_attr_list_insert((AttrList), tmp_attr_);      \
+    }G_STMT_END
+
+    // Underline attribute does not work with pango_itemize().
+    if ((pctx.draw_flags & PRT_DRAW_BOLD))
+       INSERT_PANGO_ATTR(pango_attr_weight_new(PANGO_WEIGHT_BOLD),
+               attr_list, 0, len);
+    if (pctx.draw_flags & PRT_DRAW_ITALIC)
+       INSERT_PANGO_ATTR(pango_attr_style_new(PANGO_STYLE_ITALIC),
+               attr_list, 0, len);
+
+#undef INSERT_PANGO_ATTR
+
+    item_list = pango_itemize(pctx.text_context,
+           (const char *)str, 0, len, attr_list, NULL);
+    pango_attr_list_unref(attr_list);
+
+    glyphs = pango_glyph_string_new();
+
+    // Loop is probably unecessary, because "str" represents a single character.
+    // Do it anyways to be sure.
+    while (item_list != NULL)
+    {
+       PangoItem   *item;
+       double      o;
+
+       item = (PangoItem *)item_list->data;
+       item_list = g_list_delete_link(item_list, item_list);
+
+       pango_shape_full((const char *)str + item->offset, item->length,
+               (const char *)str, len, &item->analysis, glyphs);
+
+       // Benefit of directly getting the width instead of deriving it from the
+       // char width and number of cells, is that non-monospace fonts look
+       // better.
+       o = (double)pango_glyph_string_get_width(glyphs) / PANGO_SCALE;
+
+       // Draw background color (only if different). Cannot batch bg color,
+       // because then we would overwrite glyphs.
+       if (pctx.cur_bg != PRCOLOR_WHITE)
+       {
+           long_to_rgb(pctx.cur_bg, &r, &g, &b);
+           cairo_set_source_rgb(pctx.cr, r, g, b);
+           cairo_rectangle(pctx.cr, pctx.cur_x + width,
+                   GET_Y(pctx.cur_line) - pctx.line_spacing, o, pctx.char_height);
+           cairo_fill(pctx.cr);
+       }
+
+       // Draw actual text
+       long_to_rgb(pctx.cur_fg, &r, &g, &b);
+       cairo_set_source_rgb(pctx.cr, r, g, b);
+       cairo_move_to(pctx.cr, pctx.cur_x + width, GET_TEXT_Y(pctx.cur_line));
+       pango_cairo_show_glyph_string(pctx.cr, item->analysis.font, glyphs);
+
+       pango_item_free(item);
+       width += o;
+    }
+
+    // Draw underline if needed
+    if (pctx.draw_flags & PRT_DRAW_UNDERLINE)
+    {
+       double y = GET_TEXT_Y(pctx.cur_line) - pctx.ul_pos;
+
+       long_to_rgb(pctx.cur_fg, &r, &g, &b);
+       cairo_set_source_rgb(pctx.cr, r, g, b);
+       cairo_set_line_width(pctx.cr, pctx.ul_thickness);
+       cairo_move_to(pctx.cr, pctx.cur_x, y);
+       cairo_line_to(pctx.cr, pctx.cur_x + width, y);
+       cairo_stroke(pctx.cr);
+    }
+
+    pango_glyph_string_free(glyphs);
+
+    pctx.cur_x += width;
+
+    next_x = pctx.cur_x + pctx.char_width;
+
+    if (output_conv.vc_type != CONV_NONE)
+       vim_free(str);
+
+    // Use epsilon when comparing floating point
+    return (next_x > pctx.right_margin) &&
+       ((next_x - pctx.right_margin) > (pctx.right_margin * 1e-5));
+}
+
+    void
+mch_print_set_font(int bold, int italic, int underline)
+{
+    pctx.draw_flags = 0;
+
+    if (bold)
+       pctx.draw_flags |= PRT_DRAW_BOLD;
+    if (italic)
+       pctx.draw_flags |= PRT_DRAW_ITALIC;
+    if (underline)
+       pctx.draw_flags |= PRT_DRAW_UNDERLINE;
+}
+
+    void
+mch_print_set_bg(long_u bgcol)
+{
+    pctx.cur_bg = bgcol;
+}
+
+    void
+mch_print_set_fg(long_u fgcol)
+{
+    // Probably can just call cairo_set_source_rgba() here but defer it later to
+    // be consistent with bg color.
+    pctx.cur_fg = fgcol;
+}
+
+    void
+mch_print_cleanup(void)
+{
+    g_clear_pointer(&pctx.cr, cairo_destroy);
+    g_clear_pointer(&pctx.surface, cairo_surface_destroy);
+    g_clear_object(&pctx.text_context);
+    g_clear_pointer(&pctx.font, pango_font_description_free);
+
+#ifdef USE_GTK4_PRINT_DIALOG
+    // If using dialog, defer the temp file deletion later
+    if (pctx.dialog)
+       gui_gtk4_print_finish();
+#endif
+
+    g_clear_pointer(&pctx.filename, vim_free);
+}
+
+#endif // FEAT_PRINT_PANGO
diff --git a/src/hardcopy_pango.h b/src/hardcopy_pango.h
new file mode 100644 (file)
index 0000000..26a4c22
--- /dev/null
@@ -0,0 +1,32 @@
+/* vi:set ts=8 sts=4 sw=4 noet:
+ *
+ * VIM - Vi IMproved   by Bram Moolenaar
+ *
+ * Do ":help uganda"  in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ * See README.txt for an overview of the Vim source code.
+ */
+
+#ifndef HARDCOPY_PANGO_H
+#define HARDCOPY_PANGO_H
+
+#include "vim.h"
+
+#if defined(FEAT_PRINT_PANGO)
+
+int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit);
+int mch_print_begin(prt_settings_T *psettings);
+void mch_print_end(prt_settings_T *psettings);
+int mch_print_end_page(void);
+int mch_print_begin_page(char_u *str);
+int mch_print_blank_page(void);
+void mch_print_start_line(int margin, int page_line);
+int mch_print_text_out(char_u *textp, int len);
+void mch_print_set_font(int bold, int italic, int underline);
+void mch_print_set_bg(long_u bgcol);
+void mch_print_set_fg(long_u fgcol);
+void mch_print_cleanup(void);
+
+#endif
+
+#endif
diff --git a/src/hardcopy_postscript.c b/src/hardcopy_postscript.c
new file mode 100644 (file)
index 0000000..123b9f3
--- /dev/null
@@ -0,0 +1,2512 @@
+/* vi:set ts=8 sts=4 sw=4 noet:
+ *
+ * VIM - Vi IMproved   by Bram Moolenaar
+ *
+ * Do ":help uganda"  in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ * See README.txt for an overview of the Vim source code.
+ */
+
+/*
+ * PS printer stuff.
+ *
+ * Sources of information to help maintain the PS printing code:
+ *
+ * 1. PostScript Language Reference, 3rd Edition,
+ *      Addison-Wesley, 1999, ISBN 0-201-37922-8
+ * 2. PostScript Language Program Design,
+ *      Addison-Wesley, 1988, ISBN 0-201-14396-8
+ * 3. PostScript Tutorial and Cookbook,
+ *      Addison Wesley, 1985, ISBN 0-201-10179-3
+ * 4. PostScript Language Document Structuring Conventions Specification,
+ *    version 3.0,
+ *      Adobe Technote 5001, 25th September 1992
+ * 5. PostScript Printer Description File Format Specification, Version 4.3,
+ *      Adobe technote 5003, 9th February 1996
+ * 6. Adobe Font Metrics File Format Specification, Version 4.1,
+ *      Adobe Technote 5007, 7th October 1998
+ * 7. Adobe CMap and CIDFont Files Specification, Version 1.0,
+ *      Adobe Technote 5014, 8th October 1996
+ * 8. Adobe CJKV Character Collections and CMaps for CID-Keyed Fonts,
+ *      Adoboe Technote 5094, 8th September, 2001
+ * 9. CJKV Information Processing, 2nd Edition,
+ *      O'Reilly, 2002, ISBN 1-56592-224-7
+ *
+ * Some of these documents can be found in PDF form on Adobe's web site -
+ * http://www.adobe.com
+ */
+
+#include "vim.h"
+#include "version.h"
+
+#ifdef FEAT_POSTSCRIPT
+
+# include "hardcopy.h"
+# include "hardcopy_postscript.h"
+
+option_table_T mbfont_opts[OPT_MBFONT_NUM_OPTIONS] =
+{
+    {"c",      FALSE, 0, NULL, 0, FALSE},
+    {"a",      FALSE, 0, NULL, 0, FALSE},
+    {"r",      FALSE, 0, NULL, 0, FALSE},
+    {"b",      FALSE, 0, NULL, 0, FALSE},
+    {"i",      FALSE, 0, NULL, 0, FALSE},
+    {"o",      FALSE, 0, NULL, 0, FALSE},
+};
+
+# define PRT_PS_DEFAULT_DPI        (72)    // Default user space resolution
+# define PRT_PS_DEFAULT_FONTSIZE     (10)
+# define PRT_PS_DEFAULT_BUFFER_SIZE  (80)
+
+// PS font names, must be in Roman, Bold, Italic, Bold-Italic order
+struct prt_ps_font_S
+{
+    int                wx;
+    int                uline_offset;
+    int                uline_width;
+    int                bbox_min_y;
+    int                bbox_max_y;
+    char       *(ps_fontname[4]);
+};
+
+# define PRT_PS_FONT_ROMAN     (0)
+# define PRT_PS_FONT_BOLD      (1)
+# define PRT_PS_FONT_OBLIQUE   (2)
+# define PRT_PS_FONT_BOLDOBLIQUE (3)
+
+// Standard font metrics for Courier family
+static struct prt_ps_font_S prt_ps_courier_font =
+{
+    600,
+    -100, 50,
+    -250, 805,
+    {"Courier", "Courier-Bold", "Courier-Oblique", "Courier-BoldOblique"}
+};
+
+// Generic font metrics for multi-byte fonts
+static struct prt_ps_font_S prt_ps_mb_font =
+{
+    1000,
+    -100, 50,
+    -250, 805,
+    {NULL, NULL, NULL, NULL}
+};
+
+// Pointer to current font set being used
+static struct prt_ps_font_S* prt_ps_font;
+
+// Structures to map user named encoding and mapping to PS equivalents for
+// building CID font name
+struct prt_ps_encoding_S
+{
+    char       *encoding;
+    char       *cmap_encoding;
+    int                needs_charset;
+};
+
+struct prt_ps_charset_S
+{
+    char       *charset;
+    char       *cmap_charset;
+    int                has_charset;
+};
+
+# define CS_JIS_C_1978   (0x01)
+# define CS_JIS_X_1983   (0x02)
+# define CS_JIS_X_1990   (0x04)
+# define CS_NEC                (0x08)
+# define CS_MSWINDOWS  (0x10)
+# define CS_CP932      (0x20)
+# define CS_KANJITALK6 (0x40)
+# define CS_KANJITALK7   (0x80)
+
+// Japanese encodings and charsets
+static struct prt_ps_encoding_S j_encodings[] =
+{
+    {"iso-2022-jp", NULL,       (CS_JIS_C_1978|CS_JIS_X_1983|CS_JIS_X_1990|
+                                                                   CS_NEC)},
+    {"euc-jp",     "EUC",      (CS_JIS_C_1978|CS_JIS_X_1983|CS_JIS_X_1990)},
+    {"sjis",       "RKSJ",     (CS_JIS_C_1978|CS_JIS_X_1983|CS_MSWINDOWS|
+                                               CS_KANJITALK6|CS_KANJITALK7)},
+    {"cp932",       "RKSJ",     CS_JIS_X_1983},
+    {"ucs-2",       "UCS2",     CS_JIS_X_1990},
+    {"utf-8",       "UTF8" ,    CS_JIS_X_1990}
+};
+static struct prt_ps_charset_S j_charsets[] =
+{
+    {"JIS_C_1978",  "78",       CS_JIS_C_1978},
+    {"JIS_X_1983",  NULL,       CS_JIS_X_1983},
+    {"JIS_X_1990",  "Hojo",     CS_JIS_X_1990},
+    {"NEC",        "Ext",      CS_NEC},
+    {"MSWINDOWS",   "90ms",     CS_MSWINDOWS},
+    {"CP932",       "90ms",     CS_JIS_X_1983},
+    {"KANJITALK6",  "83pv",     CS_KANJITALK6},
+    {"KANJITALK7",  "90pv",     CS_KANJITALK7}
+};
+
+# define CS_GB_2312_80       (0x01)
+# define CS_GBT_12345_90     (0x02)
+# define CS_GBK2K          (0x04)
+# define CS_SC_MAC         (0x08)
+# define CS_GBT_90_MAC     (0x10)
+# define CS_GBK                    (0x20)
+# define CS_SC_ISO10646      (0x40)
+
+// Simplified Chinese encodings and charsets
+static struct prt_ps_encoding_S sc_encodings[] =
+{
+    {"iso-2022",    NULL,       (CS_GB_2312_80|CS_GBT_12345_90)},
+    {"gb18030",     NULL,       CS_GBK2K},
+    {"euc-cn",      "EUC",      (CS_GB_2312_80|CS_GBT_12345_90|CS_SC_MAC|
+                                                               CS_GBT_90_MAC)},
+    {"gbk",        "EUC",      CS_GBK},
+    {"ucs-2",       "UCS2",     CS_SC_ISO10646},
+    {"utf-8",       "UTF8",     CS_SC_ISO10646}
+};
+static struct prt_ps_charset_S sc_charsets[] =
+{
+    {"GB_2312-80",  "GB",       CS_GB_2312_80},
+    {"GBT_12345-90","GBT",      CS_GBT_12345_90},
+    {"MAC",        "GBpc",     CS_SC_MAC},
+    {"GBT-90_MAC",  "GBTpc",   CS_GBT_90_MAC},
+    {"GBK",        "GBK",      CS_GBK},
+    {"GB18030",     "GBK2K",    CS_GBK2K},
+    {"ISO10646",    "UniGB",    CS_SC_ISO10646}
+};
+
+# define CS_CNS_PLANE_1      (0x01)
+# define CS_CNS_PLANE_2      (0x02)
+# define CS_CNS_PLANE_1_2    (0x04)
+# define CS_B5             (0x08)
+# define CS_ETEN                   (0x10)
+# define CS_HK_GCCS        (0x20)
+# define CS_HK_SCS         (0x40)
+# define CS_HK_SCS_ETEN            (0x80)
+# define CS_MTHKL          (0x100)
+# define CS_MTHKS          (0x200)
+# define CS_DLHKL          (0x400)
+# define CS_DLHKS          (0x800)
+# define CS_TC_ISO10646            (0x1000)
+
+// Traditional Chinese encodings and charsets
+static struct prt_ps_encoding_S tc_encodings[] =
+{
+    {"iso-2022",    NULL,       (CS_CNS_PLANE_1|CS_CNS_PLANE_2)},
+    {"euc-tw",      "EUC",      CS_CNS_PLANE_1_2},
+    {"big5",       "B5",       (CS_B5|CS_ETEN|CS_HK_GCCS|CS_HK_SCS|
+                                   CS_HK_SCS_ETEN|CS_MTHKL|CS_MTHKS|CS_DLHKL|
+                                                                   CS_DLHKS)},
+    {"cp950",       "B5",       CS_B5},
+    {"ucs-2",       "UCS2",     CS_TC_ISO10646},
+    {"utf-8",       "UTF8",     CS_TC_ISO10646},
+    {"utf-16",      "UTF16",    CS_TC_ISO10646},
+    {"utf-32",      "UTF32",    CS_TC_ISO10646}
+};
+static struct prt_ps_charset_S tc_charsets[] =
+{
+    {"CNS_1992_1",  "CNS1",     CS_CNS_PLANE_1},
+    {"CNS_1992_2",  "CNS2",     CS_CNS_PLANE_2},
+    {"CNS_1993",    "CNS",      CS_CNS_PLANE_1_2},
+    {"BIG5",       NULL,       CS_B5},
+    {"CP950",      NULL,       CS_B5},
+    {"ETEN",       "ETen",     CS_ETEN},
+    {"HK_GCCS",     "HKgccs",  CS_HK_GCCS},
+    {"SCS",        "HKscs",    CS_HK_SCS},
+    {"SCS_ETEN",    "ETHK",     CS_HK_SCS_ETEN},
+    {"MTHKL",       "HKm471",   CS_MTHKL},
+    {"MTHKS",       "HKm314",   CS_MTHKS},
+    {"DLHKL",       "HKdla",    CS_DLHKL},
+    {"DLHKS",       "HKdlb",    CS_DLHKS},
+    {"ISO10646",    "UniCNS",   CS_TC_ISO10646}
+};
+
+# define CS_KR_X_1992      (0x01)
+# define CS_KR_MAC         (0x02)
+# define CS_KR_X_1992_MS     (0x04)
+# define CS_KR_ISO10646      (0x08)
+
+// Korean encodings and charsets
+static struct prt_ps_encoding_S k_encodings[] =
+{
+    {"iso-2022-kr", NULL,       CS_KR_X_1992},
+    {"euc-kr",      "EUC",      (CS_KR_X_1992|CS_KR_MAC)},
+    {"johab",       "Johab",    CS_KR_X_1992},
+    {"cp1361",      "Johab",    CS_KR_X_1992},
+    {"uhc",        "UHC",      CS_KR_X_1992_MS},
+    {"cp949",       "UHC",      CS_KR_X_1992_MS},
+    {"ucs-2",       "UCS2",     CS_KR_ISO10646},
+    {"utf-8",       "UTF8",     CS_KR_ISO10646}
+};
+static struct prt_ps_charset_S k_charsets[] =
+{
+    {"KS_X_1992",   "KSC",      CS_KR_X_1992},
+    {"CP1361",      "KSC",      CS_KR_X_1992},
+    {"MAC",        "KSCpc",    CS_KR_MAC},
+    {"MSWINDOWS",   "KSCms",    CS_KR_X_1992_MS},
+    {"CP949",       "KSCms",    CS_KR_X_1992_MS},
+    {"WANSUNG",     "KSCms",    CS_KR_X_1992_MS},
+    {"ISO10646",    "UniKS",    CS_KR_ISO10646}
+};
+
+// Collections of encodings and charsets for multi-byte printing
+struct prt_ps_mbfont_S
+{
+    int                                num_encodings;
+    struct prt_ps_encoding_S   *encodings;
+    int                                num_charsets;
+    struct prt_ps_charset_S    *charsets;
+    char                       *ascii_enc;
+    char                       *defcs;
+};
+
+static struct prt_ps_mbfont_S prt_ps_mbfonts[] =
+{
+    {
+       ARRAY_LENGTH(j_encodings),
+       j_encodings,
+       ARRAY_LENGTH(j_charsets),
+       j_charsets,
+       "jis_roman",
+       "JIS_X_1983"
+    },
+    {
+       ARRAY_LENGTH(sc_encodings),
+       sc_encodings,
+       ARRAY_LENGTH(sc_charsets),
+       sc_charsets,
+       "gb_roman",
+       "GB_2312-80"
+    },
+    {
+       ARRAY_LENGTH(tc_encodings),
+       tc_encodings,
+       ARRAY_LENGTH(tc_charsets),
+       tc_charsets,
+       "cns_roman",
+       "BIG5"
+    },
+    {
+       ARRAY_LENGTH(k_encodings),
+       k_encodings,
+       ARRAY_LENGTH(k_charsets),
+       k_charsets,
+       "ks_roman",
+       "KS_X_1992"
+    }
+};
+
+struct prt_ps_resource_S
+{
+    char_u  name[64];
+    char_u  filename[MAXPATHL + 1];
+    int     type;
+    char_u  title[256];
+    char_u  version[256];
+};
+
+// Types of PS resource file currently used
+# define PRT_RESOURCE_TYPE_PROCSET   (0)
+# define PRT_RESOURCE_TYPE_ENCODING  (1)
+# define PRT_RESOURCE_TYPE_CMAP      (2)
+
+// The PS prolog file version number has to match - if the prolog file is
+// updated, increment the number in the file and here.  Version checking was
+// added as of VIM 6.2.
+// The CID prolog file version number behaves as per PS prolog.
+// Table of VIM and prolog versions:
+//
+// VIM      Prolog  CIDProlog
+// 6.2      1.3
+// 7.0      1.4            1.0
+# define PRT_PROLOG_VERSION  ((char_u *)"1.4")
+# define PRT_CID_PROLOG_VERSION  ((char_u *)"1.0")
+
+// String versions of PS resource types - indexed by constants above so don't
+// re-order!
+static char *prt_resource_types[] =
+{
+    "procset",
+    "encoding",
+    "cmap"
+};
+
+// Strings to look for in a PS resource file
+# define PRT_RESOURCE_HEADER       "%!PS-Adobe-"
+# define PRT_RESOURCE_RESOURCE     "Resource-"
+# define PRT_RESOURCE_PROCSET      "ProcSet"
+# define PRT_RESOURCE_ENCODING     "Encoding"
+# define PRT_RESOURCE_CMAP         "CMap"
+
+
+// Data for table based DSC comment recognition, easy to extend if VIM needs to
+// read more comments.
+# define PRT_DSC_MISC_TYPE         (-1)
+# define PRT_DSC_TITLE_TYPE        (1)
+# define PRT_DSC_VERSION_TYPE      (2)
+# define PRT_DSC_ENDCOMMENTS_TYPE    (3)
+
+# define PRT_DSC_TITLE             "%%Title:"
+# define PRT_DSC_VERSION                   "%%Version:"
+# define PRT_DSC_ENDCOMMENTS       "%%EndComments:"
+
+struct prt_dsc_comment_S
+{
+    char       *string;
+    int                len;
+    int                type;
+};
+
+struct prt_dsc_line_S
+{
+    int                type;
+    char_u     *string;
+    int                len;
+};
+
+
+# define SIZEOF_CSTR(s)      (sizeof(s) - 1)
+static struct prt_dsc_comment_S prt_dsc_table[] =
+{
+    {PRT_DSC_TITLE,       SIZEOF_CSTR(PRT_DSC_TITLE),     PRT_DSC_TITLE_TYPE},
+    {PRT_DSC_VERSION,     SIZEOF_CSTR(PRT_DSC_VERSION),
+                                                       PRT_DSC_VERSION_TYPE},
+    {PRT_DSC_ENDCOMMENTS, SIZEOF_CSTR(PRT_DSC_ENDCOMMENTS),
+                                                    PRT_DSC_ENDCOMMENTS_TYPE}
+};
+
+static void prt_write_file_len(char_u *buffer, int bytes);
+static int prt_next_dsc(struct prt_dsc_line_S *p_dsc_line);
+
+/*
+ * Variables for the output PostScript file.
+ */
+static FILE *prt_ps_fd;
+static int prt_file_error;
+static char_u *prt_ps_file_name = NULL;
+
+/*
+ * Various offsets and dimensions in default PostScript user space (points).
+ * Used for text positioning calculations
+ */
+static float prt_page_width;
+static float prt_page_height;
+static float prt_left_margin;
+static float prt_right_margin;
+static float prt_top_margin;
+static float prt_bottom_margin;
+static float prt_line_height;
+static float prt_first_line_height;
+static float prt_char_width;
+static float prt_number_width;
+static float prt_bgcol_offset;
+static float prt_pos_x_moveto = 0.0;
+static float prt_pos_y_moveto = 0.0;
+
+/*
+ * Various control variables used to decide when and how to change the
+ * PostScript graphics state.
+ */
+static int prt_need_moveto;
+static int prt_do_moveto;
+static int prt_need_font;
+static int prt_font;
+static int prt_need_underline;
+static int prt_underline;
+static int prt_do_underline;
+static int prt_need_fgcol;
+static int prt_fgcol;
+static int prt_need_bgcol;
+static int prt_do_bgcol;
+static int prt_bgcol;
+static int prt_new_bgcol;
+static int prt_attribute_change;
+static float prt_text_run;
+static int prt_page_num;
+static int prt_bufsiz;
+
+/*
+ * Variables controlling physical printing.
+ */
+static int prt_media;
+static int prt_portrait;
+static int prt_num_copies;
+static int prt_duplex;
+static int prt_tumble;
+static int prt_collate;
+
+/*
+ * Buffers used when generating PostScript output
+ */
+static char_u prt_line_buffer[257];
+static garray_T prt_ps_buffer;
+
+static int prt_do_conv;
+static vimconv_T prt_conv;
+
+static int prt_out_mbyte;
+static int prt_custom_cmap;
+static char prt_cmap[80];
+static int prt_use_courier;
+static int prt_in_ascii;
+static int prt_half_width;
+static char *prt_ascii_encoding;
+static const char_u prt_hexchar[] = "0123456789abcdef";
+
+    static void
+prt_write_file_raw_len(char_u *buffer, int bytes)
+{
+    if (!prt_file_error
+           && fwrite(buffer, sizeof(char_u), bytes, prt_ps_fd)
+                                                            != (size_t)bytes)
+    {
+       emsg(_(e_error_writing_to_postscript_output_file));
+       prt_file_error = TRUE;
+    }
+}
+
+    static void
+prt_write_file(char_u *buffer)
+{
+    prt_write_file_len(buffer, (int)STRLEN(buffer));
+}
+
+    static void
+prt_write_file_len(char_u *buffer, int bytes)
+{
+    prt_write_file_raw_len(buffer, bytes);
+}
+
+/*
+ * Write a string.
+ */
+    static void
+prt_write_string(char *s)
+{
+    vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer), "%s", s);
+    prt_write_file(prt_line_buffer);
+}
+
+/*
+ * Write an int and a space.
+ */
+    static void
+prt_write_int(int i)
+{
+    sprintf((char *)prt_line_buffer, "%d ", i);
+    prt_write_file(prt_line_buffer);
+}
+
+/*
+ * Write a boolean and a space.
+ */
+    static void
+prt_write_boolean(int b)
+{
+    sprintf((char *)prt_line_buffer, "%s ", (b ? "T" : "F"));
+    prt_write_file(prt_line_buffer);
+}
+
+/*
+ * Write PostScript to re-encode and define the font.
+ */
+    static void
+prt_def_font(
+    char       *new_name,
+    char       *encoding,
+    int                height,
+    char       *font)
+{
+    vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
+                         "/_%s /VIM-%s /%s ref\n", new_name, encoding, font);
+    prt_write_file(prt_line_buffer);
+    if (prt_out_mbyte)
+       sprintf((char *)prt_line_buffer, "/%s %d %f /_%s sffs\n",
+                      new_name, height, 500./prt_ps_courier_font.wx, new_name);
+    else
+       vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
+                            "/%s %d /_%s ffs\n", new_name, height, new_name);
+    prt_write_file(prt_line_buffer);
+}
+
+/*
+ * Write a line to define the CID font.
+ */
+    static void
+prt_def_cidfont(char *new_name, int height, char *cidfont)
+{
+    vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
+             "/_%s /%s[/%s] vim_composefont\n", new_name, prt_cmap, cidfont);
+    prt_write_file(prt_line_buffer);
+    vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
+                            "/%s %d /_%s ffs\n", new_name, height, new_name);
+    prt_write_file(prt_line_buffer);
+}
+
+/*
+ * Write a line to define a duplicate of a CID font
+ */
+    static void
+prt_dup_cidfont(char *original_name, char *new_name)
+{
+    vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
+                                      "/%s %s d\n", new_name, original_name);
+    prt_write_file(prt_line_buffer);
+}
+
+/*
+ * Convert a real value into an integer and fractional part as integers, with
+ * the fractional part being in the range [0,10^precision).  The fractional part
+ * is also rounded based on the precision + 1'th fractional digit.
+ */
+    static void
+prt_real_bits(
+    double      real,
+    int                precision,
+    int                *pinteger,
+    int                *pfraction)
+{
+    int     i;
+    int     integer;
+    float   fraction;
+
+    integer = (int)real;
+    fraction = (float)(real - integer);
+    if (real < (double)integer)
+       fraction = -fraction;
+    for (i = 0; i < precision; i++)
+       fraction *= 10.0;
+
+    *pinteger = integer;
+    *pfraction = (int)(fraction + 0.5);
+}
+
+/*
+ * Write a real and a space.  Save bytes if real value has no fractional part!
+ * We use prt_real_bits() as %f in sprintf uses the locale setting to decide
+ * what decimal point character to use, but PS always requires a '.'.
+ */
+    static void
+prt_write_real(double val, int prec)
+{
+    int     integer;
+    int     fraction;
+
+    prt_real_bits(val, prec, &integer, &fraction);
+    // Emit integer part
+    sprintf((char *)prt_line_buffer, "%d", integer);
+    prt_write_file(prt_line_buffer);
+    // Only emit fraction if necessary
+    if (fraction != 0)
+    {
+       // Remove any trailing zeros
+       while ((fraction % 10) == 0)
+       {
+           prec--;
+           fraction /= 10;
+       }
+       // Emit fraction left padded with zeros
+       sprintf((char *)prt_line_buffer, ".%0*d", prec, fraction);
+       prt_write_file(prt_line_buffer);
+    }
+    sprintf((char *)prt_line_buffer, " ");
+    prt_write_file(prt_line_buffer);
+}
+
+/*
+ * Write a line to define a numeric variable.
+ */
+    static void
+prt_def_var(char *name, double value, int prec)
+{
+    vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
+                                                               "/%s ", name);
+    prt_write_file(prt_line_buffer);
+    prt_write_real(value, prec);
+    sprintf((char *)prt_line_buffer, "d\n");
+    prt_write_file(prt_line_buffer);
+}
+
+// Convert size from font space to user space at current font scale
+# define PRT_PS_FONT_TO_USER(scale, size)    ((size) * ((scale)/1000.0))
+
+    static void
+prt_flush_buffer(void)
+{
+    if (prt_ps_buffer.ga_len <= 0)
+       return;
+
+    // Any background color must be drawn first
+    if (prt_do_bgcol && (prt_new_bgcol != PRCOLOR_WHITE))
+    {
+       int     r, g, b;
+
+       if (prt_do_moveto)
+       {
+           prt_write_real(prt_pos_x_moveto, 2);
+           prt_write_real(prt_pos_y_moveto, 2);
+           prt_write_string("m\n");
+           prt_do_moveto = FALSE;
+       }
+
+       // Size of rect of background color on which text is printed
+       prt_write_real(prt_text_run, 2);
+       prt_write_real(prt_line_height, 2);
+
+       // Lastly add the color of the background
+       r = ((unsigned)prt_new_bgcol & 0xff0000) >> 16;
+       g = ((unsigned)prt_new_bgcol & 0xff00) >> 8;
+       b = prt_new_bgcol & 0xff;
+       prt_write_real(r / 255.0, 3);
+       prt_write_real(g / 255.0, 3);
+       prt_write_real(b / 255.0, 3);
+       prt_write_string("bg\n");
+    }
+    // Draw underlines before the text as it makes it slightly easier to
+    // find the starting point.
+    if (prt_do_underline)
+    {
+       if (prt_do_moveto)
+       {
+           prt_write_real(prt_pos_x_moveto, 2);
+           prt_write_real(prt_pos_y_moveto, 2);
+           prt_write_string("m\n");
+           prt_do_moveto = FALSE;
+       }
+
+       // Underline length of text run
+       prt_write_real(prt_text_run, 2);
+       prt_write_string("ul\n");
+    }
+    // Draw the text
+    if (prt_out_mbyte)
+       prt_write_string("<");
+    else
+       prt_write_string("(");
+    prt_write_file_raw_len(prt_ps_buffer.ga_data, prt_ps_buffer.ga_len);
+    if (prt_out_mbyte)
+       prt_write_string(">");
+    else
+       prt_write_string(")");
+    // Add a moveto if need be and use the appropriate show procedure
+    if (prt_do_moveto)
+    {
+       prt_write_real(prt_pos_x_moveto, 2);
+       prt_write_real(prt_pos_y_moveto, 2);
+       // moveto and a show
+       prt_write_string("ms\n");
+       prt_do_moveto = FALSE;
+    }
+    else // Simple show
+       prt_write_string("s\n");
+
+    ga_clear(&prt_ps_buffer);
+    ga_init2(&prt_ps_buffer, sizeof(char), prt_bufsiz);
+}
+
+
+    static void
+prt_resource_name(char_u *filename, void *cookie)
+{
+    char_u *resource_filename = cookie;
+
+    if (STRLEN(filename) >= MAXPATHL)
+       *resource_filename = NUL;
+    else
+       STRCPY(resource_filename, filename);
+}
+
+    static int
+prt_find_resource(char *name, struct prt_ps_resource_S *resource)
+{
+    char_u     *buffer;
+    int                retval;
+
+    buffer = alloc(MAXPATHL + 1);
+    if (buffer == NULL)
+       return FALSE;
+
+    vim_strncpy(resource->name, (char_u *)name, 63);
+    // Look for named resource file in runtimepath
+    STRCPY(buffer, "print");
+    add_pathsep(buffer);
+    vim_strcat(buffer, (char_u *)name, MAXPATHL);
+    vim_strcat(buffer, (char_u *)".ps", MAXPATHL);
+    resource->filename[0] = NUL;
+    retval = (do_in_runtimepath(buffer, 0, prt_resource_name,
+                                                          resource->filename)
+           && resource->filename[0] != NUL);
+    vim_free(buffer);
+    return retval;
+}
+
+// PS CR and LF characters have platform independent values
+# define PSLF  (0x0a)
+# define PSCR  (0x0d)
+
+// Static buffer to read initial comments in a resource file, some can have a
+// couple of KB of comments!
+# define PRT_FILE_BUFFER_LEN (2048)
+struct prt_resfile_buffer_S
+{
+    char_u  buffer[PRT_FILE_BUFFER_LEN];
+    int     len;
+    int     line_start;
+    int     line_end;
+};
+
+static struct prt_resfile_buffer_S prt_resfile;
+
+    static int
+prt_resfile_next_line(void)
+{
+    int     idx;
+
+    // Move to start of next line and then find end of line
+    idx = prt_resfile.line_end + 1;
+    while (idx < prt_resfile.len)
+    {
+       if (prt_resfile.buffer[idx] != PSLF && prt_resfile.buffer[idx] != PSCR)
+           break;
+       idx++;
+    }
+    prt_resfile.line_start = idx;
+
+    while (idx < prt_resfile.len)
+    {
+       if (prt_resfile.buffer[idx] == PSLF || prt_resfile.buffer[idx] == PSCR)
+           break;
+       idx++;
+    }
+    prt_resfile.line_end = idx;
+
+    return (idx < prt_resfile.len);
+}
+
+    static int
+prt_resfile_strncmp(int offset, char *string, int len)
+{
+    // Force not equal if string is longer than remainder of line
+    if (len > (prt_resfile.line_end - (prt_resfile.line_start + offset)))
+       return 1;
+
+    return STRNCMP(&prt_resfile.buffer[prt_resfile.line_start + offset],
+                                                               string, len);
+}
+
+    static int
+prt_resfile_skip_nonws(int offset)
+{
+    int     idx;
+
+    idx = prt_resfile.line_start + offset;
+    while (idx < prt_resfile.line_end)
+    {
+       if (SAFE_isspace(prt_resfile.buffer[idx]))
+           return idx - prt_resfile.line_start;
+       idx++;
+    }
+    return -1;
+}
+
+    static int
+prt_resfile_skip_ws(int offset)
+{
+    int     idx;
+
+    idx = prt_resfile.line_start + offset;
+    while (idx < prt_resfile.line_end)
+    {
+       if (!SAFE_isspace(prt_resfile.buffer[idx]))
+           return idx - prt_resfile.line_start;
+       idx++;
+    }
+    return -1;
+}
+
+// prt_next_dsc() - returns detail on next DSC comment line found.  Returns true
+// if a DSC comment is found, else false
+    static int
+prt_next_dsc(struct prt_dsc_line_S *p_dsc_line)
+{
+    int     comment;
+    int     offset;
+
+    // Move to start of next line
+    if (!prt_resfile_next_line())
+       return FALSE;
+
+    // DSC comments always start %%
+    if (prt_resfile_strncmp(0, "%%", 2) != 0)
+       return FALSE;
+
+    // Find type of DSC comment
+    for (comment = 0; comment < (int)ARRAY_LENGTH(prt_dsc_table); comment++)
+       if (prt_resfile_strncmp(0, prt_dsc_table[comment].string,
+                                           prt_dsc_table[comment].len) == 0)
+           break;
+
+    if (comment != ARRAY_LENGTH(prt_dsc_table))
+    {
+       // Return type of comment
+       p_dsc_line->type = prt_dsc_table[comment].type;
+       offset = prt_dsc_table[comment].len;
+    }
+    else
+    {
+       // Unrecognised DSC comment, skip to ws after comment leader
+       p_dsc_line->type = PRT_DSC_MISC_TYPE;
+       offset = prt_resfile_skip_nonws(0);
+       if (offset == -1)
+           return FALSE;
+    }
+
+    // Skip ws to comment value
+    offset = prt_resfile_skip_ws(offset);
+    if (offset == -1)
+       return FALSE;
+
+    p_dsc_line->string = &prt_resfile.buffer[prt_resfile.line_start + offset];
+    p_dsc_line->len = prt_resfile.line_end - (prt_resfile.line_start + offset);
+
+    return TRUE;
+}
+
+/*
+ * Improved hand crafted parser to get the type, title, and version number of a
+ * PS resource file so the file details can be added to the DSC header comments.
+ */
+    static int
+prt_open_resource(struct prt_ps_resource_S *resource)
+{
+    int                offset;
+    int                seen_all;
+    int                seen_title;
+    int                seen_version;
+    FILE       *fd_resource;
+    struct prt_dsc_line_S dsc_line;
+
+    fd_resource = mch_fopen((char *)resource->filename, READBIN);
+    if (fd_resource == NULL)
+    {
+       semsg(_(e_cant_open_file_str_3), resource->filename);
+       return FALSE;
+    }
+    CLEAR_FIELD(prt_resfile.buffer);
+
+    // Parse first line to ensure valid resource file
+    prt_resfile.len = (int)fread((char *)prt_resfile.buffer, sizeof(char_u),
+                                           PRT_FILE_BUFFER_LEN, fd_resource);
+    if (ferror(fd_resource))
+    {
+       semsg(_(e_cant_read_postscript_resource_file_str),
+               resource->filename);
+       fclose(fd_resource);
+       return FALSE;
+    }
+    fclose(fd_resource);
+
+    prt_resfile.line_end = -1;
+    prt_resfile.line_start = 0;
+    if (!prt_resfile_next_line())
+       return FALSE;
+
+    offset = 0;
+
+    if (prt_resfile_strncmp(offset, PRT_RESOURCE_HEADER,
+                              (int)STRLEN_LITERAL(PRT_RESOURCE_HEADER)) != 0)
+    {
+       semsg(_(e_file_str_is_not_postscript_resource_file),
+                                                          resource->filename);
+       return FALSE;
+    }
+
+    // Skip over any version numbers and following ws
+    offset += (int)STRLEN_LITERAL(PRT_RESOURCE_HEADER);
+    offset = prt_resfile_skip_nonws(offset);
+    if (offset == -1)
+       return FALSE;
+    offset = prt_resfile_skip_ws(offset);
+    if (offset == -1)
+       return FALSE;
+
+    if (prt_resfile_strncmp(offset, PRT_RESOURCE_RESOURCE,
+                            (int)STRLEN_LITERAL(PRT_RESOURCE_RESOURCE)) != 0)
+    {
+       semsg(_(e_file_str_is_not_supported_postscript_resource_file),
+                                                          resource->filename);
+       return FALSE;
+    }
+    offset += (int)STRLEN_LITERAL(PRT_RESOURCE_RESOURCE);
+
+    // Decide type of resource in the file
+    if (prt_resfile_strncmp(offset, PRT_RESOURCE_PROCSET,
+                             (int)STRLEN_LITERAL(PRT_RESOURCE_PROCSET)) == 0)
+       resource->type = PRT_RESOURCE_TYPE_PROCSET;
+    else if (prt_resfile_strncmp(offset, PRT_RESOURCE_ENCODING,
+                            (int)STRLEN_LITERAL(PRT_RESOURCE_ENCODING)) == 0)
+       resource->type = PRT_RESOURCE_TYPE_ENCODING;
+    else if (prt_resfile_strncmp(offset, PRT_RESOURCE_CMAP,
+                                (int)STRLEN_LITERAL(PRT_RESOURCE_CMAP)) == 0)
+       resource->type = PRT_RESOURCE_TYPE_CMAP;
+    else
+    {
+       semsg(_(e_file_str_is_not_supported_postscript_resource_file),
+                                                          resource->filename);
+       return FALSE;
+    }
+
+    // Look for title and version of resource
+    resource->title[0] = '\0';
+    resource->version[0] = '\0';
+    seen_title = FALSE;
+    seen_version = FALSE;
+    seen_all = FALSE;
+    while (!seen_all && prt_next_dsc(&dsc_line))
+    {
+       switch (dsc_line.type)
+       {
+       case PRT_DSC_TITLE_TYPE:
+           vim_strncpy(resource->title, dsc_line.string, dsc_line.len);
+           seen_title = TRUE;
+           if (seen_version)
+               seen_all = TRUE;
+           break;
+
+       case PRT_DSC_VERSION_TYPE:
+           vim_strncpy(resource->version, dsc_line.string, dsc_line.len);
+           seen_version = TRUE;
+           if (seen_title)
+               seen_all = TRUE;
+           break;
+
+       case PRT_DSC_ENDCOMMENTS_TYPE:
+           // Won't find title or resource after this comment, stop searching
+           seen_all = TRUE;
+           break;
+
+       case PRT_DSC_MISC_TYPE:
+           // Not interested in whatever comment this line had
+           break;
+       }
+    }
+
+    if (!seen_title || !seen_version)
+    {
+       semsg(_(e_file_str_is_not_supported_postscript_resource_file),
+                                                          resource->filename);
+       return FALSE;
+    }
+
+    return TRUE;
+}
+
+    static int
+prt_check_resource(struct prt_ps_resource_S *resource, char_u *version)
+{
+    // Version number m.n should match, the revision number does not matter
+    if (STRNCMP(resource->version, version, STRLEN(version)) != 0)
+    {
+       semsg(_(e_str_resource_file_has_wrong_version),
+               resource->name);
+       return FALSE;
+    }
+
+    // Other checks to be added as needed
+    return TRUE;
+}
+
+    static void
+prt_dsc_start(void)
+{
+    prt_write_string("%!PS-Adobe-3.0\n");
+}
+
+    static void
+prt_dsc_noarg(char *comment)
+{
+    vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
+                                                        "%%%%%s\n", comment);
+    prt_write_file(prt_line_buffer);
+}
+
+    static void
+prt_dsc_textline(char *comment, char *text)
+{
+    vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
+                                              "%%%%%s: %s\n", comment, text);
+    prt_write_file(prt_line_buffer);
+}
+
+    static void
+prt_dsc_text(char *comment, char *text)
+{
+    // TODO - should scan 'text' for any chars needing escaping!
+    vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
+                                            "%%%%%s: (%s)\n", comment, text);
+    prt_write_file(prt_line_buffer);
+}
+
+# define prt_dsc_atend(c)      prt_dsc_text((c), "atend")
+
+    static void
+prt_dsc_ints(char *comment, int count, int *ints)
+{
+    int                i;
+
+    vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
+                                                         "%%%%%s:", comment);
+    prt_write_file(prt_line_buffer);
+
+    for (i = 0; i < count; i++)
+    {
+       sprintf((char *)prt_line_buffer, " %d", ints[i]);
+       prt_write_file(prt_line_buffer);
+    }
+
+    prt_write_string("\n");
+}
+
+    static void
+prt_dsc_resources(
+    char       *comment,       // if NULL add to previous
+    char       *type,
+    char       *string)
+{
+    if (comment != NULL)
+       vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
+                                                "%%%%%s: %s", comment, type);
+    else
+       vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
+                                                           "%%%%+ %s", type);
+    prt_write_file(prt_line_buffer);
+
+    vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
+                                                            " %s\n", string);
+    prt_write_file(prt_line_buffer);
+}
+
+    static void
+prt_dsc_font_resource(char *resource, struct prt_ps_font_S *ps_font)
+{
+    int     i;
+
+    prt_dsc_resources(resource, "font",
+                                   ps_font->ps_fontname[PRT_PS_FONT_ROMAN]);
+    for (i = PRT_PS_FONT_BOLD ; i <= PRT_PS_FONT_BOLDOBLIQUE ; i++)
+       if (ps_font->ps_fontname[i] != NULL)
+           prt_dsc_resources(NULL, "font", ps_font->ps_fontname[i]);
+}
+
+    static void
+prt_dsc_requirements(
+    int                duplex,
+    int                tumble,
+    int                collate,
+    int                color,
+    int                num_copies)
+{
+    // Only output the comment if we need to.
+    // Note: tumble is ignored if we are not duplexing
+    if (!(duplex || collate || color || (num_copies > 1)))
+       return;
+
+    sprintf((char *)prt_line_buffer, "%%%%Requirements:");
+    prt_write_file(prt_line_buffer);
+
+    if (duplex)
+    {
+       prt_write_string(" duplex");
+       if (tumble)
+           prt_write_string("(tumble)");
+    }
+    if (collate)
+       prt_write_string(" collate");
+    if (color)
+       prt_write_string(" color");
+    if (num_copies > 1)
+    {
+       prt_write_string(" numcopies(");
+       // Note: no space wanted so don't use prt_write_int()
+       sprintf((char *)prt_line_buffer, "%d", num_copies);
+       prt_write_file(prt_line_buffer);
+       prt_write_string(")");
+    }
+    prt_write_string("\n");
+}
+
+    static void
+prt_dsc_docmedia(
+    char       *paper_name,
+    double     width,
+    double     height,
+    double     weight,
+    char       *colour,
+    char       *type)
+{
+    vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
+                                       "%%%%DocumentMedia: %s ", paper_name);
+    prt_write_file(prt_line_buffer);
+    prt_write_real(width, 2);
+    prt_write_real(height, 2);
+    prt_write_real(weight, 2);
+    if (colour == NULL)
+       prt_write_string("()");
+    else
+       prt_write_string(colour);
+    prt_write_string(" ");
+    if (type == NULL)
+       prt_write_string("()");
+    else
+       prt_write_string(type);
+    prt_write_string("\n");
+}
+
+    void
+mch_print_cleanup(void)
+{
+    if (prt_out_mbyte)
+    {
+       int     i;
+
+       // Free off all CID font names created, but first clear duplicate
+       // pointers to the same string (when the same font is used for more than
+       // one style).
+       for (i = PRT_PS_FONT_ROMAN; i <= PRT_PS_FONT_BOLDOBLIQUE; i++)
+       {
+           if (prt_ps_mb_font.ps_fontname[i] != NULL)
+               VIM_CLEAR(prt_ps_mb_font.ps_fontname[i]);
+       }
+    }
+
+    if (prt_do_conv)
+    {
+       convert_setup(&prt_conv, NULL, NULL);
+       prt_do_conv = FALSE;
+    }
+    if (prt_ps_fd != NULL)
+    {
+       fclose(prt_ps_fd);
+       prt_ps_fd = NULL;
+       prt_file_error = FALSE;
+    }
+    if (prt_ps_file_name != NULL)
+       VIM_CLEAR(prt_ps_file_name);
+}
+
+    static float
+to_device_units(int idx, double physsize, int def_number)
+{
+    float      ret;
+    int                u;
+    int                nr;
+
+    u = prt_get_unit(idx);
+    if (u == PRT_UNIT_NONE)
+    {
+       u = PRT_UNIT_PERC;
+       nr = def_number;
+    }
+    else
+       nr = printer_opts[idx].number;
+
+    switch (u)
+    {
+       case PRT_UNIT_INCH:
+           ret = (float)(nr * PRT_PS_DEFAULT_DPI);
+           break;
+       case PRT_UNIT_MM:
+           ret = (float)(nr * PRT_PS_DEFAULT_DPI) / (float)25.4;
+           break;
+       case PRT_UNIT_POINT:
+           ret = (float)nr;
+           break;
+       case PRT_UNIT_PERC:
+       default:
+           ret = (float)(physsize * nr) / 100;
+           break;
+    }
+
+    return ret;
+}
+
+/*
+ * Calculate margins for given width and height from printoptions settings.
+ */
+    static void
+prt_page_margins(
+    double     width,
+    double     height,
+    double     *left,
+    double     *right,
+    double     *top,
+    double     *bottom)
+{
+    *left   = to_device_units(OPT_PRINT_LEFT, width, 10);
+    *right  = width - to_device_units(OPT_PRINT_RIGHT, width, 5);
+    *top    = height - to_device_units(OPT_PRINT_TOP, height, 5);
+    *bottom = to_device_units(OPT_PRINT_BOT, height, 5);
+}
+
+    static void
+prt_font_metrics(int font_scale)
+{
+    prt_line_height = (float)font_scale;
+    prt_char_width = (float)PRT_PS_FONT_TO_USER(font_scale, prt_ps_font->wx);
+}
+
+
+    static int
+prt_get_cpl(void)
+{
+    if (prt_use_number())
+    {
+       prt_number_width = PRINT_NUMBER_WIDTH * prt_char_width;
+       // If we are outputting multi-byte characters then line numbers will be
+       // printed with half width characters
+       if (prt_out_mbyte)
+           prt_number_width /= 2;
+       prt_left_margin += prt_number_width;
+    }
+    else
+       prt_number_width = 0.0;
+
+    return (int)((prt_right_margin - prt_left_margin) / prt_char_width);
+}
+
+    static int
+prt_build_cid_fontname(int font, char_u *name, int name_len)
+{
+    char    *fontname;
+
+    fontname = alloc(name_len + 1);
+    if (fontname == NULL)
+       return FALSE;
+    vim_strncpy((char_u *)fontname, name, name_len);
+    prt_ps_mb_font.ps_fontname[font] = fontname;
+
+    return TRUE;
+}
+
+/*
+ * Get number of lines of text that fit on a page (excluding the header).
+ */
+    static int
+prt_get_lpp(void)
+{
+    int lpp;
+
+    /*
+     * Calculate offset to lower left corner of background rect based on actual
+     * font height (based on its bounding box) and the line height, handling the
+     * case where the font height can exceed the line height.
+     */
+    prt_bgcol_offset = (float)PRT_PS_FONT_TO_USER(prt_line_height,
+                                          prt_ps_font->bbox_min_y);
+    if ((prt_ps_font->bbox_max_y - prt_ps_font->bbox_min_y) < 1000.0)
+    {
+       prt_bgcol_offset -= (float)PRT_PS_FONT_TO_USER(prt_line_height,
+                               (1000.0 - (prt_ps_font->bbox_max_y -
+                                           prt_ps_font->bbox_min_y)) / 2);
+    }
+
+    // Get height for topmost line based on background rect offset.
+    prt_first_line_height = prt_line_height + prt_bgcol_offset;
+
+    // Calculate lpp
+    lpp = (int)((prt_top_margin - prt_bottom_margin) / prt_line_height);
+
+    // Adjust top margin if there is a header
+    prt_top_margin -= prt_line_height * prt_header_height();
+
+    return lpp - prt_header_height();
+}
+
+    static int
+prt_match_encoding(
+    char                       *p_encoding,
+    struct prt_ps_mbfont_S     *p_cmap,
+    struct prt_ps_encoding_S   **pp_mbenc)
+{
+    int                                mbenc;
+    int                                enc_len;
+    struct prt_ps_encoding_S   *p_mbenc;
+
+    *pp_mbenc = NULL;
+    // Look for recognised encoding
+    enc_len = (int)STRLEN(p_encoding);
+    p_mbenc = p_cmap->encodings;
+    for (mbenc = 0; mbenc < p_cmap->num_encodings; mbenc++)
+    {
+       if (STRNICMP(p_mbenc->encoding, p_encoding, enc_len) == 0)
+       {
+           *pp_mbenc = p_mbenc;
+           return TRUE;
+       }
+       p_mbenc++;
+    }
+    return FALSE;
+}
+
+    static int
+prt_match_charset(
+    char                   *p_charset,
+    struct prt_ps_mbfont_S  *p_cmap,
+    struct prt_ps_charset_S **pp_mbchar)
+{
+    int                            mbchar;
+    int                            char_len;
+    struct prt_ps_charset_S *p_mbchar;
+
+    // Look for recognised character set, using default if one is not given
+    if (*p_charset == NUL)
+       p_charset = p_cmap->defcs;
+    char_len = (int)STRLEN(p_charset);
+    p_mbchar = p_cmap->charsets;
+    for (mbchar = 0; mbchar < p_cmap->num_charsets; mbchar++)
+    {
+       if (STRNICMP(p_mbchar->charset, p_charset, char_len) == 0)
+       {
+           *pp_mbchar = p_mbchar;
+           return TRUE;
+       }
+       p_mbchar++;
+    }
+    return FALSE;
+}
+
+    int
+mch_print_init(
+    prt_settings_T *psettings,
+    char_u     *jobname,
+    int                forceit UNUSED)
+{
+    int                i;
+    char       *paper_name;
+    int                paper_strlen;
+    int                fontsize;
+    char_u     *p;
+    double      left;
+    double      right;
+    double      top;
+    double      bottom;
+    int                props;
+    int                cmap = 0;
+    char_u     *p_encoding;
+    struct prt_ps_encoding_S *p_mbenc;
+    struct prt_ps_encoding_S *p_mbenc_first;
+    struct prt_ps_charset_S  *p_mbchar = NULL;
+
+# if 0
+    /*
+     * TODO:
+     * If "forceit" is false: pop up a dialog to select:
+     * - printer name
+     * - copies
+     * - collated/uncollated
+     * - duplex off/long side/short side
+     * - paper size
+     * - portrait/landscape
+     * - font size
+     *
+     * If "forceit" is true: use the default printer and settings
+     */
+    if (forceit)
+       s_pd.Flags |= PD_RETURNDEFAULT;
+# endif
+
+    /*
+     * Set up font and encoding.
+     */
+    p_encoding = enc_skip(p_penc);
+    if (*p_encoding == NUL)
+       p_encoding = enc_skip(p_enc);
+
+    // Look for a multi-byte font that matches the encoding and character set.
+    // Only look if multi-byte character set is defined, or using multi-byte
+    // encoding other than Unicode.  This is because a Unicode encoding does not
+    // uniquely identify a CJK character set to use.
+    p_mbenc = NULL;
+    props = enc_canon_props(p_encoding);
+    if (!(props & ENC_8BIT) && ((*p_pmcs != NUL) || !(props & ENC_UNICODE)))
+    {
+       int cmap_first = 0;
+
+       p_mbenc_first = NULL;
+       for (cmap = 0; cmap < (int)ARRAY_LENGTH(prt_ps_mbfonts); cmap++)
+           if (prt_match_encoding((char *)p_encoding, &prt_ps_mbfonts[cmap],
+                                                                   &p_mbenc))
+           {
+               if (p_mbenc_first == NULL)
+               {
+                   p_mbenc_first = p_mbenc;
+                   cmap_first = cmap;
+               }
+               if (prt_match_charset((char *)p_pmcs, &prt_ps_mbfonts[cmap],
+                                                                  &p_mbchar))
+                   break;
+           }
+
+       // Use first encoding matched if no charset matched
+       if (p_mbchar == NULL && p_mbenc_first != NULL)
+       {
+           p_mbenc = p_mbenc_first;
+           cmap = cmap_first;
+       }
+    }
+
+    prt_out_mbyte = (p_mbenc != NULL);
+    if (prt_out_mbyte)
+    {
+       // Build CMap name - will be same for all multi-byte fonts used
+       prt_cmap[0] = NUL;
+
+       prt_custom_cmap = (p_mbchar == NULL);
+       if (!prt_custom_cmap)
+       {
+           // Check encoding and character set are compatible
+           if ((p_mbenc->needs_charset & p_mbchar->has_charset) == 0)
+           {
+               emsg(_(e_incompatible_multi_byte_encoding_and_character_set));
+               return FALSE;
+           }
+
+           // Add charset name if not empty
+           if (p_mbchar->cmap_charset != NULL)
+           {
+               vim_strncpy((char_u *)prt_cmap,
+                     (char_u *)p_mbchar->cmap_charset, sizeof(prt_cmap) - 3);
+               STRCAT(prt_cmap, "-");
+           }
+       }
+       else
+       {
+           // Add custom CMap character set name
+           if (*p_pmcs == NUL)
+           {
+               emsg(_(e_printmbcharset_cannot_be_empty_with_multi_byte_encoding));
+               return FALSE;
+           }
+           vim_strncpy((char_u *)prt_cmap, p_pmcs, sizeof(prt_cmap) - 3);
+           STRCAT(prt_cmap, "-");
+       }
+
+       // CMap name ends with (optional) encoding name and -H for horizontal
+       if (p_mbenc->cmap_encoding != NULL && STRLEN(prt_cmap)
+                     + STRLEN(p_mbenc->cmap_encoding) + 3 < sizeof(prt_cmap))
+       {
+           STRCAT(prt_cmap, p_mbenc->cmap_encoding);
+           STRCAT(prt_cmap, "-");
+       }
+       STRCAT(prt_cmap, "H");
+
+       if (!mbfont_opts[OPT_MBFONT_REGULAR].present)
+       {
+           emsg(_(e_no_default_font_specified_for_multi_byte_printing));
+           return FALSE;
+       }
+
+       // Derive CID font names with fallbacks if not defined
+       if (!prt_build_cid_fontname(PRT_PS_FONT_ROMAN,
+                                   mbfont_opts[OPT_MBFONT_REGULAR].string,
+                                   mbfont_opts[OPT_MBFONT_REGULAR].strlen))
+           return FALSE;
+       if (mbfont_opts[OPT_MBFONT_BOLD].present)
+           if (!prt_build_cid_fontname(PRT_PS_FONT_BOLD,
+                                       mbfont_opts[OPT_MBFONT_BOLD].string,
+                                       mbfont_opts[OPT_MBFONT_BOLD].strlen))
+               return FALSE;
+       if (mbfont_opts[OPT_MBFONT_OBLIQUE].present)
+           if (!prt_build_cid_fontname(PRT_PS_FONT_OBLIQUE,
+                                       mbfont_opts[OPT_MBFONT_OBLIQUE].string,
+                                       mbfont_opts[OPT_MBFONT_OBLIQUE].strlen))
+               return FALSE;
+       if (mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].present)
+           if (!prt_build_cid_fontname(PRT_PS_FONT_BOLDOBLIQUE,
+                                  mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].string,
+                                 mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].strlen))
+               return FALSE;
+
+       // Check if need to use Courier for ASCII code range, and if so pick up
+       // the encoding to use
+       prt_use_courier = mbfont_opts[OPT_MBFONT_USECOURIER].present &&
+           (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_USECOURIER].string[0]) == 'y');
+       if (prt_use_courier)
+       {
+           // Use national ASCII variant unless ASCII wanted
+           if (mbfont_opts[OPT_MBFONT_ASCII].present &&
+               (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_ASCII].string[0]) == 'y'))
+               prt_ascii_encoding = "ascii";
+           else
+               prt_ascii_encoding = prt_ps_mbfonts[cmap].ascii_enc;
+       }
+
+       prt_ps_font = &prt_ps_mb_font;
+    }
+    else
+    {
+       prt_use_courier = FALSE;
+       prt_ps_font = &prt_ps_courier_font;
+    }
+
+    /*
+     * Find the size of the paper and set the margins.
+     */
+    prt_portrait = (!printer_opts[OPT_PRINT_PORTRAIT].present
+          || TOLOWER_ASC(printer_opts[OPT_PRINT_PORTRAIT].string[0]) == 'y');
+    if (printer_opts[OPT_PRINT_PAPER].present)
+    {
+       paper_name = (char *)printer_opts[OPT_PRINT_PAPER].string;
+       paper_strlen = printer_opts[OPT_PRINT_PAPER].strlen;
+    }
+    else
+    {
+       paper_name = "A4";
+       paper_strlen = 2;
+    }
+    for (i = 0; i < (int)PRT_MEDIASIZE_LEN; ++i)
+       if (STRLEN(prt_mediasize[i].name) == (unsigned)paper_strlen
+               && STRNICMP(prt_mediasize[i].name, paper_name,
+                                                          paper_strlen) == 0)
+           break;
+    if (i == PRT_MEDIASIZE_LEN)
+       i = 0;
+    prt_media = i;
+
+    /*
+     * Set PS pagesize based on media dimensions and print orientation.
+     * Note: Media and page sizes have defined meanings in PostScript and should
+     * be kept distinct.  Media is the paper (or transparency, or ...) that is
+     * printed on, whereas the page size is the area that the PostScript
+     * interpreter renders into.
+     */
+    if (prt_portrait)
+    {
+       prt_page_width = prt_mediasize[i].width;
+       prt_page_height = prt_mediasize[i].height;
+    }
+    else
+    {
+       prt_page_width = prt_mediasize[i].height;
+       prt_page_height = prt_mediasize[i].width;
+    }
+
+    /*
+     * Set PS page margins based on the PS pagesize, not the mediasize - this
+     * needs to be done before the cpl and lpp are calculated.
+     */
+    prt_page_margins(prt_page_width, prt_page_height, &left, &right, &top,
+                                                                   &bottom);
+    prt_left_margin = (float)left;
+    prt_right_margin = (float)right;
+    prt_top_margin = (float)top;
+    prt_bottom_margin = (float)bottom;
+
+    /*
+     * Set up the font size.
+     */
+    fontsize = PRT_PS_DEFAULT_FONTSIZE;
+    for (p = p_pfn; (p = vim_strchr(p, ':')) != NULL; ++p)
+       if (p[1] == 'h' && VIM_ISDIGIT(p[2]))
+           fontsize = atoi((char *)p + 2);
+    prt_font_metrics(fontsize);
+
+    /*
+     * Return the number of characters per line, and lines per page for the
+     * generic print code.
+     */
+    psettings->chars_per_line = prt_get_cpl();
+    psettings->lines_per_page = prt_get_lpp();
+
+    // Catch margin settings that leave no space for output!
+    if (psettings->chars_per_line <= 0 || psettings->lines_per_page <= 0)
+       return FAIL;
+
+    /*
+     * Sort out the number of copies to be printed.  PS by default will do
+     * uncollated copies for you, so once we know how many uncollated copies are
+     * wanted cache it away and lie to the generic code that we only want one
+     * uncollated copy.
+     */
+    psettings->n_collated_copies = 1;
+    psettings->n_uncollated_copies = 1;
+    prt_num_copies = 1;
+    prt_collate = (!printer_opts[OPT_PRINT_COLLATE].present
+           || TOLOWER_ASC(printer_opts[OPT_PRINT_COLLATE].string[0]) == 'y');
+    if (prt_collate)
+    {
+       // TODO: Get number of collated copies wanted.
+       psettings->n_collated_copies = 1;
+    }
+    else
+    {
+       // TODO: Get number of uncollated copies wanted and update the cached
+       // count.
+       prt_num_copies = 1;
+    }
+
+    psettings->jobname = jobname;
+
+    /*
+     * Set up printer duplex and tumble based on Duplex option setting - default
+     * is long sided duplex printing (i.e. no tumble).
+     */
+    prt_duplex = TRUE;
+    prt_tumble = FALSE;
+    psettings->duplex = 1;
+    if (printer_opts[OPT_PRINT_DUPLEX].present)
+    {
+       if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "off", 3) == 0)
+       {
+           prt_duplex = FALSE;
+           psettings->duplex = 0;
+       }
+       else if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "short", 5)
+                                                                        == 0)
+           prt_tumble = TRUE;
+    }
+
+    // For now user abort not supported
+    psettings->user_abort = 0;
+
+    // If the user didn't specify a file name, use a temp file.
+    if (psettings->outfile == NULL)
+    {
+       prt_ps_file_name = vim_tempname('p', TRUE);
+       if (prt_ps_file_name == NULL)
+       {
+           emsg(_(e_cant_get_temp_file_name));
+           return FAIL;
+       }
+       prt_ps_fd = mch_fopen((char *)prt_ps_file_name, WRITEBIN);
+    }
+    else
+    {
+       p = expand_env_save(psettings->outfile);
+       if (p != NULL)
+       {
+           prt_ps_fd = mch_fopen((char *)p, WRITEBIN);
+           vim_free(p);
+       }
+    }
+    if (prt_ps_fd == NULL)
+    {
+       emsg(_(e_cant_open_postscript_output_file));
+       mch_print_cleanup();
+       return FAIL;
+    }
+
+    prt_bufsiz = psettings->chars_per_line;
+    if (prt_out_mbyte)
+       prt_bufsiz *= 2;
+    ga_init2(&prt_ps_buffer, sizeof(char), prt_bufsiz);
+
+    prt_page_num = 0;
+
+    prt_attribute_change = FALSE;
+    prt_need_moveto = FALSE;
+    prt_need_font = FALSE;
+    prt_need_fgcol = FALSE;
+    prt_need_bgcol = FALSE;
+    prt_need_underline = FALSE;
+
+    prt_file_error = FALSE;
+
+    return OK;
+}
+
+    static int
+prt_add_resource(struct prt_ps_resource_S *resource)
+{
+    FILE*      fd_resource;
+    char_u     resource_buffer[512];
+    size_t     bytes_read;
+
+    fd_resource = mch_fopen((char *)resource->filename, READBIN);
+    if (fd_resource == NULL)
+    {
+       semsg(_(e_cant_open_file_str_2), resource->filename);
+       return FALSE;
+    }
+    prt_dsc_resources("BeginResource", prt_resource_types[resource->type],
+                                                    (char *)resource->title);
+
+    prt_dsc_textline("BeginDocument", (char *)resource->filename);
+
+    for (;;)
+    {
+       bytes_read = fread((char *)resource_buffer, sizeof(char_u),
+                          sizeof(resource_buffer), fd_resource);
+       if (ferror(fd_resource))
+       {
+           semsg(_(e_cant_read_postscript_resource_file_str),
+                                                           resource->filename);
+           fclose(fd_resource);
+           return FALSE;
+       }
+       if (bytes_read == 0)
+           break;
+       prt_write_file_raw_len(resource_buffer, (int)bytes_read);
+       if (prt_file_error)
+       {
+           fclose(fd_resource);
+           return FALSE;
+       }
+    }
+    fclose(fd_resource);
+
+    prt_dsc_noarg("EndDocument");
+
+    prt_dsc_noarg("EndResource");
+
+    return TRUE;
+}
+
+    int
+mch_print_begin(prt_settings_T *psettings)
+{
+    int                bbox[4];
+    double      left;
+    double      right;
+    double      top;
+    double      bottom;
+    struct prt_ps_resource_S *res_prolog;
+    struct prt_ps_resource_S *res_encoding;
+    char       buffer[256];
+    char_u      *p_encoding;
+    struct prt_ps_resource_S *res_cidfont;
+    struct prt_ps_resource_S *res_cmap;
+    int                retval = FALSE;
+
+    res_prolog = ALLOC_ONE(struct prt_ps_resource_S);
+    res_encoding = ALLOC_ONE(struct prt_ps_resource_S);
+    res_cidfont = ALLOC_ONE(struct prt_ps_resource_S);
+    res_cmap = ALLOC_ONE(struct prt_ps_resource_S);
+    if (res_prolog == NULL || res_encoding == NULL
+           || res_cidfont == NULL || res_cmap == NULL)
+       goto theend;
+
+    /*
+     * PS DSC Header comments - no PS code!
+     */
+    prt_dsc_start();
+    prt_dsc_textline("Title", (char *)psettings->jobname);
+    if (!get_user_name((char_u *)buffer, 256))
+       STRCPY(buffer, "Unknown");
+    prt_dsc_textline("For", buffer);
+    prt_dsc_textline("Creator", VIM_VERSION_LONG);
+    // Note: to ensure Clean8bit I don't think we can use LC_TIME
+
+    prt_dsc_textline("CreationDate", get_ctime(time(NULL), FALSE));
+    prt_dsc_textline("DocumentData", "Clean8Bit");
+    prt_dsc_textline("Orientation", prt_portrait ? "Portrait" : "Landscape");
+    prt_dsc_atend("Pages");
+    prt_dsc_textline("PageOrder", "Ascend");
+    // The bbox does not change with orientation - it is always in the default
+    // user coordinate system!  We have to recalculate right and bottom
+    // coordinates based on the font metrics for the bbox to be accurate.
+    prt_page_margins(prt_mediasize[prt_media].width,
+                                           prt_mediasize[prt_media].height,
+                                           &left, &right, &top, &bottom);
+    bbox[0] = (int)left;
+    if (prt_portrait)
+    {
+       // In portrait printing the fixed point is the top left corner so we
+       // derive the bbox from that point.  We have the expected cpl chars
+       // across the media and lpp lines down the media.
+       bbox[1] = (int)(top - (psettings->lines_per_page + prt_header_height())
+                                                   * (double)prt_line_height);
+       bbox[2] = (int)(left + psettings->chars_per_line
+                                              * (double)prt_char_width + 0.5);
+       bbox[3] = (int)(top + 0.5);
+    }
+    else
+    {
+       // In landscape printing the fixed point is the bottom left corner so we
+       // derive the bbox from that point.  We have lpp chars across the media
+       // and cpl lines up the media.
+       bbox[1] = (int)bottom;
+       bbox[2] = (int)(left + ((psettings->lines_per_page
+                             + prt_header_height()) * prt_line_height) + 0.5);
+       bbox[3] = (int)(bottom + psettings->chars_per_line
+                                              * (double)prt_char_width + 0.5);
+    }
+    prt_dsc_ints("BoundingBox", 4, bbox);
+    // The media width and height does not change with landscape printing!
+    prt_dsc_docmedia(prt_mediasize[prt_media].name,
+                               prt_mediasize[prt_media].width,
+                               prt_mediasize[prt_media].height,
+                               (double)0, NULL, NULL);
+    // Define fonts needed
+    if (!prt_out_mbyte || prt_use_courier)
+       prt_dsc_font_resource("DocumentNeededResources", &prt_ps_courier_font);
+    if (prt_out_mbyte)
+    {
+       prt_dsc_font_resource((prt_use_courier ? NULL
+                               : "DocumentNeededResources"), &prt_ps_mb_font);
+       if (!prt_custom_cmap)
+           prt_dsc_resources(NULL, "cmap", prt_cmap);
+    }
+
+    // Search for external resources VIM supplies
+    if (!prt_find_resource("prolog", res_prolog))
+    {
+       semsg(_(e_cant_find_postscript_resource_file_str_ps), "prolog");
+       goto theend;
+    }
+    if (!prt_open_resource(res_prolog))
+       goto theend;
+    if (!prt_check_resource(res_prolog, PRT_PROLOG_VERSION))
+       goto theend;
+    if (prt_out_mbyte)
+    {
+       // Look for required version of multi-byte printing procset
+       if (!prt_find_resource("cidfont", res_cidfont))
+       {
+           semsg(_(e_cant_find_postscript_resource_file_str_ps), "cidfont");
+           goto theend;
+       }
+       if (!prt_open_resource(res_cidfont))
+           goto theend;
+       if (!prt_check_resource(res_cidfont, PRT_CID_PROLOG_VERSION))
+           goto theend;
+    }
+
+    // Find an encoding to use for printing.
+    // Check 'printencoding'. If not set or not found, then use 'encoding'. If
+    // that cannot be found then default to "latin1".
+    // Note: VIM specific encoding header is always skipped.
+    if (!prt_out_mbyte)
+    {
+       p_encoding = enc_skip(p_penc);
+       if (*p_encoding == NUL
+               || !prt_find_resource((char *)p_encoding, res_encoding))
+       {
+           // 'printencoding' not set or not supported - find alternate
+           int         props;
+
+           p_encoding = enc_skip(p_enc);
+           props = enc_canon_props(p_encoding);
+           if (!(props & ENC_8BIT)
+                   || !prt_find_resource((char *)p_encoding, res_encoding))
+               // 8-bit 'encoding' is not supported
+           {
+               // Use latin1 as default printing encoding
+               p_encoding = (char_u *)"latin1";
+               if (!prt_find_resource((char *)p_encoding, res_encoding))
+               {
+                   semsg(_(e_cant_find_postscript_resource_file_str_ps),
+                           p_encoding);
+                   goto theend;
+               }
+           }
+       }
+       if (!prt_open_resource(res_encoding))
+           goto theend;
+       // For the moment there are no checks on encoding resource files to
+       // perform
+    }
+    else
+    {
+       p_encoding = enc_skip(p_penc);
+       if (*p_encoding == NUL)
+           p_encoding = enc_skip(p_enc);
+       if (prt_use_courier)
+       {
+           // Include ASCII range encoding vector
+           if (!prt_find_resource(prt_ascii_encoding, res_encoding))
+           {
+               semsg(_(e_cant_find_postscript_resource_file_str_ps),
+                                                         prt_ascii_encoding);
+               goto theend;
+           }
+           if (!prt_open_resource(res_encoding))
+               goto theend;
+           // For the moment there are no checks on encoding resource files to
+           // perform
+       }
+    }
+
+    prt_conv.vc_type = CONV_NONE;
+    if (!(enc_canon_props(p_enc) & enc_canon_props(p_encoding) & ENC_8BIT))
+    {
+       // Set up encoding conversion if required
+       if (FAIL == convert_setup(&prt_conv, p_enc, p_encoding))
+       {
+           semsg(_(e_unable_to_convert_to_print_encoding_str), p_encoding);
+           goto theend;
+       }
+       prt_do_conv = TRUE;
+    }
+    prt_do_conv = prt_conv.vc_type != CONV_NONE;
+
+    if (prt_out_mbyte && prt_custom_cmap)
+    {
+       // Find user supplied CMap
+       if (!prt_find_resource(prt_cmap, res_cmap))
+       {
+           semsg(_(e_cant_find_postscript_resource_file_str_ps), prt_cmap);
+           goto theend;
+       }
+       if (!prt_open_resource(res_cmap))
+           goto theend;
+    }
+
+    // List resources supplied
+    STRCPY(buffer, res_prolog->title);
+    STRCAT(buffer, " ");
+    STRCAT(buffer, res_prolog->version);
+    prt_dsc_resources("DocumentSuppliedResources", "procset", buffer);
+    if (prt_out_mbyte)
+    {
+       STRCPY(buffer, res_cidfont->title);
+       STRCAT(buffer, " ");
+       STRCAT(buffer, res_cidfont->version);
+       prt_dsc_resources(NULL, "procset", buffer);
+
+       if (prt_custom_cmap)
+       {
+           STRCPY(buffer, res_cmap->title);
+           STRCAT(buffer, " ");
+           STRCAT(buffer, res_cmap->version);
+           prt_dsc_resources(NULL, "cmap", buffer);
+       }
+    }
+    if (!prt_out_mbyte || prt_use_courier)
+    {
+       STRCPY(buffer, res_encoding->title);
+       STRCAT(buffer, " ");
+       STRCAT(buffer, res_encoding->version);
+       prt_dsc_resources(NULL, "encoding", buffer);
+    }
+    prt_dsc_requirements(prt_duplex, prt_tumble, prt_collate,
+# ifdef FEAT_SYN_HL
+                                       psettings->do_syntax
+# else
+                                       0
+# endif
+                                       , prt_num_copies);
+    prt_dsc_noarg("EndComments");
+
+    /*
+     * PS Document page defaults
+     */
+    prt_dsc_noarg("BeginDefaults");
+
+    // List font resources most likely common to all pages
+    if (!prt_out_mbyte || prt_use_courier)
+       prt_dsc_font_resource("PageResources", &prt_ps_courier_font);
+    if (prt_out_mbyte)
+    {
+       prt_dsc_font_resource((prt_use_courier ? NULL : "PageResources"),
+                                                           &prt_ps_mb_font);
+       if (!prt_custom_cmap)
+           prt_dsc_resources(NULL, "cmap", prt_cmap);
+    }
+
+    // Paper will be used for all pages
+    prt_dsc_textline("PageMedia", prt_mediasize[prt_media].name);
+
+    prt_dsc_noarg("EndDefaults");
+
+    /*
+     * PS Document prolog inclusion - all required procsets.
+     */
+    prt_dsc_noarg("BeginProlog");
+
+    // Add required procsets - NOTE: order is important!
+    if (!prt_add_resource(res_prolog))
+       goto theend;
+    if (prt_out_mbyte)
+    {
+       // Add CID font procset, and any user supplied CMap
+       if (!prt_add_resource(res_cidfont))
+           goto theend;
+       if (prt_custom_cmap && !prt_add_resource(res_cmap))
+           goto theend;
+    }
+
+    if (!prt_out_mbyte || prt_use_courier)
+       // There will be only one Roman font encoding to be included in the PS
+       // file.
+       if (!prt_add_resource(res_encoding))
+           goto theend;
+
+    prt_dsc_noarg("EndProlog");
+
+    /*
+     * PS Document setup - must appear after the prolog
+     */
+    prt_dsc_noarg("BeginSetup");
+
+    // Device setup - page size and number of uncollated copies
+    prt_write_int((int)prt_mediasize[prt_media].width);
+    prt_write_int((int)prt_mediasize[prt_media].height);
+    prt_write_int(0);
+    prt_write_string("sps\n");
+    prt_write_int(prt_num_copies);
+    prt_write_string("nc\n");
+    prt_write_boolean(prt_duplex);
+    prt_write_boolean(prt_tumble);
+    prt_write_string("dt\n");
+    prt_write_boolean(prt_collate);
+    prt_write_string("c\n");
+
+    // Font resource inclusion and definition
+    if (!prt_out_mbyte || prt_use_courier)
+    {
+       // When using Courier for ASCII range when printing multi-byte, need to
+       // pick up ASCII encoding to use with it.
+       if (prt_use_courier)
+           p_encoding = (char_u *)prt_ascii_encoding;
+       prt_dsc_resources("IncludeResource", "font",
+                         prt_ps_courier_font.ps_fontname[PRT_PS_FONT_ROMAN]);
+       prt_def_font("F0", (char *)p_encoding, (int)prt_line_height,
+                    prt_ps_courier_font.ps_fontname[PRT_PS_FONT_ROMAN]);
+       prt_dsc_resources("IncludeResource", "font",
+                         prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLD]);
+       prt_def_font("F1", (char *)p_encoding, (int)prt_line_height,
+                    prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLD]);
+       prt_dsc_resources("IncludeResource", "font",
+                         prt_ps_courier_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
+       prt_def_font("F2", (char *)p_encoding, (int)prt_line_height,
+                    prt_ps_courier_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
+       prt_dsc_resources("IncludeResource", "font",
+                         prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
+       prt_def_font("F3", (char *)p_encoding, (int)prt_line_height,
+                    prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
+    }
+    if (prt_out_mbyte)
+    {
+       // Define the CID fonts to be used in the job.  Typically CJKV fonts do
+       // not have an italic form being a western style, so where no font is
+       // defined for these faces VIM falls back to an existing face.
+       // Note: if using Courier for the ASCII range then the printout will
+       // have bold/italic/bolditalic regardless of the setting of printmbfont.
+       prt_dsc_resources("IncludeResource", "font",
+                         prt_ps_mb_font.ps_fontname[PRT_PS_FONT_ROMAN]);
+       if (!prt_custom_cmap)
+           prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
+       prt_def_cidfont("CF0", (int)prt_line_height,
+                       prt_ps_mb_font.ps_fontname[PRT_PS_FONT_ROMAN]);
+
+       if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD] != NULL)
+       {
+           prt_dsc_resources("IncludeResource", "font",
+                             prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD]);
+           if (!prt_custom_cmap)
+               prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
+           prt_def_cidfont("CF1", (int)prt_line_height,
+                           prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD]);
+       }
+       else
+           // Use ROMAN for BOLD
+           prt_dup_cidfont("CF0", "CF1");
+
+       if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE] != NULL)
+       {
+           prt_dsc_resources("IncludeResource", "font",
+                             prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
+           if (!prt_custom_cmap)
+               prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
+           prt_def_cidfont("CF2", (int)prt_line_height,
+                           prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
+       }
+       else
+           // Use ROMAN for OBLIQUE
+           prt_dup_cidfont("CF0", "CF2");
+
+       if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE] != NULL)
+       {
+           prt_dsc_resources("IncludeResource", "font",
+                             prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
+           if (!prt_custom_cmap)
+               prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
+           prt_def_cidfont("CF3", (int)prt_line_height,
+                           prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
+       }
+       else
+           // Use BOLD for BOLDOBLIQUE
+           prt_dup_cidfont("CF1", "CF3");
+    }
+
+    // Misc constant vars used for underlining and background rects
+    prt_def_var("UO", PRT_PS_FONT_TO_USER(prt_line_height,
+                                               prt_ps_font->uline_offset), 2);
+    prt_def_var("UW", PRT_PS_FONT_TO_USER(prt_line_height,
+                                                prt_ps_font->uline_width), 2);
+    prt_def_var("BO", prt_bgcol_offset, 2);
+
+    prt_dsc_noarg("EndSetup");
+
+    // Fail if any problems writing out to the PS file
+    retval = !prt_file_error;
+
+theend:
+    vim_free(res_prolog);
+    vim_free(res_encoding);
+    vim_free(res_cidfont);
+    vim_free(res_cmap);
+
+    return retval;
+}
+
+    void
+mch_print_end(prt_settings_T *psettings)
+{
+    prt_dsc_noarg("Trailer");
+
+    /*
+     * Output any info we don't know in toto until we finish
+     */
+    prt_dsc_ints("Pages", 1, &prt_page_num);
+
+    prt_dsc_noarg("EOF");
+
+    // Write CTRL-D to close serial communication link if used.
+    // NOTHING MUST BE WRITTEN AFTER THIS!
+    prt_write_file((char_u *)"\004");
+
+    if (!prt_file_error && psettings->outfile == NULL
+                                       && !got_int && !psettings->user_abort)
+    {
+       // Close the file first.
+       if (prt_ps_fd != NULL)
+       {
+           fclose(prt_ps_fd);
+           prt_ps_fd = NULL;
+       }
+       prt_message((char_u *)_("Sending to printer..."));
+
+       // Not printing to a file: use 'printexpr' to print the file.
+       if (eval_printexpr(prt_ps_file_name, psettings->arguments) == FAIL)
+           emsg(_(e_failed_to_print_postscript_file));
+       else
+           prt_message((char_u *)_("Print job sent."));
+    }
+
+    mch_print_cleanup();
+}
+
+    int
+mch_print_end_page(void)
+{
+    prt_flush_buffer();
+
+    prt_write_string("re sp\n");
+
+    prt_dsc_noarg("PageTrailer");
+
+    return !prt_file_error;
+}
+
+    int
+mch_print_begin_page(char_u *str UNUSED)
+{
+    int                page_num[2];
+
+    prt_page_num++;
+
+    page_num[0] = page_num[1] = prt_page_num;
+    prt_dsc_ints("Page", 2, page_num);
+
+    prt_dsc_noarg("BeginPageSetup");
+
+    prt_write_string("sv\n0 g\n");
+    prt_in_ascii = !prt_out_mbyte;
+    if (prt_out_mbyte)
+       prt_write_string("CF0 sf\n");
+    else
+       prt_write_string("F0 sf\n");
+    prt_fgcol = PRCOLOR_BLACK;
+    prt_bgcol = PRCOLOR_WHITE;
+    prt_font = PRT_PS_FONT_ROMAN;
+
+    // Set up page transformation for landscape printing.
+    if (!prt_portrait)
+    {
+       prt_write_int(-((int)prt_mediasize[prt_media].width));
+       prt_write_string("sl\n");
+    }
+
+    prt_dsc_noarg("EndPageSetup");
+
+    // We have reset the font attributes, force setting them again.
+    prt_curr_bg = (long_u)0xffffffff;
+    prt_curr_fg = (long_u)0xffffffff;
+    prt_curr_bold = MAYBE;
+
+    return !prt_file_error;
+}
+
+    int
+mch_print_blank_page(void)
+{
+    return (mch_print_begin_page(NULL) ? (mch_print_end_page()) : FALSE);
+}
+
+static float prt_pos_x = 0;
+static float prt_pos_y = 0;
+
+    void
+mch_print_start_line(int margin, int page_line)
+{
+    prt_pos_x = prt_left_margin;
+    if (margin)
+       prt_pos_x -= prt_number_width;
+
+    prt_pos_y = prt_top_margin - prt_first_line_height -
+                                       page_line * prt_line_height;
+
+    prt_attribute_change = TRUE;
+    prt_need_moveto = TRUE;
+    prt_half_width = FALSE;
+}
+
+    int
+mch_print_text_out(char_u *textp, int len UNUSED)
+{
+    char_u     *p = textp;
+    int                need_break;
+    char_u     ch;
+    char_u      ch_buff[8];
+    float       char_width;
+    float       next_pos;
+    int                in_ascii;
+    int                half_width;
+    char_u     *tofree = NULL;
+
+    char_width = prt_char_width;
+
+    // Ideally VIM would create a rearranged CID font to combine a Roman and
+    // CJKV font to do what VIM is doing here - use a Roman font for characters
+    // in the ASCII range, and the original CID font for everything else.
+    // The problem is that GhostScript still (as of 8.13) does not support
+    // rearranged fonts even though they have been documented by Adobe for 7
+    // years!  If they ever do, a lot of this code will disappear.
+    if (prt_use_courier)
+    {
+       in_ascii = (len == 1 && *p < 0x80);
+       if (prt_in_ascii)
+       {
+           if (!in_ascii)
+           {
+               // No longer in ASCII range - need to switch font
+               prt_in_ascii = FALSE;
+               prt_need_font = TRUE;
+               prt_attribute_change = TRUE;
+           }
+       }
+       else if (in_ascii)
+       {
+           // Now in ASCII range - need to switch font
+           prt_in_ascii = TRUE;
+           prt_need_font = TRUE;
+           prt_attribute_change = TRUE;
+       }
+    }
+    if (prt_out_mbyte)
+    {
+       half_width = ((*mb_ptr2cells)(p) == 1);
+       if (half_width)
+           char_width /= 2;
+       if (prt_half_width)
+       {
+           if (!half_width)
+           {
+               prt_half_width = FALSE;
+               prt_pos_x += prt_char_width/4;
+               prt_need_moveto = TRUE;
+               prt_attribute_change = TRUE;
+           }
+       }
+       else if (half_width)
+       {
+           prt_half_width = TRUE;
+           prt_pos_x += prt_char_width/4;
+           prt_need_moveto = TRUE;
+           prt_attribute_change = TRUE;
+       }
+    }
+
+    // Output any required changes to the graphics state, after flushing any
+    // text buffered so far.
+    if (prt_attribute_change)
+    {
+       prt_flush_buffer();
+       // Reset count of number of chars that will be printed
+       prt_text_run = 0;
+
+       if (prt_need_moveto)
+       {
+           prt_pos_x_moveto = prt_pos_x;
+           prt_pos_y_moveto = prt_pos_y;
+           prt_do_moveto = TRUE;
+
+           prt_need_moveto = FALSE;
+       }
+       if (prt_need_font)
+       {
+           if (!prt_in_ascii)
+               prt_write_string("CF");
+           else
+               prt_write_string("F");
+           prt_write_int(prt_font);
+           prt_write_string("sf\n");
+           prt_need_font = FALSE;
+       }
+       if (prt_need_fgcol)
+       {
+           int     r, g, b;
+           r = ((unsigned)prt_fgcol & 0xff0000) >> 16;
+           g = ((unsigned)prt_fgcol & 0xff00) >> 8;
+           b = prt_fgcol & 0xff;
+
+           prt_write_real(r / 255.0, 3);
+           if (r == g && g == b)
+               prt_write_string("g\n");
+           else
+           {
+               prt_write_real(g / 255.0, 3);
+               prt_write_real(b / 255.0, 3);
+               prt_write_string("r\n");
+           }
+           prt_need_fgcol = FALSE;
+       }
+
+       if (prt_bgcol != PRCOLOR_WHITE)
+       {
+           prt_new_bgcol = prt_bgcol;
+           if (prt_need_bgcol)
+               prt_do_bgcol = TRUE;
+       }
+       else
+           prt_do_bgcol = FALSE;
+       prt_need_bgcol = FALSE;
+
+       if (prt_need_underline)
+           prt_do_underline = prt_underline;
+       prt_need_underline = FALSE;
+
+       prt_attribute_change = FALSE;
+    }
+
+    if (prt_do_conv)
+    {
+       // Convert from multi-byte to 8-bit encoding
+       tofree = p = string_convert(&prt_conv, p, &len);
+       if (p == NULL)
+       {
+           p = (char_u *)"";
+           len = 0;
+       }
+    }
+
+    if (prt_out_mbyte)
+    {
+       // Multi-byte character strings are represented more efficiently as hex
+       // strings when outputting clean 8 bit PS.
+       while (len-- > 0)
+       {
+           ch = prt_hexchar[(unsigned)(*p) >> 4];
+           ga_append(&prt_ps_buffer, ch);
+           ch = prt_hexchar[(*p) & 0xf];
+           ga_append(&prt_ps_buffer, ch);
+           p++;
+       }
+    }
+    else
+    {
+       // Add next character to buffer of characters to output.
+       // Note: One printed character may require several PS characters to
+       // represent it, but we only count them as one printed character.
+       ch = *p;
+       if (ch < 32 || ch == '(' || ch == ')' || ch == '\\')
+       {
+           // Convert non-printing characters to either their escape or octal
+           // sequence, ensures PS sent over a serial line does not interfere
+           // with the comms protocol.
+           ga_append(&prt_ps_buffer, '\\');
+           switch (ch)
+           {
+               case BS:   ga_append(&prt_ps_buffer, 'b'); break;
+               case TAB:  ga_append(&prt_ps_buffer, 't'); break;
+               case NL:   ga_append(&prt_ps_buffer, 'n'); break;
+               case FF:   ga_append(&prt_ps_buffer, 'f'); break;
+               case CAR:  ga_append(&prt_ps_buffer, 'r'); break;
+               case '(':  ga_append(&prt_ps_buffer, '('); break;
+               case ')':  ga_append(&prt_ps_buffer, ')'); break;
+               case '\\': ga_append(&prt_ps_buffer, '\\'); break;
+
+               default:
+                          sprintf((char *)ch_buff, "%03o", (unsigned int)ch);
+                          ga_append(&prt_ps_buffer, ch_buff[0]);
+                          ga_append(&prt_ps_buffer, ch_buff[1]);
+                          ga_append(&prt_ps_buffer, ch_buff[2]);
+                          break;
+           }
+       }
+       else
+           ga_append(&prt_ps_buffer, ch);
+    }
+
+    // Need to free any translated characters
+    vim_free(tofree);
+
+    prt_text_run += char_width;
+    prt_pos_x += char_width;
+
+    // The downside of fp - use relative error on right margin check
+    next_pos = prt_pos_x + prt_char_width;
+    need_break = (next_pos > prt_right_margin) &&
+                   ((next_pos - prt_right_margin) > (prt_right_margin*1e-5));
+
+    if (need_break)
+       prt_flush_buffer();
+
+    return need_break;
+}
+
+    void
+mch_print_set_font(int iBold, int iItalic, int iUnderline)
+{
+    int                font = 0;
+
+    if (iBold)
+       font |= 0x01;
+    if (iItalic)
+       font |= 0x02;
+
+    if (font != prt_font)
+    {
+       prt_font = font;
+       prt_attribute_change = TRUE;
+       prt_need_font = TRUE;
+    }
+    if (prt_underline != iUnderline)
+    {
+       prt_underline = iUnderline;
+       prt_attribute_change = TRUE;
+       prt_need_underline = TRUE;
+    }
+}
+
+    void
+mch_print_set_bg(long_u bgcol)
+{
+    prt_bgcol = (int)bgcol;
+    prt_attribute_change = TRUE;
+    prt_need_bgcol = TRUE;
+}
+
+    void
+mch_print_set_fg(long_u fgcol)
+{
+    if (fgcol == (long_u)prt_fgcol)
+       return;
+
+    prt_fgcol = (int)fgcol;
+    prt_attribute_change = TRUE;
+    prt_need_fgcol = TRUE;
+}
+
+#endif // FEAT_POSTSCRIPT
diff --git a/src/hardcopy_postscript.h b/src/hardcopy_postscript.h
new file mode 100644 (file)
index 0000000..fe41df8
--- /dev/null
@@ -0,0 +1,42 @@
+/* vi:set ts=8 sts=4 sw=4 noet:
+ *
+ * VIM - Vi IMproved   by Bram Moolenaar
+ *
+ * Do ":help uganda"  in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ * See README.txt for an overview of the Vim source code.
+ */
+
+#ifndef HARDCOPY_POSTSCRIPT_H
+#define HARDCOPY_POSTSCRIPT_H
+
+#include "vim.h"
+
+#if defined(FEAT_POSTSCRIPT)
+
+# define OPT_MBFONT_USECOURIER  0
+# define OPT_MBFONT_ASCII       1
+# define OPT_MBFONT_REGULAR     2
+# define OPT_MBFONT_BOLD       3
+# define OPT_MBFONT_OBLIQUE     4
+# define OPT_MBFONT_BOLDOBLIQUE 5
+# define OPT_MBFONT_NUM_OPTIONS 6
+
+extern option_table_T mbfont_opts[OPT_MBFONT_NUM_OPTIONS];
+
+void mch_print_cleanup(void);
+int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit);
+int mch_print_begin(prt_settings_T *psettings);
+void mch_print_end(prt_settings_T *psettings);
+int mch_print_end_page(void);
+int mch_print_begin_page(char_u *str);
+int mch_print_blank_page(void);
+void mch_print_start_line(int margin, int page_line);
+int mch_print_text_out(char_u *textp, int len);
+void mch_print_set_font(int iBold, int iItalic, int iUnderline);
+void mch_print_set_bg(long_u bgcol);
+void mch_print_set_fg(long_u fgcol);
+
+#endif
+
+#endif
index aa4c990dcba0f7bd2f4c84dbf8be1885d7e6f3b5..353082cdab40a3f59f564e9882acde772f533c91 100644 (file)
@@ -323,7 +323,7 @@ set_init_default_printencoding(void)
 #endif
 }
 
-#ifdef FEAT_POSTSCRIPT
+#if defined(FEAT_POSTSCRIPT) || defined(FEAT_PRINT_PANGO)
 /*
  * Initialize the 'printexpr' option to a default value.
  */
@@ -724,7 +724,7 @@ set_init_1(int clean_arg)
     set_init_default_maxmemtot();
     set_init_default_cdpath();
     set_init_default_printencoding();
-#ifdef FEAT_POSTSCRIPT
+#if defined(FEAT_POSTSCRIPT) || defined(FEAT_PRINT_PANGO)
     set_init_default_printexpr();
 #endif
 
index c3df71fcaf75d958e513ece7f69cea0c7d44f985..326b48b6301454b67fa797fc9155327d0bfb4b1f 100644 (file)
@@ -666,10 +666,12 @@ EXTERN char_u     *p_pvp;         // 'previewpopup'
 EXTERN char_u  *p_pdev;        // 'printdevice'
 # ifdef FEAT_POSTSCRIPT
 EXTERN char_u  *p_penc;        // 'printencoding'
-EXTERN char_u  *p_pexpr;       // 'printexpr'
 EXTERN char_u  *p_pmfn;        // 'printmbfont'
 EXTERN char_u  *p_pmcs;        // 'printmbcharset'
 # endif
+# if defined(FEAT_POSTSCRIPT) || defined(FEAT_PRINT_PANGO)
+EXTERN char_u  *p_pexpr;       // 'printexpr'
+# endif
 EXTERN char_u  *p_pfn;         // 'printfont'
 EXTERN char_u  *p_popt;        // 'printoptions'
 EXTERN char_u  *p_header;      // 'printheader'
index f238209a75d4bb92de38c094899bc020566da5ca..187747368a8eb8e7a39cfb86fcbbdd8c512246b2 100644 (file)
@@ -2022,7 +2022,7 @@ static struct vimoption options[] =
 #endif
                            SCTX_INIT},
     {"printexpr", "pexpr",  P_STRING|P_VI_DEF|P_SECURE,
-#ifdef FEAT_POSTSCRIPT
+#if defined(FEAT_POSTSCRIPT) || defined(FEAT_PRINT_PANGO)
                            (char_u *)&p_pexpr, PV_NONE, did_set_optexpr, NULL,
                            {(char_u *)"", (char_u *)0L}
 #else
@@ -2036,6 +2036,8 @@ static struct vimoption options[] =
                            {
 # ifdef MSWIN
                                (char_u *)"Courier_New:h10",
+# elif defined(FEAT_PRINT_PANGO)
+                               (char_u *)"Courier New 10",
 # else
                                (char_u *)"courier",
 # endif
index 3c5f7a9250ebece371e9411d6a414fc8cef46a0b..d4a9b52832b8b030eee296ffd92fba9a086e09df 100644 (file)
@@ -109,5 +109,6 @@ int gui_mch_dialog(int type, char_u *title, char_u *message, char_u *buttons, in
 void gui_mch_find_dialog(exarg_T *eap);
 void gui_mch_replace_dialog(exarg_T *eap);
 void ex_helpfind(exarg_T *eap);
-void gui_gtk4_hardcopy(exarg_T *eap);
+char_u *gui_gtk4_print_dialog(prt_settings_T *psettings, char_u *jobname, double *page_width, double *page_height);
+void gui_gtk4_print_finish(void);
 /* vim: set ft=c : */
index 8a8c73e56f1eb57ce97a7d7c23f34fe3736e2f9d..369aab398c662c70e663cc293a8cb2f5c432bfa1 100644 (file)
@@ -4,17 +4,6 @@ char *parse_printmbfont(optset_T *args);
 int prt_header_height(void);
 int prt_use_number(void);
 int prt_get_unit(int idx);
+void prt_message(char_u *s);
 void ex_hardcopy(exarg_T *eap);
-void mch_print_cleanup(void);
-int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit);
-int mch_print_begin(prt_settings_T *psettings);
-void mch_print_end(prt_settings_T *psettings);
-int mch_print_end_page(void);
-int mch_print_begin_page(char_u *str);
-int mch_print_blank_page(void);
-void mch_print_start_line(int margin, int page_line);
-int mch_print_text_out(char_u *textp, int len);
-void mch_print_set_font(int iBold, int iItalic, int iUnderline);
-void mch_print_set_bg(long_u bgcol);
-void mch_print_set_fg(long_u fgcol);
 /* vim: set ft=c : */
index 38037c9d5ea8a01377cef7a259a05a9f580d29c8..b1ec3fa78868ce047ca302b794afc8d013822aff 100644 (file)
@@ -4831,8 +4831,11 @@ typedef struct
     int                do_syntax;
 #endif
     int                user_abort;
+#ifdef FEAT_PRINT_PANGO
+    int                user_abort_msg;
+#endif
     char_u     *jobname;
-#ifdef FEAT_POSTSCRIPT
+#if defined(FEAT_POSTSCRIPT) || defined(FEAT_PRINT_PANGO)
     char_u     *outfile;
     char_u     *arguments;
 #endif
index 33ef4bff1bfb11cf813b3c75515d05b8cea9ba62..62065139c7ced19cca27fc6e76365c7fb57f8468 100644 (file)
@@ -1,5 +1,13 @@
 " Test :hardcopy
 
+func s:HasPostscript()
+  if has('postscript') || has('pango')
+    return
+  endif
+  throw 'Skipped: hardcopy backend does not use postscript'
+endfunc
+command -nargs=0 HasPostscript call s:HasPostscript()
+
 func Test_printoptions()
   edit test_hardcopy.vim
   syn on
@@ -97,7 +105,7 @@ func Test_printmbcharset()
 endfunc
 
 func Test_printexpr()
-  CheckFeature postscript
+  HasPostscript
 
   " Not a very useful printexpr value, but enough to test
   " hardcopy with 'printexpr'.
@@ -136,7 +144,7 @@ func Test_printexpr()
 endfunc
 
 func Test_errors()
-  CheckFeature postscript
+  HasPostscript
 
   edit test_hardcopy.vim
   call assert_fails('hardcopy >', 'E324:')
@@ -150,7 +158,7 @@ func Test_dark_background()
   for bg in ['dark', 'light']
     exe 'set background=' .. bg
 
-    if has('postscript')
+    if has('postscript') || has('pango')
       hardcopy > Xhardcopy_dark_background
       let lines = readfile('Xhardcopy_dark_background')
       call assert_true(len(lines) > 20)
@@ -164,7 +172,7 @@ func Test_dark_background()
 endfun
 
 func Test_empty_buffer()
-  CheckFeature postscript
+  HasPostscript
 
   new
   call assert_equal("\nNo text to be printed", execute('hardcopy'))
@@ -182,7 +190,7 @@ func Test_printheader_parsing()
 endfunc
 
 func Test_fname_with_spaces()
-  CheckFeature postscript
+  HasPostscript
 
   split t\ e\ s\ t.txt
   call setline(1, ['just', 'some', 'text'])
@@ -208,27 +216,34 @@ func Test_illegal_byte()
 endfunc
 
 func Test_printoptions_portrait()
-  CheckFeature postscript
+  HasPostscript
   edit test_hardcopy.vim
   syn on
 
   set printoptions=portrait:y
   1,50hardcopy > Xhardcopy_printoptions_portrait
   let lines = readfile('Xhardcopy_printoptions_portrait')
-  call assert_match('Orientation: Portrait', lines[6])
-  call assert_match('BoundingBox: 59 49 564 800', lines[9])
-  call assert_match('DocumentMedia: A4', lines[10])
-  call assert_match('PageMedia: A4', lines[24])
-  call delete('Xhardcopy_printoptions')
+
+  call assert_true(match(lines, 'Orientation: Portrait') != -1)
+  if has('postscript')
+    call assert_true(match(lines, 'BoundingBox: 59 49 564 800') != -1)
+  endif
+  call assert_true(match(lines, 'DocumentMedia: A4') != -1)
+  call assert_true(match(lines, 'PageMedia: A4') != -1)
+  call delete('Xhardcopy_printoptions_portrait')
 
   set printoptions=portrait:n
   1,50hardcopy > Xhardcopy_printoptions_portrait
   let lines = readfile('Xhardcopy_printoptions_portrait')
-  call assert_match('Orientation: Landscape', lines[6])
-  call assert_match('BoundingBox: 59 42 590 756', lines[9])
-  call assert_match('DocumentMedia: A4', lines[10])
-  call assert_match('PageMedia: A4', lines[24])
-  call delete('Xhardcopy_printoptions')
+  call delete('Xhardcopy_printoptions_portrait')
+
+  call assert_true(match(lines, 'Orientation: Landscape') != -1)
+  if has('postscript')
+    call assert_true(match(lines, 'BoundingBox: 59 42 590 756') != -1)
+  endif
+  call assert_true(match(lines, 'DocumentMedia: A4') != -1)
+  call assert_true(match(lines, 'PageMedia: A4') != -1)
+  call delete('Xhardcopy_printoptions_portrait')
 
   set printoptions&
   bwipe
index 7c831d2ae25b3684d338898eb874d05d826b20a4..fedd56dba8677440319b0f5b7062ed1ff5f69c92 100644 (file)
@@ -461,6 +461,11 @@ static char *(features[]) =
        "+packages",
 #else
        "-packages",
+#endif
+#ifdef FEAT_PRINT_PANGO
+       "+pango",
+#else
+       "-pango",
 #endif
        "+path_extra",
 #ifdef FEAT_PERL
@@ -758,6 +763,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    898,
 /**/
     897,
 /**/
index 0caf437408150a1b11b691f4cf8619fcaed6e00f..ce402f6662b438ae386164257452eea91501aa4b 100644 (file)
--- a/src/vim.h
+++ b/src/vim.h
@@ -2780,6 +2780,12 @@ typedef int (*opt_expand_cb_T)(optexpand_T *args, int *numMatches, char_u ***mat
 # endif
 #endif
 
+#if defined(FEAT_PRINT_PANGO) && defined(FEAT_GUI_GTK) && defined(USE_GTK4)
+# if GTK_CHECK_VERSION(4, 14, 0)
+#  define USE_GTK4_PRINT_DIALOG
+# endif
+#endif
+
 #ifndef FEAT_NETBEANS_INTG
 # undef NBDEBUG
 #endif