/* * ESC & f <pp_enum> S * * Contrary to what is indicated in the "PCL 5 Printer Language Technical * Reference Manual", October 1992 ed., p. 6-16, pushd cursors are stored * in logical page space, not device space. */ static int push_pop_cursor(pcl_args_t * pargs, pcl_state_t * pcs) { int type = uint_arg(pargs); if ((type == 0) && (pcs->cursor_stk_size < countof(pcs->cursor_stk))) { gs_point *ppt = &(pcs->cursor_stk[pcs->cursor_stk_size++]); ppt->x = (double)pcs->cap.x; ppt->y = (double)pcs->cap.y; gs_point_transform(ppt->x, ppt->y, &(pcs->xfm_state.pd2lp_mtx), ppt); } else if ((type == 1) && (pcs->cursor_stk_size > 0)) { gs_point *ppt = &(pcs->cursor_stk[--pcs->cursor_stk_size]); gs_matrix lp2pd; pcl_invert_mtx(&(pcs->xfm_state.pd2lp_mtx), &lp2pd); gs_point_transform(ppt->x, ppt->y, &lp2pd, ppt); pcl_set_cap_x(pcs, (coord) ppt->x, false, false); pcl_set_cap_y(pcs, (coord) ppt->y - pcs->margins.top, false, false, false, false); } return 0; }
/* * End (raster) graphics mode. This may be called explicitly by either of the * end graphics mode commands (<esc>*rB or <esc>*rC), or implicitly by any * commmand which is neither legal nor ignored in graphics mode. */ int pcl_end_graphics_mode( pcl_state_t * pcs ) { gs_point cur_pt; gs_matrix dev2pd; /* close the raster; exit graphics mode */ pcl_complete_raster(pcs); pcs->raster_state.graphics_mode = false; /* get the new current point; then restore the graphic state */ gs_transform(pcs->pgs, 0.0, 0.0, &cur_pt); pcl_grestore(pcs); /* transform the new point back to "pseudo print direction" space */ pcl_invert_mtx(&(pcs->xfm_state.pd2dev_mtx), &dev2pd); gs_point_transform(cur_pt.x, cur_pt.y, &dev2pd, &cur_pt); pcl_set_cap_x(pcs, (coord)(cur_pt.x + 0.5) - adjust_pres_mode(pcs), false, false); return pcl_set_cap_y( pcs, (coord)(cur_pt.y + 0.5) - pcs->margins.top, false, false, false, false ); }
/* * Get the effective printing region in raster space */ static void get_raster_print_rect( const gs_memory_t *mem, const gs_rect * plp_print_rect, gs_rect * prst_print_rect, const gs_matrix * prst2lp ) { gs_matrix lp2rst; pcl_invert_mtx(prst2lp, &lp2rst); pcl_transform_rect(mem, plp_print_rect, prst_print_rect, &lp2rst); intersect_with_positive_quadrant(prst_print_rect); }
/* * Convert the current point and text margin set back into "pseudo print * direction" space. */ static void restore_cap_and_margins(pcl_state_t * pcs, const gs_point * pcur_pt, const gs_rect * ptext_rect) { gs_matrix lp2pd; gs_point tmp_pt; gs_rect tmp_rect; pcl_invert_mtx(&(pcs->xfm_state.pd2lp_mtx), &lp2pd); gs_point_transform(pcur_pt->x, pcur_pt->y, &lp2pd, &tmp_pt); pcs->cap.x = (coord) tmp_pt.x; pcs->cap.y = (coord) tmp_pt.y; pcl_transform_rect(pcs->memory, ptext_rect, &tmp_rect, &lp2pd); pcs->margins.left = (coord) tmp_rect.p.x; pcs->margins.top = (coord) tmp_rect.p.y; pcs->margins.right = (coord) tmp_rect.q.x; pcs->margins.length = (coord) tmp_rect.q.y - pcs->margins.top; }
/* * Update the transformations stored in the PCL state. This will also update * the device clipping region information in device and logical page space. * The text region margins are preserved. * * This routine should be called for: * * changes in the paper size * transition from page front to page back for duplexing * (this facility is not currently implemented) * change of left or top offset registration * change of logical page orientation * change of print direction * * The paper size, left/top offsets, logical page orientation, and print * direction should be set before this procedure is called. */ static void update_xfm_state( pcl_state_t * pcs, bool reset_initial ) { pcl_xfm_state_t * pxfmst = &(pcs->xfm_state); const pcl_paper_size_t * psize = pxfmst->paper_size; coord offset; gs_matrix pg2dev, pg2lp; gs_rect print_rect, dev_rect, text_rect; gs_point cur_pt; floatp loff = pxfmst->left_offset_cp; floatp toff = pxfmst->top_offset_cp; /* preserve the current point and text rectangle in logical page space */ if ( !reset_initial ) preserve_cap_and_margins(pcs, &cur_pt, &text_rect); /* get the page to device transformation */ gs_defaultmatrix(pcs->pgs, &pg2dev); /* * Get the logical to page space transformation, and the dimensions of the * logical page. * * NOT YET IMPLEMENT - if on back of a duplex page, change size of offsets * * if (duplex_back(pcs)) { * loff = -loff; * toff = -toff; * } */ pcl_make_rotation( pxfmst->lp_orient, (floatp)(psize->width), (floatp)(psize->height), &(pxfmst->lp2pg_mtx) ); pxfmst->lp2pg_mtx.tx += loff; pxfmst->lp2pg_mtx.ty += toff; if ( pcs->personality == rtl ) offset = 0; else offset = ( (pxfmst->lp_orient & 0x1) != 0 ? psize->offset_landscape : psize->offset_portrait ); /* we need an extra 1/10 inch on each side to support 80 characters vs. 78 at 10 cpi. Only apply to A4. */ if ( ( pcs->wide_a4 ) && (psize->width == 59520) && (psize->height == 84168) ) offset -= inch2coord(1.0/10.0); gs_matrix_translate( &(pxfmst->lp2pg_mtx), (floatp)offset, 0.0, &(pxfmst->lp2pg_mtx) ); if ((pxfmst->lp_orient & 0x1) != 0) { pxfmst->lp_size.x = psize->height - 2 * offset; pxfmst->lp_size.y = psize->width; } else { pxfmst->lp_size.x = psize->width - 2 * offset; pxfmst->lp_size.y = psize->height; } /* then the logical page to device transformation */ gs_matrix_multiply(&(pxfmst->lp2pg_mtx), &pg2dev, &(pxfmst->lp2dev_mtx)); pg2dev.ty = round(pg2dev.ty); pg2dev.tx = round(pg2dev.tx); pxfmst->lp2dev_mtx.tx = round(pxfmst->lp2dev_mtx.tx); pxfmst->lp2dev_mtx.ty = round(pxfmst->lp2dev_mtx.ty); /* the "pseudo page direction to logical page/device transformations */ pcl_make_rotation( pxfmst->print_dir, (floatp)pxfmst->lp_size.x, (floatp)pxfmst->lp_size.y, &(pxfmst->pd2lp_mtx) ); gs_matrix_multiply( &(pxfmst->pd2lp_mtx), &(pxfmst->lp2dev_mtx), &(pxfmst->pd2dev_mtx) ); /* calculate the print direction page size */ if ((pxfmst->print_dir) & 0x1) { pxfmst->pd_size.x = pxfmst->lp_size.y; pxfmst->pd_size.y = pxfmst->lp_size.x; } else pxfmst->pd_size = pxfmst->lp_size; { gx_device *pdev = gs_currentdevice(pcs->pgs); /* We must not set up a clipping region beyond the hardware margins of the device, but the pcl language definition requires hardware margins to be 1/6". We set all margins to the the maximum of the PCL language defined 1/6" and the actual hardware margin. If 1/6" is not available pcl will not work correctly all of the time. */ if ( pcs->personality == rtl ) { print_rect.p.x = inch2coord(pdev->HWMargins[0] / 72.0); print_rect.p.y = inch2coord(pdev->HWMargins[1]) / 72.0; print_rect.q.x = psize->width - inch2coord(pdev->HWMargins[2] / 72.0); print_rect.q.y = psize->height - inch2coord(pdev->HWMargins[3] / 72.0); } else { print_rect.p.x = max(PRINTABLE_MARGIN_CP, inch2coord(pdev->HWMargins[0] / 72.0)); print_rect.p.y = max(PRINTABLE_MARGIN_CP, inch2coord(pdev->HWMargins[1]) / 72.0); print_rect.q.x = psize->width - max(PRINTABLE_MARGIN_CP, inch2coord(pdev->HWMargins[2] / 72.0)); print_rect.q.y = psize->height - max(PRINTABLE_MARGIN_CP, inch2coord(pdev->HWMargins[3] / 72.0)); } pcl_transform_rect(pcs->memory, &print_rect, &dev_rect, &pg2dev); pxfmst->dev_print_rect.p.x = float2fixed(round(dev_rect.p.x)); pxfmst->dev_print_rect.p.y = float2fixed(round(dev_rect.p.y)); pxfmst->dev_print_rect.q.x = float2fixed(round(dev_rect.q.x)); pxfmst->dev_print_rect.q.y = float2fixed(round(dev_rect.q.y)); } pcl_invert_mtx(&(pxfmst->lp2pg_mtx), &pg2lp); pcl_transform_rect(pcs->memory, &print_rect, &(pxfmst->lp_print_rect), &pg2lp); /* restablish the current point and text region */ if ( !reset_initial ) restore_cap_and_margins(pcs, &cur_pt, &text_rect); /* * No need to worry about pat_orient or pat_ref_pt; these will always * be recalculated just prior to use. */ }
/* * Enter raster graphics mode. * * The major function of this routine is to establish the raster to device * space transformations. This is rather involved: * * 1. The first feature to be established is the orientation of raster space * relative to page space. Three state parameters are involved in * determining this orientation: the logical page orientation, the current * print direction, and the raster presentation mode. These are combined * in the following manner: * * tr = (print_direction / 90) + logical_page_orientation * * raster_rotate = (presentation_mode == 0 ? tr : tr & 0x2) * * 2. The next step is to determine the location of the origin of the raster * to page transformation. Intially this origin is set at the appropriate * corner of the logical page, based on the orientation determined above. * The origin is then shift based on the manner in which graphics mode is * entered (the mode operand): * * If entry is IMPLICIT (i.e.: via a transfer data command rather than * an enter graphics mode command), translation by the existing left * graphics margin is used, in the orientation of raster space. * * If entry is via an enter graphics mode command which specifies moving * the origin to the logical page boundary (NO_SCALE_LEFT_MARG (0) or * SCALE_LEFT_MARG (2)), action depends on whether or not horizontal * access of print direction space and of raster space are the same: * * if there are the same, the origin is left unchanged * * if they are not the same, the origin is shifted 1/6" (1200 centi- * points) in the positive horizontal raster space axis. * * The latter correction is not documented by HP, and there is no clear * reason why it should apply, but it has been verified to be the case * for all HP products testd. * * If entry is via an enter graphics mode command with specifies use * of the current point (NO_SCALE_CUR_PT(1) or SCALE_CUR_PT(3)), the * current point is transformed to raster space and its "horizontal" * component is used as the new graphics margin. * * Irrespective of how the "horizontal" component of the raster image origin * is specified, the vertical component is always derived from the current * addressable point, by converting the point to raster space. * * 3. Next, the scale of the raster to page space transformation is established. * This depends on whether or not PCL raster scaling is to be employed. * For raster scaling to be used, all of the following must hold: * * the scale_raster flag in the PCL raster state must be set * the current palette must be writable * the raster source height and width must have been explicitly set * * The scale_raster flag in the PCL raster state is normally set by the * enter raster graphics command. Hence, if graphics mode is entered * explicitly, the first requirement follows the behavior of the HP Color * LaserJet 5/5M. The DeskJet 1600C/CM behaves differently: it will never * user raster scaling if graphics mode is entered implicitly. * * The reason for the second requirement is undoubtedly related to some * backwards compatibility requirement, but is otherwise obscure. The * restriction is, however, both document and uniformly applied by all * HP products that support raster scaling. * * If raster scaling is not used, the scale of raster space is determined * by the ratio of the graphics resolution (set by the graphics resolution * command) and unit of page space (centi-points). This factor is applied * in both scan directions. * * If scaling is employed, the situation is somewhat more complicated. It * is necessary, in this case, to know which of the raster destination * dimensions have been explicitly set: * * If both dimensions are specified, the ration of these dimensions * to the source raster width and height determine the raster scale. * * If only one destination dimension is specified, the ratio of this * dimension to the corresponding source dimension determins the * raster scale for both dimensions; With strange interactions with * the 1200centipoint margin and rotated pages (Bug emulation). * * If neither dimension is specified, the page printable region is * transformed to raster space, the intersection of this with the * positive quadrant is taken. The dimensions of the resulting region * are compared with the dimensions of the source raster. The smaller * of the two dest_dim / src_dim ratios is used as the ratio for * the raster scale in both dimensions (i.e.: select the largest * isotropic scaling that does not cause clipping). * * 4. Finally, the extent of raster space must be determined. This is done by * converting the page printable region to raster space and intersecting * the result with the positive quadrant. This region is used to determine * the useable source raster width and height. * */ int pcl_enter_graphics_mode( pcl_state_t * pcs, pcl_gmode_entry_t mode ) { floatp scale_x, scale_y; pcl_xfm_state_t * pxfmst = &(pcs->xfm_state); pcl_raster_state_t * prstate = &(pcs->raster_state); float gmargin_cp = (float)prstate->gmargin_cp; gs_point cur_pt; gs_matrix rst2lp, rst2dev, lp2rst; gs_rect print_rect; uint src_wid, src_hgt; int rot; int code = 0; double dwid, dhgt; int clip_x, clip_y; /* * Check if the raster is to be clipped fully; see rtrstst.h for details. * Since this is a discontinuous effect, the equality checks below * should be made while still in centipoints. */ prstate->clip_all = ( (pcs->cap.x == pxfmst->pd_size.x) || (pcs->cap.y == pxfmst->pd_size.y) ); /* create to raster space to logical page space transformation */ rot = pxfmst->lp_orient + pxfmst->print_dir; if (prstate->pres_mode_3) rot &= 0x2; rot = (rot - pxfmst->lp_orient) & 0x3; if (prstate->y_advance == -1) rot = (rot + 2) & 0x3; pcl_make_rotation(rot, pxfmst->lp_size.x, pxfmst->lp_size.y, &rst2lp); pcl_invert_mtx(&rst2lp, &lp2rst); /* convert the current point to raster space */ cur_pt.x = (double)pcs->cap.x + adjust_pres_mode(pcs); cur_pt.y = (double)pcs->cap.y; pcl_xfm_to_logical_page_space(pcs, &cur_pt); gs_point_transform(cur_pt.x, cur_pt.y, &lp2rst, &cur_pt); /* translate the origin of the forward transformation */ if (((int)mode & 0x1) != 0) gmargin_cp = cur_pt.x; gs_matrix_translate(&rst2lp, gmargin_cp, cur_pt.y, &rst2lp); prstate->gmargin_cp = gmargin_cp; /* isotropic scaling with missing parameter is based on clipped raster dimensions */ /* transform the clipping window to raster space */ get_raster_print_rect(pcs->memory, &(pxfmst->lp_print_rect), &print_rect, &rst2lp); dwid = print_rect.q.x - print_rect.p.x; dhgt = print_rect.q.y - print_rect.p.y; clip_x = pxfmst->lp_print_rect.p.x; /* if neg then: */ clip_y = pxfmst->lp_print_rect.p.y; /* = 1200centipoints */ /* set the matrix scale */ if ( !prstate->scale_raster || !prstate->src_width_set || !prstate->src_height_set || (pcs->ppalet->pindexed->pfixed && mode == IMPLICIT) ) { scale_x = 7200.0 / (floatp)prstate->resolution; scale_y = scale_x; } else if (prstate->dest_width_set) { scale_x = (floatp)prstate->dest_width_cp / (floatp)prstate->src_width; if ( clip_x < 0 && pxfmst->lp_orient == 3 ) { scale_y = (floatp)(prstate->dest_width_cp - clip_y ) / (floatp)prstate->src_width; if ( rot == 2 && scale_y <= 2* prstate->src_width) /* empirical test 1 */ scale_y = scale_x; } else if ( clip_x < 0 && pxfmst->lp_orient == 1 && rot == 3 ) { scale_y = (floatp)(prstate->dest_width_cp - clip_y) / (floatp)prstate->src_width; if ( prstate->dest_width_cp <= 7200 ) /* empirical test 2 */ scale_y = (floatp)(prstate->dest_width_cp + clip_y) / (floatp)prstate->src_width; } else scale_y = scale_x; if (prstate->dest_height_set) scale_y = (floatp)prstate->dest_height_cp / (floatp)prstate->src_height; } else if (prstate->dest_height_set) { scale_x = scale_y = (floatp)prstate->dest_height_cp / (floatp)prstate->src_height; } else { /* select isotropic scaling with no clipping */ scale_x = (floatp)dwid / (floatp)prstate->src_width; scale_y = (floatp)dhgt / (floatp)prstate->src_height; if (scale_x > scale_y) scale_x = scale_y; else scale_y = scale_x; } gs_matrix_scale(&rst2lp, scale_x, scale_y, &rst2lp); gs_matrix_multiply(&rst2lp, &(pxfmst->lp2dev_mtx), &rst2dev); rst2dev.tx = (double)((int)(rst2dev.tx + 0.5)); rst2dev.ty = (double)((int)(rst2dev.ty + 0.5)); /* * Set up the graphic stat for rasters. This turns out to be more difficult * than might first be imagined. * * One problem is that two halftones may be needed simultaneously: * * the foreground CRD and halftone, in case the current "texture" is a * a solid color or an uncolored pattern * * the palette CRD and halftone, to be used in rendering the raster * itself * * Since the graphic state can only hold one CRD and one halftone method * at a time, this presents a bit of a problem. * * To get around the problem, an extra graphic state is necessary. Patterns * in the graphic library are given their own graphic state. Hence, by * replacing a solid color with an uncolored pattern that takes the * foreground value everywhere, the desired effect can be achieved. Code * in pcpatrn.c handles these matters. * * The second problem is a limitation in the graphic library's support of * CIE color spaces. These spaces require a joint cache, which is only * created when the color space is installed in the graphic state. However, * the current color space at the time a raster is rendered may need to * be a pattern color space, so that the proper interaction between the * raster and the texture generated by the pattern. To work around this * problem, we install the raster's color space in the current graphic * state, perform a gsave, then place what may be a patterned color space * in the new graphic state. */ pcl_set_graphics_state(pcs); pcl_set_drawing_color(pcs, pcl_pattern_raster_cspace, 0, true); pcl_gsave(pcs); pcl_set_drawing_color(pcs, pcs->pattern_type, pcs->current_pattern_id, true); gs_setmatrix(pcs->pgs, &rst2dev); /* translate the origin of the forward transformation */ /* tansform the clipping window to raster space; udpate source dimensions */ get_raster_print_rect(pcs->memory, &(pxfmst->lp_print_rect), &print_rect, &rst2lp); /* min size is 1 pixel */ src_wid = max(1, (uint)(floor(print_rect.q.x) - floor(print_rect.p.x))); src_hgt = max(1, (uint)(floor(print_rect.q.y) - floor(print_rect.p.y))); if (prstate->src_width_set && (src_wid > prstate->src_width)) src_wid = prstate->src_width; if (prstate->src_height_set && (src_hgt > prstate->src_height)) src_hgt = prstate->src_height; if (src_wid <= 0 || src_hgt <= 0) { pcl_grestore(pcs); return 1; /* hack, we want to return a non critical warning */ } /* determine (conservatively) if the region of interest has been marked */ pcs->page_marked = true; if ((code = pcl_start_raster(src_wid, src_hgt, pcs)) >= 0) prstate->graphics_mode = true; else pcl_grestore(pcs); return code; }