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