/* Return gs_error_undefinedresult if the matrix is not invertible. */ int gs_point_transform_inverse(floatp x, floatp y, const gs_matrix * pmat, gs_point * ppt) { if (is_xxyy(pmat)) { if (is_fzero(pmat->xx) || is_fzero(pmat->yy)) return_error(gs_error_undefinedresult); ppt->x = (x - pmat->tx) / pmat->xx; ppt->y = (y - pmat->ty) / pmat->yy; return 0; } else if (is_xyyx(pmat)) { if (is_fzero(pmat->xy) || is_fzero(pmat->yx)) return_error(gs_error_undefinedresult); ppt->x = (y - pmat->ty) / pmat->xy; ppt->y = (x - pmat->tx) / pmat->yx; return 0; } else { /* There are faster ways to do this, */ /* but we won't implement one unless we have to. */ gs_matrix imat; int code = gs_matrix_invert(pmat, &imat); if (code < 0) return code; return gs_point_transform(x, y, &imat, ppt); } }
/* * Compute the scale for transforming the line width and dash pattern for a * stroke operation, and, if necessary to handle anisotropic scaling, a full * transformation matrix to be inverse-applied to the path elements as well. * Return 0 if only scaling, 1 if a full matrix is needed. */ int gdev_vector_stroke_scaling(const gx_device_vector *vdev, const gs_imager_state *pis, double *pscale, gs_matrix *pmat) { bool set_ctm = true; double scale = 1; /* * If the CTM is not uniform, stroke width depends on angle. * We'd like to avoid resetting the CTM, so we check for uniform * CTMs explicitly. Note that in PDF, unlike PostScript, it is * the CTM at the time of the stroke operation, not the CTM at * the time the path was constructed, that is used for transforming * the points of the path; so if we have to reset the CTM, we must * do it before constructing the path, and inverse-transform all * the coordinates. */ if (is_xxyy(&pis->ctm)) { scale = fabs(pis->ctm.xx); set_ctm = fabs(pis->ctm.yy) != scale; } else if (is_xyyx(&pis->ctm)) { scale = fabs(pis->ctm.xy); set_ctm = fabs(pis->ctm.yx) != scale; } else if ((pis->ctm.xx == pis->ctm.yy && pis->ctm.xy == -pis->ctm.yx) || (pis->ctm.xx == -pis->ctm.yy && pis->ctm.xy == pis->ctm.yx) ) { scale = hypot(pis->ctm.xx, pis->ctm.xy); set_ctm = false; } if (set_ctm) { /* * Adobe Acrobat Reader has limitations on the maximum user * coordinate value. If we scale the matrix down too far, the * coordinates will get too big: limit the scale factor to prevent * this from happening. (This does no harm for other output * formats.) */ double mxx = pis->ctm.xx / vdev->scale.x, mxy = pis->ctm.xy / vdev->scale.y, myx = pis->ctm.yx / vdev->scale.x, myy = pis->ctm.yy / vdev->scale.y; scale = 0.5 * (fabs(mxx) + fabs(mxy) + fabs(myx) + fabs(myy)); pmat->xx = mxx / scale, pmat->xy = mxy / scale; pmat->yx = myx / scale, pmat->yy = myy / scale; pmat->tx = pmat->ty = 0; } *pscale = scale; return (int)set_ctm; }
/* Return gs_error_undefinedresult if the matrix is not invertible. */ int gs_distance_transform_inverse(floatp dx, floatp dy, const gs_matrix * pmat, gs_point * pdpt) { if (is_xxyy(pmat)) { if (is_fzero(pmat->xx) || is_fzero(pmat->yy)) return_error(gs_error_undefinedresult); pdpt->x = dx / pmat->xx; pdpt->y = dy / pmat->yy; } else if (is_xyyx(pmat)) { if (is_fzero(pmat->xy) || is_fzero(pmat->yx)) return_error(gs_error_undefinedresult); pdpt->x = dy / pmat->xy; pdpt->y = dx / pmat->yx; } else { double det = pmat->xx * pmat->yy - pmat->xy * pmat->yx; if (det == 0) return_error(gs_error_undefinedresult); pdpt->x = (dx * pmat->yy - dy * pmat->yx) / det; pdpt->y = (dy * pmat->xx - dx * pmat->xy) / det; } return 0; }
int gdev_vector_dopath(gx_device_vector *vdev, const gx_path * ppath, gx_path_type_t type, const gs_matrix *pmat) { bool do_close = (type & (gx_path_type_stroke | gx_path_type_always_close)) != 0; gs_fixed_rect rbox; gx_path_rectangular_type rtype = gx_path_is_rectangular(ppath, &rbox); gs_path_enum cenum; gdev_vector_dopath_state_t state; gs_fixed_point line_start, line_end; bool incomplete_line = false; bool need_moveto = false; int code; gdev_vector_dopath_init(&state, vdev, type, pmat); /* * if the path type is stroke, we only recognize closed * rectangles; otherwise, we recognize all rectangles. * Note that for stroking with a transformation, we can't use dorect, * which requires (untransformed) device coordinates. */ if (rtype != prt_none && (!(type & gx_path_type_stroke) || rtype == prt_closed) && (pmat == 0 || is_xxyy(pmat) || is_xyyx(pmat)) && (state.scale_mat.xx == 1.0 && state.scale_mat.yy == 1.0 && is_xxyy(&state.scale_mat) && is_fzero2(state.scale_mat.tx, state.scale_mat.ty)) ) { gs_point p, q; gs_point_transform_inverse((double)rbox.p.x, (double)rbox.p.y, &state.scale_mat, &p); gs_point_transform_inverse((double)rbox.q.x, (double)rbox.q.y, &state.scale_mat, &q); code = vdev_proc(vdev, dorect)(vdev, (fixed)p.x, (fixed)p.y, (fixed)q.x, (fixed)q.y, type); if (code >= 0) return code; /* If the dorect proc failed, use a general path. */ } code = vdev_proc(vdev, beginpath)(vdev, type); if (code < 0) return code; gx_path_enum_init(&cenum, ppath); for (;;) { gs_fixed_point vs[3]; int pe_op = gx_path_enum_next(&cenum, vs); 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]; incomplete_line = true; continue; } /* * Merge collinear horizontal or vertical line segments * going in the same direction. */ if (vs[0].x == line_end.x) { if (vs[0].x == line_start.x && coord_between(line_start.y, line_end.y, vs[0].y) ) { line_end.y = vs[0].y; continue; } } else if (vs[0].y == line_end.y) { if (vs[0].y == line_start.y && coord_between(line_start.x, line_end.x, vs[0].x) ) { line_end.x = vs[0].x; continue; } } } if (incomplete_line) { if (need_moveto) { /* see gs_pe_moveto case */ code = gdev_vector_dopath_segment(&state, gs_pe_moveto, &line_start); if (code < 0) return code; need_moveto = false; } code = gdev_vector_dopath_segment(&state, gs_pe_lineto, &line_end); if (code < 0) return code; line_start = line_end; incomplete_line = false; goto opt; } } switch (pe_op) { case 0: /* done */ done: code = vdev_proc(vdev, endpath)(vdev, type); return (code < 0 ? code : 0); case gs_pe_curveto: if (need_moveto) { /* see gs_pe_moveto case */ code = gdev_vector_dopath_segment(&state, gs_pe_moveto, &line_start); if (code < 0) return code; need_moveto = false; } line_start = vs[2]; goto draw; case gs_pe_moveto: /* * A bug in Acrobat Reader 4 causes it to draw a single pixel * for a fill with an isolated moveto. If we're doing a fill * without a stroke, defer emitting a moveto until we know that * the subpath has more elements. */ line_start = vs[0]; if (!(type & gx_path_type_stroke) && (type & gx_path_type_fill)) { need_moveto = true; continue; } 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); if (code < 0) return code; need_moveto = false; } line_start = vs[0]; goto draw; case gs_pe_closepath: if (need_moveto) { /* see gs_pe_moveto case */ need_moveto = false; continue; } if (!do_close) { pe_op = gx_path_enum_next(&cenum, vs); if (pe_op == 0) goto done; code = gdev_vector_dopath_segment(&state, gs_pe_closepath, vs); if (code < 0) return code; goto sw; } /* falls through */ draw: code = gdev_vector_dopath_segment(&state, pe_op, vs); if (code < 0) return code; } incomplete_line = false; /* only needed if optimizing */ } }
static int pdf_pattern(gx_device_pdf *pdev, const gx_drawing_color *pdc, const gx_color_tile *p_tile, const gx_color_tile *m_tile, cos_stream_t *pcs_image, pdf_resource_t **ppres) { pdf_resource_t *pres; int code = pdf_alloc_resource(pdev, resourcePattern, pdc->mask.id, ppres, 0L); cos_stream_t *pcos; cos_dict_t *pcd; cos_dict_t *pcd_Resources = cos_dict_alloc(pdev, "pdf_pattern(Resources)"); const gx_color_tile *tile = (p_tile ? p_tile : m_tile); const gx_strip_bitmap *btile = (p_tile ? &p_tile->tbits : &m_tile->tmask); bool mask = p_tile == 0; gs_point step; gs_matrix smat; if (code < 0) return code; if (!tile_size_ok(pdev, p_tile, m_tile)) return_error(gs_error_limitcheck); /* * We currently can't handle Patterns whose X/Y step isn't parallel * to the coordinate axes. */ if (is_xxyy(&tile->step_matrix)) step.x = tile->step_matrix.xx, step.y = tile->step_matrix.yy; else if (is_xyyx(&tile->step_matrix)) step.x = tile->step_matrix.yx, step.y = tile->step_matrix.xy; else return_error(gs_error_rangecheck); if (pcd_Resources == 0) return_error(gs_error_VMerror); gs_make_identity(&smat); smat.xx = btile->rep_width / (pdev->HWResolution[0] / 72.0); smat.yy = btile->rep_height / (pdev->HWResolution[1] / 72.0); smat.tx = tile->step_matrix.tx / (pdev->HWResolution[0] / 72.0); smat.ty = tile->step_matrix.ty / (pdev->HWResolution[1] / 72.0); pres = *ppres; { cos_dict_t *pcd_XObject = cos_dict_alloc(pdev, "pdf_pattern(XObject)"); char key[MAX_REF_CHARS + 3]; cos_value_t v; if (pcd_XObject == 0) return_error(gs_error_VMerror); sprintf(key, "/R%ld", pcs_image->id); COS_OBJECT_VALUE(&v, pcs_image); if ((code = cos_dict_put(pcd_XObject, (byte *)key, strlen(key), &v)) < 0 || (code = cos_dict_put_c_key_object(pcd_Resources, "/XObject", COS_OBJECT(pcd_XObject))) < 0 ) return code; } if ((code = cos_dict_put_c_strings(pcd_Resources, "/ProcSet", (mask ? "[/PDF/ImageB]" : "[/PDF/ImageC]"))) < 0) return code; cos_become(pres->object, cos_type_stream); pcos = (cos_stream_t *)pres->object; pcd = cos_stream_dict(pcos); if ((code = cos_dict_put_c_key_int(pcd, "/PatternType", 1)) < 0 || (code = cos_dict_put_c_key_int(pcd, "/PaintType", (mask ? 2 : 1))) < 0 || (code = cos_dict_put_c_key_int(pcd, "/TilingType", tile->tiling_type)) < 0 || (code = cos_dict_put_c_key_object(pcd, "/Resources", COS_OBJECT(pcd_Resources))) < 0 || (code = cos_dict_put_c_strings(pcd, "/BBox", "[0 0 1 1]")) < 0 || (code = cos_dict_put_matrix(pcd, "/Matrix", &smat)) < 0 || (code = cos_dict_put_c_key_real(pcd, "/XStep", step.x / btile->rep_width)) < 0 || (code = cos_dict_put_c_key_real(pcd, "/YStep", step.y / btile->rep_height)) < 0 ) { return code; } { char buf[MAX_REF_CHARS + 6 + 1]; /* +6 for /R# Do\n */ sprintf(buf, "/R%ld Do\n", pcs_image->id); cos_stream_add_bytes(pcos, (const byte *)buf, strlen(buf)); } return 0; }