diff options
author | Bram Moolenaar <Bram@vim.org> | 2005-07-24 21:16:51 +0000 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2005-07-24 21:16:51 +0000 |
commit | 81366db6d6668a753c44cacbbe7669b7f37cac37 (patch) | |
tree | afe014176787de8a737a1718025687908a27f249 /src/hardcopy.c | |
parent | 65de869c4f48789ce3a937339468444432dcfb0f (diff) |
updated for version 7.0115
Diffstat (limited to 'src/hardcopy.c')
-rw-r--r-- | src/hardcopy.c | 3650 |
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}, |