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