cairo-render-context.cpp revision aeaafdeb5c9081ecbe43e77cdad9660372057e30
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh/** \file
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * Rendering with Cairo.
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh */
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh/*
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * Author:
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * Miklos Erdelyi <erdelyim@gmail.com>
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * Jon A. Cruz <jon@joncruz.org>
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * Abhishek Sharma
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh *
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * Copyright (C) 2006 Miklos Erdelyi
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh *
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * Licensed under GNU GPL
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh */
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#ifdef HAVE_CONFIG_H
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh# include "config.h"
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#endif
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#ifndef PANGO_ENABLE_BACKEND
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#define PANGO_ENABLE_BACKEND
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#endif
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#ifndef PANGO_ENABLE_ENGINE
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#define PANGO_ENABLE_ENGINE
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#endif
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#include <signal.h>
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#include <errno.h>
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#include <2geom/pathvector.h>
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#include <glib.h>
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#include <glibmm/i18n.h>
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#include "display/drawing.h"
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#include "display/curve.h"
fba63a357654d8b3e84c60007e40aa698cd45d19miklosh#include "display/canvas-bpath.h"
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#include "display/cairo-utils.h"
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#include "sp-item.h"
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#include "sp-item-group.h"
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#include "style.h"
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#include "sp-linear-gradient.h"
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#include "sp-radial-gradient.h"
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#include "sp-pattern.h"
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#include "sp-mask.h"
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#include "sp-clippath.h"
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#include "util/units.h"
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#ifdef WIN32
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#include "libnrtype/FontFactory.h" // USE_PANGO_WIN32
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#endif
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#include "cairo-render-context.h"
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#include "cairo-renderer.h"
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#include "extension/system.h"
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#include "io/sys.h"
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#include "svg/stringstream.h"
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#include <cairo.h>
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh// include support for only the compiled-in surface types
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#ifdef CAIRO_HAS_PDF_SURFACE
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#include <cairo-pdf.h>
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#endif
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#ifdef CAIRO_HAS_PS_SURFACE
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#include <cairo-ps.h>
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#endif
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#ifdef CAIRO_HAS_FT_FONT
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#include <cairo-ft.h>
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#endif
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#ifdef CAIRO_HAS_WIN32_FONT
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#include <cairo-win32.h>
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#include <pango/pangowin32.h>
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#endif
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#include <pango/pangofc-fontmap.h>
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh//#define TRACE(_args) g_printf _args
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh//#define TRACE(_args) g_message _args
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#define TRACE(_args)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh//#define TEST(_args) _args
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#define TEST(_args)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
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;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh};
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshstruct SPMaskView {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh SPMaskView *next;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh unsigned int key;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh Inkscape::DrawingItem *arenaitem;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh Geom::OptRect bbox;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh};*/
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshnamespace Inkscape {
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshnamespace Extension {
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshnamespace Internal {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshstatic cairo_status_t _write_callback(void *closure, const unsigned char *data, unsigned int length);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshCairoRenderContext::CairoRenderContext(CairoRenderer *parent) :
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _dpi(72),
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _pdf_level(1),
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _ps_level(1),
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _eps(false),
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _is_texttopath(FALSE),
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _is_omittext(FALSE),
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _is_filtertobitmap(FALSE),
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _bitmapresolution(72),
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _stream(NULL),
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _is_valid(FALSE),
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _vector_based_target(FALSE),
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _cr(NULL), // Cairo context
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _surface(NULL),
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _target(CAIRO_SURFACE_TYPE_IMAGE),
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _target_format(CAIRO_FORMAT_ARGB32),
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _layout(NULL),
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _state(NULL),
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _renderer(parent),
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _render_mode(RENDER_MODE_NORMAL),
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _clip_mode(CLIP_MODE_MASK),
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _omittext_state(EMPTY)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh font_table = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, font_data_free);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh}
fba63a357654d8b3e84c60007e40aa698cd45d19miklosh
fba63a357654d8b3e84c60007e40aa698cd45d19mikloshCairoRenderContext::~CairoRenderContext(void)
fba63a357654d8b3e84c60007e40aa698cd45d19miklosh{
fba63a357654d8b3e84c60007e40aa698cd45d19miklosh if(font_table != NULL) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh g_hash_table_remove_all(font_table);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh }
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if (_cr) cairo_destroy(_cr);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if (_surface) cairo_surface_destroy(_surface);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if (_layout) g_object_unref(_layout);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh}
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshvoid CairoRenderContext::font_data_free(gpointer data)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh cairo_font_face_t *font_face = (cairo_font_face_t *)data;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (font_face) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_font_face_destroy(font_face);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh}
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshCairoRenderer* CairoRenderContext::getRenderer(void) const
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return _renderer;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh}
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshCairoRenderState* CairoRenderContext::getCurrentState(void) const
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return _state;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh}
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshCairoRenderState* CairoRenderContext::getParentState(void) const
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh // if this is the root node just return it
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if (g_slist_length(_state_stack) == 1) {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return _state;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh } else {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return static_cast<CairoRenderState *>(g_slist_nth_data(_state_stack, 1));
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh }
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh}
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshvoid CairoRenderContext::setStateForStyle(SPStyle const *style)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh{
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
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if (style->fill.isPaintserver() || style->stroke.isPaintserver())
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _state->merge_opacity = FALSE;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh // disable rendering of opacity if there's a stroke on the fill
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if (_state->merge_opacity
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh && !style->fill.isNone()
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh && !style->stroke.isNone())
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _state->merge_opacity = FALSE;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh}
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh/**
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * \brief Creates a new render context which will be compatible with the given context's Cairo surface
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh *
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * \param width width of the surface to be created
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * \param height height of the surface to be created
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh */
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshCairoRenderContext*
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshCairoRenderContext::cloneMe(double width, double height) const
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh g_assert( _is_valid );
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh g_assert( width > 0.0 && height > 0.0 );
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh CairoRenderContext *new_context = _renderer->createContext();
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_surface_t *surface = cairo_surface_create_similar(cairo_get_target(_cr), CAIRO_CONTENT_COLOR_ALPHA,
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh (int)ceil(width), (int)ceil(height));
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh new_context->_cr = cairo_create(surface);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh new_context->_surface = surface;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh new_context->_width = width;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh new_context->_height = height;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh new_context->_is_valid = TRUE;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return new_context;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh}
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshCairoRenderContext* CairoRenderContext::cloneMe(void) const
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh g_assert( _is_valid );
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return cloneMe(_width, _height);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh}
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshbool CairoRenderContext::setImageTarget(cairo_format_t format)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh // format cannot be set on an already initialized surface
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if (_is_valid)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return false;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh switch (format) {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh case CAIRO_FORMAT_ARGB32:
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh case CAIRO_FORMAT_RGB24:
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh case CAIRO_FORMAT_A8:
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh case CAIRO_FORMAT_A1:
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _target_format = format;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _target = CAIRO_SURFACE_TYPE_IMAGE;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return true;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh break;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh default:
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh break;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh }
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return false;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh}
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshbool CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#ifndef CAIRO_HAS_PDF_SURFACE
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return false;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#else
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _target = CAIRO_SURFACE_TYPE_PDF;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _vector_based_target = TRUE;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#endif
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh FILE *osf = NULL;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh FILE *osp = NULL;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh gsize bytesRead = 0;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh gsize bytesWritten = 0;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh GError *error = NULL;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh gchar *local_fn = g_filename_from_utf8(utf8_fn,
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh -1, &bytesRead, &bytesWritten, &error);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh gchar const *fn = local_fn;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
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 */
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if (fn != NULL) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (*fn == '|') {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh fn += 1;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh while (isspace(*fn)) fn += 1;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#ifndef WIN32
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh osp = popen(fn, "w");
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#else
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh osp = _popen(fn, "w");
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#endif
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if (!osp) {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh fprintf(stderr, "inkscape: popen(%s): %s\n",
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh fn, strerror(errno));
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return false;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh }
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _stream = osp;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh } else if (*fn == '>') {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh fn += 1;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh while (isspace(*fn)) fn += 1;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh Inkscape::IO::dump_fopen_call(fn, "K");
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh osf = Inkscape::IO::fopen_utf8name(fn, "w+");
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if (!osf) {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh fprintf(stderr, "inkscape: fopen(%s): %s\n",
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh fn, strerror(errno));
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return false;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _stream = osf;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh } else {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh /* put cwd stuff in here */
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh gchar *qn = ( *fn
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh : g_strdup("lpr") );
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#ifndef WIN32
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh osp = popen(qn, "w");
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#else
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh osp = _popen(qn, "w");
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#endif
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if (!osp) {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh fprintf(stderr, "inkscape: popen(%s): %s\n",
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh qn, strerror(errno));
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return false;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh }
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh g_free(qn);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _stream = osp;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh }
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh }
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh g_free(local_fn);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if (_stream) {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh /* fixme: this is kinda icky */
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#if !defined(_WIN32) && !defined(__WIN32__)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh (void) signal(SIGPIPE, SIG_IGN);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#endif
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return true;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh}
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshbool CairoRenderContext::setPsTarget(gchar const *utf8_fn)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#ifndef CAIRO_HAS_PS_SURFACE
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return false;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#else
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _target = CAIRO_SURFACE_TYPE_PS;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _vector_based_target = TRUE;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#endif
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh FILE *osf = NULL;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh FILE *osp = NULL;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh gsize bytesRead = 0;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh gsize bytesWritten = 0;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh GError *error = NULL;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh gchar *local_fn = g_filename_from_utf8(utf8_fn,
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh -1, &bytesRead, &bytesWritten, &error);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh gchar const *fn = local_fn;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
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 */
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (fn != NULL) {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if (*fn == '|') {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh fn += 1;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh while (isspace(*fn)) fn += 1;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#ifndef WIN32
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh osp = popen(fn, "w");
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#else
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh osp = _popen(fn, "w");
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#endif
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (!osp) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh fprintf(stderr, "inkscape: popen(%s): %s\n",
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh fn, strerror(errno));
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return false;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh }
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _stream = osp;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh } else if (*fn == '>') {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh fn += 1;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh while (isspace(*fn)) fn += 1;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh Inkscape::IO::dump_fopen_call(fn, "K");
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh osf = Inkscape::IO::fopen_utf8name(fn, "w+");
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (!osf) {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh fprintf(stderr, "inkscape: fopen(%s): %s\n",
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh fn, strerror(errno));
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return false;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh }
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _stream = osf;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh } else {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh /* put cwd stuff in here */
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh gchar *qn = ( *fn
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh : g_strdup("lpr") );
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#ifndef WIN32
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh osp = popen(qn, "w");
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#else
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh osp = _popen(qn, "w");
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#endif
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if (!osp) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh fprintf(stderr, "inkscape: popen(%s): %s\n",
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh qn, strerror(errno));
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return false;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh g_free(qn);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _stream = osp;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh g_free(local_fn);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if (_stream) {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh /* fixme: this is kinda icky */
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#if !defined(_WIN32) && !defined(__WIN32__)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh (void) signal(SIGPIPE, SIG_IGN);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#endif
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh }
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return true;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh}
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshvoid CairoRenderContext::setPSLevel(unsigned int level)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _ps_level = level;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh}
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshvoid CairoRenderContext::setEPS(bool eps)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _eps = eps;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh}
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshunsigned int CairoRenderContext::getPSLevel(void)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return _ps_level;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh}
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshvoid CairoRenderContext::setPDFLevel(unsigned int level)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _pdf_level = level;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh}
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshvoid CairoRenderContext::setTextToPath(bool texttopath)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh{
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _is_texttopath = texttopath;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh}
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshvoid CairoRenderContext::setOmitText(bool omittext)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh{
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _is_omittext = omittext;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh}
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshbool CairoRenderContext::getOmitText(void)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return _is_omittext;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh}
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshvoid CairoRenderContext::setFilterToBitmap(bool filtertobitmap)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _is_filtertobitmap = filtertobitmap;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh}
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshbool CairoRenderContext::getFilterToBitmap(void)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return _is_filtertobitmap;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh}
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshvoid CairoRenderContext::setBitmapResolution(int resolution)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh{
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _bitmapresolution = resolution;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh}
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshint CairoRenderContext::getBitmapResolution(void)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return _bitmapresolution;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh}
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshcairo_surface_t*
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshCairoRenderContext::getSurface(void)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh g_assert( _is_valid );
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return _surface;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh}
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshbool
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshCairoRenderContext::saveAsPng(const char *file_name)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if (status)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return false;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh else
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return true;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh}
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshvoid
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshCairoRenderContext::setRenderMode(CairoRenderMode mode)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh switch (mode) {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh case RENDER_MODE_NORMAL:
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh case RENDER_MODE_CLIP:
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _render_mode = mode;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh break;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh default:
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _render_mode = RENDER_MODE_NORMAL;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh break;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh }
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh}
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshCairoRenderContext::CairoRenderMode
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshCairoRenderContext::getRenderMode(void) const
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return _render_mode;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh}
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshvoid
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshCairoRenderContext::setClipMode(CairoClipMode mode)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh switch (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.
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _clip_mode = mode;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh break;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh default:
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _clip_mode = CLIP_MODE_PATH;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh break;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh }
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh}
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshCairoRenderContext::CairoClipMode
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshCairoRenderContext::getClipMode(void) const
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh{
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return _clip_mode;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh}
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshCairoRenderState* CairoRenderContext::_createState(void)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh CairoRenderState *state = static_cast<CairoRenderState*>(g_try_malloc(sizeof(CairoRenderState)));
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh g_assert( state != NULL );
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh state->has_filtereffect = FALSE;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh state->merge_opacity = TRUE;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh state->opacity = 1.0;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh state->need_layer = FALSE;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh state->has_overflow = FALSE;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh state->parent_has_userspace = FALSE;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh state->clip_path = NULL;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh state->mask = NULL;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return state;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh}
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshvoid CairoRenderContext::pushLayer(void)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh{
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh g_assert( _is_valid );
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh TRACE(("--pushLayer\n"));
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh cairo_push_group(_cr);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // clear buffer
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if (!_vector_based_target) {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh cairo_save(_cr);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh cairo_paint(_cr);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh cairo_restore(_cr);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh }
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh}
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshvoid
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshCairoRenderContext::popLayer(void)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh g_assert( _is_valid );
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh float opacity = _state->opacity;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh TRACE(("--popLayer w/ opacity %f\n", opacity));
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh /*
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 Notes:
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.
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh */
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh SPClipPath *clip_path = _state->clip_path;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh SPMask *mask = _state->mask;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (clip_path || mask) {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh CairoRenderContext *clip_ctx = 0;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh cairo_surface_t *clip_mask = 0;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // Apply any clip path first
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (clip_path) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh TRACE((" Applying clip\n"));
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if (_render_mode == RENDER_MODE_CLIP)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh mask = NULL; // disable mask when performing nested clipping
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if (_vector_based_target) {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh setClipMode(CLIP_MODE_PATH); // Vector
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if (!mask) {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh cairo_pop_group_to_source(_cr);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _renderer->applyClipPath(this, clip_path); // Uses cairo_clip()
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if (opacity == 1.0)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh cairo_paint(_cr);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh else
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh cairo_paint_with_alpha(_cr, opacity);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh } else {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh // the clipPath will be applied before masking
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh } else {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // setup a new rendering context
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh clip_ctx = _renderer->createContext();
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh clip_ctx->setImageTarget(CAIRO_FORMAT_A8);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh clip_ctx->setClipMode(CLIP_MODE_MASK); // Raster
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.
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if (!clip_ctx->setupSurface(_width, _height)) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh TRACE(("clip: setupSurface failed\n"));
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh _renderer->destroyContext(clip_ctx);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // clear buffer
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh cairo_save(clip_ctx->_cr);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh cairo_set_operator(clip_ctx->_cr, CAIRO_OPERATOR_CLEAR);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh cairo_paint(clip_ctx->_cr);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh cairo_restore(clip_ctx->_cr);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh // If a mask won't be applied set opacity too. (The clip is represented by a solid Cairo mask.)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if (!mask)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh else
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // copy over the correct CTM
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // It must be stored in item_transform of current state after pushState.
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh Geom::Affine item_transform;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (_state->parent_has_userspace)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh item_transform = getParentState()->transform * _state->item_transform;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh else
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh item_transform = _state->item_transform;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh // apply the clip path
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh clip_ctx->pushState();
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh clip_ctx->getCurrentState()->item_transform = item_transform;
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh _renderer->applyClipPath(clip_ctx, clip_path);
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh clip_ctx->popState();
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh clip_mask = clip_ctx->getSurface();
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh TEST(clip_ctx->saveAsPng("clip_mask.png"));
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh if (!mask) {
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh cairo_pop_group_to_source(_cr);
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh cairo_mask_surface(_cr, clip_mask, 0, 0);
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh _renderer->destroyContext(clip_ctx);
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh }
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh }
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh }
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh // Apply any mask second
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh if (mask) {
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh TRACE((" Applying mask\n"));
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh // create rendering context for mask
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh CairoRenderContext *mask_ctx = _renderer->createContext();
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
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 float surface_width = _width;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh float surface_height = _height;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if( _vector_based_target ) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh surface_width *= 1.25;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh surface_height *= 1.25;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (!mask_ctx->setupSurface( surface_width, surface_height )) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh TRACE(("mask: setupSurface failed\n"));
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _renderer->destroyContext(mask_ctx);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh TRACE(("mask surface: %f x %f at %i dpi\n", surface_width, surface_height, _dpi ));
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
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 cairo_fill(mask_ctx->_cr);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // set rendering mode to normal
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh setRenderMode(RENDER_MODE_NORMAL);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // copy the correct CTM to mask context
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh /*
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh if (_state->parent_has_userspace)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh mask_ctx->setTransform(getParentState()->transform);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh else
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh mask_ctx->setTransform(_state->transform);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh */
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // This is probably not correct... but it seems to do the trick.
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh mask_ctx->setTransform(_state->item_transform);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // render mask contents to mask_ctx
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _renderer->applyMask(mask_ctx, mask);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh TEST(mask_ctx->saveAsPng("mask.png"));
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // composite with clip mask
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (clip_path && _clip_mode == CLIP_MODE_MASK) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_mask_surface(mask_ctx->_cr, clip_mask, 0, 0);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _renderer->destroyContext(clip_ctx);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
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
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.
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh TRACE(("premul w/ %f\n", opacity));
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh const float coeff_r = 0.2125 / 255.0;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh const float coeff_g = 0.7154 / 255.0;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh const float coeff_b = 0.0721 / 255.0;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh for (int row = 0 ; row < height; row++) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh unsigned char *row_data = pixels + (row * stride);
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 ((*pixel & 0x0000ff00) >> 8) * coeff_g +
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh ((*pixel & 0x000000ff) ) * coeff_b );
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);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_pop_group_to_source(_cr);
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh if (_clip_mode == CLIP_MODE_PATH) {
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh // we have to do the clipping after cairo_pop_group_to_source
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh _renderer->applyClipPath(this, clip_path);
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh }
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh // apply the mask onto the layer
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh cairo_mask_surface(_cr, mask_image, 0, 0);
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh _renderer->destroyContext(mask_ctx);
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh }
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh } else {
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh // No clip path or mask
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh cairo_pop_group_to_source(_cr);
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh if (opacity == 1.0)
c4723fe0caa2096d00cb31a7d1506351ba8102dbmiklosh cairo_paint(_cr);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh else
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_paint_with_alpha(_cr, opacity);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh}
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshvoid
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshCairoRenderContext::addClipPath(Geom::PathVector const &pv, SPIEnum const *fill_rule)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh{
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh g_assert( _is_valid );
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // here it should be checked whether the current clip winding changed
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // so we could switch back to masked clipping
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (fill_rule->value == SP_WIND_RULE_EVENODD) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh } else {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh addPathVector(pv);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh}
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshvoid
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshCairoRenderContext::addClippingRect(double x, double y, double width, double height)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh{
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh g_assert( _is_valid );
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_rectangle(_cr, x, y, width, height);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_clip(_cr);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh}
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshbool
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshCairoRenderContext::setupSurface(double width, double height)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh{
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // Is the surface already set up?
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (_is_valid)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return true;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (_vector_based_target && _stream == NULL)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return false;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _width = width;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _height = height;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh Inkscape::SVGOStringStream os_bbox;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh Inkscape::SVGOStringStream os_pagebbox;
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
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_surface_t *surface = NULL;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_matrix_t ctm;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_matrix_init_identity (&ctm);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh switch (_target) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh case CAIRO_SURFACE_TYPE_IMAGE:
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh break;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#ifdef CAIRO_HAS_PDF_SURFACE
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh case CAIRO_SURFACE_TYPE_PDF:
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 break;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#endif
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#ifdef CAIRO_HAS_PS_SURFACE
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh case CAIRO_SURFACE_TYPE_PS:
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 return FALSE;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
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#endif
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh break;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#endif
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh default:
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return false;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh break;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return _finishSurfaceSetup (surface, &ctm);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh}
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
d0b0b05662fc5cb6d734420166562b4d84dcbc75mikloshbool
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshCairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector, cairo_matrix_t *ctm)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh{
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (_is_valid || !surface)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return false;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _vector_based_target = is_vector;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh bool ret = _finishSurfaceSetup (surface, ctm);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (ret)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_surface_reference (surface);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return ret;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh}
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshbool
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshCairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface, cairo_matrix_t *ctm)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh{
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if(surface == NULL) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return false;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return false;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _cr = cairo_create(surface);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if(CAIRO_STATUS_SUCCESS != cairo_status(_cr)) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return false;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (ctm)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_set_matrix(_cr, ctm);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _surface = surface;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (_vector_based_target) {
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 cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_rectangle(_cr, 0, 0, _width, _height);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_fill(_cr);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _is_valid = TRUE;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return true;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh}
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshbool
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshCairoRenderContext::finish(void)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh{
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh g_assert( _is_valid );
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (_vector_based_target)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_show_page(_cr);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_destroy(_cr);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_surface_finish(_surface);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_status_t status = cairo_surface_status(_surface);
d0b0b05662fc5cb6d734420166562b4d84dcbc75miklosh cairo_surface_destroy(_surface);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _cr = NULL;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _surface = NULL;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (_layout)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh g_object_unref(_layout);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _is_valid = FALSE;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (_vector_based_target && _stream) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh /* Flush stream to be sure. */
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh (void) fflush(_stream);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh fclose(_stream);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _stream = NULL;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (status == CAIRO_STATUS_SUCCESS)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return true;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh else
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return false;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh}
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshvoid
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshCairoRenderContext::transform(Geom::Affine const &transform)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh{
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh g_assert( _is_valid );
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_matrix_t matrix;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _initCairoMatrix(&matrix, transform);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_transform(_cr, &matrix);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // store new CTM
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _state->transform = getTransform();
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh}
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshvoid
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshCairoRenderContext::setTransform(Geom::Affine const &transform)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh{
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh g_assert( _is_valid );
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_matrix_t matrix;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _initCairoMatrix(&matrix, transform);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_set_matrix(_cr, &matrix);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _state->transform = transform;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh}
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshGeom::Affine CairoRenderContext::getTransform() const
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh{
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh g_assert( _is_valid );
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_matrix_t ctm;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_get_matrix(_cr, &ctm);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh Geom::Affine ret;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh ret[0] = ctm.xx;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh ret[1] = ctm.yx;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh ret[2] = ctm.xy;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh ret[3] = ctm.yy;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh ret[4] = ctm.x0;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh ret[5] = ctm.y0;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return ret;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh}
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshGeom::Affine CairoRenderContext::getParentTransform() const
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh{
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh g_assert( _is_valid );
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh CairoRenderState *parent_state = getParentState();
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return parent_state->transform;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh}
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshvoid CairoRenderContext::pushState(void)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh{
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh g_assert( _is_valid );
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_save(_cr);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh CairoRenderState *new_state = _createState();
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // copy current state's transform
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh new_state->transform = _state->transform;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _state_stack = g_slist_prepend(_state_stack, new_state);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _state = new_state;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh}
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshvoid CairoRenderContext::popState(void)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh{
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh g_assert( _is_valid );
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_restore(_cr);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh g_free(_state_stack->data);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _state_stack = g_slist_remove_link(_state_stack, _state_stack);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _state = static_cast<CairoRenderState*>(_state_stack->data);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh g_assert( g_slist_length(_state_stack) > 0 );
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh}
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshstatic bool pattern_hasItemChildren(SPPattern *pat)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh{
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh bool hasItems = false;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh for ( SPObject *child = pat->firstChild() ; child && !hasItems; child = child->getNext() ) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (SP_IS_ITEM (child)) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh hasItems = true;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return hasItems;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh}
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshcairo_pattern_t*
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshCairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, Geom::OptRect const &pbox)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh{
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh g_assert( SP_IS_PATTERN(paintserver) );
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh SPPattern *pat = SP_PATTERN (paintserver);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh Geom::Affine ps2user, pcs2dev;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh ps2user = Geom::identity();
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh pcs2dev = Geom::identity();
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh double x = pattern_x(pat);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh double y = pattern_y(pat);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh double width = pattern_width(pat);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh double height = pattern_height(pat);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh double bbox_width_scaler;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh double bbox_height_scaler;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh TRACE(("%f x %f pattern\n", width, height));
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
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 bbox_width_scaler = pbox->width();
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh bbox_height_scaler = pbox->height();
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh ps2user[4] = x * bbox_width_scaler + pbox->left();
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh ps2user[5] = y * bbox_height_scaler + pbox->top();
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh } else {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh bbox_width_scaler = 1.0;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh bbox_height_scaler = 1.0;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh ps2user[4] = x;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh ps2user[5] = y;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // apply pattern transformation
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh Geom::Affine pattern_transform(pattern_patternTransform(pat));
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh ps2user *= pattern_transform;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh Geom::Point ori (ps2user[4], ps2user[5]);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // create pattern contents coordinate system
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (pat->viewBox_set) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh Geom::Rect view_box = *pattern_viewBox(pat);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh double x, y, w, h;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh x = 0;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh y = 0;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh w = width * bbox_width_scaler;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh h = height * bbox_height_scaler;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh pcs2dev[0] = w / view_box.width();
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh pcs2dev[3] = h / view_box.height();
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh pcs2dev[4] = x - view_box.left() * pcs2dev[0];
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh pcs2dev[5] = y - view_box.top() * pcs2dev[3];
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh pcs2dev[0] = pbox->width();
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh pcs2dev[3] = pbox->height();
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // Calculate the size of the surface which has to be created
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#define SUBPIX_SCALE 100
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
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 pcs2dev *= Geom::Scale(SUBPIX_SCALE,SUBPIX_SCALE);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh ps2user *= Geom::Scale(1.0/SUBPIX_SCALE,1.0/SUBPIX_SCALE);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // despite scaling up/down by subpixel scaler, the origin point of the pattern must be the same
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh ps2user[4] = ori[Geom::X];
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh ps2user[5] = ori[Geom::Y];
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh pattern_ctx->setTransform(pcs2dev);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh pattern_ctx->pushState();
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // create drawing and group
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh Inkscape::Drawing drawing;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh unsigned dkey = SPItem::display_key_new(1);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
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 if (SP_IS_ITEM(child)) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh SP_ITEM(child)->invoke_show(drawing, dkey, SP_ITEM_REFERENCE_FLAGS);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _renderer->renderItem(pattern_ctx, SP_ITEM(child));
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh break; // do not go further up the chain if children are found
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh pattern_ctx->popState();
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // setup a cairo_pattern_t
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh TEST(pattern_ctx->saveAsPng("pattern.png"));
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // set pattern transformation
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_matrix_t pattern_matrix;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh _initCairoMatrix(&pattern_matrix, ps2user);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_matrix_invert(&pattern_matrix);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_pattern_set_matrix(result, &pattern_matrix);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh delete pattern_ctx;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
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 if (SP_IS_ITEM(child)) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh SP_ITEM(child)->invoke_hide(dkey);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh break; // do not go further up the chain if children are found
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return result;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh}
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshcairo_pattern_t*
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshCairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh Geom::OptRect const &pbox, float alpha)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh{
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_pattern_t *pattern = NULL;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh bool apply_bbox2user = FALSE;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (SP_IS_LINEARGRADIENT (paintserver)) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh SP_GRADIENT(lg)->ensureVector(); // when exporting from commandline, vector is not built
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh Geom::Point p1 (lg->x1.computed, lg->y1.computed);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh Geom::Point p2 (lg->x2.computed, lg->y2.computed);
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 p1 *= bbox2user;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh p2 *= bbox2user;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // create linear gradient pattern
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh pattern = cairo_pattern_create_linear(p1[Geom::X], p1[Geom::Y], p2[Geom::X], p2[Geom::Y]);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // add stops
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh float rgb[3];
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 }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh } else if (SP_IS_RADIALGRADIENT (paintserver)) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh SP_GRADIENT(rg)->ensureVector(); // when exporting from commandline, vector is not built
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh Geom::Point c (rg->cx.computed, rg->cy.computed);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh Geom::Point f (rg->fx.computed, rg->fy.computed);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh double r = rg->r.computed;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (pbox && SP_GRADIENT(rg)->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh apply_bbox2user = true;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
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
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // add stops
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh float rgb[3];
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 }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh } else if (SP_IS_PATTERN (paintserver)) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh pattern = _createPatternPainter(paintserver, pbox);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh } else {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return NULL;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (pattern && SP_IS_GRADIENT(paintserver)) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh SPGradient *g = SP_GRADIENT(paintserver);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // set extend type
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh SPGradientSpread spread = g->fetchSpread();
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh switch (spread) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh case SP_GRADIENT_SPREAD_REPEAT: {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh break;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh case SP_GRADIENT_SPREAD_REFLECT: { // not supported by cairo-pdf yet
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh break;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh case SP_GRADIENT_SPREAD_PAD: { // not supported by cairo-pdf yet
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh break;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh default: {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh break;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_matrix_t pattern_matrix;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (g->gradientTransform_set) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // apply gradient transformation
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_matrix_init(&pattern_matrix,
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh g->gradientTransform[0], g->gradientTransform[1],
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh g->gradientTransform[2], g->gradientTransform[3],
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh g->gradientTransform[4], g->gradientTransform[5]);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh } else {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_matrix_init_identity (&pattern_matrix);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (apply_bbox2user) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // convert to userspace
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_matrix_t bbox2user;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_matrix_init (&bbox2user, pbox->width(), 0, 0, pbox->height(), pbox->left(), pbox->top());
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh cairo_matrix_invert(&pattern_matrix); // because Cairo expects a userspace->patternspace matrix
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh cairo_pattern_set_matrix(pattern, &pattern_matrix);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh }
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return pattern;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh}
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshvoid
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshCairoRenderContext::_setFillStyle(SPStyle const *const style, Geom::OptRect const &pbox)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh g_return_if_fail( !style->fill.set
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh || style->fill.isColor()
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh || style->fill.isPaintserver() );
float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
if (_state->merge_opacity) {
alpha *= _state->opacity;
TRACE(("merged op=%f\n", alpha));
}
if (style->fill.isColor()) {
float rgb[3];
sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
} else if (!style->fill.set) { // unset fill is black
cairo_set_source_rgba(_cr, 0, 0, 0, alpha);
} else {
g_assert( style->fill.isPaintserver()
|| SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
|| SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
if (pattern) {
cairo_set_source(_cr, pattern);
cairo_pattern_destroy(pattern);
}
}
}
void
CairoRenderContext::_setStrokeStyle(SPStyle const *style, Geom::OptRect const &pbox)
{
float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
if (_state->merge_opacity)
alpha *= _state->opacity;
if (style->stroke.isColor()) {
float rgb[3];
sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
} else {
g_assert( style->stroke.isPaintserver()
|| SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
|| SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
if (pattern) {
cairo_set_source(_cr, pattern);
cairo_pattern_destroy(pattern);
}
}
if (style->stroke_dash.n_dash &&
style->stroke_dash.dash )
{
cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
} else {
cairo_set_dash(_cr, NULL, 0, 0.0); // disable dashing
}
cairo_set_line_width(_cr, style->stroke_width.computed);
// set line join type
cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
switch (style->stroke_linejoin.computed) {
case SP_STROKE_LINEJOIN_MITER:
join = CAIRO_LINE_JOIN_MITER;
break;
case SP_STROKE_LINEJOIN_ROUND:
join = CAIRO_LINE_JOIN_ROUND;
break;
case SP_STROKE_LINEJOIN_BEVEL:
join = CAIRO_LINE_JOIN_BEVEL;
break;
}
cairo_set_line_join(_cr, join);
// set line cap type
cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
switch (style->stroke_linecap.computed) {
case SP_STROKE_LINECAP_BUTT:
cap = CAIRO_LINE_CAP_BUTT;
break;
case SP_STROKE_LINECAP_ROUND:
cap = CAIRO_LINE_CAP_ROUND;
break;
case SP_STROKE_LINECAP_SQUARE:
cap = CAIRO_LINE_CAP_SQUARE;
break;
}
cairo_set_line_cap(_cr, cap);
cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
}
void
CairoRenderContext::_prepareRenderGraphic()
{
// Only PDFLaTeX supports importing a single page of a graphics file,
// so only PDF backend gets interleaved text/graphics
if (_is_omittext && _target == CAIRO_SURFACE_TYPE_PDF) {
if (_omittext_state == NEW_PAGE_ON_GRAPHIC)
cairo_show_page(_cr);
_omittext_state = GRAPHIC_ON_TOP;
}
}
void
CairoRenderContext::_prepareRenderText()
{
// Only PDFLaTeX supports importing a single page of a graphics file,
// so only PDF backend gets interleaved text/graphics
if (_is_omittext && _target == CAIRO_SURFACE_TYPE_PDF) {
if (_omittext_state == GRAPHIC_ON_TOP)
_omittext_state = NEW_PAGE_ON_GRAPHIC;
}
}
bool
CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, Geom::OptRect const &pbox)
{
g_assert( _is_valid );
_prepareRenderGraphic();
if (_render_mode == RENDER_MODE_CLIP) {
if (_clip_mode == CLIP_MODE_PATH) {
addClipPath(pathv, &style->fill_rule);
} else {
setPathVector(pathv);
if (style->fill_rule.computed == SP_WIND_RULE_EVENODD) {
cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
} else {
cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
}
cairo_fill(_cr);
TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
}
return true;
}
bool no_fill = style->fill.isNone() || style->fill_opacity.value == 0;
bool no_stroke = style->stroke.isNone() || style->stroke_width.computed < 1e-9 ||
style->stroke_opacity.value == 0;
if (no_fill && no_stroke)
return true;
bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
if (!need_layer)
cairo_save(_cr);
else
pushLayer();
if (!no_fill) {
_setFillStyle(style, pbox);
setPathVector(pathv);
if (style->fill_rule.computed == SP_WIND_RULE_EVENODD) {
cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
} else {
cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
}
if (no_stroke)
cairo_fill(_cr);
else
cairo_fill_preserve(_cr);
}
if (!no_stroke) {
_setStrokeStyle(style, pbox);
if (no_fill)
setPathVector(pathv);
cairo_stroke(_cr);
}
if (need_layer)
popLayer();
else
cairo_restore(_cr);
return true;
}
bool CairoRenderContext::renderImage(Inkscape::Pixbuf *pb,
Geom::Affine const &image_transform, SPStyle const *style)
{
g_assert( _is_valid );
if (_render_mode == RENDER_MODE_CLIP) {
return true;
}
_prepareRenderGraphic();
int w = pb->width();
int h = pb->height();
// TODO: reenable merge_opacity if useful
float opacity = _state->opacity;
cairo_surface_t *image_surface = pb->getSurfaceRaw();
if (cairo_surface_status(image_surface)) {
TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
return false;
}
cairo_save(_cr);
// scaling by width & height is not needed because it will be done by Cairo
transform(image_transform);
cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
// set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
if (_vector_based_target) {
cairo_new_path(_cr);
cairo_rectangle(_cr, 0, 0, w, h);
cairo_clip(_cr);
}
// Cairo filter method will be mapped to PS/PDF 'interpolate' true/false).
// 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
// style.h/style.cpp
switch (style->image_rendering.computed) {
case SP_CSS_COLOR_RENDERING_AUTO:
// Do nothing
break;
case SP_CSS_COLOR_RENDERING_OPTIMIZEQUALITY:
cairo_pattern_set_filter(cairo_get_source(_cr), CAIRO_FILTER_BEST );
break;
case SP_CSS_COLOR_RENDERING_OPTIMIZESPEED:
default:
cairo_pattern_set_filter(cairo_get_source(_cr), CAIRO_FILTER_NEAREST );
break;
}
}
cairo_paint_with_alpha(_cr, opacity);
cairo_restore(_cr);
return true;
}
#define GLYPH_ARRAY_SIZE 64
// TODO investigate why the font is being ignored:
unsigned int CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont * /*font*/, std::vector<CairoGlyphInfo> const &glyphtext, bool path)
{
cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
cairo_glyph_t *glyphs = glyph_array;
unsigned int num_glyphs = glyphtext.size();
if (num_glyphs > GLYPH_ARRAY_SIZE) {
glyphs = (cairo_glyph_t*)g_try_malloc(sizeof(cairo_glyph_t) * num_glyphs);
if(glyphs == NULL) {
g_warning("CairorenderContext::_showGlyphs: can not allocate memory for %d glyphs.", num_glyphs);
return 0;
}
}
unsigned int num_invalid_glyphs = 0;
unsigned int i = 0; // is a counter for indexing the glyphs array, only counts the valid glyphs
for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; ++it_info) {
// skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
// or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
TRACE(("INVALID GLYPH found\n"));
g_message("Invalid glyph found, continuing...");
num_invalid_glyphs++;
continue;
}
glyphs[i].index = it_info->index;
glyphs[i].x = it_info->x;
glyphs[i].y = it_info->y;
i++;
}
if (path) {
cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
} else {
cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
}
if (num_glyphs > GLYPH_ARRAY_SIZE) {
g_free(glyphs);
}
return num_glyphs - num_invalid_glyphs;
}
bool
CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Affine const &font_matrix,
std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
{
_prepareRenderText();
if (_is_omittext)
return true;
// create a cairo_font_face from PangoFont
double size = style->font_size.computed; /// \fixme why is this variable never used?
gpointer fonthash = (gpointer)font;
cairo_font_face_t *font_face = (cairo_font_face_t *)g_hash_table_lookup(font_table, fonthash);
FcPattern *fc_pattern = NULL;
#ifdef USE_PANGO_WIN32
# ifdef CAIRO_HAS_WIN32_FONT
LOGFONTA *lfa = pango_win32_font_logfont(font);
LOGFONTW lfw;
ZeroMemory(&lfw, sizeof(LOGFONTW));
memcpy(&lfw, lfa, sizeof(LOGFONTA));
MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
if(font_face == NULL) {
font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
g_hash_table_insert(font_table, fonthash, font_face);
}
# endif
#else
# ifdef CAIRO_HAS_FT_FONT
PangoFcFont *fc_font = PANGO_FC_FONT(font);
fc_pattern = fc_font->font_pattern;
if(font_face == NULL) {
font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
g_hash_table_insert(font_table, fonthash, font_face);
}
# endif
#endif
cairo_save(_cr);
cairo_set_font_face(_cr, font_face);
if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
size = 12.0;
// set the given font matrix
cairo_matrix_t matrix;
_initCairoMatrix(&matrix, font_matrix);
cairo_set_font_matrix(_cr, &matrix);
if (_render_mode == RENDER_MODE_CLIP) {
if (_clip_mode == CLIP_MODE_MASK) {
if (style->fill_rule.computed == SP_WIND_RULE_EVENODD) {
cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
} else {
cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
}
_showGlyphs(_cr, font, glyphtext, FALSE);
} else {
// just add the glyph paths to the current context
_showGlyphs(_cr, font, glyphtext, TRUE);
}
} else {
bool fill = false, stroke = false, have_path = false;
if (style->fill.isColor() || style->fill.isPaintserver()) {
fill = true;
}
if (style->stroke.isColor() || style->stroke.isPaintserver()) {
stroke = true;
}
if (fill) {
_setFillStyle(style, Geom::OptRect());
if (_is_texttopath) {
_showGlyphs(_cr, font, glyphtext, true);
have_path = true;
if (stroke) cairo_fill_preserve(_cr);
else cairo_fill(_cr);
} else {
_showGlyphs(_cr, font, glyphtext, false);
}
}
if (stroke) {
_setStrokeStyle(style, Geom::OptRect());
if (!have_path) _showGlyphs(_cr, font, glyphtext, true);
cairo_stroke(_cr);
}
}
cairo_restore(_cr);
// if (font_face)
// cairo_font_face_destroy(font_face);
return true;
}
/* Helper functions */
void
CairoRenderContext::setPathVector(Geom::PathVector const &pv)
{
cairo_new_path(_cr);
addPathVector(pv);
}
void
CairoRenderContext::addPathVector(Geom::PathVector const &pv)
{
feed_pathvector_to_cairo(_cr, pv);
}
void
CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
{
cairo_matrix_t matrix;
cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
cairo_transform(cr, &matrix);
}
void
CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, Geom::Affine const &transform)
{
matrix->xx = transform[0];
matrix->yx = transform[1];
matrix->xy = transform[2];
matrix->yy = transform[3];
matrix->x0 = transform[4];
matrix->y0 = transform[5];
}
void
CairoRenderContext::_concatTransform(cairo_t *cr, Geom::Affine const &transform)
{
_concatTransform(cr, transform[0], transform[1],
transform[2], transform[3],
transform[4], transform[5]);
}
static cairo_status_t
_write_callback(void *closure, const unsigned char *data, unsigned int length)
{
size_t written;
FILE *file = (FILE*)closure;
written = fwrite (data, 1, length, file);
if (written == length)
return CAIRO_STATUS_SUCCESS;
else
return CAIRO_STATUS_WRITE_ERROR;
}
#include "clear-n_.h"
} /* namespace Internal */
} /* namespace Extension */
} /* namespace Inkscape */
#undef TRACE
#undef TEST
/* End of GNU GPL code */
/*
Local Variables:
mode:c++
c-file-style:"stroustrup"
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
indent-tabs-mode:nil
fill-column:99
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :