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 file contains a low-level method for adjusting the font of an HP-GL
20    or PCL device to agree with an HPGL or PCL Plotter's notion of what it
21    should be, prior to plotting a label.  Note: The `PCL 5' output by any
22    PCL Plotter is simply a wrapped version of HP-GL/2.
23 
24    Before HP-GL/2 (introduced c. 1990), HP-GL devices supported only Stick
25    fonts.  In modern PCL5 printers, the suite of 45 PCL fonts is accessible
26    too.  Only a few modern high-end PCL5/PS printers (e.g. LaserJet 4000
27    series laser printers) also support PS fonts in PCL mode.  PS fonts are
28    supported by HP-GL/2 and PCL Plotters if the `--enable-ps-fonts-in-pcl'
29    option is specified at configure time.
30 
31    After selecting the font, this method invokes the HP-GL DR/SR/SL
32    instructions to size and slant the font, as needed.
33 
34    The font selection itself is accomplished in either of two ways.
35 
36    1. In versions of HP-GL prior to HP-GL/2, 7-bit font halves are selected
37    with `CS' and `CA' instructions.  They must be switched between when the
38    label is plotted via SO/SI; see h_text.c.  The HP-GL device will usually
39    supply the upper font half in the Roman-8 encoding, and that too will
40    need to be taken into account when the label is plotted.
41 
42    2. In HP-GL/2, a single 8-bit font is selected with the HP-GL/2 `SD'
43    instruction.  In principle, no switching between 7-bit font halves is
44    needed.
45 
46    In practice, it's more complicated than that.  For ISO-Latin-1 PCL
47    fonts, the SD instruction allegedly allows the ISO-Latin-1 encoding to
48    be requested.  But it doesn't work!  One or two characters in the lower
49    half (!) don't come out right.  So instead, we use the `SD' instruction
50    to retrieve an 8-bit version that uses the Roman-8 encoding, and the
51    `AD' instruction to retrieve an alternative 8-bit version that uses the
52    ISO-Latin-1 encoding.  We'll use the former for characters in the lower
53    half, and the latter for characters in the upper half.  This is bizarre,
54    but it works.  See additional comments in h_text.c. */
55 
56 /* NOTE: This code assumes that P1 and P2 have different x coordinates, and
57    different y coordinates.  If that isn't the case, it'll divide by zero.
58    So we check for that possibility in _pl_h_paint_text_string() before
59    calling this function.  See comment in h_text.c. */
60 
61 #include "sys-defines.h"
62 #include "extern.h"
63 
64 /* Shearing factor for oblique fonts, new_x = x + SHEAR * y  */
65 
66 #define SHEAR (2.0/7.0)
67 
68 void
_pl_h_set_font(S___ (Plotter * _plotter))69 _pl_h_set_font (S___(Plotter *_plotter))
70 {
71   bool font_changed = false;
72   bool oblique;
73   double cos_slant = 1.0, sin_slant = 0.0;
74   double new_relative_label_run, new_relative_label_rise;
75   double theta, sintheta, costheta;
76   plVector base, up, base_native, up_native;
77   double base_native_len, up_native_len, tan_slant;
78 
79   /* sanity check, should be unnecessary */
80   if (_plotter->drawstate->font_type == PL_F_HERSHEY)
81     return;
82 
83   if (_plotter->drawstate->font_type == PL_F_STICK)
84     /* check whether obliquing of this font is called for */
85     {
86       int master_font_index;
87 
88       /* compute index of font in master table of fonts, in g_fontdb.c */
89       master_font_index =
90 	(_pl_g_stick_typeface_info[_plotter->drawstate->typeface_index].fonts)[_plotter->drawstate->font_index];
91       oblique = _pl_g_stick_font_info[master_font_index].obliquing;
92     }
93   else
94     oblique = false;
95 
96   /* label rotation angle in radians, in user frame */
97   theta = M_PI * _plotter->drawstate->text_rotation / 180.0;
98   costheta = cos (theta);
99   sintheta = sin (theta);
100 
101   /* compute in the device frame a `base vector' which is (in the user
102      frame) directed along the label, with length equal to the font size) */
103   base.x = _plotter->drawstate->true_font_size * XDV(costheta,sintheta);
104   base.y = _plotter->drawstate->true_font_size * YDV(costheta,sintheta);
105 
106   /* Compute rise and run, relative to x-distance and y-distance between
107      scaling points P1,P2. (Either rise or run can be negative; overall
108      normalization, e.g. the `100', is irrelevant.  We include the `100' to
109      express them as percentages.) */
110   new_relative_label_run =  100 * base.x / (HPGL_SCALED_DEVICE_RIGHT - HPGL_SCALED_DEVICE_LEFT);
111   new_relative_label_rise = 100 * base.y / (HPGL_SCALED_DEVICE_TOP - HPGL_SCALED_DEVICE_BOTTOM);
112   if (new_relative_label_run != 0.0 || new_relative_label_rise != 0.0)
113     /* (will always be true except when the font size is so small
114        there's really no point in printing the label) */
115     {
116       /* update device-frame label rotation angle if needed */
117       if (_plotter->hpgl_rel_label_run != new_relative_label_run
118 	  || _plotter->hpgl_rel_label_rise != new_relative_label_rise)
119 	{
120 	  sprintf (_plotter->data->page->point, "DR%.3f,%.3f;",
121 		   new_relative_label_run, new_relative_label_rise);
122 	  _update_buffer (_plotter->data->page);
123 	  _plotter->hpgl_rel_label_run = new_relative_label_run;
124 	  _plotter->hpgl_rel_label_rise = new_relative_label_rise;
125 	}
126     }
127 
128   /* emit command to select new font, if needed (see below) */
129   if (_plotter->hpgl_version == 2)
130     font_changed = _pl_h_hpgl2_maybe_update_font (S___(_plotter));
131   else				/* 0 or 1, i.e. generic HP-GL or HP7550A */
132     font_changed = _pl_h_hpgl_maybe_update_font (S___(_plotter));
133 
134   /* Compute image, in the device frame, of a so-called `up vector': a
135      vector which in the user frame is perpendicular to the above `base'
136      vector, and has the same length.  Some fonts are specially obliqued,
137      so we take font obliquing (if any) into account here. */
138 
139   up.x = _plotter->drawstate->true_font_size * XDV(-sintheta,costheta);
140   up.y = _plotter->drawstate->true_font_size * YDV(-sintheta,costheta);
141   up.x += (oblique ? SHEAR : 0.0) * base.x;
142   up.y += (oblique ? SHEAR : 0.0) * base.y;
143 
144   /* Our `device frame' base and up vectors are really vectors in the
145      normalized device frame, in which the viewport has a fixed size.  See
146      h_defplot.c.  E.g., the viewport corners (0,0) and (1,1) in the NDC
147      frame are respectively mapped to
148      (HPGL_SCALED_DEVICE_LEFT,HPGL_SCALED_DEVICE_BOTTOM) and
149      (HPGL_SCALED_DEVICE_RIGHT,HPGL_SCALED_DEVICE_TOP) in the normalized
150      device frame.  The further mapping to native HP-GL coordinates is
151      accomplished by an `SC' scaling instruction emitted at the head of the
152      output file; see h_openpl.c.  This further mapping depends on the
153      PAGESIZE parameter.
154 
155      Unfortunately, when dealing with anamorphically transformed fonts we
156      need to manipulate not just vectors in the normalized device frame,
157      but also vectors in the true device frame, i.e., in native HP-GL
158      units. */
159 
160   /* These vectors use native HP-GL units. */
161   base_native.x = base.x * (_plotter->hpgl_p2.x - _plotter->hpgl_p1.x) / (HPGL_SCALED_DEVICE_RIGHT - HPGL_SCALED_DEVICE_LEFT);
162   base_native.y = base.y * (_plotter->hpgl_p2.y - _plotter->hpgl_p1.y) / (HPGL_SCALED_DEVICE_TOP - HPGL_SCALED_DEVICE_BOTTOM);
163   up_native.x = up.x * (_plotter->hpgl_p2.x - _plotter->hpgl_p1.x) / (HPGL_SCALED_DEVICE_RIGHT - HPGL_SCALED_DEVICE_LEFT);
164   up_native.y = up.y * (_plotter->hpgl_p2.y - _plotter->hpgl_p1.y) / (HPGL_SCALED_DEVICE_TOP - HPGL_SCALED_DEVICE_BOTTOM);
165 
166   base_native_len = sqrt (base_native.x * base_native.x + base_native.y * base_native.y);
167   up_native_len = sqrt (up_native.x * up_native.x + up_native.y * up_native.y);
168 
169   /* compute character slant angle (in the true device frame, NOT in the
170      normalized device frame) */
171   if (base_native_len == 0.0 || up_native_len == 0.0) /* a bad situation */
172     tan_slant = 0.0;
173   else
174     {
175       sin_slant = ((base_native.x * up_native.x + base_native.y * up_native.y)
176 		   / (base_native_len * up_native_len));
177       cos_slant = sqrt (1 - sin_slant * sin_slant);
178       tan_slant = sin_slant / cos_slant;
179     }
180 
181   /* Compute nominal horizontal and vertical character sizes as percentages
182      of the horizontal and vertical distances between scaling points P1 and
183      P2, and specify them with the SR instruction.
184 
185      The two arguments of the SR instruction (the horizontal and vertical
186      character sizes) should apparently be 0.5 times the font size, and 0.7
187      times the font size.
188 
189      Why? The 0.5 and 0.7 = 1.4 * 0.5 factors are undocumented HP magic.
190      This convention must have been introduced by HP to adapt the SR
191      instruction, which dates back to fixed-width plotter fonts (i.e., the
192      original Stick font), to modern outline fonts.  Fixed-width plotter
193      fonts did not have a font size in the modern sense: they had a
194      character width and a character height.  (The former being the width
195      of the character proper, which occupied the left 2/3 of a character
196      cell, and the latter being what we would nowadays call a cap height.)
197 
198      The convention probably arose because Stick fonts look best if the
199      aspect ratio is 1.4 (= 0.7/0.5), i.e. if the `character height' is 1.4
200      times the `character width'.  I am not sure where the 0.5 came from.
201      Possibly back in stick font days, the nominal font size was defined to
202      be 4/3 times the width of a character cell, or equivalently the width
203      of a character cell was chosen to be 3/4 times the nominal font size.
204      This would make the maximum character width (2/3)x(3/4) = (1/2) times
205      the nominal font size. */
206 
207   {
208     double fractional_char_width = 0.5;
209     double fractional_char_height = 1.4 * 0.5;
210     double new_relative_char_width, new_relative_char_height;
211 
212     /* If, in the physical device frame, the font is reflected, we must
213        flip the sign of HP-GL's `character height', as used in the SR
214        instruction.  To determine whether this sign-flipping is needed, we
215        use the fact that the user_frame->physical_device_frame map is the
216        product of the user_frame->normalized_device_frame map and the
217        normalized_device_frame->physical_device_frame map.  Whether the
218        first includes a reflection is precomputed and stored in the drawing
219        state.  The second will include a reflection only if exactly one of
220        the xsize,ysize fields of PAGESIZE is negative.  We can easily check
221        for that by comparing the x,y coordinates of the HP-GL scaling
222        points P1,P2. */
223 
224     int orientation = _plotter->drawstate->transform.nonreflection ? 1 : -1;
225 
226     if ((_plotter->hpgl_p2.x - _plotter->hpgl_p1.x) / (HPGL_SCALED_DEVICE_RIGHT - HPGL_SCALED_DEVICE_LEFT) < 0.0)
227       orientation *= -1;
228     if ((_plotter->hpgl_p2.y - _plotter->hpgl_p1.y) / (HPGL_SCALED_DEVICE_TOP - HPGL_SCALED_DEVICE_BOTTOM) < 0.0)
229       orientation *= -1;
230 
231     new_relative_char_width = fractional_char_width * 100 * base_native_len / (_plotter->hpgl_p2.x - _plotter->hpgl_p1.x);
232     new_relative_char_height =
233       fractional_char_height * 100 * orientation * cos_slant * up_native_len / (_plotter->hpgl_p2.y - _plotter->hpgl_p1.y);
234 
235     /* emit SR instruction only if font was changed or if current
236        size was wrong */
237     if (font_changed ||
238 	(new_relative_char_width != _plotter->hpgl_rel_char_width
239 	 || new_relative_char_height != _plotter->hpgl_rel_char_height))
240       {
241 	sprintf (_plotter->data->page->point, "SR%.3f,%.3f;",
242 		 new_relative_char_width, new_relative_char_height);
243 	_update_buffer (_plotter->data->page);
244 	_plotter->hpgl_rel_char_width = new_relative_char_width;
245 	_plotter->hpgl_rel_char_height = new_relative_char_height;
246       }
247   }
248 
249   /* update slant angle if necessary */
250   if (tan_slant != _plotter->hpgl_tan_char_slant)
251     {
252       sprintf (_plotter->data->page->point, "SL%.3f;", tan_slant);
253       _update_buffer (_plotter->data->page);
254       _plotter->hpgl_tan_char_slant = tan_slant;
255     }
256 }
257 
258 /* If needed, emit a new-style (HP-GL/2) `SD' font-selection command.
259    Return value indicates whether font was changed. */
260 
261 bool
_pl_h_hpgl2_maybe_update_font(S___ (Plotter * _plotter))262 _pl_h_hpgl2_maybe_update_font (S___(Plotter *_plotter))
263 {
264   bool font_change = false;
265   bool font_is_iso_latin_1;
266   int master_font_index;
267   int symbol_set, spacing, posture, stroke_weight, typeface;
268 
269   /* PCL, PS, and Stick fonts are handled separately here only because the
270      font information for them is stored in different tables in g_fontdb.c.
271      We compute parameters we'll need for the HP-GL/2 `SD' font-selection
272      command. */
273 
274   switch (_plotter->drawstate->font_type)
275     {
276     case PL_F_PCL:
277     default:
278       /* compute index of font in master table of fonts, in g_fontdb.c */
279       master_font_index =
280 	(_pl_g_pcl_typeface_info[_plotter->drawstate->typeface_index].fonts)[_plotter->drawstate->font_index];
281 
282       /* #1: symbol set */
283       symbol_set = _pl_g_pcl_font_info[master_font_index].hpgl_symbol_set;
284       /* #2: spacing */
285       spacing = _pl_g_pcl_font_info[master_font_index].hpgl_spacing;
286       /* #3, #4 are pitch and height (we use defaults) */
287       /* #5: posture */
288       posture = _pl_g_pcl_font_info[master_font_index].hpgl_posture;
289       /* #6: stroke weight */
290       stroke_weight = _pl_g_pcl_font_info[master_font_index].hpgl_stroke_weight;
291       /* #7: typeface */
292       typeface = _pl_g_pcl_font_info[master_font_index].pcl_typeface;
293       /* ISO-Latin-1 after reencoding (if any)? */
294       font_is_iso_latin_1 = _pl_g_pcl_font_info[master_font_index].iso8859_1;
295       break;
296     case PL_F_POSTSCRIPT:
297       /* compute index of font in master table of fonts, in g_fontdb.c */
298       master_font_index =
299 	(_pl_g_ps_typeface_info[_plotter->drawstate->typeface_index].fonts)[_plotter->drawstate->font_index];
300 
301       /* #1: symbol set */
302       symbol_set = _pl_g_ps_font_info[master_font_index].hpgl_symbol_set;
303       /* #2: spacing */
304       spacing = _pl_g_ps_font_info[master_font_index].hpgl_spacing;
305       /* #3, #4 are pitch and height (we use defaults) */
306       /* #5: posture */
307       posture = _pl_g_ps_font_info[master_font_index].hpgl_posture;
308       /* #6: stroke weight */
309       stroke_weight = _pl_g_ps_font_info[master_font_index].hpgl_stroke_weight;
310       /* #7: typeface */
311       typeface = _pl_g_ps_font_info[master_font_index].pcl_typeface;
312       /* ISO-Latin-1 after reencoding (if any)? */
313       font_is_iso_latin_1 = _pl_g_ps_font_info[master_font_index].iso8859_1;
314       break;
315     case PL_F_STICK:
316       /* compute index of font in master table of fonts, in g_fontdb.c */
317       master_font_index =
318 	(_pl_g_stick_typeface_info[_plotter->drawstate->typeface_index].fonts)[_plotter->drawstate->font_index];
319 
320       /* #1: symbol set */
321       symbol_set = _pl_g_stick_font_info[master_font_index].hpgl_symbol_set;
322       /* #2: spacing */
323       spacing = _pl_g_stick_font_info[master_font_index].hpgl_spacing;
324       /* #3, #4 are pitch and height (we use defaults) */
325       /* #5: posture */
326       posture = _pl_g_stick_font_info[master_font_index].hpgl_posture;
327       /* #6: stroke weight */
328       stroke_weight = _pl_g_stick_font_info[master_font_index].hpgl_stroke_weight;
329       /* #7: typeface */
330       typeface = _pl_g_stick_font_info[master_font_index].pcl_typeface;
331       /* ISO-Latin-1 after reencoding (if any)? */
332       font_is_iso_latin_1 = _pl_g_stick_font_info[master_font_index].iso8859_1;
333       break;
334     }
335 
336   if (symbol_set != _plotter->hpgl_symbol_set
337       || spacing != _plotter->hpgl_spacing
338       || posture != _plotter->hpgl_posture
339       || stroke_weight != _plotter->hpgl_stroke_weight
340       || typeface != _plotter->hpgl_pcl_typeface)
341     font_change = true;
342 
343   if (font_change)
344     {
345       if (spacing == HPGL2_FIXED_SPACING)
346 	/* fixed-width font */
347 	sprintf (_plotter->data->page->point,
348 		 /* #4 (nominal point size) not needed but included anyway */
349 		 "SD1,%d,2,%d,3,%.3f,4,%.3f,5,%d,6,%d,7,%d;",
350 		 symbol_set, spacing,
351 		 (double)HPGL2_NOMINAL_CHARS_PER_INCH, (double)HPGL2_NOMINAL_POINT_SIZE,
352 		 posture, stroke_weight, typeface);
353       else
354 	/* variable-width font */
355 	sprintf (_plotter->data->page->point,
356 		 /* #3 (nominal chars per inch) not needed but incl'd anyway */
357 		 "SD1,%d,2,%d,3,%.3f,4,%.3f,5,%d,6,%d,7,%d;",
358 		 symbol_set, spacing,
359 		 (double)HPGL2_NOMINAL_CHARS_PER_INCH, (double)HPGL2_NOMINAL_POINT_SIZE,
360 		 posture, stroke_weight, typeface);
361       _update_buffer (_plotter->data->page);
362 
363       /* A hack.  Due to HP's idiosyncratic definition of `ISO-Latin-1
364 	 encoding' for PCL fonts, when plotting a label in an ISO-Latin-1
365 	 PCL font we'll map characters in the lower half into HP's Roman-8
366 	 encoding, and characters in the upper half into HP's ISO-Latin-1
367 	 encoding.  We implement this by using two fonts: standard and
368 	 alternative.  See h_text.c for the DFA that switches back and
369 	 forth (if necessary) when the label is rendered. */
370       if (_plotter->drawstate->font_type == PL_F_PCL
371 	  && font_is_iso_latin_1
372 	  && symbol_set == PCL_ROMAN_8)
373 	{
374 	  if (spacing == HPGL2_FIXED_SPACING)
375 	    /* fixed-width font */
376 	    sprintf (_plotter->data->page->point,
377 		     /* #4 (nominal point size) not needed but included anyway */
378 		     "AD1,%d,2,%d,3,%.3f,4,%.3f,5,%d,6,%d,7,%d;",
379 		     PCL_ISO_8859_1, spacing,
380 		     (double)HPGL2_NOMINAL_CHARS_PER_INCH, (double)HPGL2_NOMINAL_POINT_SIZE,
381 		     posture, stroke_weight, typeface);
382 	  else
383 	    /* variable-width font */
384 	    sprintf (_plotter->data->page->point,
385 		    /* #3 (nominal chars per inch) not needed but included anyway */
386 		     "AD1,%d,2,%d,3,%.3f,4,%.3f,5,%d,6,%d,7,%d;",
387 		     PCL_ISO_8859_1, spacing,
388 		     (double)HPGL2_NOMINAL_CHARS_PER_INCH, (double)HPGL2_NOMINAL_POINT_SIZE,
389 		     posture, stroke_weight, typeface);
390 	  _update_buffer (_plotter->data->page);
391 	}
392 
393       _plotter->hpgl_symbol_set = symbol_set;
394       _plotter->hpgl_spacing = spacing;
395       _plotter->hpgl_posture = posture;
396       _plotter->hpgl_stroke_weight = stroke_weight;
397       _plotter->hpgl_pcl_typeface = typeface;
398     }
399 
400   return font_change;		/* was font changed? */
401 }
402 
403 /* If needed, emit an old-style (pre-HP-GL/2) `CS' font-selection command,
404    and also a `CA' font-change command to make the upper half of the
405    selected font available via SO/SI.  Return value indicates whether font
406    was changed.  (This is used only for Stick fonts, which is all that
407    pre-HP/GL-2 HP-GL devices had.)  */
408 
409 bool
_pl_h_hpgl_maybe_update_font(S___ (Plotter * _plotter))410 _pl_h_hpgl_maybe_update_font (S___(Plotter *_plotter))
411 {
412   bool font_change = false;
413   int new_hpgl_charset_lower, new_hpgl_charset_upper, master_font_index;
414 
415   /* compute index of font in master table of fonts, in g_fontdb.c */
416   master_font_index =
417     (_pl_g_stick_typeface_info[_plotter->drawstate->typeface_index].fonts)[_plotter->drawstate->font_index];
418 
419   /* determine HP character set numbers (old style, pre-HP-GL/2) */
420   new_hpgl_charset_lower = _pl_g_stick_font_info[master_font_index].hpgl_charset_lower;
421   new_hpgl_charset_upper = _pl_g_stick_font_info[master_font_index].hpgl_charset_upper;
422 
423   /* using `CS', select charset for lower half of font */
424   if (new_hpgl_charset_lower != _plotter->hpgl_charset_lower)
425     {
426       sprintf (_plotter->data->page->point, "CS%d;", new_hpgl_charset_lower);
427       _update_buffer (_plotter->data->page);
428       _plotter->hpgl_charset_lower = new_hpgl_charset_lower;
429       font_change = true;
430     }
431 
432   /* using `CA', select charset for upper half, if we have a genuine one (a
433      negative value for the upper charset is our way of flagging that this
434      is a 7-bit font; see comment in h_text.c) */
435   if (new_hpgl_charset_upper >= 0
436       && new_hpgl_charset_upper != _plotter->hpgl_charset_upper)
437     {
438       sprintf (_plotter->data->page->point, "CA%d;", new_hpgl_charset_upper);
439       _update_buffer (_plotter->data->page);
440       _plotter->hpgl_charset_upper = new_hpgl_charset_upper;
441       font_change = true;
442     }
443 
444   return font_change;
445 }
446