/* Finish writing out any pending commands to the stream. This *functionmustbecalledbythesurfacebeforeemittinganything *intothePDFstream. * *pdf_operatorsmayleavetheemittedPDFforsomeoperations *unfinishedincasesubsequentoperationscanbemerged.This *functionwillfinishoffanyincompleteoperationsothestream *willbeinastatewherethesurfacemayemititsownPDF *operations(egchangingpatterns). *
*/
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 *assumptionswillbemadeaboutthestate.Thenexttimea *particulargraphicsstateisrequired(eglinewidth)thestate *operatorisalwaysemittedandthenrememberedforsubsequent *operations. * *Thisshouldbecalledwhenstartinganewstreamorafteremitting *the'Q'operator(wherepdf-operatorsfunctionswerecalledinside *theq/Qpair).
*/ 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 *topofanexistingoutputstream.Thewordwrappingisquite *simple,usingisspacetodeterminecharactersthatseparate *words.Anywordthatwillcausethecolumncountexceedthegiven *max_columnwillhavea'\n'characteremittedbeforeit. * *Thestreamiscarefultomaintainintegrityforwordsthatcross *theboundaryfromonecalltowritetothenext. * *Note:Thisstreamdoesnotguaranteethattheoutputwillnever *exceedmax_column.Inparticular,ifasinglewordislargerthan *max_columnitwillnotbebrokenup.
*/
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 *ofcolumnsremaining.
*/ 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;
/* PostScript has "special needs" when it comes to zero-length *dashsegmentswithbuttcaps.Itapparently(atleast *accordingtoghostscript)drawshairlinesforthis *case.That'snotwhatthecairosemanticswant,sowefirst *touchupthearraytoeliminateany0.0valuesthatwill *resultin"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 *interpretedasbothonandoff.Sowefirstexplicitly *expandthearraytoremovetheduplicateusagesothatwe *canmodifysomeofthevalues.
*/ 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 *replaythisstroketoanimagefallback.
*/ 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 *twoelementsfromtheendofthelisttothefront *ofthelistbeforefoldingawaythe0.0.Or,if *thereareonlytwodashelements,thenthereis *nothingatalltodraw.
*/ 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 *cannotexist,sotherotationof2elements
* 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 *werotateda0.0valuetothefrontofthelist.
* 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 *translationcomponentsis1.0.Returnthescalerequiredtorestore *thematrixtotheoriginalvalues. * *egthematrix[10000502010] * *isrescaledto[1000.50.20.1] *andthescalereturnedis100
*/ 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.Thereareotherctmcasesthatcouldbeoptimized *howeverthisisthemostcommon.
*/ 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 *sothecorrectpenshapewillbeused.Thisalsorequiresthat *thepathbetransformedtouserspacewhenemitted.The *conversionofpathcoordinatestouserspacemaycauserounding *errors.Forexamplethedevicespacepoint(1.234,3.142)when *transformedtoauserspaceCTMof[1000010000]willbe *emittedas(0.012,0.031). * *ToavoidtheroundingproblemwescaletheuserspaceCTM *matrixsothatallthenontranslationcomponentsofthematrix *are<=1.Thelinewidthandanddashesarescaledbythe *inverseofthescaleappliedtotheCTM.Thismaintainsthe *shapeofthestrokepenwhilekeepingtheuserspaceCTMwithin *therangethatmaximizestheprecisionoftheemittedpath.
*/ if (has_ctm) {
m = *ctm; /* Zero out the translation since it does not affect the pen *shapehoweveritmaycauseunnecessarydigitstobeemitted.
*/
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. * *TheTJoperatortakesanarrayofstringsofglyphs.Eachstringof *glyphsisdisplayedusingtheglyphadvancesofeachglyphto *positiontheglyphs.Arelativeadjustmenttotheglyphadvancemay *bespecifiedbyincludingtheadjustmentbetweentwostrings.The *adjustmentisinunitsoftextspace*-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, *roundingtoanintegershouldstillprovidesufficient *precision.WeroundthedeltabeforeaddingtoTm_xso *thatwekeeptrackoftheaccumulatedroundingerrorin *thePDFinterpreterandcompensateforitwhen *calculatingsubsequentdeltas.
*/
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 *spacebeforeaddingtothecurrenttext
* 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'operatorisusedtotransformthetextmatrix.
*/ 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;
/* Select the font using the 'Tf' operator. The font size is set to 1 *asweusethe'Tm'operatortosetthefontscale.
*/ 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 *thehorizontalpositionoftheglyphs.Iftheyposition *(intextspace)changes,usetheTdoperatortochangethe *currentpositiontothenextglyph.WealsousetheTd *operatortomovethecurrentpositionifthehorizontal *positionchangesbymorethan10(intextspace *units).Thisisbecausethehorizontalglyphpositioning *intheTJoperatorisintendedforkerningandtheremaybe *PDFconsumersthatdonothandleverylargeposition *adjustmentsinTJ.
*/ 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 *emptystring.
*/ 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 *firsttry_map_glyph()withtheunicodestringtoseeifitcan *usetoUnicodetomapourglyphtotheunicode.Thiswillfail *iftheglyphisalreadymappedtoadifferentunicodestring. * *Wealsogothroughthispathifnounicodemappingwas *supplied(utf8_len<0). * *Mappingaglyphtoazerolengthunicodestringrequiresthe *useofActualText.
*/ 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.