cairo-render-context.cpp revision aeaafdeb5c9081ecbe43e77cdad9660372057e30
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * Rendering with Cairo.
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * Miklos Erdelyi <erdelyim@gmail.com>
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * Jon A. Cruz <jon@joncruz.org>
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * Abhishek Sharma
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * Copyright (C) 2006 Miklos Erdelyi
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * Licensed under GNU GPL
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#include "libnrtype/FontFactory.h" // USE_PANGO_WIN32
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh// include support for only the compiled-in surface types
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh//#define TRACE(_args) g_printf _args
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh//#define TRACE(_args) g_message _args
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh//#define TEST(_args) _args
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh// FIXME: expose these from sp-clippath/mask.cpp
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh/*struct SPClipPathView {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh SPClipPathView *next;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh unsigned int key;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh Inkscape::DrawingItem *arenaitem;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh Geom::OptRect bbox;
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshstruct SPMaskView {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh SPMaskView *next;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh unsigned int key;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh Inkscape::DrawingItem *arenaitem;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh Geom::OptRect bbox;
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshstatic cairo_status_t _write_callback(void *closure, const unsigned char *data, unsigned int length);
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshCairoRenderContext::CairoRenderContext(CairoRenderer *parent) :
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh font_table = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, font_data_free);
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshvoid CairoRenderContext::font_data_free(gpointer data)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh cairo_font_face_t *font_face = (cairo_font_face_t *)data;
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshCairoRenderer* CairoRenderContext::getRenderer(void) const
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshCairoRenderState* CairoRenderContext::getCurrentState(void) const
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshCairoRenderState* CairoRenderContext::getParentState(void) const
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh // if this is the root node just return it
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return static_cast<CairoRenderState *>(g_slist_nth_data(_state_stack, 1));
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshvoid CairoRenderContext::setStateForStyle(SPStyle const *style)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh // only opacity & overflow is stored for now
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _state->opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _state->has_overflow = (style->overflow.set && style->overflow.value != SP_CSS_OVERFLOW_VISIBLE);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _state->has_filtereffect = (style->filter.set != 0) ? TRUE : FALSE;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if (style->fill.isPaintserver() || style->stroke.isPaintserver())
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh // disable rendering of opacity if there's a stroke on the fill
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * \brief Creates a new render context which will be compatible with the given context's Cairo surface
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * \param width width of the surface to be created
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * \param height height of the surface to be created
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshCairoRenderContext::cloneMe(double width, double height) const
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh CairoRenderContext *new_context = _renderer->createContext();
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_surface_t *surface = cairo_surface_create_similar(cairo_get_target(_cr), CAIRO_CONTENT_COLOR_ALPHA,
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshCairoRenderContext* CairoRenderContext::cloneMe(void) const
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshbool CairoRenderContext::setImageTarget(cairo_format_t format)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh // format cannot be set on an already initialized surface
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return false;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return true;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return false;
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshbool CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return false;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh /* TODO: Replace the below fprintf's with something that does the right thing whether in
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * return code.
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return false;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return false;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh /* put cwd stuff in here */
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return false;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh /* fixme: this is kinda icky */
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return true;
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshbool CairoRenderContext::setPsTarget(gchar const *utf8_fn)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return false;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh /* TODO: Replace the below fprintf's with something that does the right thing whether in
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * return code.
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return false;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return false;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh /* put cwd stuff in here */
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return false;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh /* fixme: this is kinda icky */
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return true;
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshvoid CairoRenderContext::setPSLevel(unsigned int level)
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshvoid CairoRenderContext::setPDFLevel(unsigned int level)
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshvoid CairoRenderContext::setTextToPath(bool texttopath)
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshvoid CairoRenderContext::setFilterToBitmap(bool filtertobitmap)
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshvoid CairoRenderContext::setBitmapResolution(int resolution)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return false;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return true;
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshCairoRenderContext::setRenderMode(CairoRenderMode mode)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh case CLIP_MODE_PATH: // Clip is rendered as a path for vector output
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh case CLIP_MODE_MASK: // Clip is rendered as a bitmap for raster output.
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshCairoRenderState* CairoRenderContext::_createState(void)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh CairoRenderState *state = static_cast<CairoRenderState*>(g_try_malloc(sizeof(CairoRenderState)));
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // clear buffer
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh At this point, the Cairo source is ready. A Cairo mask must be created if required.
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh Care must be taken of transformatons as Cairo, like PS and PDF, treats clip paths and
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh masks independently of the objects they effect while in SVG the clip paths and masks
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh are defined relative to the objects they are attached to.
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh 1. An SVG object may have both a clip path and a mask!
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh 2. An SVG clip path can be composed of an object with a clip path. This is not handled properly.
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh 3. An SVG clipped or masked object may be first drawn off the page and then translated onto
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh the page (document). This is also not handled properly.
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh 4. The code converts all SVG masks to bitmaps. This shouldn't be necessary.
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh 5. Cairo expects a mask to use only the alpha channel. SVG masks combine the RGB luminance with
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh alpha. This is handled here by doing a pixel by pixel conversion.
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // Apply any clip path first
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh mask = NULL; // disable mask when performing nested clipping
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _renderer->applyClipPath(this, clip_path); // Uses cairo_clip()
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh // the clipPath will be applied before masking
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // setup a new rendering context
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh // This code ties the clipping to the document coordinates. It doesn't allow
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh // for a clipped object intially drawn off the page and then translated onto
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // the page.
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // clear buffer
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh cairo_set_operator(clip_ctx->_cr, CAIRO_OPERATOR_CLEAR);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh // If a mask won't be applied set opacity too. (The clip is represented by a solid Cairo mask.)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // copy over the correct CTM
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // It must be stored in item_transform of current state after pushState.
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh item_transform = getParentState()->transform * _state->item_transform;
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh // apply the clip path
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh clip_ctx->getCurrentState()->item_transform = item_transform;
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh // Apply any mask second
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh // create rendering context for mask
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh CairoRenderContext *mask_ctx = _renderer->createContext();
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // Fix Me: This is a kludge. PDF and PS output is set to 72 dpi but the
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // Cairo surface is expecting the mask to be 90 dpi.
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (!mask_ctx->setupSurface( surface_width, surface_height )) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh TRACE(("mask surface: %f x %f at %i dpi\n", surface_width, surface_height, _dpi ));
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // Mask should start black, but it is created white.
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_set_source_rgba(mask_ctx->_cr, 0.0, 0.0, 0.0, 1.0);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_rectangle(mask_ctx->_cr, 0, 0, surface_width, surface_height);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // set rendering mode to normal
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // copy the correct CTM to mask context
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh if (_state->parent_has_userspace)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh mask_ctx->setTransform(getParentState()->transform);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh mask_ctx->setTransform(_state->transform);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // This is probably not correct... but it seems to do the trick.
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // render mask contents to mask_ctx
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // composite with clip mask
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_surface_t *mask_image = mask_ctx->getSurface();
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh int width = cairo_image_surface_get_width(mask_image);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh int height = cairo_image_surface_get_height(mask_image);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh int stride = cairo_image_surface_get_stride(mask_image);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh unsigned char *pixels = cairo_image_surface_get_data(mask_image);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // In SVG, the rgb channels as well as the alpha channel is used in masking.
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh // In Cairo, only the alpha channel is used thus requiring this conversion.
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh // SVG specifies that RGB be converted to alpha using luminance-to-alpha.
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh // Notes: This calculation assumes linear RGB values. VERIFY COLOR SPACE!
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh // The incoming pixel values already include alpha, fill-opacity, etc.,
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh // however, opacity must still be applied.
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh for (int i = 0 ; i < width; i++) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh guint32 *pixel = reinterpret_cast<guint32 *>(row_data) + i;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh float lum_alpha = (((*pixel & 0x00ff0000) >> 16) * coeff_r +
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // lum_alpha can be slightly greater than 1 due to rounding errors...
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // but this should be OK since it doesn't matter what the lower
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // six hexadecimal numbers of *pixel are.
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh *pixel = (guint32)(0xff000000 * lum_alpha * opacity);
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh // we have to do the clipping after cairo_pop_group_to_source
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh // apply the mask onto the layer
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh // No clip path or mask
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshCairoRenderContext::addClipPath(Geom::PathVector const &pv, SPIEnum const *fill_rule)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // here it should be checked whether the current clip winding changed
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // so we could switch back to masked clipping
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshCairoRenderContext::addClippingRect(double x, double y, double width, double height)
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshCairoRenderContext::setupSurface(double width, double height)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // Is the surface already set up?
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return true;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return false;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh os_bbox.setf(std::ios::fixed); // don't use scientific notation
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh os_pagebbox.setf(std::ios::fixed); // don't use scientific notation
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh os_bbox << "%%BoundingBox: 0 0 " << (int)ceil(width) << (int)ceil(height); // apparently, the numbers should be integers. (see bug 380501)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh os_pagebbox << "%%PageBoundingBox: 0 0 " << (int)ceil(width) << (int)ceil(height);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_pdf_surface_restrict_to_version(surface, (cairo_pdf_version_t)_pdf_level);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_ps_surface_restrict_to_level(surface, (cairo_ps_level_t)_ps_level);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_ps_surface_set_eps(surface, (cairo_bool_t) _eps);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // Cairo calculates the bounding box itself, however we want to override this. See Launchpad bug #380501
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 11, 2))
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh// cairo_ps_dsc_comment(surface, os_bbox.str().c_str());
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh// cairo_ps_dsc_begin_page(surface);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh// cairo_ps_dsc_comment(surface, os_pagebbox.str().c_str());
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return false;
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshCairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector, cairo_matrix_t *ctm)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return false;
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshCairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface, cairo_matrix_t *ctm)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return false;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return false;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return false;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_scale(_cr, Inkscape::Util::Quantity::convert(1, "px", "pt"), Inkscape::Util::Quantity::convert(1, "px", "pt"));
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // set background color on non-alpha surfaces
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // TODO: bgcolor should be derived from SPDocument
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return true;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_status_t status = cairo_surface_status(_surface);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh /* Flush stream to be sure. */
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return true;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return false;
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshCairoRenderContext::transform(Geom::Affine const &transform)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // store new CTM
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshCairoRenderContext::setTransform(Geom::Affine const &transform)
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshGeom::Affine CairoRenderContext::getTransform() const
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshGeom::Affine CairoRenderContext::getParentTransform() const
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // copy current state's transform
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _state_stack = g_slist_prepend(_state_stack, new_state);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _state_stack = g_slist_remove_link(_state_stack, _state_stack);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _state = static_cast<CairoRenderState*>(_state_stack->data);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh bool hasItems = false;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh for ( SPObject *child = pat->firstChild() ; child && !hasItems; child = child->getNext() ) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshCairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, Geom::OptRect const &pbox)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh //Geom::Affine bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // apply pattern transformation
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh Geom::Affine pattern_transform(pattern_patternTransform(pat));
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // create pattern contents coordinate system
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh double x, y, w, h;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // Calculate the size of the surface which has to be created
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // Cairo requires an integer pattern surface width/height.
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel.
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // Multiply by SUBPIX_SCALE to allow for less than a pixel precision
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh double surface_width = MAX(ceil(SUBPIX_SCALE * bbox_width_scaler * width - 0.5), 1);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh double surface_height = MAX(ceil(SUBPIX_SCALE * bbox_height_scaler * height - 0.5), 1);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh TRACE(("pattern surface size: %f x %f\n", surface_width, surface_height));
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // create new rendering context
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // adjust the size of the painted pattern to fit exactly the created surface
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // this has to be done because of the rounding to obtain an integer pattern surface width/height
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh double scale_width = surface_width / (bbox_width_scaler * width);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh double scale_height = surface_height / (bbox_height_scaler * height);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh ps2user *= Geom::Scale(1.0/SUBPIX_SCALE,1.0/SUBPIX_SCALE);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // despite scaling up/down by subpixel scaler, the origin point of the pattern must be the same
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // create drawing and group
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // show items and render them
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (pat_i && SP_IS_OBJECT(pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh for ( SPObject *child = pat_i->firstChild() ; child; child = child->getNext() ) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh SP_ITEM(child)->invoke_show(drawing, dkey, SP_ITEM_REFERENCE_FLAGS);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh break; // do not go further up the chain if children are found
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // setup a cairo_pattern_t
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // set pattern transformation
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // hide all items
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (pat_i && SP_IS_OBJECT(pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh for ( SPObject *child = pat_i->firstChild() ; child; child = child->getNext() ) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh break; // do not go further up the chain if children are found
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshCairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh SP_GRADIENT(lg)->ensureVector(); // when exporting from commandline, vector is not built
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (pbox && SP_GRADIENT(lg)->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // convert to userspace
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh Geom::Affine bbox2user(pbox->width(), 0, 0, pbox->height(), pbox->left(), pbox->top());
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // create linear gradient pattern
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh pattern = cairo_pattern_create_linear(p1[Geom::X], p1[Geom::Y], p2[Geom::X], p2[Geom::Y]);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // add stops
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh SP_GRADIENT(rg)->ensureVector(); // when exporting from commandline, vector is not built
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (pbox && SP_GRADIENT(rg)->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // create radial gradient pattern
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh pattern = cairo_pattern_create_radial(f[Geom::X], f[Geom::Y], 0, c[Geom::X], c[Geom::Y], r);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // add stops
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // set extend type
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh case SP_GRADIENT_SPREAD_REFLECT: { // not supported by cairo-pdf yet
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh case SP_GRADIENT_SPREAD_PAD: { // not supported by cairo-pdf yet
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // apply gradient transformation
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // convert to userspace
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_matrix_init (&bbox2user, pbox->width(), 0, 0, pbox->height(), pbox->left(), pbox->top());
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh cairo_matrix_invert(&pattern_matrix); // because Cairo expects a userspace->patternspace matrix
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshCairoRenderContext::_setFillStyle(SPStyle const *const style, Geom::OptRect const &pbox)
if (pattern) {
cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
if (pattern) {
case SP_STROKE_LINEJOIN_MITER:
case SP_STROKE_LINEJOIN_ROUND:
case SP_STROKE_LINEJOIN_BEVEL:
case SP_STROKE_LINECAP_BUTT:
case SP_STROKE_LINECAP_ROUND:
case SP_STROKE_LINECAP_SQUARE:
CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, Geom::OptRect const &pbox)
if (!need_layer)
pushLayer();
if (!no_fill) {
if (no_stroke)
if (!no_stroke) {
if (no_fill)
if (need_layer)
popLayer();
TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
if (_vector_based_target) {
// See cairo-pdf-surface.c
if (style) {
// See: http://www.w3.org/TR/SVG/painting.html#ImageRenderingProperty
// http://www.w3.org/TR/css4-images/#the-image-rendering
unsigned int CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont * /*font*/, std::vector<CairoGlyphInfo> const &glyphtext, bool path)
unsigned int num_invalid_glyphs = 0;
for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; ++it_info) {
if (path) {
if (_is_omittext)
#ifdef USE_PANGO_WIN32
# ifdef CAIRO_HAS_WIN32_FONT
MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
# ifdef CAIRO_HAS_FT_FONT
fill = true;
stroke = true;
if (fill) {
if (_is_texttopath) {
have_path = true;
if (stroke) {
CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
static cairo_status_t
return CAIRO_STATUS_SUCCESS;
return CAIRO_STATUS_WRITE_ERROR;
#include "clear-n_.h"