static void cairo_gl_shader_emit_variable (cairo_output_stream_t *stream, cairo_gl_var_type_t type, cairo_gl_tex_t name) { switch (type) { default: ASSERT_NOT_REACHED; case CAIRO_GL_VAR_NONE: break; case CAIRO_GL_VAR_TEXCOORDS: _cairo_output_stream_printf (stream, "attribute vec4 MultiTexCoord%d;\n" "varying vec2 %s_texcoords;\n", name, operand_names[name]); break; case CAIRO_GL_VAR_TEXGEN: _cairo_output_stream_printf (stream, "uniform mat3 %s_texgen;\n" "varying vec2 %s_texcoords;\n", operand_names[name], operand_names[name]); break; } }
static cairo_status_t _cairo_type3_glyph_surface_emit_image (cairo_type3_glyph_surface_t *surface, cairo_image_surface_t *image, cairo_matrix_t *image_matrix) { cairo_status_t status; /* The only image type supported by Type 3 fonts are 1-bit masks */ image = _cairo_image_surface_coerce_to_format (image, CAIRO_FORMAT_A1); status = image->base.status; if (unlikely (status)) return status; _cairo_output_stream_printf (surface->stream, "q %f %f %f %f %f %f cm\n", image_matrix->xx, image_matrix->xy, image_matrix->yx, image_matrix->yy, image_matrix->x0, image_matrix->y0); status = surface->emit_image (image, surface->stream); cairo_surface_destroy (&image->base); _cairo_output_stream_printf (surface->stream, "Q\n"); return status; }
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 void cairo_gl_shader_emit_variable (cairo_output_stream_t *stream, cairo_gl_var_type_t type, cairo_gl_tex_t name, cairo_bool_t use_atlas) { switch (type) { default: ASSERT_NOT_REACHED; case CAIRO_GL_VAR_NONE: break; case CAIRO_GL_VAR_COLOR: _cairo_output_stream_printf (stream, "varying vec4 fragment_color;\n"); break; case CAIRO_GL_VAR_TEXCOORDS: _cairo_output_stream_printf (stream, "varying vec2 %s_texcoords;\n", operand_names[name]); if (use_atlas) _cairo_output_stream_printf (stream, "varying vec2 %s_start_coords;\n" "varying vec2 %s_stop_coords;\n", operand_names[name], operand_names[name]); break; } }
/* * Emits the wrap function used by an operand. * * In OpenGL ES 2.0, repeat wrap modes (GL_REPEAT and GL_MIRRORED REPEAT) are * only available for NPOT textures if the GL_OES_texture_npot is supported. * If GL_OES_texture_npot is not supported, we need to implement the wrapping * functionality in the shader. */ static void _cairo_gl_shader_emit_wrap (cairo_gl_context_t *ctx, cairo_output_stream_t *stream, cairo_gl_operand_t *operand, cairo_gl_tex_t name) { const char *namestr = operand_names[name]; cairo_extend_t extend = _cairo_gl_operand_get_extend (operand); _cairo_output_stream_printf (stream, "vec2 %s_wrap(vec2 coords)\n" "{\n", namestr); if (! ctx->has_npot_repeat && (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT)) { if (extend == CAIRO_EXTEND_REPEAT) { _cairo_output_stream_printf (stream, " return fract(coords);\n"); } else { /* CAIRO_EXTEND_REFLECT */ _cairo_output_stream_printf (stream, " return mix(fract(coords), 1.0 - fract(coords), floor(mod(coords, 2.0)));\n"); } } else { _cairo_output_stream_printf (stream, " return coords;\n"); } _cairo_output_stream_printf (stream, "}\n"); }
/* 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; } _cairo_pdf_operators_emit_glyph_index (pdf_operators, stream, pdf_operators->glyphs[i].glyph_index); pdf_operators->cur_x += pdf_operators->glyphs[i].x_advance; } _cairo_output_stream_printf (stream, "%s]TJ\n", pdf_operators->is_latin ? ")" : ">"); return _cairo_output_stream_get_status (stream); }
/* 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; 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); }
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); }
/* Emit hexstring bytes up to either the end of the ASCII hexstring or the number * of columns remaining. */ static int _word_wrap_stream_count_hexstring_up_to (word_wrap_stream_t *stream, const unsigned char *data, int length) { const unsigned char *s = data; int count = 0; cairo_bool_t newline = FALSE; while (length--) { count++; stream->column++; if (*s == '>') { stream->state = WRAP_STATE_DELIMITER; break; } if (stream->column > stream->max_column) { newline = TRUE; break; } s++; } if (count) _cairo_output_stream_write (stream->output, data, count); if (newline) { _cairo_output_stream_printf (stream->output, "\n"); stream->column = 0; } return count; }
/* 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; _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; pdf_operators->is_latin = subset_glyph->is_latin; 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_gl_shader_get_vertex_source (cairo_gl_var_type_t src, cairo_gl_var_type_t mask, cairo_gl_var_type_t dest, char **out) { cairo_output_stream_t *stream = _cairo_memory_stream_create (); unsigned char *source; unsigned int length; cairo_status_t status; cairo_gl_shader_emit_variable (stream, src, CAIRO_GL_TEX_SOURCE); cairo_gl_shader_emit_variable (stream, mask, CAIRO_GL_TEX_MASK); _cairo_output_stream_printf (stream, "void main()\n" "{\n" " gl_Position = ftransform();\n"); cairo_gl_shader_emit_vertex (stream, src, CAIRO_GL_TEX_SOURCE); cairo_gl_shader_emit_vertex (stream, mask, CAIRO_GL_TEX_MASK); _cairo_output_stream_write (stream, "}\n\0", 3); status = _cairo_memory_stream_destroy (stream, &source, &length); if (unlikely (status)) return status; *out = (char *) source; return CAIRO_STATUS_SUCCESS; }
static cairo_status_t cairo_gl_shader_get_fragment_source (GLuint tex_target, cairo_gl_shader_in_t in, cairo_gl_operand_type_t src, cairo_gl_operand_type_t mask, cairo_gl_operand_type_t dest, char **out) { cairo_output_stream_t *stream = _cairo_memory_stream_create (); unsigned char *source; unsigned int length; cairo_status_t status; cairo_gl_shader_emit_color (stream, tex_target, src, CAIRO_GL_TEX_SOURCE); cairo_gl_shader_emit_color (stream, tex_target, mask, CAIRO_GL_TEX_MASK); _cairo_output_stream_printf (stream, "void main()\n" "{\n"); switch (in) { case CAIRO_GL_SHADER_IN_COUNT: default: ASSERT_NOT_REACHED; case CAIRO_GL_SHADER_IN_NORMAL: _cairo_output_stream_printf (stream, " gl_FragColor = get_source() * get_mask().a;\n"); break; case CAIRO_GL_SHADER_IN_CA_SOURCE: _cairo_output_stream_printf (stream, " gl_FragColor = get_source() * get_mask();\n"); break; case CAIRO_GL_SHADER_IN_CA_SOURCE_ALPHA: _cairo_output_stream_printf (stream, " gl_FragColor = get_source().a * get_mask();\n"); break; } _cairo_output_stream_write (stream, "}\n\0", 3); status = _cairo_memory_stream_destroy (stream, &source, &length); if (unlikely (status)) return status; *out = (char *) source; return CAIRO_STATUS_SUCCESS; }
/* 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; _cairo_output_stream_printf (stream, "%s", pdf_operators->is_latin ? "(" : "<"); for (i = 0; i < pdf_operators->num_glyphs; i++) { _cairo_pdf_operators_emit_glyph_index (pdf_operators, stream, pdf_operators->glyphs[i].glyph_index); pdf_operators->cur_x += pdf_operators->glyphs[i].x_advance; } _cairo_output_stream_printf (stream, "%sTj\n", pdf_operators->is_latin ? ")" : ">"); return _cairo_output_stream_get_status (stream); }
/* 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; _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_gl_shader_get_vertex_source (cairo_gl_var_type_t src_type, cairo_gl_var_type_t mask_type, cairo_bool_t src_use_atlas, cairo_bool_t mask_use_atlas, cairo_bool_t use_coverage, cairo_gl_var_type_t dest, char **out) { cairo_output_stream_t *stream = _cairo_memory_stream_create (); unsigned char *source; unsigned long length; cairo_status_t status; cairo_gl_shader_emit_variable (stream, src_type, CAIRO_GL_TEX_SOURCE, src_use_atlas); cairo_gl_shader_emit_variable (stream, mask_type, CAIRO_GL_TEX_MASK, mask_use_atlas); if (use_coverage) cairo_gl_shader_dcl_coverage (stream); _cairo_output_stream_printf (stream, "attribute vec4 Vertex;\n" "attribute vec4 Color;\n" "attribute vec4 Coverage;\n" "attribute vec4 MultiTexCoord0;\n" "attribute vec4 MultiTexCoord1;\n" "attribute vec2 StartCoords0;\n" "attribute vec2 StartCoords1;\n" "attribute vec2 StopCoords0;\n" "attribute vec2 StopCoords1;\n" "uniform mat4 ModelViewProjectionMatrix;\n" "void main()\n" "{\n" " gl_Position = ModelViewProjectionMatrix * Vertex;\n"); cairo_gl_shader_emit_vertex (stream, src_type, CAIRO_GL_TEX_SOURCE); cairo_gl_shader_emit_vertex (stream, mask_type, CAIRO_GL_TEX_MASK); if (use_coverage) cairo_gl_shader_def_coverage (stream); if (src_use_atlas) cairo_gl_shader_def_use_atlas (stream, src_type, CAIRO_GL_TEX_SOURCE); if (mask_use_atlas) cairo_gl_shader_def_use_atlas (stream, mask_type, CAIRO_GL_TEX_MASK); _cairo_output_stream_write (stream, "}\n\0", 3); status = _cairo_memory_stream_destroy (stream, &source, &length); if (unlikely (status)) return status; *out = (char *) source; return CAIRO_STATUS_SUCCESS; }
static void _cairo_pdf_operators_emit_glyph_index (cairo_pdf_operators_t *pdf_operators, cairo_output_stream_t *stream, unsigned int glyph) { if (pdf_operators->is_latin) { if (glyph == '(' || glyph == ')' || glyph == '\\') _cairo_output_stream_printf (stream, "\\%c", glyph); else if (glyph >= 0x20 && glyph <= 0x7e) _cairo_output_stream_printf (stream, "%c", glyph); else _cairo_output_stream_printf (stream, "\\%03o", glyph); } else { _cairo_output_stream_printf (stream, "%0*x", pdf_operators->hex_width, glyph); } }
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); }
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 void cairo_gl_shader_emit_vertex (cairo_output_stream_t *stream, cairo_gl_var_type_t type, cairo_gl_tex_t name) { switch (type) { default: ASSERT_NOT_REACHED; case CAIRO_GL_VAR_NONE: break; case CAIRO_GL_VAR_COLOR: _cairo_output_stream_printf (stream, " fragment_color = Color;\n"); break; case CAIRO_GL_VAR_TEXCOORDS: _cairo_output_stream_printf (stream, " %s_texcoords = MultiTexCoord%d.xy;\n", operand_names[name], name); break; } }
static void cairo_gl_shader_emit_variable (cairo_output_stream_t *stream, cairo_gl_var_type_t type, cairo_gl_tex_t name) { switch (type) { default: ASSERT_NOT_REACHED; case CAIRO_GL_VAR_NONE: break; case CAIRO_GL_VAR_TEXCOORDS: _cairo_output_stream_printf (stream, "varying vec2 %s_texcoords;\n", operand_names[name]); break; case CAIRO_GL_VAR_COVERAGE: _cairo_output_stream_printf (stream, "varying float %s_coverage;\n", operand_names[name]); break; } }
static void cairo_gl_shader_def_use_atlas (cairo_output_stream_t *stream, cairo_gl_var_type_t type, cairo_gl_tex_t name) { if (type == CAIRO_GL_VAR_TEXCOORDS) { _cairo_output_stream_printf (stream, " %s_start_coords = StartCoords%d.xy;\n" " %s_stop_coords = StopCoords%d.xy;\n", operand_names[name], name, operand_names[name], name); } }
static cairo_status_t _cairo_type3_glyph_surface_emit_image (cairo_type3_glyph_surface_t *surface, cairo_image_surface_t *image, cairo_matrix_t *image_matrix) { cairo_status_t status; cairo_image_surface_t *image_mask; /* The only image type supported by Type 3 fonts are 1-bit image * masks */ if (image->format == CAIRO_FORMAT_A1) { image_mask = image; } else { image_mask = _cairo_image_surface_clone (image, CAIRO_FORMAT_A1); status = cairo_surface_status (&image->base); if (status) return status; } _cairo_output_stream_printf (surface->stream, "q %f %f %f %f %f %f cm\n", image_matrix->xx, image_matrix->xy, image_matrix->yx, image_matrix->yy, image_matrix->x0, image_matrix->y0); status = surface->emit_image (image_mask, surface->stream); _cairo_output_stream_printf (surface->stream, "Q\n"); if (image_mask != image) cairo_surface_destroy (&image_mask->base); return status; }
cairo_status_t _cairo_type3_glyph_surface_emit_notdef_glyph (void *abstract_surface, cairo_output_stream_t *stream, cairo_box_t *bbox, double *width) { bbox->p1.x = 0; bbox->p1.y = 0; bbox->p2.x = 0; bbox->p2.y = 0; *width = 0.0; _cairo_output_stream_printf (stream, "0 0 0 0 0 0 d1\n"); return CAIRO_STATUS_SUCCESS; }
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); }
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_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; int count; while (length) { switch (stream->state) { case WRAP_STATE_WORD: count = _word_wrap_stream_count_word_up_to (stream, data, length); break; case WRAP_STATE_HEXSTRING: count = _word_wrap_stream_count_hexstring_up_to (stream, data, length); break; case WRAP_STATE_STRING: count = _word_wrap_stream_count_string_up_to (stream, data, length); break; case WRAP_STATE_DELIMITER: count = 1; stream->column++; if (*data == '\n' || stream->column >= stream->max_column) { _cairo_output_stream_printf (stream->output, "\n"); stream->column = 0; } if (*data == '<') { stream->state = WRAP_STATE_HEXSTRING; } else if (*data == '(') { stream->state = WRAP_STATE_STRING; } else if (!_cairo_isspace (*data)) { stream->state = WRAP_STATE_WORD; } if (*data != '\n') _cairo_output_stream_write (stream->output, data, 1); break; default: ASSERT_NOT_REACHED; count = length; break; } data += count; length -= count; } return _cairo_output_stream_get_status (stream->output); }
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_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_int_status_t _cairo_type3_glyph_surface_intersect_clip_path (void *abstract_surface, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias) { cairo_type3_glyph_surface_t *surface = abstract_surface; if (path == NULL) { _cairo_output_stream_printf (surface->stream, "Q q\n"); return CAIRO_STATUS_SUCCESS; } return _cairo_pdf_operators_clip (&surface->pdf_operators, path, fill_rule); }