/* * Compute the adjustment matrix for scaling and/or rotating the page * to match the medium. If the medium is completely flexible in a given * dimension (e.g., roll media in one dimension, or displays in both), * we must adjust its size in that dimension to match the request. * We recognize this by an unreasonably small medium->p.{x,y}. */ static void make_adjustment_matrix(const gs_point * request, const gs_rect * medium, gs_matrix * pmat, bool scale, int rotate) { double rx = request->x, ry = request->y; double mx = medium->q.x, my = medium->q.y; /* Rotate the request if necessary. */ if (rotate & 1) { double temp = rx; rx = ry, ry = temp; } /* If 'medium' is flexible, adjust 'mx' and 'my' towards 'rx' and 'ry', respectively. Note that 'mx' and 'my' have just acquired the largest permissible value, medium->q. */ if (medium->p.x < mx) { /* non-empty width range */ if (rx < medium->p.x) mx = medium->p.x; /* use minimum of the range */ else if (rx < mx) mx = rx; /* fits */ /* else leave mx == medium->q.x, i.e., the maximum */ } if (medium->p.y < my) { /* non-empty height range */ if (ry < medium->p.y) my = medium->p.y; /* use minimum of the range */ else if (ry < my) my = ry; /* fits */ /* else leave my == medium->q.y, i.e., the maximum */ } /* Translate to align the centers. */ gs_make_translation(mx / 2, my / 2, pmat); /* Rotate if needed. */ if (rotate) gs_matrix_rotate(pmat, 90.0 * rotate, pmat); /* Scale if needed. */ if (scale) { double xfactor = mx / rx; double yfactor = my / ry; double factor = min(xfactor, yfactor); if (factor < 1) gs_matrix_scale(pmat, factor, factor, pmat); } /* Now translate the origin back, */ /* using the original, unswapped request. */ gs_matrix_translate(pmat, -request->x / 2, -request->y / 2, pmat); }
static int vu_begin_image(px_state_t * pxs) { px_vendor_state_t *v_state = pxs->vendor_state; gs_image_t image = v_state->image; px_bitmap_params_t params; gs_point origin; int code; if (v_state->color_space == eGraySub) params.color_space = eGray; else params.color_space = eSRGB; params.width = params.dest_width = v_state->SourceWidth; params.height = params.dest_height = v_state->BlockHeight; params.depth = 8; params.indexed = false; code = px_image_color_space(&image, ¶ms, (const gs_string *)&pxs->pxgs->palette, pxs->pgs); if (code < 0) { return code; } /* Set up the image parameters. */ if (gs_currentpoint(pxs->pgs, &origin) < 0) return_error(errorCurrentCursorUndefined); image.Width = v_state->SourceWidth; image.Height = v_state->BlockHeight; { gs_matrix imat, dmat; gs_make_scaling(image.Width, image.Height, &imat); gs_make_translation(origin.x, origin.y + v_state->StartLine, &dmat); gs_matrix_scale(&dmat, image.Width, image.Height, &dmat); /* The ImageMatrix is dmat' * imat. */ gs_matrix_invert(&dmat, &dmat); gs_matrix_multiply(&dmat, &imat, &image.ImageMatrix); } image.CombineWithColor = true; image.Interpolate = pxs->interpolate; code = pl_begin_image(pxs->pgs, &image, &v_state->info); if (code < 0) return code; return 0; }
/* Initialize for writing a path using the default implementation. */ void gdev_vector_dopath_init(gdev_vector_dopath_state_t *state, gx_device_vector *vdev, gx_path_type_t type, const gs_matrix *pmat) { state->vdev = vdev; state->type = type; if (pmat) { state->scale_mat = *pmat; /* * The path element writing procedures all divide the coordinates * by the scale, so we must compensate for that here. */ gs_matrix_scale(&state->scale_mat, 1.0 / vdev->scale.x, 1.0 / vdev->scale.y, &state->scale_mat); } else { gs_make_scaling(vdev->scale.x, vdev->scale.y, &state->scale_mat); } state->first = true; }
/* * Form the transformation matrix required to render a pattern. */ void pcl_xfm_get_pat_xfm( const pcl_state_t * pcs, pcl_pattern_t * pptrn, gs_matrix * pmat ) { const pcl_xfm_state_t * pxfmst = &(pcs->xfm_state); int rot = (pcs->pat_orient - pxfmst->lp_orient) & 0x3; *pmat = pxfmst->lp2dev_mtx; pmat->tx = pcs->pat_ref_pt.x; pmat->ty = pcs->pat_ref_pt.y; /* record the referenc point used in the rendering structure */ pptrn->ref_pt = pcs->pat_ref_pt; /* rotate as necessar */ if (rot != 0) gs_matrix_multiply(&(rot_mtx[rot]), pmat, pmat); /* scale to the appropriate resolution (before print direction rotation) */ gs_matrix_scale( pmat, inch2coord(1.0 / (floatp)pptrn->ppat_data->xres), inch2coord(1.0 / (floatp)pptrn->ppat_data->yres), pmat ); /* avoid parameters that are slightly different from integers */ pmat->xx = adjust_param(pmat->xx); pmat->xy = adjust_param(pmat->xy); pmat->yx = adjust_param(pmat->yx); pmat->yy = adjust_param(pmat->yy); /* record the rotation used for rendering */ pptrn->orient = pcs->pat_orient; }
/* * Reset all parameters which must be reset whenever the page size changes. * * The third operand indicates if this routine is being called as part of * an initial reset. In that case, done't call HPGL's reset - the reset * will do that later. */ static void new_page_size( pcl_state_t * pcs, const pcl_paper_size_t * psize, bool reset_initial, bool for_passthrough ) { floatp width_pts = psize->width * 0.01; floatp height_pts = psize->height * 0.01; float page_size[2]; static float old_page_size[2] = { 0, 0 }; gs_state * pgs = pcs->pgs; gs_matrix mat; bool changed_page_size; page_size[0] = width_pts; page_size[1] = height_pts; old_page_size[0] = pcs->xfm_state.paper_size ? pcs->xfm_state.paper_size->width : 0; old_page_size[1] = pcs->xfm_state.paper_size ? pcs->xfm_state.paper_size->height : 0; put_param1_float_array(pcs, "PageSize", page_size); /* * Reset the default transformation. * * The graphic library provides a coordinate system in points, with the * origin at the lower left corner of the page. The PCL code uses a * coordinate system in centi-points, with the origin at the upper left * corner of the page. */ gs_setdefaultmatrix(pgs, NULL); gs_initmatrix(pgs); gs_currentmatrix(pgs, &mat); gs_matrix_translate(&mat, 0.0, height_pts, &mat); gs_matrix_scale(&mat, 0.01, -0.01, &mat); gs_setdefaultmatrix(pgs, &mat); pcs->xfm_state.paper_size = psize; pcs->overlay_enabled = false; update_xfm_state(pcs, reset_initial); reset_margins(pcs, for_passthrough); /* check if update_xfm_state changed the page size */ changed_page_size = !(old_page_size[0] == pcs->xfm_state.paper_size->width && old_page_size[1] == pcs->xfm_state.paper_size->height); /* * make sure underlining is disabled (homing the cursor may cause * an underline to be put out. */ pcs->underline_enabled = false; pcl_home_cursor(pcs); pcl_xfm_reset_pcl_pat_ref_pt(pcs); if (!reset_initial) hpgl_do_reset(pcs, pcl_reset_page_params); if ( pcs->end_page == pcl_end_page_top ) { /* don't erase in snippet mode */ if (pcs->page_marked || changed_page_size) { gs_erasepage(pcs->pgs); pcs->page_marked = false; } } }
/* Compute the FontDescriptor metrics for a font. */ int pdf_compute_font_descriptor(gx_device_pdf *pdev, pdf_font_descriptor_t *pfd) { gs_font_base *bfont = pdf_base_font_font(pfd->base_font, false); gs_glyph glyph, notdef; int index; int wmode = bfont->WMode; int members = (GLYPH_INFO_WIDTH0 << wmode) | GLYPH_INFO_BBOX | GLYPH_INFO_NUM_PIECES; pdf_font_descriptor_values_t desc; gs_matrix smat; gs_matrix *pmat = NULL; int fixed_width = 0; int small_descent = 0, small_height = 0; bool small_present = false; int x_height = 0; int cap_height = 0; gs_rect bbox_colon, bbox_period, bbox_I; bool is_cid = (bfont->FontType == ft_CID_encrypted || bfont->FontType == ft_CID_TrueType); bool have_colon = false, have_period = false, have_I = false; int code; memset(&bbox_colon, 0, sizeof(bbox_colon)); /* quiet gcc warnings. */ memset(&bbox_period, 0, sizeof(bbox_period)); /* quiet gcc warnings. */ memset(&bbox_I, 0, sizeof(bbox_I)); /* quiet gcc warnings. */ memset(&desc, 0, sizeof(desc)); if (is_cid && bfont->FontBBox.p.x != bfont->FontBBox.q.x && bfont->FontBBox.p.y != bfont->FontBBox.q.y) { int scale = (bfont->FontType == ft_TrueType || bfont->FontType == ft_CID_TrueType ? 1000 : 1); desc.FontBBox.p.x = (int)(bfont->FontBBox.p.x * scale); desc.FontBBox.p.y = (int)(bfont->FontBBox.p.y * scale); desc.FontBBox.q.x = (int)(bfont->FontBBox.q.x * scale); desc.FontBBox.q.y = (int)(bfont->FontBBox.q.y * scale); desc.Ascent = desc.FontBBox.q.y; members &= ~GLYPH_INFO_BBOX; } else { desc.FontBBox.p.x = desc.FontBBox.p.y = max_int; desc.FontBBox.q.x = desc.FontBBox.q.y = min_int; } /* * Embedded TrueType fonts use a 1000-unit character space, but the * font itself uses a 1-unit space. Compensate for this here. */ switch (bfont->FontType) { case ft_TrueType: case ft_CID_TrueType: gs_make_scaling(1000.0, 1000.0, &smat); pmat = &smat; /* Type 3 fonts may use a FontMatrix in PDF, so we don't * need to deal with non-standard matrices */ case ft_GL2_531: case ft_PCL_user_defined: case ft_GL2_stick_user_defined: case ft_MicroType: case ft_user_defined: break; /* Other font types may use a non-standard (not 1000x1000) design grid * The FontMatrix is used to map to the unit square. However PDF files * don't allow FontMatrix entries, all fonts are nominally 1000x1000. * If we have a font with a non-standard matrix we must account for that * here by scaling the font outline. */ default: gs_matrix_scale(&bfont->FontMatrix, 1000.0, 1000.0, &smat); pmat = &smat; break; } /* * Scan the entire glyph space to compute Ascent, Descent, FontBBox, and * the fixed width if any. For non-symbolic fonts, also note the * bounding boxes for Latin letters and a couple of other characters, * for computing the remaining descriptor values (CapHeight, * ItalicAngle, StemV, XHeight, and flags SERIF, SCRIPT, ITALIC, * ALL_CAPS, and SMALL_CAPS). (The algorithms are pretty crude.) */ notdef = GS_NO_GLYPH; for (index = 0; (bfont->procs.enumerate_glyph((gs_font *)bfont, &index, (is_cid ? GLYPH_SPACE_INDEX : GLYPH_SPACE_NAME), &glyph)) >= 0 && index != 0; ) { gs_glyph_info_t info; gs_const_string gname; gs_glyph glyph_known_enc; gs_char position=0; code = bfont->procs.glyph_info((gs_font *)bfont, glyph, pmat, members, &info); if (code == gs_error_VMerror) return code; if (code < 0) { /* * Since this function may be indirtectly called from gx_device_finalize, * we are unable to propagate error code to the interpreter. * Therefore we skip it here hoping that few errors can be * recovered by the integration through entire glyph set. */ continue; } if (members & GLYPH_INFO_BBOX) { /* rect_merge(desc.FontBBox, info.bbox); Expanding due to type cast :*/ if (info.bbox.p.x < desc.FontBBox.p.x) desc.FontBBox.p.x = (int)info.bbox.p.x; if (info.bbox.q.x > desc.FontBBox.q.x) desc.FontBBox.q.x = (int)info.bbox.q.x; if (info.bbox.p.y < desc.FontBBox.p.y) desc.FontBBox.p.y = (int)info.bbox.p.y; if (info.bbox.q.y > desc.FontBBox.q.y) desc.FontBBox.q.y = (int)info.bbox.q.y; if (!info.num_pieces) desc.Ascent = max(desc.Ascent, (int)info.bbox.q.y); } if (notdef == GS_NO_GLYPH && gs_font_glyph_is_notdef(bfont, glyph)) { notdef = glyph; desc.MissingWidth = (int)info.width[wmode].x; } if (info.width[wmode].y != 0) fixed_width = min_int; else if (fixed_width == 0) fixed_width = (int)info.width[wmode].x; else if (info.width[wmode].x != fixed_width) fixed_width = min_int; if (desc.Flags & FONT_IS_SYMBOLIC) continue; /* skip Roman-only computation */ if (is_cid) continue; code = bfont->procs.glyph_name((gs_font *)bfont, glyph, &gname); if (code < 0) { /* If we fail to get the glyph name, best assume this is a symbolic font */ desc.Flags |= FONT_IS_SYMBOLIC; continue; } /* See if the glyph name is in any of the known encodings */ glyph_known_enc = gs_c_name_glyph(gname.data, gname.size); if (glyph_known_enc == gs_no_glyph) { desc.Flags |= FONT_IS_SYMBOLIC; continue; } /* Finally check if the encoded glyph is in Standard Encoding */ /* gs_c_decode always fails to find .notdef, its always present so * don't worry about it */ if(strncmp(".notdef", (const char *)gname.data, gname.size)) { position = gs_c_decode(glyph_known_enc, 0); if (position == GS_NO_CHAR) { desc.Flags |= FONT_IS_SYMBOLIC; continue; } } switch (gname.size) { case 5: if (!memcmp(gname.data, "colon", 5)) bbox_colon = info.bbox, have_colon = true; continue; case 6: if (!memcmp(gname.data, "period", 6)) bbox_period = info.bbox, have_period = true; continue; case 1: break; default: continue; } if (gname.data[0] >= 'A' && gname.data[0] <= 'Z') { cap_height = max(cap_height, (int)info.bbox.q.y); if (gname.data[0] == 'I') bbox_I = info.bbox, have_I = true; } else if (gname.data[0] >= 'a' && gname.data[0] <= 'z') { int y0 = (int)(info.bbox.p.y), y1 = (int)(info.bbox.q.y); small_present = true; switch (gname.data[0]) { case 'b': case 'd': case 'f': case 'h': case 'k': case 'l': case 't': /* ascender */ small_height = max(small_height, y1); case 'i': /* anomalous ascent */ break; case 'j': /* descender with anomalous ascent */ small_descent = min(small_descent, y0); break; case 'g': case 'p': case 'q': case 'y': /* descender */ small_descent = min(small_descent, y0); default: /* no ascender or descender */ x_height = max(x_height, y1); } } } if (!(desc.Flags & FONT_IS_SYMBOLIC)) { desc.Flags |= FONT_IS_ADOBE_ROMAN; /* required if not symbolic */ desc.XHeight = (int)x_height; if (!small_present && (!pdev->PDFA != 0 || bfont->FontType != ft_TrueType)) desc.Flags |= FONT_IS_ALL_CAPS; desc.CapHeight = cap_height; /* * Look at various glyphs to determine ItalicAngle, StemV, * SERIF, SCRIPT, and ITALIC. */ if (have_colon && have_period) { /* Calculate the dominant angle. */ int angle = (int)(atan2((bbox_colon.q.y - bbox_colon.p.y) - (bbox_period.q.y - bbox_period.p.y), (bbox_colon.q.x - bbox_colon.p.x) - (bbox_period.q.x - bbox_period.p.x)) * radians_to_degrees) - 90; /* Normalize to [-90..90]. */ while (angle > 90) angle -= 180; while (angle < -90) angle += 180; if (angle < -30) angle = -30; else if (angle > 30) angle = 30; /* * For script or embellished fonts, we can get an angle that is * slightly off from zero even for non-italic fonts. * Compensate for this now. */ if (angle <= 2 && angle >= -2) angle = 0; desc.ItalicAngle = angle; } if (desc.ItalicAngle) desc.Flags |= FONT_IS_ITALIC; if (have_I) { double wdot = bbox_period.q.x - bbox_period.p.x; double wcolon = bbox_I.q.x - bbox_I.p.x; double wI = bbox_period.q.x - bbox_period.p.x; desc.StemV = (int)wdot; if (wI > wcolon * 2.5 || wI > (bbox_period.q.y - bbox_period.p.y) * 0.25) desc.Flags |= FONT_IS_SERIF; } } if (desc.Ascent == 0) desc.Ascent = desc.FontBBox.q.y; desc.Descent = desc.FontBBox.p.y; if (!(desc.Flags & (FONT_IS_SYMBOLIC | FONT_IS_ALL_CAPS)) && (small_descent > desc.Descent / 3 || desc.XHeight > small_height * 0.9) && (!pdev->PDFA != 0 || bfont->FontType != ft_TrueType) ) desc.Flags |= FONT_IS_SMALL_CAPS; if (fixed_width > 0 && (!pdev->PDFA != 0 || bfont->FontType != ft_TrueType)) { desc.Flags |= FONT_IS_FIXED_WIDTH; desc.AvgWidth = desc.MaxWidth = desc.MissingWidth = fixed_width; } if (desc.CapHeight == 0) desc.CapHeight = desc.Ascent; if (desc.StemV == 0) desc.StemV = (int)(desc.FontBBox.q.x * 0.15); pfd->common.values = desc; return 0; }
int xps_parse_tiling_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *root, int (*func)(xps_context_t*, char*, xps_resource_t*, xps_item_t*, void*), void *user) { xps_item_t *node; int code; char *opacity_att; char *transform_att; char *viewbox_att; char *viewport_att; char *tile_mode_att; /*char *viewbox_units_att;*/ /*char *viewport_units_att;*/ xps_item_t *transform_tag = NULL; gs_matrix transform; gs_rect viewbox; gs_rect viewport; float scalex, scaley; int tile_mode; opacity_att = xps_att(root, "Opacity"); transform_att = xps_att(root, "Transform"); viewbox_att = xps_att(root, "Viewbox"); viewport_att = xps_att(root, "Viewport"); tile_mode_att = xps_att(root, "TileMode"); /*viewbox_units_att = xps_att(root, "ViewboxUnits");*/ /*viewport_units_att = xps_att(root, "ViewportUnits");*/ for (node = xps_down(root); node; node = xps_next(node)) { if (!strcmp(xps_tag(node), "ImageBrush.Transform")) transform_tag = xps_down(node); if (!strcmp(xps_tag(node), "VisualBrush.Transform")) transform_tag = xps_down(node); } xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL); gs_make_identity(&transform); if (transform_att) xps_parse_render_transform(ctx, transform_att, &transform); if (transform_tag) xps_parse_matrix_transform(ctx, transform_tag, &transform); viewbox.p.x = 0.0; viewbox.p.y = 0.0; viewbox.q.x = 1.0; viewbox.q.y = 1.0; if (viewbox_att) xps_parse_rectangle(ctx, viewbox_att, &viewbox); viewport.p.x = 0.0; viewport.p.y = 0.0; viewport.q.x = 1.0; viewport.q.y = 1.0; if (viewport_att) xps_parse_rectangle(ctx, viewport_att, &viewport); /* some sanity checks on the viewport/viewbox size */ if (fabs(viewport.q.x - viewport.p.x) < 0.01) { gs_warn("skipping tile with zero width view port"); return 0; } if (fabs(viewport.q.y - viewport.p.y) < 0.01) { gs_warn("skipping tile with zero height view port"); return 0; } if (fabs(viewbox.q.x - viewbox.p.x) < 0.01) { gs_warn("skipping tile with zero width view box"); return 0; } if (fabs(viewbox.q.y - viewbox.p.y) < 0.01) { gs_warn("skipping tile with zero height view box"); return 0; } scalex = (viewport.q.x - viewport.p.x) / (viewbox.q.x - viewbox.p.x); scaley = (viewport.q.y - viewport.p.y) / (viewbox.q.y - viewbox.p.y); tile_mode = TILE_NONE; if (tile_mode_att) { if (!strcmp(tile_mode_att, "None")) tile_mode = TILE_NONE; if (!strcmp(tile_mode_att, "Tile")) tile_mode = TILE_TILE; if (!strcmp(tile_mode_att, "FlipX")) tile_mode = TILE_FLIP_X; if (!strcmp(tile_mode_att, "FlipY")) tile_mode = TILE_FLIP_Y; if (!strcmp(tile_mode_att, "FlipXY")) tile_mode = TILE_FLIP_X_Y; } gs_gsave(ctx->pgs); code = xps_begin_opacity(ctx, base_uri, dict, opacity_att, NULL, false, false); if (code) { gs_grestore(ctx->pgs); return gs_rethrow(code, "cannot create transparency group"); } /* TODO(tor): check viewport and tiling to see if we can set it to TILE_NONE */ if (tile_mode != TILE_NONE) { struct tile_closure_s closure; gs_client_pattern gspat; gs_client_color gscolor; gs_color_space *cs; bool sa; closure.ctx = ctx; closure.base_uri = base_uri; closure.dict = dict; closure.tag = root; closure.tile_mode = tile_mode; closure.user = user; closure.func = func; closure.viewbox.p.x = viewbox.p.x; closure.viewbox.p.y = viewbox.p.y; closure.viewbox.q.x = viewbox.q.x; closure.viewbox.q.y = viewbox.q.y; gs_pattern1_init(&gspat); uid_set_UniqueID(&gspat.uid, gs_next_ids(ctx->memory, 1)); gspat.PaintType = 1; gspat.TilingType = 2; gspat.PaintProc = xps_remap_pattern; gspat.client_data = &closure; /* We need to know if this tiling brush includes transparency. We could do a proper scan, but for now we'll be lazy and just look at the flag from scanning the page. */ gspat.uses_transparency = ctx->has_transparency; gspat.XStep = viewbox.q.x - viewbox.p.x; gspat.YStep = viewbox.q.y - viewbox.p.y; gspat.BBox.p.x = viewbox.p.x; gspat.BBox.p.y = viewbox.p.y; gspat.BBox.q.x = viewbox.q.x; gspat.BBox.q.y = viewbox.q.y; if (tile_mode == TILE_FLIP_X || tile_mode == TILE_FLIP_X_Y) { gspat.BBox.q.x += gspat.XStep; gspat.XStep *= 2; } if (tile_mode == TILE_FLIP_Y || tile_mode == TILE_FLIP_X_Y) { gspat.BBox.q.y += gspat.YStep; gspat.YStep *= 2; } gs_matrix_translate(&transform, viewport.p.x, viewport.p.y, &transform); gs_matrix_scale(&transform, scalex, scaley, &transform); gs_matrix_translate(&transform, -viewbox.p.x, -viewbox.p.y, &transform); cs = ctx->srgb; gs_setcolorspace(ctx->pgs, cs); gsicc_profile_reference(cs->cmm_icc_profile_data, 1); sa = gs_currentstrokeadjust(ctx->pgs); gs_setstrokeadjust(ctx->pgs, false); gs_makepattern(&gscolor, &gspat, &transform, ctx->pgs, NULL); gs_setpattern(ctx->pgs, &gscolor); xps_fill(ctx); gs_setstrokeadjust(ctx->pgs, sa); gsicc_profile_reference(cs->cmm_icc_profile_data, -1); /* gs_makepattern increments the pattern count stored in the color * structure. We will discard the color struct (its on the stack) * so we need to decrement the reference before we throw away * the structure. */ gs_pattern_reference(&gscolor, -1); } else { xps_clip(ctx); gs_concat(ctx->pgs, &transform); gs_translate(ctx->pgs, viewport.p.x, viewport.p.y); gs_scale(ctx->pgs, scalex, scaley); gs_translate(ctx->pgs, -viewbox.p.x, -viewbox.p.y); gs_moveto(ctx->pgs, viewbox.p.x, viewbox.p.y); gs_lineto(ctx->pgs, viewbox.p.x, viewbox.q.y); gs_lineto(ctx->pgs, viewbox.q.x, viewbox.q.y); gs_lineto(ctx->pgs, viewbox.q.x, viewbox.p.y); gs_closepath(ctx->pgs); gs_clip(ctx->pgs); gs_newpath(ctx->pgs); code = func(ctx, base_uri, dict, root, user); if (code < 0) { xps_end_opacity(ctx, base_uri, dict, opacity_att, NULL); gs_grestore(ctx->pgs); return gs_rethrow(code, "cannot draw tile"); } } xps_end_opacity(ctx, base_uri, dict, opacity_att, NULL); gs_grestore(ctx->pgs); return 0; }
/* * 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; }