Code

fix copy error
[inkscape.git] / src / extension / internal / cairo-render-context.cpp
index e9ac98df8e18a59c4c0bbaa80a799b11dbe20014..066324ebf96a2ee1145702625fdc0c81c4b99793 100644 (file)
@@ -80,6 +80,7 @@
 #include <pango/pangofc-fontmap.h>
 
 //#define TRACE(_args) g_printf _args
+//#define TRACE(_args) g_message _args
 #define TRACE(_args)
 //#define TEST(_args) _args
 #define TEST(_args)
@@ -107,7 +108,7 @@ static cairo_status_t _write_callback(void *closure, const unsigned char *data,
 
 CairoRenderContext::CairoRenderContext(CairoRenderer *parent) :
     _dpi(72),
-    _pdf_level(0),
+    _pdf_level(1),
     _ps_level(1),
     _eps(false),
     _is_texttopath(FALSE),
@@ -125,14 +126,27 @@ CairoRenderContext::CairoRenderContext(CairoRenderer *parent) :
     _renderer(parent),
     _render_mode(RENDER_MODE_NORMAL),
     _clip_mode(CLIP_MODE_MASK)
-{}
+{
+    font_table = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, font_data_free);
+}
 
 CairoRenderContext::~CairoRenderContext(void)
 {
+    if(font_table != NULL) {
+        g_hash_table_remove_all(font_table);
+    }
+
     if (_cr) cairo_destroy(_cr);
     if (_surface) cairo_surface_destroy(_surface);
     if (_layout) g_object_unref(_layout);
 }
+void CairoRenderContext::font_data_free(gpointer data)
+{
+    cairo_font_face_t *font_face = (cairo_font_face_t *)data;
+    if (font_face) {
+        cairo_font_face_destroy(font_face);
+    }
+}
 
 CairoRenderer*
 CairoRenderContext::getRenderer(void) const
@@ -192,6 +206,8 @@ CairoRenderContext::cloneMe(double width, double height) const
                                                             (int)ceil(width), (int)ceil(height));
     new_context->_cr = cairo_create(surface);
     new_context->_surface = surface;
+    new_context->_width = width;
+    new_context->_height = height;
     new_context->_is_valid = TRUE;
 
     return new_context;
@@ -610,7 +626,7 @@ CairoRenderContext::popLayer(void)
 
                 // copy over the correct CTM
                 // It must be stored in item_transform of current state after pushState.
-                Geom::Matrix item_transform; 
+                Geom::Matrix item_transform;
                 if (_state->parent_has_userspace)
                     item_transform = getParentState()->transform * _state->item_transform;
                 else
@@ -647,7 +663,11 @@ CairoRenderContext::popLayer(void)
                 surface_width *= 1.25;
                 surface_height *= 1.25;
             }
-            mask_ctx->setupSurface( surface_width, surface_height );
+            if (!mask_ctx->setupSurface( surface_width, surface_height )) {
+                TRACE(("mask: setupSurface failed\n"));
+                _renderer->destroyContext(mask_ctx);
+                return;
+            }
             TRACE(("mask surface: %f x %f at %i dpi\n", surface_width, surface_height, _dpi ));
 
             // set rendering mode to normal
@@ -749,7 +769,12 @@ CairoRenderContext::setupSurface(double width, double height)
     if (_vector_based_target && _stream == NULL)
         return false;
 
+    _width = width;
+    _height = height;
+
     cairo_surface_t *surface = NULL;
+    cairo_matrix_t ctm;
+    cairo_matrix_init_identity (&ctm);
     switch (_target) {
         case CAIRO_SURFACE_TYPE_IMAGE:
             surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
@@ -757,17 +782,20 @@ CairoRenderContext::setupSurface(double width, double height)
 #ifdef CAIRO_HAS_PDF_SURFACE
         case CAIRO_SURFACE_TYPE_PDF:
             surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
+#if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 10, 0))
+            cairo_pdf_surface_restrict_to_version(surface, (cairo_pdf_version_t)_pdf_level);   
+#endif
             break;
 #endif
 #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);
+#if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2))
+            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
@@ -776,7 +804,7 @@ CairoRenderContext::setupSurface(double width, double height)
             break;
     }
 
-    return _finishSurfaceSetup (surface);
+    return _finishSurfaceSetup (surface, &ctm);
 }
 
 bool
@@ -796,13 +824,16 @@ bool
 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface, cairo_matrix_t *ctm)
 {
     if(surface == NULL) {
-        return FALSE;
+        return false;
     }
     if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
-        return FALSE;
+        return false;
     }
 
     _cr = cairo_create(surface);
+    if(CAIRO_STATUS_SUCCESS != cairo_status(_cr)) {
+        return false;
+    }
     if (ctm)
         cairo_set_matrix(_cr, ctm);
     _surface = surface;
@@ -1005,7 +1036,7 @@ CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver
 
     }
 
-    // Calculate the size of the surface which has to be created 
+    // 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.
@@ -1092,11 +1123,11 @@ CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const pain
 
             SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
 
-            sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built
+            SP_GRADIENT(lg)->ensureVector(); // when exporting from commandline, vector is not built
 
             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) {
+            if (pbox && SP_GRADIENT(lg)->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
                 // convert to userspace
                 Geom::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
                 p1 *= bbox2user;
@@ -1116,12 +1147,12 @@ CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const pain
 
         SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
 
-        sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built
+        SP_GRADIENT(rg)->ensureVector(); // when exporting from commandline, vector is not built
 
         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)
+        if (pbox && SP_GRADIENT(rg)->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
             apply_bbox2user = true;
 
         // create radial gradient pattern
@@ -1144,7 +1175,7 @@ CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const pain
         SPGradient *g = SP_GRADIENT(paintserver);
 
         // set extend type
-        SPGradientSpread spread = sp_gradient_get_spread(g);
+        SPGradientSpread spread = g->fetchSpread();
         switch (spread) {
             case SP_GRADIENT_SPREAD_REPEAT: {
                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
@@ -1191,7 +1222,8 @@ CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const pain
 void
 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
 {
-    g_return_if_fail( style->fill.isColor()
+    g_return_if_fail( !style->fill.set
+                      || style->fill.isColor()
                       || style->fill.isPaintserver() );
 
     float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
@@ -1205,6 +1237,10 @@ CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox
         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))
@@ -1232,7 +1268,7 @@ CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
 
         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
     } else {
-        g_assert( style->fill.isPaintserver()
+        g_assert( style->stroke.isPaintserver()
                   || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
                   || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
 
@@ -1308,8 +1344,8 @@ CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle con
     }
 
     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;
+    bool no_stroke = style->stroke.isNone() || style->stroke_width.computed < 1e-9 ||
+                    style->stroke_opacity.value == 0;
 
     if (no_fill && no_stroke)
         return true;
@@ -1435,7 +1471,7 @@ CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsi
 #define GLYPH_ARRAY_SIZE 64
 
 unsigned int
-CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool is_stroke)
+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;
@@ -1444,30 +1480,26 @@ CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoG
         glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
 
     unsigned int num_invalid_glyphs = 0;
-    unsigned int i = 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 - num_invalid_glyphs].index = it_info->index;
-        glyphs[i - num_invalid_glyphs].x = it_info->x;
-        glyphs[i - num_invalid_glyphs].y = it_info->y;
+        glyphs[i].index = it_info->index;
+        glyphs[i].x     = it_info->x;
+        glyphs[i].y     = it_info->y;
         i++;
     }
 
-    if (is_stroke) {
+    if (path) {
         cairo_glyph_path(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);
-        }
+        cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
     }
 
     if (num_glyphs > GLYPH_ARRAY_SIZE)
@@ -1482,10 +1514,11 @@ CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Matrix const *font_ma
 {
     // create a cairo_font_face from PangoFont
     double size = style->font_size.computed;
-    cairo_font_face_t *font_face = NULL;
+    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);
@@ -1494,17 +1527,23 @@ CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Matrix const *font_ma
     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);
+
+    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;
-    font_face = cairo_ft_font_face_create_for_pattern(fc_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);
 
@@ -1529,28 +1568,36 @@ CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Matrix const *font_ma
             _showGlyphs(_cr, font, glyphtext, TRUE);
         }
     } else {
-
+        bool fill = false, stroke = false, have_path = false;
         if (style->fill.isColor() || style->fill.isPaintserver()) {
-            // set fill style
-            _setFillStyle(style, NULL);
-
-            _showGlyphs(_cr, font, glyphtext, FALSE);
+            fill = true;
         }
 
         if (style->stroke.isColor() || style->stroke.isPaintserver()) {
-            // set stroke style
+            stroke = true;
+        }
+        if (fill) {
+            _setFillStyle(style, NULL);
+            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, NULL);
-
-            // paint stroke
-            _showGlyphs(_cr, font, glyphtext, TRUE);
+            if (!have_path) _showGlyphs(_cr, font, glyphtext, true);
             cairo_stroke(_cr);
         }
     }
 
     cairo_restore(_cr);
 
-    if (font_face)
-        cairo_font_face_destroy(font_face);
+//    if (font_face)
+//        cairo_font_face_destroy(font_face);
 
     return true;
 }