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