Re: Commit: patch 9.2.0898: printing support is lacking

Hisashi T Fujinaka <[email protected]> Sun, 2 Aug 2026 17:35:51 -0700 (PDT)
Newsgroups gmane.editors.vim.devel
Message-ID <[email protected]>
I'm seeing:

gcc -c -I. -Iproto -DHAVE_CONFIG_H -DFEAT_GUI_GTK  -I/opt/sw/include/gtk-2.=
0 -I/opt/sw/lib/gtk-2.0/include -I/opt/sw/include/pango-1.0 -I/opt/sw/inclu=
de/atk-1.0 -I/opt/sw/include/cairo -I/opt/sw/include/glib-2.0 -I/opt/sw/lib=
/glib-2.0/include -I/opt/sw/include -I/opt/sw/include/freetype2 -I/opt/X11/=
include -MD -I/opt/sw/include -DMACOS_X  -g -O2 -D_REENTRANT -U_FORTIFY_SOU=
RCE -D_FORTIFY_SOURCE=3D1     -I/usr/X11/include     -I/opt/sw/include/cair=
o -I/opt/sw/include -I/opt/sw/include/freetype2 -I/opt/X11/include  -I/opt/=
sw/include/pango-1.0 -I/opt/sw/include/glib-2.0 -I/opt/sw/lib/glib-2.0/incl=
ude -I/opt/sw/include/cairo -I/opt/sw/include -I/opt/sw/include/freetype2 -=
I/opt/X11/include -o objects/hardcopy_pango.o hardcopy_pango.c
hardcopy_pango.c:383:7: error: call to undeclared function 'pango_font_metr=
ics_get_height'; ISO C99 and later do not support implicit function declara=
tions [-Wimplicit-function-declaration]
   383 |             (pango_font_metrics_get_height(metrics) - (ascent + de=
scent))
       |              ^

Do I need a newer pango?

On Sun, 2 Aug 2026, Christian Brabandt wrote:

> patch 9.2.0898: printing support is lacking
>
> Commit: https://github.com/vim/vim/commit/f141c19dca84331202ea06f3035c04f=
fc63066d0
> Author: Foxe Chen <[email protected]>
> Date:   Sun Aug 2 18:27:01 2026 +0000
>
>    patch 9.2.0898: printing support is lacking
>
>    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 <[email protected]>
>    Signed-off-by: Christian Brabandt <[email protected]>
>
> diff --git a/Filelist b/Filelist
> index 07853f4c6..7a2f1219b 100644
> --- a/Filelist
> +++ b/Filelist
> @@ -93,6 +93,11 @@ SRC_ALL =3D	\
> 		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 \
> diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
> index 647e2a622..a9d8f06f5 100644
> --- a/runtime/doc/options.txt
> +++ b/runtime/doc/options.txt
> @@ -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 descripti=
on 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
> diff --git a/runtime/doc/print.txt b/runtime/doc/print.txt
> index 2e8466482..392ad2687 100644
> --- a/runtime/doc/print.txt
> +++ b/runtime/doc/print.txt
> @@ -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|, a=
n 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 wit=
h
> |: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' opti=
on, except that only one
> font may be named, and the special "guifont=3D*" syntax is not available.
>
> In the Win32 GUI version this specifies a font name with its extra attrib=
utes,
> -as with the 'guifont' option.
> +as with the 'guifont' option.  When |+pango| is enabled, the format is t=
he
> +same as 'guifont' for the GTK GUI (e.g. "Courier New 10").
>
> For other systems, only ":h11" is recognized, where "11" is the point siz=
e 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 Post=
Script printing:
>   unknown characters.  Printing will fail if Vim cannot convert the multi=
byte
>   to the 8-bit encoding.
>
> +When using the Pango backend for printing, these limitations are solved.=
  See
> +|pango-printing| for more information.
> +
> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D
> 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.
>
> +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D
> +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 gene=
rated
> +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 termina=
l
> +builds of Vim, "--enable-hardcopy-pango" must be passed to the configure
> +options. The linked Cairo library must have the Postscript and PDF backe=
nds
> +enabled.
> +
> +							*gtk4-print*
> +When using the GTK4 GUI, |:hardcopy| will use a native graphical print d=
ialog,
> +similar to MS-Windows.  See |:hardcopy| for more information.
>
> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D
> -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 recog=
nize
> diff --git a/runtime/doc/tags b/runtime/doc/tags
> index 9f59289a8..e83116386 100644
> --- a/runtime/doc/tags
> +++ b/runtime/doc/tags
> @@ -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*
> diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt
> index 0a5ff259c..f0e9acaae 100644
> --- a/runtime/doc/various.txt
> +++ b/runtime/doc/various.txt
> @@ -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|
> diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt
> index 738e95bdb..4db123621 100644
> --- a/runtime/doc/version9.txt
> +++ b/runtime/doc/version9.txt
> @@ -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=3Dgtk4` 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, se=
e
> +  |pango-printing|.
>
> Popups ~
> ------
> diff --git a/src/Makefile b/src/Makefile
> index a3878d0c8..a8a046da7 100644
> --- a/src/Makefile
> +++ b/src/Makefile
> @@ -1417,6 +1417,8 @@ ALL_LIBS =3D \
> 	   $(X_PRE_LIBS) \
> 	   $(X_LIBS) \
> 	   $(WAYLAND_LIBS) \
> +	   $(CAIRO_LIBS) \
> +	   $(PANGO_LIBS) \
> 	   $(X_EXTRA_LIBS) \
> 	   $(MZSCHEME_LIBS) \
> 	   $(LIBS) \
> @@ -1543,6 +1545,8 @@ BASIC_SRC =3D \
> 	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 =3D \
> 	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_CPPFLA=
GS) -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_posts=
cript.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 \
> diff --git a/src/auto/configure b/src/auto/configure
> index 8fd4ba3b7..a47cb58d2 100755
> --- a/src/auto/configure
> +++ b/src/auto/configure
> @@ -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 progra=
m
>   --disable-icon-cache-update        update disabled
>   --disable-desktop-database-update  update disabled
> +  --enable-hardcopy-pango     Use Pango and Cairo for improved hardcopy =
printing. default=3Dyes 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
> " "no" >&6; }
> fi
>
> +{ printf "%s
> " "$as_me:${as_lineno-$LINENO}: checking for cairo" >&5
> +printf %s "checking for cairo... " >&6; }
> +have_cairo=3D"no"
> +if "$PKG_CONFIG" --exists 'cairo-ps'; then
> +  if "$PKG_CONFIG" --exists 'cairo-pdf'; then
> +    have_cairo=3D"yes"
> +    { printf "%s
> " "$as_me:${as_lineno-$LINENO}: result: yes" >&5
> +printf "%s
> " "yes" >&6; }
> +
> +    CAIRO_CFLAGS=3D`$PKG_CONFIG --cflags-only-other cairo-ps cairo-pdf`
> +    CAIRO_CPPFLAGS=3D`$PKG_CONFIG --cflags-only-I cairo-ps cairo-pdf`
> +    CAIRO_LIBS=3D`$PKG_CONFIG --libs cairo-ps cairo-pdf`
> +  else
> +    { printf "%s
> " "$as_me:${as_lineno-$LINENO}: result: no" >&5
> +printf "%s
> " "no" >&6; }
> +  fi
> +else
> +  { printf "%s
> " "$as_me:${as_lineno-$LINENO}: result: no" >&5
> +printf "%s
> " "no" >&6; }
> +fi
> +
> +{ printf "%s
> " "$as_me:${as_lineno-$LINENO}: checking for pango" >&5
> +printf %s "checking for pango... " >&6; }
> +have_pango=3D"no"
> +if "$PKG_CONFIG" --exists 'pango' && "$PKG_CONFIG" --exists 'pangocairo'=
; then
> +  have_pango=3D"yes"
> +  { printf "%s
> " "$as_me:${as_lineno-$LINENO}: result: yes" >&5
> +printf "%s
> " "yes" >&6; }
> +
> +  PANGO_CFLAGS=3D`$PKG_CONFIG --cflags-only-other pango pangocairo`
> +  PANGO_CPPFLAGS=3D`$PKG_CONFIG --cflags-only-I pango pangocairo`
> +  PANGO_LIBS=3D`$PKG_CONFIG --libs pango pangocairo`
> +else
> +  { printf "%s
> " "$as_me:${as_lineno-$LINENO}: result: no" >&5
> +printf "%s
> " "no" >&6; }
> +fi
> +
> if test "x$enable_gui" =3D "xgtk4"; then
>   with_x=3Dno
> fi
> @@ -11494,6 +11539,58 @@ fi
>
>
>
> +{ printf "%s
> " "$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=3D$enable_hardcopy_pango;
> +fi
> +
> +
> +if test "x$have_pango" =3D "xno" || test "x$have_cairo" =3D "xno"; then
> +  { printf "%s
> " "$as_me:${as_lineno-$LINENO}: result: no, Pango or Cairo not found" >&5
> +printf "%s
> " "no, Pango or Cairo not found" >&6; }
> +else
> +  if test "x$GUITYPE" =3D "xGTK" || test "x$GUITYPE" =3D "xGTK4"; then
> +    enable_hardcopy_pango=3D"yes"
> +    { printf "%s
> " "$as_me:${as_lineno-$LINENO}: result: yes, GTK GUI enabled" >&5
> +printf "%s
> " "yes, GTK GUI enabled" >&6; }
> +  else
> +    if test "$enable_hardcopy_pango" =3D "yes"; then
> +      { printf "%s
> " "$as_me:${as_lineno-$LINENO}: result: yes" >&5
> +printf "%s
> " "yes" >&6; }
> +    else
> +      { printf "%s
> " "$as_me:${as_lineno-$LINENO}: result: no" >&5
> +printf "%s
> " "no" >&6; }
> +    fi
> +  fi
> +fi
> +
> +if test "$enable_hardcopy_pango" =3D "yes"; then
> +  cppflags_save=3D$CPPFLAGS
> +  cflags_save=3D$CFLAGS
> +
> +  printf "%s
> " "#define FEAT_PRINT_PANGO 1" >>confdefs.h
> +
> +
> +  CPPFLAGS=3D"$CPPFLAGS $PANGO_CPPFLAGS"
> +  CFLAGS=3D"$CFLAGS $PANGO_CFLAGS"
> +
> +
> +
> +
> +  CPPFLAGS=3D"$CPPFLAGS $CAIRO_CPPFLAGS"
> +  CFLAGS=3D"$CFLAGS $CAIRO_CFLAGS"
> +
> +
> +
> +
> +  CPPFLAGS=3D$cppflags_save
> +  CFLAGS=3D$cflags_save
> +fi
> +
> +
>
> if test -z "$SKIP_MOTIF"; then
>   gui_XXX=3D"/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/lo=
cal/Motif*/XXX /usr/local/XXX/Motif* /usr/local/XXX /usr/local/X11*/XXX /us=
r/local/LessTif/Motif*/XXX $MOTIFHOME/XXX"
> diff --git a/src/config.h.in b/src/config.h.in
> index d26bd2078..2dc3b2dec 100644
> --- a/src/config.h.in
> +++ b/src/config.h.in
> @@ -499,6 +499,9 @@
> /* 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
>
> diff --git a/src/config.mk.in b/src/config.mk.in
> index a46a140a7..06a77aed6 100644
> --- a/src/config.mk.in
> +++ b/src/config.mk.in
> @@ -43,6 +43,14 @@ WAYLAND_OBJ	        =3D @WAYLAND_OBJ@
> WAYLAND_CFLAGS	    =3D @WAYLAND_CFLAGS@
> WAYLAND_CPPFLAGS    =3D @WAYLAND_CPPFLAGS@
>
> +CAIRO_LIBS  	=3D @CAIRO_LIBS@
> +CAIRO_CFLAGS	=3D @CAIRO_CFLAGS@
> +CAIRO_CPPFLAGS  =3D @CAIRO_CPPFLAGS@
> +
> +PANGO_LIBS  	=3D @PANGO_LIBS@
> +PANGO_CFLAGS	=3D @PANGO_CFLAGS@
> +PANGO_CPPFLAGS  =3D @PANGO_CPPFLAGS@
> +
> XDIFF_OBJS_USED	=3D @XDIFF_OBJS_USED@
>
> LUA_LIBS	=3D @LUA_LIBS@
> diff --git a/src/configure.ac b/src/configure.ac
> index 654959874..2fdc51637 100644
> --- a/src/configure.ac
> +++ b/src/configure.ac
> @@ -2473,6 +2473,36 @@ else
>   AC_MSG_RESULT(no)
> fi
>
> +AC_MSG_CHECKING(for cairo)
> +have_cairo=3D"no"
> +if "$PKG_CONFIG" --exists 'cairo-ps'; then
> +  if "$PKG_CONFIG" --exists 'cairo-pdf'; then
> +    have_cairo=3D"yes"
> +    AC_MSG_RESULT([yes])
> +
> +    CAIRO_CFLAGS=3D`$PKG_CONFIG --cflags-only-other cairo-ps cairo-pdf`
> +    CAIRO_CPPFLAGS=3D`$PKG_CONFIG --cflags-only-I cairo-ps cairo-pdf`
> +    CAIRO_LIBS=3D`$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=3D"no"
> +if "$PKG_CONFIG" --exists 'pango' && "$PKG_CONFIG" --exists 'pangocairo'=
; then
> +  have_pango=3D"yes"
> +  AC_MSG_RESULT([yes])
> +
> +  PANGO_CFLAGS=3D`$PKG_CONFIG --cflags-only-other pango pangocairo`
> +  PANGO_CPPFLAGS=3D`$PKG_CONFIG --cflags-only-I pango pangocairo`
> +  PANGO_LIBS=3D`$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=3Dyes 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 hardcop=
y printing. [default=3Dyes when GTK GUI enabled]],
> +	,, enable_hardcopy_pango=3D"no")
> +
> +if test "x$have_pango" =3D "xno" || test "x$have_cairo" =3D "xno"; then
> +  AC_MSG_RESULT([no, Pango or Cairo not found])
> +else
> +  if test "x$GUITYPE" =3D "xGTK" || test "x$GUITYPE" =3D "xGTK4"; then
> +    enable_hardcopy_pango=3D"yes"
> +    AC_MSG_RESULT([yes, GTK GUI enabled])
> +  else
> +    if test "$enable_hardcopy_pango" =3D "yes"; then
> +      AC_MSG_RESULT([yes])
> +    else
> +      AC_MSG_RESULT([no])
> +    fi
> +  fi
> +fi
> +
> +if test "$enable_hardcopy_pango" =3D "yes"; then
> +  cppflags_save=3D$CPPFLAGS
> +  cflags_save=3D$CFLAGS
> +
> +  AC_DEFINE(FEAT_PRINT_PANGO)
> +
> +  CPPFLAGS=3D"$CPPFLAGS $PANGO_CPPFLAGS"
> +  CFLAGS=3D"$CFLAGS $PANGO_CFLAGS"
> +  AC_SUBST(PANGO_LIBS)
> +  AC_SUBST(PANGO_CFLAGS)
> +  AC_SUBST(PANGO_CPPFLAGS)
> +
> +  CPPFLAGS=3D"$CPPFLAGS $CAIRO_CPPFLAGS"
> +  CFLAGS=3D"$CFLAGS $CAIRO_CFLAGS"
> +  AC_SUBST(CAIRO_LIBS)
> +  AC_SUBST(CAIRO_CFLAGS)
> +  AC_SUBST(CAIRO_CPPFLAGS)
> +
> +  CPPFLAGS=3D$cppflags_save
> +  CFLAGS=3D$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.
> diff --git a/src/errors.h b/src/errors.h
> index 143e7d7e6..350ffe704 100644
> --- a/src/errors.h
> +++ b/src/errors.h
> @@ -569,7 +569,7 @@ EXTERN char e_font_str_is_not_fixed_width[]
> 	INIT(=3D 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(=3D 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_en=
d[]
> 	INIT(=3D "E322: Line number out of range: %ld past the end");
> EXTERN char e_line_count_wrong_in_block_nr[]
> 	INIT(=3D "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(=3D N_("E324: Can't open PostScript output file"));
> #endif
> @@ -915,7 +915,7 @@ EXTERN char e_pattern_uses_more_memory_than_maxmempat=
tern[]
> EXTERN char e_library_call_failed_for_str[]
> 	INIT(=3D 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(=3D 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(=3D 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(=3D N_("E613: Unknown printer font: %s"));
> #endif
> diff --git a/src/evalfunc.c b/src/evalfunc.c
> index bad28de7c..74ca2716e 100644
> --- a/src/evalfunc.c
> +++ b/src/evalfunc.c
> @@ -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",
> diff --git a/src/evalvars.c b/src/evalvars.c
> index 854cb1fc0..c08a9d6e0 100644
> --- a/src/evalvars.c
> +++ b/src/evalvars.c
> @@ -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)
> {
> diff --git a/src/feature.h b/src/feature.h
> index 5d5512615..ee7475276 100644
> --- a/src/feature.h
> +++ b/src/feature.h
> @@ -291,17 +291,13 @@
> 	&& !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
>
> /*
> diff --git a/src/globals.h b/src/globals.h
> index 8e0a37494..305725c3e 100644
> --- a/src/globals.h
> +++ b/src/globals.h
> @@ -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_OP=
TIONS]
> 	{"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
>     ;
> diff --git a/src/gui_gtk4.c b/src/gui_gtk4.c
> index f5fb4487d..40fa89d89 100644
> --- a/src/gui_gtk4.c
> +++ b/src/gui_gtk4.c
> @@ -5043,322 +5043,188 @@ ex_helpfind(exarg_T *eap UNUSED)
>     do_cmdline_cmd((char_u *)"emenu ToolBar.FindHelp");
> }
>
> -
> /*
>  * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
> - * Printing with GtkPrintOperation
> + * Native print dialog
>  * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
>  */
> -#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 =3D NULL;
> +static GtkPrintSettings *print_settings =3D NULL;
> +static GtkPageSetup *page_setup =3D NULL;
> +static GtkPrintSetup *print_setup =3D 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 =3D (print_data_T *)user_data;
> -    PangoLayout	    *layout;
> -    PangoFontMetrics *metrics;
> -    double	    page_height;
> -    int		    total_lines;
> -
> -    page_height =3D gtk_print_context_get_height(context);
> -
> -    // Create a PangoLayout to measure font metrics on the print surface=
.
> -    layout =3D gtk_print_context_create_pango_layout(context);
> -    pango_layout_set_font_description(layout, pd->font_desc);
> -
> -    metrics =3D pango_context_get_metrics(
> -	    pango_layout_get_context(layout),
> -	    pd->font_desc, NULL);
> -
> -    pd->line_height =3D (double)(pango_font_metrics_get_ascent(metrics)
> -	    + pango_font_metrics_get_descent(metrics)) / PANGO_SCALE;
> -    pd->char_width =3D (double)pango_font_metrics_get_approximate_char_w=
idth(
> -	    metrics) / PANGO_SCALE;
> -
> -    pango_font_metrics_unref(metrics);
> -    g_object_unref(layout);
> -
> -    if (pd->line_height <=3D 0)
> -	pd->line_height =3D 12.0;
> +    GError *error =3D NULL;
> +    print_setup =3D gtk_print_dialog_setup_finish(dialog, result, &error=
);
>
> -    pd->lines_per_page =3D (int)(page_height / pd->line_height);
> -    if (pd->lines_per_page <=3D 0)
> -	pd->lines_per_page =3D 1;
> -
> -    total_lines =3D (int)(pd->last_line - pd->first_line + 1);
> -    pd->n_pages =3D (total_lines + pd->lines_per_page - 1) / pd->lines_p=
er_page;
> -    if (pd->n_pages <=3D 0)
> -	pd->n_pages =3D 1;
> -
> -    gtk_print_operation_set_n_pages(op, pd->n_pages);
> +    if (print_setup =3D=3D NULL)
> +    {
> +	semsg(_(e_print_error_str), error->message);
> +	g_error_free(error);
> +    }
> +    *done =3D 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 fai=
lure
> + * (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 =3D (print_data_T *)user_data;
> -    cairo_t	    *cr;
> -    linenr_T	    lnum;
> -    linenr_T	    first;
> -    linenr_T	    last;
> -    double	    y;
> -
> -    cr =3D gtk_print_context_get_cairo_context(context);
> +    GtkPrintDialog  *dialog =3D gtk_print_dialog_new();
> +    gboolean	    done =3D FALSE;
> +    char_u	    *temp;
>
> -    first =3D pd->first_line + (linenr_T)page_nr * pd->lines_per_page;
> -    last =3D first + pd->lines_per_page - 1;
> -    if (last > pd->last_line)
> -	last =3D pd->last_line;
> +    gtk_print_dialog_set_title(dialog, (const char *)jobname);
> +    gtk_print_dialog_set_modal(dialog, TRUE);
>
> -    y =3D 0;
> +    print_dialog =3D NULL;
> +    print_setup =3D NULL;
>
> -    for (lnum =3D first; lnum <=3D last; ++lnum)
> +    if (print_settings =3D=3D NULL)
>     {
> -	char_u		*line;
> -	PangoLayout	*layout;
> -	PangoAttrList	*attr_list;
> +	option_table_T *opt;
>
> -	line =3D ml_get(lnum);
> -	layout =3D gtk_print_context_create_pango_layout(context);
> -	pango_layout_set_font_description(layout, pd->font_desc);
> +	print_settings =3D gtk_print_settings_new();
>
> -	attr_list =3D pango_attr_list_new();
> -
> -# ifdef FEAT_SYN_HL
> -	if (pd->do_syntax && syntax_present(curwin))
> +	opt =3D &printer_opts[OPT_PRINT_DUPLEX];
> +	if (opt->present)
> 	{
> -	    colnr_T	col;
> -	    int		prev_syn_id =3D -1;
> -	    int		attr_start =3D 0;
> -	    long_u	prev_fg =3D 0;
> -	    int		prev_bold =3D FALSE;
> -	    int		prev_italic =3D FALSE;
> -	    int		len =3D (int)STRLEN(line);
> -
> -	    for (col =3D 0; col < len; )
> -	    {
> -		int	    id;
> -		int	    outputlen;
> -		long_u	    fg_color;
> -		int	    is_bold;
> -		int	    is_italic;
> -
> -		if (has_mbyte)
> -		{
> -		    outputlen =3D (*mb_ptr2len)(line + col);
> -		    if (outputlen < 1)
> -			outputlen =3D 1;
> -		}
> -		else
> -		    outputlen =3D 1;
> -
> -		id =3D syn_get_id(curwin, lnum, col, 1, NULL, FALSE);
> -		if (id > 0)
> -		    id =3D syn_get_final_id(id);
> -		else
> -		    id =3D 0;
> -		// syn_get_id may invalidate the line pointer.
> -		line =3D ml_get(lnum);
> -
> -		fg_color =3D highlight_gui_color_rgb(id, TRUE);
> -		is_bold =3D (highlight_has_attr(id, HL_BOLD, 'g') !=3D NULL);
> -		is_italic =3D (highlight_has_attr(id, HL_ITALIC, 'g') !=3D NULL);
> -
> -		// When attributes change, flush the previous run.
> -		if (id !=3D prev_syn_id && col > 0)
> -		{
> -		    if (prev_fg !=3D 0 && prev_fg !=3D (long_u)0xffffffL)
> -		    {
> -			PangoAttribute *a =3D pango_attr_foreground_new(
> -				(guint16)(((prev_fg >> 16) & 0xff) * 257),
> -				(guint16)(((prev_fg >> 8) & 0xff) * 257),
> -				(guint16)((prev_fg & 0xff) * 257));
> -			a->start_index =3D attr_start;
> -			a->end_index =3D col;
> -			pango_attr_list_insert(attr_list, a);
> -		    }
> -		    if (prev_bold)
> -		    {
> -			PangoAttribute *a =3D pango_attr_weight_new(
> -				PANGO_WEIGHT_BOLD);
> -			a->start_index =3D attr_start;
> -			a->end_index =3D col;
> -			pango_attr_list_insert(attr_list, a);
> -		    }
> -		    if (prev_italic)
> -		    {
> -			PangoAttribute *a =3D pango_attr_style_new(
> -				PANGO_STYLE_ITALIC);
> -			a->start_index =3D attr_start;
> -			a->end_index =3D col;
> -			pango_attr_list_insert(attr_list, a);
> -		    }
> -		    attr_start =3D col;
> -		}
> -
> -		prev_syn_id =3D id;
> -		prev_fg =3D fg_color;
> -		prev_bold =3D is_bold;
> -		prev_italic =3D is_italic;
> -
> -		col +=3D outputlen;
> -	    }
> +	    if (STRNICMP(opt->string, "off", 3) =3D=3D 0)
> +		gtk_print_settings_set_duplex(print_settings,
> +			GTK_PRINT_DUPLEX_SIMPLEX);
> +	    else if (STRNICMP(opt->string, "short", 5) =3D=3D 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 !=3D 0 && prev_fg !=3D (long_u)0xffffffL)
> -		{
> -		    PangoAttribute *a =3D pango_attr_foreground_new(
> -			    (guint16)(((prev_fg >> 16) & 0xff) * 257),
> -			    (guint16)(((prev_fg >> 8) & 0xff) * 257),
> -			    (guint16)((prev_fg & 0xff) * 257));
> -		    a->start_index =3D attr_start;
> -		    a->end_index =3D len;
> -		    pango_attr_list_insert(attr_list, a);
> -		}
> -		if (prev_bold)
> -		{
> -		    PangoAttribute *a =3D pango_attr_weight_new(
> -			    PANGO_WEIGHT_BOLD);
> -		    a->start_index =3D attr_start;
> -		    a->end_index =3D len;
> -		    pango_attr_list_insert(attr_list, a);
> -		}
> -		if (prev_italic)
> -		{
> -		    PangoAttribute *a =3D pango_attr_style_new(
> -			    PANGO_STYLE_ITALIC);
> -		    a->start_index =3D attr_start;
> -		    a->end_index =3D len;
> -		    pango_attr_list_insert(attr_list, a);
> -		}
> -	    }
> +	opt =3D &printer_opts[OPT_PRINT_PORTRAIT];
> +	if (opt->present)
> +	{
> +	    if (*opt->string =3D=3D '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 =3D &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 =3D &printer_opts[OPT_PRINT_COLLATE];
> +	if (opt->present)
> 	{
> -	    PangoTabArray *tabs;
> -	    int tab_width =3D (int)(curbuf->b_p_ts * pd->char_width);
> -
> -	    if (tab_width <=3D 0)
> -		tab_width =3D (int)(8 * pd->char_width);
> -	    tabs =3D 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 =3D=3D '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 =3D=3D NULL)
> +	page_setup =3D 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 +=3D pd->line_height;
> +    while (!done)
> +	g_main_context_iteration(NULL, TRUE);
> +
> +    if (print_setup =3D=3D NULL)
> +    {
> +	g_object_unref(dialog);
> +	g_clear_pointer(&print_setup, gtk_print_setup_unref);
> +
> +	psettings->user_abort_msg =3D 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 =3D g_object_ref(
> +	    gtk_print_setup_get_print_settings(print_setup));
> +    page_setup =3D g_object_ref(gtk_print_setup_get_page_setup(print_set=
up));
>
> -    static GtkPrintSettings *settings =3D NULL;
> +    {
> +	GtkPrintDuplex duplex =3D gtk_print_settings_get_duplex(print_settings)=
;
> +	psettings->duplex =3D duplex !=3D GTK_PRINT_DUPLEX_SIMPLEX;
> +    }
>
> -    CLEAR_FIELD(pd);
> -    pd.first_line =3D eap->line1;
> -    pd.last_line =3D eap->line2;
> +    psettings->has_color =3D gtk_print_settings_get_use_color(print_sett=
ings);
>
> -    // Use 'printfont' if set, otherwise fall back to 'guifont'.
> -    font_name =3D *p_pfn !=3D NUL ? p_pfn : p_guifont;
> -    if (font_name =3D=3D NULL || *font_name =3D=3D NUL)
> -	font_name =3D (char_u *)"Monospace 10";
> +    *page_width =3D gtk_page_setup_get_paper_width(page_setup,
> +	    GTK_UNIT_POINTS);
> +    *page_height =3D gtk_page_setup_get_paper_height(page_setup,
> +	    GTK_UNIT_POINTS);
>
> -    pd.font_desc =3D pango_font_description_from_string((const char *)fo=
nt_name);
> -    if (pd.font_desc =3D=3D NULL)
> +    temp  =3D vim_tempname('p', FALSE);
> +    if (temp =3D=3D 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) =3D=3D 0)
> -	pango_font_description_set_size(pd.font_desc, 10 * PANGO_SCALE);
> +	psettings->user_abort_msg =3D FALSE;
> +	return NULL;
> +    }
> +    print_file =3D g_file_new_for_path((char *)temp);
>
> -# ifdef FEAT_SYN_HL
> -    pd.do_syntax =3D syntax_present(curwin);
> -# endif
> +    print_dialog =3D dialog;
>
> -    op =3D gtk_print_operation_new();
> +    return temp;
> +}
>
> -    if (settings !=3D NULL)
> -	gtk_print_operation_set_print_settings(op, settings);
> +    static void
> +print_file_cb(
> +	GtkPrintDialog	*dialog,
> +	GAsyncResult	*result,
> +	GFile		*file)
> +{
> +    char *path =3D g_file_get_path(file);
>
> -    gtk_print_operation_set_job_name(op,
> -	    curbuf->b_fname !=3D 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), &p=
d);
> +    // Don't use g_file_delete() to be consistent
> +    mch_remove(path);
> +    g_free(path);
> +}
>
> -    res =3D gtk_print_operation_run(op,
> -	    GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
> -	    GTK_WINDOW(gui.mainwin), NULL);
>
> -    if (res =3D=3D GTK_PRINT_OPERATION_RESULT_APPLY)
> +    void
> +gui_gtk4_print_finish(void)
> +{
> +    if (print_dialog !=3D NULL)
>     {
> -	if (settings !=3D NULL)
> -	    g_object_unref(settings);
> -	settings =3D 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 =3D 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
> diff --git a/src/hardcopy.c b/src/hardcopy.c
> index 10c3beb8f..d7f229895 100644
> --- a/src/hardcopy.c
> +++ b/src/hardcopy.c
> @@ -15,6 +15,15 @@
> #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] =3D
> 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] =3D
> -{
> -    {"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] =3D {
> +    {"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, pr=
t_pos_T *ppos);
> @@ -334,34 +339,34 @@ prt_get_attr(
>     static void
> prt_set_fg(long_u fg)
> {
> -    if (fg =3D=3D curr_fg)
> +    if (fg =3D=3D prt_curr_fg)
> 	return;
>
> -    curr_fg =3D fg;
> +    prt_curr_fg =3D fg;
>     mch_print_set_fg(fg);
> }
>
>     static void
> prt_set_bg(long_u bg)
> {
> -    if (bg =3D=3D curr_bg)
> +    if (bg =3D=3D prt_curr_bg)
> 	return;
>
> -    curr_bg =3D bg;
> +    prt_curr_bg =3D bg;
>     mch_print_set_bg(bg);
> }
>
>     static void
> prt_set_font(int bold, int italic, int underline)
> {
> -    if (curr_bold =3D=3D bold
> -	    && curr_italic =3D=3D italic
> -	    && curr_underline =3D=3D underline)
> +    if (prt_curr_bold =3D=3D bold
> +	    && prt_curr_italic =3D=3D italic
> +	    && prt_curr_underline =3D=3D underline)
> 	return;
>
> -    curr_underline =3D underline;
> -    curr_italic =3D italic;
> -    curr_bold =3D bold;
> +    prt_curr_underline =3D underline;
> +    prt_curr_italic =3D italic;
> +    prt_curr_bold =3D 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 =3D 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 !=3D '>')
> -    {
> -	gui_gtk4_hardcopy(eap);
> -	return;
> -    }
> +#ifdef FEAT_PRINT_PANGO
> +    settings.user_abort_msg =3D TRUE;
> #endif
>
> -#ifdef FEAT_POSTSCRIPT
> +#if defined(FEAT_POSTSCRIPT) || defined(FEAT_PRINT_PANGO)
>     if (*eap->arg =3D=3D '>')
>     {
> 	char	*errormsg =3D NULL;
> @@ -659,11 +657,11 @@ ex_hardcopy(exarg_T *eap)
>     }
>
>     // Set colors and font to normal.
> -    curr_bg =3D (long_u)0xffffffffL;
> -    curr_fg =3D (long_u)0xffffffffL;
> -    curr_italic =3D MAYBE;
> -    curr_bold =3D MAYBE;
> -    curr_underline =3D MAYBE;
> +    prt_curr_bg =3D (long_u)0xffffffffL;
> +    prt_curr_fg =3D (long_u)0xffffffffL;
> +    prt_curr_italic =3D MAYBE;
> +    prt_curr_bold =3D MAYBE;
> +    prt_curr_underline =3D 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 =3D 0;
> +    prt_page_count =3D 0;
>     for (collated_copies =3D 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 =3D 0; prtpos.file_line <=3D eap->line2; ++page_count)
> +	for (prt_page_count =3D 0; prtpos.file_line <=3D 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 =3D 0; page_line < settings.lines_per_page;
> @@ -796,7 +794,7 @@ ex_hardcopy(exarg_T *eap)
> 		}
> 	    }
> 	    if (settings.duplex && prtpos.file_line <=3D eap->line2)
> -		++page_count;
> +		++prt_page_count;
>
> 	    // Remember the position where the next page starts.
> 	    page_prtpos =3D 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[] =3D
> -{
> -    {"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 =3D
> -{
> -    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 =3D
> -{
> -    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 f=
or
> -// 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[] =3D
> -{
> -    {"iso-2022-jp", NULL,       (CS_JIS_C_1978|CS_JIS_X_1983|CS_JIS_X_19=
90|
> -								    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[] =3D
> -{
> -    {"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[] =3D
> -{
> -    {"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[] =3D
> -{
> -    {"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[] =3D
> -{
> -    {"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[] =3D
> -{
> -    {"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[] =3D
> -{
> -    {"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[] =3D
> -{
> -    {"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[] =3D
> -{
> -    {
> -	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 i=
s
> -// 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[] =3D
> -{
> -    "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 n=
eeds 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[] =3D
> -{
> -    {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 =3D NULL;
> -
> -/*
> - * Various offsets and dimensions in default PostScript user space (poin=
ts).
> - * 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 =3D 0.0;
> -static float prt_pos_y_moveto =3D 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[] =3D "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)
> -							     !=3D (size_t)bytes)
> -    {
> -	emsg(_(e_error_writing_to_postscript_output_file));
> -	prt_file_error =3D 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
> ", new_name, encoding, font);
> -    prt_write_file(prt_line_buffer);
> -    if (prt_out_mbyte)
> -	sprintf((char *)prt_line_buffer, "/%s %d %f /_%s sffs
> ",
> -		       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
> ", 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
> ", 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
> ", 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
> ", 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 fractio=
nal 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 =3D (int)real;
> -    fraction =3D (float)(real - integer);
> -    if (real < (double)integer)
> -	fraction =3D -fraction;
> -    for (i =3D 0; i < precision; i++)
> -	fraction *=3D 10.0;
> -
> -    *pinteger =3D integer;
> -    *pfraction =3D (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 de=
cide
> - * 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 !=3D 0)
> -    {
> -	// Remove any trailing zeros
> -	while ((fraction % 10) =3D=3D 0)
> -	{
> -	    prec--;
> -	    fraction /=3D 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
> ");
> -    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 <=3D 0)
> -	return;
> -
> -    // Any background color must be drawn first
> -    if (prt_do_bgcol && (prt_new_bgcol !=3D 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
> ");
> -	    prt_do_moveto =3D 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 =3D ((unsigned)prt_new_bgcol & 0xff0000) >> 16;
> -	g =3D ((unsigned)prt_new_bgcol & 0xff00) >> 8;
> -	b =3D 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
> ");
> -    }
> -    // 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
> ");
> -	    prt_do_moveto =3D FALSE;
> -	}
> -
> -	// Underline length of text run
> -	prt_write_real(prt_text_run, 2);
> -	prt_write_string("ul
> ");
> -    }
> -    // 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
> ");
> -	prt_do_moveto =3D FALSE;
> -    }
> -    else // Simple show
> -	prt_write_string("s
> ");
> -
> -    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 =3D cookie;
> -
> -    if (STRLEN(filename) >=3D MAXPATHL)
> -	*resource_filename =3D 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 =3D alloc(MAXPATHL + 1);
> -    if (buffer =3D=3D 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] =3D NUL;
> -    retval =3D (do_in_runtimepath(buffer, 0, prt_resource_name,
> -							   resource->filename)
> -	    && resource->filename[0] !=3D 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 h=
ave 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 =3D prt_resfile.line_end + 1;
> -    while (idx < prt_resfile.len)
> -    {
> -	if (prt_resfile.buffer[idx] !=3D PSLF && prt_resfile.buffer[idx] !=3D P=
SCR)
> -	    break;
> -	idx++;
> -    }
> -    prt_resfile.line_start =3D idx;
> -
> -    while (idx < prt_resfile.len)
> -    {
> -	if (prt_resfile.buffer[idx] =3D=3D PSLF || prt_resfile.buffer[idx] =3D=
=3D PSCR)
> -	    break;
> -	idx++;
> -    }
> -    prt_resfile.line_end =3D 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 =3D 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 =3D 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.  Retu=
rns 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) !=3D 0)
> -	return FALSE;
> -
> -    // Find type of DSC comment
> -    for (comment =3D 0; comment < (int)ARRAY_LENGTH(prt_dsc_table); comm=
ent++)
> -	if (prt_resfile_strncmp(0, prt_dsc_table[comment].string,
> -					    prt_dsc_table[comment].len) =3D=3D 0)
> -	    break;
> -
> -    if (comment !=3D ARRAY_LENGTH(prt_dsc_table))
> -    {
> -	// Return type of comment
> -	p_dsc_line->type =3D prt_dsc_table[comment].type;
> -	offset =3D prt_dsc_table[comment].len;
> -    }
> -    else
> -    {
> -	// Unrecognised DSC comment, skip to ws after comment leader
> -	p_dsc_line->type =3D PRT_DSC_MISC_TYPE;
> -	offset =3D prt_resfile_skip_nonws(0);
> -	if (offset =3D=3D -1)
> -	    return FALSE;
> -    }
> -
> -    // Skip ws to comment value
> -    offset =3D prt_resfile_skip_ws(offset);
> -    if (offset =3D=3D -1)
> -	return FALSE;
> -
> -    p_dsc_line->string =3D &prt_resfile.buffer[prt_resfile.line_start + =
offset];
> -    p_dsc_line->len =3D prt_resfile.line_end - (prt_resfile.line_start +=
 offset);
> -
> -    return TRUE;
> -}
> -
> -/*
> - * Improved hand crafted parser to get the type, title, and version numb=
er of a
> - * PS resource file so the file details can be added to the DSC header c=
omments.
> - */
> -    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 =3D mch_fopen((char *)resource->filename, READBIN);
> -    if (fd_resource =3D=3D 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 =3D (int)fread((char *)prt_resfile.buffer, sizeof(ch=
ar_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 =3D -1;
> -    prt_resfile.line_start =3D 0;
> -    if (!prt_resfile_next_line())
> -	return FALSE;
> -
> -    offset =3D 0;
> -
> -    if (prt_resfile_strncmp(offset, PRT_RESOURCE_HEADER,
> -			       (int)STRLEN_LITERAL(PRT_RESOURCE_HEADER)) !=3D 0)
> -    {
> -	semsg(_(e_file_str_is_not_postscript_resource_file),
> -							   resource->filename);
> -	return FALSE;
> -    }
> -
> -    // Skip over any version numbers and following ws
> -    offset +=3D (int)STRLEN_LITERAL(PRT_RESOURCE_HEADER);
> -    offset =3D prt_resfile_skip_nonws(offset);
> -    if (offset =3D=3D -1)
> -	return FALSE;
> -    offset =3D prt_resfile_skip_ws(offset);
> -    if (offset =3D=3D -1)
> -	return FALSE;
> -
> -    if (prt_resfile_strncmp(offset, PRT_RESOURCE_RESOURCE,
> -			     (int)STRLEN_LITERAL(PRT_RESOURCE_RESOURCE)) !=3D 0)
> -    {
> -	semsg(_(e_file_str_is_not_supported_postscript_resource_file),
> -							   resource->filename);
> -	return FALSE;
> -    }
> -    offset +=3D (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)) =3D=3D 0)
> -	resource->type =3D PRT_RESOURCE_TYPE_PROCSET;
> -    else if (prt_resfile_strncmp(offset, PRT_RESOURCE_ENCODING,
> -			     (int)STRLEN_LITERAL(PRT_RESOURCE_ENCODING)) =3D=3D 0)
> -	resource->type =3D PRT_RESOURCE_TYPE_ENCODING;
> -    else if (prt_resfile_strncmp(offset, PRT_RESOURCE_CMAP,
> -				 (int)STRLEN_LITERAL(PRT_RESOURCE_CMAP)) =3D=3D 0)
> -	resource->type =3D 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] =3D '
> -    resource->version[0] =3D '
> -    seen_title =3D FALSE;
> -    seen_version =3D FALSE;
> -    seen_all =3D 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 =3D TRUE;
> -	    if (seen_version)
> -		seen_all =3D TRUE;
> -	    break;
> -
> -	case PRT_DSC_VERSION_TYPE:
> -	    vim_strncpy(resource->version, dsc_line.string, dsc_line.len);
> -	    seen_version =3D TRUE;
> -	    if (seen_title)
> -		seen_all =3D TRUE;
> -	    break;
> -
> -	case PRT_DSC_ENDCOMMENTS_TYPE:
> -	    // Won't find title or resource after this comment, stop searching
> -	    seen_all =3D 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 mat=
ter
> -    if (STRNCMP(resource->version, version, STRLEN(version)) !=3D 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
> ");
> -}
> -
> -    static void
> -prt_dsc_noarg(char *comment)
> -{
> -    vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
> -							 "%%%%%s
> ", 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
> ", 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)
> ", 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 =3D 0; i < count; i++)
> -    {
> -	sprintf((char *)prt_line_buffer, " %d", ints[i]);
> -	prt_write_file(prt_line_buffer);
> -    }
> -
> -    prt_write_string("
> ");
> -}
> -
> -    static void
> -prt_dsc_resources(
> -    char	*comment,	// if NULL add to previous
> -    char	*type,
> -    char	*string)
> -{
> -    if (comment !=3D 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
> ", 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 =3D PRT_PS_FONT_BOLD ; i <=3D PRT_PS_FONT_BOLDOBLIQUE ; i++)
> -	if (ps_font->ps_fontname[i] !=3D 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("
> ");
> -}
> -
> -    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 =3D=3D NULL)
> -	prt_write_string("()");
> -    else
> -	prt_write_string(colour);
> -    prt_write_string(" ");
> -    if (type =3D=3D NULL)
> -	prt_write_string("()");
> -    else
> -	prt_write_string(type);
> -    prt_write_string("
> ");
> -}
> -
> -    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 tha=
n
> -	// one style).
> -	for (i =3D PRT_PS_FONT_ROMAN; i <=3D PRT_PS_FONT_BOLDOBLIQUE; i++)
> -	{
> -	    if (prt_ps_mb_font.ps_fontname[i] !=3D NULL)
> -		VIM_CLEAR(prt_ps_mb_font.ps_fontname[i]);
> -	}
> -    }
> -
> -    if (prt_do_conv)
> -    {
> -	convert_setup(&prt_conv, NULL, NULL);
> -	prt_do_conv =3D FALSE;
> -    }
> -    if (prt_ps_fd !=3D NULL)
> -    {
> -	fclose(prt_ps_fd);
> -	prt_ps_fd =3D NULL;
> -	prt_file_error =3D FALSE;
> -    }
> -    if (prt_ps_file_name !=3D 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 =3D prt_get_unit(idx);
> -    if (u =3D=3D PRT_UNIT_NONE)
> -    {
> -	u =3D PRT_UNIT_PERC;
> -	nr =3D def_number;
> -    }
> -    else
> -	nr =3D printer_opts[idx].number;
> -
> -    switch (u)
> -    {
> -	case PRT_UNIT_INCH:
> -	    ret =3D (float)(nr * PRT_PS_DEFAULT_DPI);
> -	    break;
> -	case PRT_UNIT_MM:
> -	    ret =3D (float)(nr * PRT_PS_DEFAULT_DPI) / (float)25.4;
> -	    break;
> -	case PRT_UNIT_POINT:
> -	    ret =3D (float)nr;
> -	    break;
> -	case PRT_UNIT_PERC:
> -	default:
> -	    ret =3D (float)(physsize * nr) / 100;
> -	    break;
> -    }
> -
> -    return ret;
> -}
> -
> -/*
> - * Calculate margins for given width and height from printoptions settin=
gs.
> - */
> -    static void
> -prt_page_margins(
> -    double	width,
> -    double	height,
> -    double	*left,
> -    double	*right,
> -    double	*top,
> -    double	*bottom)
> -{
> -    *left   =3D to_device_units(OPT_PRINT_LEFT, width, 10);
> -    *right  =3D width - to_device_units(OPT_PRINT_RIGHT, width, 5);
> -    *top    =3D height - to_device_units(OPT_PRINT_TOP, height, 5);
> -    *bottom =3D to_device_units(OPT_PRINT_BOT, height, 5);
> -}
> -
> -    static void
> -prt_font_metrics(int font_scale)
> -{
> -    prt_line_height =3D (float)font_scale;
> -    prt_char_width =3D (float)PRT_PS_FONT_TO_USER(font_scale, prt_ps_fon=
t->wx);
> -}
> -
> -
> -    static int
> -prt_get_cpl(void)
> -{
> -    if (prt_use_number())
> -    {
> -	prt_number_width =3D 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 /=3D 2;
> -	prt_left_margin +=3D prt_number_width;
> -    }
> -    else
> -	prt_number_width =3D 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 =3D alloc(name_len + 1);
> -    if (fontname =3D=3D NULL)
> -	return FALSE;
> -    vim_strncpy((char_u *)fontname, name, name_len);
> -    prt_ps_mb_font.ps_fontname[font] =3D 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, hand=
ling the
> -     * case where the font height can exceed the line height.
> -     */
> -    prt_bgcol_offset =3D (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 -=3D (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 =3D prt_line_height + prt_bgcol_offset;
> -
> -    // Calculate lpp
> -    lpp =3D (int)((prt_top_margin - prt_bottom_margin) / prt_line_height=
);
> -
> -    // Adjust top margin if there is a header
> -    prt_top_margin -=3D 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 =3D NULL;
> -    // Look for recognised encoding
> -    enc_len =3D (int)STRLEN(p_encoding);
> -    p_mbenc =3D p_cmap->encodings;
> -    for (mbenc =3D 0; mbenc < p_cmap->num_encodings; mbenc++)
> -    {
> -	if (STRNICMP(p_mbenc->encoding, p_encoding, enc_len) =3D=3D 0)
> -	{
> -	    *pp_mbenc =3D 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 gi=
ven
> -    if (*p_charset =3D=3D NUL)
> -	p_charset =3D p_cmap->defcs;
> -    char_len =3D (int)STRLEN(p_charset);
> -    p_mbchar =3D p_cmap->charsets;
> -    for (mbchar =3D 0; mbchar < p_cmap->num_charsets; mbchar++)
> -    {
> -	if (STRNICMP(p_mbchar->charset, p_charset, char_len) =3D=3D 0)
> -	{
> -	    *pp_mbchar =3D 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 =3D 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 =3D 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 |=3D PD_RETURNDEFAULT;
> -# endif
> -
> -    /*
> -     * Set up font and encoding.
> -     */
> -    p_encoding =3D enc_skip(p_penc);
> -    if (*p_encoding =3D=3D NUL)
> -	p_encoding =3D enc_skip(p_enc);
> -
> -    // Look for a multi-byte font that matches the encoding and characte=
r 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 =3D NULL;
> -    props =3D enc_canon_props(p_encoding);
> -    if (!(props & ENC_8BIT) && ((*p_pmcs !=3D NUL) || !(props & ENC_UNIC=
ODE)))
> -    {
> -	int cmap_first =3D 0;
> -
> -	p_mbenc_first =3D NULL;
> -	for (cmap =3D 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 =3D=3D NULL)
> -		{
> -		    p_mbenc_first =3D p_mbenc;
> -		    cmap_first =3D 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 =3D=3D NULL && p_mbenc_first !=3D NULL)
> -	{
> -	    p_mbenc =3D p_mbenc_first;
> -	    cmap =3D cmap_first;
> -	}
> -    }
> -
> -    prt_out_mbyte =3D (p_mbenc !=3D NULL);
> -    if (prt_out_mbyte)
> -    {
> -	// Build CMap name - will be same for all multi-byte fonts used
> -	prt_cmap[0] =3D NUL;
> -
> -	prt_custom_cmap =3D (p_mbchar =3D=3D NULL);
> -	if (!prt_custom_cmap)
> -	{
> -	    // Check encoding and character set are compatible
> -	    if ((p_mbenc->needs_charset & p_mbchar->has_charset) =3D=3D 0)
> -	    {
> -		emsg(_(e_incompatible_multi_byte_encoding_and_character_set));
> -		return FALSE;
> -	    }
> -
> -	    // Add charset name if not empty
> -	    if (p_mbchar->cmap_charset !=3D 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 =3D=3D 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 !=3D 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 =3D mbfont_opts[OPT_MBFONT_USECOURIER].present &&
> -	    (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_USECOURIER].string[0]) =3D=3D '=
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]) =3D=3D 'y'))
> -		prt_ascii_encoding =3D "ascii";
> -	    else
> -		prt_ascii_encoding =3D prt_ps_mbfonts[cmap].ascii_enc;
> -	}
> -
> -	prt_ps_font =3D &prt_ps_mb_font;
> -    }
> -    else
> -    {
> -	prt_use_courier =3D FALSE;
> -	prt_ps_font =3D &prt_ps_courier_font;
> -    }
> -
> -    /*
> -     * Find the size of the paper and set the margins.
> -     */
> -    prt_portrait =3D (!printer_opts[OPT_PRINT_PORTRAIT].present
> -	   || TOLOWER_ASC(printer_opts[OPT_PRINT_PORTRAIT].string[0]) =3D=3D 'y=
');
> -    if (printer_opts[OPT_PRINT_PAPER].present)
> -    {
> -	paper_name =3D (char *)printer_opts[OPT_PRINT_PAPER].string;
> -	paper_strlen =3D printer_opts[OPT_PRINT_PAPER].strlen;
> -    }
> -    else
> -    {
> -	paper_name =3D "A4";
> -	paper_strlen =3D 2;
> -    }
> -    for (i =3D 0; i < (int)PRT_MEDIASIZE_LEN; ++i)
> -	if (STRLEN(prt_mediasize[i].name) =3D=3D (unsigned)paper_strlen
> -		&& STRNICMP(prt_mediasize[i].name, paper_name,
> -							   paper_strlen) =3D=3D 0)
> -	    break;
> -    if (i =3D=3D PRT_MEDIASIZE_LEN)
> -	i =3D 0;
> -    prt_media =3D i;
> -
> -    /*
> -     * Set PS pagesize based on media dimensions and print orientation.
> -     * Note: Media and page sizes have defined meanings in PostScript an=
d should
> -     * be kept distinct.  Media is the paper (or transparency, or ...) t=
hat is
> -     * printed on, whereas the page size is the area that the PostScript
> -     * interpreter renders into.
> -     */
> -    if (prt_portrait)
> -    {
> -	prt_page_width =3D prt_mediasize[i].width;
> -	prt_page_height =3D prt_mediasize[i].height;
> -    }
> -    else
> -    {
> -	prt_page_width =3D prt_mediasize[i].height;
> -	prt_page_height =3D 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, &to=
p,
> -								    &bottom);
> -    prt_left_margin =3D (float)left;
> -    prt_right_margin =3D (float)right;
> -    prt_top_margin =3D (float)top;
> -    prt_bottom_margin =3D (float)bottom;
> -
> -    /*
> -     * Set up the font size.
> -     */
> -    fontsize =3D PRT_PS_DEFAULT_FONTSIZE;
> -    for (p =3D p_pfn; (p =3D vim_strchr(p, ':')) !=3D NULL; ++p)
> -	if (p[1] =3D=3D 'h' && VIM_ISDIGIT(p[2]))
> -	    fontsize =3D 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 =3D prt_get_cpl();
> -    psettings->lines_per_page =3D prt_get_lpp();
> -
> -    // Catch margin settings that leave no space for output!
> -    if (psettings->chars_per_line <=3D 0 || psettings->lines_per_page <=
=3D 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 co=
pies are
> -     * wanted cache it away and lie to the generic code that we only wan=
t one
> -     * uncollated copy.
> -     */
> -    psettings->n_collated_copies =3D 1;
> -    psettings->n_uncollated_copies =3D 1;
> -    prt_num_copies =3D 1;
> -    prt_collate =3D (!printer_opts[OPT_PRINT_COLLATE].present
> -	    || TOLOWER_ASC(printer_opts[OPT_PRINT_COLLATE].string[0]) =3D=3D 'y=
');
> -    if (prt_collate)
> -    {
> -	// TODO: Get number of collated copies wanted.
> -	psettings->n_collated_copies =3D 1;
> -    }
> -    else
> -    {
> -	// TODO: Get number of uncollated copies wanted and update the cached
> -	// count.
> -	prt_num_copies =3D 1;
> -    }
> -
> -    psettings->jobname =3D jobname;
> -
> -    /*
> -     * Set up printer duplex and tumble based on Duplex option setting -=
 default
> -     * is long sided duplex printing (i.e. no tumble).
> -     */
> -    prt_duplex =3D TRUE;
> -    prt_tumble =3D FALSE;
> -    psettings->duplex =3D 1;
> -    if (printer_opts[OPT_PRINT_DUPLEX].present)
> -    {
> -	if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "off", 3) =3D=3D 0)
> -	{
> -	    prt_duplex =3D FALSE;
> -	    psettings->duplex =3D 0;
> -	}
> -	else if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "short", 5)
> -									 =3D=3D 0)
> -	    prt_tumble =3D TRUE;
> -    }
> -
> -    // For now user abort not supported
> -    psettings->user_abort =3D 0;
> -
> -    // If the user didn't specify a file name, use a temp file.
> -    if (psettings->outfile =3D=3D NULL)
> -    {
> -	prt_ps_file_name =3D vim_tempname('p', TRUE);
> -	if (prt_ps_file_name =3D=3D NULL)
> -	{
> -	    emsg(_(e_cant_get_temp_file_name));
> -	    return FAIL;
> -	}
> -	prt_ps_fd =3D mch_fopen((char *)prt_ps_file_name, WRITEBIN);
> -    }
> -    else
> -    {
> -	p =3D expand_env_save(psettings->outfile);
> -	if (p !=3D NULL)
> -	{
> -	    prt_ps_fd =3D mch_fopen((char *)p, WRITEBIN);
> -	    vim_free(p);
> -	}
> -    }
> -    if (prt_ps_fd =3D=3D NULL)
> -    {
> -	emsg(_(e_cant_open_postscript_output_file));
> -	mch_print_cleanup();
> -	return FAIL;
> -    }
> -
> -    prt_bufsiz =3D psettings->chars_per_line;
> -    if (prt_out_mbyte)
> -	prt_bufsiz *=3D 2;
> -    ga_init2(&prt_ps_buffer, sizeof(char), prt_bufsiz);
> -
> -    prt_page_num =3D 0;
> -
> -    prt_attribute_change =3D FALSE;
> -    prt_need_moveto =3D FALSE;
> -    prt_need_font =3D FALSE;
> -    prt_need_fgcol =3D FALSE;
> -    prt_need_bgcol =3D FALSE;
> -    prt_need_underline =3D FALSE;
> -
> -    prt_file_error =3D 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 =3D mch_fopen((char *)resource->filename, READBIN);
> -    if (fd_resource =3D=3D 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 =3D 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 =3D=3D 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 =3D FALSE;
> -
> -    res_prolog =3D ALLOC_ONE(struct prt_ps_resource_S);
> -    res_encoding =3D ALLOC_ONE(struct prt_ps_resource_S);
> -    res_cidfont =3D ALLOC_ONE(struct prt_ps_resource_S);
> -    res_cmap =3D ALLOC_ONE(struct prt_ps_resource_S);
> -    if (res_prolog =3D=3D NULL || res_encoding =3D=3D NULL
> -	    || res_cidfont =3D=3D NULL || res_cmap =3D=3D 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" : "Landsca=
pe");
> -    prt_dsc_atend("Pages");
> -    prt_dsc_textline("PageOrder", "Ascend");
> -    // The bbox does not change with orientation - it is always in the d=
efault
> -    // 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] =3D (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] =3D (int)(top - (psettings->lines_per_page + prt_header_height(=
))
> -						    * (double)prt_line_height);
> -	bbox[2] =3D (int)(left + psettings->chars_per_line
> -					       * (double)prt_char_width + 0.5);
> -	bbox[3] =3D (int)(top + 0.5);
> -    }
> -    else
> -    {
> -	// In landscape printing the fixed point is the bottom left corner so w=
e
> -	// derive the bbox from that point.  We have lpp chars across the media
> -	// and cpl lines up the media.
> -	bbox[1] =3D (int)bottom;
> -	bbox[2] =3D (int)(left + ((psettings->lines_per_page
> -			      + prt_header_height()) * prt_line_height) + 0.5);
> -	bbox[3] =3D (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 printin=
g!
> -    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 'encodin=
g'. If
> -    // that cannot be found then default to "latin1".
> -    // Note: VIM specific encoding header is always skipped.
> -    if (!prt_out_mbyte)
> -    {
> -	p_encoding =3D enc_skip(p_penc);
> -	if (*p_encoding =3D=3D NUL
> -		|| !prt_find_resource((char *)p_encoding, res_encoding))
> -	{
> -	    // 'printencoding' not set or not supported - find alternate
> -	    int		props;
> -
> -	    p_encoding =3D enc_skip(p_enc);
> -	    props =3D 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 =3D (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 =3D enc_skip(p_penc);
> -	if (*p_encoding =3D=3D NUL)
> -	    p_encoding =3D 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 =3D CONV_NONE;
> -    if (!(enc_canon_props(p_enc) & enc_canon_props(p_encoding) & ENC_8BI=
T))
> -    {
> -	// Set up encoding conversion if required
> -	if (FAIL =3D=3D convert_setup(&prt_conv, p_enc, p_encoding))
> -	{
> -	    semsg(_(e_unable_to_convert_to_print_encoding_str), p_encoding);
> -	    goto theend;
> -	}
> -	prt_do_conv =3D TRUE;
> -    }
> -    prt_do_conv =3D prt_conv.vc_type !=3D 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
> ");
> -    prt_write_int(prt_num_copies);
> -    prt_write_string("nc
> ");
> -    prt_write_boolean(prt_duplex);
> -    prt_write_boolean(prt_tumble);
> -    prt_write_string("dt
> ");
> -    prt_write_boolean(prt_collate);
> -    prt_write_string("c
> ");
> -
> -    // 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 =3D (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] !=3D 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] !=3D 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] !=3D 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 =3D !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 *)" ");
> -
> -    if (!prt_file_error && psettings->outfile =3D=3D NULL
> -					&& !got_int && !psettings->user_abort)
> -    {
> -	// Close the file first.
> -	if (prt_ps_fd !=3D NULL)
> -	{
> -	    fclose(prt_ps_fd);
> -	    prt_ps_fd =3D 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) =3D=3D 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
> ");
> -
> -    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] =3D page_num[1] =3D prt_page_num;
> -    prt_dsc_ints("Page", 2, page_num);
> -
> -    prt_dsc_noarg("BeginPageSetup");
> -
> -    prt_write_string("sv
> 0 g
> ");
> -    prt_in_ascii =3D !prt_out_mbyte;
> -    if (prt_out_mbyte)
> -	prt_write_string("CF0 sf
> ");
> -    else
> -	prt_write_string("F0 sf
> ");
> -    prt_fgcol =3D PRCOLOR_BLACK;
> -    prt_bgcol =3D PRCOLOR_WHITE;
> -    prt_font =3D 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
> ");
> -    }
> -
> -    prt_dsc_noarg("EndPageSetup");
> -
> -    // We have reset the font attributes, force setting them again.
> -    curr_bg =3D (long_u)0xffffffff;
> -    curr_fg =3D (long_u)0xffffffff;
> -    curr_bold =3D 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 =3D 0;
> -static float prt_pos_y =3D 0;
> -
> -    void
> -mch_print_start_line(int margin, int page_line)
> -{
> -    prt_pos_x =3D prt_left_margin;
> -    if (margin)
> -	prt_pos_x -=3D prt_number_width;
> -
> -    prt_pos_y =3D prt_top_margin - prt_first_line_height -
> -					page_line * prt_line_height;
> -
> -    prt_attribute_change =3D TRUE;
> -    prt_need_moveto =3D TRUE;
> -    prt_half_width =3D FALSE;
> -}
> -
> -    int
> -mch_print_text_out(char_u *textp, int len UNUSED)
> -{
> -    char_u	*p =3D 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 =3D NULL;
> -
> -    char_width =3D 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 cha=
racters
> -    // in the ASCII range, and the original CID font for everything else=
.
> -    // The problem is that GhostScript still (as of 8.13) does not suppo=
rt
> -    // rearranged fonts even though they have been documented by Adobe f=
or 7
> -    // years!  If they ever do, a lot of this code will disappear.
> -    if (prt_use_courier)
> -    {
> -	in_ascii =3D (len =3D=3D 1 && *p < 0x80);
> -	if (prt_in_ascii)
> -	{
> -	    if (!in_ascii)
> -	    {
> -		// No longer in ASCII range - need to switch font
> -		prt_in_ascii =3D FALSE;
> -		prt_need_font =3D TRUE;
> -		prt_attribute_change =3D TRUE;
> -	    }
> -	}
> -	else if (in_ascii)
> -	{
> -	    // Now in ASCII range - need to switch font
> -	    prt_in_ascii =3D TRUE;
> -	    prt_need_font =3D TRUE;
> -	    prt_attribute_change =3D TRUE;
> -	}
> -    }
> -    if (prt_out_mbyte)
> -    {
> -	half_width =3D ((*mb_ptr2cells)(p) =3D=3D 1);
> -	if (half_width)
> -	    char_width /=3D 2;
> -	if (prt_half_width)
> -	{
> -	    if (!half_width)
> -	    {
> -		prt_half_width =3D FALSE;
> -		prt_pos_x +=3D prt_char_width/4;
> -		prt_need_moveto =3D TRUE;
> -		prt_attribute_change =3D TRUE;
> -	    }
> -	}
> -	else if (half_width)
> -	{
> -	    prt_half_width =3D TRUE;
> -	    prt_pos_x +=3D prt_char_width/4;
> -	    prt_need_moveto =3D TRUE;
> -	    prt_attribute_change =3D 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 =3D 0;
> -
> -	if (prt_need_moveto)
> -	{
> -	    prt_pos_x_moveto =3D prt_pos_x;
> -	    prt_pos_y_moveto =3D prt_pos_y;
> -	    prt_do_moveto =3D TRUE;
> -
> -	    prt_need_moveto =3D 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
> ");
> -	    prt_need_font =3D FALSE;
> -	}
> -	if (prt_need_fgcol)
> -	{
> -	    int     r, g, b;
> -	    r =3D ((unsigned)prt_fgcol & 0xff0000) >> 16;
> -	    g =3D ((unsigned)prt_fgcol & 0xff00) >> 8;
> -	    b =3D prt_fgcol & 0xff;
> -
> -	    prt_write_real(r / 255.0, 3);
> -	    if (r =3D=3D g && g =3D=3D b)
> -		prt_write_string("g
> ");
> -	    else
> -	    {
> -		prt_write_real(g / 255.0, 3);
> -		prt_write_real(b / 255.0, 3);
> -		prt_write_string("r
> ");
> -	    }
> -	    prt_need_fgcol =3D FALSE;
> -	}
> -
> -	if (prt_bgcol !=3D PRCOLOR_WHITE)
> -	{
> -	    prt_new_bgcol =3D prt_bgcol;
> -	    if (prt_need_bgcol)
> -		prt_do_bgcol =3D TRUE;
> -	}
> -	else
> -	    prt_do_bgcol =3D FALSE;
> -	prt_need_bgcol =3D FALSE;
> -
> -	if (prt_need_underline)
> -	    prt_do_underline =3D prt_underline;
> -	prt_need_underline =3D FALSE;
> -
> -	prt_attribute_change =3D FALSE;
> -    }
> -
> -    if (prt_do_conv)
> -    {
> -	// Convert from multi-byte to 8-bit encoding
> -	tofree =3D p =3D string_convert(&prt_conv, p, &len);
> -	if (p =3D=3D NULL)
> -	{
> -	    p =3D (char_u *)"";
> -	    len =3D 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 =3D prt_hexchar[(unsigned)(*p) >> 4];
> -	    ga_append(&prt_ps_buffer, ch);
> -	    ch =3D 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 =3D *p;
> -	if (ch < 32 || ch =3D=3D '(' || ch =3D=3D ')' || ch =3D=3D '\')
> -	{
> -	    // 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 +=3D char_width;
> -    prt_pos_x +=3D char_width;
> -
> -    // The downside of fp - use relative error on right margin check
> -    next_pos =3D prt_pos_x + prt_char_width;
> -    need_break =3D (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 =3D 0;
> -
> -    if (iBold)
> -	font |=3D 0x01;
> -    if (iItalic)
> -	font |=3D 0x02;
> -
> -    if (font !=3D prt_font)
> -    {
> -	prt_font =3D font;
> -	prt_attribute_change =3D TRUE;
> -	prt_need_font =3D TRUE;
> -    }
> -    if (prt_underline !=3D iUnderline)
> -    {
> -	prt_underline =3D iUnderline;
> -	prt_attribute_change =3D TRUE;
> -	prt_need_underline =3D TRUE;
> -    }
> -}
> -
> -    void
> -mch_print_set_bg(long_u bgcol)
> -{
> -    prt_bgcol =3D (int)bgcol;
> -    prt_attribute_change =3D TRUE;
> -    prt_need_bgcol =3D TRUE;
> -}
> -
> -    void
> -mch_print_set_fg(long_u fgcol)
> -{
> -    if (fgcol =3D=3D (long_u)prt_fgcol)
> -	return;
> -
> -    prt_fgcol =3D (int)fgcol;
> -    prt_attribute_change =3D TRUE;
> -    prt_need_fgcol =3D TRUE;
> -}
> -
> -#endif //FEAT_POSTSCRIPT
> #endif //FEAT_PRINTER
> diff --git a/src/hardcopy.h b/src/hardcopy.h
> new file mode 100644
> index 000000000..0d8d6e574
> --- /dev/null
> +++ b/src/hardcopy.h
> @@ -0,0 +1,46 @@
> +/* vi:set ts=3D8 sts=3D4 sw=3D4 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
> index 000000000..e3d46a57b
> --- /dev/null
> +++ b/src/hardcopy_pango.c
> @@ -0,0 +1,783 @@
> +/* vi:set ts=3D8 sts=3D4 sw=3D4 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-pd=
f-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[] =3D {
> +    [PRT_FORMAT_PS] =3D "postscript",
> +    [PRT_FORMAT_PDF] =3D "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 =3D prt_get_unit(idx);
> +    if (u =3D=3D PRT_UNIT_NONE)
> +    {
> +	u =3D PRT_UNIT_PERC;
> +	nr =3D def_number;
> +    }
> +    else
> +	nr =3D printer_opts[idx].number;
> +
> +    switch (u)
> +    {
> +	case PRT_UNIT_INCH:
> +	    ret =3D (nr * PRINT_DPI);
> +	    break;
> +	case PRT_UNIT_MM:
> +	    ret =3D (nr * PRINT_DPI) / 25.4;
> +	    break;
> +	case PRT_UNIT_POINT:
> +	    ret =3D (double)nr;
> +	    break;
> +	case PRT_UNIT_PERC:
> +	default:
> +	    ret =3D physsize * (nr / 100.0);
> +	    break;
> +    }
> +
> +    return ret;
> +}
> +
> +/*
> + * Calculate margins for given width and height from printoptions settin=
gs.
> + */
> +    static void
> +get_page_margins(
> +	double	width,
> +	double	height,
> +	double	*left,
> +	double	*right,
> +	double	*top,
> +	double	*bottom)
> +{
> +    *left =3D to_device_units(OPT_PRINT_LEFT, width, 10);
> +    *right =3D width - to_device_units(OPT_PRINT_RIGHT, width, 5);
> +
> +    *top =3D to_device_units(OPT_PRINT_TOP, height, 5);
> +    *bottom =3D 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 =3D PRINT_NUMBER_WIDTH * pctx.char_width;
> +	pctx.left_margin +=3D pctx.number_width;
> +    }
> +    else
> +	pctx.number_width =3D 0.0;
> +
> +    return (int)((pctx.right_margin - pctx.left_margin) / pctx.char_widt=
h);
> +}
> +
> +/*
> + * Get number of lines of text that fit on a page (excluding the header)=
.
> + */
> +    static int
> +get_lpp(void)
> +{
> +    int lpp;
> +
> +    // Calculate lpp
> +    lpp =3D (int)((pctx.bottom_margin - pctx.top_margin) / pctx.char_hei=
ght);
> +
> +    // Adjust top margin if there is a header
> +    pctx.top_margin +=3D 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 =3D=3D NULL)
> +	return NULL;
> +
> +    font =3D pango_font_description_from_string((const char *)name);
> +    if (font =3D=3D NULL)
> +    {
> +	semsg(_(e_unknown_printer_font_str), name);
> +	return NULL;
> +    }
> +
> +    // Ensure a size is set
> +    if (pango_font_description_get_size(font) <=3D 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 =3D NULL;
> +#ifdef USE_GTK4_PRINT_DIALOG
> +    bool	dialog =3D false;
> +
> +    if (gui.in_use && !forceit && psettings->outfile =3D=3D NULL)
> +    {
> +	filename =3D gui_gtk4_print_dialog(psettings, jobname,
> +		&pctx.page_width, &pctx.page_height);
> +
> +	if (filename =3D=3D NULL)
> +	    return FAIL;
> +	dialog =3D true;
> +    }
> +#endif
> +
> +    pctx.format =3D PRT_FORMAT_PS;
> +
> +    // Let GTK handle stuff when using dialog. Not sure if our postscrip=
t 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 =3D (!printer_opts[OPT_PRINT_PORTRAIT].present ||
> +		TOLOWER_ASC(printer_opts[OPT_PRINT_PORTRAIT].string[0]) =3D=3D 'y');
> +	pctx.portrait =3D portrait;
> +	if (printer_opts[OPT_PRINT_PAPER].present)
> +	{
> +	    paper_name =3D printer_opts[OPT_PRINT_PAPER].string;
> +	    paper_strlen =3D printer_opts[OPT_PRINT_PAPER].strlen;
> +	}
> +	else
> +	{
> +	    paper_name =3D (char_u *)"A4";
> +	    paper_strlen =3D 2;
> +	}
> +
> +	// Set width and height using given paper type
> +	for (media =3D 0; media < (int)PRT_MEDIASIZE_LEN; ++media)
> +	    if ((int)STRLEN(prt_mediasize[media].name) =3D=3D paper_strlen
> +		    && STRNICMP(prt_mediasize[media].name,
> +			paper_name, paper_strlen) =3D=3D 0)
> +		break;
> +	if (media =3D=3D PRT_MEDIASIZE_LEN)
> +	    media =3D 0;
> +	pctx.media =3D media;
> +
> +	if (portrait)
> +	{
> +	    pctx.page_width =3D prt_mediasize[media].width;
> +	    pctx.page_height =3D prt_mediasize[media].height;
> +	}
> +	else
> +	{
> +	    pctx.page_width =3D prt_mediasize[media].height;
> +	    pctx.page_height =3D prt_mediasize[media].width;
> +	}
> +
> +	if (printer_opts[OPT_PRINT_FORMAT].present)
> +	{
> +	    for (int i =3D 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) =3D=3D 0)
> +		{
> +		    pctx.format =3D i;
> +		    break;
> +		}
> +	}
> +    }
> +    psettings->user_abort =3D FALSE;
> +
> +#ifdef USE_GTK4_PRINT_DIALOG
> +    if (dialog)
> +	// Always use PDF for dialog
> +	pctx.format =3D PRT_FORMAT_PDF;
> +#endif
> +
> +    if (filename =3D=3D NULL)
> +    {
> +	// If the user didn't specify a file name, use a temp file.
> +	if (psettings->outfile =3D=3D NULL)
> +	{
> +	    filename =3D vim_tempname('p', TRUE);
> +	    if (filename =3D=3D NULL)
> +	    {
> +		emsg(_(e_cant_get_temp_file_name));
> +		return FAIL;
> +	    }
> +	}
> +	else
> +	    filename =3D expand_env_save(psettings->outfile);
> +    }
> +
> +    // If using dialog, then always use PDF
> +    if (pctx.format =3D=3D PRT_FORMAT_PS)
> +	pctx.surface =3D cairo_ps_surface_create((const char *)filename,
> +		pctx.page_width, pctx.page_height);
> +    else
> +	pctx.surface =3D cairo_pdf_surface_create((const char *)filename,
> +		pctx.page_width, pctx.page_height);
> +
> +    pctx.filename =3D (char_u *)filename;
> +
> +    if (cairo_surface_status(pctx.surface) !=3D CAIRO_STATUS_SUCCESS)
> +    {
> +	emsg(_(e_cant_open_postscript_output_file));
> +	mch_print_cleanup();
> +	return FAIL;
> +    }
> +
> +    pctx.cr =3D cairo_create(pctx.surface);
> +    pctx.text_context =3D pango_cairo_create_context(pctx.cr);
> +    pctx.font =3D get_font(p_pfn);
> +
> +    if (pctx.font =3D=3D 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 =3D pango_layout_new(pctx.text_context);
> +	pango_layout_set_font_description(layout, pctx.font);
> +
> +	metrics =3D pango_context_get_metrics(
> +		pango_layout_get_context(layout), pctx.font, NULL);
> +
> +	pctx.char_width =3D
> +	    (double)pango_font_metrics_get_approximate_char_width(metrics)
> +	    / PANGO_SCALE;
> +
> +	ascent =3D pango_font_metrics_get_ascent(metrics);
> +	descent =3D pango_font_metrics_get_descent(metrics);
> +
> +	pctx.char_ascent =3D (double)ascent / PANGO_SCALE;
> +	pctx.char_height =3D (double)(ascent + descent
> +		+ (PANGO_SCALE * 15.0) / 16.0) / PANGO_SCALE;
> +
> +	pctx.line_spacing =3D (double)
> +	    (pango_font_metrics_get_height(metrics) - (ascent + descent))
> +	    / PANGO_SCALE;
> +
> +	pctx.ul_pos =3D
> +	    (double)pango_font_metrics_get_underline_position(metrics)
> +	    / PANGO_SCALE;
> +	pctx.ul_thickness =3D
> +	    (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 =3D left;
> +	pctx.right_margin =3D right;
> +	pctx.top_margin =3D top;
> +	pctx.bottom_margin =3D bottom;
> +    }
> +
> +    psettings->chars_per_line =3D get_cpl();
> +    psettings->lines_per_page =3D 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 'printoption=
s'
> +    psettings->n_collated_copies =3D 1;
> +    psettings->n_uncollated_copies =3D 1;
> +
> +    psettings->jobname =3D jobname;
> +
> +#ifdef USE_GTK4_PRINT_DIALOG
> +    if (!dialog)
> +#endif
> +    {
> +	pctx.collate =3D (!printer_opts[OPT_PRINT_COLLATE].present ||
> +		TOLOWER_ASC(printer_opts[OPT_PRINT_COLLATE].string[0]) =3D=3D 'y');
> +
> +	pctx.duplex =3D TRUE;
> +	pctx.tumble =3D FALSE;
> +	psettings->duplex =3D TRUE;
> +	if (printer_opts[OPT_PRINT_DUPLEX].present)
> +	{
> +	    if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "off", 3) =3D=
=3D 0)
> +	    {
> +		pctx.duplex =3D FALSE;
> +		psettings->duplex =3D 0;
> +	    }
> +	    else if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "short",
> +			5) =3D=3D 0)
> +		pctx.tumble =3D TRUE;
> +	}
> +    }
> +
> +#ifdef USE_GTK4_PRINT_DIALOG
> +    pctx.dialog =3D 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 =3D sizeof(buf);
> +    int	    o;
> +    char    *s =3D buf;
> +
> +    if (fmt =3D=3D NULL)
> +    {
> +	o =3D vim_snprintf(s, len, "%%%%%s", comment);
> +	goto exit;
> +    }
> +    else
> +	o =3D vim_snprintf(s, len, "%%%%%s: ", comment);
> +
> +    len -=3D o;
> +    s +=3D o;
> +    if (len <=3D 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 =3D 0.0;
> +    pctx.cur_line =3D 0;
> +    pctx.draw_flags =3D 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 =3D=3D 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 =3D=3D PRT_FORMAT_PDF)
> +    {
> +	char_u *str =3D (char_u *)user;
> +
> +	if (output_conv.vc_type !=3D CONV_NONE)
> +	    str =3D string_convert(&output_conv, str, NULL);
> +
> +	cairo_pdf_surface_set_metadata(pctx.surface,
> +		CAIRO_PDF_METADATA_AUTHOR, (char *)str);
> +	if (output_conv.vc_type !=3D CONV_NONE)
> +	    vim_free(str);
> +
> +	str =3D psettings->jobname;
> +	if (output_conv.vc_type !=3D CONV_NONE)
> +	    str =3D string_convert(&output_conv, str, NULL);
> +	cairo_pdf_surface_set_metadata(pctx.surface,
> +		CAIRO_PDF_METADATA_TITLE, (char *)str);
> +	if (output_conv.vc_type !=3D 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 =3D=3D NULL && !got_int && !psettings->user_a=
bort
> +	    && pctx.filename !=3D 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) =3D=3D 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 =3D pctx.left_margin;
> +    if (margin)
> +	pctx.cur_x -=3D pctx.number_width;
> +
> +    pctx.cur_line =3D page_line;
> +}
> +
> +    static void
> +long_to_rgb(long_u c, double *r, double *g, double *b)
> +{
> +    *r =3D ((c >> 16) & 0xFF) / 255.0;
> +    *g =3D ((c >> 8) & 0xFF) / 255.0;
> +    *b =3D ((c >> 0) & 0xFF) / 255.0;
> +}
> +
> +/*
> + * Don't use PangoLayout for rendering the text, so that we can handle t=
he
> + * 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 =3D 0;
> +    double		next_x;
> +
> +    // Always convert to UTF-8, Pango expects that
> +    if (output_conv.vc_type =3D=3D CONV_NONE)
> +	str =3D textp;
> +    else
> +	str =3D string_convert(&output_conv, textp, &len);
> +
> +    if (str =3D=3D NULL)
> +	return TRUE;
> +
> +    attr_list =3D pango_attr_list_new();
> +
> +#define INSERT_PANGO_ATTR(Attribute, AttrList, Start, End)  \
> +    G_STMT_START{					    \
> +	PangoAttribute *tmp_attr_;			    \
> +	tmp_attr_ =3D (Attribute);			    \
> +	tmp_attr_->start_index =3D (Start);		    \
> +	tmp_attr_->end_index =3D (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 =3D pango_itemize(pctx.text_context,
> +	    (const char *)str, 0, len, attr_list, NULL);
> +    pango_attr_list_unref(attr_list);
> +
> +    glyphs =3D pango_glyph_string_new();
> +
> +    // Loop is probably unecessary, because "str" represents a single ch=
aracter.
> +    // Do it anyways to be sure.
> +    while (item_list !=3D NULL)
> +    {
> +	PangoItem   *item;
> +	double	    o;
> +
> +	item =3D (PangoItem *)item_list->data;
> +	item_list =3D 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 th=
e
> +	// char width and number of cells, is that non-monospace fonts look
> +	// better.
> +	o =3D (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 !=3D 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 +=3D o;
> +    }
> +
> +    // Draw underline if needed
> +    if (pctx.draw_flags & PRT_DRAW_UNDERLINE)
> +    {
> +	double y =3D 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 +=3D width;
> +
> +    next_x =3D pctx.cur_x + pctx.char_width;
> +
> +    if (output_conv.vc_type !=3D 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 =3D 0;
> +
> +    if (bold)
> +	pctx.draw_flags |=3D PRT_DRAW_BOLD;
> +    if (italic)
> +	pctx.draw_flags |=3D PRT_DRAW_ITALIC;
> +    if (underline)
> +	pctx.draw_flags |=3D PRT_DRAW_UNDERLINE;
> +}
> +
> +    void
> +mch_print_set_bg(long_u bgcol)
> +{
> +    pctx.cur_bg =3D 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 =3D 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
> index 000000000..26a4c227a
> --- /dev/null
> +++ b/src/hardcopy_pango.h
> @@ -0,0 +1,32 @@
> +/* vi:set ts=3D8 sts=3D4 sw=3D4 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 force=
it);
> +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
> index 000000000..123b9f340
> --- /dev/null
> +++ b/src/hardcopy_postscript.c
> @@ -0,0 +1,2512 @@
> +/* vi:set ts=3D8 sts=3D4 sw=3D4 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] =3D
> +{
> +    {"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 =3D
> +{
> +    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 =3D
> +{
> +    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 f=
or
> +// 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[] =3D
> +{
> +    {"iso-2022-jp", NULL,       (CS_JIS_C_1978|CS_JIS_X_1983|CS_JIS_X_19=
90|
> +								    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[] =3D
> +{
> +    {"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[] =3D
> +{
> +    {"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[] =3D
> +{
> +    {"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[] =3D
> +{
> +    {"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[] =3D
> +{
> +    {"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[] =3D
> +{
> +    {"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[] =3D
> +{
> +    {"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[] =3D
> +{
> +    {
> +	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 i=
s
> +// 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[] =3D
> +{
> +    "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 n=
eeds 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[] =3D
> +{
> +    {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 =3D NULL;
> +
> +/*
> + * Various offsets and dimensions in default PostScript user space (poin=
ts).
> + * 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 =3D 0.0;
> +static float prt_pos_y_moveto =3D 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[] =3D "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)
> +							     !=3D (size_t)bytes)
> +    {
> +	emsg(_(e_error_writing_to_postscript_output_file));
> +	prt_file_error =3D 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
> ", new_name, encoding, font);
> +    prt_write_file(prt_line_buffer);
> +    if (prt_out_mbyte)
> +	sprintf((char *)prt_line_buffer, "/%s %d %f /_%s sffs
> ",
> +		       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
> ", 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
> ", 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
> ", 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
> ", 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 fractio=
nal 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 =3D (int)real;
> +    fraction =3D (float)(real - integer);
> +    if (real < (double)integer)
> +	fraction =3D -fraction;
> +    for (i =3D 0; i < precision; i++)
> +	fraction *=3D 10.0;
> +
> +    *pinteger =3D integer;
> +    *pfraction =3D (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 de=
cide
> + * 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 !=3D 0)
> +    {
> +	// Remove any trailing zeros
> +	while ((fraction % 10) =3D=3D 0)
> +	{
> +	    prec--;
> +	    fraction /=3D 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
> ");
> +    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 <=3D 0)
> +	return;
> +
> +    // Any background color must be drawn first
> +    if (prt_do_bgcol && (prt_new_bgcol !=3D 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
> ");
> +	    prt_do_moveto =3D 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 =3D ((unsigned)prt_new_bgcol & 0xff0000) >> 16;
> +	g =3D ((unsigned)prt_new_bgcol & 0xff00) >> 8;
> +	b =3D 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
> ");
> +    }
> +    // 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
> ");
> +	    prt_do_moveto =3D FALSE;
> +	}
> +
> +	// Underline length of text run
> +	prt_write_real(prt_text_run, 2);
> +	prt_write_string("ul
> ");
> +    }
> +    // 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
> ");
> +	prt_do_moveto =3D FALSE;
> +    }
> +    else // Simple show
> +	prt_write_string("s
> ");
> +
> +    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 =3D cookie;
> +
> +    if (STRLEN(filename) >=3D MAXPATHL)
> +	*resource_filename =3D 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 =3D alloc(MAXPATHL + 1);
> +    if (buffer =3D=3D 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] =3D NUL;
> +    retval =3D (do_in_runtimepath(buffer, 0, prt_resource_name,
> +							   resource->filename)
> +	    && resource->filename[0] !=3D 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 h=
ave 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 =3D prt_resfile.line_end + 1;
> +    while (idx < prt_resfile.len)
> +    {
> +	if (prt_resfile.buffer[idx] !=3D PSLF && prt_resfile.buffer[idx] !=3D P=
SCR)
> +	    break;
> +	idx++;
> +    }
> +    prt_resfile.line_start =3D idx;
> +
> +    while (idx < prt_resfile.len)
> +    {
> +	if (prt_resfile.buffer[idx] =3D=3D PSLF || prt_resfile.buffer[idx] =3D=
=3D PSCR)
> +	    break;
> +	idx++;
> +    }
> +    prt_resfile.line_end =3D 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 =3D 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 =3D 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.  Retu=
rns 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) !=3D 0)
> +	return FALSE;
> +
> +    // Find type of DSC comment
> +    for (comment =3D 0; comment < (int)ARRAY_LENGTH(prt_dsc_table); comm=
ent++)
> +	if (prt_resfile_strncmp(0, prt_dsc_table[comment].string,
> +					    prt_dsc_table[comment].len) =3D=3D 0)
> +	    break;
> +
> +    if (comment !=3D ARRAY_LENGTH(prt_dsc_table))
> +    {
> +	// Return type of comment
> +	p_dsc_line->type =3D prt_dsc_table[comment].type;
> +	offset =3D prt_dsc_table[comment].len;
> +    }
> +    else
> +    {
> +	// Unrecognised DSC comment, skip to ws after comment leader
> +	p_dsc_line->type =3D PRT_DSC_MISC_TYPE;
> +	offset =3D prt_resfile_skip_nonws(0);
> +	if (offset =3D=3D -1)
> +	    return FALSE;
> +    }
> +
> +    // Skip ws to comment value
> +    offset =3D prt_resfile_skip_ws(offset);
> +    if (offset =3D=3D -1)
> +	return FALSE;
> +
> +    p_dsc_line->string =3D &prt_resfile.buffer[prt_resfile.line_start + =
offset];
> +    p_dsc_line->len =3D prt_resfile.line_end - (prt_resfile.line_start +=
 offset);
> +
> +    return TRUE;
> +}
> +
> +/*
> + * Improved hand crafted parser to get the type, title, and version numb=
er of a
> + * PS resource file so the file details can be added to the DSC header c=
omments.
> + */
> +    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 =3D mch_fopen((char *)resource->filename, READBIN);
> +    if (fd_resource =3D=3D 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 =3D (int)fread((char *)prt_resfile.buffer, sizeof(ch=
ar_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 =3D -1;
> +    prt_resfile.line_start =3D 0;
> +    if (!prt_resfile_next_line())
> +	return FALSE;
> +
> +    offset =3D 0;
> +
> +    if (prt_resfile_strncmp(offset, PRT_RESOURCE_HEADER,
> +			       (int)STRLEN_LITERAL(PRT_RESOURCE_HEADER)) !=3D 0)
> +    {
> +	semsg(_(e_file_str_is_not_postscript_resource_file),
> +							   resource->filename);
> +	return FALSE;
> +    }
> +
> +    // Skip over any version numbers and following ws
> +    offset +=3D (int)STRLEN_LITERAL(PRT_RESOURCE_HEADER);
> +    offset =3D prt_resfile_skip_nonws(offset);
> +    if (offset =3D=3D -1)
> +	return FALSE;
> +    offset =3D prt_resfile_skip_ws(offset);
> +    if (offset =3D=3D -1)
> +	return FALSE;
> +
> +    if (prt_resfile_strncmp(offset, PRT_RESOURCE_RESOURCE,
> +			     (int)STRLEN_LITERAL(PRT_RESOURCE_RESOURCE)) !=3D 0)
> +    {
> +	semsg(_(e_file_str_is_not_supported_postscript_resource_file),
> +							   resource->filename);
> +	return FALSE;
> +    }
> +    offset +=3D (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)) =3D=3D 0)
> +	resource->type =3D PRT_RESOURCE_TYPE_PROCSET;
> +    else if (prt_resfile_strncmp(offset, PRT_RESOURCE_ENCODING,
> +			     (int)STRLEN_LITERAL(PRT_RESOURCE_ENCODING)) =3D=3D 0)
> +	resource->type =3D PRT_RESOURCE_TYPE_ENCODING;
> +    else if (prt_resfile_strncmp(offset, PRT_RESOURCE_CMAP,
> +				 (int)STRLEN_LITERAL(PRT_RESOURCE_CMAP)) =3D=3D 0)
> +	resource->type =3D 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] =3D '
> +    resource->version[0] =3D '
> +    seen_title =3D FALSE;
> +    seen_version =3D FALSE;
> +    seen_all =3D 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 =3D TRUE;
> +	    if (seen_version)
> +		seen_all =3D TRUE;
> +	    break;
> +
> +	case PRT_DSC_VERSION_TYPE:
> +	    vim_strncpy(resource->version, dsc_line.string, dsc_line.len);
> +	    seen_version =3D TRUE;
> +	    if (seen_title)
> +		seen_all =3D TRUE;
> +	    break;
> +
> +	case PRT_DSC_ENDCOMMENTS_TYPE:
> +	    // Won't find title or resource after this comment, stop searching
> +	    seen_all =3D 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 mat=
ter
> +    if (STRNCMP(resource->version, version, STRLEN(version)) !=3D 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
> ");
> +}
> +
> +    static void
> +prt_dsc_noarg(char *comment)
> +{
> +    vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
> +							 "%%%%%s
> ", 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
> ", 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)
> ", 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 =3D 0; i < count; i++)
> +    {
> +	sprintf((char *)prt_line_buffer, " %d", ints[i]);
> +	prt_write_file(prt_line_buffer);
> +    }
> +
> +    prt_write_string("
> ");
> +}
> +
> +    static void
> +prt_dsc_resources(
> +    char	*comment,	// if NULL add to previous
> +    char	*type,
> +    char	*string)
> +{
> +    if (comment !=3D 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
> ", 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 =3D PRT_PS_FONT_BOLD ; i <=3D PRT_PS_FONT_BOLDOBLIQUE ; i++)
> +	if (ps_font->ps_fontname[i] !=3D 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("
> ");
> +}
> +
> +    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 =3D=3D NULL)
> +	prt_write_string("()");
> +    else
> +	prt_write_string(colour);
> +    prt_write_string(" ");
> +    if (type =3D=3D NULL)
> +	prt_write_string("()");
> +    else
> +	prt_write_string(type);
> +    prt_write_string("
> ");
> +}
> +
> +    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 tha=
n
> +	// one style).
> +	for (i =3D PRT_PS_FONT_ROMAN; i <=3D PRT_PS_FONT_BOLDOBLIQUE; i++)
> +	{
> +	    if (prt_ps_mb_font.ps_fontname[i] !=3D NULL)
> +		VIM_CLEAR(prt_ps_mb_font.ps_fontname[i]);
> +	}
> +    }
> +
> +    if (prt_do_conv)
> +    {
> +	convert_setup(&prt_conv, NULL, NULL);
> +	prt_do_conv =3D FALSE;
> +    }
> +    if (prt_ps_fd !=3D NULL)
> +    {
> +	fclose(prt_ps_fd);
> +	prt_ps_fd =3D NULL;
> +	prt_file_error =3D FALSE;
> +    }
> +    if (prt_ps_file_name !=3D 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 =3D prt_get_unit(idx);
> +    if (u =3D=3D PRT_UNIT_NONE)
> +    {
> +	u =3D PRT_UNIT_PERC;
> +	nr =3D def_number;
> +    }
> +    else
> +	nr =3D printer_opts[idx].number;
> +
> +    switch (u)
> +    {
> +	case PRT_UNIT_INCH:
> +	    ret =3D (float)(nr * PRT_PS_DEFAULT_DPI);
> +	    break;
> +	case PRT_UNIT_MM:
> +	    ret =3D (float)(nr * PRT_PS_DEFAULT_DPI) / (float)25.4;
> +	    break;
> +	case PRT_UNIT_POINT:
> +	    ret =3D (float)nr;
> +	    break;
> +	case PRT_UNIT_PERC:
> +	default:
> +	    ret =3D (float)(physsize * nr) / 100;
> +	    break;
> +    }
> +
> +    return ret;
> +}
> +
> +/*
> + * Calculate margins for given width and height from printoptions settin=
gs.
> + */
> +    static void
> +prt_page_margins(
> +    double	width,
> +    double	height,
> +    double	*left,
> +    double	*right,
> +    double	*top,
> +    double	*bottom)
> +{
> +    *left   =3D to_device_units(OPT_PRINT_LEFT, width, 10);
> +    *right  =3D width - to_device_units(OPT_PRINT_RIGHT, width, 5);
> +    *top    =3D height - to_device_units(OPT_PRINT_TOP, height, 5);
> +    *bottom =3D to_device_units(OPT_PRINT_BOT, height, 5);
> +}
> +
> +    static void
> +prt_font_metrics(int font_scale)
> +{
> +    prt_line_height =3D (float)font_scale;
> +    prt_char_width =3D (float)PRT_PS_FONT_TO_USER(font_scale, prt_ps_fon=
t->wx);
> +}
> +
> +
> +    static int
> +prt_get_cpl(void)
> +{
> +    if (prt_use_number())
> +    {
> +	prt_number_width =3D 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 /=3D 2;
> +	prt_left_margin +=3D prt_number_width;
> +    }
> +    else
> +	prt_number_width =3D 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 =3D alloc(name_len + 1);
> +    if (fontname =3D=3D NULL)
> +	return FALSE;
> +    vim_strncpy((char_u *)fontname, name, name_len);
> +    prt_ps_mb_font.ps_fontname[font] =3D 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, hand=
ling the
> +     * case where the font height can exceed the line height.
> +     */
> +    prt_bgcol_offset =3D (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 -=3D (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 =3D prt_line_height + prt_bgcol_offset;
> +
> +    // Calculate lpp
> +    lpp =3D (int)((prt_top_margin - prt_bottom_margin) / prt_line_height=
);
> +
> +    // Adjust top margin if there is a header
> +    prt_top_margin -=3D 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 =3D NULL;
> +    // Look for recognised encoding
> +    enc_len =3D (int)STRLEN(p_encoding);
> +    p_mbenc =3D p_cmap->encodings;
> +    for (mbenc =3D 0; mbenc < p_cmap->num_encodings; mbenc++)
> +    {
> +	if (STRNICMP(p_mbenc->encoding, p_encoding, enc_len) =3D=3D 0)
> +	{
> +	    *pp_mbenc =3D 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 gi=
ven
> +    if (*p_charset =3D=3D NUL)
> +	p_charset =3D p_cmap->defcs;
> +    char_len =3D (int)STRLEN(p_charset);
> +    p_mbchar =3D p_cmap->charsets;
> +    for (mbchar =3D 0; mbchar < p_cmap->num_charsets; mbchar++)
> +    {
> +	if (STRNICMP(p_mbchar->charset, p_charset, char_len) =3D=3D 0)
> +	{
> +	    *pp_mbchar =3D 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 =3D 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 =3D 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 |=3D PD_RETURNDEFAULT;
> +# endif
> +
> +    /*
> +     * Set up font and encoding.
> +     */
> +    p_encoding =3D enc_skip(p_penc);
> +    if (*p_encoding =3D=3D NUL)
> +	p_encoding =3D enc_skip(p_enc);
> +
> +    // Look for a multi-byte font that matches the encoding and characte=
r 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 =3D NULL;
> +    props =3D enc_canon_props(p_encoding);
> +    if (!(props & ENC_8BIT) && ((*p_pmcs !=3D NUL) || !(props & ENC_UNIC=
ODE)))
> +    {
> +	int cmap_first =3D 0;
> +
> +	p_mbenc_first =3D NULL;
> +	for (cmap =3D 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 =3D=3D NULL)
> +		{
> +		    p_mbenc_first =3D p_mbenc;
> +		    cmap_first =3D 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 =3D=3D NULL && p_mbenc_first !=3D NULL)
> +	{
> +	    p_mbenc =3D p_mbenc_first;
> +	    cmap =3D cmap_first;
> +	}
> +    }
> +
> +    prt_out_mbyte =3D (p_mbenc !=3D NULL);
> +    if (prt_out_mbyte)
> +    {
> +	// Build CMap name - will be same for all multi-byte fonts used
> +	prt_cmap[0] =3D NUL;
> +
> +	prt_custom_cmap =3D (p_mbchar =3D=3D NULL);
> +	if (!prt_custom_cmap)
> +	{
> +	    // Check encoding and character set are compatible
> +	    if ((p_mbenc->needs_charset & p_mbchar->has_charset) =3D=3D 0)
> +	    {
> +		emsg(_(e_incompatible_multi_byte_encoding_and_character_set));
> +		return FALSE;
> +	    }
> +
> +	    // Add charset name if not empty
> +	    if (p_mbchar->cmap_charset !=3D 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 =3D=3D 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 !=3D 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 =3D mbfont_opts[OPT_MBFONT_USECOURIER].present &&
> +	    (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_USECOURIER].string[0]) =3D=3D '=
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]) =3D=3D 'y'))
> +		prt_ascii_encoding =3D "ascii";
> +	    else
> +		prt_ascii_encoding =3D prt_ps_mbfonts[cmap].ascii_enc;
> +	}
> +
> +	prt_ps_font =3D &prt_ps_mb_font;
> +    }
> +    else
> +    {
> +	prt_use_courier =3D FALSE;
> +	prt_ps_font =3D &prt_ps_courier_font;
> +    }
> +
> +    /*
> +     * Find the size of the paper and set the margins.
> +     */
> +    prt_portrait =3D (!printer_opts[OPT_PRINT_PORTRAIT].present
> +	   || TOLOWER_ASC(printer_opts[OPT_PRINT_PORTRAIT].string[0]) =3D=3D 'y=
');
> +    if (printer_opts[OPT_PRINT_PAPER].present)
> +    {
> +	paper_name =3D (char *)printer_opts[OPT_PRINT_PAPER].string;
> +	paper_strlen =3D printer_opts[OPT_PRINT_PAPER].strlen;
> +    }
> +    else
> +    {
> +	paper_name =3D "A4";
> +	paper_strlen =3D 2;
> +    }
> +    for (i =3D 0; i < (int)PRT_MEDIASIZE_LEN; ++i)
> +	if (STRLEN(prt_mediasize[i].name) =3D=3D (unsigned)paper_strlen
> +		&& STRNICMP(prt_mediasize[i].name, paper_name,
> +							   paper_strlen) =3D=3D 0)
> +	    break;
> +    if (i =3D=3D PRT_MEDIASIZE_LEN)
> +	i =3D 0;
> +    prt_media =3D i;
> +
> +    /*
> +     * Set PS pagesize based on media dimensions and print orientation.
> +     * Note: Media and page sizes have defined meanings in PostScript an=
d should
> +     * be kept distinct.  Media is the paper (or transparency, or ...) t=
hat is
> +     * printed on, whereas the page size is the area that the PostScript
> +     * interpreter renders into.
> +     */
> +    if (prt_portrait)
> +    {
> +	prt_page_width =3D prt_mediasize[i].width;
> +	prt_page_height =3D prt_mediasize[i].height;
> +    }
> +    else
> +    {
> +	prt_page_width =3D prt_mediasize[i].height;
> +	prt_page_height =3D 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, &to=
p,
> +								    &bottom);
> +    prt_left_margin =3D (float)left;
> +    prt_right_margin =3D (float)right;
> +    prt_top_margin =3D (float)top;
> +    prt_bottom_margin =3D (float)bottom;
> +
> +    /*
> +     * Set up the font size.
> +     */
> +    fontsize =3D PRT_PS_DEFAULT_FONTSIZE;
> +    for (p =3D p_pfn; (p =3D vim_strchr(p, ':')) !=3D NULL; ++p)
> +	if (p[1] =3D=3D 'h' && VIM_ISDIGIT(p[2]))
> +	    fontsize =3D 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 =3D prt_get_cpl();
> +    psettings->lines_per_page =3D prt_get_lpp();
> +
> +    // Catch margin settings that leave no space for output!
> +    if (psettings->chars_per_line <=3D 0 || psettings->lines_per_page <=
=3D 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 co=
pies are
> +     * wanted cache it away and lie to the generic code that we only wan=
t one
> +     * uncollated copy.
> +     */
> +    psettings->n_collated_copies =3D 1;
> +    psettings->n_uncollated_copies =3D 1;
> +    prt_num_copies =3D 1;
> +    prt_collate =3D (!printer_opts[OPT_PRINT_COLLATE].present
> +	    || TOLOWER_ASC(printer_opts[OPT_PRINT_COLLATE].string[0]) =3D=3D 'y=
');
> +    if (prt_collate)
> +    {
> +	// TODO: Get number of collated copies wanted.
> +	psettings->n_collated_copies =3D 1;
> +    }
> +    else
> +    {
> +	// TODO: Get number of uncollated copies wanted and update the cached
> +	// count.
> +	prt_num_copies =3D 1;
> +    }
> +
> +    psettings->jobname =3D jobname;
> +
> +    /*
> +     * Set up printer duplex and tumble based on Duplex option setting -=
 default
> +     * is long sided duplex printing (i.e. no tumble).
> +     */
> +    prt_duplex =3D TRUE;
> +    prt_tumble =3D FALSE;
> +    psettings->duplex =3D 1;
> +    if (printer_opts[OPT_PRINT_DUPLEX].present)
> +    {
> +	if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "off", 3) =3D=3D 0)
> +	{
> +	    prt_duplex =3D FALSE;
> +	    psettings->duplex =3D 0;
> +	}
> +	else if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "short", 5)
> +									 =3D=3D 0)
> +	    prt_tumble =3D TRUE;
> +    }
> +
> +    // For now user abort not supported
> +    psettings->user_abort =3D 0;
> +
> +    // If the user didn't specify a file name, use a temp file.
> +    if (psettings->outfile =3D=3D NULL)
> +    {
> +	prt_ps_file_name =3D vim_tempname('p', TRUE);
> +	if (prt_ps_file_name =3D=3D NULL)
> +	{
> +	    emsg(_(e_cant_get_temp_file_name));
> +	    return FAIL;
> +	}
> +	prt_ps_fd =3D mch_fopen((char *)prt_ps_file_name, WRITEBIN);
> +    }
> +    else
> +    {
> +	p =3D expand_env_save(psettings->outfile);
> +	if (p !=3D NULL)
> +	{
> +	    prt_ps_fd =3D mch_fopen((char *)p, WRITEBIN);
> +	    vim_free(p);
> +	}
> +    }
> +    if (prt_ps_fd =3D=3D NULL)
> +    {
> +	emsg(_(e_cant_open_postscript_output_file));
> +	mch_print_cleanup();
> +	return FAIL;
> +    }
> +
> +    prt_bufsiz =3D psettings->chars_per_line;
> +    if (prt_out_mbyte)
> +	prt_bufsiz *=3D 2;
> +    ga_init2(&prt_ps_buffer, sizeof(char), prt_bufsiz);
> +
> +    prt_page_num =3D 0;
> +
> +    prt_attribute_change =3D FALSE;
> +    prt_need_moveto =3D FALSE;
> +    prt_need_font =3D FALSE;
> +    prt_need_fgcol =3D FALSE;
> +    prt_need_bgcol =3D FALSE;
> +    prt_need_underline =3D FALSE;
> +
> +    prt_file_error =3D 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 =3D mch_fopen((char *)resource->filename, READBIN);
> +    if (fd_resource =3D=3D 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 =3D 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 =3D=3D 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 =3D FALSE;
> +
> +    res_prolog =3D ALLOC_ONE(struct prt_ps_resource_S);
> +    res_encoding =3D ALLOC_ONE(struct prt_ps_resource_S);
> +    res_cidfont =3D ALLOC_ONE(struct prt_ps_resource_S);
> +    res_cmap =3D ALLOC_ONE(struct prt_ps_resource_S);
> +    if (res_prolog =3D=3D NULL || res_encoding =3D=3D NULL
> +	    || res_cidfont =3D=3D NULL || res_cmap =3D=3D 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" : "Landsca=
pe");
> +    prt_dsc_atend("Pages");
> +    prt_dsc_textline("PageOrder", "Ascend");
> +    // The bbox does not change with orientation - it is always in the d=
efault
> +    // 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] =3D (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] =3D (int)(top - (psettings->lines_per_page + prt_header_height(=
))
> +						    * (double)prt_line_height);
> +	bbox[2] =3D (int)(left + psettings->chars_per_line
> +					       * (double)prt_char_width + 0.5);
> +	bbox[3] =3D (int)(top + 0.5);
> +    }
> +    else
> +    {
> +	// In landscape printing the fixed point is the bottom left corner so w=
e
> +	// derive the bbox from that point.  We have lpp chars across the media
> +	// and cpl lines up the media.
> +	bbox[1] =3D (int)bottom;
> +	bbox[2] =3D (int)(left + ((psettings->lines_per_page
> +			      + prt_header_height()) * prt_line_height) + 0.5);
> +	bbox[3] =3D (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 printin=
g!
> +    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 'encodin=
g'. If
> +    // that cannot be found then default to "latin1".
> +    // Note: VIM specific encoding header is always skipped.
> +    if (!prt_out_mbyte)
> +    {
> +	p_encoding =3D enc_skip(p_penc);
> +	if (*p_encoding =3D=3D NUL
> +		|| !prt_find_resource((char *)p_encoding, res_encoding))
> +	{
> +	    // 'printencoding' not set or not supported - find alternate
> +	    int		props;
> +
> +	    p_encoding =3D enc_skip(p_enc);
> +	    props =3D 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 =3D (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 =3D enc_skip(p_penc);
> +	if (*p_encoding =3D=3D NUL)
> +	    p_encoding =3D 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 =3D CONV_NONE;
> +    if (!(enc_canon_props(p_enc) & enc_canon_props(p_encoding) & ENC_8BI=
T))
> +    {
> +	// Set up encoding conversion if required
> +	if (FAIL =3D=3D convert_setup(&prt_conv, p_enc, p_encoding))
> +	{
> +	    semsg(_(e_unable_to_convert_to_print_encoding_str), p_encoding);
> +	    goto theend;
> +	}
> +	prt_do_conv =3D TRUE;
> +    }
> +    prt_do_conv =3D prt_conv.vc_type !=3D 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
> ");
> +    prt_write_int(prt_num_copies);
> +    prt_write_string("nc
> ");
> +    prt_write_boolean(prt_duplex);
> +    prt_write_boolean(prt_tumble);
> +    prt_write_string("dt
> ");
> +    prt_write_boolean(prt_collate);
> +    prt_write_string("c
> ");
> +
> +    // 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 =3D (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] !=3D 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] !=3D 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] !=3D 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 =3D !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 *)" ");
> +
> +    if (!prt_file_error && psettings->outfile =3D=3D NULL
> +					&& !got_int && !psettings->user_abort)
> +    {
> +	// Close the file first.
> +	if (prt_ps_fd !=3D NULL)
> +	{
> +	    fclose(prt_ps_fd);
> +	    prt_ps_fd =3D 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) =3D=3D 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
> ");
> +
> +    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] =3D page_num[1] =3D prt_page_num;
> +    prt_dsc_ints("Page", 2, page_num);
> +
> +    prt_dsc_noarg("BeginPageSetup");
> +
> +    prt_write_string("sv
> 0 g
> ");
> +    prt_in_ascii =3D !prt_out_mbyte;
> +    if (prt_out_mbyte)
> +	prt_write_string("CF0 sf
> ");
> +    else
> +	prt_write_string("F0 sf
> ");
> +    prt_fgcol =3D PRCOLOR_BLACK;
> +    prt_bgcol =3D PRCOLOR_WHITE;
> +    prt_font =3D 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
> ");
> +    }
> +
> +    prt_dsc_noarg("EndPageSetup");
> +
> +    // We have reset the font attributes, force setting them again.
> +    prt_curr_bg =3D (long_u)0xffffffff;
> +    prt_curr_fg =3D (long_u)0xffffffff;
> +    prt_curr_bold =3D 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 =3D 0;
> +static float prt_pos_y =3D 0;
> +
> +    void
> +mch_print_start_line(int margin, int page_line)
> +{
> +    prt_pos_x =3D prt_left_margin;
> +    if (margin)
> +	prt_pos_x -=3D prt_number_width;
> +
> +    prt_pos_y =3D prt_top_margin - prt_first_line_height -
> +					page_line * prt_line_height;
> +
> +    prt_attribute_change =3D TRUE;
> +    prt_need_moveto =3D TRUE;
> +    prt_half_width =3D FALSE;
> +}
> +
> +    int
> +mch_print_text_out(char_u *textp, int len UNUSED)
> +{
> +    char_u	*p =3D 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 =3D NULL;
> +
> +    char_width =3D 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 cha=
racters
> +    // in the ASCII range, and the original CID font for everything else=
.
> +    // The problem is that GhostScript still (as of 8.13) does not suppo=
rt
> +    // rearranged fonts even though they have been documented by Adobe f=
or 7
> +    // years!  If they ever do, a lot of this code will disappear.
> +    if (prt_use_courier)
> +    {
> +	in_ascii =3D (len =3D=3D 1 && *p < 0x80);
> +	if (prt_in_ascii)
> +	{
> +	    if (!in_ascii)
> +	    {
> +		// No longer in ASCII range - need to switch font
> +		prt_in_ascii =3D FALSE;
> +		prt_need_font =3D TRUE;
> +		prt_attribute_change =3D TRUE;
> +	    }
> +	}
> +	else if (in_ascii)
> +	{
> +	    // Now in ASCII range - need to switch font
> +	    prt_in_ascii =3D TRUE;
> +	    prt_need_font =3D TRUE;
> +	    prt_attribute_change =3D TRUE;
> +	}
> +    }
> +    if (prt_out_mbyte)
> +    {
> +	half_width =3D ((*mb_ptr2cells)(p) =3D=3D 1);
> +	if (half_width)
> +	    char_width /=3D 2;
> +	if (prt_half_width)
> +	{
> +	    if (!half_width)
> +	    {
> +		prt_half_width =3D FALSE;
> +		prt_pos_x +=3D prt_char_width/4;
> +		prt_need_moveto =3D TRUE;
> +		prt_attribute_change =3D TRUE;
> +	    }
> +	}
> +	else if (half_width)
> +	{
> +	    prt_half_width =3D TRUE;
> +	    prt_pos_x +=3D prt_char_width/4;
> +	    prt_need_moveto =3D TRUE;
> +	    prt_attribute_change =3D 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 =3D 0;
> +
> +	if (prt_need_moveto)
> +	{
> +	    prt_pos_x_moveto =3D prt_pos_x;
> +	    prt_pos_y_moveto =3D prt_pos_y;
> +	    prt_do_moveto =3D TRUE;
> +
> +	    prt_need_moveto =3D 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
> ");
> +	    prt_need_font =3D FALSE;
> +	}
> +	if (prt_need_fgcol)
> +	{
> +	    int     r, g, b;
> +	    r =3D ((unsigned)prt_fgcol & 0xff0000) >> 16;
> +	    g =3D ((unsigned)prt_fgcol & 0xff00) >> 8;
> +	    b =3D prt_fgcol & 0xff;
> +
> +	    prt_write_real(r / 255.0, 3);
> +	    if (r =3D=3D g && g =3D=3D b)
> +		prt_write_string("g
> ");
> +	    else
> +	    {
> +		prt_write_real(g / 255.0, 3);
> +		prt_write_real(b / 255.0, 3);
> +		prt_write_string("r
> ");
> +	    }
> +	    prt_need_fgcol =3D FALSE;
> +	}
> +
> +	if (prt_bgcol !=3D PRCOLOR_WHITE)
> +	{
> +	    prt_new_bgcol =3D prt_bgcol;
> +	    if (prt_need_bgcol)
> +		prt_do_bgcol =3D TRUE;
> +	}
> +	else
> +	    prt_do_bgcol =3D FALSE;
> +	prt_need_bgcol =3D FALSE;
> +
> +	if (prt_need_underline)
> +	    prt_do_underline =3D prt_underline;
> +	prt_need_underline =3D FALSE;
> +
> +	prt_attribute_change =3D FALSE;
> +    }
> +
> +    if (prt_do_conv)
> +    {
> +	// Convert from multi-byte to 8-bit encoding
> +	tofree =3D p =3D string_convert(&prt_conv, p, &len);
> +	if (p =3D=3D NULL)
> +	{
> +	    p =3D (char_u *)"";
> +	    len =3D 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 =3D prt_hexchar[(unsigned)(*p) >> 4];
> +	    ga_append(&prt_ps_buffer, ch);
> +	    ch =3D 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 =3D *p;
> +	if (ch < 32 || ch =3D=3D '(' || ch =3D=3D ')' || ch =3D=3D '\')
> +	{
> +	    // 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 +=3D char_width;
> +    prt_pos_x +=3D char_width;
> +
> +    // The downside of fp - use relative error on right margin check
> +    next_pos =3D prt_pos_x + prt_char_width;
> +    need_break =3D (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 =3D 0;
> +
> +    if (iBold)
> +	font |=3D 0x01;
> +    if (iItalic)
> +	font |=3D 0x02;
> +
> +    if (font !=3D prt_font)
> +    {
> +	prt_font =3D font;
> +	prt_attribute_change =3D TRUE;
> +	prt_need_font =3D TRUE;
> +    }
> +    if (prt_underline !=3D iUnderline)
> +    {
> +	prt_underline =3D iUnderline;
> +	prt_attribute_change =3D TRUE;
> +	prt_need_underline =3D TRUE;
> +    }
> +}
> +
> +    void
> +mch_print_set_bg(long_u bgcol)
> +{
> +    prt_bgcol =3D (int)bgcol;
> +    prt_attribute_change =3D TRUE;
> +    prt_need_bgcol =3D TRUE;
> +}
> +
> +    void
> +mch_print_set_fg(long_u fgcol)
> +{
> +    if (fgcol =3D=3D (long_u)prt_fgcol)
> +	return;
> +
> +    prt_fgcol =3D (int)fgcol;
> +    prt_attribute_change =3D TRUE;
> +    prt_need_fgcol =3D TRUE;
> +}
> +
> +#endif // FEAT_POSTSCRIPT
> diff --git a/src/hardcopy_postscript.h b/src/hardcopy_postscript.h
> new file mode 100644
> index 000000000..fe41df8f8
> --- /dev/null
> +++ b/src/hardcopy_postscript.h
> @@ -0,0 +1,42 @@
> +/* vi:set ts=3D8 sts=3D4 sw=3D4 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 force=
it);
> +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
> diff --git a/src/option.c b/src/option.c
> index aa4c990dc..353082cda 100644
> --- a/src/option.c
> +++ b/src/option.c
> @@ -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
>
> diff --git a/src/option.h b/src/option.h
> index c3df71fca..326b48b63 100644
> --- a/src/option.h
> +++ b/src/option.h
> @@ -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'
> diff --git a/src/optiondefs.h b/src/optiondefs.h
> index f238209a7..187747368 100644
> --- a/src/optiondefs.h
> +++ b/src/optiondefs.h
> @@ -2022,7 +2022,7 @@ static struct vimoption options[] =3D
> #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[] =3D
> 			    {
> # ifdef MSWIN
> 				(char_u *)"Courier_New:h10",
> +# elif defined(FEAT_PRINT_PANGO)
> +				(char_u *)"Courier New 10",
> # else
> 				(char_u *)"courier",
> # endif
> diff --git a/src/proto/gui_gtk4.pro b/src/proto/gui_gtk4.pro
> index 3c5f7a925..d4a9b5283 100644
> --- a/src/proto/gui_gtk4.pro
> +++ b/src/proto/gui_gtk4.pro
> @@ -109,5 +109,6 @@ int gui_mch_dialog(int type, char_u *title, char_u *m=
essage, 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=3Dc : */
> diff --git a/src/proto/hardcopy.pro b/src/proto/hardcopy.pro
> index 8a8c73e56..369aab398 100644
> --- a/src/proto/hardcopy.pro
> +++ b/src/proto/hardcopy.pro
> @@ -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 force=
it);
> -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=3Dc : */
> diff --git a/src/structs.h b/src/structs.h
> index 38037c9d5..b1ec3fa78 100644
> --- a/src/structs.h
> +++ b/src/structs.h
> @@ -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
> diff --git a/src/testdir/test_hardcopy.vim b/src/testdir/test_hardcopy.vi=
m
> index 33ef4bff1..62065139c 100644
> --- a/src/testdir/test_hardcopy.vim
> +++ b/src/testdir/test_hardcopy.vim
> @@ -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=3D0 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=3D' .. bg
>
> -    if has('postscript')
> +    if has('postscript') || has('pango')
>       hardcopy > Xhardcopy_dark_background
>       let lines =3D 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("
> No 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=3Dportrait:y
>   1,50hardcopy > Xhardcopy_printoptions_portrait
>   let lines =3D 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') !=3D -1)
> +  if has('postscript')
> +    call assert_true(match(lines, 'BoundingBox: 59 49 564 800') !=3D -1)
> +  endif
> +  call assert_true(match(lines, 'DocumentMedia: A4') !=3D -1)
> +  call assert_true(match(lines, 'PageMedia: A4') !=3D -1)
> +  call delete('Xhardcopy_printoptions_portrait')
>
>   set printoptions=3Dportrait:n
>   1,50hardcopy > Xhardcopy_printoptions_portrait
>   let lines =3D 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') !=3D -1)
> +  if has('postscript')
> +    call assert_true(match(lines, 'BoundingBox: 59 42 590 756') !=3D -1)
> +  endif
> +  call assert_true(match(lines, 'DocumentMedia: A4') !=3D -1)
> +  call assert_true(match(lines, 'PageMedia: A4') !=3D -1)
> +  call delete('Xhardcopy_printoptions_portrait')
>
>   set printoptions&
>   bwipe
> diff --git a/src/version.c b/src/version.c
> index 7c831d2ae..fedd56dba 100644
> --- a/src/version.c
> +++ b/src/version.c
> @@ -461,6 +461,11 @@ static char *(features[]) =3D
> 	"+packages",
> #else
> 	"-packages",
> +#endif
> +#ifdef FEAT_PRINT_PANGO
> +	"+pango",
> +#else
> +	"-pango",
> #endif
> 	"+path_extra",
> #ifdef FEAT_PERL
> @@ -758,6 +763,8 @@ static char *(features[]) =3D
>
> static int included_patches[] =3D
> {   /* Add new patch number below this line */
> +/**/
> +    898,
> /**/
>     897,
> /**/
> diff --git a/src/vim.h b/src/vim.h
> index 0caf43740..ce402f666 100644
> --- 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_GT=
K4)
> +# if GTK_CHECK_VERSION(4, 14, 0)
> +#  define USE_GTK4_PRINT_DIALOG
> +# endif
> +#endif
> +
> #ifndef FEAT_NETBEANS_INTG
> # undef NBDEBUG
> #endif
>
>

--=20
Hisashi T Fujinaka - [email protected]
BSEE + BSChem + BAEnglish + MSCS + $2.50 =3D coffee

--=20
--=20
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---=20
You received this message because you are subscribed to the Google Groups "=
vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to [email protected].
To view this discussion visit https://groups.google.com/d/msgid/vim_dev/423=
a5932-e2fb-d8d9-51ab-432f16423717%40twofifty.com.