diff options
-rw-r--r-- | src/cairo-pdf-operators-private.h | 16 | ||||
-rw-r--r-- | src/cairo-pdf-operators.c | 293 | ||||
-rw-r--r-- | src/cairo-pdf-surface.c | 20 | ||||
-rw-r--r-- | src/cairo-ps-surface.c | 15 |
4 files changed, 264 insertions, 80 deletions
diff --git a/src/cairo-pdf-operators-private.h b/src/cairo-pdf-operators-private.h index e29834b09..b48233513 100644 --- a/src/cairo-pdf-operators-private.h +++ b/src/cairo-pdf-operators-private.h @@ -71,10 +71,12 @@ typedef struct _cairo_pdf_operators { cairo_bool_t in_text_object; /* inside BT/ET pair */ /* PDF text state */ + cairo_bool_t is_new_text_object; /* text object started but matrix and font not yet selected */ unsigned int font_id; unsigned int subset_id; cairo_matrix_t text_matrix; /* PDF text matrix (Tlm in the PDF reference) */ cairo_matrix_t cairo_to_pdftext; /* translate cairo coords to PDF text space */ + cairo_matrix_t font_matrix_inverse; double cur_x; /* Current position in PDF text space (Tm in the PDF reference) */ double cur_y; int hex_width; @@ -134,9 +136,15 @@ _cairo_pdf_operators_fill_stroke (cairo_pdf_operators_t *pdf_operators, cairo_matrix_t *ctm_inverse); cairo_private cairo_int_status_t -_cairo_pdf_operators_show_glyphs (cairo_pdf_operators_t *pdf_operators, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font); +_cairo_pdf_operators_show_text_glyphs (cairo_pdf_operators_t *pdf_operators, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_bool_t backward, + cairo_scaled_font_t *scaled_font); + #endif /* CAIRO_PDF_OPERATORS_H */ diff --git a/src/cairo-pdf-operators.c b/src/cairo-pdf-operators.c index af8eafeb2..ad62cfa59 100644 --- a/src/cairo-pdf-operators.c +++ b/src/cairo-pdf-operators.c @@ -931,7 +931,6 @@ _cairo_pdf_operators_flush_glyphs (cairo_pdf_operators_t *pdf_operators) static cairo_status_t _cairo_pdf_operators_add_glyph (cairo_pdf_operators_t *pdf_operators, cairo_scaled_font_subsets_glyph_t *glyph, - cairo_matrix_t *font_matrix_inverse, double x_position) { double x, y; @@ -939,7 +938,7 @@ _cairo_pdf_operators_add_glyph (cairo_pdf_operators_t *pdf_operators x = glyph->x_advance; y = glyph->y_advance; if (glyph->is_scaled) - cairo_matrix_transform_distance (font_matrix_inverse, &x, &y); + cairo_matrix_transform_distance (&pdf_operators->font_matrix_inverse, &x, &y); pdf_operators->glyphs[pdf_operators->num_glyphs].x_position = x_position; pdf_operators->glyphs[pdf_operators->num_glyphs].glyph_index = glyph->subset_glyph_index; @@ -1073,7 +1072,12 @@ _cairo_pdf_operators_begin_text (cairo_pdf_operators_t *pdf_operators) static cairo_status_t _cairo_pdf_operators_end_text (cairo_pdf_operators_t *pdf_operators) { - _cairo_pdf_operators_flush_glyphs (pdf_operators); + cairo_status_t status; + + status = _cairo_pdf_operators_flush_glyphs (pdf_operators); + if (status) + return status; + _cairo_output_stream_printf (pdf_operators->stream, "ET\n"); pdf_operators->in_text_object = FALSE; @@ -1092,35 +1096,215 @@ _cairo_matrix_scale_equal (cairo_matrix_t *a, cairo_matrix_t *b) a->yy == b->yy); } -cairo_int_status_t -_cairo_pdf_operators_show_glyphs (cairo_pdf_operators_t *pdf_operators, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font) +static cairo_status_t +_cairo_pdf_operators_begin_actualtext (cairo_pdf_operators_t *pdf_operators, + const char *utf8, + int utf8_len) { - cairo_scaled_font_subsets_glyph_t subset_glyph; + uint16_t *utf16; + int utf16_len; cairo_status_t status; int i; - cairo_matrix_t text_matrix, invert_y_axis, font_matrix_inverse; + + _cairo_output_stream_printf (pdf_operators->stream, "/Span << /ActualText <feff"); + if (utf8_len) { + status = _cairo_utf8_to_utf16 (utf8, utf8_len, &utf16, &utf16_len); + if (status) + return status; + + for (i = 0; i < utf16_len; i++) { + _cairo_output_stream_printf (pdf_operators->stream, + "%04x", (int) (utf16[i])); + } + free (utf16); + } + _cairo_output_stream_printf (pdf_operators->stream, "> >> BDC\n"); + + return _cairo_output_stream_get_status (pdf_operators->stream); +} + +static cairo_status_t +_cairo_pdf_operators_end_actualtext (cairo_pdf_operators_t *pdf_operators) +{ + _cairo_output_stream_printf (pdf_operators->stream, "EMC\n"); + + return _cairo_output_stream_get_status (pdf_operators->stream); +} + +static cairo_status_t +_cairo_pdf_operators_emit_glyph (cairo_pdf_operators_t *pdf_operators, + cairo_glyph_t *glyph, + cairo_scaled_font_subsets_glyph_t *subset_glyph) +{ double x, y; - cairo_bool_t new_text_object = FALSE; + cairo_status_t status; - if (num_glyphs <= 0) - return CAIRO_STATUS_SUCCESS; + 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 (status) + return status; + + status = _cairo_pdf_operators_set_font_subset (pdf_operators, subset_glyph); + if (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 becauses 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->cur_x) > 10 || + fabs(y - pdf_operators->cur_y) > GLYPH_POSITION_TOLERANCE) + { + status = _cairo_pdf_operators_flush_glyphs (pdf_operators); + if (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 (status) + return status; - font_matrix_inverse = scaled_font->font_matrix; - status = cairo_matrix_invert (&font_matrix_inverse); + 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, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_bool_t backward, + cairo_scaled_font_t *scaled_font) +{ + cairo_scaled_font_subsets_glyph_t subset_glyph; + cairo_glyph_t *cur_glyph; + cairo_status_t status; + 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 < 0 ? 0 : utf8_len, + &subset_glyph); + if (status) + return status; + + if (subset_glyph.utf8_is_mapped || utf8_len < 0) { + status = _cairo_pdf_operators_emit_glyph (pdf_operators, + glyphs, + &subset_glyph); + return 0; + } + } + + /* Fallback to using ActualText to map zero or more glyphs to a + * unicode string. */ + _cairo_pdf_operators_flush_glyphs (pdf_operators); + status = _cairo_pdf_operators_begin_actualtext (pdf_operators, utf8, utf8_len); + cur_glyph = glyphs; + for (i = 0; i < num_glyphs; i++) { + status = _cairo_scaled_font_subsets_map_glyph (pdf_operators->font_subsets, + scaled_font, + cur_glyph->index, + NULL, 0, + &subset_glyph); + if (status) + return status; + + status = _cairo_pdf_operators_emit_glyph (pdf_operators, + cur_glyph, + &subset_glyph); + if (status) + return status; + + if (backward) + cur_glyph--; + else + cur_glyph++; + } + status = _cairo_pdf_operators_flush_glyphs (pdf_operators); + if (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, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_bool_t backward, + cairo_scaled_font_t *scaled_font) +{ + cairo_status_t status; + int i; + cairo_matrix_t text_matrix, invert_y_axis; + double x, y; + const char *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; if (status) return status; + pdf_operators->is_new_text_object = FALSE; if (pdf_operators->in_text_object == FALSE) { _cairo_pdf_operators_begin_text (pdf_operators); /* Force Tm and Tf to be emitted when starting a new text * object.*/ - new_text_object = TRUE; + pdf_operators->is_new_text_object = TRUE; } cairo_matrix_init_scale (&invert_y_axis, 1, -1); @@ -1132,7 +1316,7 @@ _cairo_pdf_operators_show_glyphs (cairo_pdf_operators_t *pdf_operators, /* Invert y axis in device space */ cairo_matrix_multiply (&text_matrix, &invert_y_axis, &text_matrix); - if (new_text_object || + if (pdf_operators->is_new_text_object || ! _cairo_matrix_scale_equal (&pdf_operators->text_matrix, &text_matrix)) { _cairo_pdf_operators_flush_glyphs (pdf_operators); @@ -1148,55 +1332,36 @@ _cairo_pdf_operators_show_glyphs (cairo_pdf_operators_t *pdf_operators, return status; } - for (i = 0; i < num_glyphs; i++) { - status = _cairo_scaled_font_subsets_map_glyph (pdf_operators->font_subsets, - scaled_font, glyphs[i].index, - NULL, 0, - &subset_glyph); - if (status) - return status; - - if (new_text_object || - pdf_operators->font_id != subset_glyph.font_id || - pdf_operators->subset_id != subset_glyph.subset_id) - { - _cairo_pdf_operators_flush_glyphs (pdf_operators); - _cairo_pdf_operators_set_font_subset (pdf_operators, &subset_glyph); - new_text_object = FALSE; + if (num_clusters > 0) { + cur_text = utf8; + if (backward) + cur_glyph = glyphs + num_glyphs - 1; + else + cur_glyph = glyphs; + for (i = 0; i < num_clusters; i++) { + status = _cairo_pdf_operators_emit_cluster (pdf_operators, + cur_text, + clusters[i].num_bytes, + cur_glyph, + clusters[i].num_glyphs, + backward, + scaled_font); + cur_text += clusters[i].num_bytes; + if (backward) + cur_glyph -= clusters[i].num_glyphs; + else + cur_glyph += clusters[i].num_glyphs; } - - x = glyphs[i].x; - y = glyphs[i].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 becauses 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->cur_x) > 10 || - fabs(y - pdf_operators->cur_y) > GLYPH_POSITION_TOLERANCE) - { - _cairo_pdf_operators_flush_glyphs (pdf_operators); - - x = glyphs[i].x; - y = glyphs[i].y; - cairo_matrix_transform_point (&pdf_operators->cairo_to_pdf, &x, &y); - _cairo_pdf_operators_set_text_position (pdf_operators, x, y); - x = 0.0; - y = 0.0; + } else { + for (i = 0; i < num_glyphs; i++) { + status = _cairo_pdf_operators_emit_cluster (pdf_operators, + NULL, + -1, /* no unicode string available */ + &glyphs[i], + 1, + FALSE, + scaled_font); } - - _cairo_pdf_operators_add_glyph (pdf_operators, - &subset_glyph, - &font_matrix_inverse, - x); } return _cairo_output_stream_get_status (pdf_operators->stream); diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c index faef96436..87edbb105 100644 --- a/src/cairo-pdf-surface.c +++ b/src/cairo-pdf-surface.c @@ -3959,10 +3959,12 @@ _cairo_pdf_surface_write_smask_group (cairo_pdf_surface_t *surface, &group->ctm_inverse); break; case PDF_SHOW_GLYPHS: - status = _cairo_pdf_operators_show_glyphs (&surface->pdf_operators, - group->glyphs, - group->num_glyphs, - group->scaled_font); + status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators, + NULL, 0, + group->glyphs, group->num_glyphs, + NULL, 0, + FALSE, + group->scaled_font); break; } if (status) @@ -4797,10 +4799,12 @@ _cairo_pdf_surface_show_glyphs (void *abstract_surface, return status; } - status = _cairo_pdf_operators_show_glyphs (&surface->pdf_operators, - glyphs, - num_glyphs, - scaled_font); + status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators, + NULL, 0, + glyphs, num_glyphs, + NULL, 0, + FALSE, + scaled_font); if (status) return status; diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c index a4266d2de..326124790 100644 --- a/src/cairo-ps-surface.c +++ b/src/cairo-ps-surface.c @@ -175,6 +175,11 @@ _cairo_ps_surface_emit_header (cairo_ps_surface_t *surface) "/W* { eoclip } bind def\n" "/BT { } bind def\n" "/ET { } bind def\n" + "/pdfmark where { pop globaldict /?pdfmark /exec load put }\n" + " { globaldict begin /?pdfmark /pop load def /pdfmark\n" + " /cleartomark load def end } ifelse\n" + "/BDC { mark 3 1 roll /BDC pdfmark } bind def\n" + "/EMC { mark /EMC pdfmark } bind def\n" "/Tj { show } bind def\n" "/TJ {\n" " {\n" @@ -3091,10 +3096,12 @@ _cairo_ps_surface_show_glyphs (void *abstract_surface, if (status) return status; - return _cairo_pdf_operators_show_glyphs (&surface->pdf_operators, - glyphs, - num_glyphs, - scaled_font); + return _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators, + NULL, 0, + glyphs, num_glyphs, + NULL, 0, + FALSE, + scaled_font); } static void |