summaryrefslogtreecommitdiff
path: root/imageto/out-epsf.c
blob: 9187861b9996c12cadec1a4ed345f0755892f529 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# out-epsf.c: output the whole image as an EPS file.
#
# Copyright (C) 1992, 2011 Free Software Foundation, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

#include "config.h"

#include "hexify.h"
#include "report.h"

#include "main.h"
#include "out-epsf.h"

extern string version_string;

static void pack_scanline (one_byte *, unsigned);

/* Abbreviations for output to `eps_file'.  */
#define OUT_STRING(s) fprintf (eps_file, "%s", s)
#define OUT_SIMPLE(s) fprintf (eps_file, "%%%%%s\n", s)
#define OUT_COMMENT(s, v) fprintf (eps_file, "%%%%%s: %s\n", s, v)
#define OUT1(s, v1) fprintf (eps_file, s, v1)
#define OUT2(s, v1, v2) fprintf (eps_file, s, v1, v2)
#define OUT3(s, v1, v2, v3) fprintf (eps_file, s, v1, v2, v3)

/* Write an Encapsulated PostScript file corresponding to the image.  */

void
write_epsf (string output_name, image_header_type image_header)
{
  /* Just black & white, or do we have grayscale?  */
  boolean monochrome_p = image_header.depth == 1;
  
  /* Just for convenience.  */
  unsigned width = image_header.width;
  unsigned height = image_header.height;
  
  /* We pack the image tightly if it's monochrome.  */
  unsigned width_used = monochrome_p ? width / 8 + !!(width % 8) : width;

  /* Buffer into which we'll read the image data.  */
  unsigned scanline_count = 0;
  one_byte *scanline = xmalloc (width);
  
  /* Open the output file OUTPUT_NAME.  */
  FILE *eps_file = xfopen (output_name, "w");

  OUT_STRING ("%!PS-Adobe-3.0 EPSF-3.0\n");
  OUT2 ("%%%%BoundingBox: 0 0 %u %u\n", width, height);
  OUT_COMMENT ("Creator", version_string);
  OUT_COMMENT ("Title", output_name);
  OUT_COMMENT ("CreationDate", now ());
  OUT_COMMENT ("DocumentData", "Clean7Bit");
  OUT_SIMPLE ("EndComments");
  
  /* We map the image to the unit square for image(mask) and scale the
     coordinate system to get back to the original size.  I can't grasp
     how to use the matrix argument to image(mask) to avoid the scaling,
     but I'm sure it's possible. */
  OUT2 ("gsave\n  %u %u scale\n", width, height);
  
  /* We need a buffer to hold the string chunks as we read them.  It
     can't be of arbitrary size: it must be an exact multiple of the
     total number of data characters.  Otherwise, we will read past the
     end of the data.  */
  OUT1 ("/image-buffer %u string def\n", width_used);
  
  /* If we are monochrome, we use the `imagemask' operator; else `image'.  */
  OUT2 ("  %u %u", width, height);

  if (monochrome_p)
    OUT_STRING (" true"); /* The `invert' argument.  */
  else
    OUT1 (" %u", image_header.depth); /* bits/sample */
  
  OUT3 (" [%u 0 0 -%u 0 %u]\n", width, height, height);
  OUT_STRING ("{currentfile image-buffer readhexstring pop}\n");
  OUT1 ("%s\n", monochrome_p ? "imagemask" : "image");
  
  /* Read the image.  */
  while ((*image_get_scanline) (scanline))
    {
      string h;
      unsigned loc;

      scanline_count++;
      if (scanline_count % 10 == 0)
        REPORT1 (".%s", scanline_count % 790 == 0 ? "\n" : "");
      
      /* Monochrome images are output with eight samples/byte; grayscale
         images are output with one sample/byte.  */
      if (monochrome_p)
        pack_scanline (scanline, width);

      /* Convert binary to ASCII hexadecimal.  */
      h = hexify (scanline, width_used);

      /* Adobe says lines in EPS files should be no more than 255
         characters.  How silly.  */
      for (loc = 1; loc <= 2 * width_used; loc++)
        {
          putc (h[loc - 1], eps_file);
          if (loc % 255 == 0)
            putc ('\n', eps_file);
	}
      
      free (h);
      putc ('\n', eps_file);
    }
  
  /* Restore the ctm.  */
  OUT_STRING ("grestore\n");

  if (scanline_count != image_header.height)
    WARNING2 ("Expected %u scanlines, read %u", image_header.height,
              scanline_count); 
  
  OUT_SIMPLE ("TRAILER");
  OUT_SIMPLE ("EOF");
}

/* Change the one bit/byte representation (call each byte a `cell') of
   LENGTH bits in DATA to be eight bits/byte.  Pad the last byte with
   zero.  We don't change those bytes beyond the end of packed portion,
   thus assuming they are not looked at.  */

static void
pack_scanline (one_byte *data, unsigned length)
{
  unsigned cell; /* Which bit in the original data we're on.  */
  unsigned packing_loc = 0; /* Which byte we're currently packing.  */
  unsigned packing_bit = 8; /* How much to shift.  */
  
  /* The very first cell has to be treated specially, because we must
     initialize it with itself shifted left (if we're going to use data
     in place, that is.)  */
  data[0] <<= 7;
  
  for (cell = 0; cell < length; cell++)
    {
      packing_bit--;
      data[packing_loc] |= data[cell] << packing_bit;
      if (packing_bit == 0)
        {
          packing_bit = 8;
          packing_loc++;
          
          /* After the first byte, we can just clear the byte at
             `packing_loc', since `cell' has already moved beyond it.  */
          data[packing_loc] = 0;
	}
    }
}