/* Finish writing out any pending commands to the stream. This
 * function must be called by the surface before emitting anything
 * into the PDF stream.
 *
 * pdf_operators may leave the emitted PDF for some operations
 * unfinished in case subsequent operations can be merged. This
 * function will finish off any incomplete operation so the stream
 * will be in a state where the surface may emit its own PDF
 * operations (eg changing patterns).
 *
 */
cairo_status_t
_cairo_pdf_operators_flush (cairo_pdf_operators_t	 *pdf_operators)
{
    cairo_status_t status = CAIRO_STATUS_SUCCESS;

    if (pdf_operators->in_text_object)
	status = _cairo_pdf_operators_end_text (pdf_operators);

    return status;
}
cairo_int_status_t
_cairo_pdf_operators_tag_end (cairo_pdf_operators_t *pdf_operators)
{
    cairo_status_t status;

    if (pdf_operators->in_text_object) {
	status = _cairo_pdf_operators_end_text (pdf_operators);
	if (unlikely (status))
	    return status;
    }

    _cairo_output_stream_printf (pdf_operators->stream, "EMC\n");

    return _cairo_output_stream_get_status (pdf_operators->stream);
}
cairo_int_status_t
_cairo_pdf_operators_clip (cairo_pdf_operators_t	*pdf_operators,
			   const cairo_path_fixed_t	*path,
			   cairo_fill_rule_t		 fill_rule)
{
    const char *pdf_operator;
    cairo_status_t status;

    if (pdf_operators->in_text_object) {
	status = _cairo_pdf_operators_end_text (pdf_operators);
	if (unlikely (status))
	    return status;
    }

    if (! path->has_current_point) {
	/* construct an empty path */
	_cairo_output_stream_printf (pdf_operators->stream, "0 0 m ");
    } else {
	status = _cairo_pdf_operators_emit_path (pdf_operators,
						 path,
						 &pdf_operators->cairo_to_pdf,
						 CAIRO_LINE_CAP_ROUND);
	if (unlikely (status))
	    return status;
    }

    switch (fill_rule) {
    default:
	ASSERT_NOT_REACHED;
    case CAIRO_FILL_RULE_WINDING:
	pdf_operator = "W";
	break;
    case CAIRO_FILL_RULE_EVEN_ODD:
	pdf_operator = "W*";
	break;
    }

    _cairo_output_stream_printf (pdf_operators->stream,
				 "%s n\n",
				 pdf_operator);

    return _cairo_output_stream_get_status (pdf_operators->stream);
}
cairo_int_status_t
_cairo_pdf_operators_tag_begin (cairo_pdf_operators_t *pdf_operators,
				const char            *tag_name,
				int                    mcid)
{
    cairo_status_t status;

    if (pdf_operators->in_text_object) {
	status = _cairo_pdf_operators_end_text (pdf_operators);
	if (unlikely (status))
	    return status;
    }

    _cairo_output_stream_printf (pdf_operators->stream,
				 "/%s << /MCID %d >> BDC\n",
				 tag_name,
				 mcid);

    return _cairo_output_stream_get_status (pdf_operators->stream);
}
cairo_int_status_t
_cairo_pdf_operators_fill (cairo_pdf_operators_t	*pdf_operators,
			   const cairo_path_fixed_t	*path,
			   cairo_fill_rule_t		fill_rule)
{
    const char *pdf_operator;
    cairo_status_t status;

    if (pdf_operators->in_text_object) {
	status = _cairo_pdf_operators_end_text (pdf_operators);
	if (unlikely (status))
	    return status;
    }

    status = _cairo_pdf_operators_emit_path (pdf_operators,
					     path,
					     &pdf_operators->cairo_to_pdf,
					     CAIRO_LINE_CAP_ROUND);
    if (unlikely (status))
	return status;

    switch (fill_rule) {
    default:
	ASSERT_NOT_REACHED;
    case CAIRO_FILL_RULE_WINDING:
	pdf_operator = "f";
	break;
    case CAIRO_FILL_RULE_EVEN_ODD:
	pdf_operator = "f*";
	break;
    }

    _cairo_output_stream_printf (pdf_operators->stream,
				 "%s\n",
				 pdf_operator);

    return _cairo_output_stream_get_status (pdf_operators->stream);
}
static cairo_int_status_t
_cairo_pdf_operators_emit_stroke (cairo_pdf_operators_t		*pdf_operators,
				  const cairo_path_fixed_t	*path,
				  const cairo_stroke_style_t	*style,
				  const cairo_matrix_t		*ctm,
				  const cairo_matrix_t		*ctm_inverse,
				  const char			*pdf_operator)
{
    cairo_int_status_t status;
    cairo_matrix_t m, path_transform;
    cairo_bool_t has_ctm = TRUE;
    double scale = 1.0;

    if (pdf_operators->in_text_object) {
	status = _cairo_pdf_operators_end_text (pdf_operators);
	if (unlikely (status))
	    return status;
    }

    /* Optimize away the stroke ctm when it does not affect the
     * stroke. There are other ctm cases that could be optimized
     * however this is the most common.
     */
    if (fabs(ctm->xx) == 1.0 && fabs(ctm->yy) == 1.0 &&
	fabs(ctm->xy) == 0.0 && fabs(ctm->yx) == 0.0)
    {
	has_ctm = FALSE;
    }

    /* The PDF CTM is transformed to the user space CTM when stroking
     * so the corect pen shape will be used. This also requires that
     * the path be transformed to user space when emitted. The
     * conversion of path coordinates to user space may cause rounding
     * errors. For example the device space point (1.234, 3.142) when
     * transformed to a user space CTM of [100 0 0 100 0 0] will be
     * emitted as (0.012, 0.031).
     *
     * To avoid the rounding problem we scale the user space CTM
     * matrix so that all the non translation components of the matrix
     * are <= 1. The line width and and dashes are scaled by the
     * inverse of the scale applied to the CTM. This maintains the
     * shape of the stroke pen while keeping the user space CTM within
     * the range that maximizes the precision of the emitted path.
     */
    if (has_ctm) {
	m = *ctm;
	/* Zero out the translation since it does not affect the pen
	 * shape however it may cause unnecessary digits to be emitted.
	 */
	m.x0 = 0.0;
	m.y0 = 0.0;
	_cairo_matrix_factor_out_scale (&m, &scale);
	path_transform = m;
	status = cairo_matrix_invert (&path_transform);
	if (unlikely (status))
	    return status;

	cairo_matrix_multiply (&m, &m, &pdf_operators->cairo_to_pdf);
    }

    status = _cairo_pdf_operators_emit_stroke_style (pdf_operators, style, scale);
    if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
	return CAIRO_STATUS_SUCCESS;
    if (unlikely (status))
	return status;

    if (has_ctm) {
	_cairo_output_stream_printf (pdf_operators->stream,
				     "q %f %f %f %f %f %f cm\n",
				     m.xx, m.yx, m.xy, m.yy,
				     m.x0, m.y0);
    } else {
	path_transform = pdf_operators->cairo_to_pdf;
    }

    status = _cairo_pdf_operators_emit_path (pdf_operators,
					     path,
					     &path_transform,
					     style->line_cap);
    if (unlikely (status))
	return status;

    _cairo_output_stream_printf (pdf_operators->stream, "%s", pdf_operator);
    if (has_ctm)
	_cairo_output_stream_printf (pdf_operators->stream, " Q");

    _cairo_output_stream_printf (pdf_operators->stream, "\n");

    return _cairo_output_stream_get_status (pdf_operators->stream);
}