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