summaryrefslogtreecommitdiff
path: root/pcl/pglabel.c
diff options
context:
space:
mode:
Diffstat (limited to 'pcl/pglabel.c')
-rw-r--r--pcl/pglabel.c1476
1 files changed, 1476 insertions, 0 deletions
diff --git a/pcl/pglabel.c b/pcl/pglabel.c
new file mode 100644
index 000000000..b7175dec2
--- /dev/null
+++ b/pcl/pglabel.c
@@ -0,0 +1,1476 @@
+/* Portions Copyright (C) 2001 artofcode LLC.
+ Portions Copyright (C) 1996, 2001 Artifex Software Inc.
+ Portions Copyright (C) 1988, 2000 Aladdin Enterprises.
+ This software is based in part on the work of the Independent JPEG Group.
+ All Rights Reserved.
+
+ This software is distributed under license and may not be copied, modified
+ or distributed except as expressly authorized under the terms of that
+ license. Refer to licensing information at http://www.artifex.com/ or
+ contact Artifex Software, Inc., 101 Lucas Valley Road #110,
+ San Rafael, CA 94903, (415)492-9861, for further information. */
+/*$Id$ */
+
+/* pglabel.c - HP-GL/2 label commands */
+
+#include "math_.h"
+#include "memory_.h"
+#include "ctype_.h"
+#include "stdio_.h" /* for gdebug.h */
+#include "gdebug.h"
+#include "pcparse.h"
+#include "plvalue.h"
+#include "pgmand.h"
+#include "pginit.h"
+#include "pgfont.h"
+#include "pgdraw.h"
+#include "pggeom.h"
+#include "pgmisc.h"
+#include "pcpage.h"
+#include "pcfsel.h"
+#include "pcsymbol.h"
+#include "pcpalet.h"
+#include "pcdraw.h"
+#include "gscoord.h"
+#include "gsline.h"
+#include "gspath.h"
+#include "gsutil.h"
+#include "gxchar.h" /* for show enumerator */
+#include "gxfont.h"
+#include "gxstate.h" /* for gs_state_client_data */
+
+#define STICK_FONT_TYPEFACE 48
+
+/* NB this next constant is not quite right. The STICK_FONT_TYPEFACE
+ definition is used in the code to cover both stick and arc fonts.
+ Typically the prescription was to choose the stick font typeface
+ (48) and enable proportional spacing to get the arc font. More
+ recently we discovered a new typeface family number that can be
+ used for the proportionally spaced arc fonts (50). This has been
+ reflected in the hpgl/2 selection code but nowhere else. */
+#define ARC_FONT_TYPEFACE 50
+
+/* currently selected font */
+static pl_font_t *
+hpgl_currentfont(const hpgl_state_t *pgls)
+{
+ return pgls->g.font_selection[pgls->g.font_selected].font;
+}
+
+static bool
+hpgl_is_currentfont_stick(const hpgl_state_t *pgls)
+{
+ pl_font_t *plfont = hpgl_currentfont(pgls);
+ if (!plfont)
+ return false;
+ return ( ((plfont->params.typeface_family & 0xfff) == STICK_FONT_TYPEFACE) &&
+ (plfont->params.proportional_spacing == false) );
+}
+
+/* convert points 2 plu - agfa uses 72.307 points per inch */
+static floatp
+hpgl_points_2_plu(const hpgl_state_t *pgls, floatp points)
+{
+ const pcl_font_selection_t *pfs =
+ &pgls->g.font_selection[pgls->g.font_selected];
+ floatp ppi = 72.0;
+ if ( pfs->font->scaling_technology == plfst_Intellifont )
+ ppi = 72.307;
+ return points * (1016.0 / ppi);
+}
+
+/* ------ Next-character procedure ------ */
+
+/* is it a printable character - duplicate of pcl algorithm in
+ pctext.c */
+static bool
+hpgl_is_printable(
+ const pl_symbol_map_t * psm,
+ gs_char chr,
+ bool is_stick
+)
+{
+ if ( is_stick )
+ return (chr >= ' ') && (chr <= 0xff);
+ if ((psm == 0) || (psm->type >= 2))
+ return true;
+ else if (psm->type == 1)
+ chr &= 0x7f;
+ return (chr >= ' ') && (chr <= '\177');
+}
+
+/*
+ * Map a character through the symbol set, if needed.
+ */
+static gs_char
+hpgl_map_symbol(uint chr, const hpgl_state_t *pgls)
+{
+ const pcl_font_selection_t *pfs =
+ &pgls->g.font_selection[pgls->g.font_selected];
+ const pl_symbol_map_t *psm = pfs->map;
+
+ return pl_map_symbol(psm, chr,
+ pfs->font->storage == pcds_internal,
+ pl_complement_to_vocab(pfs->font->character_complement) == plgv_MSL,
+ false);
+}
+
+/* ------ Font selection ------- */
+
+/* Select primary (0) or alternate (1) font. */
+static void
+hpgl_select_font_pri_alt(hpgl_state_t *pgls, int index)
+{
+ if ( pgls->g.font_selected != index ) {
+ hpgl_free_stick_fonts(pgls);
+ pgls->g.font_selected = index;
+ pgls->g.font = 0;
+ }
+ return;
+}
+
+/* forward decl */
+static int hpgl_recompute_font(hpgl_state_t *pgls);
+
+/* Ensure a font is available. */
+static int
+hpgl_ensure_font(hpgl_state_t *pgls)
+{
+ if ( ( pgls->g.font == 0 ) || ( pgls->g.font->pfont == 0 ) )
+ hpgl_call(hpgl_recompute_font(pgls));
+ return 0;
+}
+
+/*
+ * The character complement for the stick font is puzzling: it doesn't seem
+ * to correspond directly to any of the MSL *or* Unicode symbol set bits
+ * described in the Comparison Guide. We set the bits for MSL Basic Latin
+ * (63) and for Unicode ASCII (31), and Latin 1 (30).
+ */
+static const byte stick_character_complement[8] = {
+ 0x7f, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xfe
+};
+
+/* Select the stick font, creating it if necessary. */
+/* We break this out only for readability: it's only called in one place. */
+static int
+hpgl_select_stick_font(hpgl_state_t *pgls)
+{ pcl_font_selection_t *pfs =
+ &pgls->g.font_selection[pgls->g.font_selected];
+ pl_font_t *font = &pgls->g.stick_font[pgls->g.font_selected]
+ [pfs->params.proportional_spacing];
+ gs_font_base *pfont;
+ int code;
+ /* Create a gs_font if none has been created yet. */
+ hpgl_free_stick_fonts(pgls);
+ pfont = gs_alloc_struct(pgls->memory, gs_font_base, &st_gs_font_base,
+ "stick/arc font");
+
+
+ if ( pfont == 0 )
+ return_error(e_Memory);
+ code = pl_fill_in_font((gs_font *)pfont, font, pgls->font_dir,
+ pgls->memory, "stick/arc font");
+ if ( code < 0 )
+ return code;
+ if ( pfs->params.proportional_spacing )
+ hpgl_fill_in_arc_font(pfont, gs_next_ids(pgls->memory, 1));
+ else
+ hpgl_fill_in_stick_font(pfont, gs_next_ids(pgls->memory, 1));
+ font->pfont = (gs_font *)pfont;
+ font->scaling_technology = plfst_TrueType;/****** WRONG ******/
+ font->font_type = plft_Unicode;
+ memcpy(font->character_complement, stick_character_complement, 8);
+ /*
+ * The stick/arc font is protean: set its proportional spacing,
+ * style, and stroke weight parameters to the requested ones.
+ * We could fill in some of the other characteristics earlier,
+ * but it's simpler to do it here.
+ */
+ font->params = pfs->params;
+ font->params.typeface_family = STICK_FONT_TYPEFACE;
+ /*
+ * The stick font is defined in a cell that's only 2/3
+ * the size of the actual character.
+ */
+ pl_fp_set_pitch_cp(&font->params, 100.0*2/3);
+ pfs->font = font;
+ {
+ byte id[2];
+
+ id[0] = pfs->params.symbol_set >> 8;
+ id[1] = pfs->params.symbol_set & 0xff;
+ pfs->map = pcl_find_symbol_map(pgls,
+ id, plgv_Unicode);
+ }
+ return 0;
+}
+
+/* Check whether the stick font supports a given symbol set. */
+static bool
+hpgl_stick_font_supports(const pcl_state_t *pcs, uint symbol_set)
+{
+ pl_glyph_vocabulary_t gv =
+ pl_complement_to_vocab(stick_character_complement);
+ byte id[2];
+ pl_symbol_map_t *map;
+
+ id[0] = symbol_set >> 8;
+ id[1] = symbol_set & 0xff;
+ if ( (map = pcl_find_symbol_map(pcs, id, gv)) == 0 )
+ return false;
+ return pcl_check_symbol_support(map->character_requirements,
+ stick_character_complement);
+}
+
+/* Recompute the current font if necessary. */
+static int
+hpgl_recompute_font(hpgl_state_t *pgls)
+{ pcl_font_selection_t *pfs =
+ &pgls->g.font_selection[pgls->g.font_selected];
+
+ if (( ((pfs->params.typeface_family & 0xfff) == STICK_FONT_TYPEFACE ||
+ (pfs->params.typeface_family & 0xfff) == ARC_FONT_TYPEFACE )
+ && pfs->params.style == 0 /* upright */
+ && hpgl_stick_font_supports(pgls,
+ pfs->params.symbol_set))
+ /* rtl only has stick fonts */
+ || ( pgls->personality == rtl )
+ )
+ hpgl_call(hpgl_select_stick_font(pgls));
+ else
+ { int code = pcl_reselect_font(pfs, pgls);
+
+ if ( code < 0 )
+ return code;
+ }
+ pgls->g.font = pfs->font;
+ pgls->g.map = pfs->map;
+ return pl_load_resident_font_data_from_file(pgls->memory, pfs->font);
+}
+
+/* ------ Position management ------ */
+
+/* accessor for character extra space takes line feed direction into account */
+static inline hpgl_real_t
+hpgl_get_character_extra_space_x(const hpgl_state_t *pgls)
+{
+ return (pgls->g.character.line_feed_direction < 0) ?
+ pgls->g.character.extra_space.y :
+ pgls->g.character.extra_space.x;
+}
+
+static inline hpgl_real_t
+hpgl_get_character_extra_space_y(const hpgl_state_t *pgls)
+{
+ return (pgls->g.character.line_feed_direction < 0) ?
+ pgls->g.character.extra_space.x :
+ pgls->g.character.extra_space.y;
+}
+
+/* Get a character width in the current font, plus extra space if any. */
+/* If the character isn't defined, return 1, otherwise return 0. */
+static int
+hpgl_get_char_width(const hpgl_state_t *pgls, gs_char ch, hpgl_real_t *width)
+{
+ gs_glyph glyph = hpgl_map_symbol(ch, pgls);
+ const pcl_font_selection_t *pfs =
+ &pgls->g.font_selection[pgls->g.font_selected];
+ int code = 0;
+ gs_point gs_width;
+ if ( pgls->g.character.size_mode == hpgl_size_not_set ) {
+ if ( pfs->params.proportional_spacing ) {
+ code = pl_font_char_width(pfs->font, (void *)(pgls->pgs), glyph, &gs_width);
+ /* hack until this code gets written properly, the
+ following should amount to a percentage of the
+ em-square the space character would occupy... */
+ if (code == 1) {
+ gs_width.y = 0;
+ gs_width.x = pl_fp_pitch_cp(&pfs->font->params) / 100.0;
+ }
+
+ if ( !pl_font_is_scalable(pfs->font) ) {
+ if ( code == 0 )
+ *width = gs_width.x * inches_2_plu(1.0 / pfs->font->resolution.x);
+ else
+ *width = coord_2_plu(pl_fp_pitch_cp(&pfs->font->params) * 10);
+ goto add;
+ }
+ else if ( code >= 0 ) {
+ *width = gs_width.x * hpgl_points_2_plu(pgls, pfs->params.height_4ths / 4.0);
+ goto add;
+ }
+ code = 1;
+ }
+ *width = hpgl_points_2_plu(pgls, pl_fp_pitch_cp(&pfs->params) / 100.0);
+ } else {
+ *width = pgls->g.character.size.x;
+ if (pgls->g.character.size_mode == hpgl_size_relative)
+ *width *= pgls->g.P2.x - pgls->g.P1.x;
+
+ }
+ add:
+
+ if ( hpgl_get_character_extra_space_x(pgls) != 0 ) {
+ /* Add extra space. */
+ if ( pfs->params.proportional_spacing && ch != ' ' ) {
+ /* Get the width of the space character. */
+ int scode =
+ pl_font_char_width(pfs->font, (void *)(pgls->pgs), hpgl_map_symbol(' ', pgls), &gs_width);
+ hpgl_real_t extra;
+
+ if ( scode >= 0 )
+ extra = gs_width.x * hpgl_points_2_plu(pgls, pfs->params.height_4ths / 4.0);
+ else
+ extra = hpgl_points_2_plu(pgls, (pl_fp_pitch_cp(&pfs->params)) / 10.0);
+ *width += extra * hpgl_get_character_extra_space_x(pgls);
+ } else {
+ /* All characters have the same width, */
+ /* or we're already getting the width of a space. */
+ *width *= 1.0 + hpgl_get_character_extra_space_x(pgls);
+ }
+ }
+ return code;
+}
+/* Get the cell height or character height in the current font, */
+/* plus extra space if any. */
+static int
+hpgl_get_current_cell_height(const hpgl_state_t *pgls, hpgl_real_t *height)
+{
+ const pcl_font_selection_t *pfs =
+ &pgls->g.font_selection[pgls->g.font_selected];
+
+ if ( pfs->font->scaling_technology != plfst_bitmap ) {
+ gs_point scale = hpgl_current_char_scale(pgls);
+ *height = fabs(scale.y);
+ } else {
+ /* NB temporary not correct */
+ *height = hpgl_points_2_plu(pgls, pfs->params.height_4ths / 4.0);
+ }
+
+ /* the HP manual says linefeed distance or cell height is 1.33
+ times point size for stick fonts and "about" 1.2 times the
+ point size for "most" fonts. Empirical results suggest the
+ stick font value is 1.28. NB This value appears to be
+ slightly different for the proportional arc font. */
+ if (pgls->g.character.text_path == hpgl_text_right || pgls->g.character.text_path == hpgl_text_left) {
+ if ( hpgl_is_currentfont_stick(pgls) )
+ *height *= 1.28;
+ else
+ *height *= 1.2;
+ } else {
+ /* INC */
+ if ( hpgl_is_currentfont_stick(pgls) )
+ *height *= .96;
+ else
+ *height *= .898;
+ }
+
+
+ *height *= 1.0 + hpgl_get_character_extra_space_y(pgls);
+ return 0;
+}
+
+/* distance tranformation for character slant */
+static int
+hpgl_slant_transform_distance(hpgl_state_t *pgls, gs_point *dxy, gs_point *s_dxy)
+{
+ if ( pgls->g.character.slant && !pgls->g.bitmap_fonts_allowed ) {
+ gs_matrix smat;
+ gs_point tmp_dxy = *dxy;
+ gs_make_identity(&smat);
+ smat.yx = pgls->g.character.slant;
+ hpgl_call(gs_distance_transform(tmp_dxy.x, tmp_dxy.y, &smat, s_dxy));
+ }
+ return 0;
+}
+
+/* distance tranformation for character direction */
+static int
+hpgl_rotation_transform_distance(hpgl_state_t *pgls, gs_point *dxy, gs_point *r_dxy)
+{
+ double run = pgls->g.character.direction.x;
+ double rise = pgls->g.character.direction.y;
+ if ( rise != 0 ) {
+ double denom = hypot(run, rise);
+ gs_point tmp_dxy = *dxy;
+ gs_matrix rmat;
+ gs_make_identity(&rmat);
+ rmat.xx = run / denom;
+ rmat.xy = rise / denom;
+ rmat.yx = -rmat.xy;
+ rmat.yy = rmat.xx;
+ hpgl_call(gs_distance_transform(tmp_dxy.x, tmp_dxy.y, &rmat, r_dxy));
+ }
+ return 0;
+}
+
+/* Reposition the cursor. This does all the work for CP, and is also */
+/* used to handle some control characters within LB. */
+/* If pwidth != 0, it points to a precomputed horizontal space width. */
+static int
+hpgl_move_cursor_by_characters(hpgl_state_t *pgls, hpgl_real_t spaces,
+ hpgl_real_t lines, const hpgl_real_t *pwidth)
+{
+ double nx, ny;
+ double dx = 0, dy = 0;
+
+ hpgl_call(hpgl_ensure_font(pgls));
+
+ lines *= pgls->g.character.line_feed_direction;
+ /* For vertical text paths, we have to swap spaces and lines. */
+ switch ( pgls->g.character.text_path )
+ {
+ case hpgl_text_right:
+ nx = spaces, ny = lines; break;
+ case hpgl_text_down:
+ nx = lines, ny = -spaces; break;
+ case hpgl_text_left:
+ nx = -spaces, ny = -lines; break;
+ case hpgl_text_up:
+ nx = -lines, ny = spaces; break;
+ }
+ /* calculate the next label position in relative coordinates. */
+ if ( nx != 0 ) {
+ hpgl_real_t width;
+ if ( pwidth != 0 )
+ width = *pwidth;
+ else
+ hpgl_get_char_width(pgls, ' ', &width);
+ dx = width * nx;
+ }
+ if ( ny != 0 ) {
+ hpgl_real_t height;
+ hpgl_call(hpgl_get_current_cell_height(pgls, &height));
+ dy = ny * height;
+ }
+
+ /*
+ * We just computed the deltas in user units if characters are
+ * using relative sizing, and in PLU otherwise.
+ * If scaling is on but characters aren't using relative
+ * sizing, we have to convert the deltas to user units.
+ */
+ if ( pgls->g.scaling_type != hpgl_scaling_none )
+ {
+ gs_matrix mat;
+ gs_point user_dxy;
+ hpgl_call(hpgl_compute_user_units_to_plu_ctm(pgls, &mat));
+ hpgl_call(gs_distance_transform_inverse(dx, dy, &mat, &user_dxy));
+ dx = user_dxy.x;
+ dy = user_dxy.y;
+ }
+
+ {
+ gs_point dxy;
+ dxy.x = dx;
+ dxy.y = dy;
+ hpgl_rotation_transform_distance(pgls, &dxy, &dxy);
+ dx = dxy.x;
+ dy = dxy.y;
+ }
+ /* a relative move to the new position */
+ hpgl_call(hpgl_add_point_to_path(pgls, dx, dy,
+ hpgl_plot_move_relative, true));
+
+ if ( lines != 0 ) {
+ /* update the position of the carriage return point */
+ pgls->g.carriage_return_pos.x += dx;
+ pgls->g.carriage_return_pos.y += dy;
+ }
+ /* free any selected stick fonts */
+ hpgl_free_stick_fonts(pgls);
+ return 0;
+}
+
+/* Execute a CR for CP or LB. */
+static int
+hpgl_do_CR(hpgl_state_t *pgls)
+{
+ return hpgl_add_point_to_path(pgls, pgls->g.carriage_return_pos.x,
+ pgls->g.carriage_return_pos.y,
+ hpgl_plot_move_absolute,
+ true);
+}
+
+/* CP [spaces,lines]; */
+/* CP [;] */
+int
+hpgl_CP(hpgl_args_t *pargs, hpgl_state_t *pgls)
+{
+ hpgl_real_t spaces, lines;
+
+ if ( hpgl_arg_c_real(pgls->memory, pargs, &spaces) )
+ {
+ if ( !hpgl_arg_c_real(pgls->memory, pargs, &lines) )
+ return e_Range;
+ }
+ else
+ {
+ /* if there are no arguments a carriage return and line feed
+ is executed */
+ hpgl_call(hpgl_do_CR(pgls));
+ spaces = 0, lines = -1;
+ }
+ return hpgl_move_cursor_by_characters(pgls, spaces, lines,
+ (const hpgl_real_t *)0);
+}
+
+/* ------ Label buffer management ------ */
+
+/* initialize the character buffer, setting state pointers for the
+ beginning of the character buffer and the current character within
+ the buffer to position 0. */
+static int
+hpgl_init_label_buffer(hpgl_state_t *pgls)
+{
+ pgls->g.label.char_count = 0;
+ pgls->g.label.buffer_size = hpgl_char_count;
+ return ((pgls->g.label.buffer =
+ gs_alloc_bytes(pgls->memory, hpgl_char_count,
+ "hpgl_init_label_buffer")) == 0 ?
+ e_Memory :
+ 0);
+}
+
+/* release the character buffer */
+static int
+hpgl_destroy_label_buffer(hpgl_state_t *pgls)
+{
+ gs_free_object(pgls->memory, pgls->g.label.buffer,
+ "hpgl_destroy_label_buffer");
+ pgls->g.label.char_count = 0;
+ pgls->g.label.buffer_size = 0;
+ pgls->g.label.buffer = 0;
+ return 0;
+}
+
+/* add a single character to the line buffer */
+static int
+hpgl_buffer_char(hpgl_state_t *pgls, byte ch)
+{
+ /* check if there is room for the new character and resize if
+ necessary */
+ if ( pgls->g.label.buffer_size == pgls->g.label.char_count )
+ { /* Resize the label buffer, currently by doubling its size. */
+ uint new_size = pgls->g.label.buffer_size << 1;
+ byte *new_mem =
+ gs_resize_object(pgls->memory, pgls->g.label.buffer, new_size,
+ "hpgl_resize_label_buffer");
+
+ if ( new_mem == 0 )
+ return_error(e_Memory);
+ pgls->g.label.buffer = new_mem;
+ pgls->g.label.buffer_size = new_size;
+ }
+ /* store the character */
+ pgls->g.label.buffer[pgls->g.label.char_count++] = ch;
+ return 0;
+}
+
+/*
+ * Test if the gl/2 drawing primitives should draw the character or
+ * "show" can be used directly.
+ */
+static bool
+hpgl_use_show(hpgl_state_t *pgls, pl_font_t *pfont)
+{
+
+ /* Show cannot be used if CF is not default since the character
+ may require additional processing by the line drawing code. */
+ if ( (pgls->g.character.fill_mode == 0 && pgls->g.character.edge_pen == 0) ||
+ (pfont->scaling_technology == plfst_bitmap) )
+ return true;
+ else
+ return false;
+}
+
+/* get the scaling factors for a gl/2 character */
+gs_point
+hpgl_current_char_scale(const hpgl_state_t *pgls)
+{
+ const pcl_font_selection_t *pfs =
+ &pgls->g.font_selection[pgls->g.font_selected];
+ pl_font_t *font = pfs->font;
+ bool bitmaps_allowed = pgls->g.bitmap_fonts_allowed;
+
+ gs_point scale;
+
+ if (pgls->g.character.size_mode == hpgl_size_not_set || font->scaling_technology == plfst_bitmap) {
+ if (font->scaling_technology == plfst_bitmap) {
+ scale.x = inches_2_plu(1.0 / font->resolution.x);
+ scale.y = -inches_2_plu(1.0 / font->resolution.y);
+ /* Scale fixed-width fonts by pitch, variable-width by height. */
+ } else if (pfs->params.proportional_spacing) {
+ if (pl_font_is_scalable(font)) {
+ scale.x = hpgl_points_2_plu(pgls, pfs->params.height_4ths / 4.0);
+ scale.y = scale.x;
+ } else {
+ double ratio = (double)pfs->params.height_4ths
+ / font->params.height_4ths;
+
+ scale.x = ratio * inches_2_plu(1.0 / font->resolution.x);
+
+ /*
+ * Bitmap fonts use the PCL coordinate system,
+ * so we must invert the Y coordinate.
+ */
+ scale.y = -(ratio * inches_2_plu(1.0 / font->resolution.y));
+ }
+ } else {
+#define PERCENT_OF_EM ((pl_fp_pitch_cp(&pfs->font->params) / 100.0))
+ scale.x = scale.y = (1.0/PERCENT_OF_EM) * hpgl_points_2_plu(pgls, pl_fp_pitch_cp(&pfs->params) / 100);
+ }
+ } else {
+ /*
+ * Note that the CTM takes P1/P2 into account unless
+ * an absolute character size is in effect.
+ */
+ /* HP is really scaling the cap height not the point size.
+ We assume point size is 1.5 times the point size */
+ scale.x = pgls->g.character.size.x * 1.5 * 1.25;
+ scale.y = pgls->g.character.size.y * 1.5;
+ if (pgls->g.character.size_mode == hpgl_size_relative)
+ scale.x *= pgls->g.P2.x - pgls->g.P1.x,
+ scale.y *= pgls->g.P2.y - pgls->g.P1.y;
+ if (bitmaps_allowed) /* no mirroring */
+ scale.x = fabs(scale.x), scale.y = fabs(scale.y);
+ }
+ return scale;
+
+}
+/*
+ * build the path and render it
+ */
+static int
+hpgl_print_char(
+ hpgl_state_t * pgls,
+ uint ch
+)
+{
+ int text_path = pgls->g.character.text_path;
+ const pcl_font_selection_t * pfs =
+ &pgls->g.font_selection[pgls->g.font_selected];
+ pl_font_t * font = pfs->font;
+ gs_state * pgs = pgls->pgs;
+ gs_matrix save_ctm;
+ gs_font * pfont = pgls->g.font->pfont;
+ gs_point scale = hpgl_current_char_scale(pgls);
+ /*
+ * All character data is relative, but we have to start at
+ * the right place.
+ */
+ hpgl_call( hpgl_add_point_to_path( pgls,
+ pgls->g.pos.x,
+ pgls->g.pos.y,
+ hpgl_plot_move_absolute,
+ true
+ ) );
+ hpgl_call(gs_currentmatrix(pgs, &save_ctm));
+
+ /*
+ * Use plotter unit ctm.
+ */
+ hpgl_call(hpgl_set_plu_ctm(pgls));
+
+ /* ?? WRONG and UGLY */
+ {
+ float metrics[4];
+ if ( (pl_font_char_metrics(font, (void *)(pgls->pgs),
+ hpgl_map_symbol(ch, pgls), metrics)) == 1)
+ ch = ' ';
+ }
+
+ /*
+ * If we're using a stroked font, patch the pen width to reflect
+ * the stroke weight. Note that when the font's build_char
+ * procedure calls stroke, the CTM is still scaled.
+ ****** WHAT IF scale.x != scale.y? ****** this should be unnecessary.
+ */
+
+ if (pfont->PaintType != 0) {
+ const float * widths = pcl_palette_get_pen_widths(pgls->ppalet);
+ float save_width = widths[hpgl_get_selected_pen(pgls)];
+ int weight = pfs->params.stroke_weight;
+ floatp nwidth;
+
+ if (weight == 9999)
+ nwidth = save_width;
+ else {
+ nwidth = 0.06 + weight * (weight < 0 ? 0.005 : 0.010);
+ nwidth *= min(scale.x, scale.y) * (hpgl_width_scale(pgls));
+ }
+ /* in points */
+ gs_setlinewidth(pgs, nwidth * (72.0/1016.0));
+ }
+
+
+ /*
+ * We know that the drawing machinery only sets the CTM
+ * once, at the beginning of the path. We now impose the scale
+ * (and other transformations) on the CTM so that we can add
+ * the symbol outline based on a 1x1-unit cell.
+ */
+ {
+ gs_font * pfont = pgls->g.font->pfont;
+ bool bitmaps_allowed = pgls->g.bitmap_fonts_allowed;
+ bool use_show = hpgl_use_show(pgls, font);
+ gs_matrix pre_rmat, rmat, advance_mat;
+ int angle = -1; /* a multiple of 90 if used */
+ gs_text_enum_t *penum;
+ byte str[2];
+ int code;
+ gs_point start_pt, end_pt;
+ hpgl_real_t space_width;
+ int space_code;
+ hpgl_real_t width;
+
+ /* Handle size. */
+
+ gs_scale(pgs, scale.x, scale.y);
+ /* Handle rotation. */
+ {
+ double run = pgls->g.character.direction.x,
+ rise = pgls->g.character.direction.y;
+
+ if (pgls->g.character.direction_relative)
+ run *= pgls->g.P2.x - pgls->g.P1.x,
+ rise *= pgls->g.P2.y - pgls->g.P1.y;
+ gs_make_identity(&rmat);
+ if ((run < 0) || (rise != 0)) {
+ double denom = hypot(run, rise);
+
+ rmat.xx = run / denom;
+ rmat.xy = rise / denom;
+ rmat.yx = -rmat.xy;
+ rmat.yy = rmat.xx;
+ if ( bitmaps_allowed &&
+ (run != 0) &&
+ (rise != 0) ) { /* not a multple of 90 degrees */
+ /*
+ * If bitmap fonts are allowed, rotate to the nearest
+ * multiple of 90 degrees. We have to do something
+ * special at the end to create the correct escapement.
+ */
+ gs_currentmatrix(pgs, &pre_rmat);
+ if (run >= 0) {
+ if (rise >= 0)
+ angle = (run >= rise ? 0 : 90);
+ else
+ angle = (-rise >= run ? 270 : 0);
+ } else {
+ if (rise >= 0)
+ angle = (rise >= -run ? 90 : 180);
+ else
+ angle = (-run >= -rise ? 180 : 270);
+ }
+ }
+ gs_concat(pgs, &rmat);
+ }
+ }
+
+ /* Handle slant. */
+ if (pgls->g.character.slant && !bitmaps_allowed) {
+ gs_matrix smat;
+
+ gs_make_identity(&smat);
+ smat.yx = pgls->g.character.slant;
+ gs_concat(pgs, &smat);
+ }
+
+ gs_setfont(pgs, pfont);
+ pfont->FontMatrix = pfont->orig_FontMatrix;
+
+ /*
+ * Adjust the initial position of the character according to
+ * the text path. And the left side bearing. It appears
+ * HPGL/2 renders all characters without a left side bearing.
+ */
+ hpgl_call(gs_currentpoint(pgs, &start_pt));
+ if (text_path == hpgl_text_left) {
+ hpgl_get_char_width(pgls, ch, &width);
+ start_pt.x -= width / scale.x;
+ hpgl_call(hpgl_add_point_to_path(pgls, start_pt.x, start_pt.y,
+ hpgl_plot_move_absolute, false));
+
+ }
+
+ /*
+ * Reset the rotation if we're using a bitmap font.
+ */
+ gs_currentmatrix(pgs, &advance_mat);
+ if (angle >= 0) {
+ gs_setmatrix(pgs, &pre_rmat);
+ gs_rotate(pgs, (floatp)angle);
+ }
+
+ str[0] = ch;
+ str[1] = 0;
+
+ /* If SP is a control code, get the width of the space character. */
+ if (ch == ' ') {
+ space_code = hpgl_get_char_width(pgls, ' ', &space_width);
+
+ if ( 0 == space_code &&
+ pl_font_is_scalable(font) &&
+ pfs->params.proportional_spacing )
+ space_code = 1; /* NB hpgl_get_width lies. */
+
+ if (space_code == 1) {
+ /* Space is a control code. */
+ if ( pl_font_is_scalable(font) ) {
+ if (pfs->params.proportional_spacing)
+ space_width =
+ (coord_2_plu(pl_fp_pitch_cp(&pfs->font->params)
+ * pfs->params.height_4ths / 4) ) / scale.x;
+ else
+ space_width = 1.0;
+ /* error! NB scalable fixed pitch space_code == 0 */
+ } else
+ space_width =
+ ( coord_2_plu(pl_fp_pitch_cp(&pfs->font->params) * 10.0) ) / scale.x;
+ space_width *= (1.0 + hpgl_get_character_extra_space_x(pgls));
+ }
+ }
+
+ /* Check for SP control code. */
+ if (ch == ' ' && space_code != 0) {
+ /* Space is a control code. Just advance the position. */
+ gs_setmatrix(pgs, &advance_mat);
+ hpgl_call(hpgl_add_point_to_path(pgls, space_width, 0.0,
+ hpgl_plot_move_relative, false));
+ hpgl_call(gs_currentpoint(pgs, &end_pt));
+ /* at this point we will assume the page is marked */
+ pgls->page_marked = true;
+ } else {
+ gs_text_params_t text;
+ gs_char mychar_buff[1];
+ mychar_buff[0] = hpgl_map_symbol(ch, pgls);
+ if (use_show) {
+ /* not a path that needs to be drawn by the hpgl/2
+ vector drawing code. */
+ hpgl_call(hpgl_set_drawing_color(pgls, hpgl_rm_character));
+ text.operation = TEXT_FROM_CHARS | TEXT_DO_DRAW | TEXT_RETURN_WIDTH;
+ } else
+ text.operation = TEXT_FROM_CHARS | TEXT_DO_TRUE_CHARPATH | TEXT_RETURN_WIDTH;
+ text.data.chars = mychar_buff;
+ /* always on char (gs_chars (ints)) at a time */
+ text.size = 1;
+ code = gs_text_begin(pgs, &text, pgls->memory, &penum);
+ if ( code >= 0 )
+ code = gs_text_process(penum);
+
+ if ( code >= 0 ) {
+ /* we just check the current position for
+ "insidedness" - this seems to address the dirty
+ page issue in practice. */
+ pcl_mark_page_for_current_pos(pgls);
+ }
+ gs_text_release(penum, "hpgl_print_char");
+ if ( code < 0 )
+ return code;
+ gs_setmatrix(pgs, &advance_mat);
+ if (angle >= 0) {
+ /* Compensate for bitmap font non-rotation. */
+ if (text_path == hpgl_text_right) {
+ hpgl_get_char_width(pgls, ch, &width);
+ hpgl_call(hpgl_add_point_to_path(pgls, start_pt.x + width / scale.x,
+ start_pt.y, hpgl_plot_move_absolute, false));
+ }
+ }
+ hpgl_call(gs_currentpoint(pgs, &end_pt));
+ if ( start_pt.x == end_pt.x && start_pt.y == end_pt.y ) {
+ /* freetype doesn't move currentpoint in gs_show(),
+ * since gs cache is not used. NB we don't support
+ * freetype anymore is this necessary?
+ */
+ hpgl_get_char_width(pgls, ch, &width);
+ hpgl_call(hpgl_add_point_to_path(pgls, width / scale.x, 0.0,
+ hpgl_plot_move_relative, false));
+ hpgl_call(gs_currentpoint(pgs, &end_pt));
+ }
+ if ( (text_path == hpgl_text_right) &&
+ (hpgl_get_character_extra_space_x(pgls) != 0) ) {
+ hpgl_get_char_width(pgls, ch, &width);
+ end_pt.x = start_pt.x + width / scale.x;
+ hpgl_call(hpgl_add_point_to_path(pgls, end_pt.x, end_pt.y, hpgl_plot_move_absolute, false));
+ }
+ }
+ /*
+ * Adjust the final position according to the text path.
+ */
+ switch (text_path) {
+ case hpgl_text_right:
+ break;
+
+ case hpgl_text_down:
+ {
+ hpgl_real_t height;
+
+ hpgl_call(hpgl_get_current_cell_height(pgls, &height));
+ hpgl_call( hpgl_add_point_to_path(pgls, start_pt.x, end_pt.y - height / scale.y,
+ hpgl_plot_move_absolute, false) );
+
+ }
+ break;
+
+ case hpgl_text_left:
+ hpgl_call(hpgl_add_point_to_path(pgls, start_pt.x, start_pt.y,
+ hpgl_plot_move_absolute, false));
+ break;
+ case hpgl_text_up:
+ {
+ hpgl_real_t height;
+
+ hpgl_call(hpgl_get_current_cell_height(pgls, &height));
+ hpgl_call(hpgl_add_point_to_path(pgls, start_pt.x,
+ end_pt.y + height / scale.y, hpgl_plot_move_absolute, false));
+ }
+ break;
+ }
+
+ gs_setmatrix(pgs, &save_ctm);
+ hpgl_call(gs_currentpoint(pgs, &end_pt));
+ if (!use_show)
+ hpgl_call(hpgl_draw_current_path(pgls, hpgl_rm_character));
+
+ hpgl_call( hpgl_add_point_to_path( pgls,
+ end_pt.x,
+ end_pt.y,
+ hpgl_plot_move_absolute,
+ true
+ ) );
+ }
+
+ return 0;
+}
+
+/* Determine whether labels can concatenate. */
+/* Note that LO requires a pre-scan iff this is false. */
+static bool
+hpgl_can_concat_labels(const hpgl_state_t *pgls)
+{ /* The following is per TRM 23-78. */
+ static const byte can_concat[22] = {
+ 0, 9, 1, 3, 8, 0, 2, 12, 4, 6,
+ 0, 9, 1, 3, 8, 0, 2, 12, 4, 6,
+ 0, 9
+ };
+
+ return (can_concat[pgls->g.label.origin] &
+ (1 << pgls->g.character.text_path)) != 0;
+}
+
+
+/* return relative coordinates to compensate for origin placement -- LO */
+static int
+hpgl_get_character_origin_offset(hpgl_state_t *pgls, int origin,
+ hpgl_real_t width, hpgl_real_t height,
+ gs_point *offset)
+{
+ double pos_x = 0.0, pos_y = 0.0;
+ double off_x, off_y;
+ hpgl_real_t adjusted_height = height;
+
+#ifdef CHECK_UNIMPLEMENTED
+ if (pgls->g.character.extra_space.x != 0 || pgls->g.character.extra_space.y != 0)
+ dprintf("warning origin offset with non zero extra space not supported\n");
+#endif
+ adjusted_height /= 1.6;
+ if (hpgl_is_currentfont_stick(pgls))
+ adjusted_height /= 1.4;
+
+ /* offset values specified by the documentation */
+ if ( (origin >= 11 && origin <= 14) || (origin >= 16 && origin <= 19) ) {
+ if (hpgl_is_currentfont_stick(pgls)) {
+ off_x = off_y = 0.33 * adjusted_height;
+ } else {
+ /* the documentation says this should be .25, experiments
+ indicate .33 like stick fonts. */
+ off_x = off_y = 0.33 * adjusted_height;
+ }
+ }
+
+ switch ( origin ) {
+ case 11:
+ pos_x = -off_x;
+ pos_y = -off_y;
+ case 1:
+ break;
+ case 12:
+ pos_x = -off_x;
+ case 2:
+ pos_y = .5 * adjusted_height;
+ break;
+ case 13:
+ pos_x = -off_x;
+ pos_y = off_y;
+ case 3:
+ pos_y += adjusted_height;
+ break;
+ case 14:
+ pos_y = -off_y;
+ case 4:
+ pos_x = .5 * width;
+ break;
+ case 15:
+ case 5:
+ pos_x = .5 * width;
+ pos_y = .5 * adjusted_height;
+ break;
+ case 16:
+ pos_y = off_y;
+ case 6:
+ pos_x = .5 * width;
+ pos_y += adjusted_height;
+ break;
+ case 17:
+ pos_x = off_x;
+ pos_y = -off_y;
+ case 7:
+ pos_x += width;
+ break;
+ case 18:
+ pos_x = off_x;
+ case 8:
+ pos_x += width;
+ pos_y = .5 * adjusted_height;
+ break;
+ case 19:
+ pos_x = off_x;
+ pos_y = off_y;
+ case 9:
+ pos_x += width;
+ pos_y += adjusted_height;
+ break;
+ case 21:
+ {
+ /* // LO21 prints at the current position not pcl CAP.
+ gs_matrix save_ctm;
+ gs_point pcl_pos_dev, label_origin;
+
+ gs_currentmatrix(pgls->pgs, &save_ctm);
+ pcl_set_ctm(pgls, false);
+ hpgl_call(gs_transform(pgls->pgs, (floatp)pgls->cap.x,
+ (floatp)pgls->cap.y, &pcl_pos_dev));
+ gs_setmatrix(pgls->pgs, &save_ctm);
+ hpgl_call(gs_itransform(pgls->pgs, (floatp)pcl_pos_dev.x,
+ (floatp)pcl_pos_dev.y, &label_origin));
+ pos_x = -(pgls->g.pos.x - label_origin.x);
+ pos_y = (pgls->g.pos.y - label_origin.y);
+ */
+ }
+ break;
+ default:
+ dprintf("unknown label parameter");
+
+ }
+ /* a relative move to the new position */
+ offset->x = pos_x;
+ offset->y = pos_y;
+
+ /* account for character direction and slant */
+ hpgl_rotation_transform_distance(pgls, offset, offset);
+ hpgl_slant_transform_distance(pgls, offset, offset);
+
+ switch ( pgls->g.character.text_path ) {
+ case hpgl_text_left:
+ offset->x -= width;
+ break;
+ case hpgl_text_down:
+ offset->y -= height;
+ break;
+ default:
+ DO_NOTHING;
+ }
+
+ {
+ gs_matrix mat;
+ hpgl_compute_user_units_to_plu_ctm(pgls, &mat);
+ offset->x /= mat.xx;
+ offset->y /= mat.yy;
+ }
+ /* convert to user units */
+ return 0;
+}
+
+static gs_char
+hpgl_next_char(hpgl_state_t *pgls, byte **ppb)
+{
+ byte *pb = *ppb;
+ gs_char chr = *pb++;
+
+ if (pgls->g.label.double_byte)
+ chr = (chr << 8) + *pb++;
+ *ppb = pb;
+ return chr;
+}
+
+/* Prints a buffered line of characters. */
+/* If there is a CR, it is the last character in the buffer. */
+static int
+hpgl_process_buffer(hpgl_state_t *pgls, gs_point *offset)
+{
+ hpgl_real_t label_length = 0.0, label_height = 0.0;
+ bool vertical = hpgl_text_is_vertical(pgls->g.character.text_path);
+ int i, inc;
+
+ /* a peculiar side effect of LABEL parsing double byte characters
+ is it leaves an extra byte in the buffer - fix that now, and
+ properly set the increment for the parallel loops below. */
+ if ( pgls->g.label.double_byte ) {
+ pgls->g.label.char_count--;
+ inc = 2;
+ } else {
+ inc = 1;
+ }
+
+
+ /*
+ * NOTE: the two loops below must be consistent with each other!
+ */
+
+ {
+ hpgl_real_t width = 0.0, height = 0.0;
+ int save_index = pgls->g.font_selected;
+ bool first_char_on_line = true;
+ byte *b = pgls->g.label.buffer;
+
+ for ( i=0; i < pgls->g.label.char_count; i+=inc ) {
+ gs_char ch = hpgl_next_char(pgls, &b);
+ if ( ch < 0x20 && !pgls->g.transparent_data )
+ switch (ch) {
+ case BS :
+ if ( width == 0.0 ) { /* BS as first char of string */
+ hpgl_call(hpgl_ensure_font(pgls));
+ hpgl_get_char_width(pgls, ' ', &width);
+ hpgl_call(hpgl_get_current_cell_height(pgls, &height));
+ }
+ if ( vertical ) { /* Vertical text path, back up in Y. */
+ label_height -= height;
+ if ( label_height < 0.0 )
+ label_height = 0.0;
+ } else { /* Horizontal text path, back up in X. */
+ label_length -= width;
+ if ( label_length < 0.0 )
+ label_length = 0.0;
+ }
+ continue;
+ case LF :
+ first_char_on_line = true;
+ continue;
+ case CR :
+ continue;
+ case FF :
+ continue;
+ case HT :
+ hpgl_call(hpgl_ensure_font(pgls));
+ hpgl_get_char_width(pgls, ' ', &width);
+ width *= 5;
+ goto acc_ht;
+ case SI :
+ hpgl_select_font_pri_alt(pgls, 0);
+ continue;
+ case SO :
+ hpgl_select_font_pri_alt(pgls, 1);
+ continue;
+ default :
+ break;
+ }
+ hpgl_call(hpgl_ensure_font(pgls));
+ hpgl_get_char_width(pgls, ch, &width);
+acc_ht: hpgl_call(hpgl_get_current_cell_height(pgls, &height));
+ if ( vertical ) {
+ if ( width > label_length )
+ label_length = width;
+ if ( !first_char_on_line )
+ label_height += height;
+ else
+ first_char_on_line = false;
+ } else { /* Horizontal text path: sum widths, take max of heights. */
+ label_length += width;
+ if ( height > label_height )
+ label_height = height;
+ }
+ }
+ hpgl_select_font_pri_alt(pgls, save_index);
+ }
+ hpgl_call(hpgl_get_character_origin_offset(pgls, pgls->g.label.origin,
+ label_length, label_height,
+ offset));
+
+ /* now add the offsets in relative plu coordinates */
+ hpgl_call(hpgl_add_point_to_path(pgls, -offset->x, -offset->y,
+ hpgl_plot_move_relative, false));
+
+ {
+ byte *b = pgls->g.label.buffer;
+ for ( i = 0; i < pgls->g.label.char_count; i+=inc ) {
+ gs_char ch = hpgl_next_char(pgls, &b);
+ if ( ch < 0x20 && !pgls->g.transparent_data ) {
+ hpgl_real_t spaces, lines;
+
+ switch (ch) {
+ case BS :
+ spaces = -1, lines = 0;
+ break;
+ case LF :
+ /*
+ * If the text path is vertical, we must use the
+ * computed label (horizontal) width, not the width
+ * of a space.
+ */
+ if ( vertical ) {
+ const pcl_font_selection_t * pfs =
+ &pgls->g.font_selection[pgls->g.font_selected];
+ hpgl_real_t label_advance;
+ /* Handle size. */
+ if (pgls->g.character.size_mode == hpgl_size_not_set) {
+ /* Scale fixed-width fonts by pitch, variable-width by height. */
+ if (pfs->params.proportional_spacing) {
+ if (pl_font_is_scalable(pfs->font))
+ label_advance = hpgl_points_2_plu(pgls, pfs->params.height_4ths / 4.0);
+ else {
+ double ratio = (double)pfs->params.height_4ths
+ / pfs->font->params.height_4ths;
+ label_advance = ratio * inches_2_plu(1.0 / pfs->font->resolution.x);
+ }
+ } else
+ label_advance = hpgl_points_2_plu(pgls, pl_fp_pitch_cp(&pfs->params) /
+ pl_fp_pitch_cp(&pfs->font->params) );
+ if ( hpgl_get_character_extra_space_x(pgls) != 0 )
+ label_advance *= 1.0 + hpgl_get_character_extra_space_x(pgls);
+ } else {
+ /*
+ * Note that the CTM takes P1/P2 into account unless
+ * an absolute character size is in effect.
+ *
+ *
+ * HACKS - I am not sure what this should be the
+ * actual values ???
+ */
+ label_advance = pgls->g.character.size.x * 1.5 * 1.25;
+ if (pgls->g.character.size_mode == hpgl_size_relative)
+ label_advance *= pgls->g.P2.x - pgls->g.P1.x;
+ if (pgls->g.bitmap_fonts_allowed) /* no mirroring */
+ label_advance = fabs(label_advance);
+ }
+ hpgl_move_cursor_by_characters(pgls, 0, -1,
+ &label_advance);
+ continue;
+ }
+ spaces = 0, lines = -1;
+ break;
+ case CR :
+ hpgl_call(hpgl_do_CR(pgls));
+ continue;
+ case FF :
+ /* does nothing */
+ spaces = 0, lines = 0;
+ break;
+ case HT :
+ /* appears to expand to 5 spaces */
+ spaces = 5, lines = 0;
+ break;
+ case SI :
+ hpgl_select_font_pri_alt(pgls, 0);
+ continue;
+ case SO :
+ hpgl_select_font_pri_alt(pgls, 1);
+ continue;
+ default :
+ goto print;
+ }
+ hpgl_move_cursor_by_characters(pgls, spaces, lines,
+ (const hpgl_real_t *)0);
+ continue;
+ }
+print: {
+ /* if this a printable character print it
+ otherwise continue, a character can be
+ printable and undefined in which case
+ it is printed as a space character */
+ const pcl_font_selection_t *pfs =
+ &pgls->g.font_selection[pgls->g.font_selected];
+ if ( !hpgl_is_printable(pfs->map, ch,
+ (pfs->params.typeface_family & 0xfff) == STICK_FONT_TYPEFACE ) )
+ continue;
+ }
+ hpgl_call(hpgl_ensure_font(pgls));
+ hpgl_call(hpgl_print_char(pgls, ch));
+ }
+ }
+ pgls->g.label.char_count = 0;
+ return 0;
+}
+
+
+/**
+ * used by hpgl_LB() to find the end of a label
+ *
+ * 8bit and 16bit label terminator check
+ * (prev << 8) & curr -> 16 bit
+ * have_16bits allows per byte call with true on 16bit boundary.
+ */
+static
+bool is_terminator( hpgl_state_t *pgls, byte prev, byte curr, bool have_16bits )
+{
+ return
+ pgls->g.label.double_byte ?
+ ( have_16bits && prev == 0 && curr == pgls->g.label.terminator ) :
+ ( curr == pgls->g.label.terminator );
+}
+
+#define GL_LB_HAVE_16BITS (pgls->g.label.have_16bits)
+#define GL_LB_CH (pgls->g.label.ch)
+#define GL_LB_PREV_CH (pgls->g.label.prev_ch)
+
+/* LB ..text..terminator */
+int
+hpgl_LB(hpgl_args_t *pargs, hpgl_state_t *pgls)
+{ const byte *p = pargs->source.ptr;
+ const byte *rlimit = pargs->source.limit;
+ bool print_terminator = pgls->g.label.print_terminator;
+
+ if ( pargs->phase == 0 )
+ {
+ /* initialize the character buffer and CTM first time only */
+ hpgl_call(hpgl_draw_current_path(pgls, hpgl_rm_vector));
+ hpgl_call(hpgl_init_label_buffer(pgls));
+ hpgl_call(hpgl_set_ctm(pgls));
+ hpgl_call(hpgl_set_clipping_region(pgls, hpgl_rm_vector));
+ pgls->g.label.initial_pos = pgls->g.pos;
+ GL_LB_HAVE_16BITS = true;
+ GL_LB_CH = 0xff;
+ GL_LB_PREV_CH = 0xff; /* for two byte terminators */
+ pargs->phase = 1;
+ }
+
+ while ( p < rlimit )
+ {
+ /* This is not ugly and unintuitive */
+ GL_LB_HAVE_16BITS = !GL_LB_HAVE_16BITS;
+ GL_LB_PREV_CH = GL_LB_CH;
+ GL_LB_CH = *++p;
+ if_debug1('I',
+ (GL_LB_CH == '\\' ? " \\%c" : GL_LB_CH >= 33 && GL_LB_CH <= 126 ? " %c" :
+ " \\%03o"),
+ GL_LB_CH);
+ if ( is_terminator(pgls, GL_LB_PREV_CH, GL_LB_CH, GL_LB_HAVE_16BITS) )
+ {
+ if ( !print_terminator )
+ {
+ gs_point lo_offsets;
+ hpgl_call(hpgl_process_buffer(pgls, &lo_offsets));
+ hpgl_call(hpgl_destroy_label_buffer(pgls));
+ pargs->source.ptr = p;
+ /*
+ * Depending on the DV/LO combination, conditionally
+ * restore the initial position, per TRM 23-78.
+ */
+ if ( !hpgl_can_concat_labels(pgls) ) {
+ hpgl_call(hpgl_add_point_to_path(pgls,
+ pgls->g.carriage_return_pos.x,
+ pgls->g.carriage_return_pos.y,
+ hpgl_plot_move_absolute, true));
+ } else {
+ /* undo the label origin offsets */
+ hpgl_call(hpgl_add_point_to_path(pgls, lo_offsets.x, lo_offsets.y,
+ hpgl_plot_move_relative, false));
+ }
+ /* clear the current path terminating carriage
+ returns and linefeeds will leave "moveto's" in
+ the path */
+ hpgl_call(hpgl_clear_current_path(pgls));
+ /* also clean up stick fonts - they are likely to
+ become dangling references in the current font
+ scheme since they don't have a dictionary entry */
+ hpgl_free_stick_fonts(pgls);
+ return 0;
+ }
+ /*
+ * Process the character in the ordinary way, then come here
+ * again. We do things this way to simplify the case where
+ * the terminator is a control character.
+ */
+ --p;
+ print_terminator = false;
+ }
+
+ /* process the buffer for a carriage return so that we can
+ treat the label origin correctly, and initialize a new
+ buffer */
+
+ hpgl_call(hpgl_buffer_char(pgls, GL_LB_CH));
+ if ( GL_LB_CH == CR && !pgls->g.transparent_data )
+ {
+ gs_point lo_offsets;
+ hpgl_call(hpgl_process_buffer(pgls, &lo_offsets));
+ hpgl_call(hpgl_destroy_label_buffer(pgls));
+ hpgl_call(hpgl_init_label_buffer(pgls));
+ }
+ }
+ pargs->source.ptr = p;
+ return e_NeedData;
+}
+
+void
+hpgl_free_stick_fonts(hpgl_state_t *pgls)
+{
+ pcl_font_selection_t *pfs =
+ &pgls->g.font_selection[pgls->g.font_selected];
+ pl_font_t *font = &pgls->g.stick_font[pgls->g.font_selected]
+ [pfs->params.proportional_spacing];
+
+ /* no stick fonts - nothing to do */
+ if ( font->pfont == 0 )
+ return;
+
+ gs_free_object(pgls->memory, font->pfont, "stick/arc font");
+ font->pfont = 0;
+ return;
+}
+
+int
+hpgl_print_symbol_mode_char(hpgl_state_t *pgls)
+{
+ /* save the original origin since symbol mode character are
+ always centered */
+ int saved_origin = pgls->g.label.origin;
+ gs_point save_pos = pgls->g.pos;
+ gs_point lo_offsets;
+ hpgl_call(hpgl_gsave(pgls));
+ /* HAS this need checking. I don't know how text direction
+ and label origin interact in symbol mode */
+ pgls->g.label.origin = 5;
+ /* HAS - alot of work for one character */
+ hpgl_call(hpgl_clear_current_path(pgls));
+ hpgl_call(hpgl_init_label_buffer(pgls));
+ hpgl_call(hpgl_buffer_char(pgls, pgls->g.symbol_mode));
+ hpgl_call(hpgl_process_buffer(pgls, &lo_offsets));
+ hpgl_call(hpgl_destroy_label_buffer(pgls));
+ hpgl_call(hpgl_grestore(pgls));
+ /* restore the origin */
+ pgls->g.label.origin = saved_origin;
+ hpgl_call(hpgl_set_current_position(pgls, &save_pos));
+ hpgl_free_stick_fonts(pgls);
+ return 0;
+}
+
+/* Initialization */
+static int
+pglabel_do_registration(
+ pcl_parser_state_t *pcl_parser_state,
+ gs_memory_t *mem
+)
+{ /* Register commands */
+ DEFINE_HPGL_COMMANDS(mem)
+ HPGL_COMMAND('C', 'P', hpgl_CP, hpgl_cdf_lost_mode_cleared|hpgl_cdf_pcl_rtl_both),
+ /* LB also has special argument parsing. */
+ HPGL_COMMAND('L', 'B', hpgl_LB, hpgl_cdf_polygon|hpgl_cdf_lost_mode_cleared|hpgl_cdf_pcl_rtl_both),
+ END_HPGL_COMMANDS
+ return 0;
+}
+
+const pcl_init_t pglabel_init = {
+ pglabel_do_registration, 0
+};