summaryrefslogtreecommitdiffstats
path: root/src/hardcopy.c
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2005-07-24 21:16:51 +0000
committerBram Moolenaar <Bram@vim.org>2005-07-24 21:16:51 +0000
commit81366db6d6668a753c44cacbbe7669b7f37cac37 (patch)
treeafe014176787de8a737a1718025687908a27f249 /src/hardcopy.c
parent65de869c4f48789ce3a937339468444432dcfb0f (diff)
updated for version 7.0115
Diffstat (limited to 'src/hardcopy.c')
-rw-r--r--src/hardcopy.c3650
1 files changed, 3650 insertions, 0 deletions
diff --git a/src/hardcopy.c b/src/hardcopy.c
new file mode 100644
index 0000000000..c6d157f8f6
--- /dev/null
+++ b/src/hardcopy.c
@@ -0,0 +1,3650 @@
+/* vi:set ts=8 sts=4 sw=4:
+ *
+ * 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.c: printing to paper
+ */
+
+#include "vim.h"
+#include "version.h"
+
+#if defined(FEAT_PRINTER) || defined(PROTO)
+/*
+ * To implement printing on a platform, the following functions must be
+ * defined:
+ *
+ * int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit)
+ * Called once. Code should display printer dialogue (if appropriate) and
+ * determine printer font and margin settings. Reset has_color if the printer
+ * doesn't support colors at all.
+ * Returns FAIL to abort.
+ *
+ * int mch_print_begin(prt_settings_T *settings)
+ * Called to start the print job.
+ * Return FALSE to abort.
+ *
+ * int mch_print_begin_page(char_u *msg)
+ * Called at the start of each page.
+ * "msg" indicates the progress of the print job, can be NULL.
+ * Return FALSE to abort.
+ *
+ * int mch_print_end_page()
+ * Called at the end of each page.
+ * Return FALSE to abort.
+ *
+ * int mch_print_blank_page()
+ * Called to generate a blank page for collated, duplex, multiple copy
+ * document. Return FALSE to abort.
+ *
+ * void mch_print_end(prt_settings_T *psettings)
+ * Called at normal end of print job.
+ *
+ * void mch_print_cleanup()
+ * Called if print job ends normally or is abandoned. Free any memory, close
+ * devices and handles. Also called when mch_print_begin() fails, but not
+ * when mch_print_init() fails.
+ *
+ * void mch_print_set_font(int Bold, int Italic, int Underline);
+ * Called whenever the font style changes.
+ *
+ * void mch_print_set_bg(long bgcol);
+ * Called to set the background color for the following text. Parameter is an
+ * RGB value.
+ *
+ * void mch_print_set_fg(long fgcol);
+ * Called to set the foreground color for the following text. Parameter is an
+ * RGB value.
+ *
+ * mch_print_start_line(int margin, int page_line)
+ * Sets the current position at the start of line "page_line".
+ * If margin is TRUE start in the left margin (for header and line number).
+ *
+ * int mch_print_text_out(char_u *p, int len);
+ * Output one character of text p[len] at the current position.
+ * Return TRUE if there is no room for another character in the same line.
+ *
+ * Note that the generic code has no idea of margins. The machine code should
+ * simply make the page look smaller! The header and the line numbers are
+ * printed in the margin.
+ */
+
+#ifdef FEAT_SYN_HL
+static const long_u cterm_color_8[8] =
+{
+ (long_u)0x000000L, (long_u)0xff0000L, (long_u)0x00ff00L, (long_u)0xffff00L,
+ (long_u)0x0000ffL, (long_u)0xff00ffL, (long_u)0x00ffffL, (long_u)0xffffffL
+};
+
+static const long_u cterm_color_16[16] =
+{
+ (long_u)0x000000L, (long_u)0x0000c0L, (long_u)0x008000L, (long_u)0x004080L,
+ (long_u)0xc00000L, (long_u)0xc000c0L, (long_u)0x808000L, (long_u)0xc0c0c0L,
+ (long_u)0x808080L, (long_u)0x6060ffL, (long_u)0x00ff00L, (long_u)0x00ffffL,
+ (long_u)0xff8080L, (long_u)0xff40ffL, (long_u)0xffff00L, (long_u)0xffffffL
+};
+
+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_MBYTE) && 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
+
+/*
+ * These values determine the print position on a page.
+ */
+typedef struct
+{
+ int lead_spaces; /* remaining spaces for a TAB */
+ int print_pos; /* virtual column for computing TABs */
+ colnr_T column; /* byte column */
+ linenr_T file_line; /* line nr in the buffer */
+ long_u bytes_printed; /* bytes printed so far */
+ int ff; /* seen form feed character */
+} prt_pos_T;
+
+static char_u *parse_list_options __ARGS((char_u *option_str, option_table_T *table, int table_size));
+
+#ifdef FEAT_SYN_HL
+static long_u darken_rgb __ARGS((long_u rgb));
+static long_u prt_get_term_color __ARGS((int colorindex));
+#endif
+static void prt_set_fg __ARGS((long_u fg));
+static void prt_set_bg __ARGS((long_u bg));
+static void prt_set_font __ARGS((int bold, int italic, int underline));
+static void prt_line_number __ARGS((prt_settings_T *psettings, int page_line, linenr_T lnum));
+static void prt_header __ARGS((prt_settings_T *psettings, int pagenum, linenr_T lnum));
+static void prt_message __ARGS((char_u *s));
+static colnr_T hardcopy_line __ARGS((prt_settings_T *psettings, int page_line, prt_pos_T *ppos));
+static void prt_get_attr __ARGS((int hl_id, prt_text_attr_T* pattr, int modec));
+
+/*
+ * Parse 'printoptions' and set the flags in "printer_opts".
+ * Returns an error message or NULL;
+ */
+ char_u *
+parse_printoptions()
+{
+ return parse_list_options(p_popt, printer_opts, OPT_PRINT_NUM_OPTIONS);
+}
+
+#if (defined(FEAT_MBYTE) && defined(FEAT_POSTSCRIPT)) || defined(PROTO)
+/*
+ * Parse 'printoptions' and set the flags in "printer_opts".
+ * Returns an error message or NULL;
+ */
+ char_u *
+parse_printmbfont()
+{
+ return parse_list_options(p_pmfn, mbfont_opts, OPT_MBFONT_NUM_OPTIONS);
+}
+#endif
+
+/*
+ * Parse a list of options in the form
+ * option:value,option:value,option:value
+ *
+ * "value" can start with a number which is parsed out, e.g. margin:12mm
+ *
+ * Returns an error message for an illegal option, NULL otherwise.
+ * Only used for the printer at the moment...
+ */
+ static char_u *
+parse_list_options(option_str, table, table_size)
+ char_u *option_str;
+ option_table_T *table;
+ int table_size;
+{
+ char_u *stringp;
+ char_u *colonp;
+ char_u *commap;
+ char_u *p;
+ int idx = 0; /* init for GCC */
+ int len;
+
+ for (idx = 0; idx < table_size; ++idx)
+ table[idx].present = FALSE;
+
+ /*
+ * Repeat for all comma separated parts.
+ */
+ stringp = option_str;
+ while (*stringp)
+ {
+ colonp = vim_strchr(stringp, ':');
+ if (colonp == NULL)
+ return (char_u *)N_("E550: Missing colon");
+ commap = vim_strchr(stringp, ',');
+ if (commap == NULL)
+ commap = option_str + STRLEN(option_str);
+
+ len = (int)(colonp - stringp);
+
+ for (idx = 0; idx < table_size; ++idx)
+ if (STRNICMP(stringp, table[idx].name, len) == 0)
+ break;
+
+ if (idx == table_size)
+ return (char_u *)N_("E551: Illegal component");
+
+ p = colonp + 1;
+ table[idx].present = TRUE;
+
+ if (table[idx].hasnum)
+ {
+ if (!VIM_ISDIGIT(*p))
+ return (char_u *)N_("E552: digit expected");
+
+ table[idx].number = getdigits(&p); /*advances p*/
+ }
+
+ table[idx].string = p;
+ table[idx].strlen = (int)(commap - p);
+
+ stringp = commap;
+ if (*stringp == ',')
+ ++stringp;
+ }
+
+ return NULL;
+}
+
+
+#ifdef FEAT_SYN_HL
+/*
+ * If using a dark background, the colors will probably be too bright to show
+ * up well on white paper, so reduce their brightness.
+ */
+ static long_u
+darken_rgb(rgb)
+ long_u rgb;
+{
+ return ((rgb >> 17) << 16)
+ + (((rgb & 0xff00) >> 9) << 8)
+ + ((rgb & 0xff) >> 1);
+}
+
+ static long_u
+prt_get_term_color(colorindex)
+ int colorindex;
+{
+ /* TODO: Should check for xterm with 88 or 256 colors. */
+ if (t_colors > 8)
+ return cterm_color_16[colorindex % 16];
+ return cterm_color_8[colorindex % 8];
+}
+
+ static void
+prt_get_attr(hl_id, pattr, modec)
+ int hl_id;
+ prt_text_attr_T *pattr;
+ int modec;
+{
+ int colorindex;
+ long_u fg_color;
+ long_u bg_color;
+ char *color;
+
+ pattr->bold = (highlight_has_attr(hl_id, HL_BOLD, modec) != NULL);
+ pattr->italic = (highlight_has_attr(hl_id, HL_ITALIC, modec) != NULL);
+ pattr->underline = (highlight_has_attr(hl_id, HL_UNDERLINE, modec) != NULL);
+ pattr->undercurl = (highlight_has_attr(hl_id, HL_UNDERCURL, modec) != NULL);
+
+# ifdef FEAT_GUI
+ if (gui.in_use)
+ {
+ bg_color = highlight_gui_color_rgb(hl_id, FALSE);
+ if (bg_color == PRCOLOR_BLACK)
+ bg_color = PRCOLOR_WHITE;
+
+ fg_color = highlight_gui_color_rgb(hl_id, TRUE);
+ }
+ else
+# endif
+ {
+ bg_color = PRCOLOR_WHITE;
+
+ color = (char *)highlight_color(hl_id, (char_u *)"fg", modec);
+ if (color == NULL)
+ colorindex = 0;
+ else
+ colorindex = atoi(color);
+
+ if (colorindex >= 0 && colorindex < t_colors)
+ fg_color = prt_get_term_color(colorindex);
+ else
+ fg_color = PRCOLOR_BLACK;
+ }
+
+ if (fg_color == PRCOLOR_WHITE)
+ fg_color = PRCOLOR_BLACK;
+ else if (*p_bg == 'd')
+ fg_color = darken_rgb(fg_color);
+
+ pattr->fg_color = fg_color;
+ pattr->bg_color = bg_color;
+}
+#endif /* FEAT_SYN_HL */
+
+ static void
+prt_set_fg(fg)
+ long_u fg;
+{
+ if (fg != curr_fg)
+ {
+ curr_fg = fg;
+ mch_print_set_fg(fg);
+ }
+}
+
+ static void
+prt_set_bg(bg)
+ long_u bg;
+{
+ if (bg != curr_bg)
+ {
+ curr_bg = bg;
+ mch_print_set_bg(bg);
+ }
+}
+
+ static void
+prt_set_font(bold, italic, underline)
+ int bold;
+ int italic;
+ int underline;
+{
+ if (curr_bold != bold
+ || curr_italic != italic
+ || curr_underline != underline)
+ {
+ curr_underline = underline;
+ curr_italic = italic;
+ curr_bold = bold;
+ mch_print_set_font(bold, italic, underline);
+ }
+}
+
+/*
+ * Print the line number in the left margin.
+ */
+ static void
+prt_line_number(psettings, page_line, lnum)
+ prt_settings_T *psettings;
+ int page_line;
+ linenr_T lnum;
+{
+ int i;
+ char_u tbuf[20];
+
+ prt_set_fg(psettings->number.fg_color);
+ prt_set_bg(psettings->number.bg_color);
+ prt_set_font(psettings->number.bold, psettings->number.italic, psettings->number.underline);
+ mch_print_start_line(TRUE, page_line);
+
+ /* Leave two spaces between the number and the text; depends on
+ * PRINT_NUMBER_WIDTH. */
+ sprintf((char *)tbuf, "%6ld", (long)lnum);
+ for (i = 0; i < 6; i++)
+ (void)mch_print_text_out(&tbuf[i], 1);
+
+#ifdef FEAT_SYN_HL
+ if (psettings->do_syntax)
+ /* Set colors for next character. */
+ current_syn_id = -1;
+ else
+#endif
+ {
+ /* Set colors and font back to normal. */
+ prt_set_fg(PRCOLOR_BLACK);
+ prt_set_bg(PRCOLOR_WHITE);
+ prt_set_font(FALSE, FALSE, FALSE);
+ }
+}
+
+static linenr_T printer_page_num;
+
+ int
+get_printer_page_num()
+{
+ return printer_page_num;
+}
+
+/*
+ * Get the currently effective header height.
+ */
+ int
+prt_header_height()
+{
+ if (printer_opts[OPT_PRINT_HEADERHEIGHT].present)
+ return printer_opts[OPT_PRINT_HEADERHEIGHT].number;
+ return 2;
+}
+
+/*
+ * Return TRUE if using a line number for printing.
+ */
+ int
+prt_use_number()
+{
+ return (printer_opts[OPT_PRINT_NUMBER].present
+ && TOLOWER_ASC(printer_opts[OPT_PRINT_NUMBER].string[0]) == 'y');
+}
+
+/*
+ * Return the unit used in a margin item in 'printoptions'.
+ * Returns PRT_UNIT_NONE if not recognized.
+ */
+ int
+prt_get_unit(idx)
+ int idx;
+{
+ int u = PRT_UNIT_NONE;
+ int i;
+ static char *(units[4]) = PRT_UNIT_NAMES;
+
+ if (printer_opts[idx].present)
+ for (i = 0; i < 4; ++i)
+ if (STRNICMP(printer_opts[idx].string, units[i], 2) == 0)
+ {
+ u = i;
+ break;
+ }
+ return u;
+}
+
+/*
+ * Print the page header.
+ */
+/*ARGSUSED*/
+ static void
+prt_header(psettings, pagenum, lnum)
+ prt_settings_T *psettings;
+ int pagenum;
+ linenr_T lnum;
+{
+ int width = psettings->chars_per_line;
+ int page_line;
+ char_u *tbuf;
+ char_u *p;
+#ifdef FEAT_MBYTE
+ int l;
+#endif
+
+ /* Also use the space for the line number. */
+ if (prt_use_number())
+ width += PRINT_NUMBER_WIDTH;
+
+ tbuf = alloc(width + IOSIZE);
+ if (tbuf == NULL)
+ return;
+
+#ifdef FEAT_STL_OPT
+ if (*p_header != NUL)
+ {
+ linenr_T tmp_lnum, tmp_topline, tmp_botline;
+
+ /*
+ * Need to (temporarily) set current line number and first/last line
+ * number on the 'window'. Since we don't know how long the page is,
+ * set the first and current line number to the top line, and guess
+ * that the page length is 64.
+ */
+ tmp_lnum = curwin->w_cursor.lnum;
+ tmp_topline = curwin->w_topline;
+ tmp_botline = curwin->w_botline;
+ curwin->w_cursor.lnum = lnum;
+ curwin->w_topline = lnum;
+ curwin->w_botline = lnum + 63;
+ printer_page_num = pagenum;
+
+ build_stl_str_hl(curwin, tbuf, (size_t)(width + IOSIZE),
+ p_header, ' ', width, NULL);
+
+ /* Reset line numbers */
+ curwin->w_cursor.lnum = tmp_lnum;
+ curwin->w_topline = tmp_topline;
+ curwin->w_botline = tmp_botline;
+ }
+ else
+#endif
+ sprintf((char *)tbuf, _("Page %d"), pagenum);
+
+ prt_set_fg(PRCOLOR_BLACK);
+ prt_set_bg(PRCOLOR_WHITE);
+ prt_set_font(TRUE, FALSE, FALSE);
+
+ /* Use a negative line number to indicate printing in the top margin. */
+ page_line = 0 - prt_header_height();
+ mch_print_start_line(TRUE, page_line);
+ for (p = tbuf; *p != NUL; )
+ {
+ if (mch_print_text_out(p,
+#ifdef FEAT_MBYTE
+ (l = (*mb_ptr2len_check)(p))
+#else
+ 1
+#endif
+ ))
+ {
+ ++page_line;
+ if (page_line >= 0) /* out of room in header */
+ break;
+ mch_print_start_line(TRUE, page_line);
+ }
+#ifdef FEAT_MBYTE
+ p += l;
+#else
+ p++;
+#endif
+ }
+
+ vim_free(tbuf);
+
+#ifdef FEAT_SYN_HL
+ if (psettings->do_syntax)
+ /* Set colors for next character. */
+ current_syn_id = -1;
+ else
+#endif
+ {
+ /* Set colors and font back to normal. */
+ prt_set_fg(PRCOLOR_BLACK);
+ prt_set_bg(PRCOLOR_WHITE);
+ prt_set_font(FALSE, FALSE, FALSE);
+ }
+}
+
+/*
+ * Display a print status message.
+ */
+ static void
+prt_message(s)
+ char_u *s;
+{
+ screen_fill((int)Rows - 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0);
+ screen_puts(s, (int)Rows - 1, 0, hl_attr(HLF_R));
+ out_flush();
+}
+
+ void
+ex_hardcopy(eap)
+ exarg_T *eap;
+{
+ linenr_T lnum;
+ int collated_copies, uncollated_copies;
+ prt_settings_T settings;
+ long_u bytes_to_print = 0;
+ int page_line;
+ int jobsplit;
+ int id;
+
+ memset(&settings, 0, sizeof(prt_settings_T));
+ settings.has_color = TRUE;
+
+# ifdef FEAT_POSTSCRIPT
+ if (*eap->arg == '>')
+ {
+ char_u *errormsg = NULL;
+
+ /* Expand things like "%.ps". */
+ if (expand_filename(eap, eap->cmdlinep, &errormsg) == FAIL)
+ {
+ if (errormsg != NULL)
+ EMSG(errormsg);
+ return;
+ }
+ settings.outfile = skipwhite(eap->arg + 1);
+ }
+ else if (*eap->arg != NUL)
+ settings.arguments = eap->arg;
+# endif
+
+ /*
+ * Initialise for printing. Ask the user for settings, unless forceit is
+ * set.
+ * The mch_print_init() code should set up margins if applicable. (It may
+ * not be a real printer - for example the engine might generate HTML or
+ * PS.)
+ */
+ if (mch_print_init(&settings,
+ curbuf->b_fname == NULL
+ ? (char_u *)buf_spname(curbuf)
+ : curbuf->b_sfname == NULL
+ ? curbuf->b_fname
+ : curbuf->b_sfname,
+ eap->forceit) == FAIL)
+ return;
+
+#ifdef FEAT_SYN_HL
+# ifdef FEAT_GUI
+ if (gui.in_use)
+ settings.modec = 'g';
+ else
+# endif
+ if (t_colors > 1)
+ settings.modec = 'c';
+ else
+ settings.modec = 't';
+
+ if (!syntax_present(curbuf))
+ settings.do_syntax = FALSE;
+ else if (printer_opts[OPT_PRINT_SYNTAX].present
+ && TOLOWER_ASC(printer_opts[OPT_PRINT_SYNTAX].string[0]) != 'a')
+ settings.do_syntax =
+ (TOLOWER_ASC(printer_opts[OPT_PRINT_SYNTAX].string[0]) == 'y');
+ else
+ settings.do_syntax = settings.has_color;
+#endif
+
+ /* Set up printing attributes for line numbers */
+ settings.number.fg_color = PRCOLOR_BLACK;
+ settings.number.bg_color = PRCOLOR_WHITE;
+ settings.number.bold = FALSE;
+ settings.number.italic = TRUE;
+ settings.number.underline = FALSE;
+#ifdef FEAT_SYN_HL
+ /*
+ * Syntax highlighting of line numbers.
+ */
+ if (prt_use_number() && settings.do_syntax)
+ {
+ id = syn_name2id((char_u *)"LineNr");
+ if (id > 0)
+ id = syn_get_final_id(id);
+
+ prt_get_attr(id, &settings.number, settings.modec);
+ }
+#endif /* FEAT_SYN_HL */
+
+ /*
+ * Estimate the total lines to be printed
+ */
+ for (lnum = eap->line1; lnum <= eap->line2; lnum++)
+ bytes_to_print += (long_u)STRLEN(skipwhite(ml_get(lnum)));
+ if (bytes_to_print == 0)
+ {
+ MSG(_("No text to be printed"));
+ goto print_fail_no_begin;
+ }
+
+ /* 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_set_fg(PRCOLOR_BLACK);
+ prt_set_bg(PRCOLOR_WHITE);
+ prt_set_font(FALSE, FALSE, FALSE);
+#ifdef FEAT_SYN_HL
+ current_syn_id = -1;
+#endif
+
+ jobsplit = (printer_opts[OPT_PRINT_JOBSPLIT].present
+ && TOLOWER_ASC(printer_opts[OPT_PRINT_JOBSPLIT].string[0]) == 'y');
+
+ if (!mch_print_begin(&settings))
+ goto print_fail_no_begin;
+
+ /*
+ * Loop over collated copies: 1 2 3, 1 2 3, ...
+ */
+ page_count = 0;
+ for (collated_copies = 0;
+ collated_copies < settings.n_collated_copies;
+ collated_copies++)
+ {
+ prt_pos_T prtpos; /* current print position */
+ prt_pos_T page_prtpos; /* print position at page start */
+ int side;
+
+ memset(&page_prtpos, 0, sizeof(prt_pos_T));
+ page_prtpos.file_line = eap->line1;
+ prtpos = page_prtpos;
+
+ if (jobsplit && collated_copies > 0)
+ {
+ /* Splitting jobs: Stop a previous job and start a new one. */
+ mch_print_end(&settings);
+ if (!mch_print_begin(&settings))
+ goto print_fail_no_begin;
+ }
+
+ /*
+ * Loop over all pages in the print job: 1 2 3 ...
+ */
+ for (page_count = 0; prtpos.file_line <= eap->line2; ++page_count)
+ {
+ /*
+ * Loop over uncollated copies: 1 1 1, 2 2 2, 3 3 3, ...
+ * For duplex: 12 12 12 34 34 34, ...
+ */
+ for (uncollated_copies = 0;
+ uncollated_copies < settings.n_uncollated_copies;
+ uncollated_copies++)
+ {
+ /* Set the print position to the start of this page. */
+ prtpos = page_prtpos;
+
+ /*
+ * Do front and rear side of a page.
+ */
+ for (side = 0; side <= settings.duplex; ++side)
+ {
+ /*
+ * Print one page.
+ */
+
+ /* Check for interrupt character every page. */
+ ui_breakcheck();
+ if (got_int || settings.user_abort)
+ goto print_fail;
+
+ sprintf((char *)IObuff, _("Printing page %d (%d%%)"),
+ page_count + 1 + side,
+ prtpos.bytes_printed > 1000000
+ ? (int)(prtpos.bytes_printed /
+ (bytes_to_print / 100))
+ : (int)((prtpos.bytes_printed * 100)
+ / bytes_to_print));
+ if (!mch_print_begin_page(IObuff))
+ goto print_fail;
+
+ if (settings.n_collated_copies > 1)
+ sprintf((char *)IObuff + STRLEN(IObuff),
+ _(" Copy %d of %d"),
+ collated_copies + 1,
+ settings.n_collated_copies);
+ prt_message(IObuff);
+
+ /*
+ * Output header if required
+ */
+ if (prt_header_height() > 0)
+ prt_header(&settings, page_count + 1 + side,
+ prtpos.file_line);
+
+ for (page_line = 0; page_line < settings.lines_per_page;
+ ++page_line)
+ {
+ prtpos.column = hardcopy_line(&settings,
+ page_line, &prtpos);
+ if (prtpos.column == 0)
+ {
+ /* finished a file line */
+ prtpos.bytes_printed +=
+ STRLEN(skipwhite(ml_get(prtpos.file_line)));
+ if (++prtpos.file_line > eap->line2)
+ break; /* reached the end */
+ }
+ else if (prtpos.ff)
+ {
+ /* Line had a formfeed in it - start new page but
+ * stay on the current line */
+ break;
+ }
+ }
+
+ if (!mch_print_end_page())
+ goto print_fail;
+ if (prtpos.file_line > eap->line2)
+ break; /* reached the end */
+ }
+
+ /*
+ * Extra blank page for duplexing with odd number of pages and
+ * more copies to come.
+ */
+ if (prtpos.file_line > eap->line2 && settings.duplex
+ && side == 0
+ && uncollated_copies + 1 < settings.n_uncollated_copies)
+ {
+ if (!mch_print_blank_page())
+ goto print_fail;
+ }
+ }
+ if (settings.duplex && prtpos.file_line <= eap->line2)
+ ++page_count;
+
+ /* Remember the position where the next page starts. */
+ page_prtpos = prtpos;
+ }
+
+ vim_snprintf((char *)IObuff, IOSIZE, _("Printed: %s"),
+ settings.jobname);
+ prt_message(IObuff);
+ }
+
+print_fail:
+ if (got_int || settings.user_abort)
+ {
+ sprintf((char *)IObuff, "%s", _("Printing aborted"));
+ prt_message(IObuff);
+ }
+ mch_print_end(&settings);
+
+print_fail_no_begin:
+ mch_print_cleanup();
+}
+
+/*
+ * Print one page line.
+ * Return the next column to print, or zero if the line is finished.
+ */
+ static colnr_T
+hardcopy_line(psettings, page_line, ppos)
+ prt_settings_T *psettings;
+ int page_line;
+ prt_pos_T *ppos;
+{
+ colnr_T col;
+ char_u *line;
+ int need_break = FALSE;
+ int outputlen;
+ int tab_spaces;
+ long_u print_pos;
+#ifdef FEAT_SYN_HL
+ prt_text_attr_T attr;
+ int id;
+#endif
+
+ if (ppos->column == 0 || ppos->ff)
+ {
+ print_pos = 0;
+ tab_spaces = 0;
+ if (!ppos->ff && prt_use_number())
+ prt_line_number(psettings, page_line, ppos->file_line);
+ ppos->ff = FALSE;
+ }
+ else
+ {
+ /* left over from wrap halfway a tab */
+ print_pos = ppos->print_pos;
+ tab_spaces = ppos->lead_spaces;
+ }
+
+ mch_print_start_line(0, page_line);
+ line = ml_get(ppos->file_line);
+
+ /*
+ * Loop over the columns until the end of the file line or right margin.
+ */
+ for (col = ppos->column; line[col] != NUL && !need_break; col += outputlen)
+ {
+ outputlen = 1;
+#ifdef FEAT_MBYTE
+ if (has_mbyte && (outputlen = (*mb_ptr2len_check)(line + col)) < 1)
+ outputlen = 1;
+#endif
+#ifdef FEAT_SYN_HL
+ /*
+ * syntax highlighting stuff.
+ */
+ if (psettings->do_syntax)
+ {
+ id = syn_get_id(ppos->file_line, col, 1, NULL);
+ if (id > 0)
+ id = syn_get_final_id(id);
+ else
+ id = 0;
+ /* Get the line again, a multi-line regexp may invalidate it. */
+ line = ml_get(ppos->file_line);
+
+ if (id != current_syn_id)
+ {
+ current_syn_id = id;
+ prt_get_attr(id, &attr, psettings->modec);
+ prt_set_font(attr.bold, attr.italic, attr.underline);
+ prt_set_fg(attr.fg_color);
+ prt_set_bg(attr.bg_color);
+ }
+ }
+#endif /* FEAT_SYN_HL */
+
+ /*
+ * Appropriately expand any tabs to spaces.
+ */
+ if (line[col] == TAB || tab_spaces != 0)
+ {
+ if (tab_spaces == 0)
+ tab_spaces = curbuf->b_p_ts - (print_pos % curbuf->b_p_ts);
+
+ while (tab_spaces > 0)
+ {
+ need_break = mch_print_text_out((char_u *)" ", 1);
+ print_pos++;
+ tab_spaces--;
+ if (need_break)
+ break;
+ }
+ /* Keep the TAB if we didn't finish it. */
+ if (need_break && tab_spaces > 0)
+ break;
+ }
+ else if (line[col] == FF
+ && printer_opts[OPT_PRINT_FORMFEED].present
+ && TOLOWER_ASC(printer_opts[OPT_PRINT_FORMFEED].string[0])
+ == 'y')
+ {
+ ppos->ff = TRUE;
+ need_break = 1;
+ }
+ else
+ {
+ need_break = mch_print_text_out(line + col, outputlen);
+#ifdef FEAT_MBYTE
+ if (has_mbyte)
+ print_pos += (*mb_ptr2cells)(line + col);
+ else
+#endif
+ print_pos++;
+ }
+ }
+
+ ppos->lead_spaces = tab_spaces;
+ ppos->print_pos = print_pos;
+
+ /*
+ * Start next line of file if we clip lines, or have reached end of the
+ * line, unless we are doing a formfeed.
+ */
+ if (!ppos->ff
+ && (line[col] == NUL
+ || (printer_opts[OPT_PRINT_WRAP].present
+ && TOLOWER_ASC(printer_opts[OPT_PRINT_WRAP].string[0])
+ == 'n')))
+ return 0;
+ return col;
+}
+
+# if defined(FEAT_POSTSCRIPT) || defined(PROTO)
+
+/*
+ * 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 NUM_ELEMENTS(arr) (sizeof(arr)/sizeof((arr)[0]))
+
+#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 (sizeof(prt_mediasize) / sizeof(struct prt_mediasize_S))
+
+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"}
+};
+
+#ifdef FEAT_MBYTE
+/* 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}
+};
+#endif
+
+/* 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;
+};
+
+#ifdef FEAT_MBYTE
+
+#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