xref: /vim-8.2.3635/src/hardcopy.c (revision 94688b8a)
1 /* vi:set ts=8 sts=4 sw=4 noet:
2  *
3  * VIM - Vi IMproved	by Bram Moolenaar
4  *
5  * Do ":help uganda"  in Vim to read copying and usage conditions.
6  * Do ":help credits" in Vim to see a list of people who contributed.
7  * See README.txt for an overview of the Vim source code.
8  */
9 
10 /*
11  * hardcopy.c: printing to paper
12  */
13 
14 #include "vim.h"
15 #include "version.h"
16 
17 #if defined(FEAT_PRINTER) || defined(PROTO)
18 /*
19  * To implement printing on a platform, the following functions must be
20  * defined:
21  *
22  * int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit)
23  * Called once.  Code should display printer dialogue (if appropriate) and
24  * determine printer font and margin settings.  Reset has_color if the printer
25  * doesn't support colors at all.
26  * Returns FAIL to abort.
27  *
28  * int mch_print_begin(prt_settings_T *settings)
29  * Called to start the print job.
30  * Return FALSE to abort.
31  *
32  * int mch_print_begin_page(char_u *msg)
33  * Called at the start of each page.
34  * "msg" indicates the progress of the print job, can be NULL.
35  * Return FALSE to abort.
36  *
37  * int mch_print_end_page()
38  * Called at the end of each page.
39  * Return FALSE to abort.
40  *
41  * int mch_print_blank_page()
42  * Called to generate a blank page for collated, duplex, multiple copy
43  * document.  Return FALSE to abort.
44  *
45  * void mch_print_end(prt_settings_T *psettings)
46  * Called at normal end of print job.
47  *
48  * void mch_print_cleanup()
49  * Called if print job ends normally or is abandoned. Free any memory, close
50  * devices and handles.  Also called when mch_print_begin() fails, but not
51  * when mch_print_init() fails.
52  *
53  * void mch_print_set_font(int Bold, int Italic, int Underline);
54  * Called whenever the font style changes.
55  *
56  * void mch_print_set_bg(long_u bgcol);
57  * Called to set the background color for the following text. Parameter is an
58  * RGB value.
59  *
60  * void mch_print_set_fg(long_u fgcol);
61  * Called to set the foreground color for the following text. Parameter is an
62  * RGB value.
63  *
64  * mch_print_start_line(int margin, int page_line)
65  * Sets the current position at the start of line "page_line".
66  * If margin is TRUE start in the left margin (for header and line number).
67  *
68  * int mch_print_text_out(char_u *p, int len);
69  * Output one character of text p[len] at the current position.
70  * Return TRUE if there is no room for another character in the same line.
71  *
72  * Note that the generic code has no idea of margins. The machine code should
73  * simply make the page look smaller!  The header and the line numbers are
74  * printed in the margin.
75  */
76 
77 #ifdef FEAT_SYN_HL
78 static const long_u  cterm_color_8[8] =
79 {
80     (long_u)0x000000L, (long_u)0xff0000L, (long_u)0x00ff00L, (long_u)0xffff00L,
81     (long_u)0x0000ffL, (long_u)0xff00ffL, (long_u)0x00ffffL, (long_u)0xffffffL
82 };
83 
84 static const long_u  cterm_color_16[16] =
85 {
86     (long_u)0x000000L, (long_u)0x0000c0L, (long_u)0x008000L, (long_u)0x004080L,
87     (long_u)0xc00000L, (long_u)0xc000c0L, (long_u)0x808000L, (long_u)0xc0c0c0L,
88     (long_u)0x808080L, (long_u)0x6060ffL, (long_u)0x00ff00L, (long_u)0x00ffffL,
89     (long_u)0xff8080L, (long_u)0xff40ffL, (long_u)0xffff00L, (long_u)0xffffffL
90 };
91 
92 static int		current_syn_id;
93 #endif
94 
95 #define PRCOLOR_BLACK	(long_u)0
96 #define PRCOLOR_WHITE	(long_u)0xFFFFFFL
97 
98 static int	curr_italic;
99 static int	curr_bold;
100 static int	curr_underline;
101 static long_u	curr_bg;
102 static long_u	curr_fg;
103 static int	page_count;
104 
105 #if defined(FEAT_POSTSCRIPT)
106 # define OPT_MBFONT_USECOURIER  0
107 # define OPT_MBFONT_ASCII       1
108 # define OPT_MBFONT_REGULAR     2
109 # define OPT_MBFONT_BOLD	3
110 # define OPT_MBFONT_OBLIQUE     4
111 # define OPT_MBFONT_BOLDOBLIQUE 5
112 # define OPT_MBFONT_NUM_OPTIONS 6
113 
114 static option_table_T mbfont_opts[OPT_MBFONT_NUM_OPTIONS] =
115 {
116     {"c",	FALSE, 0, NULL, 0, FALSE},
117     {"a",	FALSE, 0, NULL, 0, FALSE},
118     {"r",	FALSE, 0, NULL, 0, FALSE},
119     {"b",	FALSE, 0, NULL, 0, FALSE},
120     {"i",	FALSE, 0, NULL, 0, FALSE},
121     {"o",	FALSE, 0, NULL, 0, FALSE},
122 };
123 #endif
124 
125 /*
126  * These values determine the print position on a page.
127  */
128 typedef struct
129 {
130     int		lead_spaces;	    /* remaining spaces for a TAB */
131     int		print_pos;	    /* virtual column for computing TABs */
132     colnr_T	column;		    /* byte column */
133     linenr_T	file_line;	    /* line nr in the buffer */
134     long_u	bytes_printed;	    /* bytes printed so far */
135     int		ff;		    /* seen form feed character */
136 } prt_pos_T;
137 
138 static char *parse_list_options(char_u *option_str, option_table_T *table, int table_size);
139 
140 static colnr_T hardcopy_line(prt_settings_T *psettings, int page_line, prt_pos_T *ppos);
141 
142 /*
143  * Parse 'printoptions' and set the flags in "printer_opts".
144  * Returns an error message or NULL;
145  */
146     char *
147 parse_printoptions(void)
148 {
149     return parse_list_options(p_popt, printer_opts, OPT_PRINT_NUM_OPTIONS);
150 }
151 
152 #if defined(FEAT_POSTSCRIPT) || defined(PROTO)
153 /*
154  * Parse 'printmbfont' and set the flags in "mbfont_opts".
155  * Returns an error message or NULL;
156  */
157     char *
158 parse_printmbfont(void)
159 {
160     return parse_list_options(p_pmfn, mbfont_opts, OPT_MBFONT_NUM_OPTIONS);
161 }
162 #endif
163 
164 /*
165  * Parse a list of options in the form
166  * option:value,option:value,option:value
167  *
168  * "value" can start with a number which is parsed out, e.g.  margin:12mm
169  *
170  * Returns an error message for an illegal option, NULL otherwise.
171  * Only used for the printer at the moment...
172  */
173     static char *
174 parse_list_options(
175     char_u		*option_str,
176     option_table_T	*table,
177     int			table_size)
178 {
179     option_table_T *old_opts;
180     char	*ret = NULL;
181     char_u	*stringp;
182     char_u	*colonp;
183     char_u	*commap;
184     char_u	*p;
185     int		idx = 0;		/* init for GCC */
186     int		len;
187 
188     /* Save the old values, so that they can be restored in case of an error. */
189     old_opts = (option_table_T *)alloc(sizeof(option_table_T) * table_size);
190     if (old_opts == NULL)
191 	return NULL;
192 
193     for (idx = 0; idx < table_size; ++idx)
194     {
195 	old_opts[idx] = table[idx];
196 	table[idx].present = FALSE;
197     }
198 
199     /*
200      * Repeat for all comma separated parts.
201      */
202     stringp = option_str;
203     while (*stringp)
204     {
205 	colonp = vim_strchr(stringp, ':');
206 	if (colonp == NULL)
207 	{
208 	    ret = N_("E550: Missing colon");
209 	    break;
210 	}
211 	commap = vim_strchr(stringp, ',');
212 	if (commap == NULL)
213 	    commap = option_str + STRLEN(option_str);
214 
215 	len = (int)(colonp - stringp);
216 
217 	for (idx = 0; idx < table_size; ++idx)
218 	    if (STRNICMP(stringp, table[idx].name, len) == 0)
219 		break;
220 
221 	if (idx == table_size)
222 	{
223 	    ret = N_("E551: Illegal component");
224 	    break;
225 	}
226 	p = colonp + 1;
227 	table[idx].present = TRUE;
228 
229 	if (table[idx].hasnum)
230 	{
231 	    if (!VIM_ISDIGIT(*p))
232 	    {
233 		ret = N_("E552: digit expected");
234 		break;
235 	    }
236 
237 	    table[idx].number = getdigits(&p); /*advances p*/
238 	}
239 
240 	table[idx].string = p;
241 	table[idx].strlen = (int)(commap - p);
242 
243 	stringp = commap;
244 	if (*stringp == ',')
245 	    ++stringp;
246     }
247 
248     if (ret != NULL)
249     {
250 	/* Restore old options in case of error */
251 	for (idx = 0; idx < table_size; ++idx)
252 	    table[idx] = old_opts[idx];
253     }
254     vim_free(old_opts);
255     return ret;
256 }
257 
258 
259 #ifdef FEAT_SYN_HL
260 /*
261  * If using a dark background, the colors will probably be too bright to show
262  * up well on white paper, so reduce their brightness.
263  */
264     static long_u
265 darken_rgb(long_u rgb)
266 {
267     return	((rgb >> 17) << 16)
268 	    +	(((rgb & 0xff00) >> 9) << 8)
269 	    +	((rgb & 0xff) >> 1);
270 }
271 
272     static long_u
273 prt_get_term_color(int colorindex)
274 {
275     /* TODO: Should check for xterm with 88 or 256 colors. */
276     if (t_colors > 8)
277 	return cterm_color_16[colorindex % 16];
278     return cterm_color_8[colorindex % 8];
279 }
280 
281     static void
282 prt_get_attr(
283     int			hl_id,
284     prt_text_attr_T	*pattr,
285     int			modec)
286 {
287     int     colorindex;
288     long_u  fg_color;
289     long_u  bg_color;
290     char    *color;
291 
292     pattr->bold = (highlight_has_attr(hl_id, HL_BOLD, modec) != NULL);
293     pattr->italic = (highlight_has_attr(hl_id, HL_ITALIC, modec) != NULL);
294     pattr->underline = (highlight_has_attr(hl_id, HL_UNDERLINE, modec) != NULL);
295     pattr->undercurl = (highlight_has_attr(hl_id, HL_UNDERCURL, modec) != NULL);
296 
297 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
298     if (USE_24BIT)
299     {
300 	bg_color = highlight_gui_color_rgb(hl_id, FALSE);
301 	if (bg_color == PRCOLOR_BLACK)
302 	    bg_color = PRCOLOR_WHITE;
303 
304 	fg_color = highlight_gui_color_rgb(hl_id, TRUE);
305     }
306     else
307 # endif
308     {
309 	bg_color = PRCOLOR_WHITE;
310 
311 	color = (char *)highlight_color(hl_id, (char_u *)"fg", modec);
312 	if (color == NULL)
313 	    colorindex = 0;
314 	else
315 	    colorindex = atoi(color);
316 
317 	if (colorindex >= 0 && colorindex < t_colors)
318 	    fg_color = prt_get_term_color(colorindex);
319 	else
320 	    fg_color = PRCOLOR_BLACK;
321     }
322 
323     if (fg_color == PRCOLOR_WHITE)
324 	fg_color = PRCOLOR_BLACK;
325     else if (*p_bg == 'd')
326 	fg_color = darken_rgb(fg_color);
327 
328     pattr->fg_color = fg_color;
329     pattr->bg_color = bg_color;
330 }
331 #endif /* FEAT_SYN_HL */
332 
333     static void
334 prt_set_fg(long_u fg)
335 {
336     if (fg != curr_fg)
337     {
338 	curr_fg = fg;
339 	mch_print_set_fg(fg);
340     }
341 }
342 
343     static void
344 prt_set_bg(long_u bg)
345 {
346     if (bg != curr_bg)
347     {
348 	curr_bg = bg;
349 	mch_print_set_bg(bg);
350     }
351 }
352 
353     static void
354 prt_set_font(int bold, int italic, int underline)
355 {
356     if (curr_bold != bold
357 	    || curr_italic != italic
358 	    || curr_underline != underline)
359     {
360 	curr_underline = underline;
361 	curr_italic = italic;
362 	curr_bold = bold;
363 	mch_print_set_font(bold, italic, underline);
364     }
365 }
366 
367 /*
368  * Print the line number in the left margin.
369  */
370     static void
371 prt_line_number(
372     prt_settings_T *psettings,
373     int		page_line,
374     linenr_T	lnum)
375 {
376     int		i;
377     char_u	tbuf[20];
378 
379     prt_set_fg(psettings->number.fg_color);
380     prt_set_bg(psettings->number.bg_color);
381     prt_set_font(psettings->number.bold, psettings->number.italic, psettings->number.underline);
382     mch_print_start_line(TRUE, page_line);
383 
384     /* Leave two spaces between the number and the text; depends on
385      * PRINT_NUMBER_WIDTH. */
386     sprintf((char *)tbuf, "%6ld", (long)lnum);
387     for (i = 0; i < 6; i++)
388 	(void)mch_print_text_out(&tbuf[i], 1);
389 
390 #ifdef FEAT_SYN_HL
391     if (psettings->do_syntax)
392 	/* Set colors for next character. */
393 	current_syn_id = -1;
394     else
395 #endif
396     {
397 	/* Set colors and font back to normal. */
398 	prt_set_fg(PRCOLOR_BLACK);
399 	prt_set_bg(PRCOLOR_WHITE);
400 	prt_set_font(FALSE, FALSE, FALSE);
401     }
402 }
403 
404 /*
405  * Get the currently effective header height.
406  */
407     int
408 prt_header_height(void)
409 {
410     if (printer_opts[OPT_PRINT_HEADERHEIGHT].present)
411 	return printer_opts[OPT_PRINT_HEADERHEIGHT].number;
412     return 2;
413 }
414 
415 /*
416  * Return TRUE if using a line number for printing.
417  */
418     int
419 prt_use_number(void)
420 {
421     return (printer_opts[OPT_PRINT_NUMBER].present
422 	    && TOLOWER_ASC(printer_opts[OPT_PRINT_NUMBER].string[0]) == 'y');
423 }
424 
425 /*
426  * Return the unit used in a margin item in 'printoptions'.
427  * Returns PRT_UNIT_NONE if not recognized.
428  */
429     int
430 prt_get_unit(int idx)
431 {
432     int		u = PRT_UNIT_NONE;
433     int		i;
434     static char *(units[4]) = PRT_UNIT_NAMES;
435 
436     if (printer_opts[idx].present)
437 	for (i = 0; i < 4; ++i)
438 	    if (STRNICMP(printer_opts[idx].string, units[i], 2) == 0)
439 	    {
440 		u = i;
441 		break;
442 	    }
443     return u;
444 }
445 
446 /*
447  * Print the page header.
448  */
449     static void
450 prt_header(
451     prt_settings_T  *psettings,
452     int		pagenum,
453     linenr_T	lnum UNUSED)
454 {
455     int		width = psettings->chars_per_line;
456     int		page_line;
457     char_u	*tbuf;
458     char_u	*p;
459     int		l;
460 
461     /* Also use the space for the line number. */
462     if (prt_use_number())
463 	width += PRINT_NUMBER_WIDTH;
464 
465     tbuf = alloc(width + IOSIZE);
466     if (tbuf == NULL)
467 	return;
468 
469 #ifdef FEAT_STL_OPT
470     if (*p_header != NUL)
471     {
472 	linenr_T	tmp_lnum, tmp_topline, tmp_botline;
473 	int		use_sandbox = FALSE;
474 
475 	/*
476 	 * Need to (temporarily) set current line number and first/last line
477 	 * number on the 'window'.  Since we don't know how long the page is,
478 	 * set the first and current line number to the top line, and guess
479 	 * that the page length is 64.
480 	 */
481 	tmp_lnum = curwin->w_cursor.lnum;
482 	tmp_topline = curwin->w_topline;
483 	tmp_botline = curwin->w_botline;
484 	curwin->w_cursor.lnum = lnum;
485 	curwin->w_topline = lnum;
486 	curwin->w_botline = lnum + 63;
487 	printer_page_num = pagenum;
488 
489 # ifdef FEAT_EVAL
490 	use_sandbox = was_set_insecurely((char_u *)"printheader", 0);
491 # endif
492 	build_stl_str_hl(curwin, tbuf, (size_t)(width + IOSIZE),
493 						  p_header, use_sandbox,
494 						  ' ', width, NULL, NULL);
495 
496 	/* Reset line numbers */
497 	curwin->w_cursor.lnum = tmp_lnum;
498 	curwin->w_topline = tmp_topline;
499 	curwin->w_botline = tmp_botline;
500     }
501     else
502 #endif
503 	sprintf((char *)tbuf, _("Page %d"), pagenum);
504 
505     prt_set_fg(PRCOLOR_BLACK);
506     prt_set_bg(PRCOLOR_WHITE);
507     prt_set_font(TRUE, FALSE, FALSE);
508 
509     /* Use a negative line number to indicate printing in the top margin. */
510     page_line = 0 - prt_header_height();
511     mch_print_start_line(TRUE, page_line);
512     for (p = tbuf; *p != NUL; )
513     {
514 	if (mch_print_text_out(p, (l = (*mb_ptr2len)(p))))
515 	{
516 	    ++page_line;
517 	    if (page_line >= 0) /* out of room in header */
518 		break;
519 	    mch_print_start_line(TRUE, page_line);
520 	}
521 	p += l;
522     }
523 
524     vim_free(tbuf);
525 
526 #ifdef FEAT_SYN_HL
527     if (psettings->do_syntax)
528 	/* Set colors for next character. */
529 	current_syn_id = -1;
530     else
531 #endif
532     {
533 	/* Set colors and font back to normal. */
534 	prt_set_fg(PRCOLOR_BLACK);
535 	prt_set_bg(PRCOLOR_WHITE);
536 	prt_set_font(FALSE, FALSE, FALSE);
537     }
538 }
539 
540 /*
541  * Display a print status message.
542  */
543     static void
544 prt_message(char_u *s)
545 {
546     screen_fill((int)Rows - 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0);
547     screen_puts(s, (int)Rows - 1, 0, HL_ATTR(HLF_R));
548     out_flush();
549 }
550 
551     void
552 ex_hardcopy(exarg_T *eap)
553 {
554     linenr_T		lnum;
555     int			collated_copies, uncollated_copies;
556     prt_settings_T	settings;
557     long_u		bytes_to_print = 0;
558     int			page_line;
559     int			jobsplit;
560 
561     vim_memset(&settings, 0, sizeof(prt_settings_T));
562     settings.has_color = TRUE;
563 
564 # ifdef FEAT_POSTSCRIPT
565     if (*eap->arg == '>')
566     {
567 	char	*errormsg = NULL;
568 
569 	/* Expand things like "%.ps". */
570 	if (expand_filename(eap, eap->cmdlinep, &errormsg) == FAIL)
571 	{
572 	    if (errormsg != NULL)
573 		emsg(errormsg);
574 	    return;
575 	}
576 	settings.outfile = skipwhite(eap->arg + 1);
577     }
578     else if (*eap->arg != NUL)
579 	settings.arguments = eap->arg;
580 # endif
581 
582     /*
583      * Initialise for printing.  Ask the user for settings, unless forceit is
584      * set.
585      * The mch_print_init() code should set up margins if applicable. (It may
586      * not be a real printer - for example the engine might generate HTML or
587      * PS.)
588      */
589     if (mch_print_init(&settings,
590 			curbuf->b_fname == NULL
591 			    ? (char_u *)buf_spname(curbuf)
592 			    : curbuf->b_sfname == NULL
593 				? curbuf->b_fname
594 				: curbuf->b_sfname,
595 			eap->forceit) == FAIL)
596 	return;
597 
598 #ifdef FEAT_SYN_HL
599 # ifdef  FEAT_GUI
600     if (gui.in_use)
601 	settings.modec = 'g';
602     else
603 # endif
604 	if (t_colors > 1)
605 	    settings.modec = 'c';
606 	else
607 	    settings.modec = 't';
608 
609     if (!syntax_present(curwin))
610 	settings.do_syntax = FALSE;
611     else if (printer_opts[OPT_PRINT_SYNTAX].present
612 	    && TOLOWER_ASC(printer_opts[OPT_PRINT_SYNTAX].string[0]) != 'a')
613 	settings.do_syntax =
614 	       (TOLOWER_ASC(printer_opts[OPT_PRINT_SYNTAX].string[0]) == 'y');
615     else
616 	settings.do_syntax = settings.has_color;
617 #endif
618 
619     /* Set up printing attributes for line numbers */
620     settings.number.fg_color = PRCOLOR_BLACK;
621     settings.number.bg_color = PRCOLOR_WHITE;
622     settings.number.bold = FALSE;
623     settings.number.italic = TRUE;
624     settings.number.underline = FALSE;
625 #ifdef FEAT_SYN_HL
626     /*
627      * Syntax highlighting of line numbers.
628      */
629     if (prt_use_number() && settings.do_syntax)
630     {
631 	int		id;
632 
633 	id = syn_name2id((char_u *)"LineNr");
634 	if (id > 0)
635 	    id = syn_get_final_id(id);
636 
637 	prt_get_attr(id, &settings.number, settings.modec);
638     }
639 #endif
640 
641     /*
642      * Estimate the total lines to be printed
643      */
644     for (lnum = eap->line1; lnum <= eap->line2; lnum++)
645 	bytes_to_print += (long_u)STRLEN(skipwhite(ml_get(lnum)));
646     if (bytes_to_print == 0)
647     {
648 	msg(_("No text to be printed"));
649 	goto print_fail_no_begin;
650     }
651 
652     /* Set colors and font to normal. */
653     curr_bg = (long_u)0xffffffffL;
654     curr_fg = (long_u)0xffffffffL;
655     curr_italic = MAYBE;
656     curr_bold = MAYBE;
657     curr_underline = MAYBE;
658 
659     prt_set_fg(PRCOLOR_BLACK);
660     prt_set_bg(PRCOLOR_WHITE);
661     prt_set_font(FALSE, FALSE, FALSE);
662 #ifdef FEAT_SYN_HL
663     current_syn_id = -1;
664 #endif
665 
666     jobsplit = (printer_opts[OPT_PRINT_JOBSPLIT].present
667 	   && TOLOWER_ASC(printer_opts[OPT_PRINT_JOBSPLIT].string[0]) == 'y');
668 
669     if (!mch_print_begin(&settings))
670 	goto print_fail_no_begin;
671 
672     /*
673      * Loop over collated copies: 1 2 3, 1 2 3, ...
674      */
675     page_count = 0;
676     for (collated_copies = 0;
677 	    collated_copies < settings.n_collated_copies;
678 	    collated_copies++)
679     {
680 	prt_pos_T	prtpos;		/* current print position */
681 	prt_pos_T	page_prtpos;	/* print position at page start */
682 	int		side;
683 
684 	vim_memset(&page_prtpos, 0, sizeof(prt_pos_T));
685 	page_prtpos.file_line = eap->line1;
686 	prtpos = page_prtpos;
687 
688 	if (jobsplit && collated_copies > 0)
689 	{
690 	    /* Splitting jobs: Stop a previous job and start a new one. */
691 	    mch_print_end(&settings);
692 	    if (!mch_print_begin(&settings))
693 		goto print_fail_no_begin;
694 	}
695 
696 	/*
697 	 * Loop over all pages in the print job: 1 2 3 ...
698 	 */
699 	for (page_count = 0; prtpos.file_line <= eap->line2; ++page_count)
700 	{
701 	    /*
702 	     * Loop over uncollated copies: 1 1 1, 2 2 2, 3 3 3, ...
703 	     * For duplex: 12 12 12 34 34 34, ...
704 	     */
705 	    for (uncollated_copies = 0;
706 		    uncollated_copies < settings.n_uncollated_copies;
707 		    uncollated_copies++)
708 	    {
709 		/* Set the print position to the start of this page. */
710 		prtpos = page_prtpos;
711 
712 		/*
713 		 * Do front and rear side of a page.
714 		 */
715 		for (side = 0; side <= settings.duplex; ++side)
716 		{
717 		    /*
718 		     * Print one page.
719 		     */
720 
721 		    /* Check for interrupt character every page. */
722 		    ui_breakcheck();
723 		    if (got_int || settings.user_abort)
724 			goto print_fail;
725 
726 		    sprintf((char *)IObuff, _("Printing page %d (%d%%)"),
727 			    page_count + 1 + side,
728 			    prtpos.bytes_printed > 1000000
729 				? (int)(prtpos.bytes_printed /
730 						       (bytes_to_print / 100))
731 				: (int)((prtpos.bytes_printed * 100)
732 							   / bytes_to_print));
733 		    if (!mch_print_begin_page(IObuff))
734 			goto print_fail;
735 
736 		    if (settings.n_collated_copies > 1)
737 			sprintf((char *)IObuff + STRLEN(IObuff),
738 				_(" Copy %d of %d"),
739 				collated_copies + 1,
740 				settings.n_collated_copies);
741 		    prt_message(IObuff);
742 
743 		    /*
744 		     * Output header if required
745 		     */
746 		    if (prt_header_height() > 0)
747 			prt_header(&settings, page_count + 1 + side,
748 							prtpos.file_line);
749 
750 		    for (page_line = 0; page_line < settings.lines_per_page;
751 								  ++page_line)
752 		    {
753 			prtpos.column = hardcopy_line(&settings,
754 							   page_line, &prtpos);
755 			if (prtpos.column == 0)
756 			{
757 			    /* finished a file line */
758 			    prtpos.bytes_printed +=
759 				  STRLEN(skipwhite(ml_get(prtpos.file_line)));
760 			    if (++prtpos.file_line > eap->line2)
761 				break; /* reached the end */
762 			}
763 			else if (prtpos.ff)
764 			{
765 			    /* Line had a formfeed in it - start new page but
766 			     * stay on the current line */
767 			    break;
768 			}
769 		    }
770 
771 		    if (!mch_print_end_page())
772 			goto print_fail;
773 		    if (prtpos.file_line > eap->line2)
774 			break; /* reached the end */
775 		}
776 
777 		/*
778 		 * Extra blank page for duplexing with odd number of pages and
779 		 * more copies to come.
780 		 */
781 		if (prtpos.file_line > eap->line2 && settings.duplex
782 								 && side == 0
783 		    && uncollated_copies + 1 < settings.n_uncollated_copies)
784 		{
785 		    if (!mch_print_blank_page())
786 			goto print_fail;
787 		}
788 	    }
789 	    if (settings.duplex && prtpos.file_line <= eap->line2)
790 		++page_count;
791 
792 	    /* Remember the position where the next page starts. */
793 	    page_prtpos = prtpos;
794 	}
795 
796 	vim_snprintf((char *)IObuff, IOSIZE, _("Printed: %s"),
797 							    settings.jobname);
798 	prt_message(IObuff);
799     }
800 
801 print_fail:
802     if (got_int || settings.user_abort)
803     {
804 	sprintf((char *)IObuff, "%s", _("Printing aborted"));
805 	prt_message(IObuff);
806     }
807     mch_print_end(&settings);
808 
809 print_fail_no_begin:
810     mch_print_cleanup();
811 }
812 
813 /*
814  * Print one page line.
815  * Return the next column to print, or zero if the line is finished.
816  */
817     static colnr_T
818 hardcopy_line(
819     prt_settings_T	*psettings,
820     int			page_line,
821     prt_pos_T		*ppos)
822 {
823     colnr_T	col;
824     char_u	*line;
825     int		need_break = FALSE;
826     int		outputlen;
827     int		tab_spaces;
828     long_u	print_pos;
829 #ifdef FEAT_SYN_HL
830     prt_text_attr_T attr;
831     int		id;
832 #endif
833 
834     if (ppos->column == 0 || ppos->ff)
835     {
836 	print_pos = 0;
837 	tab_spaces = 0;
838 	if (!ppos->ff && prt_use_number())
839 	    prt_line_number(psettings, page_line, ppos->file_line);
840 	ppos->ff = FALSE;
841     }
842     else
843     {
844 	/* left over from wrap halfway a tab */
845 	print_pos = ppos->print_pos;
846 	tab_spaces = ppos->lead_spaces;
847     }
848 
849     mch_print_start_line(0, page_line);
850     line = ml_get(ppos->file_line);
851 
852     /*
853      * Loop over the columns until the end of the file line or right margin.
854      */
855     for (col = ppos->column; line[col] != NUL && !need_break; col += outputlen)
856     {
857 	outputlen = 1;
858 	if (has_mbyte && (outputlen = (*mb_ptr2len)(line + col)) < 1)
859 	    outputlen = 1;
860 #ifdef FEAT_SYN_HL
861 	/*
862 	 * syntax highlighting stuff.
863 	 */
864 	if (psettings->do_syntax)
865 	{
866 	    id = syn_get_id(curwin, ppos->file_line, col, 1, NULL, FALSE);
867 	    if (id > 0)
868 		id = syn_get_final_id(id);
869 	    else
870 		id = 0;
871 	    /* Get the line again, a multi-line regexp may invalidate it. */
872 	    line = ml_get(ppos->file_line);
873 
874 	    if (id != current_syn_id)
875 	    {
876 		current_syn_id = id;
877 		prt_get_attr(id, &attr, psettings->modec);
878 		prt_set_font(attr.bold, attr.italic, attr.underline);
879 		prt_set_fg(attr.fg_color);
880 		prt_set_bg(attr.bg_color);
881 	    }
882 	}
883 #endif
884 
885 	/*
886 	 * Appropriately expand any tabs to spaces.
887 	 */
888 	if (line[col] == TAB || tab_spaces != 0)
889 	{
890 	    if (tab_spaces == 0)
891 #ifdef FEAT_VARTABS
892 		tab_spaces = tabstop_padding(print_pos, curbuf->b_p_ts,
893 							curbuf->b_p_vts_array);
894 #else
895 		tab_spaces = (int)(curbuf->b_p_ts - (print_pos % curbuf->b_p_ts));
896 #endif
897 
898 	    while (tab_spaces > 0)
899 	    {
900 		need_break = mch_print_text_out((char_u *)" ", 1);
901 		print_pos++;
902 		tab_spaces--;
903 		if (need_break)
904 		    break;
905 	    }
906 	    /* Keep the TAB if we didn't finish it. */
907 	    if (need_break && tab_spaces > 0)
908 		break;
909 	}
910 	else if (line[col] == FF
911 		&& printer_opts[OPT_PRINT_FORMFEED].present
912 		&& TOLOWER_ASC(printer_opts[OPT_PRINT_FORMFEED].string[0])
913 								       == 'y')
914 	{
915 	    ppos->ff = TRUE;
916 	    need_break = 1;
917 	}
918 	else
919 	{
920 	    need_break = mch_print_text_out(line + col, outputlen);
921 	    if (has_mbyte)
922 		print_pos += (*mb_ptr2cells)(line + col);
923 	    else
924 		print_pos++;
925 	}
926     }
927 
928     ppos->lead_spaces = tab_spaces;
929     ppos->print_pos = (int)print_pos;
930 
931     /*
932      * Start next line of file if we clip lines, or have reached end of the
933      * line, unless we are doing a formfeed.
934      */
935     if (!ppos->ff
936 	    && (line[col] == NUL
937 		|| (printer_opts[OPT_PRINT_WRAP].present
938 		    && TOLOWER_ASC(printer_opts[OPT_PRINT_WRAP].string[0])
939 								     == 'n')))
940 	return 0;
941     return col;
942 }
943 
944 # if defined(FEAT_POSTSCRIPT) || defined(PROTO)
945 
946 /*
947  * PS printer stuff.
948  *
949  * Sources of information to help maintain the PS printing code:
950  *
951  * 1. PostScript Language Reference, 3rd Edition,
952  *      Addison-Wesley, 1999, ISBN 0-201-37922-8
953  * 2. PostScript Language Program Design,
954  *      Addison-Wesley, 1988, ISBN 0-201-14396-8
955  * 3. PostScript Tutorial and Cookbook,
956  *      Addison Wesley, 1985, ISBN 0-201-10179-3
957  * 4. PostScript Language Document Structuring Conventions Specification,
958  *    version 3.0,
959  *      Adobe Technote 5001, 25th September 1992
960  * 5. PostScript Printer Description File Format Specification, Version 4.3,
961  *      Adobe technote 5003, 9th February 1996
962  * 6. Adobe Font Metrics File Format Specification, Version 4.1,
963  *      Adobe Technote 5007, 7th October 1998
964  * 7. Adobe CMap and CIDFont Files Specification, Version 1.0,
965  *      Adobe Technote 5014, 8th October 1996
966  * 8. Adobe CJKV Character Collections and CMaps for CID-Keyed Fonts,
967  *      Adoboe Technote 5094, 8th September, 2001
968  * 9. CJKV Information Processing, 2nd Edition,
969  *      O'Reilly, 2002, ISBN 1-56592-224-7
970  *
971  * Some of these documents can be found in PDF form on Adobe's web site -
972  * http://www.adobe.com
973  */
974 
975 #define NUM_ELEMENTS(arr)   (sizeof(arr)/sizeof((arr)[0]))
976 
977 #define PRT_PS_DEFAULT_DPI	    (72)    /* Default user space resolution */
978 #define PRT_PS_DEFAULT_FONTSIZE     (10)
979 #define PRT_PS_DEFAULT_BUFFER_SIZE  (80)
980 
981 struct prt_mediasize_S
982 {
983     char	*name;
984     float	width;		/* width and height in points for portrait */
985     float	height;
986 };
987 
988 #define PRT_MEDIASIZE_LEN  (sizeof(prt_mediasize) / sizeof(struct prt_mediasize_S))
989 
990 static struct prt_mediasize_S prt_mediasize[] =
991 {
992     {"A4",		595.0,  842.0},
993     {"letter",		612.0,  792.0},
994     {"10x14",		720.0, 1008.0},
995     {"A3",		842.0, 1191.0},
996     {"A5",		420.0,  595.0},
997     {"B4",		729.0, 1032.0},
998     {"B5",		516.0,  729.0},
999     {"executive",	522.0,  756.0},
1000     {"folio",		595.0,  935.0},
1001     {"ledger",	       1224.0,  792.0},   /* Yes, it is wider than taller! */
1002     {"legal",		612.0, 1008.0},
1003     {"quarto",		610.0,  780.0},
1004     {"statement",	396.0,  612.0},
1005     {"tabloid",		792.0, 1224.0}
1006 };
1007 
1008 /* PS font names, must be in Roman, Bold, Italic, Bold-Italic order */
1009 struct prt_ps_font_S
1010 {
1011     int		wx;
1012     int		uline_offset;
1013     int		uline_width;
1014     int		bbox_min_y;
1015     int		bbox_max_y;
1016     char	*(ps_fontname[4]);
1017 };
1018 
1019 #define PRT_PS_FONT_ROMAN	(0)
1020 #define PRT_PS_FONT_BOLD	(1)
1021 #define PRT_PS_FONT_OBLIQUE	(2)
1022 #define PRT_PS_FONT_BOLDOBLIQUE (3)
1023 
1024 /* Standard font metrics for Courier family */
1025 static struct prt_ps_font_S prt_ps_courier_font =
1026 {
1027     600,
1028     -100, 50,
1029     -250, 805,
1030     {"Courier", "Courier-Bold", "Courier-Oblique", "Courier-BoldOblique"}
1031 };
1032 
1033 /* Generic font metrics for multi-byte fonts */
1034 static struct prt_ps_font_S prt_ps_mb_font =
1035 {
1036     1000,
1037     -100, 50,
1038     -250, 805,
1039     {NULL, NULL, NULL, NULL}
1040 };
1041 
1042 /* Pointer to current font set being used */
1043 static struct prt_ps_font_S* prt_ps_font;
1044 
1045 /* Structures to map user named encoding and mapping to PS equivalents for
1046  * building CID font name */
1047 struct prt_ps_encoding_S
1048 {
1049     char	*encoding;
1050     char	*cmap_encoding;
1051     int		needs_charset;
1052 };
1053 
1054 struct prt_ps_charset_S
1055 {
1056     char	*charset;
1057     char	*cmap_charset;
1058     int		has_charset;
1059 };
1060 
1061 
1062 #define CS_JIS_C_1978   (0x01)
1063 #define CS_JIS_X_1983   (0x02)
1064 #define CS_JIS_X_1990   (0x04)
1065 #define CS_NEC		(0x08)
1066 #define CS_MSWINDOWS	(0x10)
1067 #define CS_CP932	(0x20)
1068 #define CS_KANJITALK6	(0x40)
1069 #define CS_KANJITALK7   (0x80)
1070 
1071 /* Japanese encodings and charsets */
1072 static struct prt_ps_encoding_S j_encodings[] =
1073 {
1074     {"iso-2022-jp", NULL,       (CS_JIS_C_1978|CS_JIS_X_1983|CS_JIS_X_1990|
1075 								    CS_NEC)},
1076     {"euc-jp",	    "EUC",	(CS_JIS_C_1978|CS_JIS_X_1983|CS_JIS_X_1990)},
1077     {"sjis",	    "RKSJ",	(CS_JIS_C_1978|CS_JIS_X_1983|CS_MSWINDOWS|
1078 						CS_KANJITALK6|CS_KANJITALK7)},
1079     {"cp932",       "RKSJ",     CS_JIS_X_1983},
1080     {"ucs-2",       "UCS2",     CS_JIS_X_1990},
1081     {"utf-8",       "UTF8" ,    CS_JIS_X_1990}
1082 };
1083 static struct prt_ps_charset_S j_charsets[] =
1084 {
1085     {"JIS_C_1978",  "78",       CS_JIS_C_1978},
1086     {"JIS_X_1983",  NULL,       CS_JIS_X_1983},
1087     {"JIS_X_1990",  "Hojo",     CS_JIS_X_1990},
1088     {"NEC",	    "Ext",	CS_NEC},
1089     {"MSWINDOWS",   "90ms",     CS_MSWINDOWS},
1090     {"CP932",       "90ms",     CS_JIS_X_1983},
1091     {"KANJITALK6",  "83pv",     CS_KANJITALK6},
1092     {"KANJITALK7",  "90pv",     CS_KANJITALK7}
1093 };
1094 
1095 #define CS_GB_2312_80       (0x01)
1096 #define CS_GBT_12345_90     (0x02)
1097 #define CS_GBK2K	    (0x04)
1098 #define CS_SC_MAC	    (0x08)
1099 #define CS_GBT_90_MAC	    (0x10)
1100 #define CS_GBK		    (0x20)
1101 #define CS_SC_ISO10646      (0x40)
1102 
1103 /* Simplified Chinese encodings and charsets */
1104 static struct prt_ps_encoding_S sc_encodings[] =
1105 {
1106     {"iso-2022",    NULL,       (CS_GB_2312_80|CS_GBT_12345_90)},
1107     {"gb18030",     NULL,       CS_GBK2K},
1108     {"euc-cn",      "EUC",      (CS_GB_2312_80|CS_GBT_12345_90|CS_SC_MAC|
1109 								CS_GBT_90_MAC)},
1110     {"gbk",	    "EUC",	CS_GBK},
1111     {"ucs-2",       "UCS2",     CS_SC_ISO10646},
1112     {"utf-8",       "UTF8",     CS_SC_ISO10646}
1113 };
1114 static struct prt_ps_charset_S sc_charsets[] =
1115 {
1116     {"GB_2312-80",  "GB",       CS_GB_2312_80},
1117     {"GBT_12345-90","GBT",      CS_GBT_12345_90},
1118     {"MAC",	    "GBpc",	CS_SC_MAC},
1119     {"GBT-90_MAC",  "GBTpc",	CS_GBT_90_MAC},
1120     {"GBK",	    "GBK",	CS_GBK},
1121     {"GB18030",     "GBK2K",    CS_GBK2K},
1122     {"ISO10646",    "UniGB",    CS_SC_ISO10646}
1123 };
1124 
1125 #define CS_CNS_PLANE_1      (0x01)
1126 #define CS_CNS_PLANE_2      (0x02)
1127 #define CS_CNS_PLANE_1_2    (0x04)
1128 #define CS_B5		    (0x08)
1129 #define CS_ETEN		    (0x10)
1130 #define CS_HK_GCCS	    (0x20)
1131 #define CS_HK_SCS	    (0x40)
1132 #define CS_HK_SCS_ETEN	    (0x80)
1133 #define CS_MTHKL	    (0x100)
1134 #define CS_MTHKS	    (0x200)
1135 #define CS_DLHKL	    (0x400)
1136 #define CS_DLHKS	    (0x800)
1137 #define CS_TC_ISO10646	    (0x1000)
1138 
1139 /* Traditional Chinese encodings and charsets */
1140 static struct prt_ps_encoding_S tc_encodings[] =
1141 {
1142     {"iso-2022",    NULL,       (CS_CNS_PLANE_1|CS_CNS_PLANE_2)},
1143     {"euc-tw",      "EUC",      CS_CNS_PLANE_1_2},
1144     {"big5",	    "B5",	(CS_B5|CS_ETEN|CS_HK_GCCS|CS_HK_SCS|
1145 				    CS_HK_SCS_ETEN|CS_MTHKL|CS_MTHKS|CS_DLHKL|
1146 								    CS_DLHKS)},
1147     {"cp950",       "B5",       CS_B5},
1148     {"ucs-2",       "UCS2",     CS_TC_ISO10646},
1149     {"utf-8",       "UTF8",     CS_TC_ISO10646},
1150     {"utf-16",      "UTF16",    CS_TC_ISO10646},
1151     {"utf-32",      "UTF32",    CS_TC_ISO10646}
1152 };
1153 static struct prt_ps_charset_S tc_charsets[] =
1154 {
1155     {"CNS_1992_1",  "CNS1",     CS_CNS_PLANE_1},
1156     {"CNS_1992_2",  "CNS2",     CS_CNS_PLANE_2},
1157     {"CNS_1993",    "CNS",      CS_CNS_PLANE_1_2},
1158     {"BIG5",	    NULL,	CS_B5},
1159     {"CP950",	    NULL,	CS_B5},
1160     {"ETEN",	    "ETen",	CS_ETEN},
1161     {"HK_GCCS",     "HKgccs",	CS_HK_GCCS},
1162     {"SCS",	    "HKscs",	CS_HK_SCS},
1163     {"SCS_ETEN",    "ETHK",     CS_HK_SCS_ETEN},
1164     {"MTHKL",       "HKm471",   CS_MTHKL},
1165     {"MTHKS",       "HKm314",   CS_MTHKS},
1166     {"DLHKL",       "HKdla",    CS_DLHKL},
1167     {"DLHKS",       "HKdlb",    CS_DLHKS},
1168     {"ISO10646",    "UniCNS",   CS_TC_ISO10646}
1169 };
1170 
1171 #define CS_KR_X_1992	    (0x01)
1172 #define CS_KR_MAC	    (0x02)
1173 #define CS_KR_X_1992_MS     (0x04)
1174 #define CS_KR_ISO10646      (0x08)
1175 
1176 /* Korean encodings and charsets */
1177 static struct prt_ps_encoding_S k_encodings[] =
1178 {
1179     {"iso-2022-kr", NULL,       CS_KR_X_1992},
1180     {"euc-kr",      "EUC",      (CS_KR_X_1992|CS_KR_MAC)},
1181     {"johab",       "Johab",    CS_KR_X_1992},
1182     {"cp1361",      "Johab",    CS_KR_X_1992},
1183     {"uhc",	    "UHC",	CS_KR_X_1992_MS},
1184     {"cp949",       "UHC",      CS_KR_X_1992_MS},
1185     {"ucs-2",       "UCS2",     CS_KR_ISO10646},
1186     {"utf-8",       "UTF8",     CS_KR_ISO10646}
1187 };
1188 static struct prt_ps_charset_S k_charsets[] =
1189 {
1190     {"KS_X_1992",   "KSC",      CS_KR_X_1992},
1191     {"CP1361",      "KSC",      CS_KR_X_1992},
1192     {"MAC",	    "KSCpc",	CS_KR_MAC},
1193     {"MSWINDOWS",   "KSCms",    CS_KR_X_1992_MS},
1194     {"CP949",       "KSCms",    CS_KR_X_1992_MS},
1195     {"WANSUNG",     "KSCms",    CS_KR_X_1992_MS},
1196     {"ISO10646",    "UniKS",    CS_KR_ISO10646}
1197 };
1198 
1199 /* Collections of encodings and charsets for multi-byte printing */
1200 struct prt_ps_mbfont_S
1201 {
1202     int				num_encodings;
1203     struct prt_ps_encoding_S	*encodings;
1204     int				num_charsets;
1205     struct prt_ps_charset_S	*charsets;
1206     char			*ascii_enc;
1207     char			*defcs;
1208 };
1209 
1210 static struct prt_ps_mbfont_S prt_ps_mbfonts[] =
1211 {
1212     {
1213 	NUM_ELEMENTS(j_encodings),
1214 	j_encodings,
1215 	NUM_ELEMENTS(j_charsets),
1216 	j_charsets,
1217 	"jis_roman",
1218 	"JIS_X_1983"
1219     },
1220     {
1221 	NUM_ELEMENTS(sc_encodings),
1222 	sc_encodings,
1223 	NUM_ELEMENTS(sc_charsets),
1224 	sc_charsets,
1225 	"gb_roman",
1226 	"GB_2312-80"
1227     },
1228     {
1229 	NUM_ELEMENTS(tc_encodings),
1230 	tc_encodings,
1231 	NUM_ELEMENTS(tc_charsets),
1232 	tc_charsets,
1233 	"cns_roman",
1234 	"BIG5"
1235     },
1236     {
1237 	NUM_ELEMENTS(k_encodings),
1238 	k_encodings,
1239 	NUM_ELEMENTS(k_charsets),
1240 	k_charsets,
1241 	"ks_roman",
1242 	"KS_X_1992"
1243     }
1244 };
1245 
1246 struct prt_ps_resource_S
1247 {
1248     char_u  name[64];
1249     char_u  filename[MAXPATHL + 1];
1250     int     type;
1251     char_u  title[256];
1252     char_u  version[256];
1253 };
1254 
1255 /* Types of PS resource file currently used */
1256 #define PRT_RESOURCE_TYPE_PROCSET   (0)
1257 #define PRT_RESOURCE_TYPE_ENCODING  (1)
1258 #define PRT_RESOURCE_TYPE_CMAP      (2)
1259 
1260 /* The PS prolog file version number has to match - if the prolog file is
1261  * updated, increment the number in the file and here.  Version checking was
1262  * added as of VIM 6.2.
1263  * The CID prolog file version number behaves as per PS prolog.
1264  * Table of VIM and prolog versions:
1265  *
1266  * VIM      Prolog  CIDProlog
1267  * 6.2      1.3
1268  * 7.0      1.4	    1.0
1269  */
1270 #define PRT_PROLOG_VERSION  ((char_u *)"1.4")
1271 #define PRT_CID_PROLOG_VERSION  ((char_u *)"1.0")
1272 
1273 /* String versions of PS resource types - indexed by constants above so don't
1274  * re-order!
1275  */
1276 static char *prt_resource_types[] =
1277 {
1278     "procset",
1279     "encoding",
1280     "cmap"
1281 };
1282 
1283 /* Strings to look for in a PS resource file */
1284 #define PRT_RESOURCE_HEADER	    "%!PS-Adobe-"
1285 #define PRT_RESOURCE_RESOURCE	    "Resource-"
1286 #define PRT_RESOURCE_PROCSET	    "ProcSet"
1287 #define PRT_RESOURCE_ENCODING	    "Encoding"
1288 #define PRT_RESOURCE_CMAP	    "CMap"
1289 
1290 
1291 /* Data for table based DSC comment recognition, easy to extend if VIM needs to
1292  * read more comments. */
1293 #define PRT_DSC_MISC_TYPE	    (-1)
1294 #define PRT_DSC_TITLE_TYPE	    (1)
1295 #define PRT_DSC_VERSION_TYPE	    (2)
1296 #define PRT_DSC_ENDCOMMENTS_TYPE    (3)
1297 
1298 #define PRT_DSC_TITLE		    "%%Title:"
1299 #define PRT_DSC_VERSION		    "%%Version:"
1300 #define PRT_DSC_ENDCOMMENTS	    "%%EndComments:"
1301 
1302 struct prt_dsc_comment_S
1303 {
1304     char	*string;
1305     int		len;
1306     int		type;
1307 };
1308 
1309 struct prt_dsc_line_S
1310 {
1311     int		type;
1312     char_u	*string;
1313     int		len;
1314 };
1315 
1316 
1317 #define SIZEOF_CSTR(s)      (sizeof(s) - 1)
1318 static struct prt_dsc_comment_S prt_dsc_table[] =
1319 {
1320     {PRT_DSC_TITLE,       SIZEOF_CSTR(PRT_DSC_TITLE),     PRT_DSC_TITLE_TYPE},
1321     {PRT_DSC_VERSION,     SIZEOF_CSTR(PRT_DSC_VERSION),
1322 							PRT_DSC_VERSION_TYPE},
1323     {PRT_DSC_ENDCOMMENTS, SIZEOF_CSTR(PRT_DSC_ENDCOMMENTS),
1324 						     PRT_DSC_ENDCOMMENTS_TYPE}
1325 };
1326 
1327 static void prt_write_file_len(char_u *buffer, int bytes);
1328 static int prt_next_dsc(struct prt_dsc_line_S *p_dsc_line);
1329 
1330 /*
1331  * Variables for the output PostScript file.
1332  */
1333 static FILE *prt_ps_fd;
1334 static int prt_file_error;
1335 static char_u *prt_ps_file_name = NULL;
1336 
1337 /*
1338  * Various offsets and dimensions in default PostScript user space (points).
1339  * Used for text positioning calculations
1340  */
1341 static float prt_page_width;
1342 static float prt_page_height;
1343 static float prt_left_margin;
1344 static float prt_right_margin;
1345 static float prt_top_margin;
1346 static float prt_bottom_margin;
1347 static float prt_line_height;
1348 static float prt_first_line_height;
1349 static float prt_char_width;
1350 static float prt_number_width;
1351 static float prt_bgcol_offset;
1352 static float prt_pos_x_moveto = 0.0;
1353 static float prt_pos_y_moveto = 0.0;
1354 
1355 /*
1356  * Various control variables used to decide when and how to change the
1357  * PostScript graphics state.
1358  */
1359 static int prt_need_moveto;
1360 static int prt_do_moveto;
1361 static int prt_need_font;
1362 static int prt_font;
1363 static int prt_need_underline;
1364 static int prt_underline;
1365 static int prt_do_underline;
1366 static int prt_need_fgcol;
1367 static int prt_fgcol;
1368 static int prt_need_bgcol;
1369 static int prt_do_bgcol;
1370 static int prt_bgcol;
1371 static int prt_new_bgcol;
1372 static int prt_attribute_change;
1373 static float prt_text_run;
1374 static int prt_page_num;
1375 static int prt_bufsiz;
1376 
1377 /*
1378  * Variables controlling physical printing.
1379  */
1380 static int prt_media;
1381 static int prt_portrait;
1382 static int prt_num_copies;
1383 static int prt_duplex;
1384 static int prt_tumble;
1385 static int prt_collate;
1386 
1387 /*
1388  * Buffers used when generating PostScript output
1389  */
1390 static char_u prt_line_buffer[257];
1391 static garray_T prt_ps_buffer;
1392 
1393 static int prt_do_conv;
1394 static vimconv_T prt_conv;
1395 
1396 static int prt_out_mbyte;
1397 static int prt_custom_cmap;
1398 static char prt_cmap[80];
1399 static int prt_use_courier;
1400 static int prt_in_ascii;
1401 static int prt_half_width;
1402 static char *prt_ascii_encoding;
1403 static char_u prt_hexchar[] = "0123456789abcdef";
1404 
1405     static void
1406 prt_write_file_raw_len(char_u *buffer, int bytes)
1407 {
1408     if (!prt_file_error
1409 	    && fwrite(buffer, sizeof(char_u), bytes, prt_ps_fd)
1410 							     != (size_t)bytes)
1411     {
1412 	emsg(_("E455: Error writing to PostScript output file"));
1413 	prt_file_error = TRUE;
1414     }
1415 }
1416 
1417     static void
1418 prt_write_file(char_u *buffer)
1419 {
1420     prt_write_file_len(buffer, (int)STRLEN(buffer));
1421 }
1422 
1423     static void
1424 prt_write_file_len(char_u *buffer, int bytes)
1425 {
1426 #ifdef EBCDIC
1427     ebcdic2ascii(buffer, bytes);
1428 #endif
1429     prt_write_file_raw_len(buffer, bytes);
1430 }
1431 
1432 /*
1433  * Write a string.
1434  */
1435     static void
1436 prt_write_string(char *s)
1437 {
1438     vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer), "%s", s);
1439     prt_write_file(prt_line_buffer);
1440 }
1441 
1442 /*
1443  * Write an int and a space.
1444  */
1445     static void
1446 prt_write_int(int i)
1447 {
1448     sprintf((char *)prt_line_buffer, "%d ", i);
1449     prt_write_file(prt_line_buffer);
1450 }
1451 
1452 /*
1453  * Write a boolean and a space.
1454  */
1455     static void
1456 prt_write_boolean(int b)
1457 {
1458     sprintf((char *)prt_line_buffer, "%s ", (b ? "T" : "F"));
1459     prt_write_file(prt_line_buffer);
1460 }
1461 
1462 /*
1463  * Write PostScript to re-encode and define the font.
1464  */
1465     static void
1466 prt_def_font(
1467     char	*new_name,
1468     char	*encoding,
1469     int		height,
1470     char	*font)
1471 {
1472     vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
1473 			  "/_%s /VIM-%s /%s ref\n", new_name, encoding, font);
1474     prt_write_file(prt_line_buffer);
1475     if (prt_out_mbyte)
1476 	sprintf((char *)prt_line_buffer, "/%s %d %f /_%s sffs\n",
1477 		       new_name, height, 500./prt_ps_courier_font.wx, new_name);
1478     else
1479 	vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
1480 			     "/%s %d /_%s ffs\n", new_name, height, new_name);
1481     prt_write_file(prt_line_buffer);
1482 }
1483 
1484 /*
1485  * Write a line to define the CID font.
1486  */
1487     static void
1488 prt_def_cidfont(char *new_name, int height, char *cidfont)
1489 {
1490     vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
1491 	      "/_%s /%s[/%s] vim_composefont\n", new_name, prt_cmap, cidfont);
1492     prt_write_file(prt_line_buffer);
1493     vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
1494 			     "/%s %d /_%s ffs\n", new_name, height, new_name);
1495     prt_write_file(prt_line_buffer);
1496 }
1497 
1498 /*
1499  * Write a line to define a duplicate of a CID font
1500  */
1501     static void
1502 prt_dup_cidfont(char *original_name, char *new_name)
1503 {
1504     vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
1505 				       "/%s %s d\n", new_name, original_name);
1506     prt_write_file(prt_line_buffer);
1507 }
1508 
1509 /*
1510  * Convert a real value into an integer and fractional part as integers, with
1511  * the fractional part being in the range [0,10^precision).  The fractional part
1512  * is also rounded based on the precision + 1'th fractional digit.
1513  */
1514     static void
1515 prt_real_bits(
1516     double      real,
1517     int		precision,
1518     int		*pinteger,
1519     int		*pfraction)
1520 {
1521     int     i;
1522     int     integer;
1523     float   fraction;
1524 
1525     integer = (int)real;
1526     fraction = (float)(real - integer);
1527     if (real < (double)integer)
1528 	fraction = -fraction;
1529     for (i = 0; i < precision; i++)
1530 	fraction *= 10.0;
1531 
1532     *pinteger = integer;
1533     *pfraction = (int)(fraction + 0.5);
1534 }
1535 
1536 /*
1537  * Write a real and a space.  Save bytes if real value has no fractional part!
1538  * We use prt_real_bits() as %f in sprintf uses the locale setting to decide
1539  * what decimal point character to use, but PS always requires a '.'.
1540  */
1541     static void
1542 prt_write_real(double val, int prec)
1543 {
1544     int     integer;
1545     int     fraction;
1546 
1547     prt_real_bits(val, prec, &integer, &fraction);
1548     /* Emit integer part */
1549     sprintf((char *)prt_line_buffer, "%d", integer);
1550     prt_write_file(prt_line_buffer);
1551     /* Only emit fraction if necessary */
1552     if (fraction != 0)
1553     {
1554 	/* Remove any trailing zeros */
1555 	while ((fraction % 10) == 0)
1556 	{
1557 	    prec--;
1558 	    fraction /= 10;
1559 	}
1560 	/* Emit fraction left padded with zeros */
1561 	sprintf((char *)prt_line_buffer, ".%0*d", prec, fraction);
1562 	prt_write_file(prt_line_buffer);
1563     }
1564     sprintf((char *)prt_line_buffer, " ");
1565     prt_write_file(prt_line_buffer);
1566 }
1567 
1568 /*
1569  * Write a line to define a numeric variable.
1570  */
1571     static void
1572 prt_def_var(char *name, double value, int prec)
1573 {
1574     vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
1575 								"/%s ", name);
1576     prt_write_file(prt_line_buffer);
1577     prt_write_real(value, prec);
1578     sprintf((char *)prt_line_buffer, "d\n");
1579     prt_write_file(prt_line_buffer);
1580 }
1581 
1582 /* Convert size from font space to user space at current font scale */
1583 #define PRT_PS_FONT_TO_USER(scale, size)    ((size) * ((scale)/1000.0))
1584 
1585     static void
1586 prt_flush_buffer(void)
1587 {
1588     if (prt_ps_buffer.ga_len > 0)
1589     {
1590 	/* Any background color must be drawn first */
1591 	if (prt_do_bgcol && (prt_new_bgcol != PRCOLOR_WHITE))
1592 	{
1593 	    int     r, g, b;
1594 
1595 	    if (prt_do_moveto)
1596 	    {
1597 		prt_write_real(prt_pos_x_moveto, 2);
1598 		prt_write_real(prt_pos_y_moveto, 2);
1599 		prt_write_string("m\n");
1600 		prt_do_moveto = FALSE;
1601 	    }
1602 
1603 	    /* Size of rect of background color on which text is printed */
1604 	    prt_write_real(prt_text_run, 2);
1605 	    prt_write_real(prt_line_height, 2);
1606 
1607 	    /* Lastly add the color of the background */
1608 	    r = ((unsigned)prt_new_bgcol & 0xff0000) >> 16;
1609 	    g = ((unsigned)prt_new_bgcol & 0xff00) >> 8;
1610 	    b = prt_new_bgcol & 0xff;
1611 	    prt_write_real(r / 255.0, 3);
1612 	    prt_write_real(g / 255.0, 3);
1613 	    prt_write_real(b / 255.0, 3);
1614 	    prt_write_string("bg\n");
1615 	}
1616 	/* Draw underlines before the text as it makes it slightly easier to
1617 	 * find the starting point.
1618 	 */
1619 	if (prt_do_underline)
1620 	{
1621 	    if (prt_do_moveto)
1622 	    {
1623 		prt_write_real(prt_pos_x_moveto, 2);
1624 		prt_write_real(prt_pos_y_moveto, 2);
1625 		prt_write_string("m\n");
1626 		prt_do_moveto = FALSE;
1627 	    }
1628 
1629 	    /* Underline length of text run */
1630 	    prt_write_real(prt_text_run, 2);
1631 	    prt_write_string("ul\n");
1632 	}
1633 	/* Draw the text
1634 	 * Note: we write text out raw - EBCDIC conversion is handled in the
1635 	 * PostScript world via the font encoding vector. */
1636 	if (prt_out_mbyte)
1637 	    prt_write_string("<");
1638 	else
1639 	    prt_write_string("(");
1640 	prt_write_file_raw_len(prt_ps_buffer.ga_data, prt_ps_buffer.ga_len);
1641 	if (prt_out_mbyte)
1642 	    prt_write_string(">");
1643 	else
1644 	    prt_write_string(")");
1645 	/* Add a moveto if need be and use the appropriate show procedure */
1646 	if (prt_do_moveto)
1647 	{
1648 	    prt_write_real(prt_pos_x_moveto, 2);
1649 	    prt_write_real(prt_pos_y_moveto, 2);
1650 	    /* moveto and a show */
1651 	    prt_write_string("ms\n");
1652 	    prt_do_moveto = FALSE;
1653 	}
1654 	else /* Simple show */
1655 	    prt_write_string("s\n");
1656 
1657 	ga_clear(&prt_ps_buffer);
1658 	ga_init2(&prt_ps_buffer, (int)sizeof(char), prt_bufsiz);
1659     }
1660 }
1661 
1662 
1663     static void
1664 prt_resource_name(char_u *filename, void *cookie)
1665 {
1666     char_u *resource_filename = cookie;
1667 
1668     if (STRLEN(filename) >= MAXPATHL)
1669 	*resource_filename = NUL;
1670     else
1671 	STRCPY(resource_filename, filename);
1672 }
1673 
1674     static int
1675 prt_find_resource(char *name, struct prt_ps_resource_S *resource)
1676 {
1677     char_u	*buffer;
1678     int		retval;
1679 
1680     buffer = alloc(MAXPATHL + 1);
1681     if (buffer == NULL)
1682 	return FALSE;
1683 
1684     vim_strncpy(resource->name, (char_u *)name, 63);
1685     /* Look for named resource file in runtimepath */
1686     STRCPY(buffer, "print");
1687     add_pathsep(buffer);
1688     vim_strcat(buffer, (char_u *)name, MAXPATHL);
1689     vim_strcat(buffer, (char_u *)".ps", MAXPATHL);
1690     resource->filename[0] = NUL;
1691     retval = (do_in_runtimepath(buffer, 0, prt_resource_name,
1692 							   resource->filename)
1693 	    && resource->filename[0] != NUL);
1694     vim_free(buffer);
1695     return retval;
1696 }
1697 
1698 /* PS CR and LF characters have platform independent values */
1699 #define PSLF  (0x0a)
1700 #define PSCR  (0x0d)
1701 
1702 /* Static buffer to read initial comments in a resource file, some can have a
1703  * couple of KB of comments! */
1704 #define PRT_FILE_BUFFER_LEN (2048)
1705 struct prt_resfile_buffer_S
1706 {
1707     char_u  buffer[PRT_FILE_BUFFER_LEN];
1708     int     len;
1709     int     line_start;
1710     int     line_end;
1711 };
1712 
1713 static struct prt_resfile_buffer_S prt_resfile;
1714 
1715     static int
1716 prt_resfile_next_line(void)
1717 {
1718     int     idx;
1719 
1720     /* Move to start of next line and then find end of line */
1721     idx = prt_resfile.line_end + 1;
1722     while (idx < prt_resfile.len)
1723     {
1724 	if (prt_resfile.buffer[idx] != PSLF && prt_resfile.buffer[idx] != PSCR)
1725 	    break;
1726 	idx++;
1727     }
1728     prt_resfile.line_start = idx;
1729 
1730     while (idx < prt_resfile.len)
1731     {
1732 	if (prt_resfile.buffer[idx] == PSLF || prt_resfile.buffer[idx] == PSCR)
1733 	    break;
1734 	idx++;
1735     }
1736     prt_resfile.line_end = idx;
1737 
1738     return (idx < prt_resfile.len);
1739 }
1740 
1741     static int
1742 prt_resfile_strncmp(int offset, char *string, int len)
1743 {
1744     /* Force not equal if string is longer than remainder of line */
1745     if (len > (prt_resfile.line_end - (prt_resfile.line_start + offset)))
1746 	return 1;
1747 
1748     return STRNCMP(&prt_resfile.buffer[prt_resfile.line_start + offset],
1749 								string, len);
1750 }
1751 
1752     static int
1753 prt_resfile_skip_nonws(int offset)
1754 {
1755     int     idx;
1756 
1757     idx = prt_resfile.line_start + offset;
1758     while (idx < prt_resfile.line_end)
1759     {
1760 	if (isspace(prt_resfile.buffer[idx]))
1761 	    return idx - prt_resfile.line_start;
1762 	idx++;
1763     }
1764     return -1;
1765 }
1766 
1767     static int
1768 prt_resfile_skip_ws(int offset)
1769 {
1770     int     idx;
1771 
1772     idx = prt_resfile.line_start + offset;
1773     while (idx < prt_resfile.line_end)
1774     {
1775 	if (!isspace(prt_resfile.buffer[idx]))
1776 	    return idx - prt_resfile.line_start;
1777 	idx++;
1778     }
1779     return -1;
1780 }
1781 
1782 /* prt_next_dsc() - returns detail on next DSC comment line found.  Returns true
1783  * if a DSC comment is found, else false */
1784     static int
1785 prt_next_dsc(struct prt_dsc_line_S *p_dsc_line)
1786 {
1787     int     comment;
1788     int     offset;
1789 
1790     /* Move to start of next line */
1791     if (!prt_resfile_next_line())
1792 	return FALSE;
1793 
1794     /* DSC comments always start %% */
1795     if (prt_resfile_strncmp(0, "%%", 2) != 0)
1796 	return FALSE;
1797 
1798     /* Find type of DSC comment */
1799     for (comment = 0; comment < (int)NUM_ELEMENTS(prt_dsc_table); comment++)
1800 	if (prt_resfile_strncmp(0, prt_dsc_table[comment].string,
1801 					    prt_dsc_table[comment].len) == 0)
1802 	    break;
1803 
1804     if (comment != NUM_ELEMENTS(prt_dsc_table))
1805     {
1806 	/* Return type of comment */
1807 	p_dsc_line->type = prt_dsc_table[comment].type;
1808 	offset = prt_dsc_table[comment].len;
1809     }
1810     else
1811     {
1812 	/* Unrecognised DSC comment, skip to ws after comment leader */
1813 	p_dsc_line->type = PRT_DSC_MISC_TYPE;
1814 	offset = prt_resfile_skip_nonws(0);
1815 	if (offset == -1)
1816 	    return FALSE;
1817     }
1818 
1819     /* Skip ws to comment value */
1820     offset = prt_resfile_skip_ws(offset);
1821     if (offset == -1)
1822 	return FALSE;
1823 
1824     p_dsc_line->string = &prt_resfile.buffer[prt_resfile.line_start + offset];
1825     p_dsc_line->len = prt_resfile.line_end - (prt_resfile.line_start + offset);
1826 
1827     return TRUE;
1828 }
1829 
1830 /* Improved hand crafted parser to get the type, title, and version number of a
1831  * PS resource file so the file details can be added to the DSC header comments.
1832  */
1833     static int
1834 prt_open_resource(struct prt_ps_resource_S *resource)
1835 {
1836     int		offset;
1837     int		seen_all;
1838     int		seen_title;
1839     int		seen_version;
1840     FILE	*fd_resource;
1841     struct prt_dsc_line_S dsc_line;
1842 
1843     fd_resource = mch_fopen((char *)resource->filename, READBIN);
1844     if (fd_resource == NULL)
1845     {
1846 	semsg(_("E624: Can't open file \"%s\""), resource->filename);
1847 	return FALSE;
1848     }
1849     vim_memset(prt_resfile.buffer, NUL, PRT_FILE_BUFFER_LEN);
1850 
1851     /* Parse first line to ensure valid resource file */
1852     prt_resfile.len = (int)fread((char *)prt_resfile.buffer, sizeof(char_u),
1853 					    PRT_FILE_BUFFER_LEN, fd_resource);
1854     if (ferror(fd_resource))
1855     {
1856 	semsg(_("E457: Can't read PostScript resource file \"%s\""),
1857 		resource->filename);
1858 	fclose(fd_resource);
1859 	return FALSE;
1860     }
1861     fclose(fd_resource);
1862 
1863     prt_resfile.line_end = -1;
1864     prt_resfile.line_start = 0;
1865     if (!prt_resfile_next_line())
1866 	return FALSE;
1867 
1868     offset = 0;
1869 
1870     if (prt_resfile_strncmp(offset, PRT_RESOURCE_HEADER,
1871 				       (int)STRLEN(PRT_RESOURCE_HEADER)) != 0)
1872     {
1873 	semsg(_("E618: file \"%s\" is not a PostScript resource file"),
1874 		resource->filename);
1875 	return FALSE;
1876     }
1877 
1878     /* Skip over any version numbers and following ws */
1879     offset += (int)STRLEN(PRT_RESOURCE_HEADER);
1880     offset = prt_resfile_skip_nonws(offset);
1881     if (offset == -1)
1882 	return FALSE;
1883     offset = prt_resfile_skip_ws(offset);
1884     if (offset == -1)
1885 	return FALSE;
1886 
1887     if (prt_resfile_strncmp(offset, PRT_RESOURCE_RESOURCE,
1888 				     (int)STRLEN(PRT_RESOURCE_RESOURCE)) != 0)
1889     {
1890 	semsg(_("E619: file \"%s\" is not a supported PostScript resource file"),
1891 		resource->filename);
1892 	return FALSE;
1893     }
1894     offset += (int)STRLEN(PRT_RESOURCE_RESOURCE);
1895 
1896     /* Decide type of resource in the file */
1897     if (prt_resfile_strncmp(offset, PRT_RESOURCE_PROCSET,
1898 				      (int)STRLEN(PRT_RESOURCE_PROCSET)) == 0)
1899 	resource->type = PRT_RESOURCE_TYPE_PROCSET;
1900     else if (prt_resfile_strncmp(offset, PRT_RESOURCE_ENCODING,
1901 				     (int)STRLEN(PRT_RESOURCE_ENCODING)) == 0)
1902 	resource->type = PRT_RESOURCE_TYPE_ENCODING;
1903     else if (prt_resfile_strncmp(offset, PRT_RESOURCE_CMAP,
1904 					 (int)STRLEN(PRT_RESOURCE_CMAP)) == 0)
1905 	resource->type = PRT_RESOURCE_TYPE_CMAP;
1906     else
1907     {
1908 	semsg(_("E619: file \"%s\" is not a supported PostScript resource file"),
1909 		resource->filename);
1910 	return FALSE;
1911     }
1912 
1913     /* Look for title and version of resource */
1914     resource->title[0] = '\0';
1915     resource->version[0] = '\0';
1916     seen_title = FALSE;
1917     seen_version = FALSE;
1918     seen_all = FALSE;
1919     while (!seen_all && prt_next_dsc(&dsc_line))
1920     {
1921 	switch (dsc_line.type)
1922 	{
1923 	case PRT_DSC_TITLE_TYPE:
1924 	    vim_strncpy(resource->title, dsc_line.string, dsc_line.len);
1925 	    seen_title = TRUE;
1926 	    if (seen_version)
1927 		seen_all = TRUE;
1928 	    break;
1929 
1930 	case PRT_DSC_VERSION_TYPE:
1931 	    vim_strncpy(resource->version, dsc_line.string, dsc_line.len);
1932 	    seen_version = TRUE;
1933 	    if (seen_title)
1934 		seen_all = TRUE;
1935 	    break;
1936 
1937 	case PRT_DSC_ENDCOMMENTS_TYPE:
1938 	    /* Wont find title or resource after this comment, stop searching */
1939 	    seen_all = TRUE;
1940 	    break;
1941 
1942 	case PRT_DSC_MISC_TYPE:
1943 	    /* Not interested in whatever comment this line had */
1944 	    break;
1945 	}
1946     }
1947 
1948     if (!seen_title || !seen_version)
1949     {
1950 	semsg(_("E619: file \"%s\" is not a supported PostScript resource file"),
1951 		resource->filename);
1952 	return FALSE;
1953     }
1954 
1955     return TRUE;
1956 }
1957 
1958     static int
1959 prt_check_resource(struct prt_ps_resource_S *resource, char_u *version)
1960 {
1961     /* Version number m.n should match, the revision number does not matter */
1962     if (STRNCMP(resource->version, version, STRLEN(version)))
1963     {
1964 	semsg(_("E621: \"%s\" resource file has wrong version"),
1965 		resource->name);
1966 	return FALSE;
1967     }
1968 
1969     /* Other checks to be added as needed */
1970     return TRUE;
1971 }
1972 
1973     static void
1974 prt_dsc_start(void)
1975 {
1976     prt_write_string("%!PS-Adobe-3.0\n");
1977 }
1978 
1979     static void
1980 prt_dsc_noarg(char *comment)
1981 {
1982     vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
1983 							 "%%%%%s\n", comment);
1984     prt_write_file(prt_line_buffer);
1985 }
1986 
1987     static void
1988 prt_dsc_textline(char *comment, char *text)
1989 {
1990     vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
1991 					       "%%%%%s: %s\n", comment, text);
1992     prt_write_file(prt_line_buffer);
1993 }
1994 
1995     static void
1996 prt_dsc_text(char *comment, char *text)
1997 {
1998     /* TODO - should scan 'text' for any chars needing escaping! */
1999     vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
2000 					     "%%%%%s: (%s)\n", comment, text);
2001     prt_write_file(prt_line_buffer);
2002 }
2003 
2004 #define prt_dsc_atend(c)	prt_dsc_text((c), "atend")
2005 
2006     static void
2007 prt_dsc_ints(char *comment, int count, int *ints)
2008 {
2009     int		i;
2010 
2011     vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
2012 							  "%%%%%s:", comment);
2013     prt_write_file(prt_line_buffer);
2014 
2015     for (i = 0; i < count; i++)
2016     {
2017 	sprintf((char *)prt_line_buffer, " %d", ints[i]);
2018 	prt_write_file(prt_line_buffer);
2019     }
2020 
2021     prt_write_string("\n");
2022 }
2023 
2024     static void
2025 prt_dsc_resources(
2026     char	*comment,	/* if NULL add to previous */
2027     char	*type,
2028     char	*string)
2029 {
2030     if (comment != NULL)
2031 	vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
2032 						 "%%%%%s: %s", comment, type);
2033     else
2034 	vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
2035 							    "%%%%+ %s", type);
2036     prt_write_file(prt_line_buffer);
2037 
2038     vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
2039 							     " %s\n", string);
2040     prt_write_file(prt_line_buffer);
2041 }
2042 
2043     static void
2044 prt_dsc_font_resource(char *resource, struct prt_ps_font_S *ps_font)
2045 {
2046     int     i;
2047 
2048     prt_dsc_resources(resource, "font",
2049 				    ps_font->ps_fontname[PRT_PS_FONT_ROMAN]);
2050     for (i = PRT_PS_FONT_BOLD ; i <= PRT_PS_FONT_BOLDOBLIQUE ; i++)
2051 	if (ps_font->ps_fontname[i] != NULL)
2052 	    prt_dsc_resources(NULL, "font", ps_font->ps_fontname[i]);
2053 }
2054 
2055     static void
2056 prt_dsc_requirements(
2057     int		duplex,
2058     int		tumble,
2059     int		collate,
2060     int		color,
2061     int		num_copies)
2062 {
2063     /* Only output the comment if we need to.
2064      * Note: tumble is ignored if we are not duplexing
2065      */
2066     if (!(duplex || collate || color || (num_copies > 1)))
2067 	return;
2068 
2069     sprintf((char *)prt_line_buffer, "%%%%Requirements:");
2070     prt_write_file(prt_line_buffer);
2071 
2072     if (duplex)
2073     {
2074 	prt_write_string(" duplex");
2075 	if (tumble)
2076 	    prt_write_string("(tumble)");
2077     }
2078     if (collate)
2079 	prt_write_string(" collate");
2080     if (color)
2081 	prt_write_string(" color");
2082     if (num_copies > 1)
2083     {
2084 	prt_write_string(" numcopies(");
2085 	/* Note: no space wanted so don't use prt_write_int() */
2086 	sprintf((char *)prt_line_buffer, "%d", num_copies);
2087 	prt_write_file(prt_line_buffer);
2088 	prt_write_string(")");
2089     }
2090     prt_write_string("\n");
2091 }
2092 
2093     static void
2094 prt_dsc_docmedia(
2095     char	*paper_name,
2096     double	width,
2097     double	height,
2098     double	weight,
2099     char	*colour,
2100     char	*type)
2101 {
2102     vim_snprintf((char *)prt_line_buffer, sizeof(prt_line_buffer),
2103 					"%%%%DocumentMedia: %s ", paper_name);
2104     prt_write_file(prt_line_buffer);
2105     prt_write_real(width, 2);
2106     prt_write_real(height, 2);
2107     prt_write_real(weight, 2);
2108     if (colour == NULL)
2109 	prt_write_string("()");
2110     else
2111 	prt_write_string(colour);
2112     prt_write_string(" ");
2113     if (type == NULL)
2114 	prt_write_string("()");
2115     else
2116 	prt_write_string(type);
2117     prt_write_string("\n");
2118 }
2119 
2120     void
2121 mch_print_cleanup(void)
2122 {
2123     if (prt_out_mbyte)
2124     {
2125 	int     i;
2126 
2127 	/* Free off all CID font names created, but first clear duplicate
2128 	 * pointers to the same string (when the same font is used for more than
2129 	 * one style).
2130 	 */
2131 	for (i = PRT_PS_FONT_ROMAN; i <= PRT_PS_FONT_BOLDOBLIQUE; i++)
2132 	{
2133 	    if (prt_ps_mb_font.ps_fontname[i] != NULL)
2134 		VIM_CLEAR(prt_ps_mb_font.ps_fontname[i]);
2135 	}
2136     }
2137 
2138     if (prt_do_conv)
2139     {
2140 	convert_setup(&prt_conv, NULL, NULL);
2141 	prt_do_conv = FALSE;
2142     }
2143     if (prt_ps_fd != NULL)
2144     {
2145 	fclose(prt_ps_fd);
2146 	prt_ps_fd = NULL;
2147 	prt_file_error = FALSE;
2148     }
2149     if (prt_ps_file_name != NULL)
2150 	VIM_CLEAR(prt_ps_file_name);
2151 }
2152 
2153     static float
2154 to_device_units(int idx, double physsize, int def_number)
2155 {
2156     float	ret;
2157     int		u;
2158     int		nr;
2159 
2160     u = prt_get_unit(idx);
2161     if (u == PRT_UNIT_NONE)
2162     {
2163 	u = PRT_UNIT_PERC;
2164 	nr = def_number;
2165     }
2166     else
2167 	nr = printer_opts[idx].number;
2168 
2169     switch (u)
2170     {
2171 	case PRT_UNIT_INCH:
2172 	    ret = (float)(nr * PRT_PS_DEFAULT_DPI);
2173 	    break;
2174 	case PRT_UNIT_MM:
2175 	    ret = (float)(nr * PRT_PS_DEFAULT_DPI) / (float)25.4;
2176 	    break;
2177 	case PRT_UNIT_POINT:
2178 	    ret = (float)nr;
2179 	    break;
2180 	case PRT_UNIT_PERC:
2181 	default:
2182 	    ret = (float)(physsize * nr) / 100;
2183 	    break;
2184     }
2185 
2186     return ret;
2187 }
2188 
2189 /*
2190  * Calculate margins for given width and height from printoptions settings.
2191  */
2192     static void
2193 prt_page_margins(
2194     double	width,
2195     double	height,
2196     double	*left,
2197     double	*right,
2198     double	*top,
2199     double	*bottom)
2200 {
2201     *left   = to_device_units(OPT_PRINT_LEFT, width, 10);
2202     *right  = width - to_device_units(OPT_PRINT_RIGHT, width, 5);
2203     *top    = height - to_device_units(OPT_PRINT_TOP, height, 5);
2204     *bottom = to_device_units(OPT_PRINT_BOT, height, 5);
2205 }
2206 
2207     static void
2208 prt_font_metrics(int font_scale)
2209 {
2210     prt_line_height = (float)font_scale;
2211     prt_char_width = (float)PRT_PS_FONT_TO_USER(font_scale, prt_ps_font->wx);
2212 }
2213 
2214 
2215     static int
2216 prt_get_cpl(void)
2217 {
2218     if (prt_use_number())
2219     {
2220 	prt_number_width = PRINT_NUMBER_WIDTH * prt_char_width;
2221 	/* If we are outputting multi-byte characters then line numbers will be
2222 	 * printed with half width characters
2223 	 */
2224 	if (prt_out_mbyte)
2225 	    prt_number_width /= 2;
2226 	prt_left_margin += prt_number_width;
2227     }
2228     else
2229 	prt_number_width = 0.0;
2230 
2231     return (int)((prt_right_margin - prt_left_margin) / prt_char_width);
2232 }
2233 
2234     static int
2235 prt_build_cid_fontname(int font, char_u *name, int name_len)
2236 {
2237     char    *fontname;
2238 
2239     fontname = (char *)alloc(name_len + 1);
2240     if (fontname == NULL)
2241 	return FALSE;
2242     vim_strncpy((char_u *)fontname, name, name_len);
2243     prt_ps_mb_font.ps_fontname[font] = fontname;
2244 
2245     return TRUE;
2246 }
2247 
2248 /*
2249  * Get number of lines of text that fit on a page (excluding the header).
2250  */
2251     static int
2252 prt_get_lpp(void)
2253 {
2254     int lpp;
2255 
2256     /*
2257      * Calculate offset to lower left corner of background rect based on actual
2258      * font height (based on its bounding box) and the line height, handling the
2259      * case where the font height can exceed the line height.
2260      */
2261     prt_bgcol_offset = (float)PRT_PS_FONT_TO_USER(prt_line_height,
2262 					   prt_ps_font->bbox_min_y);
2263     if ((prt_ps_font->bbox_max_y - prt_ps_font->bbox_min_y) < 1000.0)
2264     {
2265 	prt_bgcol_offset -= (float)PRT_PS_FONT_TO_USER(prt_line_height,
2266 				(1000.0 - (prt_ps_font->bbox_max_y -
2267 					    prt_ps_font->bbox_min_y)) / 2);
2268     }
2269 
2270     /* Get height for topmost line based on background rect offset. */
2271     prt_first_line_height = prt_line_height + prt_bgcol_offset;
2272 
2273     /* Calculate lpp */
2274     lpp = (int)((prt_top_margin - prt_bottom_margin) / prt_line_height);
2275 
2276     /* Adjust top margin if there is a header */
2277     prt_top_margin -= prt_line_height * prt_header_height();
2278 
2279     return lpp - prt_header_height();
2280 }
2281 
2282     static int
2283 prt_match_encoding(
2284     char			*p_encoding,
2285     struct prt_ps_mbfont_S	*p_cmap,
2286     struct prt_ps_encoding_S	**pp_mbenc)
2287 {
2288     int				mbenc;
2289     int				enc_len;
2290     struct prt_ps_encoding_S	*p_mbenc;
2291 
2292     *pp_mbenc = NULL;
2293     /* Look for recognised encoding */
2294     enc_len = (int)STRLEN(p_encoding);
2295     p_mbenc = p_cmap->encodings;
2296     for (mbenc = 0; mbenc < p_cmap->num_encodings; mbenc++)
2297     {
2298 	if (STRNICMP(p_mbenc->encoding, p_encoding, enc_len) == 0)
2299 	{
2300 	    *pp_mbenc = p_mbenc;
2301 	    return TRUE;
2302 	}
2303 	p_mbenc++;
2304     }
2305     return FALSE;
2306 }
2307 
2308     static int
2309 prt_match_charset(
2310     char		    *p_charset,
2311     struct prt_ps_mbfont_S  *p_cmap,
2312     struct prt_ps_charset_S **pp_mbchar)
2313 {
2314     int			    mbchar;
2315     int			    char_len;
2316     struct prt_ps_charset_S *p_mbchar;
2317 
2318     /* Look for recognised character set, using default if one is not given */
2319     if (*p_charset == NUL)
2320 	p_charset = p_cmap->defcs;
2321     char_len = (int)STRLEN(p_charset);
2322     p_mbchar = p_cmap->charsets;
2323     for (mbchar = 0; mbchar < p_cmap->num_charsets; mbchar++)
2324     {
2325 	if (STRNICMP(p_mbchar->charset, p_charset, char_len) == 0)
2326 	{
2327 	    *pp_mbchar = p_mbchar;
2328 	    return TRUE;
2329 	}
2330 	p_mbchar++;
2331     }
2332     return FALSE;
2333 }
2334 
2335     int
2336 mch_print_init(
2337     prt_settings_T *psettings,
2338     char_u	*jobname,
2339     int		forceit UNUSED)
2340 {
2341     int		i;
2342     char	*paper_name;
2343     int		paper_strlen;
2344     int		fontsize;
2345     char_u	*p;
2346     double      left;
2347     double      right;
2348     double      top;
2349     double      bottom;
2350     int		props;
2351     int		cmap = 0;
2352     char_u	*p_encoding;
2353     struct prt_ps_encoding_S *p_mbenc;
2354     struct prt_ps_encoding_S *p_mbenc_first;
2355     struct prt_ps_charset_S  *p_mbchar = NULL;
2356 
2357 #if 0
2358     /*
2359      * TODO:
2360      * If "forceit" is false: pop up a dialog to select:
2361      *	- printer name
2362      *	- copies
2363      *	- collated/uncollated
2364      *	- duplex off/long side/short side
2365      *	- paper size
2366      *	- portrait/landscape
2367      *	- font size
2368      *
2369      * If "forceit" is true: use the default printer and settings
2370      */
2371     if (forceit)
2372 	s_pd.Flags |= PD_RETURNDEFAULT;
2373 #endif
2374 
2375     /*
2376      * Set up font and encoding.
2377      */
2378     p_encoding = enc_skip(p_penc);
2379     if (*p_encoding == NUL)
2380 	p_encoding = enc_skip(p_enc);
2381 
2382     /* Look for a multi-byte font that matches the encoding and character set.
2383      * Only look if multi-byte character set is defined, or using multi-byte
2384      * encoding other than Unicode.  This is because a Unicode encoding does not
2385      * uniquely identify a CJK character set to use. */
2386     p_mbenc = NULL;
2387     props = enc_canon_props(p_encoding);
2388     if (!(props & ENC_8BIT) && ((*p_pmcs != NUL) || !(props & ENC_UNICODE)))
2389     {
2390 	int cmap_first = 0;
2391 
2392 	p_mbenc_first = NULL;
2393 	for (cmap = 0; cmap < (int)NUM_ELEMENTS(prt_ps_mbfonts); cmap++)
2394 	    if (prt_match_encoding((char *)p_encoding, &prt_ps_mbfonts[cmap],
2395 								    &p_mbenc))
2396 	    {
2397 		if (p_mbenc_first == NULL)
2398 		{
2399 		    p_mbenc_first = p_mbenc;
2400 		    cmap_first = cmap;
2401 		}
2402 		if (prt_match_charset((char *)p_pmcs, &prt_ps_mbfonts[cmap],
2403 								   &p_mbchar))
2404 		    break;
2405 	    }
2406 
2407 	/* Use first encoding matched if no charset matched */
2408 	if (p_mbchar == NULL && p_mbenc_first != NULL)
2409 	{
2410 	    p_mbenc = p_mbenc_first;
2411 	    cmap = cmap_first;
2412 	}
2413     }
2414 
2415     prt_out_mbyte = (p_mbenc != NULL);
2416     if (prt_out_mbyte)
2417     {
2418 	/* Build CMap name - will be same for all multi-byte fonts used */
2419 	prt_cmap[0] = NUL;
2420 
2421 	prt_custom_cmap = (p_mbchar == NULL);
2422 	if (!prt_custom_cmap)
2423 	{
2424 	    /* Check encoding and character set are compatible */
2425 	    if ((p_mbenc->needs_charset & p_mbchar->has_charset) == 0)
2426 	    {
2427 		emsg(_("E673: Incompatible multi-byte encoding and character set."));
2428 		return FALSE;
2429 	    }
2430 
2431 	    /* Add charset name if not empty */
2432 	    if (p_mbchar->cmap_charset != NULL)
2433 	    {
2434 		vim_strncpy((char_u *)prt_cmap,
2435 		      (char_u *)p_mbchar->cmap_charset, sizeof(prt_cmap) - 3);
2436 		STRCAT(prt_cmap, "-");
2437 	    }
2438 	}
2439 	else
2440 	{
2441 	    /* Add custom CMap character set name */
2442 	    if (*p_pmcs == NUL)
2443 	    {
2444 		emsg(_("E674: printmbcharset cannot be empty with multi-byte encoding."));
2445 		return FALSE;
2446 	    }
2447 	    vim_strncpy((char_u *)prt_cmap, p_pmcs, sizeof(prt_cmap) - 3);
2448 	    STRCAT(prt_cmap, "-");
2449 	}
2450 
2451 	/* CMap name ends with (optional) encoding name and -H for horizontal */
2452 	if (p_mbenc->cmap_encoding != NULL && STRLEN(prt_cmap)
2453 		      + STRLEN(p_mbenc->cmap_encoding) + 3 < sizeof(prt_cmap))
2454 	{
2455 	    STRCAT(prt_cmap, p_mbenc->cmap_encoding);
2456 	    STRCAT(prt_cmap, "-");
2457 	}
2458 	STRCAT(prt_cmap, "H");
2459 
2460 	if (!mbfont_opts[OPT_MBFONT_REGULAR].present)
2461 	{
2462 	    emsg(_("E675: No default font specified for multi-byte printing."));
2463 	    return FALSE;
2464 	}
2465 
2466 	/* Derive CID font names with fallbacks if not defined */
2467 	if (!prt_build_cid_fontname(PRT_PS_FONT_ROMAN,
2468 				    mbfont_opts[OPT_MBFONT_REGULAR].string,
2469 				    mbfont_opts[OPT_MBFONT_REGULAR].strlen))
2470 	    return FALSE;
2471 	if (mbfont_opts[OPT_MBFONT_BOLD].present)
2472 	    if (!prt_build_cid_fontname(PRT_PS_FONT_BOLD,
2473 					mbfont_opts[OPT_MBFONT_BOLD].string,
2474 					mbfont_opts[OPT_MBFONT_BOLD].strlen))
2475 		return FALSE;
2476 	if (mbfont_opts[OPT_MBFONT_OBLIQUE].present)
2477 	    if (!prt_build_cid_fontname(PRT_PS_FONT_OBLIQUE,
2478 					mbfont_opts[OPT_MBFONT_OBLIQUE].string,
2479 					mbfont_opts[OPT_MBFONT_OBLIQUE].strlen))
2480 		return FALSE;
2481 	if (mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].present)
2482 	    if (!prt_build_cid_fontname(PRT_PS_FONT_BOLDOBLIQUE,
2483 				   mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].string,
2484 				  mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].strlen))
2485 		return FALSE;
2486 
2487 	/* Check if need to use Courier for ASCII code range, and if so pick up
2488 	 * the encoding to use */
2489 	prt_use_courier = mbfont_opts[OPT_MBFONT_USECOURIER].present &&
2490 	    (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_USECOURIER].string[0]) == 'y');
2491 	if (prt_use_courier)
2492 	{
2493 	    /* Use national ASCII variant unless ASCII wanted */
2494 	    if (mbfont_opts[OPT_MBFONT_ASCII].present &&
2495 		(TOLOWER_ASC(mbfont_opts[OPT_MBFONT_ASCII].string[0]) == 'y'))
2496 		prt_ascii_encoding = "ascii";
2497 	    else
2498 		prt_ascii_encoding = prt_ps_mbfonts[cmap].ascii_enc;
2499 	}
2500 
2501 	prt_ps_font = &prt_ps_mb_font;
2502     }
2503     else
2504     {
2505 	prt_use_courier = FALSE;
2506 	prt_ps_font = &prt_ps_courier_font;
2507     }
2508 
2509     /*
2510      * Find the size of the paper and set the margins.
2511      */
2512     prt_portrait = (!printer_opts[OPT_PRINT_PORTRAIT].present
2513 	   || TOLOWER_ASC(printer_opts[OPT_PRINT_PORTRAIT].string[0]) == 'y');
2514     if (printer_opts[OPT_PRINT_PAPER].present)
2515     {
2516 	paper_name = (char *)printer_opts[OPT_PRINT_PAPER].string;
2517 	paper_strlen = printer_opts[OPT_PRINT_PAPER].strlen;
2518     }
2519     else
2520     {
2521 	paper_name = "A4";
2522 	paper_strlen = 2;
2523     }
2524     for (i = 0; i < (int)PRT_MEDIASIZE_LEN; ++i)
2525 	if (STRLEN(prt_mediasize[i].name) == (unsigned)paper_strlen
2526 		&& STRNICMP(prt_mediasize[i].name, paper_name,
2527 							   paper_strlen) == 0)
2528 	    break;
2529     if (i == PRT_MEDIASIZE_LEN)
2530 	i = 0;
2531     prt_media = i;
2532 
2533     /*
2534      * Set PS pagesize based on media dimensions and print orientation.
2535      * Note: Media and page sizes have defined meanings in PostScript and should
2536      * be kept distinct.  Media is the paper (or transparency, or ...) that is
2537      * printed on, whereas the page size is the area that the PostScript
2538      * interpreter renders into.
2539      */
2540     if (prt_portrait)
2541     {
2542 	prt_page_width = prt_mediasize[i].width;
2543 	prt_page_height = prt_mediasize[i].height;
2544     }
2545     else
2546     {
2547 	prt_page_width = prt_mediasize[i].height;
2548 	prt_page_height = prt_mediasize[i].width;
2549     }
2550 
2551     /*
2552      * Set PS page margins based on the PS pagesize, not the mediasize - this
2553      * needs to be done before the cpl and lpp are calculated.
2554      */
2555     prt_page_margins(prt_page_width, prt_page_height, &left, &right, &top,
2556 								    &bottom);
2557     prt_left_margin = (float)left;
2558     prt_right_margin = (float)right;
2559     prt_top_margin = (float)top;
2560     prt_bottom_margin = (float)bottom;
2561 
2562     /*
2563      * Set up the font size.
2564      */
2565     fontsize = PRT_PS_DEFAULT_FONTSIZE;
2566     for (p = p_pfn; (p = vim_strchr(p, ':')) != NULL; ++p)
2567 	if (p[1] == 'h' && VIM_ISDIGIT(p[2]))
2568 	    fontsize = atoi((char *)p + 2);
2569     prt_font_metrics(fontsize);
2570 
2571     /*
2572      * Return the number of characters per line, and lines per page for the
2573      * generic print code.
2574      */
2575     psettings->chars_per_line = prt_get_cpl();
2576     psettings->lines_per_page = prt_get_lpp();
2577 
2578     /* Catch margin settings that leave no space for output! */
2579     if (psettings->chars_per_line <= 0 || psettings->lines_per_page <= 0)
2580 	return FAIL;
2581 
2582     /*
2583      * Sort out the number of copies to be printed.  PS by default will do
2584      * uncollated copies for you, so once we know how many uncollated copies are
2585      * wanted cache it away and lie to the generic code that we only want one
2586      * uncollated copy.
2587      */
2588     psettings->n_collated_copies = 1;
2589     psettings->n_uncollated_copies = 1;
2590     prt_num_copies = 1;
2591     prt_collate = (!printer_opts[OPT_PRINT_COLLATE].present
2592 	    || TOLOWER_ASC(printer_opts[OPT_PRINT_COLLATE].string[0]) == 'y');
2593     if (prt_collate)
2594     {
2595 	/* TODO: Get number of collated copies wanted. */
2596 	psettings->n_collated_copies = 1;
2597     }
2598     else
2599     {
2600 	/* TODO: Get number of uncollated copies wanted and update the cached
2601 	 * count.
2602 	 */
2603 	prt_num_copies = 1;
2604     }
2605 
2606     psettings->jobname = jobname;
2607 
2608     /*
2609      * Set up printer duplex and tumble based on Duplex option setting - default
2610      * is long sided duplex printing (i.e. no tumble).
2611      */
2612     prt_duplex = TRUE;
2613     prt_tumble = FALSE;
2614     psettings->duplex = 1;
2615     if (printer_opts[OPT_PRINT_DUPLEX].present)
2616     {
2617 	if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "off", 3) == 0)
2618 	{
2619 	    prt_duplex = FALSE;
2620 	    psettings->duplex = 0;
2621 	}
2622 	else if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "short", 5)
2623 									 == 0)
2624 	    prt_tumble = TRUE;
2625     }
2626 
2627     /* For now user abort not supported */
2628     psettings->user_abort = 0;
2629 
2630     /* If the user didn't specify a file name, use a temp file. */
2631     if (psettings->outfile == NULL)
2632     {
2633 	prt_ps_file_name = vim_tempname('p', TRUE);
2634 	if (prt_ps_file_name == NULL)
2635 	{
2636 	    emsg(_(e_notmp));
2637 	    return FAIL;
2638 	}
2639 	prt_ps_fd = mch_fopen((char *)prt_ps_file_name, WRITEBIN);
2640     }
2641     else
2642     {
2643 	p = expand_env_save(psettings->outfile);
2644 	if (p != NULL)
2645 	{
2646 	    prt_ps_fd = mch_fopen((char *)p, WRITEBIN);
2647 	    vim_free(p);
2648 	}
2649     }
2650     if (prt_ps_fd == NULL)
2651     {
2652 	emsg(_("E324: Can't open PostScript output file"));
2653 	mch_print_cleanup();
2654 	return FAIL;
2655     }
2656 
2657     prt_bufsiz = psettings->chars_per_line;
2658     if (prt_out_mbyte)
2659 	prt_bufsiz *= 2;
2660     ga_init2(&prt_ps_buffer, (int)sizeof(char), prt_bufsiz);
2661 
2662     prt_page_num = 0;
2663 
2664     prt_attribute_change = FALSE;
2665     prt_need_moveto = FALSE;
2666     prt_need_font = FALSE;
2667     prt_need_fgcol = FALSE;
2668     prt_need_bgcol = FALSE;
2669     prt_need_underline = FALSE;
2670 
2671     prt_file_error = FALSE;
2672 
2673     return OK;
2674 }
2675 
2676     static int
2677 prt_add_resource(struct prt_ps_resource_S *resource)
2678 {
2679     FILE*	fd_resource;
2680     char_u	resource_buffer[512];
2681     size_t	bytes_read;
2682 
2683     fd_resource = mch_fopen((char *)resource->filename, READBIN);
2684     if (fd_resource == NULL)
2685     {
2686 	semsg(_("E456: Can't open file \"%s\""), resource->filename);
2687 	return FALSE;
2688     }
2689     prt_dsc_resources("BeginResource", prt_resource_types[resource->type],
2690 						     (char *)resource->title);
2691 
2692     prt_dsc_textline("BeginDocument", (char *)resource->filename);
2693 
2694     for (;;)
2695     {
2696 	bytes_read = fread((char *)resource_buffer, sizeof(char_u),
2697 			   sizeof(resource_buffer), fd_resource);
2698 	if (ferror(fd_resource))
2699 	{
2700 	    semsg(_("E457: Can't read PostScript resource file \"%s\""),
2701 							    resource->filename);
2702 	    fclose(fd_resource);
2703 	    return FALSE;
2704 	}
2705 	if (bytes_read == 0)
2706 	    break;
2707 	prt_write_file_raw_len(resource_buffer, (int)bytes_read);
2708 	if (prt_file_error)
2709 	{
2710 	    fclose(fd_resource);
2711 	    return FALSE;
2712 	}
2713     }
2714     fclose(fd_resource);
2715 
2716     prt_dsc_noarg("EndDocument");
2717 
2718     prt_dsc_noarg("EndResource");
2719 
2720     return TRUE;
2721 }
2722 
2723     int
2724 mch_print_begin(prt_settings_T *psettings)
2725 {
2726     time_t	now;
2727     int		bbox[4];
2728     char	*p_time;
2729     double      left;
2730     double      right;
2731     double      top;
2732     double      bottom;
2733     struct prt_ps_resource_S *res_prolog;
2734     struct prt_ps_resource_S *res_encoding;
2735     char	buffer[256];
2736     char_u      *p_encoding;
2737     char_u	*p;
2738     struct prt_ps_resource_S *res_cidfont;
2739     struct prt_ps_resource_S *res_cmap;
2740     int		retval = FALSE;
2741 
2742     res_prolog = (struct prt_ps_resource_S *)
2743 				      alloc(sizeof(struct prt_ps_resource_S));
2744     res_encoding = (struct prt_ps_resource_S *)
2745 				      alloc(sizeof(struct prt_ps_resource_S));
2746     res_cidfont = (struct prt_ps_resource_S *)
2747 				      alloc(sizeof(struct prt_ps_resource_S));
2748     res_cmap = (struct prt_ps_resource_S *)
2749 				      alloc(sizeof(struct prt_ps_resource_S));
2750     if (res_prolog == NULL || res_encoding == NULL
2751 	    || res_cidfont == NULL || res_cmap == NULL)
2752 	goto theend;
2753 
2754     /*
2755      * PS DSC Header comments - no PS code!
2756      */
2757     prt_dsc_start();
2758     prt_dsc_textline("Title", (char *)psettings->jobname);
2759     if (!get_user_name((char_u *)buffer, 256))
2760 	STRCPY(buffer, "Unknown");
2761     prt_dsc_textline("For", buffer);
2762     prt_dsc_textline("Creator", VIM_VERSION_LONG);
2763     /* Note: to ensure Clean8bit I don't think we can use LC_TIME */
2764     now = time(NULL);
2765     p_time = ctime(&now);
2766     /* Note: ctime() adds a \n so we have to remove it :-( */
2767     p = vim_strchr((char_u *)p_time, '\n');
2768     if (p != NULL)
2769 	*p = NUL;
2770     prt_dsc_textline("CreationDate", p_time);
2771     prt_dsc_textline("DocumentData", "Clean8Bit");
2772     prt_dsc_textline("Orientation", "Portrait");
2773     prt_dsc_atend("Pages");
2774     prt_dsc_textline("PageOrder", "Ascend");
2775     /* The bbox does not change with orientation - it is always in the default
2776      * user coordinate system!  We have to recalculate right and bottom
2777      * coordinates based on the font metrics for the bbox to be accurate. */
2778     prt_page_margins(prt_mediasize[prt_media].width,
2779 					    prt_mediasize[prt_media].height,
2780 					    &left, &right, &top, &bottom);
2781     bbox[0] = (int)left;
2782     if (prt_portrait)
2783     {
2784 	/* In portrait printing the fixed point is the top left corner so we
2785 	 * derive the bbox from that point.  We have the expected cpl chars
2786 	 * across the media and lpp lines down the media.
2787 	 */
2788 	bbox[1] = (int)(top - (psettings->lines_per_page + prt_header_height())
2789 							    * prt_line_height);
2790 	bbox[2] = (int)(left + psettings->chars_per_line * prt_char_width
2791 									+ 0.5);
2792 	bbox[3] = (int)(top + 0.5);
2793     }
2794     else
2795     {
2796 	/* In landscape printing the fixed point is the bottom left corner so we
2797 	 * derive the bbox from that point.  We have lpp chars across the media
2798 	 * and cpl lines up the media.
2799 	 */
2800 	bbox[1] = (int)bottom;
2801 	bbox[2] = (int)(left + ((psettings->lines_per_page
2802 			      + prt_header_height()) * prt_line_height) + 0.5);
2803 	bbox[3] = (int)(bottom + psettings->chars_per_line * prt_char_width
2804 									+ 0.5);
2805     }
2806     prt_dsc_ints("BoundingBox", 4, bbox);
2807     /* The media width and height does not change with landscape printing! */
2808     prt_dsc_docmedia(prt_mediasize[prt_media].name,
2809 				prt_mediasize[prt_media].width,
2810 				prt_mediasize[prt_media].height,
2811 				(double)0, NULL, NULL);
2812     /* Define fonts needed */
2813     if (!prt_out_mbyte || prt_use_courier)
2814 	prt_dsc_font_resource("DocumentNeededResources", &prt_ps_courier_font);
2815     if (prt_out_mbyte)
2816     {
2817 	prt_dsc_font_resource((prt_use_courier ? NULL
2818 				 : "DocumentNeededResources"), &prt_ps_mb_font);
2819 	if (!prt_custom_cmap)
2820 	    prt_dsc_resources(NULL, "cmap", prt_cmap);
2821     }
2822 
2823     /* Search for external resources VIM supplies */
2824     if (!prt_find_resource("prolog", res_prolog))
2825     {
2826 	emsg(_("E456: Can't find PostScript resource file \"prolog.ps\""));
2827 	goto theend;
2828     }
2829     if (!prt_open_resource(res_prolog))
2830 	goto theend;
2831     if (!prt_check_resource(res_prolog, PRT_PROLOG_VERSION))
2832 	goto theend;
2833     if (prt_out_mbyte)
2834     {
2835 	/* Look for required version of multi-byte printing procset */
2836 	if (!prt_find_resource("cidfont", res_cidfont))
2837 	{
2838 	    emsg(_("E456: Can't find PostScript resource file \"cidfont.ps\""));
2839 	    goto theend;
2840 	}
2841 	if (!prt_open_resource(res_cidfont))
2842 	    goto theend;
2843 	if (!prt_check_resource(res_cidfont, PRT_CID_PROLOG_VERSION))
2844 	    goto theend;
2845     }
2846 
2847     /* Find an encoding to use for printing.
2848      * Check 'printencoding'. If not set or not found, then use 'encoding'. If
2849      * that cannot be found then default to "latin1".
2850      * Note: VIM specific encoding header is always skipped.
2851      */
2852     if (!prt_out_mbyte)
2853     {
2854 	p_encoding = enc_skip(p_penc);
2855 	if (*p_encoding == NUL
2856 		|| !prt_find_resource((char *)p_encoding, res_encoding))
2857 	{
2858 	    /* 'printencoding' not set or not supported - find alternate */
2859 	    int		props;
2860 
2861 	    p_encoding = enc_skip(p_enc);
2862 	    props = enc_canon_props(p_encoding);
2863 	    if (!(props & ENC_8BIT)
2864 		    || !prt_find_resource((char *)p_encoding, res_encoding))
2865 		/* 8-bit 'encoding' is not supported */
2866 		{
2867 		/* Use latin1 as default printing encoding */
2868 		p_encoding = (char_u *)"latin1";
2869 		if (!prt_find_resource((char *)p_encoding, res_encoding))
2870 		{
2871 		    semsg(_("E456: Can't find PostScript resource file \"%s.ps\""),
2872 			    p_encoding);
2873 		    goto theend;
2874 		}
2875 	    }
2876 	}
2877 	if (!prt_open_resource(res_encoding))
2878 	    goto theend;
2879 	/* For the moment there are no checks on encoding resource files to
2880 	 * perform */
2881     }
2882     else
2883     {
2884 	p_encoding = enc_skip(p_penc);
2885 	if (*p_encoding == NUL)
2886 	    p_encoding = enc_skip(p_enc);
2887 	if (prt_use_courier)
2888 	{
2889 	    /* Include ASCII range encoding vector */
2890 	    if (!prt_find_resource(prt_ascii_encoding, res_encoding))
2891 	    {
2892 		semsg(_("E456: Can't find PostScript resource file \"%s.ps\""),
2893 							  prt_ascii_encoding);
2894 		goto theend;
2895 	    }
2896 	    if (!prt_open_resource(res_encoding))
2897 		goto theend;
2898 	    /* For the moment there are no checks on encoding resource files to
2899 	     * perform */
2900 	}
2901     }
2902 
2903     prt_conv.vc_type = CONV_NONE;
2904     if (!(enc_canon_props(p_enc) & enc_canon_props(p_encoding) & ENC_8BIT)) {
2905 	/* Set up encoding conversion if required */
2906 	if (FAIL == convert_setup(&prt_conv, p_enc, p_encoding))
2907 	{
2908 	    semsg(_("E620: Unable to convert to print encoding \"%s\""),
2909 		    p_encoding);
2910 	    goto theend;
2911 	}
2912 	prt_do_conv = TRUE;
2913     }
2914     prt_do_conv = prt_conv.vc_type != CONV_NONE;
2915 
2916     if (prt_out_mbyte && prt_custom_cmap)
2917     {
2918 	/* Find user supplied CMap */
2919 	if (!prt_find_resource(prt_cmap, res_cmap))
2920 	{
2921 	    semsg(_("E456: Can't find PostScript resource file \"%s.ps\""),
2922 								    prt_cmap);
2923 	    goto theend;
2924 	}
2925 	if (!prt_open_resource(res_cmap))
2926 	    goto theend;
2927     }
2928 
2929     /* List resources supplied */
2930     STRCPY(buffer, res_prolog->title);
2931     STRCAT(buffer, " ");
2932     STRCAT(buffer, res_prolog->version);
2933     prt_dsc_resources("DocumentSuppliedResources", "procset", buffer);
2934     if (prt_out_mbyte)
2935     {
2936 	STRCPY(buffer, res_cidfont->title);
2937 	STRCAT(buffer, " ");
2938 	STRCAT(buffer, res_cidfont->version);
2939 	prt_dsc_resources(NULL, "procset", buffer);
2940 
2941 	if (prt_custom_cmap)
2942 	{
2943 	    STRCPY(buffer, res_cmap->title);
2944 	    STRCAT(buffer, " ");
2945 	    STRCAT(buffer, res_cmap->version);
2946 	    prt_dsc_resources(NULL, "cmap", buffer);
2947 	}
2948     }
2949     if (!prt_out_mbyte || prt_use_courier)
2950     {
2951 	STRCPY(buffer, res_encoding->title);
2952 	STRCAT(buffer, " ");
2953 	STRCAT(buffer, res_encoding->version);
2954 	prt_dsc_resources(NULL, "encoding", buffer);
2955     }
2956     prt_dsc_requirements(prt_duplex, prt_tumble, prt_collate,
2957 #ifdef FEAT_SYN_HL
2958 					psettings->do_syntax
2959 #else
2960 					0
2961 #endif
2962 					, prt_num_copies);
2963     prt_dsc_noarg("EndComments");
2964 
2965     /*
2966      * PS Document page defaults
2967      */
2968     prt_dsc_noarg("BeginDefaults");
2969 
2970     /* List font resources most likely common to all pages */
2971     if (!prt_out_mbyte || prt_use_courier)
2972 	prt_dsc_font_resource("PageResources", &prt_ps_courier_font);
2973     if (prt_out_mbyte)
2974     {
2975 	prt_dsc_font_resource((prt_use_courier ? NULL : "PageResources"),
2976 							    &prt_ps_mb_font);
2977 	if (!prt_custom_cmap)
2978 	    prt_dsc_resources(NULL, "cmap", prt_cmap);
2979     }
2980 
2981     /* Paper will be used for all pages */
2982     prt_dsc_textline("PageMedia", prt_mediasize[prt_media].name);
2983 
2984     prt_dsc_noarg("EndDefaults");
2985 
2986     /*
2987      * PS Document prolog inclusion - all required procsets.
2988      */
2989     prt_dsc_noarg("BeginProlog");
2990 
2991     /* Add required procsets - NOTE: order is important! */
2992     if (!prt_add_resource(res_prolog))
2993 	goto theend;
2994     if (prt_out_mbyte)
2995     {
2996 	/* Add CID font procset, and any user supplied CMap */
2997 	if (!prt_add_resource(res_cidfont))
2998 	    goto theend;
2999 	if (prt_custom_cmap && !prt_add_resource(res_cmap))
3000 	    goto theend;
3001     }
3002 
3003     if (!prt_out_mbyte || prt_use_courier)
3004 	/* There will be only one Roman font encoding to be included in the PS
3005 	 * file. */
3006 	if (!prt_add_resource(res_encoding))
3007 	    goto theend;
3008 
3009     prt_dsc_noarg("EndProlog");
3010 
3011     /*
3012      * PS Document setup - must appear after the prolog
3013      */
3014     prt_dsc_noarg("BeginSetup");
3015 
3016     /* Device setup - page size and number of uncollated copies */
3017     prt_write_int((int)prt_mediasize[prt_media].width);
3018     prt_write_int((int)prt_mediasize[prt_media].height);
3019     prt_write_int(0);
3020     prt_write_string("sps\n");
3021     prt_write_int(prt_num_copies);
3022     prt_write_string("nc\n");
3023     prt_write_boolean(prt_duplex);
3024     prt_write_boolean(prt_tumble);
3025     prt_write_string("dt\n");
3026     prt_write_boolean(prt_collate);
3027     prt_write_string("c\n");
3028 
3029     /* Font resource inclusion and definition */
3030     if (!prt_out_mbyte || prt_use_courier)
3031     {
3032 	/* When using Courier for ASCII range when printing multi-byte, need to
3033 	 * pick up ASCII encoding to use with it. */
3034 	if (prt_use_courier)
3035 	    p_encoding = (char_u *)prt_ascii_encoding;
3036 	prt_dsc_resources("IncludeResource", "font",
3037 			  prt_ps_courier_font.ps_fontname[PRT_PS_FONT_ROMAN]);
3038 	prt_def_font("F0", (char *)p_encoding, (int)prt_line_height,
3039 		     prt_ps_courier_font.ps_fontname[PRT_PS_FONT_ROMAN]);
3040 	prt_dsc_resources("IncludeResource", "font",
3041 			  prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLD]);
3042 	prt_def_font("F1", (char *)p_encoding, (int)prt_line_height,
3043 		     prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLD]);
3044 	prt_dsc_resources("IncludeResource", "font",
3045 			  prt_ps_courier_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
3046 	prt_def_font("F2", (char *)p_encoding, (int)prt_line_height,
3047 		     prt_ps_courier_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
3048 	prt_dsc_resources("IncludeResource", "font",
3049 			  prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
3050 	prt_def_font("F3", (char *)p_encoding, (int)prt_line_height,
3051 		     prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
3052     }
3053     if (prt_out_mbyte)
3054     {
3055 	/* Define the CID fonts to be used in the job.	Typically CJKV fonts do
3056 	 * not have an italic form being a western style, so where no font is
3057 	 * defined for these faces VIM falls back to an existing face.
3058 	 * Note: if using Courier for the ASCII range then the printout will
3059 	 * have bold/italic/bolditalic regardless of the setting of printmbfont.
3060 	 */
3061 	prt_dsc_resources("IncludeResource", "font",
3062 			  prt_ps_mb_font.ps_fontname[PRT_PS_FONT_ROMAN]);
3063 	if (!prt_custom_cmap)
3064 	    prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
3065 	prt_def_cidfont("CF0", (int)prt_line_height,
3066 			prt_ps_mb_font.ps_fontname[PRT_PS_FONT_ROMAN]);
3067 
3068 	if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD] != NULL)
3069 	{
3070 	    prt_dsc_resources("IncludeResource", "font",
3071 			      prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD]);
3072 	    if (!prt_custom_cmap)
3073 		prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
3074 	    prt_def_cidfont("CF1", (int)prt_line_height,
3075 			    prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD]);
3076 	}
3077 	else
3078 	    /* Use ROMAN for BOLD */
3079 	    prt_dup_cidfont("CF0", "CF1");
3080 
3081 	if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE] != NULL)
3082 	{
3083 	    prt_dsc_resources("IncludeResource", "font",
3084 			      prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
3085 	    if (!prt_custom_cmap)
3086 		prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
3087 	    prt_def_cidfont("CF2", (int)prt_line_height,
3088 			    prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
3089 	}
3090 	else
3091 	    /* Use ROMAN for OBLIQUE */
3092 	    prt_dup_cidfont("CF0", "CF2");
3093 
3094 	if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE] != NULL)
3095 	{
3096 	    prt_dsc_resources("IncludeResource", "font",
3097 			      prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
3098 	    if (!prt_custom_cmap)
3099 		prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
3100 	    prt_def_cidfont("CF3", (int)prt_line_height,
3101 			    prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
3102 	}
3103 	else
3104 	    /* Use BOLD for BOLDOBLIQUE */
3105 	    prt_dup_cidfont("CF1", "CF3");
3106     }
3107 
3108     /* Misc constant vars used for underlining and background rects */
3109     prt_def_var("UO", PRT_PS_FONT_TO_USER(prt_line_height,
3110 						prt_ps_font->uline_offset), 2);
3111     prt_def_var("UW", PRT_PS_FONT_TO_USER(prt_line_height,
3112 						 prt_ps_font->uline_width), 2);
3113     prt_def_var("BO", prt_bgcol_offset, 2);
3114 
3115     prt_dsc_noarg("EndSetup");
3116 
3117     /* Fail if any problems writing out to the PS file */
3118     retval = !prt_file_error;
3119 
3120 theend:
3121     vim_free(res_prolog);
3122     vim_free(res_encoding);
3123     vim_free(res_cidfont);
3124     vim_free(res_cmap);
3125 
3126     return retval;
3127 }
3128 
3129     void
3130 mch_print_end(prt_settings_T *psettings)
3131 {
3132     prt_dsc_noarg("Trailer");
3133 
3134     /*
3135      * Output any info we don't know in toto until we finish
3136      */
3137     prt_dsc_ints("Pages", 1, &prt_page_num);
3138 
3139     prt_dsc_noarg("EOF");
3140 
3141     /* Write CTRL-D to close serial communication link if used.
3142      * NOTHING MUST BE WRITTEN AFTER THIS! */
3143     prt_write_file((char_u *)IF_EB("\004", "\067"));
3144 
3145     if (!prt_file_error && psettings->outfile == NULL
3146 					&& !got_int && !psettings->user_abort)
3147     {
3148 	/* Close the file first. */
3149 	if (prt_ps_fd != NULL)
3150 	{
3151 	    fclose(prt_ps_fd);
3152 	    prt_ps_fd = NULL;
3153 	}
3154 	prt_message((char_u *)_("Sending to printer..."));
3155 
3156 	/* Not printing to a file: use 'printexpr' to print the file. */
3157 	if (eval_printexpr(prt_ps_file_name, psettings->arguments) == FAIL)
3158 	    emsg(_("E365: Failed to print PostScript file"));
3159 	else
3160 	    prt_message((char_u *)_("Print job sent."));
3161     }
3162 
3163     mch_print_cleanup();
3164 }
3165 
3166     int
3167 mch_print_end_page(void)
3168 {
3169     prt_flush_buffer();
3170 
3171     prt_write_string("re sp\n");
3172 
3173     prt_dsc_noarg("PageTrailer");
3174 
3175     return !prt_file_error;
3176 }
3177 
3178     int
3179 mch_print_begin_page(char_u *str UNUSED)
3180 {
3181     int		page_num[2];
3182 
3183     prt_page_num++;
3184 
3185     page_num[0] = page_num[1] = prt_page_num;
3186     prt_dsc_ints("Page", 2, page_num);
3187 
3188     prt_dsc_noarg("BeginPageSetup");
3189 
3190     prt_write_string("sv\n0 g\n");
3191     prt_in_ascii = !prt_out_mbyte;
3192     if (prt_out_mbyte)
3193 	prt_write_string("CF0 sf\n");
3194     else
3195 	prt_write_string("F0 sf\n");
3196     prt_fgcol = PRCOLOR_BLACK;
3197     prt_bgcol = PRCOLOR_WHITE;
3198     prt_font = PRT_PS_FONT_ROMAN;
3199 
3200     /* Set up page transformation for landscape printing. */
3201     if (!prt_portrait)
3202     {
3203 	prt_write_int(-((int)prt_mediasize[prt_media].width));
3204 	prt_write_string("sl\n");
3205     }
3206 
3207     prt_dsc_noarg("EndPageSetup");
3208 
3209     /* We have reset the font attributes, force setting them again. */
3210     curr_bg = (long_u)0xffffffff;
3211     curr_fg = (long_u)0xffffffff;
3212     curr_bold = MAYBE;
3213 
3214     return !prt_file_error;
3215 }
3216 
3217     int
3218 mch_print_blank_page(void)
3219 {
3220     return (mch_print_begin_page(NULL) ? (mch_print_end_page()) : FALSE);
3221 }
3222 
3223 static float prt_pos_x = 0;
3224 static float prt_pos_y = 0;
3225 
3226     void
3227 mch_print_start_line(int margin, int page_line)
3228 {
3229     prt_pos_x = prt_left_margin;
3230     if (margin)
3231 	prt_pos_x -= prt_number_width;
3232 
3233     prt_pos_y = prt_top_margin - prt_first_line_height -
3234 					page_line * prt_line_height;
3235 
3236     prt_attribute_change = TRUE;
3237     prt_need_moveto = TRUE;
3238     prt_half_width = FALSE;
3239 }
3240 
3241     int
3242 mch_print_text_out(char_u *textp, int len UNUSED)
3243 {
3244     char_u	*p = textp;
3245     int		need_break;
3246     char_u	ch;
3247     char_u      ch_buff[8];
3248     float       char_width;
3249     float       next_pos;
3250     int		in_ascii;
3251     int		half_width;
3252     char_u	*tofree = NULL;
3253 
3254     char_width = prt_char_width;
3255 
3256     /* Ideally VIM would create a rearranged CID font to combine a Roman and
3257      * CJKV font to do what VIM is doing here - use a Roman font for characters
3258      * in the ASCII range, and the original CID font for everything else.
3259      * The problem is that GhostScript still (as of 8.13) does not support
3260      * rearranged fonts even though they have been documented by Adobe for 7
3261      * years!  If they ever do, a lot of this code will disappear.
3262      */
3263     if (prt_use_courier)
3264     {
3265 	in_ascii = (len == 1 && *p < 0x80);
3266 	if (prt_in_ascii)
3267 	{
3268 	    if (!in_ascii)
3269 	    {
3270 		/* No longer in ASCII range - need to switch font */
3271 		prt_in_ascii = FALSE;
3272 		prt_need_font = TRUE;
3273 		prt_attribute_change = TRUE;
3274 	    }
3275 	}
3276 	else if (in_ascii)
3277 	{
3278 	    /* Now in ASCII range - need to switch font */
3279 	    prt_in_ascii = TRUE;
3280 	    prt_need_font = TRUE;
3281 	    prt_attribute_change = TRUE;
3282 	}
3283     }
3284     if (prt_out_mbyte)
3285     {
3286 	half_width = ((*mb_ptr2cells)(p) == 1);
3287 	if (half_width)
3288 	    char_width /= 2;
3289 	if (prt_half_width)
3290 	{
3291 	    if (!half_width)
3292 	    {
3293 		prt_half_width = FALSE;
3294 		prt_pos_x += prt_char_width/4;
3295 		prt_need_moveto = TRUE;
3296 		prt_attribute_change = TRUE;
3297 	    }
3298 	}
3299 	else if (half_width)
3300 	{
3301 	    prt_half_width = TRUE;
3302 	    prt_pos_x += prt_char_width/4;
3303 	    prt_need_moveto = TRUE;
3304 	    prt_attribute_change = TRUE;
3305 	}
3306     }
3307 
3308     /* Output any required changes to the graphics state, after flushing any
3309      * text buffered so far.
3310      */
3311     if (prt_attribute_change)
3312     {
3313 	prt_flush_buffer();
3314 	/* Reset count of number of chars that will be printed */
3315 	prt_text_run = 0;
3316 
3317 	if (prt_need_moveto)
3318 	{
3319 	    prt_pos_x_moveto = prt_pos_x;
3320 	    prt_pos_y_moveto = prt_pos_y;
3321 	    prt_do_moveto = TRUE;
3322 
3323 	    prt_need_moveto = FALSE;
3324 	}
3325 	if (prt_need_font)
3326 	{
3327 	    if (!prt_in_ascii)
3328 		prt_write_string("CF");
3329 	    else
3330 		prt_write_string("F");
3331 	    prt_write_int(prt_font);
3332 	    prt_write_string("sf\n");
3333 	    prt_need_font = FALSE;
3334 	}
3335 	if (prt_need_fgcol)
3336 	{
3337 	    int     r, g, b;
3338 	    r = ((unsigned)prt_fgcol & 0xff0000) >> 16;
3339 	    g = ((unsigned)prt_fgcol & 0xff00) >> 8;
3340 	    b = prt_fgcol & 0xff;
3341 
3342 	    prt_write_real(r / 255.0, 3);
3343 	    if (r == g && g == b)
3344 		prt_write_string("g\n");
3345 	    else
3346 	    {
3347 		prt_write_real(g / 255.0, 3);
3348 		prt_write_real(b / 255.0, 3);
3349 		prt_write_string("r\n");
3350 	    }
3351 	    prt_need_fgcol = FALSE;
3352 	}
3353 
3354 	if (prt_bgcol != PRCOLOR_WHITE)
3355 	{
3356 	    prt_new_bgcol = prt_bgcol;
3357 	    if (prt_need_bgcol)
3358 		prt_do_bgcol = TRUE;
3359 	}
3360 	else
3361 	    prt_do_bgcol = FALSE;
3362 	prt_need_bgcol = FALSE;
3363 
3364 	if (prt_need_underline)
3365 	    prt_do_underline = prt_underline;
3366 	prt_need_underline = FALSE;
3367 
3368 	prt_attribute_change = FALSE;
3369     }
3370 
3371     if (prt_do_conv)
3372     {
3373 	/* Convert from multi-byte to 8-bit encoding */
3374 	tofree = p = string_convert(&prt_conv, p, &len);
3375 	if (p == NULL)
3376 	{
3377 	    p = (char_u *)"";
3378 	    len = 0;
3379 	}
3380     }
3381 
3382     if (prt_out_mbyte)
3383     {
3384 	/* Multi-byte character strings are represented more efficiently as hex
3385 	 * strings when outputting clean 8 bit PS.
3386 	 */
3387 	while (len-- > 0)
3388 	{
3389 	    ch = prt_hexchar[(unsigned)(*p) >> 4];
3390 	    ga_append(&prt_ps_buffer, ch);
3391 	    ch = prt_hexchar[(*p) & 0xf];
3392 	    ga_append(&prt_ps_buffer, ch);
3393 	    p++;
3394 	}
3395     }
3396     else
3397     {
3398 	/* Add next character to buffer of characters to output.
3399 	 * Note: One printed character may require several PS characters to
3400 	 * represent it, but we only count them as one printed character.
3401 	 */
3402 	ch = *p;
3403 	if (ch < 32 || ch == '(' || ch == ')' || ch == '\\')
3404 	{
3405 	    /* Convert non-printing characters to either their escape or octal
3406 	     * sequence, ensures PS sent over a serial line does not interfere
3407 	     * with the comms protocol.  Note: For EBCDIC we need to write out
3408 	     * the escape sequences as ASCII codes!
3409 	     * Note 2: Char codes < 32 are identical in EBCDIC and ASCII AFAIK!
3410 	     */
3411 	    ga_append(&prt_ps_buffer, IF_EB('\\', 0134));
3412 	    switch (ch)
3413 	    {
3414 		case BS:   ga_append(&prt_ps_buffer, IF_EB('b', 0142)); break;
3415 		case TAB:  ga_append(&prt_ps_buffer, IF_EB('t', 0164)); break;
3416 		case NL:   ga_append(&prt_ps_buffer, IF_EB('n', 0156)); break;
3417 		case FF:   ga_append(&prt_ps_buffer, IF_EB('f', 0146)); break;
3418 		case CAR:  ga_append(&prt_ps_buffer, IF_EB('r', 0162)); break;
3419 		case '(':  ga_append(&prt_ps_buffer, IF_EB('(', 0050)); break;
3420 		case ')':  ga_append(&prt_ps_buffer, IF_EB(')', 0051)); break;
3421 		case '\\': ga_append(&prt_ps_buffer, IF_EB('\\', 0134)); break;
3422 
3423 		default:
3424 			   sprintf((char *)ch_buff, "%03o", (unsigned int)ch);
3425 #ifdef EBCDIC
3426 			   ebcdic2ascii(ch_buff, 3);
3427 #endif
3428 			   ga_append(&prt_ps_buffer, ch_buff[0]);
3429 			   ga_append(&prt_ps_buffer, ch_buff[1]);
3430 			   ga_append(&prt_ps_buffer, ch_buff[2]);
3431 			   break;
3432 	    }
3433 	}
3434 	else
3435 	    ga_append(&prt_ps_buffer, ch);
3436     }
3437 
3438     /* Need to free any translated characters */
3439     vim_free(tofree);
3440 
3441     prt_text_run += char_width;
3442     prt_pos_x += char_width;
3443 
3444     /* The downside of fp - use relative error on right margin check */
3445     next_pos = prt_pos_x + prt_char_width;
3446     need_break = (next_pos > prt_right_margin) &&
3447 		    ((next_pos - prt_right_margin) > (prt_right_margin*1e-5));
3448 
3449     if (need_break)
3450 	prt_flush_buffer();
3451 
3452     return need_break;
3453 }
3454 
3455     void
3456 mch_print_set_font(int iBold, int iItalic, int iUnderline)
3457 {
3458     int		font = 0;
3459 
3460     if (iBold)
3461 	font |= 0x01;
3462     if (iItalic)
3463 	font |= 0x02;
3464 
3465     if (font != prt_font)
3466     {
3467 	prt_font = font;
3468 	prt_attribute_change = TRUE;
3469 	prt_need_font = TRUE;
3470     }
3471     if (prt_underline != iUnderline)
3472     {
3473 	prt_underline = iUnderline;
3474 	prt_attribute_change = TRUE;
3475 	prt_need_underline = TRUE;
3476     }
3477 }
3478 
3479     void
3480 mch_print_set_bg(long_u bgcol)
3481 {
3482     prt_bgcol = (int)bgcol;
3483     prt_attribute_change = TRUE;
3484     prt_need_bgcol = TRUE;
3485 }
3486 
3487     void
3488 mch_print_set_fg(long_u fgcol)
3489 {
3490     if (fgcol != (long_u)prt_fgcol)
3491     {
3492 	prt_fgcol = (int)fgcol;
3493 	prt_attribute_change = TRUE;
3494 	prt_need_fgcol = TRUE;
3495     }
3496 }
3497 
3498 # endif /*FEAT_POSTSCRIPT*/
3499 #endif /*FEAT_PRINTER*/
3500