h_text.c 14 KB

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