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