~ubuntu-branches/ubuntu/jaunty/plotutils/jaunty

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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
/* This file is part of the GNU plotutils package.  Copyright (C) 1995,
   1996, 1997, 1998, 1999, 2000, 2005, Free Software Foundation, Inc.

   The GNU plotutils package is free software.  You may redistribute it
   and/or modify it under the terms of the GNU General Public License as
   published by the Free Software foundation; either version 2, or (at your
   option) any later version.

   The GNU plotutils package 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 the GNU plotutils package; see the file COPYING.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor,
   Boston, MA 02110-1301, USA. */

/* This file contains a low-level method for adjusting the font of an HP-GL
   or PCL device to agree with an HPGL or PCL Plotter's notion of what it
   should be, prior to plotting a label.  Note: The `PCL 5' output by any
   PCL Plotter is simply a wrapped version of HP-GL/2.

   Before HP-GL/2 (introduced c. 1990), HP-GL devices supported only Stick
   fonts.  In modern PCL5 printers, the suite of 45 PCL fonts is accessible
   too.  Only a few modern high-end PCL5/PS printers (e.g. LaserJet 4000
   series laser printers) also support PS fonts in PCL mode.  PS fonts are
   supported by HP-GL/2 and PCL Plotters if the `--enable-ps-fonts-in-pcl'
   option is specified at configure time.

   After selecting the font, this method invokes the HP-GL DR/SR/SL
   instructions to size and slant the font, as needed.

   The font selection itself is accomplished in either of two ways.
   
   1. In versions of HP-GL prior to HP-GL/2, 7-bit font halves are selected
   with `CS' and `CA' instructions.  They must be switched between when the
   label is plotted via SO/SI; see h_text.c.  The HP-GL device will usually
   supply the upper font half in the Roman-8 encoding, and that too will
   need to be taken into account when the label is plotted.

   2. In HP-GL/2, a single 8-bit font is selected with the HP-GL/2 `SD'
   instruction.  In principle, no switching between 7-bit font halves is
   needed.

   In practice, it's more complicated than that.  For ISO-Latin-1 PCL
   fonts, the SD instruction allegedly allows the ISO-Latin-1 encoding to
   be requested.  But it doesn't work!  One or two characters in the lower
   half (!) don't come out right.  So instead, we use the `SD' instruction
   to retrieve an 8-bit version that uses the Roman-8 encoding, and the
   `AD' instruction to retrieve an alternative 8-bit version that uses the
   ISO-Latin-1 encoding.  We'll use the former for characters in the lower
   half, and the latter for characters in the upper half.  This is bizarre,
   but it works.  See additional comments in h_text.c. */

/* NOTE: This code assumes that P1 and P2 have different x coordinates, and
   different y coordinates.  If that isn't the case, it'll divide by zero.
   So we check for that possibility in _pl_h_paint_text_string() before
   calling this function.  See comment in h_text.c. */

#include "sys-defines.h"
#include "extern.h"

/* Shearing factor for oblique fonts, new_x = x + SHEAR * y  */

#define SHEAR (2.0/7.0)

void
_pl_h_set_font (S___(Plotter *_plotter))
{
  bool font_changed = false;
  bool oblique;
  double cos_slant = 1.0, sin_slant = 0.0;
  double new_relative_label_run, new_relative_label_rise;
  double theta, sintheta, costheta;
  plVector base, up, base_native, up_native;
  double base_native_len, up_native_len, tan_slant;
  
  /* sanity check, should be unnecessary */
  if (_plotter->drawstate->font_type == PL_F_HERSHEY)
    return;

  if (_plotter->drawstate->font_type == PL_F_STICK)
    /* check whether obliquing of this font is called for */
    {
      int master_font_index;

      /* compute index of font in master table of fonts, in g_fontdb.c */
      master_font_index =
	(_pl_g_stick_typeface_info[_plotter->drawstate->typeface_index].fonts)[_plotter->drawstate->font_index];
      oblique = _pl_g_stick_font_info[master_font_index].obliquing;
    }
  else
    oblique = false;

  /* label rotation angle in radians, in user frame */
  theta = M_PI * _plotter->drawstate->text_rotation / 180.0;
  costheta = cos (theta);
  sintheta = sin (theta);

  /* compute in the device frame a `base vector' which is (in the user
     frame) directed along the label, with length equal to the font size) */
  base.x = _plotter->drawstate->true_font_size * XDV(costheta,sintheta);
  base.y = _plotter->drawstate->true_font_size * YDV(costheta,sintheta);

  /* Compute rise and run, relative to x-distance and y-distance between
     scaling points P1,P2. (Either rise or run can be negative; overall
     normalization, e.g. the `100', is irrelevant.  We include the `100' to
     express them as percentages.) */
  new_relative_label_run =  100 * base.x / (HPGL_SCALED_DEVICE_RIGHT - HPGL_SCALED_DEVICE_LEFT);
  new_relative_label_rise = 100 * base.y / (HPGL_SCALED_DEVICE_TOP - HPGL_SCALED_DEVICE_BOTTOM);
  if (new_relative_label_run != 0.0 || new_relative_label_rise != 0.0)
    /* (will always be true except when the font size is so small
       there's really no point in printing the label) */
    {
      /* update device-frame label rotation angle if needed */
      if (_plotter->hpgl_rel_label_run != new_relative_label_run
	  || _plotter->hpgl_rel_label_rise != new_relative_label_rise)
	{    
	  sprintf (_plotter->data->page->point, "DR%.3f,%.3f;",
		   new_relative_label_run, new_relative_label_rise);
	  _update_buffer (_plotter->data->page);
	  _plotter->hpgl_rel_label_run = new_relative_label_run;
	  _plotter->hpgl_rel_label_rise = new_relative_label_rise;
	}
    }

  /* emit command to select new font, if needed (see below) */
  if (_plotter->hpgl_version == 2)
    font_changed = _pl_h_hpgl2_maybe_update_font (S___(_plotter));
  else				/* 0 or 1, i.e. generic HP-GL or HP7550A */
    font_changed = _pl_h_hpgl_maybe_update_font (S___(_plotter));

  /* Compute image, in the device frame, of a so-called `up vector': a
     vector which in the user frame is perpendicular to the above `base'
     vector, and has the same length.  Some fonts are specially obliqued,
     so we take font obliquing (if any) into account here. */

  up.x = _plotter->drawstate->true_font_size * XDV(-sintheta,costheta);
  up.y = _plotter->drawstate->true_font_size * YDV(-sintheta,costheta);
  up.x += (oblique ? SHEAR : 0.0) * base.x;
  up.y += (oblique ? SHEAR : 0.0) * base.y;  

  /* Our `device frame' base and up vectors are really vectors in the
     normalized device frame, in which the viewport has a fixed size.  See
     h_defplot.c.  E.g., the viewport corners (0,0) and (1,1) in the NDC
     frame are respectively mapped to
     (HPGL_SCALED_DEVICE_LEFT,HPGL_SCALED_DEVICE_BOTTOM) and
     (HPGL_SCALED_DEVICE_RIGHT,HPGL_SCALED_DEVICE_TOP) in the normalized
     device frame.  The further mapping to native HP-GL coordinates is
     accomplished by an `SC' scaling instruction emitted at the head of the
     output file; see h_openpl.c.  This further mapping depends on the
     PAGESIZE parameter.

     Unfortunately, when dealing with anamorphically transformed fonts we
     need to manipulate not just vectors in the normalized device frame,
     but also vectors in the true device frame, i.e., in native HP-GL
     units. */

  /* These vectors use native HP-GL units. */
  base_native.x = base.x * (_plotter->hpgl_p2.x - _plotter->hpgl_p1.x) / (HPGL_SCALED_DEVICE_RIGHT - HPGL_SCALED_DEVICE_LEFT);
  base_native.y = base.y * (_plotter->hpgl_p2.y - _plotter->hpgl_p1.y) / (HPGL_SCALED_DEVICE_TOP - HPGL_SCALED_DEVICE_BOTTOM);
  up_native.x = up.x * (_plotter->hpgl_p2.x - _plotter->hpgl_p1.x) / (HPGL_SCALED_DEVICE_RIGHT - HPGL_SCALED_DEVICE_LEFT);
  up_native.y = up.y * (_plotter->hpgl_p2.y - _plotter->hpgl_p1.y) / (HPGL_SCALED_DEVICE_TOP - HPGL_SCALED_DEVICE_BOTTOM);

  base_native_len = sqrt (base_native.x * base_native.x + base_native.y * base_native.y);
  up_native_len = sqrt (up_native.x * up_native.x + up_native.y * up_native.y);

  /* compute character slant angle (in the true device frame, NOT in the
     normalized device frame) */
  if (base_native_len == 0.0 || up_native_len == 0.0) /* a bad situation */
    tan_slant = 0.0;
  else
    {
      sin_slant = ((base_native.x * up_native.x + base_native.y * up_native.y) 
		   / (base_native_len * up_native_len));
      cos_slant = sqrt (1 - sin_slant * sin_slant);
      tan_slant = sin_slant / cos_slant;
    }

  /* Compute nominal horizontal and vertical character sizes as percentages
     of the horizontal and vertical distances between scaling points P1 and
     P2, and specify them with the SR instruction.  

     The two arguments of the SR instruction (the horizontal and vertical
     character sizes) should apparently be 0.5 times the font size, and 0.7
     times the font size.
     
     Why? The 0.5 and 0.7 = 1.4 * 0.5 factors are undocumented HP magic.
     This convention must have been introduced by HP to adapt the SR
     instruction, which dates back to fixed-width plotter fonts (i.e., the
     original Stick font), to modern outline fonts.  Fixed-width plotter
     fonts did not have a font size in the modern sense: they had a
     character width and a character height.  (The former being the width
     of the character proper, which occupied the left 2/3 of a character
     cell, and the latter being what we would nowadays call a cap height.)

     The convention probably arose because Stick fonts look best if the
     aspect ratio is 1.4 (= 0.7/0.5), i.e. if the `character height' is 1.4
     times the `character width'.  I am not sure where the 0.5 came from.
     Possibly back in stick font days, the nominal font size was defined to
     be 4/3 times the width of a character cell, or equivalently the width
     of a character cell was chosen to be 3/4 times the nominal font size.
     This would make the maximum character width (2/3)x(3/4) = (1/2) times
     the nominal font size. */

  {
    double fractional_char_width = 0.5;
    double fractional_char_height = 1.4 * 0.5;
    double new_relative_char_width, new_relative_char_height;

    /* If, in the physical device frame, the font is reflected, we must
       flip the sign of HP-GL's `character height', as used in the SR
       instruction.  To determine whether this sign-flipping is needed, we
       use the fact that the user_frame->physical_device_frame map is the
       product of the user_frame->normalized_device_frame map and the
       normalized_device_frame->physical_device_frame map.  Whether the
       first includes a reflection is precomputed and stored in the drawing
       state.  The second will include a reflection only if exactly one of
       the xsize,ysize fields of PAGESIZE is negative.  We can easily check
       for that by comparing the x,y coordinates of the HP-GL scaling
       points P1,P2. */

    int orientation = _plotter->drawstate->transform.nonreflection ? 1 : -1;

    if ((_plotter->hpgl_p2.x - _plotter->hpgl_p1.x) / (HPGL_SCALED_DEVICE_RIGHT - HPGL_SCALED_DEVICE_LEFT) < 0.0)
      orientation *= -1;
    if ((_plotter->hpgl_p2.y - _plotter->hpgl_p1.y) / (HPGL_SCALED_DEVICE_TOP - HPGL_SCALED_DEVICE_BOTTOM) < 0.0)
      orientation *= -1;

    new_relative_char_width = fractional_char_width * 100 * base_native_len / (_plotter->hpgl_p2.x - _plotter->hpgl_p1.x);
    new_relative_char_height = 
      fractional_char_height * 100 * orientation * cos_slant * up_native_len / (_plotter->hpgl_p2.y - _plotter->hpgl_p1.y);
    
    /* emit SR instruction only if font was changed or if current
       size was wrong */
    if (font_changed || 
	(new_relative_char_width != _plotter->hpgl_rel_char_width
	 || new_relative_char_height != _plotter->hpgl_rel_char_height))
      {
	sprintf (_plotter->data->page->point, "SR%.3f,%.3f;", 
		 new_relative_char_width, new_relative_char_height);
	_update_buffer (_plotter->data->page);
	_plotter->hpgl_rel_char_width = new_relative_char_width;
	_plotter->hpgl_rel_char_height = new_relative_char_height;
      }
  }

  /* update slant angle if necessary */
  if (tan_slant != _plotter->hpgl_tan_char_slant)
    {
      sprintf (_plotter->data->page->point, "SL%.3f;", tan_slant);
      _update_buffer (_plotter->data->page);
      _plotter->hpgl_tan_char_slant = tan_slant;
    }
}

/* If needed, emit a new-style (HP-GL/2) `SD' font-selection command.
   Return value indicates whether font was changed. */

bool
_pl_h_hpgl2_maybe_update_font (S___(Plotter *_plotter))
{
  bool font_change = false;
  bool font_is_iso_latin_1;
  int master_font_index;
  int symbol_set, spacing, posture, stroke_weight, typeface;

  /* PCL, PS, and Stick fonts are handled separately here only because the
     font information for them is stored in different tables in g_fontdb.c.
     We compute parameters we'll need for the HP-GL/2 `SD' font-selection
     command. */

  switch (_plotter->drawstate->font_type)
    {
    case PL_F_PCL:
    default:
      /* compute index of font in master table of fonts, in g_fontdb.c */
      master_font_index =
	(_pl_g_pcl_typeface_info[_plotter->drawstate->typeface_index].fonts)[_plotter->drawstate->font_index];
      
      /* #1: symbol set */
      symbol_set = _pl_g_pcl_font_info[master_font_index].hpgl_symbol_set;
      /* #2: spacing */
      spacing = _pl_g_pcl_font_info[master_font_index].hpgl_spacing;
      /* #3, #4 are pitch and height (we use defaults) */
      /* #5: posture */
      posture = _pl_g_pcl_font_info[master_font_index].hpgl_posture;
      /* #6: stroke weight */
      stroke_weight = _pl_g_pcl_font_info[master_font_index].hpgl_stroke_weight;
      /* #7: typeface */
      typeface = _pl_g_pcl_font_info[master_font_index].pcl_typeface;  
      /* ISO-Latin-1 after reencoding (if any)? */
      font_is_iso_latin_1 = _pl_g_pcl_font_info[master_font_index].iso8859_1;
      break;
    case PL_F_POSTSCRIPT:
      /* compute index of font in master table of fonts, in g_fontdb.c */
      master_font_index =
	(_pl_g_ps_typeface_info[_plotter->drawstate->typeface_index].fonts)[_plotter->drawstate->font_index];
      
      /* #1: symbol set */
      symbol_set = _pl_g_ps_font_info[master_font_index].hpgl_symbol_set;
      /* #2: spacing */
      spacing = _pl_g_ps_font_info[master_font_index].hpgl_spacing;
      /* #3, #4 are pitch and height (we use defaults) */
      /* #5: posture */
      posture = _pl_g_ps_font_info[master_font_index].hpgl_posture;
      /* #6: stroke weight */
      stroke_weight = _pl_g_ps_font_info[master_font_index].hpgl_stroke_weight;
      /* #7: typeface */
      typeface = _pl_g_ps_font_info[master_font_index].pcl_typeface;  
      /* ISO-Latin-1 after reencoding (if any)? */
      font_is_iso_latin_1 = _pl_g_ps_font_info[master_font_index].iso8859_1;
      break;
    case PL_F_STICK:
      /* compute index of font in master table of fonts, in g_fontdb.c */
      master_font_index =
	(_pl_g_stick_typeface_info[_plotter->drawstate->typeface_index].fonts)[_plotter->drawstate->font_index];
      
      /* #1: symbol set */
      symbol_set = _pl_g_stick_font_info[master_font_index].hpgl_symbol_set;
      /* #2: spacing */
      spacing = _pl_g_stick_font_info[master_font_index].hpgl_spacing;
      /* #3, #4 are pitch and height (we use defaults) */
      /* #5: posture */
      posture = _pl_g_stick_font_info[master_font_index].hpgl_posture;
      /* #6: stroke weight */
      stroke_weight = _pl_g_stick_font_info[master_font_index].hpgl_stroke_weight;
      /* #7: typeface */
      typeface = _pl_g_stick_font_info[master_font_index].pcl_typeface;  
      /* ISO-Latin-1 after reencoding (if any)? */
      font_is_iso_latin_1 = _pl_g_stick_font_info[master_font_index].iso8859_1;
      break;
    }
  
  if (symbol_set != _plotter->hpgl_symbol_set
      || spacing != _plotter->hpgl_spacing
      || posture != _plotter->hpgl_posture
      || stroke_weight != _plotter->hpgl_stroke_weight
      || typeface != _plotter->hpgl_pcl_typeface)
    font_change = true;
  
  if (font_change)
    {
      if (spacing == HPGL2_FIXED_SPACING)
	/* fixed-width font */
	sprintf (_plotter->data->page->point, 
		 /* #4 (nominal point size) not needed but included anyway */
		 "SD1,%d,2,%d,3,%.3f,4,%.3f,5,%d,6,%d,7,%d;",
		 symbol_set, spacing, 
		 (double)HPGL2_NOMINAL_CHARS_PER_INCH, (double)HPGL2_NOMINAL_POINT_SIZE, 
		 posture, stroke_weight, typeface);
      else
	/* variable-width font */
	sprintf (_plotter->data->page->point, 
		 /* #3 (nominal chars per inch) not needed but incl'd anyway */
		 "SD1,%d,2,%d,3,%.3f,4,%.3f,5,%d,6,%d,7,%d;",
		 symbol_set, spacing, 
		 (double)HPGL2_NOMINAL_CHARS_PER_INCH, (double)HPGL2_NOMINAL_POINT_SIZE, 
		 posture, stroke_weight, typeface);
      _update_buffer (_plotter->data->page);

      /* A hack.  Due to HP's idiosyncratic definition of `ISO-Latin-1
	 encoding' for PCL fonts, when plotting a label in an ISO-Latin-1
	 PCL font we'll map characters in the lower half into HP's Roman-8
	 encoding, and characters in the upper half into HP's ISO-Latin-1
	 encoding.  We implement this by using two fonts: standard and
	 alternative.  See h_text.c for the DFA that switches back and
	 forth (if necessary) when the label is rendered. */
      if (_plotter->drawstate->font_type == PL_F_PCL
	  && font_is_iso_latin_1
	  && symbol_set == PCL_ROMAN_8)
	{
	  if (spacing == HPGL2_FIXED_SPACING)
	    /* fixed-width font */
	    sprintf (_plotter->data->page->point, 
		     /* #4 (nominal point size) not needed but included anyway */
		     "AD1,%d,2,%d,3,%.3f,4,%.3f,5,%d,6,%d,7,%d;",
		     PCL_ISO_8859_1, spacing, 
		     (double)HPGL2_NOMINAL_CHARS_PER_INCH, (double)HPGL2_NOMINAL_POINT_SIZE, 
		     posture, stroke_weight, typeface);
	  else
	    /* variable-width font */
	    sprintf (_plotter->data->page->point, 
		    /* #3 (nominal chars per inch) not needed but included anyway */
		     "AD1,%d,2,%d,3,%.3f,4,%.3f,5,%d,6,%d,7,%d;",
		     PCL_ISO_8859_1, spacing, 
		     (double)HPGL2_NOMINAL_CHARS_PER_INCH, (double)HPGL2_NOMINAL_POINT_SIZE, 
		     posture, stroke_weight, typeface);
	  _update_buffer (_plotter->data->page);
	}

      _plotter->hpgl_symbol_set = symbol_set;
      _plotter->hpgl_spacing = spacing;
      _plotter->hpgl_posture = posture;
      _plotter->hpgl_stroke_weight = stroke_weight;
      _plotter->hpgl_pcl_typeface = typeface;
    }

  return font_change;		/* was font changed? */
}

/* If needed, emit an old-style (pre-HP-GL/2) `CS' font-selection command,
   and also a `CA' font-change command to make the upper half of the
   selected font available via SO/SI.  Return value indicates whether font
   was changed.  (This is used only for Stick fonts, which is all that
   pre-HP/GL-2 HP-GL devices had.)  */

bool
_pl_h_hpgl_maybe_update_font (S___(Plotter *_plotter))
{
  bool font_change = false;
  int new_hpgl_charset_lower, new_hpgl_charset_upper, master_font_index;

  /* compute index of font in master table of fonts, in g_fontdb.c */
  master_font_index =
    (_pl_g_stick_typeface_info[_plotter->drawstate->typeface_index].fonts)[_plotter->drawstate->font_index];
  
  /* determine HP character set numbers (old style, pre-HP-GL/2) */
  new_hpgl_charset_lower = _pl_g_stick_font_info[master_font_index].hpgl_charset_lower;
  new_hpgl_charset_upper = _pl_g_stick_font_info[master_font_index].hpgl_charset_upper;

  /* using `CS', select charset for lower half of font */
  if (new_hpgl_charset_lower != _plotter->hpgl_charset_lower)
    {
      sprintf (_plotter->data->page->point, "CS%d;", new_hpgl_charset_lower);
      _update_buffer (_plotter->data->page);
      _plotter->hpgl_charset_lower = new_hpgl_charset_lower;
      font_change = true;
    }

  /* using `CA', select charset for upper half, if we have a genuine one (a
     negative value for the upper charset is our way of flagging that this
     is a 7-bit font; see comment in h_text.c) */
  if (new_hpgl_charset_upper >= 0 
      && new_hpgl_charset_upper != _plotter->hpgl_charset_upper)
    {
      sprintf (_plotter->data->page->point, "CA%d;", new_hpgl_charset_upper);
      _update_buffer (_plotter->data->page);
      _plotter->hpgl_charset_upper = new_hpgl_charset_upper;
      font_change = true;
    }

  return font_change;
}