1 /* This file is part of the GNU plotutils package. Copyright (C) 1995,
2 1996, 1997, 1998, 1999, 2000, 2005, 2008, Free Software Foundation, Inc.
3
4 The GNU plotutils package is free software. You may redistribute it
5 and/or modify it under the terms of the GNU General Public License as
6 published by the Free Software foundation; either version 2, or (at your
7 option) any later version.
8
9 The GNU plotutils package is distributed in the hope that it will be
10 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with the GNU plotutils package; see the file COPYING. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor,
17 Boston, MA 02110-1301, USA. */
18
19 /* This is the low-level method which is used by any HP-GL or PCL Plotter
20 for rendering a single-font label in a PCL, PS, or Stick font. Note:
21 The `PCL 5' output by any PCL Plotter is simply a wrapped version of
22 HP-GL/2.
23
24 This method internally invokes _pl_h_set_font to select the font. See
25 h_font.c for the font selection code.
26
27 Before HP-GL/2 (introduced c. 1990), HP-GL devices supported only Stick
28 fonts. In modern PCL5 printers, the suite of 45 PCL fonts is accessible
29 too. Only a few modern high-end PCL5/PS printers (e.g. LaserJet 4000
30 series laser printers) also support PS fonts in PCL mode. PS fonts are
31 supported by HP-GL/2 and PCL Plotters if the `--enable-ps-fonts-in-pcl'
32 option is specified at configure time.
33
34 Novel features of this driver include (1) the rightward shift that all
35 fonts need, when accessed through HP-GL, (2) the re-encoding that Stick
36 fonts need, (3) the compensation for the kerning used by full-featured
37 HP-GL devices when rendering variable-width Stick fonts, and (4) the
38 bizarre re-encoding that we apply to ISO-Latin-1 PCL fonts, due to HP's
39 idiosyncratic definition of ISO-Latin-1 ("ECMA-96 Latin 1"):
40
41 1. HP-GL rendering of a string is displaced leftward, relative to PS
42 rendering, by an amount equal to the distance between the bounding box
43 left edge and the left edge (`first ink') for the first character. This
44 is so that the first ink will be put on the page right where we start
45 rendering the string. This convention dates back to pen plotter days.
46
47 The offset[] arrays in g_fontdb.c hold the information that we need to
48 undo this leftward shift by a compensating initial rightward shift.
49 After rendering the string, we undo the shift.
50
51 2. On most devices, stick fonts are available only in HP's Roman-8
52 encoding. So we need to remap them, if they are to be ISO-Latin-1
53 fonts. There are a few characters missing, but we do our best.
54
55 3. Ideally, any HP-GL device kerns the variable-width Stick fonts that
56 it supports, if any. We compensate for this in g_alabel.c by using the
57 spacing tables in g_fontd2.c when computing label widths. The inclusion
58 of kerning in the label width computation affects the horizontal
59 positioning of the label, if it is centered or right-justifified rather
60 than left-justified.
61
62 4. PCL fonts (and the PS fonts available in PCL mode on a few high-end
63 devices) in principle support ISO-Latin-1 encoding, natively. However,
64 HP interprets ISO-Latin-1 in an idiosyncratic way. For example,
65 left-quote and right-quote show up as accents, and tilde shows up as a
66 tilde accent. For this reason, for ISO-Latin-1 PCL fonts we use HP's
67 Roman-8 encoding for the lower half, and HP's ISO-Latin-1 encoding for
68 the upper half. */
69
70 #include "sys-defines.h"
71 #include "extern.h"
72 #include "h_roman8.h"
73
74 /* for switching to upper half of font charset, and switching back */
75 #define SHIFT_OUT 14 /* i.e. ASCII 0x0e, i.e. ^N */
76 #define SHIFT_IN 15 /* i.e. ASCII 0x0f, i.e. ^O */
77
78 /* for DFA that keeps track of which half we're in */
79 typedef enum { LOWER_HALF, UPPER_HALF } state_type;
80
81 /* kludge, see comment in code */
82 #define HP_ROMAN_8_MINUS_CHAR 0366
83
84 /* ARGS: h_just,v_just are PL_JUST_{LEFT|CENTER|RIGHT}, PL_JUST_{TOP etc.} */
85 double
_pl_h_paint_text_string(R___ (Plotter * _plotter)const unsigned char * s,int h_just,int v_just)86 _pl_h_paint_text_string (R___(Plotter *_plotter) const unsigned char *s, int h_just, int v_just)
87 {
88 bool created_temp_string = false;
89 bool reencode_iso_as_roman8 = false;
90 double hp_offset;
91 double theta, costheta, sintheta;
92 int master_font_index;
93 unsigned char *t;
94 unsigned char instruction_buf[4];
95
96 /* if empty string, nothing to do */
97 if (*s == (unsigned char)'\0')
98 return 0.0;
99
100 /* sanity checks: this routine supports only baseline positioning and
101 left-justification */
102 if (v_just != PL_JUST_BASE || h_just != PL_JUST_LEFT)
103 return 0.0;
104
105 /* sanity check, should be unnecessary */
106 #ifndef USE_PS_FONTS_IN_PCL
107 if (_plotter->drawstate->font_type != PL_F_PCL
108 && _plotter->drawstate->font_type != PL_F_STICK)
109 return 0.0;
110 #else /* USE_PS_FONTS_IN_PCL */
111 if (_plotter->drawstate->font_type != PL_F_POSTSCRIPT
112 && _plotter->drawstate->font_type != PL_F_PCL
113 && _plotter->drawstate->font_type != PL_F_STICK)
114 return 0.0;
115 #endif
116
117 /* Many HP-GL interpreters can't handle zero font size. So bail if the
118 font size we'll emit is zero. */
119 if (_plotter->drawstate->true_font_size == 0.0)
120 return 0.0;
121
122 /* Our font selection code in h_font.c will divide by zero if the
123 viewport in the device frame has zero area, i.e., if the HP-GL scaling
124 points P1,P2 have the same x or y coordinates. So bail now if that's
125 the case. */
126 if (_plotter->hpgl_p1.x == _plotter->hpgl_p2.x
127 || _plotter->hpgl_p1.y == _plotter->hpgl_p2.y)
128 return _plotter->get_text_width (R___(_plotter) s);
129
130 /* compute index of font in master table in g_fontdb.c */
131 switch (_plotter->drawstate->font_type)
132 {
133 case PL_F_PCL:
134 default:
135 master_font_index =
136 (_pl_g_pcl_typeface_info[_plotter->drawstate->typeface_index].fonts)[_plotter->drawstate->font_index];
137 break;
138 case PL_F_POSTSCRIPT:
139 master_font_index =
140 (_pl_g_ps_typeface_info[_plotter->drawstate->typeface_index].fonts)[_plotter->drawstate->font_index];
141 break;
142 case PL_F_STICK:
143 master_font_index =
144 (_pl_g_stick_typeface_info[_plotter->drawstate->typeface_index].fonts)[_plotter->drawstate->font_index];
145 break;
146 }
147
148 /* label rotation angle in radians, in user frame */
149 theta = M_PI * _plotter->drawstate->text_rotation / 180.0;
150 sintheta = sin (theta);
151 costheta = cos (theta);
152
153 switch (_plotter->drawstate->font_type)
154 {
155 case PL_F_PCL:
156 default:
157 if (_pl_g_pcl_font_info[master_font_index].hpgl_symbol_set == PCL_ROMAN_8
158 && _pl_g_pcl_font_info[master_font_index].iso8859_1)
159 /* An ISO-Latin-1 PCL font, for which we use HP's Roman-8 for lower
160 half and HP's Latin-1 for upper half. Why? Because it's what
161 works best; see comments in the font retrieval code in h_font.c.
162
163 There is one exception to this: right here, we map the ASCII
164 minus character `-', in the lower half, to
165 HP_ROMAN_8_MINUS_CHAR, i.e., to 0366. This is a kludge, needed
166 to get a character whose width matches the width in the AFM
167 files that HP distributes. */
168 {
169 state_type dfa_state = LOWER_HALF;
170 unsigned const char *sptr = s;
171 unsigned char *tptr;
172
173 /* temp string for rewritten label */
174 t = (unsigned char *)_pl_xmalloc (3 * strlen ((const char *)s) + 1);
175 tptr = t;
176 created_temp_string = true;
177
178 /* SHIFT_OUT switches to alt. charset, SHIFT_IN back to standard */
179 while (*sptr)
180 {
181 unsigned char c;
182
183 c = *sptr++;
184 if (c < 0x80)
185 /* lower half of font, use standard font (HP Roman-8) */
186 {
187 if (c == '-') /* kludge, map to a char in upper half */
188 c = HP_ROMAN_8_MINUS_CHAR;
189 if (dfa_state == UPPER_HALF)
190 {
191 *tptr++ = SHIFT_IN;
192 dfa_state = LOWER_HALF;
193 }
194 *tptr++ = c;
195 }
196 else
197 /* upper half of font, use alt. font (HP ECMA-96 Latin-1) */
198 {
199 if (dfa_state == LOWER_HALF)
200 {
201 *tptr++ = SHIFT_OUT;
202 dfa_state = UPPER_HALF;
203 }
204 *tptr++ = c;
205 }
206 }
207
208 if (dfa_state == UPPER_HALF)
209 *tptr++ = SHIFT_IN;
210 *tptr = '\0'; /* end of rewritten label */
211 }
212 else
213 /* a non-ISO-Latin-1 PCL font, no need for reencoding */
214 t = (unsigned char *)s;
215 break;
216 case PL_F_POSTSCRIPT:
217 /* no need for reencoding (HP's encoding of the font is good enough) */
218 t = (unsigned char *)s;
219 break;
220 case PL_F_STICK:
221 if (_pl_g_stick_font_info[master_font_index].hpgl_symbol_set == PCL_ROMAN_8
222 && _pl_g_stick_font_info[master_font_index].iso8859_1)
223 /* stick font uses HP's Roman-8 encoding for its upper half, so
224 must reencode ISO-Latin-1 as Roman-8 */
225 reencode_iso_as_roman8 = true;
226
227 if (_plotter->hpgl_version <= 1)
228 /* HP-GL version is no greater than "1.5", i.e. HP7550A; so in
229 h_font.c, we'll have made both lower and upper font halves
230 available as 7-bit fonts that can be switched between via SO/SI */
231 {
232 bool bogus_upper_half = false;
233 state_type dfa_state = LOWER_HALF;
234 unsigned const char *sptr = s;
235 unsigned char *tptr;
236
237 /* Check whether font is meant to be a pure 7-bit font with no
238 upper half; if so, we'll ignore all 8-bit characters. This
239 case is recognized by the charset number for the upper half
240 being -1 (see table in g_fontdb.c). */
241 if (_pl_g_stick_font_info[master_font_index].hpgl_charset_upper < 0)
242 bogus_upper_half = true;
243
244 /* temp string for rewritten label */
245 t = (unsigned char *)_pl_xmalloc (3 * strlen ((const char *)s) + 1);
246 tptr = t;
247 created_temp_string = true;
248
249 /* do 7-bit reencoding, using SO/SI */
250 /* SHIFT_OUT switches to alt. charset, SHIFT_IN back to standard */
251 while (*sptr)
252 {
253 unsigned char c;
254
255 c = *sptr++;
256 if (c >= 0x80 && reencode_iso_as_roman8)
257 /* reencode upper half via lookup table in h_roman8.h */
258 c = iso_to_roman8[c - 0x80];
259
260 if (c < 0x80)
261 /* lower half of font, pass through */
262 {
263 if (dfa_state == UPPER_HALF)
264 {
265 *tptr++ = SHIFT_IN;
266 dfa_state = LOWER_HALF;
267 }
268 *tptr++ = c;
269 }
270 else
271 /* upper half of font, move to lower half */
272 if (bogus_upper_half == false)
273 {
274 if (dfa_state == LOWER_HALF)
275 {
276 *tptr++ = SHIFT_OUT;
277 dfa_state = UPPER_HALF;
278 }
279 *tptr++ = c - 0x80;
280 }
281 }
282
283 /* ensure we switch back to standard font at end of label */
284 if (dfa_state == UPPER_HALF)
285 *tptr++ = SHIFT_IN;
286 *tptr = '\0'; /* end of rewritten label */
287 }
288 else
289 /* HP-GL version is "2", i.e. HP-GL/2, so the only Stick fonts we
290 have are 8-bit ones; no need for 7-bit reencoding via a DFA.
291 Will still need to map ISO-Latin-1 to Roman-8, though. */
292 {
293 unsigned const char *sptr = s;
294 unsigned char *tptr;
295
296 t = (unsigned char *)_pl_xmalloc (strlen ((const char *)s) + 1);
297 tptr = t;
298 created_temp_string = true;
299 while (*sptr)
300 {
301 if (*sptr < 0x80)
302 *tptr++ = *sptr++;
303 else
304 {
305 if (reencode_iso_as_roman8)
306 /* reencode upper half via lookup table in h_roman8.h */
307 *tptr++ = iso_to_roman8[(*sptr++) - 0x80];
308 else
309 *tptr++ = *sptr++;
310 }
311 }
312 *tptr = '\0'; /* end of rewritten label */
313 }
314 break;
315 }
316
317 /* compute abovementioned HP-style rightward shift; depends on `offset'
318 for first character in label, i.e. its `first ink' */
319 switch (_plotter->drawstate->font_type)
320 {
321 case PL_F_PCL:
322 default:
323 /* per-character offset expressed in units where font size = 1000 */
324 hp_offset = _pl_g_pcl_font_info[master_font_index].offset[*((unsigned char *)s)] / 1000.0;
325 break;
326 case PL_F_POSTSCRIPT:
327 /* per-character offset expressed in units where font size = 1000 */
328 hp_offset = _pl_g_ps_font_info[master_font_index].offset[*((unsigned char *)s)] / 1000.0;
329 break;
330 case PL_F_STICK:
331 /* Offset expressed in HP's abstract raster units, need to divide by
332 what the font size equals in raster units.
333 (Font size = 2 * raster width, by definition.) */
334
335 /* For Stick fonts that we've defined in such a way that the raster
336 width differs between lower and upper halves, not sure what to do
337 here. In particular ArcANK has JIS-ASCII encoding for lower half,
338 with raster width 42, and half-width Katakana encoding for upper
339 half, with raster width 45. For now, just use the raster width
340 for the lower half. */
341
342 hp_offset = (((double)(_pl_g_stick_font_info[master_font_index].offset)) /
343 (2.0 * _pl_g_stick_font_info[master_font_index].raster_width_lower));
344 break;
345 }
346
347 /* do the rightward shift */
348 _plotter->drawstate->pos.x +=
349 costheta * _plotter->drawstate->true_font_size * hp_offset;
350 _plotter->drawstate->pos.y +=
351 sintheta * _plotter->drawstate->true_font_size * hp_offset;
352
353 /* sync font and pen position */
354 _pl_h_set_font (S___(_plotter));
355 _pl_h_set_position (S___(_plotter));
356
357 /* Sync pen color. This may set the _plotter->hpgl_bad_pen flag (if optimal
358 pen is #0 [white] and we're not allowed to use pen #0 to draw with).
359 So we test _plotter->hpgl_bad_pen before drawing the label (see below). */
360 _pl_h_set_pen_color (R___(_plotter) HPGL_OBJECT_LABEL);
361
362 if (t[0] != '\0' /* i.e. label nonempty */
363 && _plotter->hpgl_bad_pen == false)
364 /* output the label via an `LB' instruction, including label
365 terminator; don't use sprintf to avoid having to escape % and \ */
366 {
367 strcpy (_plotter->data->page->point, "LB");
368 _update_buffer (_plotter->data->page);
369 strcpy (_plotter->data->page->point, (const char *)t);
370 _update_buffer (_plotter->data->page);
371 instruction_buf[0] = (unsigned char)3; /* ^C = default label terminator*/
372 instruction_buf[1] = ';';
373 instruction_buf[2] = '\0';
374 strcpy (_plotter->data->page->point, (const char *)instruction_buf);
375 _update_buffer (_plotter->data->page);
376
377 /* where is the plotter pen now located?? we don't know, exactly */
378 _plotter->hpgl_position_is_unknown = true;
379 }
380
381 if (created_temp_string)
382 /* created a temp string, so free it */
383 free (t);
384
385 /* Undo HP's rightward shift */
386
387 _plotter->drawstate->pos.x -=
388 costheta * _plotter->drawstate->true_font_size * hp_offset;
389 _plotter->drawstate->pos.y -=
390 sintheta * _plotter->drawstate->true_font_size * hp_offset;
391
392 return _plotter->get_text_width (R___(_plotter) s);
393 }
394