/* Change the output stream to a different stream. * _cairo_pdf_operators_flush() should always be called before calling * this function.
*/ void
_cairo_pdf_operators_set_stream (cairo_pdf_operators_t *pdf_operators,
cairo_output_stream_t *stream)
{
pdf_operators->stream = stream;
pdf_operators->has_line_style = FALSE;
}
/* 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;
}
/* Reset the known graphics state of the PDF consumer. ie no * assumptions will be made about the state. The next time a * particular graphics state is required (eg line width) the state * operator is always emitted and then remembered for subsequent * operations. * * This should be called when starting a new stream or after emitting * the 'Q' operator (where pdf-operators functions were called inside * the q/Q pair).
*/ void
_cairo_pdf_operators_reset (cairo_pdf_operators_t *pdf_operators)
{
pdf_operators->has_line_style = FALSE;
}
/* A word wrap stream can be used as a filter to do word wrapping on * top of an existing output stream. The word wrapping is quite * simple, using isspace to determine characters that separate * words. Any word that will cause the column count exceed the given * max_column will have a '\n' character emitted before it. * * The stream is careful to maintain integrity for words that cross * the boundary from one call to write to the next. * * Note: This stream does not guarantee that the output will never * exceed max_column. In particular, if a single word is larger than * max_column it will not be broken up.
*/
typedefstruct _word_wrap_stream {
cairo_output_stream_t base;
cairo_output_stream_t *output; int max_column;
cairo_bool_t ps_output; int column;
cairo_word_wrap_state_t state;
cairo_bool_t in_escape; int escape_digits;
} word_wrap_stream_t;
/* Emit word bytes up to the next delimiter character */ staticint
_word_wrap_stream_count_word_up_to (word_wrap_stream_t *stream, constunsignedchar *data, int length)
{ constunsignedchar *s = data; int count = 0;
while (length--) { if (_cairo_isspace (*s) || *s == '<' || *s == '(') {
stream->state = WRAP_STATE_DELIMITER; break;
}
count++;
stream->column++;
s++;
}
if (count)
_cairo_output_stream_write (stream->output, data, count);
return count;
}
/* Emit hexstring bytes up to either the end of the ASCII hexstring or the number * of columns remaining.
*/ staticint
_word_wrap_stream_count_hexstring_up_to (word_wrap_stream_t *stream, constunsignedchar *data, int length)
{ constunsignedchar *s = data; int count = 0;
cairo_bool_t newline = FALSE;
while (length--) {
count++;
stream->column++; if (*s == '>') {
stream->state = WRAP_STATE_DELIMITER; break;
}
if (count)
_cairo_output_stream_write (stream->output, data, count);
if (newline) {
_cairo_output_stream_printf (stream->output, "\n");
stream->column = 0;
}
return count;
}
/* Count up to either the end of the string or the number of columns * remaining.
*/ staticint
_word_wrap_stream_count_string_up_to (word_wrap_stream_t *stream, constunsignedchar *data, int length)
{ constunsignedchar *s = data; int count = 0;
cairo_bool_t newline = FALSE;
/* The line cap value is needed to workaround the fact that PostScript * and PDF semantics for stroking degenerate sub-paths do not match * cairo semantics. (PostScript draws something for any line cap * value, while cairo draws something only for round caps). * * When using this function to emit a path to be filled, rather than * stroked, simply pass %CAIRO_LINE_CAP_ROUND which will guarantee that * the stroke workaround will not modify the path being emitted.
*/ static cairo_status_t
_cairo_pdf_operators_emit_path (cairo_pdf_operators_t *pdf_operators, const cairo_path_fixed_t*path,
cairo_matrix_t *path_transform,
cairo_line_cap_t line_cap)
{
cairo_output_stream_t *word_wrap;
cairo_status_t status, status2;
pdf_path_info_t info;
cairo_box_t box;
word_wrap = _word_wrap_stream_create (pdf_operators->stream, pdf_operators->ps_output, 72);
status = _cairo_output_stream_get_status (word_wrap); if (unlikely (status)) return _cairo_output_stream_destroy (word_wrap);
/* PostScript has "special needs" when it comes to zero-length * dash segments with butt caps. It apparently (at least * according to ghostscript) draws hairlines for this * case. That's not what the cairo semantics want, so we first * touch up the array to eliminate any 0.0 values that will * result in "on" segments.
*/ if (num_dashes && style->line_cap == CAIRO_LINE_CAP_BUTT) { int i;
/* If there's an odd number of dash values they will each get * interpreted as both on and off. So we first explicitly * expand the array to remove the duplicate usage so that we * can modify some of the values.
*/ if (num_dashes % 2) {
dash = _cairo_malloc_abc (num_dashes, 2, sizeof (double)); if (unlikely (dash == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY);
for (i = 0; i < num_dashes; i += 2) { if (dash[i] == 0.0) { /* Do not modify the dashes in-place, as we may need to also * replay this stroke to an image fallback.
*/ if (dash == style->dash) {
dash = _cairo_malloc_ab (num_dashes, sizeof (double)); if (unlikely (dash == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY);
memcpy (dash, style->dash, num_dashes * sizeof (double));
}
/* If we're at the front of the list, we first rotate * two elements from the end of the list to the front * of the list before folding away the 0.0. Or, if * there are only two dash elements, then there is * nothing at all to draw.
*/ if (i == 0) { double last_two[2];
if (num_dashes == 2) {
free (dash); return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
/* The cases of num_dashes == 0, 1, or 3 elements * cannot exist, so the rotation of 2 elements
* will always be safe */
memcpy (last_two, dash + num_dashes - 2, sizeof (last_two));
memmove (dash + 2, dash, (num_dashes - 2) * sizeof (double));
memcpy (dash, last_two, sizeof (last_two));
dash_offset += dash[0] + dash[1];
i = 2;
}
dash[i-1] += dash[i+1];
num_dashes -= 2;
memmove (dash + i, dash + i + 2, (num_dashes - i) * sizeof (double)); /* If we might have just rotated, it's possible that * we rotated a 0.0 value to the front of the list.
* Set i to -2 so it will get incremented to 0. */ if (i == 2)
i = -2;
}
}
}
/* Scale the matrix so the largest absolute value of the non * translation components is 1.0. Return the scale required to restore * the matrix to the original values. * * eg the matrix [ 100 0 0 50 20 10 ] * * is rescaled to [ 1 0 0 0.5 0.2 0.1 ] * and the scale returned is 100
*/ staticvoid
_cairo_matrix_factor_out_scale (cairo_matrix_t *m, double *scale)
{ double s;
s = fabs (m->xx); if (fabs (m->xy) > s)
s = fabs (m->xy); if (fabs (m->yx) > s)
s = fabs (m->yx); if (fabs (m->yy) > s)
s = fabs (m->yy);
*scale = s;
s = 1.0/s;
cairo_matrix_scale (m, s, s);
}
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 correct 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;
/* Emit the string of glyphs using the 'Tj' operator. This requires
* that the glyphs are positioned at their natural glyph advances. */ static cairo_status_t
_cairo_pdf_operators_emit_glyph_string (cairo_pdf_operators_t *pdf_operators,
cairo_output_stream_t *stream)
{ int i;
/* Emit the string of glyphs using the 'TJ' operator. * * The TJ operator takes an array of strings of glyphs. Each string of * glyphs is displayed using the glyph advances of each glyph to * position the glyphs. A relative adjustment to the glyph advance may * be specified by including the adjustment between two strings. The * adjustment is in units of text space * -1000.
*/ static cairo_status_t
_cairo_pdf_operators_emit_glyph_string_with_positioning (
cairo_pdf_operators_t *pdf_operators,
cairo_output_stream_t *stream)
{ int i;
_cairo_output_stream_printf (stream, "[%s", pdf_operators->is_latin ? "(" : "<"); for (i = 0; i < pdf_operators->num_glyphs; i++) { if (pdf_operators->glyphs[i].x_position != pdf_operators->cur_x)
{ double delta = pdf_operators->glyphs[i].x_position - pdf_operators->cur_x; int rounded_delta;
delta = -1000.0*delta; /* As the delta is in 1/1000 of a unit of text space, * rounding to an integer should still provide sufficient * precision. We round the delta before adding to Tm_x so * that we keep track of the accumulated rounding error in * the PDF interpreter and compensate for it when * calculating subsequent deltas.
*/
rounded_delta = _cairo_lround (delta); if (abs(rounded_delta) < 3)
rounded_delta = 0; if (rounded_delta != 0) { if (pdf_operators->is_latin) {
_cairo_output_stream_printf (stream, ")%d(",
rounded_delta);
} else {
_cairo_output_stream_printf (stream, ">%d<",
rounded_delta);
}
}
/* Convert the rounded delta back to text * space before adding to the current text
* position. */
delta = rounded_delta/-1000.0;
pdf_operators->cur_x += delta;
}
if (pdf_operators->num_glyphs == 0) return CAIRO_STATUS_SUCCESS;
word_wrap_stream = _word_wrap_stream_create (pdf_operators->stream, pdf_operators->ps_output, 72);
status = _cairo_output_stream_get_status (word_wrap_stream); if (unlikely (status)) return _cairo_output_stream_destroy (word_wrap_stream);
/* Check if glyph advance used to position every glyph */
x = pdf_operators->cur_x; for (i = 0; i < pdf_operators->num_glyphs; i++) { if (fabs(pdf_operators->glyphs[i].x_position - x) > GLYPH_POSITION_TOLERANCE) break;
x += pdf_operators->glyphs[i].x_advance;
} if (i == pdf_operators->num_glyphs) {
status = _cairo_pdf_operators_emit_glyph_string (pdf_operators,
word_wrap_stream);
} else {
status = _cairo_pdf_operators_emit_glyph_string_with_positioning (
pdf_operators, word_wrap_stream);
}
pdf_operators->num_glyphs = 0;
pdf_operators->glyph_buf_x_pos = pdf_operators->cur_x;
status2 = _cairo_output_stream_destroy (word_wrap_stream); if (status == CAIRO_STATUS_SUCCESS)
status = status2;
/* Use 'Tm' operator to set the PDF text matrix. */ static cairo_status_t
_cairo_pdf_operators_set_text_matrix (cairo_pdf_operators_t *pdf_operators,
cairo_matrix_t *matrix)
{
cairo_matrix_t inverse;
cairo_status_t status;
/* We require the matrix to be invertable. */
inverse = *matrix;
status = cairo_matrix_invert (&inverse); if (unlikely (status)) return status;
/* Set the translation components of the PDF text matrix to x, y. The * 'Td' operator is used to transform the text matrix.
*/ static cairo_status_t
_cairo_pdf_operators_set_text_position (cairo_pdf_operators_t *pdf_operators, double x, double y)
{
cairo_matrix_t translate, inverse;
cairo_status_t status;
/* The Td operator transforms the text_matrix with: * * text_matrix' = T x text_matrix * * where T is a translation matrix with the translation components * set to the Td operands tx and ty.
*/
inverse = pdf_operators->text_matrix;
status = cairo_matrix_invert (&inverse);
assert (status == CAIRO_STATUS_SUCCESS);
pdf_operators->text_matrix.x0 = x;
pdf_operators->text_matrix.y0 = y;
cairo_matrix_multiply (&translate, &pdf_operators->text_matrix, &inverse); if (fabs(translate.x0) < TEXT_MATRIX_TOLERANCE)
translate.x0 = 0.0; if (fabs(translate.y0) < TEXT_MATRIX_TOLERANCE)
translate.y0 = 0.0;
_cairo_output_stream_printf (pdf_operators->stream, "%f %f Td\n",
translate.x0,
translate.y0);
pdf_operators->cur_x = 0;
pdf_operators->cur_y = 0;
pdf_operators->glyph_buf_x_pos = 0;
/* Select the font using the 'Tf' operator. The font size is set to 1 * as we use the 'Tm' operator to set the font scale.
*/ static cairo_status_t
_cairo_pdf_operators_set_font_subset (cairo_pdf_operators_t *pdf_operators,
cairo_scaled_font_subsets_glyph_t *subset_glyph)
{
cairo_status_t status;
if (pdf_operators->is_new_text_object ||
pdf_operators->font_id != subset_glyph->font_id ||
pdf_operators->subset_id != subset_glyph->subset_id)
{
status = _cairo_pdf_operators_flush_glyphs (pdf_operators); if (unlikely (status)) return status;
status = _cairo_pdf_operators_set_font_subset (pdf_operators, subset_glyph); if (unlikely (status)) return status;
pdf_operators->is_new_text_object = FALSE;
}
x = glyph->x;
y = glyph->y;
cairo_matrix_transform_point (&pdf_operators->cairo_to_pdftext, &x, &y);
/* The TJ operator for displaying text strings can only set * the horizontal position of the glyphs. If the y position * (in text space) changes, use the Td operator to change the * current position to the next glyph. We also use the Td * operator to move the current position if the horizontal * position changes by more than 10 (in text space * units). This is because the horizontal glyph positioning * in the TJ operator is intended for kerning and there may be * PDF consumers that do not handle very large position * adjustments in TJ.
*/ if (fabs(x - pdf_operators->glyph_buf_x_pos) > 10 ||
fabs(y - pdf_operators->cur_y) > GLYPH_POSITION_TOLERANCE)
{
status = _cairo_pdf_operators_flush_glyphs (pdf_operators); if (unlikely (status)) return status;
x = glyph->x;
y = glyph->y;
cairo_matrix_transform_point (&pdf_operators->cairo_to_pdf, &x, &y);
status = _cairo_pdf_operators_set_text_position (pdf_operators, x, y); if (unlikely (status)) return status;
x = 0.0;
y = 0.0;
}
status = _cairo_pdf_operators_add_glyph (pdf_operators,
subset_glyph,
x); return status;
}
/* A utf8_len of -1 indicates no unicode text. A utf8_len = 0 is an * empty string.
*/ static cairo_int_status_t
_cairo_pdf_operators_emit_cluster (cairo_pdf_operators_t *pdf_operators, constchar *utf8, int utf8_len,
cairo_glyph_t *glyphs, int num_glyphs,
cairo_text_cluster_flags_t cluster_flags,
cairo_scaled_font_t *scaled_font)
{
cairo_scaled_font_subsets_glyph_t subset_glyph;
cairo_glyph_t *cur_glyph;
cairo_status_t status = CAIRO_STATUS_SUCCESS; int i;
/* If the cluster maps 1 glyph to 1 or more unicode characters, we * first try _map_glyph() with the unicode string to see if it can * use toUnicode to map our glyph to the unicode. This will fail * if the glyph is already mapped to a different unicode string. * * We also go through this path if no unicode mapping was * supplied (utf8_len < 0). * * Mapping a glyph to a zero length unicode string requires the * use of ActualText.
*/ if (num_glyphs == 1 && utf8_len != 0) {
status = _cairo_scaled_font_subsets_map_glyph (pdf_operators->font_subsets,
scaled_font,
glyphs->index,
utf8,
utf8_len,
&subset_glyph); if (unlikely (status)) return status;
if (subset_glyph.utf8_is_mapped || utf8_len < 0) {
status = _cairo_pdf_operators_emit_glyph (pdf_operators,
glyphs,
&subset_glyph); if (unlikely (status)) return status;
return CAIRO_STATUS_SUCCESS;
}
}
if (pdf_operators->use_actual_text) { /* Fallback to using ActualText to map zero or more glyphs to a
* unicode string. */
status = _cairo_pdf_operators_flush_glyphs (pdf_operators); if (unlikely (status)) return status;
status = _cairo_pdf_operators_begin_actualtext (pdf_operators, utf8, utf8_len); if (unlikely (status)) return status;
}
/* XXX
* If no glyphs, we should put *something* here for the text to be selectable. */ for (i = 0; i < num_glyphs; i++) {
status = _cairo_scaled_font_subsets_map_glyph (pdf_operators->font_subsets,
scaled_font,
cur_glyph->index,
NULL, -1,
&subset_glyph); if (unlikely (status)) return status;
status = _cairo_pdf_operators_emit_glyph (pdf_operators,
cur_glyph,
&subset_glyph); if (unlikely (status)) return status;
if ((cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD))
cur_glyph--; else
cur_glyph++;
}
if (pdf_operators->use_actual_text) {
status = _cairo_pdf_operators_flush_glyphs (pdf_operators); if (unlikely (status)) return status;
status = _cairo_pdf_operators_end_actualtext (pdf_operators);
}
return status;
}
cairo_int_status_t
_cairo_pdf_operators_show_text_glyphs (cairo_pdf_operators_t *pdf_operators, constchar *utf8, int utf8_len,
cairo_glyph_t *glyphs, int num_glyphs, const cairo_text_cluster_t *clusters, int num_clusters,
cairo_text_cluster_flags_t cluster_flags,
cairo_scaled_font_t *scaled_font)
{
cairo_status_t status; int i;
cairo_matrix_t text_matrix, invert_y_axis; double x, y; constchar *cur_text;
cairo_glyph_t *cur_glyph;
pdf_operators->font_matrix_inverse = scaled_font->font_matrix;
status = cairo_matrix_invert (&pdf_operators->font_matrix_inverse); if (status == CAIRO_STATUS_INVALID_MATRIX) return CAIRO_STATUS_SUCCESS;
assert (status == CAIRO_STATUS_SUCCESS);
pdf_operators->is_new_text_object = FALSE; if (pdf_operators->in_text_object == FALSE) {
status = _cairo_pdf_operators_begin_text (pdf_operators); if (unlikely (status)) return status;
/* Force Tm and Tf to be emitted when starting a new text
* object.*/
pdf_operators->is_new_text_object = TRUE;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.