Code

BUG 284680 convert text to paths on cairo output
[inkscape.git] / src / extension / internal / cairo-render-context.cpp
index 8511973e408879bd48837090c50e07d5316b0d87..e9ac98df8e18a59c4c0bbaa80a799b11dbe20014 100644 (file)
 
 #include <signal.h>
 #include <errno.h>
-
-#include <libnr/n-art-bpath.h>
-#include <libnr/nr-matrix-ops.h>
-#include <libnr/nr-matrix-fns.h>
-#include <libnr/nr-matrix-translate-ops.h>
-#include <libnr/nr-scale-matrix-ops.h>
+#include <2geom/pathvector.h>
 
 #include <glib/gmem.h>
 
@@ -42,6 +37,7 @@
 #include "display/nr-arena-group.h"
 #include "display/curve.h"
 #include "display/canvas-bpath.h"
+#include "display/inkscape-cairo.h"
 #include "sp-item.h"
 #include "sp-item-group.h"
 #include "style.h"
@@ -50,6 +46,9 @@
 #include "sp-pattern.h"
 #include "sp-mask.h"
 #include "sp-clippath.h"
+#ifdef WIN32
+#include "libnrtype/FontFactory.h" // USE_PANGO_WIN32
+#endif
 
 #include <unit-constants.h>
 
 #endif
 
 
-#ifndef PANGO_ENABLE_BACKEND
+#ifdef CAIRO_HAS_FT_FONT
 #include <cairo-ft.h>
 #endif
+#ifdef CAIRO_HAS_WIN32_FONT
+#include <cairo-win32.h>
+#include <pango/pangowin32.h>
+#endif
 
 #include <pango/pangofc-fontmap.h>
 
@@ -104,10 +107,16 @@ static cairo_status_t _write_callback(void *closure, const unsigned char *data,
 
 CairoRenderContext::CairoRenderContext(CairoRenderer *parent) :
     _dpi(72),
+    _pdf_level(0),
+    _ps_level(1),
+    _eps(false),
+    _is_texttopath(FALSE),
+    _is_filtertobitmap(FALSE),
+    _bitmapresolution(72),
     _stream(NULL),
     _is_valid(FALSE),
     _vector_based_target(FALSE),
-    _cr(NULL),
+    _cr(NULL), // Cairo context
     _surface(NULL),
     _target(CAIRO_SURFACE_TYPE_IMAGE),
     _target_format(CAIRO_FORMAT_ARGB32),
@@ -154,6 +163,7 @@ CairoRenderContext::setStateForStyle(SPStyle const *style)
     // only opacity & overflow is stored for now
     _state->opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
     _state->has_overflow = (style->overflow.set && style->overflow.value != SP_CSS_OVERFLOW_VISIBLE);
+    _state->has_filtereffect = (style->filter.set != 0) ? TRUE : FALSE;
 
     if (style->fill.isPaintserver() || style->stroke.isPaintserver())
         _state->merge_opacity = FALSE;
@@ -384,6 +394,51 @@ CairoRenderContext::setPsTarget(gchar const *utf8_fn)
     return true;
 }
 
+void CairoRenderContext::setPSLevel(unsigned int level)
+{
+    _ps_level = level;
+}
+
+void CairoRenderContext::setEPS(bool eps)
+{
+    _eps = eps;
+}
+
+unsigned int CairoRenderContext::getPSLevel(void)
+{
+    return _ps_level;
+}
+
+void CairoRenderContext::setPDFLevel(unsigned int level)
+{
+    _pdf_level = level;
+}
+
+void CairoRenderContext::setTextToPath(bool texttopath)
+{
+    _is_texttopath = texttopath;
+}
+
+void CairoRenderContext::setFilterToBitmap(bool filtertobitmap)
+{
+    _is_filtertobitmap = filtertobitmap;
+}
+
+bool CairoRenderContext::getFilterToBitmap(void)
+{
+    return _is_filtertobitmap;
+}
+
+void CairoRenderContext::setBitmapResolution(int resolution)
+{
+    _bitmapresolution = resolution;
+}
+
+int CairoRenderContext::getBitmapResolution(void)
+{
+    return _bitmapresolution;
+}
+
 cairo_surface_t*
 CairoRenderContext::getSurface(void)
 {
@@ -426,8 +481,8 @@ void
 CairoRenderContext::setClipMode(CairoClipMode mode)
 {
     switch (mode) {
-        case CLIP_MODE_PATH:
-        case CLIP_MODE_MASK:
+        case CLIP_MODE_PATH: // Clip is rendered as a path for vector output
+        case CLIP_MODE_MASK: // Clip is rendered as a bitmap for raster output.
             _clip_mode = mode;
             break;
         default:
@@ -448,6 +503,7 @@ CairoRenderContext::_createState(void)
     CairoRenderState *state = (CairoRenderState*)g_malloc(sizeof(CairoRenderState));
     g_assert( state != NULL );
 
+    state->has_filtereffect = FALSE;
     state->merge_opacity = TRUE;
     state->opacity = 1.0;
     state->need_layer = FALSE;
@@ -482,9 +538,23 @@ CairoRenderContext::popLayer(void)
     g_assert( _is_valid );
 
     float opacity = _state->opacity;
-    TRACE(("--popLayer w/ %f\n", opacity));
+    TRACE(("--popLayer w/ opacity %f\n", opacity));
+
+    /*
+     At this point, the Cairo source is ready. A Cairo mask must be created if required.
+     Care must be taken of transformatons as Cairo, like PS and PDF, treats clip paths and
+     masks independently of the objects they effect while in SVG the clip paths and masks
+     are defined relative to the objects they are attached to.
+     Notes:
+     1. An SVG object may have both a clip path and a mask!
+     2. An SVG clip path can be composed of an object with a clip path. This is not handled properly.
+     3. An SVG clipped or masked object may be first drawn off the page and then translated onto
+        the page (document). This is also not handled properly.
+     4. The code converts all SVG masks to bitmaps. This shouldn't be necessary.
+     5. Cairo expects a mask to use only the alpha channel. SVG masks combine the RGB luminance with
+        alpha. This is handled here by doing a pixel by pixel conversion.
+    */
 
-    // apply clipPath or mask if present
     SPClipPath *clip_path = _state->clip_path;
     SPMask *mask = _state->mask;
     if (clip_path || mask) {
@@ -492,15 +562,17 @@ CairoRenderContext::popLayer(void)
         CairoRenderContext *clip_ctx = 0;
         cairo_surface_t *clip_mask = 0;
 
+        // Apply any clip path first
         if (clip_path) {
+            TRACE(("  Applying clip\n"));
             if (_render_mode == RENDER_MODE_CLIP)
                 mask = NULL;    // disable mask when performing nested clipping
 
             if (_vector_based_target) {
-                setClipMode(CLIP_MODE_PATH);
+                setClipMode(CLIP_MODE_PATH); // Vector
                 if (!mask) {
                     cairo_pop_group_to_source(_cr);
-                    _renderer->applyClipPath(this, clip_path);
+                    _renderer->applyClipPath(this, clip_path); // Uses cairo_clip()
                     if (opacity == 1.0)
                         cairo_paint(_cr);
                     else
@@ -514,9 +586,12 @@ CairoRenderContext::popLayer(void)
                 // setup a new rendering context
                 clip_ctx = _renderer->createContext();
                 clip_ctx->setImageTarget(CAIRO_FORMAT_A8);
-                clip_ctx->setClipMode(CLIP_MODE_MASK);
+                clip_ctx->setClipMode(CLIP_MODE_MASK);  // Raster
+                // This code ties the clipping to the document coordinates. It doesn't allow
+                // for a clipped object intially drawn off the page and then translated onto
+                // the page.
                 if (!clip_ctx->setupSurface(_width, _height)) {
-                    TRACE(("setupSurface failed\n"));
+                    TRACE(("clip: setupSurface failed\n"));
                     _renderer->destroyContext(clip_ctx);
                     return;
                 }
@@ -527,20 +602,23 @@ CairoRenderContext::popLayer(void)
                 cairo_paint(clip_ctx->_cr);
                 cairo_restore(clip_ctx->_cr);
 
-                // if a mask won't be applied set opacity too
+                // If a mask won't be applied set opacity too. (The clip is represented by a solid Cairo mask.)
                 if (!mask)
                     cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
                 else
                     cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
 
                 // copy over the correct CTM
+                // It must be stored in item_transform of current state after pushState.
+                Geom::Matrix item_transform; 
                 if (_state->parent_has_userspace)
-                    clip_ctx->setTransform(&getParentState()->transform);
+                    item_transform = getParentState()->transform * _state->item_transform;
                 else
-                    clip_ctx->setTransform(&_state->transform);
+                    item_transform = _state->item_transform;
 
                 // apply the clip path
                 clip_ctx->pushState();
+                clip_ctx->getCurrentState()->item_transform = item_transform;
                 _renderer->applyClipPath(clip_ctx, clip_path);
                 clip_ctx->popState();
 
@@ -555,19 +633,35 @@ CairoRenderContext::popLayer(void)
             }
         }
 
+        // Apply any mask second
         if (mask) {
+            TRACE(("  Applying mask\n"));
             // create rendering context for mask
             CairoRenderContext *mask_ctx = _renderer->createContext();
-            mask_ctx->setupSurface(_width, _height);
+
+            // Fix Me: This is a kludge. PDF and PS output is set to 72 dpi but the
+            // Cairo surface is expecting the mask to be 90 dpi.
+            float surface_width = _width;
+            float surface_height = _height;
+            if( _vector_based_target ) {
+                surface_width *= 1.25;
+                surface_height *= 1.25;
+            }
+            mask_ctx->setupSurface( surface_width, surface_height );
+            TRACE(("mask surface: %f x %f at %i dpi\n", surface_width, surface_height, _dpi ));
 
             // set rendering mode to normal
             setRenderMode(RENDER_MODE_NORMAL);
 
             // copy the correct CTM to mask context
+            /*
             if (_state->parent_has_userspace)
                 mask_ctx->setTransform(&getParentState()->transform);
             else
                 mask_ctx->setTransform(&_state->transform);
+            */
+            // This is probably not correct... but it seems to do the trick.
+            mask_ctx->setTransform(&_state->item_transform);
 
             // render mask contents to mask_ctx
             _renderer->applyMask(mask_ctx, mask);
@@ -587,18 +681,18 @@ CairoRenderContext::popLayer(void)
             unsigned char *pixels = cairo_image_surface_get_data(mask_image);
 
             // premultiply with opacity
-            if (_state->opacity != 1.0) {
-                TRACE(("premul w/ %f\n", opacity));
-                guint8 int_opacity = (guint8)(255 * opacity);
-                for (int row = 0 ; row < height; row++) {
-                    unsigned char *row_data = pixels + (row * stride);
-                    for (int i = 0 ; i < width; i++) {
-                        guint32 *pixel = (guint32 *)row_data + i;
-                        *pixel = ((((*pixel & 0x00ff0000) >> 16) * 13817 +
-                                ((*pixel & 0x0000ff00) >>  8) * 46518 +
-                                ((*pixel & 0x000000ff)      ) * 4688) *
-                                int_opacity);
-                    }
+            // In SVG, the rgb channels as well as the alpha channel is used in masking.
+            // In Cairo, only the alpha channel is used thus requiring this conversion.
+            TRACE(("premul w/ %f\n", opacity));
+            guint8 int_opacity = (guint8)(255 * opacity);
+            for (int row = 0 ; row < height; row++) {
+                unsigned char *row_data = pixels + (row * stride);
+                for (int i = 0 ; i < width; i++) {
+                    guint32 *pixel = (guint32 *)row_data + i;
+                    *pixel = ((((*pixel & 0x00ff0000) >> 16) * 13817 +
+                               ((*pixel & 0x0000ff00) >>  8) * 46518 +
+                               ((*pixel & 0x000000ff)      ) * 4688) *
+                              int_opacity);
                 }
             }
 
@@ -612,6 +706,7 @@ CairoRenderContext::popLayer(void)
             _renderer->destroyContext(mask_ctx);
         }
     } else {
+        // No clip path or mask
         cairo_pop_group_to_source(_cr);
         if (opacity == 1.0)
             cairo_paint(_cr);
@@ -621,7 +716,7 @@ CairoRenderContext::popLayer(void)
 }
 
 void
-CairoRenderContext::addClipPath(NArtBpath const *bp, SPIEnum const *fill_rule)
+CairoRenderContext::addClipPath(Geom::PathVector const &pv, SPIEnum const *fill_rule)
 {
     g_assert( _is_valid );
 
@@ -632,7 +727,7 @@ CairoRenderContext::addClipPath(NArtBpath const *bp, SPIEnum const *fill_rule)
     } else {
         cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
     }
-    addBpath(bp);
+    addPathVector(pv);
 }
 
 void
@@ -667,6 +762,13 @@ CairoRenderContext::setupSurface(double width, double height)
 #ifdef CAIRO_HAS_PS_SURFACE
         case CAIRO_SURFACE_TYPE_PS:
             surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
+#if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2))
+            if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
+                return FALSE;
+            }
+            cairo_ps_surface_restrict_to_level (surface, (cairo_ps_level_t)_ps_level);
+            cairo_ps_surface_set_eps (surface, (cairo_bool_t) _eps);
+#endif
             break;
 #endif
         default:
@@ -678,22 +780,31 @@ CairoRenderContext::setupSurface(double width, double height)
 }
 
 bool
-CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector)
+CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector, cairo_matrix_t *ctm)
 {
     if (_is_valid || !surface)
         return false;
 
     _vector_based_target = is_vector;
-    bool ret = _finishSurfaceSetup (surface);
+    bool ret = _finishSurfaceSetup (surface, ctm);
     if (ret)
         cairo_surface_reference (surface);
     return ret;
 }
 
 bool
-CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface)
+CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface, cairo_matrix_t *ctm)
 {
+    if(surface == NULL) {
+        return FALSE;
+    }
+    if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
+        return FALSE;
+    }
+
     _cr = cairo_create(surface);
+    if (ctm)
+        cairo_set_matrix(_cr, ctm);
     _surface = surface;
 
     if (_vector_based_target) {
@@ -720,6 +831,8 @@ CairoRenderContext::finish(void)
         cairo_show_page(_cr);
 
     cairo_destroy(_cr);
+    cairo_surface_finish(_surface);
+    cairo_status_t status = cairo_surface_status(_surface);
     cairo_surface_destroy(_surface);
     _cr = NULL;
     _surface = NULL;
@@ -737,11 +850,14 @@ CairoRenderContext::finish(void)
         _stream = NULL;
     }
 
-    return true;
+    if (status == CAIRO_STATUS_SUCCESS)
+        return true;
+    else
+        return false;
 }
 
 void
-CairoRenderContext::transform(NRMatrix const *transform)
+CairoRenderContext::transform(Geom::Matrix const *transform)
 {
     g_assert( _is_valid );
 
@@ -754,7 +870,7 @@ CairoRenderContext::transform(NRMatrix const *transform)
 }
 
 void
-CairoRenderContext::setTransform(NRMatrix const *transform)
+CairoRenderContext::setTransform(Geom::Matrix const *transform)
 {
     g_assert( _is_valid );
 
@@ -765,7 +881,7 @@ CairoRenderContext::setTransform(NRMatrix const *transform)
 }
 
 void
-CairoRenderContext::getTransform(NRMatrix *copy) const
+CairoRenderContext::getTransform(Geom::Matrix *copy) const
 {
     g_assert( _is_valid );
 
@@ -780,12 +896,12 @@ CairoRenderContext::getTransform(NRMatrix *copy) const
 }
 
 void
-CairoRenderContext::getParentTransform(NRMatrix *copy) const
+CairoRenderContext::getParentTransform(Geom::Matrix *copy) const
 {
     g_assert( _is_valid );
 
     CairoRenderState *parent_state = getParentState();
-    memcpy(copy, &parent_state->transform, sizeof(NRMatrix));
+    memcpy(copy, &parent_state->transform, sizeof(Geom::Matrix));
 }
 
 void
@@ -833,9 +949,9 @@ CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver
 
     SPPattern *pat = SP_PATTERN (paintserver);
 
-    NRMatrix ps2user, pcs2dev;
-    nr_matrix_set_identity(&ps2user);
-    nr_matrix_set_identity(&pcs2dev);
+    Geom::Matrix ps2user, pcs2dev;
+    ps2user = Geom::identity();
+    pcs2dev = Geom::identity();
 
     double x = pattern_x(pat);
     double y = pattern_y(pat);
@@ -847,7 +963,7 @@ CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver
     TRACE(("%f x %f pattern\n", width, height));
 
     if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
-        //NR::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
+        //Geom::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
         bbox_width_scaler = pbox->x1 - pbox->x0;
         bbox_height_scaler = pbox->y1 - pbox->y0;
         ps2user[4] = x * bbox_width_scaler + pbox->x0;
@@ -860,8 +976,9 @@ CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver
     }
 
     // apply pattern transformation
-    NRMatrix pattern_transform(pattern_patternTransform(pat));
-    nr_matrix_multiply(&ps2user, &ps2user, &pattern_transform);
+    Geom::Matrix pattern_transform(pattern_patternTransform(pat));
+    ps2user *= pattern_transform;
+    Geom::Point ori (ps2user[4], ps2user[5]);
 
     // create pattern contents coordinate system
     if (pat->viewBox_set) {
@@ -885,23 +1002,17 @@ CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver
     } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
         pcs2dev[0] = pbox->x1 - pbox->x0;
         pcs2dev[3] = pbox->y1 - pbox->y0;
-    }
-
-    // calculate the size of the surface which has to be created
-    // the scaling needs to be taken into account in the ctm after the pattern transformation
-    NRMatrix temp;
-    nr_matrix_multiply(&temp, &pattern_transform, &_state->transform);
-    double width_scaler = sqrt(temp[0] * temp[0] + temp[2] * temp[2]);
-    double height_scaler = sqrt(temp[1] * temp[1] + temp[3] * temp[3]);
 
-    if (_vector_based_target) {
-        // eliminate PT_PER_PX mul from these
-        width_scaler *= 1.25;
-        height_scaler *= 1.25;
     }
-    double surface_width = ceil(bbox_width_scaler * width_scaler * width);
-    double surface_height = ceil(bbox_height_scaler * height_scaler * height);
-    TRACE(("surface size: %f x %f\n", surface_width, surface_height));
+
+    // Calculate the size of the surface which has to be created 
+#define SUBPIX_SCALE 100
+    // Cairo requires an integer pattern surface width/height.
+    // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel.
+    // Multiply by SUBPIX_SCALE to allow for less than a pixel precision
+    double surface_width = MAX(ceil(SUBPIX_SCALE * bbox_width_scaler * width - 0.5), 1);
+    double surface_height = MAX(ceil(SUBPIX_SCALE * bbox_height_scaler * height - 0.5), 1);
+    TRACE(("pattern surface size: %f x %f\n", surface_width, surface_height));
     // create new rendering context
     CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
 
@@ -911,14 +1022,14 @@ CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver
     double scale_height = surface_height / (bbox_height_scaler * height);
     if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
         TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
-        NRMatrix scale;
-        nr_matrix_set_scale(&scale, 1.0 / scale_width, 1.0 / scale_height);
-        nr_matrix_multiply(&pcs2dev, &pcs2dev, &scale);
-
-        nr_matrix_set_scale(&scale, scale_width, scale_height);
-        nr_matrix_multiply(&ps2user, &ps2user, &scale);
+        pcs2dev *= Geom::Scale(SUBPIX_SCALE,SUBPIX_SCALE);
+        ps2user *= Geom::Scale(1.0/SUBPIX_SCALE,1.0/SUBPIX_SCALE);
     }
 
+    // despite scaling up/down by subpixel scaler, the origin point of the pattern must be the same
+    ps2user[4] = ori[Geom::X];
+    ps2user[5] = ori[Geom::Y];
+
     pattern_ctx->setTransform(&pcs2dev);
     pattern_ctx->pushState();
 
@@ -983,17 +1094,17 @@ CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const pain
 
             sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built
 
-            NR::Point p1 (lg->x1.computed, lg->y1.computed);
-            NR::Point p2 (lg->x2.computed, lg->y2.computed);
+            Geom::Point p1 (lg->x1.computed, lg->y1.computed);
+            Geom::Point p2 (lg->x2.computed, lg->y2.computed);
             if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
                 // convert to userspace
-                NR::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
+                Geom::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
                 p1 *= bbox2user;
                 p2 *= bbox2user;
             }
 
             // create linear gradient pattern
-            pattern = cairo_pattern_create_linear(p1[NR::X], p1[NR::Y], p2[NR::X], p2[NR::Y]);
+            pattern = cairo_pattern_create_linear(p1[Geom::X], p1[Geom::Y], p2[Geom::X], p2[Geom::Y]);
 
             // add stops
             for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
@@ -1007,14 +1118,14 @@ CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const pain
 
         sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built
 
-        NR::Point c (rg->cx.computed, rg->cy.computed);
-        NR::Point f (rg->fx.computed, rg->fy.computed);
+        Geom::Point c (rg->cx.computed, rg->cy.computed);
+        Geom::Point f (rg->fx.computed, rg->fy.computed);
         double r = rg->r.computed;
         if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
             apply_bbox2user = true;
 
         // create radial gradient pattern
-        pattern = cairo_pattern_create_radial(f[NR::X], f[NR::Y], 0, c[NR::X], c[NR::Y], r);
+        pattern = cairo_pattern_create_radial(f[Geom::X], f[Geom::Y], 0, c[Geom::X], c[Geom::Y], r);
 
         // add stops
         for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
@@ -1176,27 +1287,31 @@ CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
 }
 
 bool
-CairoRenderContext::renderPath(NRBPath const *bpath, SPStyle const *style, NRRect const *pbox)
+CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, NRRect const *pbox)
 {
     g_assert( _is_valid );
 
     if (_render_mode == RENDER_MODE_CLIP) {
         if (_clip_mode == CLIP_MODE_PATH) {
-            addClipPath(bpath->path, &style->fill_rule);
+            addClipPath(pathv, &style->fill_rule);
         } else {
-            setBpath(bpath->path);
+            setPathVector(pathv);
             if (style->fill_rule.value == 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);
-            cairo_surface_write_to_png (_surface, "gtar2.png");
+            TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
         }
         return true;
     }
 
-    if (style->fill.isNone() && style->stroke.isNone())
+    bool no_fill = style->fill.isNone() || style->fill_opacity.value == 0;
+    bool no_stroke = style->stroke.isNone() || style->stroke_width.computed < 1e-9 || 
+                    style->fill_opacity.value == 0;
+
+    if (no_fill && no_stroke)
         return true;
 
     bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
@@ -1207,9 +1322,9 @@ CairoRenderContext::renderPath(NRBPath const *bpath, SPStyle const *style, NRRec
     else
         pushLayer();
 
-    if (!style->fill.isNone()) {
+    if (!no_fill) {
         _setFillStyle(style, pbox);
-        setBpath(bpath->path);
+        setPathVector(pathv);
 
         if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
@@ -1217,16 +1332,16 @@ CairoRenderContext::renderPath(NRBPath const *bpath, SPStyle const *style, NRRec
             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
         }
 
-        if (style->stroke.isNone())
+        if (no_stroke)
             cairo_fill(_cr);
         else
             cairo_fill_preserve(_cr);
     }
 
-    if (!style->stroke.isNone()) {
+    if (!no_stroke) {
         _setStrokeStyle(style, pbox);
-        if (style->fill.isNone())
-            setBpath(bpath->path);
+        if (no_fill)
+            setPathVector(pathv);
 
         cairo_stroke(_cr);
     }
@@ -1241,7 +1356,7 @@ CairoRenderContext::renderPath(NRBPath const *bpath, SPStyle const *style, NRRec
 
 bool
 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
-                                NRMatrix const *image_transform, SPStyle const *style)
+                                Geom::Matrix const *image_transform, SPStyle const *style)
 {
     g_assert( _is_valid );
 
@@ -1344,10 +1459,16 @@ CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoG
         i++;
     }
 
-    if (is_stroke)
+    if (is_stroke) {
         cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
-    else
-        cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
+    } else {
+        if (_is_texttopath) {
+            cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
+            cairo_fill_preserve(cr);
+        } else {
+            cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
+        }
+    }
 
     if (num_glyphs > GLYPH_ARRAY_SIZE)
         g_free(glyphs);
@@ -1356,21 +1477,38 @@ CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoG
 }
 
 bool
-CairoRenderContext::renderGlyphtext(PangoFont *font, NRMatrix const *font_matrix,
+CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Matrix const *font_matrix,
                                     std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
 {
     // create a cairo_font_face from PangoFont
     double size = style->font_size.computed;
+    cairo_font_face_t *font_face = NULL;
+
+    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);
+    
+    font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
+# endif
+#else
+# ifdef CAIRO_HAS_FT_FONT
     PangoFcFont *fc_font = PANGO_FC_FONT(font);
-    FcPattern *fc_pattern = fc_font->font_pattern;
-
+    fc_pattern = fc_font->font_pattern;
+    font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
+# endif
+#endif
+    
     cairo_save(_cr);
-
-#ifndef PANGO_ENABLE_BACKEND
-    cairo_font_face_t *font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
     cairo_set_font_face(_cr, font_face);
 
-    if (FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
+    if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
         size = 12.0;
 
     // set the given font matrix
@@ -1385,25 +1523,21 @@ CairoRenderContext::renderGlyphtext(PangoFont *font, NRMatrix const *font_matrix
             } else {
                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
             }
-            cairo_set_source_rgba (_cr, 1.0, 1.0, 1.0, 1.0);
-            cairo_rectangle (_cr, 0, 0, 30, 40);
-            cairo_fill (_cr);
             _showGlyphs(_cr, font, glyphtext, FALSE);
-            //cairo_fill(_cr);
         } else {
             // just add the glyph paths to the current context
             _showGlyphs(_cr, font, glyphtext, TRUE);
         }
     } else {
 
-        if (style->fill.type == SP_PAINT_TYPE_COLOR || style->fill.type == SP_PAINT_TYPE_PAINTSERVER) {
+        if (style->fill.isColor() || style->fill.isPaintserver()) {
             // set fill style
             _setFillStyle(style, NULL);
 
             _showGlyphs(_cr, font, glyphtext, FALSE);
         }
 
-        if (style->stroke.type == SP_PAINT_TYPE_COLOR || style->stroke.type == SP_PAINT_TYPE_PAINTSERVER) {
+        if (style->stroke.isColor() || style->stroke.isPaintserver()) {
             // set stroke style
             _setStrokeStyle(style, NULL);
 
@@ -1415,11 +1549,8 @@ CairoRenderContext::renderGlyphtext(PangoFont *font, NRMatrix const *font_matrix
 
     cairo_restore(_cr);
 
-    cairo_font_face_destroy(font_face);
-#else
-    (void)size;
-    (void)fc_pattern;
-#endif
+    if (font_face)
+        cairo_font_face_destroy(font_face);
 
     return true;
 }
@@ -1427,47 +1558,16 @@ CairoRenderContext::renderGlyphtext(PangoFont *font, NRMatrix const *font_matrix
 /* Helper functions */
 
 void
-CairoRenderContext::addBpath(NArtBpath const *bp)
+CairoRenderContext::setPathVector(Geom::PathVector const &pv)
 {
-    bool closed = false;
-    while (bp->code != NR_END) {
-        switch (bp->code) {
-            case NR_MOVETO:
-                if (closed) {
-                    cairo_close_path(_cr);
-                }
-                closed = true;
-                cairo_move_to(_cr, bp->x3, bp->y3);
-                break;
-            case NR_MOVETO_OPEN:
-                if (closed) {
-                    cairo_close_path(_cr);
-                }
-                closed = false;
-                cairo_move_to(_cr, bp->x3, bp->y3);
-                break;
-            case NR_LINETO:
-                cairo_line_to(_cr, bp->x3, bp->y3);
-                break;
-            case NR_CURVETO:
-                cairo_curve_to(_cr, bp->x1, bp->y1, bp->x2, bp->y2, bp->x3, bp->y3);
-                break;
-            default:
-                break;
-        }
-        bp += 1;
-    }
-    if (closed) {
-        cairo_close_path(_cr);
-    }
+    cairo_new_path(_cr);
+    addPathVector(pv);
 }
 
 void
-CairoRenderContext::setBpath(NArtBpath const *bp)
+CairoRenderContext::addPathVector(Geom::PathVector const &pv)
 {
-    cairo_new_path(_cr);
-    if (bp)
-        addBpath(bp);
+    feed_pathvector_to_cairo(_cr, pv);
 }
 
 void
@@ -1480,7 +1580,7 @@ CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double x
 }
 
 void
-CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, NRMatrix const *transform)
+CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, Geom::Matrix const *transform)
 {
     matrix->xx = (*transform)[0];
     matrix->yx = (*transform)[1];
@@ -1491,7 +1591,7 @@ CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, NRMatrix const *tra
 }
 
 void
-CairoRenderContext::_concatTransform(cairo_t *cr, NRMatrix const *transform)
+CairoRenderContext::_concatTransform(cairo_t *cr, Geom::Matrix const *transform)
 {
     _concatTransform(cr, (*transform)[0], (*transform)[1],
                      (*transform)[2], (*transform)[3],