diff options
author | Robin Watts <robin.watts@artifex.com> | 2012-02-27 03:11:02 +0000 |
---|---|---|
committer | Robin Watts <robin.watts@artifex.com> | 2012-04-13 19:57:28 +0100 |
commit | d1579963f90d1bfff7fa7e6e7a0417fa8be266b4 (patch) | |
tree | 44bf2c4d2a3e7b8efd61d8229862f67f746b4af0 | |
parent | 34b74d7bb34390ad75ba8ca52b6bd5e94e85a51e (diff) |
Support HPGL style path handling.
HPGL handles paths slightly differently to the postscript imaging model.
When a path is filled, successive moveto's are treated as linetos.
(i.e. the 'place we close the path to' is left at the first moveto,
and the area remains fillable). Stroking is unaffected however.
To model this in Ghostscript we add a new path segment type 's_gap'.
The filling code treats this as a lineto. The stroking code is updated
to not stroke such edges (and not to break the subpath at this point).
We add a new parameter to the imager state (hpgl_path_mode), new
accessor functions (gs_sethpglpathmode, gs_currenthpglpathmode),
and new postscript operators (.sethpglpathmode and .currenthpglpathmode).
If hpgl path mode is set to a non-zero value, then path construction
treats movetos in an open subpath as gaptos.
Currently this is disabled (see pcl/pctop.c for where it would be
enabled) until we get the pcl interpreter to generate paths in
exactly the right form.
Still to do:
* Update PDF write to spot such paths and to convert them as
appropriate when writing out.
-rw-r--r-- | gs/base/gdevpdfd.c | 1 | ||||
-rw-r--r-- | gs/base/gdevtrac.c | 4 | ||||
-rw-r--r-- | gs/base/gdevvec.c | 3 | ||||
-rw-r--r-- | gs/base/gspath.c | 22 | ||||
-rw-r--r-- | gs/base/gspath1.c | 1 | ||||
-rw-r--r-- | gs/base/gspenum.h | 1 | ||||
-rw-r--r-- | gs/base/gsstate.c | 14 | ||||
-rw-r--r-- | gs/base/gsstate.h | 2 | ||||
-rw-r--r-- | gs/base/gxclpath.c | 44 | ||||
-rw-r--r-- | gs/base/gxclpath.h | 2 | ||||
-rw-r--r-- | gs/base/gxclrast.c | 12 | ||||
-rw-r--r-- | gs/base/gxcpath.c | 4 | ||||
-rw-r--r-- | gs/base/gxistate.h | 3 | ||||
-rw-r--r-- | gs/base/gxline.h | 2 | ||||
-rw-r--r-- | gs/base/gxpath.c | 37 | ||||
-rw-r--r-- | gs/base/gxpath.h | 1 | ||||
-rw-r--r-- | gs/base/gxpath2.c | 15 | ||||
-rw-r--r-- | gs/base/gxpcopy.c | 46 | ||||
-rw-r--r-- | gs/base/gxpdash.c | 13 | ||||
-rw-r--r-- | gs/base/gxstroke.c | 31 | ||||
-rw-r--r-- | gs/base/gxttfb.c | 1 | ||||
-rw-r--r-- | gs/base/gzpath.h | 4 | ||||
-rw-r--r-- | gs/doc/Language.htm | 18 | ||||
-rw-r--r-- | gs/psi/zgstate.c | 16 | ||||
-rw-r--r-- | pcl/pctop.c | 3 |
25 files changed, 274 insertions, 26 deletions
diff --git a/gs/base/gdevpdfd.c b/gs/base/gdevpdfd.c index b5ad3199a..0f058515d 100644 --- a/gs/base/gdevpdfd.c +++ b/gs/base/gdevpdfd.c @@ -260,6 +260,7 @@ pdf_is_same_clip_path(gx_device_pdf * pdev, const gx_clip_path * pcpath) return 0; case gs_pe_moveto: case gs_pe_lineto: + case gs_pe_gapto: if (vs0[0].x != vs1[0].x || vs0[0].y != vs1[0].y) return 0; } diff --git a/gs/base/gdevtrac.c b/gs/base/gdevtrac.c index 535cebc01..be1492f01 100644 --- a/gs/base/gdevtrac.c +++ b/gs/base/gdevtrac.c @@ -100,6 +100,10 @@ trace_path(const gx_path *path) dprintf2(" %g %g lineto\n", fixed2float(pts[0].x), fixed2float(pts[0].y)); continue; + case gs_pe_gapto: + dprintf2(" %g %g gapto\n", fixed2float(pts[0].x), + fixed2float(pts[0].y)); + continue; case gs_pe_curveto: dprintf6(" %g %g %g %g %g %g curveto\n", fixed2float(pts[0].x), fixed2float(pts[0].y), fixed2float(pts[1].x), diff --git a/gs/base/gdevvec.c b/gs/base/gdevvec.c index b4a6ef4b3..ecff2d230 100644 --- a/gs/base/gdevvec.c +++ b/gs/base/gdevvec.c @@ -101,6 +101,7 @@ gdev_vector_dopath(gx_device_vector *vdev, const gx_path * ppath, sw: if (type & gx_path_type_optimize) { opt: + /* RJW: We fail to optimize gaptos */ if (pe_op == gs_pe_lineto) { if (!incomplete_line) { line_end = vs[0]; @@ -173,6 +174,7 @@ gdev_vector_dopath(gx_device_vector *vdev, const gx_path * ppath, } goto draw; case gs_pe_lineto: + case gs_pe_gapto: if (need_moveto) { /* see gs_pe_moveto case */ code = gdev_vector_dopath_segment(&state, gs_pe_moveto, &line_start); @@ -642,6 +644,7 @@ gdev_vector_dopath_segment(gdev_vector_dopath_state_t *state, int pe_op, state->prev = vp[0]; break; case gs_pe_lineto: + case gs_pe_gapto: /* FIXME */ code = gs_point_transform_inverse(fixed2float(vs[0].x), fixed2float(vs[0].y), pmat, &vp[0]); if (code < 0) diff --git a/gs/base/gspath.c b/gs/base/gspath.c index 4964b2a08..771a73059 100644 --- a/gs/base/gspath.c +++ b/gs/base/gspath.c @@ -154,12 +154,22 @@ gs_moveto_aux(gs_imager_state *pis, gx_path *ppath, floatp x, floatp y) code = clamp_point_aux(pis->clamp_coordinates, &pt, x, y); if (code < 0) return code; - code = gx_path_add_point(ppath, pt.x, pt.y); - if (code < 0) - return code; - ppath->start_flags = ppath->state_flags; - gx_setcurrentpoint(pis, x, y); - pis->subpath_start = pis->current_point; + if (pis->hpgl_path_mode && path_subpath_open(ppath)) + { + code = gx_path_add_gap_notes(ppath, pt.x, pt.y, 0); + if (code < 0) + return code; + gx_setcurrentpoint(pis, x, y); + } + else + { + code = gx_path_add_point(ppath, pt.x, pt.y); + if (code < 0) + return code; + ppath->start_flags = ppath->state_flags; + gx_setcurrentpoint(pis, x, y); + pis->subpath_start = pis->current_point; + } pis->current_point_valid = true; return 0; } diff --git a/gs/base/gspath1.c b/gs/base/gspath1.c index 4f4d760ae..f886460f8 100644 --- a/gs/base/gspath1.c +++ b/gs/base/gspath1.c @@ -641,6 +641,7 @@ gs_path_enum_next(gs_path_enum * penum, gs_point ppts[3]) /* falls through */ case gs_pe_moveto: case gs_pe_lineto: + case gs_pe_gapto: if ((code = gs_point_transform_inverse( fixed2float(fpts[0].x), fixed2float(fpts[0].y), diff --git a/gs/base/gspenum.h b/gs/base/gspenum.h index 920a72d92..5cda5033d 100644 --- a/gs/base/gspenum.h +++ b/gs/base/gspenum.h @@ -22,6 +22,7 @@ #define gs_pe_lineto 2 #define gs_pe_curveto 3 #define gs_pe_closepath 4 +#define gs_pe_gapto 5 /* Define an abstract type for the path enumerator. */ typedef struct gs_path_enum_s gs_path_enum; diff --git a/gs/base/gsstate.c b/gs/base/gsstate.c index 470ca8eea..9859fa822 100644 --- a/gs/base/gsstate.c +++ b/gs/base/gsstate.c @@ -839,6 +839,20 @@ gs_currenttextrenderingmode(const gs_state * pgs) return pgs->text_rendering_mode; } +/* sethpglpathmode */ +void +gs_sethpglpathmode(gs_state * pgs, int path) +{ + pgs->hpgl_path_mode = path; +} + +/* currenthpglpathmode */ +int +gs_currenthpglpathmode(const gs_state * pgs) +{ + return pgs->hpgl_path_mode; +} + /* ------ Internal routines ------ */ /* Free the privately allocated parts of a gstate. */ diff --git a/gs/base/gsstate.h b/gs/base/gsstate.h index 5b6bea5a0..c9509d8e2 100644 --- a/gs/base/gsstate.h +++ b/gs/base/gsstate.h @@ -90,5 +90,7 @@ void gs_settextrenderingmode(gs_state * pgs, uint trm); uint gs_currenttextrenderingmode(const gs_state * pgs); #include "gscpm.h" gs_in_cache_device_t gs_incachedevice(const gs_state *); +void gs_sethpglpathmode(gs_state *, int); +int gs_currenthpglpathmode(const gs_state *); #endif /* gsstate_INCLUDED */ diff --git a/gs/base/gxclpath.c b/gs/base/gxclpath.c index 01fa187ef..84e96f76a 100644 --- a/gs/base/gxclpath.c +++ b/gs/base/gxclpath.c @@ -1201,6 +1201,8 @@ cmd_put_segment(cmd_segment_writer * psw, byte op, /* Put out a line segment command. */ #define cmd_put_rmoveto(psw, operands)\ cmd_put_segment(psw, cmd_opv_rmoveto, operands, sn_none) +#define cmd_put_rgapto(psw, operands, notes)\ + cmd_put_segment(psw, cmd_opv_rgapto, operands, notes) #define cmd_put_rlineto(psw, operands, notes)\ cmd_put_segment(psw, cmd_opv_rlineto, operands, notes) @@ -1339,6 +1341,48 @@ cmd_put_path(gx_device_clist_writer * cldev, gx_clist_state * pcls, if_debug2('p', "[p]moveto (%g,%g)\n", fixed2float(px), fixed2float(py)); break; + case gs_pe_gapto: + { + int next_side = which_side(B); + segment_notes notes = + gx_path_enum_notes(&cenum) & keep_notes; + + if (next_side == side && side != 0) { /* Skip a line completely outside the clip region. */ + if (open < 0) + start_skip = true; + out.x = A, out.y = B; + out_notes = notes; + if_debug3('p', "[p]skip gapto (%g,%g) side %d\n", + fixed2float(out.x), fixed2float(out.y), + side); + continue; + } + /* If we skipped any segments, put out a moveto/lineto. */ + if (side && (px != out.x || py != out.y || first_point())) { + C = out.x - px, D = out.y - py; + if (open < 0) { + first = out; + code = cmd_put_rmoveto(&writer, &C); + } else + code = cmd_put_rlineto(&writer, &C, out_notes); + if (code < 0) + return code; + px = out.x, py = out.y; + if_debug3('p', "[p]catchup %s (%g,%g) for line\n", + (open < 0 ? "moveto" : "lineto"), + fixed2float(px), fixed2float(py)); + } + if ((side = next_side) != 0) { /* Note a vertex going outside the clip region. */ + out.x = A, out.y = B; + } + C = A - px, D = B - py; + px = A, py = B; + open = 1; + code = cmd_put_rgapto(&writer, &C, notes); + } + if_debug3('p', "[p]gapto (%g,%g) side %d\n", + fixed2float(px), fixed2float(py), side); + break; case gs_pe_lineto: { int next_side = which_side(B); diff --git a/gs/base/gxclpath.h b/gs/base/gxclpath.h index 0c28f18d0..170eeaf54 100644 --- a/gs/base/gxclpath.h +++ b/gs/base/gxclpath.h @@ -134,7 +134,7 @@ typedef enum { cmd_opv_closepath = 0xef, /* (nothing) */ cmd_op_path = 0xf0, /* (see below) */ cmd_opv_fill = 0xf0, - /* cmd_opv_htfill = 0xf1, */ /* obsolete */ + cmd_opv_rgapto = 0xf1, /* dx%, dy% */ /* was cmd_opv_htfill */ /* cmd_opv_colorfill = 0xf2, */ /* obsolete */ cmd_opv_eofill = 0xf3, /* cmd_opv_hteofill = 0xf4, */ /* obsolete */ diff --git a/gs/base/gxclrast.c b/gs/base/gxclrast.c index 244cae8d0..c68a1bcad 100644 --- a/gs/base/gxclrast.c +++ b/gs/base/gxclrast.c @@ -1727,7 +1727,7 @@ idata: data_size = 0; static const byte op_num_operands[] = { cmd_segment_op_num_operands_values }; - + rgapto: if (!in_path) { ppos.x = int2fixed(state.rect.x); ppos.y = int2fixed(state.rect.y); @@ -1795,7 +1795,12 @@ idata: data_size = 0; case cmd_op_path >> 4: { gx_path fpath; - gx_path * ppath = &path; + gx_path *ppath; + + if (op == cmd_opv_rgapto) + goto rgapto; + + ppath = &path; if_debug0('L', "\n"); /* if in clip, flatten path first */ @@ -3115,6 +3120,9 @@ clist_decode_segment(gx_path * ppath, int op, fixed vs[6], case cmd_opv_rlineto: code = gx_path_add_line_notes(ppath, px += A, py += B, notes); break; + case cmd_opv_rgapto: + code = gx_path_add_gap_notes(ppath, px += A, py += B, notes); + break; case cmd_opv_hlineto: code = gx_path_add_line_notes(ppath, px += A, py, notes); break; diff --git a/gs/base/gxcpath.c b/gs/base/gxcpath.c index d0ada3762..05bbcb004 100644 --- a/gs/base/gxcpath.c +++ b/gs/base/gxcpath.c @@ -421,6 +421,10 @@ gx_cpath_to_path_synthesize(const gx_clip_path * pcpath, gx_path * ppath) code = gx_path_add_line_notes(ppath, pts[0].x, pts[0].y, gx_cpath_enum_notes(&cenum)); break; + case gs_pe_gapto: + code = gx_path_add_gap_notes(ppath, pts[0].x, pts[0].y, + gx_cpath_enum_notes(&cenum)); + break; case gs_pe_curveto: code = gx_path_add_curve_notes(ppath, pts[0].x, pts[0].y, pts[1].x, pts[1].y, diff --git a/gs/base/gxistate.h b/gs/base/gxistate.h index e1ac8a1e2..e1dc2158c 100644 --- a/gs/base/gxistate.h +++ b/gs/base/gxistate.h @@ -235,6 +235,7 @@ typedef struct gs_xstate_trans_flags { gs_memory_t *memory;\ void *client_data;\ gx_line_params line_params;\ + bool hpgl_path_mode;\ gs_matrix_fixed ctm;\ bool current_point_valid;\ gs_point current_point;\ @@ -294,7 +295,7 @@ struct gs_imager_state_s { /* Initialization for gs_imager_state */ #define gs_imager_state_initial(scale, is_gstate)\ - is_gstate, 0, 0, { gx_line_params_initial },\ + is_gstate, 0, 0, { gx_line_params_initial }, 0,\ { (float)(scale), 0.0, 0.0, (float)(-(scale)), 0.0, 0.0 },\ false, {0, 0}, {0, 0}, false, \ lop_default, gx_max_color_value, BLEND_MODE_Compatible,\ diff --git a/gs/base/gxline.h b/gs/base/gxline.h index 36072b4ef..851878f29 100644 --- a/gs/base/gxline.h +++ b/gs/base/gxline.h @@ -72,6 +72,6 @@ int gx_set_dot_length(gx_line_params *, floatp, bool); #define gx_line_params_initial\ 0.0, gs_cap_butt, gs_cap_butt, gs_cap_butt, gs_join_miter, -1,\ 10.0, (float)0.20305866, 0.0, 0/*false*/,\ - { identity_matrix_body }, { gx_dash_params_initial } + { identity_matrix_body }, { gx_dash_params_initial } #endif /* gxline_INCLUDED */ diff --git a/gs/base/gxpath.c b/gs/base/gxpath.c index b4ef51468..f7c5a853a 100644 --- a/gs/base/gxpath.c +++ b/gs/base/gxpath.c @@ -66,6 +66,7 @@ static rc_free_proc(rc_free_path_segments_local); static int gz_path_add_point(gx_path *, fixed, fixed), gz_path_add_line_notes(gx_path *, fixed, fixed, segment_notes), + gz_path_add_gap_notes(gx_path *, fixed, fixed, segment_notes), gz_path_add_curve_notes(gx_path *, fixed, fixed, fixed, fixed, fixed, fixed, segment_notes), gz_path_close_subpath_notes(gx_path *, segment_notes); static byte gz_path_state_flags(gx_path *ppath, byte flags); @@ -73,6 +74,7 @@ static byte gz_path_state_flags(gx_path *ppath, byte flags); static gx_path_procs default_path_procs = { gz_path_add_point, gz_path_add_line_notes, + gz_path_add_gap_notes, gz_path_add_curve_notes, gz_path_close_subpath_notes, gz_path_state_flags @@ -84,12 +86,14 @@ static gx_path_procs default_path_procs = { static int gz_path_bbox_add_point(gx_path *, fixed, fixed), gz_path_bbox_add_line_notes(gx_path *, fixed, fixed, segment_notes), + gz_path_bbox_add_gap_notes(gx_path *, fixed, fixed, segment_notes), gz_path_bbox_add_curve_notes(gx_path *, fixed, fixed, fixed, fixed, fixed, fixed, segment_notes), gz_path_bbox_close_subpath_notes(gx_path *, segment_notes); static gx_path_procs path_bbox_procs = { gz_path_bbox_add_point, gz_path_bbox_add_line_notes, + gz_path_bbox_add_gap_notes, gz_path_bbox_add_curve_notes, gz_path_bbox_close_subpath_notes, gz_path_state_flags @@ -588,6 +592,36 @@ gz_path_bbox_add_line_notes(gx_path * ppath, fixed x, fixed y, segment_notes not gz_path_bbox_move(ppath, x, y); return 0; } +/* Add a gap to the current path (lineto). */ +int +gx_path_add_gap_notes(gx_path * ppath, fixed x, fixed y, segment_notes notes) +{ + return ppath->procs->add_gap(ppath, x, y, notes); +} +static int +gz_path_add_gap_notes(gx_path * ppath, fixed x, fixed y, segment_notes notes) +{ + subpath *psub; + line_segment *lp; + + if (ppath->bbox_set) + check_in_bbox(ppath, x, y); + path_open(); + path_alloc_segment(lp, line_segment, &st_line, s_gap, notes, + "gx_path_add_gap"); + path_alloc_link(lp); + path_set_point(lp, x, y); + path_update_draw(ppath); + trace_segment("[P]", (segment *) lp); + return 0; +} +static int +gz_path_bbox_add_gap_notes(gx_path * ppath, fixed x, fixed y, segment_notes notes) +{ + gz_path_bbox_add(ppath, x, y); + gz_path_bbox_move(ppath, x, y); + return 0; +} /* Add multiple lines to the current path. */ /* Note that all lines have the same notes. */ @@ -1019,6 +1053,9 @@ gx_print_segment(const segment * pseg) case s_line: dprintf3(" %1.4f %1.4f lineto\t%% %s\n", px, py, out); break; + case s_gap: + dprintf3(" %1.4f %1.4f gapto\t%% %s\n", px, py, out); + break; case s_dash:{ const dash_segment *const pd = (const dash_segment *)pseg; diff --git a/gs/base/gxpath.h b/gs/base/gxpath.h index b8dedafd7..48b6328d8 100644 --- a/gs/base/gxpath.h +++ b/gs/base/gxpath.h @@ -141,6 +141,7 @@ int gx_path_new(gx_path *), gx_path_add_point(gx_path *, fixed, fixed), gx_path_add_relative_point(gx_path *, fixed, fixed), gx_path_add_line_notes(gx_path *, fixed, fixed, segment_notes), + gx_path_add_gap_notes(gx_path *, fixed, fixed, segment_notes), gx_path_add_dash_notes(gx_path * ppath, fixed x, fixed y, fixed dx, fixed dy, segment_notes notes), gx_path_add_lines_notes(gx_path *, const gs_fixed_point *, int, segment_notes), gx_path_add_rectangle(gx_path *, fixed, fixed, fixed, fixed), diff --git a/gs/base/gxpath2.c b/gs/base/gxpath2.c index 01caed385..653cc03cf 100644 --- a/gs/base/gxpath2.c +++ b/gs/base/gxpath2.c @@ -186,7 +186,7 @@ gx_subpath_is_rectangular(const subpath * pseg0, gs_fixed_rect * pbox, ) { if ((pseg4 = pseg3->next) == 0 || pseg4->type == s_start) type = prt_open; /* M, L, L, L */ - else if (pseg4->type != s_line) /* must be s_line_close */ + else if (pseg4->type != s_line && pseg4->type != s_gap) /* must be s_line_close */ type = prt_closed; /* M, L, L, L, C */ else if (pseg4->pt.x != pseg0->pt.x || pseg4->pt.y != pseg0->pt.y @@ -194,7 +194,7 @@ gx_subpath_is_rectangular(const subpath * pseg0, gs_fixed_rect * pbox, return prt_none; else if (pseg4->next == 0 || pseg4->next->type == s_start) type = prt_fake_closed; /* Mo, L, L, L, Lo */ - else if (pseg4->next->type != s_line) /* must be s_line_close */ + else if (pseg4->next->type != s_line && pseg4->next->type != s_gap) /* must be s_line_close */ type = prt_closed; /* Mo, L, L, L, Lo, C */ else return prt_none; @@ -405,6 +405,10 @@ gx_path_copy_reversed(const gx_path * ppath_old, gx_path * ppath) code = gx_path_add_line_notes(ppath, prev->pt.x, prev->pt.y, prev_notes); break; + case s_gap: + code = gx_path_add_gap_notes(ppath, + prev->pt.x, prev->pt.y, prev_notes); + break; case s_line_close: /* Skip the closing line. */ code = gx_path_add_point(ppath, prev->pt.x, @@ -504,6 +508,10 @@ gx_path_append_reversed(const gx_path * ppath_old, gx_path * ppath) code = gx_path_add_line_notes(ppath, prev->pt.x, prev->pt.y, prev_notes); break; + case s_gap: + code = gx_path_add_gap_notes(ppath, + prev->pt.x, prev->pt.y, prev_notes); + break; case s_line_close: /* Skip the closing line. */ code = gx_path_add_point(ppath, prev->pt.x, @@ -588,6 +596,9 @@ gx_path_enum_next(gs_path_enum * penum, gs_fixed_point ppts[3]) case s_line: ppts[0] = pseg->pt; return gs_pe_lineto; + case s_gap: + ppts[0] = pseg->pt; + return gs_pe_gapto; case s_line_close: ppts[0] = pseg->pt; return gs_pe_closepath; diff --git a/gs/base/gxpcopy.c b/gs/base/gxpcopy.c index fa8dc7a79..06b46eed2 100644 --- a/gs/base/gxpcopy.c +++ b/gs/base/gxpcopy.c @@ -52,6 +52,32 @@ break_line_if_long(gx_path *ppath, const segment *pseg) } return 0; } +static inline int +break_gap_if_long(gx_path *ppath, const segment *pseg) +{ + fixed x0 = ppath->position.x; + fixed y0 = ppath->position.y; + + if (gx_check_fixed_diff_overflow(pseg->pt.x, x0) || + gx_check_fixed_diff_overflow(pseg->pt.y, y0)) { + fixed x, y; + + if (gx_check_fixed_sum_overflow(pseg->pt.x, x0)) + x = (pseg->pt.x >> 1) + (x0 >> 1); + else + x = (pseg->pt.x + x0) >> 1; + if (gx_check_fixed_sum_overflow(pseg->pt.y, y0)) + y = (pseg->pt.y >> 1) + (y0 >> 1); + else + y = (pseg->pt.y + y0) >> 1; + return gx_path_add_gap_notes(ppath, x, y, pseg->notes); + /* WARNING: Stringly speaking, the next half segment must get + the sn_not_first flag. We don't bother, because that flag + has no important meaning with colinear segments. + */ + } + return 0; +} /* Copy a path, optionally flattening or monotonizing it. */ /* If the copy fails, free the new path. */ @@ -221,6 +247,14 @@ gx_path_copy_reducing(const gx_path *ppath_old, gx_path *ppath, pseg->pt.x, pseg->pt.y, pseg->notes); vd_lineto(pseg->pt.x, pseg->pt.y); break; + case s_gap: + code = break_gap_if_long(ppath, pseg); + if (code < 0) + break; + code = gx_path_add_gap_notes(ppath, + pseg->pt.x, pseg->pt.y, pseg->notes); + vd_lineto(pseg->pt.x, pseg->pt.y); + break; case s_dash: { const dash_segment *pd = (const dash_segment *)pseg; @@ -348,6 +382,7 @@ gx_path__check_curves(const gx_path * ppath, gx_path_copy_options options, fixed } break; case s_line: + case s_gap: if (gx_check_fixed_diff_overflow(pseg->pt.x, pt0.x) || gx_check_fixed_diff_overflow(pseg->pt.y, pt0.y)) return false; @@ -697,12 +732,15 @@ find_contacting_segments(const subpath *sp0, segment *sp0last, Instead it either quickly finds something, or maybe not. */ for (s0 = sp0last, count0 = 0; count0 < search_limit && s0 != (segment *)sp0; s0 = s0->prev, count0++) { s0s = s0->prev; - if (s0->type == s_line && (s0s->pt.x == s0->pt.x || - (any_abs(s0s->pt.x - s0->pt.x) == 1 && any_abs(s0s->pt.y - s0->pt.y) > min_length))) { + if ((s0->type == s_line || s0->type == s_gap) && + (s0s->pt.x == s0->pt.x || + (any_abs(s0s->pt.x - s0->pt.x) == 1 && + any_abs(s0s->pt.y - s0->pt.y) > min_length))) { for (s1 = sp1last, count1 = 0; count1 < search_limit && s1 != (segment *)sp1; s1 = s1->prev, count1++) { s1s = s1->prev; - if (s1->type == s_line && (s1s->pt.x == s1->pt.x || - (any_abs(s1s->pt.x - s1->pt.x) == 1 && any_abs(s1s->pt.y - s1->pt.y) > min_length))) { + if ((s1->type == s_line || s1->type == s_gap) && + (s1s->pt.x == s1->pt.x || + (any_abs(s1s->pt.x - s1->pt.x) == 1 && any_abs(s1s->pt.y - s1->pt.y) > min_length))) { if (s0s->pt.x == s1s->pt.x || s0->pt.x == s1->pt.x || s0->pt.x == s1s->pt.x || s0s->pt.x == s1->pt.x) { if (s0s->pt.y < s0->pt.y && s1s->pt.y > s1->pt.y) { fixed y0 = max(s0s->pt.y, s1->pt.y); diff --git a/gs/base/gxpdash.c b/gs/base/gxpdash.c index 9732990bb..6284f30c2 100644 --- a/gs/base/gxpdash.c +++ b/gs/base/gxpdash.c @@ -90,7 +90,7 @@ subpath_expand_dashes(const subpath * psub, gx_path * ppath, elt_length = dash->init_dist_left; x = x0, y = y0; pseg = (const segment *)psub; - while ((pseg = pseg->next) != 0 && pseg->type != s_start) { + while ((pseg = pseg->next) != 0 && pseg->type != s_start && pseg->type != s_gap) { fixed sx = pseg->pt.x, sy = pseg->pt.y; fixed udx = sx - x, udy = sy - y; double length, dx, dy; @@ -179,7 +179,7 @@ subpath_expand_dashes(const subpath * psub, gx_path * ppath, const segment *pseg2 = pseg->next; end_notes = 0; - while (pseg2 != 0 && pseg2->type != s_start) + while (pseg2 != 0 && pseg2->type != s_start && pseg2->type != s_gap) { if ((pseg2->pt.x != sx) || (pseg2->pt.x != sy)) { /* Non degenerate. We aren't the last one */ @@ -216,7 +216,10 @@ subpath_expand_dashes(const subpath * psub, gx_path * ppath, code = gx_path_add_point(ppath, sx, sy); notes &= ~sn_not_first; if (elt_length < fixed2float(fixed_epsilon) && - (pseg->next == 0 || pseg->next->type == s_start || elt_length == 0)) { + (pseg->next == 0 || + pseg->next->type == s_start || + pseg->next->type == s_gap || + elt_length == 0)) { /* * Ink is off, but we're within epsilon of the end * of the dash element. @@ -231,7 +234,9 @@ subpath_expand_dashes(const subpath * psub, gx_path * ppath, if (++index == count) index = 0; elt_length1 = pattern[index] * scale; - if (pseg->next == 0 || pseg->next->type == s_start) { + if (pseg->next == 0 || + pseg->next->type == s_start || + pseg->next->type == s_gap) { elt_length = elt_length1; left = 0; ink_on = true; diff --git a/gs/base/gxstroke.c b/gs/base/gxstroke.c index 86d550227..0fdcdbf8f 100644 --- a/gs/base/gxstroke.c +++ b/gs/base/gxstroke.c @@ -146,6 +146,7 @@ gx_stroke_path_expansion(const gs_imager_state * pis, const gx_path * ppath, if (!(pseg->pt.x == prev.x || pseg->pt.y == prev.y)) goto not_exact; break; + case s_gap: default: /* other/unknown segment type */ goto not_exact; } @@ -699,6 +700,10 @@ gx_stroke_path_only_aux(gx_path * ppath, gx_path * to_path, gx_device * pdev, flags = nf_all_from_arc; + /* Run through each segment in the current path, drawing each segment + * delayed by 1 - that is, when we're looking at segment n, we draw + * (or not) segment n-1. This delay allows us to always know whether + * to join or cap the line. */ while ((pseg = pseg->next) != 0 && pseg->type != s_start ) { @@ -707,7 +712,7 @@ gx_stroke_path_only_aux(gx_path * ppath, gx_path * to_path, gx_device * pdev, fixed sx, udx, sy, udy; bool is_dash_segment = false; - d1:if (pseg->type != s_dash) { + d1:if (pseg->type != s_dash && pseg->type != s_gap) { sx = pseg->pt.x; sy = pseg->pt.y; udx = sx - x; @@ -729,13 +734,13 @@ gx_stroke_path_only_aux(gx_path * ppath, gx_path * to_path, gx_device * pdev, ((pseg->notes & sn_dash_tail) ? nf_dash_tail : 0) | (flags & ~nf_all_from_arc)); pl.e.p.x = sx, pl.e.p.y = sy; - if (!(udx | udy) || pseg->type == s_dash) { /* degenerate or short */ + if (!(udx | udy) || pseg->type == s_dash || pseg->type == s_gap) { /* degenerate or short */ /* * If this is the first segment of the subpath, * check the entire subpath for degeneracy. * Otherwise, ignore the degenerate segment. */ - if (index != 0 && pseg->type != s_dash) + if (index != 0 && pseg->type != s_dash && pseg->type != s_gap) continue; /* Check for a degenerate subpath. */ while ((pseg = pseg->next) != 0 && @@ -743,7 +748,7 @@ gx_stroke_path_only_aux(gx_path * ppath, gx_path * to_path, gx_device * pdev, ) { if (is_dash_segment) break; - if (pseg->type == s_dash) + if (pseg->type == s_dash || pseg->type == s_gap) goto d1; sx = pseg->pt.x, udx = sx - x; sy = pseg->pt.y, udy = sy - y; @@ -920,9 +925,19 @@ gx_stroke_path_only_aux(gx_path * ppath, gx_path * to_path, gx_device * pdev, if (is_dash_segment) /* Never join to a dash segment */ lptr = NULL; #endif + if (pseg->type == s_gap) + { + lptr = NULL; + /* We are always drawing one line segment behind, so make + * sure we don't draw the next one. */ + index = 0; + } + ensure_closed = ((to_path == &stroke_path_body && lop_is_idempotent(pis->log_op)) || (lptr == NULL ? true : lptr->thin)); + /* Draw the PREVIOUS line segment, joining it to lptr (or + * capping if lptr == NULL. */ code = (*line_proc) (to_path, to_path_reverse, ensure_closed, first, &pl_prev, lptr, pdevc, dev, pis, params, &cbox, @@ -931,6 +946,10 @@ gx_stroke_path_only_aux(gx_path * ppath, gx_path * to_path, gx_device * pdev, if (code < 0) goto exit; FILL_STROKE_PATH(pdev, always_thin, pcpath, false); + } else if (pseg->type == s_gap) { + /* If this segment is a gap, then we don't want to draw it + * next time! */ + index = 0; } else pl_first = pl; pl_prev = pl; @@ -955,6 +974,10 @@ gx_stroke_path_only_aux(gx_path * ppath, gx_path * to_path, gx_device * pdev, if (lptr && psub->type == s_dash) lptr = NULL; #endif + /* If the subpath starts with a gap, then cap, don't join! */ + if (lptr && psub->type == s_start && psub->next && psub->next->type == s_gap) + lptr = NULL; + flags = (((notes & sn_not_first) ? ((flags & nf_all_from_arc) | nf_some_from_arc) : 0) | ((notes & sn_dash_head) ? nf_dash_head : 0) | diff --git a/gs/base/gxttfb.c b/gs/base/gxttfb.c index 418d20ca8..83d68d20f 100644 --- a/gs/base/gxttfb.c +++ b/gs/base/gxttfb.c @@ -562,6 +562,7 @@ path_to_hinter(t1_hinter *h, gx_path *path) code = t1_hinter__rmoveto(h, pts[0].x - p.x, pts[0].y - p.y); break; case gs_pe_lineto: + case gs_pe_gapto: code = t1_hinter__rlineto(h, pts[0].x - p.x, pts[0].y - p.y); break; case gs_pe_curveto: diff --git a/gs/base/gzpath.h b/gs/base/gzpath.h index 4d48f8bbd..181154b63 100644 --- a/gs/base/gzpath.h +++ b/gs/base/gzpath.h @@ -37,7 +37,8 @@ typedef enum { s_line, s_line_close, s_curve, - s_dash /* only for internal use of the stroking algorithm */ + s_dash, /* only for internal use of the stroking algorithm */ + s_gap } segment_type; /* Define the common structure for all segments. */ @@ -282,6 +283,7 @@ typedef enum { typedef struct gx_path_procs_s { int (*add_point)(gx_path *, fixed, fixed); int (*add_line)(gx_path *, fixed, fixed, segment_notes); + int (*add_gap)(gx_path *, fixed, fixed, segment_notes); int (*add_curve)(gx_path *, fixed, fixed, fixed, fixed, fixed, fixed, segment_notes); int (*close_subpath)(gx_path *, segment_notes); byte (*state_flags)(gx_path *, byte); diff --git a/gs/doc/Language.htm b/gs/doc/Language.htm index e78fd9c39..7fbaeeaa1 100644 --- a/gs/doc/Language.htm +++ b/gs/doc/Language.htm @@ -738,6 +738,24 @@ The saved path can be restored by <code>newpath { exec } forall</code>. </ul> </dl> +<dl> +<dt><code>- .sethpglpathmode</code> +<dd>If the single numerical parameter is non zero, then future path +creation operations will make an HPGL-style path. HPGL style paths +differ from postscript style paths in that moveto operations only +begin a new subpath if there is not already an open subpath. (i.e. +<code>a b moveto c d moveto e f lineto closepath</code> will draw +back to (a,b), not (c,d). HPGL paths treat these non-subpath starting +<tt>moveto</tt>s as <tt>lineto</tt> segments when filling, and as +unstroked gaps when stroking. +</dl> + +<dl> +<dt><code>.currenthpglpathmode -</code> +<dd>Leave the current hpglpathmode on the stack. The initial value +of hpglpathmode is 0. +</dl> + <h4><a name="Painting"></a>Painting operators</h4> <p> diff --git a/gs/psi/zgstate.c b/gs/psi/zgstate.c index 1c06bfd24..cee0f8ee8 100644 --- a/gs/psi/zgstate.c +++ b/gs/psi/zgstate.c @@ -513,6 +513,20 @@ zcurrenttextrenderingmode(i_ctx_t *i_ctx_p) return zcurrent_uint(i_ctx_p, gs_currenttextrenderingmode); } +/* <int> .sethpglpathmode - */ +static int +zsethpglpathmode(i_ctx_t *i_ctx_p) +{ + return zset_uint(i_ctx_p, gs_sethpglpathmode); +} + +/* - .currenthpglpathmode <int> */ +static int +zcurrenthpglpathmode(i_ctx_t *i_ctx_p) +{ + return zcurrent_uint(i_ctx_p, gs_currenthpglpathmode); +} + /* ------ Initialization procedure ------ */ /* We need to split the table because of the 16-element limit. */ @@ -555,6 +569,8 @@ const op_def zgstate2_op_defs[] = { const op_def zgstate3_op_defs[] = { {"0.settextrenderingmode", zsettextrenderingmode}, {"0.currenttextrenderingmode", zcurrenttextrenderingmode}, + {"0.sethpglpathmode", zsethpglpathmode}, + {"0.currenthpglpathmode", zcurrenthpglpathmode}, op_def_end(0) }; diff --git a/pcl/pctop.c b/pcl/pctop.c index 3cd9d3a9b..cd13659a8 100644 --- a/pcl/pctop.c +++ b/pcl/pctop.c @@ -431,6 +431,9 @@ pcl_impl_set_device( goto pisdEnd; gs_setaccuratecurves(pcli->pcs.pgs, true); /* All H-P languages want accurate curves. */ +#if 0 /* Disabled until we get it fully working */ + gs_sethpglpathmode(pcli->pcs.pgs, true); /* Use HPGL fill, rather than postscript fill. */ +#endif code = gs_setfilladjust(pcli->pcs.pgs, 0, 0); if (code < 0) |