cairo-pdf-operators.c [plain text]
#include "cairoint.h"
#if CAIRO_HAS_PDF_OPERATORS
#include "cairo-error-private.h"
#include "cairo-pdf-operators-private.h"
#include "cairo-path-fixed-private.h"
#include "cairo-output-stream-private.h"
#include "cairo-scaled-font-subsets-private.h"
static cairo_status_t
_cairo_pdf_operators_end_text (cairo_pdf_operators_t *pdf_operators);
void
_cairo_pdf_operators_init (cairo_pdf_operators_t *pdf_operators,
cairo_output_stream_t *stream,
cairo_matrix_t *cairo_to_pdf,
cairo_scaled_font_subsets_t *font_subsets)
{
pdf_operators->stream = stream;
pdf_operators->cairo_to_pdf = *cairo_to_pdf;
pdf_operators->font_subsets = font_subsets;
pdf_operators->use_font_subset = NULL;
pdf_operators->use_font_subset_closure = NULL;
pdf_operators->in_text_object = FALSE;
pdf_operators->num_glyphs = 0;
pdf_operators->has_line_style = FALSE;
pdf_operators->use_actual_text = FALSE;
}
cairo_status_t
_cairo_pdf_operators_fini (cairo_pdf_operators_t *pdf_operators)
{
return _cairo_pdf_operators_flush (pdf_operators);
}
void
_cairo_pdf_operators_set_font_subsets_callback (cairo_pdf_operators_t *pdf_operators,
cairo_pdf_operators_use_font_subset_t use_font_subset,
void *closure)
{
pdf_operators->use_font_subset = use_font_subset;
pdf_operators->use_font_subset_closure = closure;
}
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;
}
void
_cairo_pdf_operators_set_cairo_to_pdf_matrix (cairo_pdf_operators_t *pdf_operators,
cairo_matrix_t *cairo_to_pdf)
{
pdf_operators->cairo_to_pdf = *cairo_to_pdf;
pdf_operators->has_line_style = FALSE;
}
cairo_private void
_cairo_pdf_operators_enable_actual_text (cairo_pdf_operators_t *pdf_operators,
cairo_bool_t enable)
{
pdf_operators->use_actual_text = enable;
}
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;
}
void
_cairo_pdf_operators_reset (cairo_pdf_operators_t *pdf_operators)
{
pdf_operators->has_line_style = FALSE;
}
typedef struct _word_wrap_stream {
cairo_output_stream_t base;
cairo_output_stream_t *output;
int max_column;
int column;
cairo_bool_t last_write_was_space;
cairo_bool_t in_hexstring;
cairo_bool_t empty_hexstring;
} word_wrap_stream_t;
static int
_count_word_up_to (const unsigned char *s, int length)
{
int word = 0;
while (length--) {
if (! (_cairo_isspace (*s) || *s == '<')) {
s++;
word++;
} else {
return word;
}
}
return word;
}
static int
_count_hexstring_up_to (const unsigned char *s, int length, int columns)
{
int word = 0;
while (length--) {
if (*s++ != '>')
word++;
else
return word;
columns--;
if (columns < 0 && word > 1)
return word;
}
return word;
}
static cairo_status_t
_word_wrap_stream_write (cairo_output_stream_t *base,
const unsigned char *data,
unsigned int length)
{
word_wrap_stream_t *stream = (word_wrap_stream_t *) base;
cairo_bool_t newline;
int word;
while (length) {
if (*data == '<') {
stream->in_hexstring = TRUE;
stream->empty_hexstring = TRUE;
stream->last_write_was_space = FALSE;
data++;
length--;
_cairo_output_stream_printf (stream->output, "<");
stream->column++;
} else if (*data == '>') {
stream->in_hexstring = FALSE;
stream->last_write_was_space = FALSE;
data++;
length--;
_cairo_output_stream_printf (stream->output, ">");
stream->column++;
} else if (_cairo_isspace (*data)) {
newline = (*data == '\n' || *data == '\r');
if (! newline && stream->column >= stream->max_column) {
_cairo_output_stream_printf (stream->output, "\n");
stream->column = 0;
}
_cairo_output_stream_write (stream->output, data, 1);
data++;
length--;
if (newline) {
stream->column = 0;
}
else
stream->column++;
stream->last_write_was_space = TRUE;
} else {
if (stream->in_hexstring) {
word = _count_hexstring_up_to (data, length,
MAX (stream->max_column - stream->column, 0));
} else {
word = _count_word_up_to (data, length);
}
if (stream->column + word >= stream->max_column) {
if (stream->last_write_was_space ||
(stream->in_hexstring && !stream->empty_hexstring))
{
_cairo_output_stream_printf (stream->output, "\n");
stream->column = 0;
}
}
_cairo_output_stream_write (stream->output, data, word);
data += word;
length -= word;
stream->column += word;
stream->last_write_was_space = FALSE;
if (stream->in_hexstring)
stream->empty_hexstring = FALSE;
}
}
return _cairo_output_stream_get_status (stream->output);
}
static cairo_status_t
_word_wrap_stream_close (cairo_output_stream_t *base)
{
word_wrap_stream_t *stream = (word_wrap_stream_t *) base;
return _cairo_output_stream_get_status (stream->output);
}
static cairo_output_stream_t *
_word_wrap_stream_create (cairo_output_stream_t *output, int max_column)
{
word_wrap_stream_t *stream;
if (output->status)
return _cairo_output_stream_create_in_error (output->status);
stream = malloc (sizeof (word_wrap_stream_t));
if (unlikely (stream == NULL)) {
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return (cairo_output_stream_t *) &_cairo_output_stream_nil;
}
_cairo_output_stream_init (&stream->base,
_word_wrap_stream_write,
NULL,
_word_wrap_stream_close);
stream->output = output;
stream->max_column = max_column;
stream->column = 0;
stream->last_write_was_space = FALSE;
stream->in_hexstring = FALSE;
stream->empty_hexstring = TRUE;
return &stream->base;
}
typedef struct _pdf_path_info {
cairo_output_stream_t *output;
cairo_matrix_t *path_transform;
cairo_line_cap_t line_cap;
cairo_point_t last_move_to_point;
cairo_bool_t has_sub_path;
} pdf_path_info_t;
static cairo_status_t
_cairo_pdf_path_move_to (void *closure,
const cairo_point_t *point)
{
pdf_path_info_t *info = closure;
double x = _cairo_fixed_to_double (point->x);
double y = _cairo_fixed_to_double (point->y);
info->last_move_to_point = *point;
info->has_sub_path = FALSE;
cairo_matrix_transform_point (info->path_transform, &x, &y);
_cairo_output_stream_printf (info->output,
"%g %g m ", x, y);
return _cairo_output_stream_get_status (info->output);
}
static cairo_status_t
_cairo_pdf_path_line_to (void *closure,
const cairo_point_t *point)
{
pdf_path_info_t *info = closure;
double x = _cairo_fixed_to_double (point->x);
double y = _cairo_fixed_to_double (point->y);
if (info->line_cap != CAIRO_LINE_CAP_ROUND &&
! info->has_sub_path &&
point->x == info->last_move_to_point.x &&
point->y == info->last_move_to_point.y)
{
return CAIRO_STATUS_SUCCESS;
}
info->has_sub_path = TRUE;
cairo_matrix_transform_point (info->path_transform, &x, &y);
_cairo_output_stream_printf (info->output,
"%g %g l ", x, y);
return _cairo_output_stream_get_status (info->output);
}
static cairo_status_t
_cairo_pdf_path_curve_to (void *closure,
const cairo_point_t *b,
const cairo_point_t *c,
const cairo_point_t *d)
{
pdf_path_info_t *info = closure;
double bx = _cairo_fixed_to_double (b->x);
double by = _cairo_fixed_to_double (b->y);
double cx = _cairo_fixed_to_double (c->x);
double cy = _cairo_fixed_to_double (c->y);
double dx = _cairo_fixed_to_double (d->x);
double dy = _cairo_fixed_to_double (d->y);
info->has_sub_path = TRUE;
cairo_matrix_transform_point (info->path_transform, &bx, &by);
cairo_matrix_transform_point (info->path_transform, &cx, &cy);
cairo_matrix_transform_point (info->path_transform, &dx, &dy);
_cairo_output_stream_printf (info->output,
"%g %g %g %g %g %g c ",
bx, by, cx, cy, dx, dy);
return _cairo_output_stream_get_status (info->output);
}
static cairo_status_t
_cairo_pdf_path_close_path (void *closure)
{
pdf_path_info_t *info = closure;
if (info->line_cap != CAIRO_LINE_CAP_ROUND &&
! info->has_sub_path)
{
return CAIRO_STATUS_SUCCESS;
}
_cairo_output_stream_printf (info->output,
"h\n");
return _cairo_output_stream_get_status (info->output);
}
static cairo_status_t
_cairo_pdf_path_rectangle (pdf_path_info_t *info, cairo_box_t *box)
{
double x1 = _cairo_fixed_to_double (box->p1.x);
double y1 = _cairo_fixed_to_double (box->p1.y);
double x2 = _cairo_fixed_to_double (box->p2.x);
double y2 = _cairo_fixed_to_double (box->p2.y);
cairo_matrix_transform_point (info->path_transform, &x1, &y1);
cairo_matrix_transform_point (info->path_transform, &x2, &y2);
_cairo_output_stream_printf (info->output,
"%g %g %g %g re ",
x1, y1, x2 - x1, y2 - y1);
return _cairo_output_stream_get_status (info->output);
}
static cairo_status_t
_cairo_pdf_operators_emit_path (cairo_pdf_operators_t *pdf_operators,
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, 72);
status = _cairo_output_stream_get_status (word_wrap);
if (unlikely (status))
return _cairo_output_stream_destroy (word_wrap);
info.output = word_wrap;
info.path_transform = path_transform;
info.line_cap = line_cap;
if (_cairo_path_fixed_is_rectangle (path, &box)) {
status = _cairo_pdf_path_rectangle (&info, &box);
} else {
status = _cairo_path_fixed_interpret (path,
CAIRO_DIRECTION_FORWARD,
_cairo_pdf_path_move_to,
_cairo_pdf_path_line_to,
_cairo_pdf_path_curve_to,
_cairo_pdf_path_close_path,
&info);
}
status2 = _cairo_output_stream_destroy (word_wrap);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
return status;
}
cairo_int_status_t
_cairo_pdf_operators_clip (cairo_pdf_operators_t *pdf_operators,
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) {
_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);
}
static int
_cairo_pdf_line_cap (cairo_line_cap_t cap)
{
switch (cap) {
case CAIRO_LINE_CAP_BUTT:
return 0;
case CAIRO_LINE_CAP_ROUND:
return 1;
case CAIRO_LINE_CAP_SQUARE:
return 2;
default:
ASSERT_NOT_REACHED;
return 0;
}
}
static int
_cairo_pdf_line_join (cairo_line_join_t join)
{
switch (join) {
case CAIRO_LINE_JOIN_MITER:
return 0;
case CAIRO_LINE_JOIN_ROUND:
return 1;
case CAIRO_LINE_JOIN_BEVEL:
return 2;
default:
ASSERT_NOT_REACHED;
return 0;
}
}
cairo_int_status_t
_cairo_pdf_operators_emit_stroke_style (cairo_pdf_operators_t *pdf_operators,
const cairo_stroke_style_t *style,
double scale)
{
double *dash = style->dash;
int num_dashes = style->num_dashes;
double dash_offset = style->dash_offset;
double line_width = style->line_width * scale;
if (num_dashes && style->line_cap == CAIRO_LINE_CAP_BUTT) {
int i;
if (num_dashes % 2) {
dash = _cairo_malloc_abc (num_dashes, 2, sizeof (double));
if (unlikely (dash == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
memcpy (dash, style->dash, num_dashes * sizeof (double));
memcpy (dash + num_dashes, style->dash, num_dashes * sizeof (double));
num_dashes *= 2;
}
for (i = 0; i < num_dashes; i += 2) {
if (dash[i] == 0.0) {
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 (i == 0) {
double last_two[2];
if (num_dashes == 2) {
free (dash);
return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
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 (i == 2)
i = -2;
}
}
}
if (!pdf_operators->has_line_style || pdf_operators->line_width != line_width) {
_cairo_output_stream_printf (pdf_operators->stream,
"%f w\n",
line_width);
pdf_operators->line_width = line_width;
}
if (!pdf_operators->has_line_style || pdf_operators->line_cap != style->line_cap) {
_cairo_output_stream_printf (pdf_operators->stream,
"%d J\n",
_cairo_pdf_line_cap (style->line_cap));
pdf_operators->line_cap = style->line_cap;
}
if (!pdf_operators->has_line_style || pdf_operators->line_join != style->line_join) {
_cairo_output_stream_printf (pdf_operators->stream,
"%d j\n",
_cairo_pdf_line_join (style->line_join));
pdf_operators->line_join = style->line_join;
}
if (num_dashes) {
int d;
_cairo_output_stream_printf (pdf_operators->stream, "[");
for (d = 0; d < num_dashes; d++)
_cairo_output_stream_printf (pdf_operators->stream, " %f", dash[d] * scale);
_cairo_output_stream_printf (pdf_operators->stream, "] %f d\n",
dash_offset * scale);
pdf_operators->has_dashes = TRUE;
} else if (!pdf_operators->has_line_style || pdf_operators->has_dashes) {
_cairo_output_stream_printf (pdf_operators->stream, "[] 0.0 d\n");
pdf_operators->has_dashes = FALSE;
}
if (dash != style->dash)
free (dash);
if (!pdf_operators->has_line_style || pdf_operators->miter_limit != style->miter_limit) {
_cairo_output_stream_printf (pdf_operators->stream,
"%f M ",
style->miter_limit < 1.0 ? 1.0 : style->miter_limit);
pdf_operators->miter_limit = style->miter_limit;
}
pdf_operators->has_line_style = TRUE;
return _cairo_output_stream_get_status (pdf_operators->stream);
}
static void
_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);
}
static cairo_int_status_t
_cairo_pdf_operators_emit_stroke (cairo_pdf_operators_t *pdf_operators,
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_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;
}
if (fabs(ctm->xx) == 1.0 && fabs(ctm->yy) == 1.0 &&
fabs(ctm->xy) == 0.0 && fabs(ctm->yx) == 0.0)
{
has_ctm = FALSE;
}
if (has_ctm) {
m = *ctm;
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);
}
cairo_int_status_t
_cairo_pdf_operators_stroke (cairo_pdf_operators_t *pdf_operators,
cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse)
{
return _cairo_pdf_operators_emit_stroke (pdf_operators,
path,
style,
ctm,
ctm_inverse,
"S");
}
cairo_int_status_t
_cairo_pdf_operators_fill (cairo_pdf_operators_t *pdf_operators,
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);
}
cairo_int_status_t
_cairo_pdf_operators_fill_stroke (cairo_pdf_operators_t *pdf_operators,
cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse)
{
const char *operator;
switch (fill_rule) {
default:
ASSERT_NOT_REACHED;
case CAIRO_FILL_RULE_WINDING:
operator = "B";
break;
case CAIRO_FILL_RULE_EVEN_ODD:
operator = "B*";
break;
}
return _cairo_pdf_operators_emit_stroke (pdf_operators,
path,
style,
ctm,
ctm_inverse,
operator);
}
#define GLYPH_POSITION_TOLERANCE 0.001
static cairo_status_t
_cairo_pdf_operators_emit_glyph_string (cairo_pdf_operators_t *pdf_operators,
cairo_output_stream_t *stream)
{
int i;
_cairo_output_stream_printf (stream, "<");
for (i = 0; i < pdf_operators->num_glyphs; i++) {
_cairo_output_stream_printf (stream,
"%0*x",
pdf_operators->hex_width,
pdf_operators->glyphs[i].glyph_index);
pdf_operators->cur_x += pdf_operators->glyphs[i].x_advance;
}
_cairo_output_stream_printf (stream, ">Tj\n");
return _cairo_output_stream_get_status (stream);
}
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, "[<");
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;
rounded_delta = _cairo_lround (delta);
if (rounded_delta != 0) {
_cairo_output_stream_printf (stream,
">%d<",
rounded_delta);
}
delta = rounded_delta/-1000.0;
pdf_operators->cur_x += delta;
}
_cairo_output_stream_printf (stream,
"%0*x",
pdf_operators->hex_width,
pdf_operators->glyphs[i].glyph_index);
pdf_operators->cur_x += pdf_operators->glyphs[i].x_advance;
}
_cairo_output_stream_printf (stream, ">]TJ\n");
return _cairo_output_stream_get_status (stream);
}
static cairo_status_t
_cairo_pdf_operators_flush_glyphs (cairo_pdf_operators_t *pdf_operators)
{
cairo_output_stream_t *word_wrap_stream;
cairo_status_t status, status2;
int i;
double x;
if (pdf_operators->num_glyphs == 0)
return CAIRO_STATUS_SUCCESS;
word_wrap_stream = _word_wrap_stream_create (pdf_operators->stream, 72);
status = _cairo_output_stream_get_status (word_wrap_stream);
if (unlikely (status))
return _cairo_output_stream_destroy (word_wrap_stream);
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;
return status;
}
static cairo_status_t
_cairo_pdf_operators_add_glyph (cairo_pdf_operators_t *pdf_operators,
cairo_scaled_font_subsets_glyph_t *glyph,
double x_position)
{
double x, y;
x = glyph->x_advance;
y = glyph->y_advance;
if (glyph->is_scaled)
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;
pdf_operators->glyphs[pdf_operators->num_glyphs].x_advance = x;
pdf_operators->glyph_buf_x_pos += x;
pdf_operators->num_glyphs++;
if (pdf_operators->num_glyphs == PDF_GLYPH_BUFFER_SIZE)
return _cairo_pdf_operators_flush_glyphs (pdf_operators);
return CAIRO_STATUS_SUCCESS;
}
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;
inverse = *matrix;
status = cairo_matrix_invert (&inverse);
if (unlikely (status))
return status;
pdf_operators->text_matrix = *matrix;
pdf_operators->cur_x = 0;
pdf_operators->cur_y = 0;
pdf_operators->glyph_buf_x_pos = 0;
_cairo_output_stream_printf (pdf_operators->stream,
"%f %f %f %f %f %f Tm\n",
pdf_operators->text_matrix.xx,
pdf_operators->text_matrix.yx,
pdf_operators->text_matrix.xy,
pdf_operators->text_matrix.yy,
pdf_operators->text_matrix.x0,
pdf_operators->text_matrix.y0);
pdf_operators->cairo_to_pdftext = *matrix;
status = cairo_matrix_invert (&pdf_operators->cairo_to_pdftext);
assert (status == CAIRO_STATUS_SUCCESS);
cairo_matrix_multiply (&pdf_operators->cairo_to_pdftext,
&pdf_operators->cairo_to_pdf,
&pdf_operators->cairo_to_pdftext);
return _cairo_output_stream_get_status (pdf_operators->stream);
}
#define TEXT_MATRIX_TOLERANCE 1e-6
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;
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;
pdf_operators->cairo_to_pdftext = pdf_operators->text_matrix;
status = cairo_matrix_invert (&pdf_operators->cairo_to_pdftext);
assert (status == CAIRO_STATUS_SUCCESS);
cairo_matrix_multiply (&pdf_operators->cairo_to_pdftext,
&pdf_operators->cairo_to_pdf,
&pdf_operators->cairo_to_pdftext);
return _cairo_output_stream_get_status (pdf_operators->stream);
}
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;
_cairo_output_stream_printf (pdf_operators->stream,
"/f-%d-%d 1 Tf\n",
subset_glyph->font_id,
subset_glyph->subset_id);
if (pdf_operators->use_font_subset) {
status = pdf_operators->use_font_subset (subset_glyph->font_id,
subset_glyph->subset_id,
pdf_operators->use_font_subset_closure);
if (unlikely (status))
return status;
}
pdf_operators->font_id = subset_glyph->font_id;
pdf_operators->subset_id = subset_glyph->subset_id;
if (subset_glyph->is_composite)
pdf_operators->hex_width = 4;
else
pdf_operators->hex_width = 2;
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_cairo_pdf_operators_begin_text (cairo_pdf_operators_t *pdf_operators)
{
_cairo_output_stream_printf (pdf_operators->stream, "BT\n");
pdf_operators->in_text_object = TRUE;
pdf_operators->num_glyphs = 0;
pdf_operators->glyph_buf_x_pos = 0;
return _cairo_output_stream_get_status (pdf_operators->stream);
}
static cairo_status_t
_cairo_pdf_operators_end_text (cairo_pdf_operators_t *pdf_operators)
{
cairo_status_t status;
status = _cairo_pdf_operators_flush_glyphs (pdf_operators);
if (unlikely (status))
return status;
_cairo_output_stream_printf (pdf_operators->stream, "ET\n");
pdf_operators->in_text_object = FALSE;
return _cairo_output_stream_get_status (pdf_operators->stream);
}
static cairo_bool_t
_cairo_matrix_scale_equal (cairo_matrix_t *a, cairo_matrix_t *b)
{
return (a->xx == b->xx &&
a->xy == b->xy &&
a->yx == b->yx &&
a->yy == b->yy);
}
static cairo_status_t
_cairo_pdf_operators_begin_actualtext (cairo_pdf_operators_t *pdf_operators,
const char *utf8,
int utf8_len)
{
uint16_t *utf16;
int utf16_len;
cairo_status_t status;
int i;
_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 (unlikely (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_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);
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;
}
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_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 (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) {
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;
}
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, -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,
const char *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;
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;
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;
pdf_operators->is_new_text_object = TRUE;
}
cairo_matrix_init_scale (&invert_y_axis, 1, -1);
text_matrix = scaled_font->scale;
cairo_matrix_multiply (&text_matrix, &text_matrix, &invert_y_axis);
cairo_matrix_multiply (&text_matrix, &invert_y_axis, &text_matrix);
if (pdf_operators->is_new_text_object ||
! _cairo_matrix_scale_equal (&pdf_operators->text_matrix, &text_matrix))
{
status = _cairo_pdf_operators_flush_glyphs (pdf_operators);
if (unlikely (status))
return status;
x = glyphs[0].x;
y = glyphs[0].y;
cairo_matrix_transform_point (&pdf_operators->cairo_to_pdf, &x, &y);
text_matrix.x0 = x;
text_matrix.y0 = y;
status = _cairo_pdf_operators_set_text_matrix (pdf_operators, &text_matrix);
if (status == CAIRO_STATUS_INVALID_MATRIX)
return CAIRO_STATUS_SUCCESS;
if (unlikely (status))
return status;
}
if (num_clusters > 0) {
cur_text = utf8;
if ((cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD))
cur_glyph = glyphs + num_glyphs;
else
cur_glyph = glyphs;
for (i = 0; i < num_clusters; i++) {
if ((cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD))
cur_glyph -= clusters[i].num_glyphs;
status = _cairo_pdf_operators_emit_cluster (pdf_operators,
cur_text,
clusters[i].num_bytes,
cur_glyph,
clusters[i].num_glyphs,
cluster_flags,
scaled_font);
if (unlikely (status))
return status;
cur_text += clusters[i].num_bytes;
if (!(cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD))
cur_glyph += clusters[i].num_glyphs;
}
} else {
for (i = 0; i < num_glyphs; i++) {
status = _cairo_pdf_operators_emit_cluster (pdf_operators,
NULL,
-1,
&glyphs[i],
1,
FALSE,
scaled_font);
if (unlikely (status))
return status;
}
}
return _cairo_output_stream_get_status (pdf_operators->stream);
}
#endif