~ubuntu-branches/ubuntu/precise/a2ps/precise-security

« back to all changes in this revision

Viewing changes to lib/psgen.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Schulze
  • Date: 2004-12-10 08:26:05 UTC
  • Revision ID: james.westby@ubuntu.com-20041210082605-rha33nklyielibe4
Tags: upstream-4.13b
ImportĀ upstreamĀ versionĀ 4.13b

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * psgen.c
 
3
 * routines for the postscript generation
 
4
 *
 
5
 * Copyright (c) 1988-1993 Miguel Santana
 
6
 * Copyright (c) 1995-1999 Akim Demaille, Miguel Santana
 
7
 */
 
8
 
 
9
/*
 
10
 * This file is part of a2ps.
 
11
 *
 
12
 * This program is free software; you can redistribute it and/or modify
 
13
 * it under the terms of the GNU General Public License as published by
 
14
 * the Free Software Foundation; either version 2, or (at your option)
 
15
 * any later version.
 
16
 *
 
17
 * This program is distributed in the hope that it will be useful,
 
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
20
 * GNU General Public License for more details.
 
21
 *
 
22
 * You should have received a copy of the GNU General Public License
 
23
 * along with this program; see the file COPYING.  If not, write to
 
24
 * the Free Software Foundation, 59 Temple Place - Suite 330,
 
25
 * Boston, MA 02111-1307, USA.
 
26
 */
 
27
 
 
28
/*
 
29
 * $Id: psgen.c,v 1.82 1998/03/02 15:37:45 demaille Exp $
 
30
 */
 
31
 
 
32
#include "a2ps.h"               /* most global variables                */
 
33
#include "prolog.h"
 
34
#include "psgen.h"
 
35
#include "routines.h"           /* general interest routines            */
 
36
#include "output.h"             /* Diverted outputs                     */
 
37
#include "media.h"
 
38
#include "faces.h"
 
39
#include "metaseq.h"
 
40
#include "message.h"
 
41
#include "fonts.h"
 
42
#include "dsc.h"
 
43
#include "jobs.h"
 
44
#include "fjobs.h"
 
45
#include "prange.h"
 
46
 
 
47
#define PRINT_HEADER                                    \
 
48
        (!IS_EMPTY(job->header))
 
49
 
 
50
#define PRINT_FOOTER                                    \
 
51
        (!IS_EMPTY(job->footer)                         \
 
52
         || !IS_EMPTY(job->left_footer)                 \
 
53
         || !IS_EMPTY(job->right_footer))
 
54
 
 
55
#define PRINT_TITLE                                     \
 
56
        (!IS_EMPTY(job->center_title)                   \
 
57
         || !IS_EMPTY(job->left_title)                  \
 
58
         || !IS_EMPTY(job->right_title))
 
59
 
 
60
/* Width in characters for line prefix  */
 
61
#define prefix_size     (job->numbering ? 5 : 0)
 
62
 
 
63
/* Is this the first page for the current file?         */
 
64
#define is_first_page   \
 
65
    ((job->pages - CURRENT_FILE (job)->first_page) > 1)
 
66
 
 
67
#define jdiv    job->divertion
 
68
 
 
69
/* return the max of two >-comparable stuff */
 
70
#define A2_MAX(X,Y)        (((X) > (Y)) ? (X) : (Y))
 
71
 
 
72
 
 
73
/****************************************************************/
 
74
/*              Formating help routines                         */
 
75
/****************************************************************/
 
76
/*
 
77
 * Print a char in a form accepted by postscript printers.
 
78
 * Returns number of columns used (on the output) to print the char.
 
79
 */
 
80
static int
 
81
ps_escape_char (a2ps_job * job, uchar c, uchar * res)
 
82
{
 
83
  int len=0;
 
84
 
 
85
  /* The number of columns used must be calculated here because of the
 
86
   * \ before non-ascii chars, and (, ), and \ itself */
 
87
 
 
88
  /* Printable and 7bit clean caracters */
 
89
  if (' ' <= c && c < 0177) {
 
90
    if (c == '(' || c == ')' || c == '\\')
 
91
      USTRCCAT(res, '\\');
 
92
    USTRCCAT(res, c);
 
93
    return 1;
 
94
  }
 
95
 
 
96
  /* Printable, but not 7bit clean caracters */
 
97
  if (encoding_char_exists (job->encoding, job->status->face, c)
 
98
      && ((0177 < c) || (c < 040))) {
 
99
    sprintf ((char *)res, "%s\\%o", res, c);
 
100
    return 1;
 
101
  }
 
102
 
 
103
  /* We are dealing with a non-printing character */
 
104
  job->status->nonprinting_chars++;
 
105
 
 
106
  /* We do this in here for speed.  We could have first escaped, then
 
107
    consider the new string as the string to ps-escape. */
 
108
  switch (job->unprintable_format) {
 
109
  case octal:
 
110
    sprintf ((char *) res, "\\\\%03o", c);
 
111
    return 4;
 
112
 
 
113
  case hexa:
 
114
    sprintf ((char *) res, "\\\\x%02x", c);
 
115
    return 4;
 
116
 
 
117
  case question_mark:
 
118
    USTRCCAT (res, '?');
 
119
    return 1;
 
120
 
 
121
  case space:
 
122
    USTRCCAT (res, ' ');
 
123
    return 1;
 
124
 
 
125
  case caret:
 
126
    if (0177 < c) {
 
127
      ustrcat(res, "M-");
 
128
      len += 2;
 
129
      c &= 0177;
 
130
    }
 
131
 
 
132
    if (c < ' ') {
 
133
      USTRCCAT(res, '^');
 
134
      len += 2;
 
135
      c += '@';
 
136
      if (c == '(' || c == ')' || c == '\\')
 
137
        USTRCCAT(res, '\\');
 
138
      USTRCCAT(res, c);
 
139
    } else if (c == 0177) {
 
140
      ustrcat(res, "^?");
 
141
      len += 2;
 
142
    } else {
 
143
      if (c == '(' || c == ')' || c == '\\')
 
144
        USTRCCAT(res, '\\');
 
145
      USTRCCAT(res, c);
 
146
      len++;
 
147
    }
 
148
    return len;
 
149
 
 
150
  case Emacs:
 
151
    if (0177 < c) {
 
152
      ustrcat(res, "M-");
 
153
      len += 2;
 
154
      c &= 0177;
 
155
    }
 
156
 
 
157
    if (c < ' ') {
 
158
      ustrcat (res, "C-");
 
159
      len += 3;
 
160
      c += '@';
 
161
      if (c == '(' || c == ')' || c == '\\')
 
162
        USTRCCAT(res, '\\');
 
163
      USTRCCAT(res, c);
 
164
    } else if (c == 0177) {
 
165
      ustrcat(res, "C-?");
 
166
      len += 3;
 
167
    } else {
 
168
      if (c == '(' || c == ')' || c == '\\')
 
169
        USTRCCAT(res, '\\');
 
170
      USTRCCAT(res, c);
 
171
      len++;
 
172
    }
 
173
    return len;
 
174
 
 
175
  }
 
176
  return 0;
 
177
}
 
178
 
 
179
/*
 
180
 * Print a string in a form accepted by postscript printers.
 
181
 */
 
182
static int
 
183
ps_escape_string (a2ps_job * job, uchar * string, uchar * res)
 
184
{
 
185
  size_t i;
 
186
  int delta_column=0;
 
187
 
 
188
  for (i = 0 ; i < ustrlen (string) ; i++)
 
189
    delta_column += ps_escape_char (job, string[i], res);
 
190
 
 
191
  return delta_column;
 
192
}
 
193
 
 
194
/*
 
195
 * Output the formated marker.
 
196
 */
 
197
static void
 
198
output_marker (a2ps_job * job, const char * kind, uchar * marker)
 
199
{
 
200
  uchar *cp, buf[256];
 
201
  int i;
 
202
 
 
203
  cp = expand_user_string (job, CURRENT_FILE (job),
 
204
                           (const uchar *) kind, marker);
 
205
 
 
206
  for (i = 0 ; cp[i] ; i++) {
 
207
    switch (cp[i]) {
 
208
    case FILE_LAST_PAGE:
 
209
      output_delayed_int (jdiv, &(CURRENT_FILE (job)->last_page));
 
210
      break;
 
211
    case FILE_LAST_SHEET:
 
212
      output_delayed_int (jdiv, &(CURRENT_FILE (job)->last_sheet));
 
213
      break;
 
214
    case FILE_NB_PAGES:
 
215
      output_delayed_int (jdiv, &(CURRENT_FILE (job)->pages));
 
216
      break;
 
217
    case FILE_NB_SHEETS:
 
218
      output_delayed_int (jdiv, &(CURRENT_FILE (job)->sheets));
 
219
      break;
 
220
    case FILE_NB_LINES:
 
221
      output_delayed_int (jdiv, &(CURRENT_FILE (job)->lines));
 
222
      break;
 
223
    case JOB_NB_PAGES:
 
224
      output_delayed_int (jdiv, &job->pages);
 
225
      break;
 
226
    case JOB_NB_SHEETS:
 
227
      output_delayed_int (jdiv, &job->sheets);
 
228
      break;
 
229
    case JOB_NB_FILES:
 
230
      output_delayed_int (jdiv, &job->total_files);
 
231
      break;
 
232
    default:
 
233
      *buf = '\0';
 
234
      ps_escape_char (job, cp[i], buf);
 
235
      output (jdiv, (char *) buf);
 
236
      break;
 
237
    }
 
238
  }
 
239
}
 
240
 
 
241
/****************************************************************/
 
242
/*              Dealing with the encodings                      */
 
243
/****************************************************************/
 
244
/*
 
245
 * Change the current encoding
 
246
 */
 
247
void
 
248
ps_set_encoding (a2ps_job * job, struct encoding * enc)
 
249
{
 
250
  set_encoding (job, enc);
 
251
  job->status->opened_encoding = enc;
 
252
  output (jdiv, "%sdict begin\n", encoding_get_key (enc));
 
253
}
 
254
 
 
255
void
 
256
ps_end_encoding (a2ps_job * job)
 
257
{
 
258
  if (job->status->opened_encoding)
 
259
    output (jdiv, "end %% of %sdict\n",
 
260
            encoding_get_key (job->status->opened_encoding));
 
261
  set_encoding (job, NULL);
 
262
  job->status->opened_encoding = NULL;
 
263
}
 
264
 
 
265
/*
 
266
 * Switch of encoding while we are _not_ in the body.  E.g., we
 
267
 * are dealing with the headers.
 
268
 *
 
269
 * We want to save the previous encoding, because, say we
 
270
 * were printing -X latin1, we have switched to latin2 dynamically.
 
271
 * Right now, we need to change page,
 
272
 * so we are using back latin1, but we must remember of latin2
 
273
 * for the following page.
 
274
 */
 
275
static void
 
276
ps_internal_switch_encoding (a2ps_job * job, struct encoding * enc)
 
277
{
 
278
  if (job->encoding != enc) {
 
279
    /* If there are no dictionary opened yet, it is because we are
 
280
     * changing of dict outside the text body (e.g. even before
 
281
     * the first output char).  In this case, do not open a
 
282
     * ps dict: it is not time yet.
 
283
     */
 
284
    if (job->status->opened_encoding) {
 
285
      ps_end_encoding (job);
 
286
      ps_set_encoding (job, enc);
 
287
    } else {
 
288
      ps_end_encoding (job);
 
289
      set_encoding (job, enc);
 
290
    }
 
291
    /* Make sure to re-declare the font */
 
292
    job->status->face_declared = false;
 
293
  }
 
294
}
 
295
 
 
296
/*
 
297
 * Switch the encoding during the print process (ie. from
 
298
 * within the body of the text)
 
299
 * We have to take care of closing the line currently being printed
 
300
 * and restore the state, i.e. opening a string to print.
 
301
 */
 
302
void
 
303
ps_switch_encoding (a2ps_job * job, struct encoding * enc)
 
304
{
 
305
  if (job->encoding != enc)
 
306
    {
 
307
      if (!job->status->start_line) {
 
308
        if (!job->status->face_declared)
 
309
          output (jdiv, ") %s\n", face_eo_ps (job->status->face));
 
310
        else
 
311
          output (jdiv, ") S\n");
 
312
      }
 
313
      ps_internal_switch_encoding (job, enc);
 
314
      job->saved_encoding = enc;
 
315
      if (!job->status->start_line)
 
316
        output_char (jdiv, '(');
 
317
    }
 
318
}
 
319
 
 
320
static void
 
321
ps_push_encoding (a2ps_job * job, struct encoding * enc)
 
322
{
 
323
  job->saved_encoding = job->encoding;
 
324
  ps_internal_switch_encoding (job, enc);
 
325
}
 
326
 
 
327
static void
 
328
ps_pop_encoding (a2ps_job * job)
 
329
{
 
330
  ps_internal_switch_encoding (job, job->saved_encoding);
 
331
}
 
332
 
 
333
/****************************************************************/
 
334
/*              Printing a physical page                        */
 
335
/****************************************************************/
 
336
/*
 
337
 * Print the prolog necessary for printing each physical page.
 
338
 * Adobe convention for page independence is enforced through this routine.
 
339
 * Note that is may called for a second time on a single sheet if two
 
340
 * different files are printed on the same sheet.
 
341
 */
 
342
static void
 
343
page_begin (a2ps_job * job)
 
344
{
 
345
  job->virtual = 1;
 
346
 
 
347
  if (print_page (job, job->pages))
 
348
    job->sheets++;
 
349
 
 
350
  /* The number of pages has not been updated yet: add 1 */
 
351
  CURRENT_FILE (job)->top_page = CURRENT_FILE (job)->pages + 1;
 
352
  /*
 
353
   * Getting ready to delay the page label
 
354
   * This means to build a new string handler, store it, and fill it later
 
355
   * We do this because one would like to have all the information on
 
356
   * what is on the current page before making the page label.
 
357
   */
 
358
  if (!output_is_to_void (jdiv))
 
359
    {
 
360
      output (jdiv, "%%%%Page: (");
 
361
      job->status->page_label = XMALLOC (uchar *, 1);
 
362
      output_delayed_string (jdiv, job->status->page_label);
 
363
      output (jdiv, ") %d\n", job->sheets);
 
364
    }
 
365
 
 
366
  /* Reinitialize state variables for each new sheet */
 
367
  output (jdiv, "%%%%BeginPageSetup\n");
 
368
  output (jdiv, "/pagesave save def\n");
 
369
 
 
370
  /* Shift front side sheets */
 
371
  if (job->margin
 
372
      && (job->duplex == simplex || (job->sheets % 2)))
 
373
    output (jdiv, "%d 0 translate\n", job->margin);
 
374
 
 
375
  if (job->orientation == landscape)
 
376
    output (jdiv, "sh 0 translate 90 rotate\n");
 
377
 
 
378
  output (jdiv, "%%%%EndPageSetup\n");
 
379
 
 
380
  if (job->debug)
 
381
    output (jdiv, "\
 
382
%% Display the bounding box\n\
 
383
  gsave\n\
 
384
    llx lly moveto\n\
 
385
    2 setlinewidth\n\
 
386
    0.9 setgray\n\
 
387
    urx lly lineto\n\
 
388
    urx ury lineto\n\
 
389
    llx ury lineto\n\
 
390
    closepath stroke\n\
 
391
  grestore\n\n");
 
392
 
 
393
  /* Set the encoding */
 
394
  ps_set_encoding (job, job->requested_encoding);
 
395
 
 
396
  /* water marks (only once per sheet) */
 
397
  if (!IS_EMPTY(job->water))
 
398
    {
 
399
      output_char (jdiv, '(');
 
400
      output_marker (job, "water mark", job->water);
 
401
      output (jdiv,
 
402
              ") %4.2f water\n",
 
403
              ((float) atan2 ((double) job->medium->w - job->margin,
 
404
                              (double) job->medium->h)
 
405
               / 3.14159265 * 180));
 
406
    }
 
407
 
 
408
  /* Move to the lower left point of the drawable area */
 
409
  output (jdiv, "gsave\n");
 
410
  output (jdiv, "llx lly %d add translate\n",
 
411
          PRINT_FOOTER * HEADERS_H);
 
412
  /* Set the encoding */
 
413
  ps_internal_switch_encoding (job, job->saved_encoding);
 
414
}
 
415
 
 
416
/* The job on the page is over: puts the headers and footers,
 
417
 * then print the physical page.
 
418
 */
 
419
static void
 
420
page_end (a2ps_job * job)
 
421
{
 
422
  /* The page label has been divertered (through a handler).
 
423
   * Fill that handler with the correct page label value
 
424
   */
 
425
  *(job->status->page_label) =
 
426
    xustrdup (expand_user_string (job, CURRENT_FILE (job),
 
427
                                  (const uchar *) "Page label",
 
428
                                  job->status->page_label_format));
 
429
 
 
430
  output (jdiv, "grestore\n");
 
431
 
 
432
  /* All the headers should be written using the requested encoding */
 
433
  ps_push_encoding (job, job->requested_encoding);
 
434
 
 
435
  /* Print the right header */
 
436
  if (PRINT_HEADER) {
 
437
    output_char (jdiv, '(');
 
438
    output_marker (job, "right header", job->header);
 
439
    output (jdiv, ") rhead\n");
 
440
  }
 
441
 
 
442
  /* Print the center footer.
 
443
   * Use dynamic markers in priority
 
444
   */
 
445
  if (PRINT_FOOTER) {
 
446
    if (!IS_EMPTY(job->footer)) {
 
447
      output_char (jdiv, '(');
 
448
      output_marker (job, "center footer", job->footer);
 
449
      output (jdiv, ") ");
 
450
    } else
 
451
      output (jdiv, "() ");
 
452
 
 
453
    /* Print the right footer */
 
454
    if (!IS_EMPTY(job->right_footer)) {
 
455
      output_char (jdiv, '(');
 
456
      output_marker (job, "right footer", job->right_footer);
 
457
      output (jdiv, ") ");
 
458
    } else
 
459
      output (jdiv, "() ");
 
460
 
 
461
    /* Print the left footer */
 
462
    if (!IS_EMPTY(job->left_footer)) {
 
463
      output_char (jdiv, '(');
 
464
      output_marker (job, "left footer", job->left_footer);
 
465
      output (jdiv, ") ");
 
466
    }
 
467
    else
 
468
      output (jdiv, "() ");
 
469
    output (jdiv, "footer\n");
 
470
  }
 
471
 
 
472
  /* Close the current encoding */
 
473
  ps_end_encoding (job);
 
474
 
 
475
  output (jdiv, "pagesave restore\n");
 
476
  output (jdiv, "showpage\n");
 
477
 
 
478
  job->virtual = 0;
 
479
}
 
480
 
 
481
/****************************************************************/
 
482
/*              Printing a virtual page                         */
 
483
/****************************************************************/
 
484
/*
 
485
 * Prints page header and page border and
 
486
 * initializes printing of the file lines.
 
487
 */
 
488
static void
 
489
virtual_begin (a2ps_job * job)
 
490
{
 
491
  job->pages++;
 
492
 
 
493
  if (print_page (job, job->pages)) {
 
494
    output_to_void (jdiv, false);
 
495
    job->virtual++;
 
496
  } else {
 
497
    output_to_void (jdiv, true);
 
498
  }
 
499
 
 
500
  /* We test to one, since it has just been incremented above */
 
501
  if (job->virtual == 1)
 
502
    page_begin (job);
 
503
 
 
504
  if (!job->encoding)
 
505
    ps_set_encoding (job, job->saved_encoding);
 
506
 
 
507
  file_job_synchronize_pages (job);
 
508
  file_job_synchronize_sheets (job);
 
509
 
 
510
  output (jdiv, "/v %d store\n", job->virtual - 1);
 
511
  output (jdiv, "/x0 x v get %f add sx cw mul add store\n",
 
512
          SIDE_MARGIN_RATIO * job->fontsize * 0.6);
 
513
  output (jdiv, "/y0 y v get bfs %s sub store\n",
 
514
          PRINT_TITLE ? "th add" : "");
 
515
  output (jdiv, "x0 y0 moveto\n");
 
516
}
 
517
 
 
518
/*
 
519
 * Adds a sheet number to the page (footnote) and prints the formatted
 
520
 * page (physical impression). Activated at the end of each source page.
 
521
 */
 
522
static void
 
523
virtual_end (a2ps_job * job)
 
524
{
 
525
  /*
 
526
   *    Print the titles with the option-given encoding
 
527
   */
 
528
  /* Draw the header and its content */
 
529
  if (PRINT_TITLE) {
 
530
    ps_push_encoding (job, job->requested_encoding);
 
531
    if (!IS_EMPTY(job->center_title)) {
 
532
      output_char (jdiv, '(');
 
533
      output_marker (job, "center title", job->center_title);
 
534
      output (jdiv, ") ");
 
535
    } else
 
536
      output (jdiv, "() ");
 
537
 
 
538
    if (!IS_EMPTY(job->right_title)) {
 
539
      output_char (jdiv, '(');
 
540
      output_marker (job, "right title", job->right_title);
 
541
      output (jdiv, ") ");
 
542
    } else
 
543
      output (jdiv, "() ");
 
544
 
 
545
    if (!IS_EMPTY(job->left_title)) {
 
546
      output_char (jdiv, '(');
 
547
      output_marker (job, "left title", job->left_title);
 
548
      output (jdiv, ") ");
 
549
    } else
 
550
      output (jdiv, "() ");
 
551
 
 
552
    output (jdiv, "title\n");
 
553
    ps_pop_encoding (job);
 
554
  }
 
555
 
 
556
  if (job->border)
 
557
    output (jdiv, "border\n");
 
558
 
 
559
  if (job->virtual == (job->columns * job->rows))
 
560
    page_end (job);
 
561
 
 
562
  job->status->line = 0;
 
563
}
 
564
 
 
565
/* Issue an empty virtual (used for file alignment). */
 
566
 
 
567
static void
 
568
virtual_empty_output (a2ps_job *job)
 
569
{
 
570
  /* Let's use a hidden option which lets choose between an empty
 
571
     virtual (with headers etc.), or nothing printed at all. */
 
572
  if (macro_meta_sequence_get (job, VAR_OPT_VIRTUAL_FORCE))
 
573
    {
 
574
      virtual_begin (job);
 
575
      virtual_end (job);
 
576
    }
 
577
  else
 
578
    {
 
579
      /* Just increase the virtual number, that's enough. */
 
580
      job->pages++;
 
581
      job->virtual++;
 
582
      if (job->virtual == (job->columns * job->rows))
 
583
        page_end (job);
 
584
    }
 
585
}
 
586
 
 
587
/*
 
588
 * Flush the (physical) page, ready for new page
 
589
 * Used
 
590
 * -- at the end of the whole job
 
591
 * -- at the end of a page
 
592
 * -- when a fresh page is required (eg, when after an a2ps
 
593
 *    generated ps page arrives a delegated ps file)
 
594
 */
 
595
void
 
596
page_flush (a2ps_job * job)
 
597
{
 
598
  /* Everything that follows _must_ be dumped */
 
599
  output_to_void (job->divertion, false);
 
600
 
 
601
  /* If the sheet has not been printed, flush it */
 
602
  if (job->virtual != 0)
 
603
    page_end (job);
 
604
}
 
605
 
 
606
/* Output an empty page (used for instance to align files.
 
607
   No page should be waiting (issue flushes before). */
 
608
 
 
609
static void
 
610
page_empty_output (a2ps_job *job)
 
611
{
 
612
  job->sheets++;
 
613
  file_job_synchronize_sheets (job);
 
614
  output (jdiv, "%%%%Page: (*) %d\n", job->sheets);
 
615
  output (jdiv, "%% Empty Page\n");
 
616
  output (jdiv, "showpage\n");
 
617
}
 
618
 
 
619
static void
 
620
sheet_flush (a2ps_job *job)
 
621
{
 
622
  page_flush (job);
 
623
 
 
624
  /* Need an empty back side? */
 
625
  if ((job->duplex == duplex || job->duplex == tumble)
 
626
      && (job->sheets % 2) != 0)
 
627
    page_empty_output (job);
 
628
}
 
629
 
 
630
/*
 
631
 * Make sure this a new sheet.
 
632
 * No DSC is done on the new page.  This is used when delegated
 
633
 * ps file is inlined.
 
634
 */
 
635
void
 
636
require_fresh_page (a2ps_job * job)
 
637
{
 
638
  /* If this is not a blank sheet, end it */
 
639
  if (job->virtual != 0)
 
640
    /* The clean up _must_ be done */
 
641
    page_flush (job);
 
642
}
 
643
 
 
644
/****************************************************************/
 
645
/*              Service routines                                */
 
646
/****************************************************************/
 
647
/*
 
648
 * Test if we have a binary file.
 
649
 *
 
650
 * Printing binary files is not very useful. We stop printing
 
651
 * if we detect one of these files. Our heuristic to detect them:
 
652
 * if 40% characters are non-printing characters,
 
653
 * the file is a binary file. (40% is taken from the case of a2ps istself).
 
654
 */
 
655
static void
 
656
check_binary_file (a2ps_job * job)
 
657
{
 
658
  if (job->status->chars > 120)
 
659
    {
 
660
      if (!job->print_binaries
 
661
          && (job->status->nonprinting_chars*100 / job->status->chars) >= 40)
 
662
        error (1, 0, _("`%s' is a binary file, printing aborted"),
 
663
               CURRENT_FILE (job)->name);
 
664
    }
 
665
}
 
666
 
 
667
static inline void
 
668
end_of_line (struct a2ps_job * job)
 
669
{
 
670
  if (!job->status->face_declared) {
 
671
    output (jdiv, ") %s n\n", face_eo_ps (job->status->face));
 
672
    job->status->face_declared = true;
 
673
  } else
 
674
    output (jdiv, ") N\n");
 
675
  job->status->line++;
 
676
  job->status->column = 0;
 
677
  job->status->wx = 0;
 
678
}
 
679
 
 
680
#define page_full       \
 
681
        (job->status->line >= job->status->linesperpage)
 
682
 
 
683
#define line_full (job->status->wx > job->status->wxperline)
 
684
 
 
685
/*
 
686
 * Fold a line too long.
 
687
 */
 
688
static inline void
 
689
fold_line (struct a2ps_job * job, enum face_e new_face)
 
690
{
 
691
  job->lines_folded++;
 
692
  end_of_line (job);
 
693
  if (page_full)
 
694
    {
 
695
      virtual_end (job);
 
696
      virtual_begin (job);
 
697
      job->status->face_declared = false ;
 
698
    }
 
699
  if (job->numbering)
 
700
    {
 
701
      output (jdiv, "0 T (");
 
702
    }
 
703
  else
 
704
    {
 
705
      /* This is a new line, hence we can consider that there is no
 
706
         need to close the current font: just consider it is the new
 
707
         font, but not declared. */
 
708
      output_char (jdiv, '(');
 
709
      job->status->face_declared &= (job->status->face
 
710
                                     == new_face);
 
711
      job->status->face = new_face;
 
712
  }
 
713
}
 
714
 
 
715
/*
 
716
 * Print a char
 
717
 */
 
718
void
 
719
ps_print_char (a2ps_job * job, int c, enum face_e new_face)
 
720
{
 
721
  /*
 
722
   * Preprocessing (before printing):
 
723
   * - TABs expansion (see interpret option)
 
724
   * - FF and BS interpretation
 
725
   * - replace non printable characters by a space or a char sequence
 
726
   *   like:
 
727
   *     ^X for ascii codes < 0x20 (X = [@, A, B, ...])
 
728
   *     ^? for del char
 
729
   *     M-c for ascii codes > 0x3f
 
730
   * - prefix parents and backslash ['(', ')', '\'] by backslash
 
731
   *   (escape character in postcript)
 
732
   */
 
733
  if (job->status->is_in_cut
 
734
      && (c != '\f' )
 
735
      /* FIXME: May be some day, using a more flexible scheme
 
736
       * would be good
 
737
      && (c != encodings[job->encoding].new_line))*/
 
738
      && (c != '\n'))
 
739
    return;
 
740
  job->status->is_in_cut = false;
 
741
 
 
742
  /* Start a new line ? */
 
743
  if (job->status->start_line)
 
744
    {
 
745
      if (job->status->start_page)
 
746
        {
 
747
          /* only if there is something to print! */
 
748
          virtual_begin (job);
 
749
          job->status->start_page = false ;
 
750
          /* This is the first line of a new page, hence we need (page
 
751
           * independance) to repeat the current font */
 
752
          job->status->face = new_face;
 
753
          job->status->face_declared = false;
 
754
 
 
755
          if (job->numbering)
 
756
            {
 
757
              if (CURRENT_FILE (job)->lines % job->numbering == 0)
 
758
                output (jdiv, "(%d) # (", CURRENT_FILE (job)->lines);
 
759
              else
 
760
                output (jdiv, "0 T (");
 
761
            }
 
762
          else
 
763
            output_char (jdiv, '(');
 
764
        }
 
765
      else
 
766
        {
 
767
          /* This is a new line, but not the first in the page */
 
768
          if (job->numbering)
 
769
            {
 
770
              if (CURRENT_FILE (job)->lines % job->numbering == 0)
 
771
                output (jdiv, "(%d) # (", CURRENT_FILE (job)->lines);
 
772
              else
 
773
                output (jdiv, "0 T (");
 
774
            }
 
775
          else
 
776
            {
 
777
              /* This is a new line, hence we can consider that there is
 
778
                 no need to close the current font: just consider it is
 
779
                 the new font, but not declared. */
 
780
              output_char (jdiv, '(');
 
781
              /* Why the hell did I do this? */
 
782
              /* FIXME: This is suppresed because of the changes of encoding */
 
783
              job->status->face_declared = (job->status->face_declared
 
784
                                            && (job->status->face == new_face));
 
785
              job->status->face = new_face;
 
786
            }
 
787
        }
 
788
      job->status->start_line = false;
 
789
    }
 
790
 
 
791
  /*
 
792
   * Interpret each character
 
793
   *
 
794
   *  One of the tricky stuff is that we want to avoid uncessary
 
795
   * Changes of font.  For instance, I see no point in
 
796
   * Updating the font right before a \n.
 
797
   */
 
798
  switch (c) {
 
799
  case '\f':            /* Form feed */
 
800
    if (!job->interpret)
 
801
      goto print;
 
802
 
 
803
    /* Close current line */
 
804
    if (!job->status->start_line)
 
805
      {
 
806
        end_of_line (job);
 
807
        job->status->start_line = true;
 
808
      }
 
809
    /* start a new page ? */
 
810
    if (job->status->start_page)
 
811
      {
 
812
        virtual_begin (job);
 
813
      }
 
814
    /* Close current page and begin another */
 
815
    virtual_end (job);
 
816
    job->status->start_page = true;
 
817
    break;
 
818
 
 
819
  case '\n':
 
820
#if 0
 
821
  /* Now the primary eol is \n.  It is up to a2ps-prog to change the
 
822
   * \r or \n\r to \n
 
823
   * The program  */
 
824
  case '\r':            /* One of these is just a plain character */
 
825
    if (c != encodings[job->encoding].new_line)
 
826
      goto print;
 
827
#endif
 
828
    (CURRENT_FILE (job))->lines++;
 
829
    job->status->start_line = true;
 
830
    end_of_line (job);
 
831
 
 
832
    if (page_full) {
 
833
      virtual_end (job);
 
834
      job->status->start_page = true ;
 
835
    }
 
836
    break;
 
837
 
 
838
  case '\t':
 
839
    if (!job->interpret)
 
840
      goto print;
 
841
 
 
842
    /* Is this a new font? */
 
843
    if (job->status->face != new_face) {
 
844
      if (!job->status->face_declared)
 
845
        output (jdiv, ") %s\n(", face_eo_ps (job->status->face));
 
846
      else
 
847
        output (jdiv, ") S\n(");
 
848
      job->status->face = new_face;
 
849
      job->status->face_declared = false;
 
850
    }
 
851
 
 
852
    /* Tabs are interpreted  but we want to go to the same
 
853
     * column as if the font were Courier
 
854
     */
 
855
    job->status->column =
 
856
      (A2_MAX(job->status->wx / COURIER_WX, job->status->column)
 
857
       / job->tabsize + 1) * job->tabsize;
 
858
    job->status->wx = job->status->column * COURIER_WX;
 
859
    if (line_full) {
 
860
      if (job->folding) {
 
861
        fold_line (job, new_face);
 
862
      } else {
 
863
        job->status->is_in_cut = true;
 
864
        return;
 
865
      }
 
866
    }
 
867
    /* Make sure that the font is given */
 
868
    if (!job->status->face_declared) {
 
869
      output (jdiv, ") %s", face_eo_ps(job->status->face));
 
870
      job->status->face_declared = true;
 
871
    } else
 
872
      output (jdiv, ") S");
 
873
    output (jdiv, " %d T (", job->status->column);
 
874
    break;
 
875
  print:
 
876
  default:
 
877
    {
 
878
      uchar buf[256];
 
879
      int nchars;
 
880
      *buf = '\0';
 
881
 
 
882
  /* Is this a new font? */
 
883
  if (job->status->face != new_face) {
 
884
    if (!job->status->face_declared)
 
885
      output (jdiv, ") %s\n(", face_eo_ps (job->status->face));
 
886
    else
 
887
      output (jdiv, ") S\n(");
 
888
    job->status->face = new_face;
 
889
    job->status->face_declared = false;
 
890
  }
 
891
 
 
892
      nchars = ps_escape_char (job, c, buf);
 
893
      job->status->wx += char_WX (job, c);
 
894
      job->status->column += nchars;
 
895
      if (line_full) {
 
896
        if (job->folding) {
 
897
          fold_line (job, new_face);
 
898
          job->status->column = nchars;
 
899
          job->status->wx = char_WX (job, c);
 
900
        } else {
 
901
          job->status->is_in_cut = true;
 
902
          return;
 
903
        }
 
904
      }
 
905
      output (jdiv, "%s", buf);
 
906
      job->status->chars++;
 
907
    }
 
908
    break;
 
909
  }
 
910
}
 
911
 
 
912
/*
 
913
 * Print the content of a C string \0 terminated
 
914
 */
 
915
void
 
916
ps_print_string (a2ps_job * job, uchar * string, enum face_e face)
 
917
{
 
918
  while (*string)
 
919
    ps_print_char (job, *(string++), face);
 
920
}
 
921
 
 
922
/*
 
923
 * Print the N first chars in BUFFER
 
924
 */
 
925
void
 
926
ps_print_buffer (a2ps_job * job,
 
927
                 const uchar * buffer,
 
928
                 size_t start, size_t end,
 
929
                 enum face_e face)
 
930
{
 
931
  size_t i;
 
932
  for (i = start ; i < end ; i++)
 
933
    ps_print_char (job, buffer [i], face);
 
934
}
 
935
 
 
936
/* Handling the input sessions in an output session, i.e., typically
 
937
   the files. */
 
938
 
 
939
/* A new file will be printed. */
 
940
 
 
941
void
 
942
ps_begin_file (a2ps_job *job)
 
943
{
 
944
  /* Reinitialize the ps status */
 
945
  initialize_ps_status (job->status);
 
946
 
 
947
  /* Alignment is not needed for the first file. */
 
948
  if (job->jobs->len == 0)
 
949
    return;
 
950
 
 
951
  switch (job->file_align)
 
952
    {
 
953
    case file_align_virtual:
 
954
      /* Nothing to do. */
 
955
      break;
 
956
 
 
957
    case file_align_rank:
 
958
      /* Issue virtual until we are in a new rank. */
 
959
      {
 
960
        int alignment = job->madir == madir_rows ? job->columns : job->rows;
 
961
        while (job->pages % alignment != 0)
 
962
          virtual_empty_output (job);
 
963
      }
 
964
      break;
 
965
 
 
966
    case file_align_page:
 
967
      /* End the page if needed. */
 
968
      page_flush (job);
 
969
      break;
 
970
 
 
971
    case file_align_sheet:
 
972
      /* End the sheet if needed. */
 
973
      sheet_flush (job);
 
974
      break;
 
975
 
 
976
    default:
 
977
      /* job->file_align is a number.  We must issue as many pages as
 
978
         needed to have a page number which is a multiple of
 
979
         FILE_ALIGN plus one. */
 
980
      page_flush (job);
 
981
      while ((job->sheets) % job->file_align != 0)
 
982
        page_empty_output (job);
 
983
      break;
 
984
    }
 
985
}
 
986
 
 
987
void
 
988
ps_end_file (a2ps_job *job)
 
989
{
 
990
  /* If a line was not finished, close it.
 
991
   * Typically, no \n at end of file */
 
992
  if (!job->status->start_line)
 
993
    {
 
994
      if (!job->status->face_declared)
 
995
        output (jdiv, ") %s\n", face_eo_ps (job->status->face));
 
996
      else
 
997
        output (jdiv, ") S\n");
 
998
    }
 
999
  if (!job->status->start_page)
 
1000
    virtual_end (job);
 
1001
 
 
1002
  /* Set the number of pages/sheets printed */
 
1003
  file_job_synchronize_pages (job);
 
1004
  file_job_synchronize_sheets (job);
 
1005
 
 
1006
  /* If we don't want to print binaries, complain and quit */
 
1007
  check_binary_file (job);
 
1008
}