diff options
| author | Jabier Arraiza Cenoz <jabier.arraiza@marker.es> | 2014-05-05 08:45:33 +0000 |
|---|---|---|
| committer | Jabiertxof <jtx@jtx.marker.es> | 2014-05-05 08:45:33 +0000 |
| commit | e3d114cbca503194d41fc8edb86848ea03074089 (patch) | |
| tree | 0019498d47ccb79f7a6f447b8a0f468372006bb7 /src | |
| parent | adding VinÃcius work whith node tips (diff) | |
| parent | Documentation. Elements of design tutorial update ((not fully translated); Up... (diff) | |
| download | inkscape-e3d114cbca503194d41fc8edb86848ea03074089.tar.gz inkscape-e3d114cbca503194d41fc8edb86848ea03074089.zip | |
update to trunk
(bzr r11950.1.340)
Diffstat (limited to 'src')
28 files changed, 542 insertions, 293 deletions
diff --git a/src/2geom/ellipse.cpp b/src/2geom/ellipse.cpp index bea99e5dd..2686844b2 100644 --- a/src/2geom/ellipse.cpp +++ b/src/2geom/ellipse.cpp @@ -36,6 +36,7 @@ #include <2geom/numeric/fitting-tool.h> #include <2geom/numeric/fitting-model.h> +using std::swap; namespace Geom { @@ -102,7 +103,7 @@ void Ellipse::set(double A, double B, double C, double D, double E, double F) // the solution is not unique so we choose always the ellipse // with a rotation angle between 0 and PI/2 - if ( swap_axes ) std::swap(rx, ry); + if ( swap_axes ) swap(rx, ry); if ( are_near(rot, M_PI/2) || are_near(rot, -M_PI/2) || are_near(rx, ry) ) @@ -233,7 +234,7 @@ Ellipse Ellipse::transformed(Affine const& m) const Point new_center = center() * m; Affine M = m.withoutTranslation(); Affine AM = A * M; - if ( are_near(AM.det(), 0) ) + if ( are_near(std::sqrt(fabs(AM.det())), 0) ) { double angle; if (AM[0] != 0) @@ -262,7 +263,7 @@ Ellipse Ellipse::transformed(Affine const& m) const Affine invm = M.inverse(); Q = invm * Q ; - std::swap( invm[1], invm[2] ); + swap( invm[1], invm[2] ); Q *= invm; Ellipse e(Q[0], 2*Q[1], Q[3], 0, 0, -1); e.m_centre = new_center; diff --git a/src/display/canvas-arena.cpp b/src/display/canvas-arena.cpp index 404a94828..25d35fc6b 100644 --- a/src/display/canvas-arena.cpp +++ b/src/display/canvas-arena.cpp @@ -227,7 +227,7 @@ sp_canvas_arena_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_ { SPCanvasArena *arena = SP_CANVAS_ARENA (item); - arena->drawing.update(Geom::IntRect::infinite(), arena->ctx, DrawingItem::STATE_PICK); + arena->drawing.update(Geom::IntRect::infinite(), arena->ctx, DrawingItem::STATE_PICK | DrawingItem::STATE_BBOX); DrawingItem *picked = arena->drawing.pick(p, arena->drawing.delta, arena->sticky); arena->picked = picked; diff --git a/src/display/drawing-item.cpp b/src/display/drawing-item.cpp index ccf905e81..0bfb00b62 100644 --- a/src/display/drawing-item.cpp +++ b/src/display/drawing-item.cpp @@ -23,7 +23,6 @@ namespace Inkscape { -#ifdef WITH_CSSBLEND void set_cairo_blend_operator( DrawingContext &dc, unsigned blend_mode ) { // All of the blend modes are implemented in Cairo as of 1.10. @@ -81,7 +80,6 @@ void set_cairo_blend_operator( DrawingContext &dc, unsigned blend_mode ) { break; } } -#endif /** * @class DrawingItem @@ -409,7 +407,7 @@ DrawingItem::setItemBounds(Geom::OptRect const &bounds) * @param reset State fields that should be reset before processing them. This is * a means to force a recomputation of internal data even if the item * considers it up to date. Mainly for internal use, such as - * propagating bunding box recomputation to children when the item's + * propagating bounding box recomputation to children when the item's * transform changes. */ void @@ -594,9 +592,8 @@ DrawingItem::render(DrawingContext &dc, Geom::IntRect const &area, unsigned flag if (_cached) { if (_cache) { _cache->prepare(); -#ifdef WITH_CSSBLEND set_cairo_blend_operator( dc, _blend_mode ); -#endif + _cache->paintFromCache(dc, carea); if (!carea) return RENDER_OK; } else { @@ -625,10 +622,8 @@ DrawingItem::render(DrawingContext &dc, Geom::IntRect const &area, unsigned flag nir |= (_filter != NULL && render_filters); // 3. it has a filter nir |= needs_opacity; // 4. it is non-opaque nir |= (_cache != NULL); // 5. it is cached -#ifdef WITH_CSSBLEND nir |= (_blend_mode != SP_CSS_BLEND_NORMAL); // 6. Blend mode not normal nir |= (_isolation == SP_CSS_ISOLATION_ISOLATE); // 7. Explicit isolatiom -#endif /* How the rendering is done. * @@ -740,9 +735,7 @@ DrawingItem::render(DrawingContext &dc, Geom::IntRect const &area, unsigned flag } dc.rectangle(*carea); dc.setSource(&intermediate); -#ifdef WITH_CSSBLEND set_cairo_blend_operator( dc, _blend_mode ); -#endif dc.fill(); dc.setSource(0,0,0,0); // the call above is to clear a ref on the intermediate surface held by dc diff --git a/src/display/drawing-shape.cpp b/src/display/drawing-shape.cpp index 92d71fad3..1a41bdb3a 100644 --- a/src/display/drawing-shape.cpp +++ b/src/display/drawing-shape.cpp @@ -149,7 +149,6 @@ DrawingShape::_updateItem(Geom::IntRect const &area, UpdateContext const &ctx, u return STATE_ALL; } -#ifdef WITH_SVG2 void DrawingShape::_renderFill(DrawingContext &dc) { @@ -183,7 +182,6 @@ DrawingShape::_renderStroke(DrawingContext &dc) dc.newPath(); // clear path } } -#endif void DrawingShape::_renderMarkers(DrawingContext &dc, Geom::IntRect const &area, unsigned flags, DrawingItem *stop_at) @@ -222,10 +220,9 @@ DrawingShape::_renderItem(DrawingContext &dc, Geom::IntRect const &area, unsigne } -#ifdef WITH_SVG2 if( _nrstyle.paint_order_layer[0] == NRStyle::PAINT_ORDER_NORMAL ) { // This is the most common case, special case so we don't call get_pathvector(), etc. twice -#endif + { // we assume the context has no path Inkscape::DrawingContext::Save save(dc); @@ -255,7 +252,6 @@ DrawingShape::_renderItem(DrawingContext &dc, Geom::IntRect const &area, unsigne _renderMarkers(dc, area, flags, stop_at); return RENDER_OK; -#ifdef WITH_SVG2 } // Handle different paint orders @@ -276,7 +272,6 @@ DrawingShape::_renderItem(DrawingContext &dc, Geom::IntRect const &area, unsigne } } return RENDER_OK; -#endif } void DrawingShape::_clipItem(DrawingContext &dc, Geom::IntRect const & /*area*/) diff --git a/src/display/drawing-shape.h b/src/display/drawing-shape.h index f37de9ce7..9d93a642f 100644 --- a/src/display/drawing-shape.h +++ b/src/display/drawing-shape.h @@ -39,10 +39,8 @@ protected: virtual DrawingItem *_pickItem(Geom::Point const &p, double delta, unsigned flags); virtual bool _canClip(); -#ifdef WITH_SVG2 void _renderFill(DrawingContext &dc); void _renderStroke(DrawingContext &dc); -#endif void _renderMarkers(DrawingContext &dc, Geom::IntRect const &area, unsigned flags, DrawingItem *stop_at); diff --git a/src/display/drawing-text.cpp b/src/display/drawing-text.cpp index a280e221a..4178cb1d8 100644 --- a/src/display/drawing-text.cpp +++ b/src/display/drawing-text.cpp @@ -451,7 +451,6 @@ unsigned DrawingText::_renderItem(DrawingContext &dc, Geom::IntRect const &/*are Inkscape::DrawingContext::Save save(dc); dc.transform(_ctm); -#ifdef WITH_SVG2 // Text doesn't have markers, we can do paint-order quick and dirty. bool fill_first = false; if( _nrstyle.paint_order_layer[0] == NRStyle::PAINT_ORDER_NORMAL || @@ -461,22 +460,20 @@ unsigned DrawingText::_renderItem(DrawingContext &dc, Geom::IntRect const &/*are } // Won't get "stroke fill stroke" but that isn't 'valid' if (has_fill && fill_first) { -#else - if (has_fill) { -#endif _nrstyle.applyFill(dc); dc.fillPreserve(); } + if (has_stroke) { _nrstyle.applyStroke(dc); dc.strokePreserve(); } -#ifdef WITH_SVG2 + if (has_fill && !fill_first) { _nrstyle.applyFill(dc); dc.fillPreserve(); } -#endif + dc.newPath(); // clear path // draw text decoration diff --git a/src/display/nr-style.cpp b/src/display/nr-style.cpp index 3d2d36483..09a28e63c 100644 --- a/src/display/nr-style.cpp +++ b/src/display/nr-style.cpp @@ -69,9 +69,7 @@ NRStyle::NRStyle() , line_through_position(0) , font_size(0) { -#ifdef WITH_SVG2 paint_order_layer[0] = PAINT_ORDER_NORMAL; -#endif } NRStyle::~NRStyle() @@ -165,7 +163,6 @@ void NRStyle::set(SPStyle *style) } -#ifdef WITH_SVG2 for( unsigned i = 0; i < PAINT_ORDER_LAYERS; ++i) { switch (style->paint_order.layer[i]) { case SP_CSS_PAINT_ORDER_NORMAL: @@ -182,7 +179,6 @@ void NRStyle::set(SPStyle *style) break; } } -#endif text_decoration_line = TEXT_DECORATION_LINE_CLEAR; if(style->text_decoration_line.inherit ){ text_decoration_line |= TEXT_DECORATION_LINE_INHERIT; } diff --git a/src/display/nr-style.h b/src/display/nr-style.h index 8b5a0ee3d..ca880c00b 100644 --- a/src/display/nr-style.h +++ b/src/display/nr-style.h @@ -68,7 +68,6 @@ struct NRStyle { cairo_pattern_t *fill_pattern; cairo_pattern_t *stroke_pattern; -#ifdef WITH_SVG2 enum PaintOrderType { PAINT_ORDER_NORMAL, PAINT_ORDER_FILL, @@ -78,7 +77,6 @@ struct NRStyle { static const size_t PAINT_ORDER_LAYERS = 3; PaintOrderType paint_order_layer[PAINT_ORDER_LAYERS]; -#endif #define TEXT_DECORATION_LINE_CLEAR 0x00 #define TEXT_DECORATION_LINE_SET 0x01 diff --git a/src/extension/extension.cpp b/src/extension/extension.cpp index d63ec7485..588efb521 100644 --- a/src/extension/extension.cpp +++ b/src/extension/extension.cpp @@ -262,6 +262,18 @@ Extension::check (void) const char * inx_failure = _(" This is caused by an improper .inx file for this extension." " An improper .inx file could have been caused by a faulty installation of Inkscape."); + + // No need to include Windows only extensions + // See LP bug #1307554 for details - https://bugs.launchpad.net/inkscape/+bug/1307554 +#ifndef WIN32 + const char* win_ext[] = {"com.vaxxine.print.win32"}; + std::vector<std::string> v (win_ext, win_ext + sizeof(win_ext)/sizeof(win_ext[0])); + std::string ext_id(id); + if (std::find(v.begin(), v.end(), ext_id) != v.end()) { + printFailure(Glib::ustring(_("the extension is designed for Windows only.")) + inx_failure); + retval = false; + } +#endif if (id == NULL) { printFailure(Glib::ustring(_("an ID was not defined for it.")) + inx_failure); retval = false; diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp index 711d7e3a4..863d1e006 100644 --- a/src/extension/internal/emf-inout.cpp +++ b/src/extension/internal/emf-inout.cpp @@ -33,9 +33,8 @@ #include <stdint.h> #include <libuemf/symbol_convert.h> -#include "sp-root.h" +#include "sp-root.h" // even though it is included indirectly by wmf-inout.h #include "sp-path.h" -#include "style.h" #include "print.h" #include "extension/system.h" #include "extension/print.h" @@ -45,12 +44,9 @@ #include "display/drawing.h" #include "display/drawing-item.h" #include "clear-n_.h" -#include "document.h" -#include "util/units.h" -#include "shape-editor.h" -#include "sp-namedview.h" -#include "document-undo.h" -#include "inkscape.h" +#include "svg/svg.h" +#include "util/units.h" // even though it is included indirectly by wmf-inout.h +#include "inkscape.h" // even though it is included indirectly by wmf-inout.h #include "emf-print.h" #include "emf-inout.h" @@ -65,8 +61,6 @@ namespace Inkscape { namespace Extension { namespace Internal { -static U_RECTL rc_old = rectl_set(pointl_set(-1,-1),pointl_set(-1,-1)); -static bool clipset = false; static uint32_t ICMmode = 0; // not used yet, but code to read it from EMF implemented static uint32_t BLTmode = 0; @@ -452,7 +446,7 @@ void Emf::enlarge_images(PEMF_CALLBACK_DATA d){ /* See if the image string is already in the list. If it is return its position (1->n, not 1-n-1) */ -int Emf::in_images(PEMF_CALLBACK_DATA d, char *test){ +int Emf::in_images(PEMF_CALLBACK_DATA d, const char *test){ int i; for(i=0; i<d->images.count; i++){ if(strcmp(test,d->images.strings[i])==0)return(i+1); @@ -623,7 +617,7 @@ void Emf::enlarge_gradients(PEMF_CALLBACK_DATA d){ /* See if the gradient name is already in the list. If it is return its position (1->n, not 1-n-1) */ -int Emf::in_gradients(PEMF_CALLBACK_DATA d, char *test){ +int Emf::in_gradients(PEMF_CALLBACK_DATA d, const char *test){ int i; for(i=0; i<d->gradients.count; i++){ if(strcmp(test,d->gradients.strings[i])==0)return(i+1); @@ -722,6 +716,65 @@ uint32_t Emf::add_gradient(PEMF_CALLBACK_DATA d, uint32_t gradientType, U_TRIVER return(idx-1); } +/* Add another 100 blank slots to the clips array. +*/ +void Emf::enlarge_clips(PEMF_CALLBACK_DATA d){ + d->clips.size += 100; + d->clips.strings = (char **) realloc(d->clips.strings,d->clips.size * sizeof(char *)); +} + +/* See if the pattern name is already in the list. If it is return its position (1->n, not 1-n-1) +*/ +int Emf::in_clips(PEMF_CALLBACK_DATA d, const char *test){ + int i; + for(i=0; i<d->clips.count; i++){ + if(strcmp(test,d->clips.strings[i])==0)return(i+1); + } + return(0); +} + +/* (Conditionally) add a clip. + If a matching clip already exists nothing happens + If one does exist it is added to the clips list, entered into <defs>. +*/ +void Emf::add_clips(PEMF_CALLBACK_DATA d, const char *clippath, unsigned int logic){ + int op = combine_ops_to_livarot(logic); + Geom::PathVector combined_vect; + char *combined = NULL; + if (op >= 0 && d->dc[d->level].clip_id) { + unsigned int real_idx = d->dc[d->level].clip_id - 1; + Geom::PathVector old_vect = sp_svg_read_pathv(d->clips.strings[real_idx]); + Geom::PathVector new_vect = sp_svg_read_pathv(clippath); + combined_vect = sp_pathvector_boolop(new_vect, old_vect, (bool_op) op , (FillRule) fill_oddEven, (FillRule) fill_oddEven); + combined = sp_svg_write_path(combined_vect); + } + else { + combined = strdup(clippath); // COPY operation, erases everything and starts a new one + } + + uint32_t idx = in_clips(d, combined); + if(!idx){ // add clip if not already present + if(d->clips.count == d->clips.size){ enlarge_clips(d); } + d->clips.strings[d->clips.count++]=strdup(combined); + d->dc[d->level].clip_id = d->clips.count; // one more than the slot where it is actually stored + SVGOStringStream tmp_clippath; + tmp_clippath << "\n<clipPath"; + tmp_clippath << "\n\tclipPathUnits=\"userSpaceOnUse\" "; + tmp_clippath << "\n\tid=\"clipEmfPath" << d->dc[d->level].clip_id << "\""; + tmp_clippath << " >"; + tmp_clippath << "\n\t<path d=\""; + tmp_clippath << combined; + tmp_clippath << "\""; + tmp_clippath << "\n\t/>"; + tmp_clippath << "\n</clipPath>"; + d->outdef += tmp_clippath.str().c_str(); + } + else { + d->dc[d->level].clip_id = idx; + } + free(combined); +} + void @@ -932,9 +985,8 @@ Emf::output_style(PEMF_CALLBACK_DATA d, int iType) tmp_style << "stroke-opacity:1;"; } tmp_style << "\" "; - if (clipset) - tmp_style << "\n\tclip-path=\"url(#clipEmfPath" << d->id << ")\" "; - clipset = false; + if (d->dc[d->level].clip_id) + tmp_style << "\n\tclip-path=\"url(#clipEmfPath" << d->dc[d->level].clip_id << ")\" "; d->outsvg += tmp_style.str().c_str(); } @@ -1104,14 +1156,11 @@ Emf::select_extpen(PEMF_CALLBACK_DATA d, int index) if (!d->dc[d->level].style.stroke_dasharray.values.empty() && (d->level==0 || (d->level>0 && d->dc[d->level].style.stroke_dasharray.values!=d->dc[d->level-1].style.stroke_dasharray.values))) d->dc[d->level].style.stroke_dasharray.values.clear(); for (unsigned int i=0; i<pEmr->elp.elpNumEntries; i++) { - int cur_level = d->level; - d->level = d->emf_obj[index].level; // Doing it this way typically results in a pattern that is tiny, better to assume the array // is the same scale as for dot/dash below, that is, no scaling should be applied // double dash_length = pix_to_abs_size( d, pEmr->elp.elpStyleEntry[i] ); double dash_length = pEmr->elp.elpStyleEntry[i]; - d->level = cur_level; - d->dc[d->level].style.stroke_dasharray.values[i] = dash_length; + d->dc[d->level].style.stroke_dasharray.values.push_back(dash_length); } d->dc[d->level].style.stroke_dasharray.set = 1; } else { @@ -1573,7 +1622,7 @@ int Emf::myEnhMetaFileProc(char *contents, unsigned int length, PEMF_CALLBACK_DA lpEMFR = (PU_ENHMETARECORD)(contents + off); // Uncomment the following to track down toxic records -//std::cout << "record type: " << lpEMFR->iType << " length: " << lpEMFR->nSize << " offset: " << off <<std::endl; +// std::cout << "record type: " << lpEMFR->iType << " name " << U_emr_names(lpEMFR->iType) << " length: " << lpEMFR->nSize << " offset: " << off <<std::endl; off += lpEMFR->nSize; SVGOStringStream tmp_outsvg; @@ -1624,7 +1673,7 @@ int Emf::myEnhMetaFileProc(char *contents, unsigned int length, PEMF_CALLBACK_DA d->dc[d->level].dirty = 0; } -//std::cout << "BEFORE DRAW logic d->mask: " << std::hex << d->mask << " emr_mask: " << emr_mask << std::dec << std::endl; +// std::cout << "BEFORE DRAW logic d->mask: " << std::hex << d->mask << " emr_mask: " << emr_mask << std::dec << std::endl; /* std::cout << "BEFORE DRAW" << " test0 " << ( d->mask & U_DRAW_VISIBLE) @@ -1651,7 +1700,7 @@ std::cout << "BEFORE DRAW" ) ) ){ -// std::cout << "PATH DRAW at TOP" << std::endl; +// std::cout << "PATH DRAW at TOP path" << *(d->path) << std::endl; d->outsvg += " <path "; // this is the ONLY place <path should be used!!! One exception, gradientfill. if(d->drawtype){ // explicit draw type EMR record output_style(d, d->drawtype); @@ -2123,7 +2172,24 @@ std::cout << "BEFORE DRAW" } break; } - case U_EMR_OFFSETCLIPRGN: dbg_str << "<!-- U_EMR_OFFSETCLIPRGN -->\n"; break; + case U_EMR_OFFSETCLIPRGN: + { + dbg_str << "<!-- U_EMR_OFFSETCLIPRGN -->\n"; + if (d->dc[d->level].clip_id) { // can only offsetan existing clipping path + PU_EMROFFSETCLIPRGN pEmr = (PU_EMROFFSETCLIPRGN) lpEMFR; + U_POINTL off = pEmr->ptlOffset; + unsigned int real_idx = d->dc[d->level].clip_id - 1; + Geom::PathVector tmp_vect = sp_svg_read_pathv(d->clips.strings[real_idx]); + double ox = pix_to_x_point(d, off.x, off.y) - pix_to_x_point(d, 0, 0); // take into account all active transforms + double oy = pix_to_y_point(d, off.x, off.y) - pix_to_y_point(d, 0, 0); + Geom::Affine tf = Geom::Translate(ox,oy); + tmp_vect *= tf; + char *tmp_path = sp_svg_write_path(tmp_vect); + add_clips(d, tmp_path, U_RGN_COPY); + free(tmp_path); + } + break; + } case U_EMR_MOVETOEX: { dbg_str << "<!-- U_EMR_MOVETOEX -->\n"; @@ -2139,36 +2205,52 @@ std::cout << "BEFORE DRAW" break; } case U_EMR_SETMETARGN: dbg_str << "<!-- U_EMR_SETMETARGN -->\n"; break; - case U_EMR_EXCLUDECLIPRECT: dbg_str << "<!-- U_EMR_EXCLUDECLIPRECT -->\n"; break; + case U_EMR_EXCLUDECLIPRECT: + { + dbg_str << "<!-- U_EMR_EXCLUDECLIPRECT -->\n"; + + PU_EMREXCLUDECLIPRECT pEmr = (PU_EMREXCLUDECLIPRECT) lpEMFR; + U_RECTL rc = pEmr->rclClip; + + SVGOStringStream tmp_path; + float faraway = 10000000; // hopefully well outside any real drawing! + //outer rect, clockwise + tmp_path << "M " << faraway << "," << faraway << " "; + tmp_path << "L " << faraway << "," << -faraway << " "; + tmp_path << "L " << -faraway << "," << -faraway << " "; + tmp_path << "L " << -faraway << "," << faraway << " "; + tmp_path << "z "; + //inner rect, counterclockwise (sign of Y is reversed) + tmp_path << "M " << pix_to_xy( d, rc.left , rc.top ) << " "; + tmp_path << "L " << pix_to_xy( d, rc.right, rc.top ) << " "; + tmp_path << "L " << pix_to_xy( d, rc.right, rc.bottom ) << " "; + tmp_path << "L " << pix_to_xy( d, rc.left, rc.bottom ) << " "; + tmp_path << "z"; + + add_clips(d, tmp_path.str().c_str(), U_RGN_AND); + + d->path = ""; + d->drawtype = 0; + break; + } case U_EMR_INTERSECTCLIPRECT: { dbg_str << "<!-- U_EMR_INTERSECTCLIPRECT -->\n"; PU_EMRINTERSECTCLIPRECT pEmr = (PU_EMRINTERSECTCLIPRECT) lpEMFR; U_RECTL rc = pEmr->rclClip; - clipset = true; - if ((rc.left == rc_old.left) && (rc.top == rc_old.top) && (rc.right == rc_old.right) && (rc.bottom == rc_old.bottom)) - break; - rc_old = rc; - double dx = pix_to_x_point( d, rc.left, rc.top ); - double dy = pix_to_y_point( d, rc.left, rc.top ); - double dw = pix_to_abs_size( d, rc.right - rc.left + 1); - double dh = pix_to_abs_size( d, rc.bottom - rc.top + 1); + SVGOStringStream tmp_path; + tmp_path << "M " << pix_to_xy( d, rc.left , rc.top ) << " "; + tmp_path << "L " << pix_to_xy( d, rc.right, rc.top ) << " "; + tmp_path << "L " << pix_to_xy( d, rc.right, rc.bottom ) << " "; + tmp_path << "L " << pix_to_xy( d, rc.left, rc.bottom ) << " "; + tmp_path << "z"; + + add_clips(d, tmp_path.str().c_str(), U_RGN_AND); - SVGOStringStream tmp_rectangle; - tmp_rectangle << "\n<clipPath\n\tclipPathUnits=\"userSpaceOnUse\" "; - tmp_rectangle << "\nid=\"clipEmfPath" << ++(d->id) << "\" >"; - tmp_rectangle << "\n<rect "; - tmp_rectangle << "\n x=\"" << dx << "\" "; - tmp_rectangle << "\n y=\"" << dy << "\" "; - tmp_rectangle << "\n width=\"" << dw << "\" "; - tmp_rectangle << "\n height=\"" << dh << "\" "; - tmp_rectangle << "\n transform=" << current_matrix(d, dx, dy, 1); // calculate appropriate offset - tmp_rectangle << "/>\n</clipPath>"; - - d->outdef += tmp_rectangle.str().c_str(); d->path = ""; + d->drawtype = 0; break; } case U_EMR_SCALEVIEWPORTEXTEX: dbg_str << "<!-- U_EMR_SCALEVIEWPORTEXTEX -->\n"; break; @@ -2179,7 +2261,7 @@ std::cout << "BEFORE DRAW" if (d->level < EMF_MAX_DC) { d->dc[d->level + 1] = d->dc[d->level]; if(d->dc[d->level].font_name){ - d->dc[d->level + 1].font_name = strdup(d->dc[d->level].font_name); // or memory access problems because font name pointer duplicated + d->dc[d->level + 1].font_name = strdup(d->dc[d->level].font_name); // or memory access problems because font name pointer duplicated } d->level = d->level + 1; } @@ -2735,7 +2817,18 @@ std::cout << "BEFORE DRAW" } case U_EMR_FLATTENPATH: dbg_str << "<!-- U_EMR_FLATTENPATH -->\n"; break; case U_EMR_WIDENPATH: dbg_str << "<!-- U_EMR_WIDENPATH -->\n"; break; - case U_EMR_SELECTCLIPPATH: dbg_str << "<!-- U_EMR_SELECTCLIPPATH -->\n"; break; + case U_EMR_SELECTCLIPPATH: + { + dbg_str << "<!-- U_EMR_SELECTCLIPPATH -->\n"; + PU_EMRSELECTCLIPPATH pEmr = (PU_EMRSELECTCLIPPATH) lpEMFR; + int logic = pEmr->iMode; + + if ((logic < U_RGN_MIN) || (logic > U_RGN_MAX)){ break; } + add_clips(d, d->path.c_str(), logic); // finds an existing one or stores this, sets clip_id + d->path = ""; + d->drawtype = 0; + break; + } case U_EMR_ABORTPATH: { dbg_str << "<!-- U_EMR_ABORTPATH -->\n"; @@ -2778,8 +2871,10 @@ std::cout << "BEFORE DRAW" dbg_str << "<!-- U_EMR_EXTSELECTCLIPRGN -->\n"; PU_EMREXTSELECTCLIPRGN pEmr = (PU_EMREXTSELECTCLIPRGN) lpEMFR; - if (pEmr->iMode == U_RGN_COPY) - clipset = false; + // the only mode we implement - this clears the clipping region + if (pEmr->iMode == U_RGN_COPY) { + d->dc[d->level].clip_id = 0; + } break; } case U_EMR_BITBLT: @@ -3342,6 +3437,8 @@ void Emf::free_emf_strings(EMF_STRINGS name){ for(int i=0; i< name.count; i++){ free(name.strings[i]); } free(name.strings); } + name.count = 0; + name.size = 0; } SPDocument * @@ -3389,6 +3486,7 @@ Emf::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri ) free_emf_strings(d.hatches); free_emf_strings(d.images); free_emf_strings(d.gradients); + free_emf_strings(d.clips); if (d.emf_obj) { int i; @@ -3405,39 +3503,7 @@ Emf::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri ) d.tri = trinfo_release_except_FC(d.tri); - // Set viewBox if it doesn't exist - if (doc && !doc->getRoot()->viewBox_set) { - bool saved = Inkscape::DocumentUndo::getUndoSensitive(doc); - Inkscape::DocumentUndo::setUndoSensitive(doc, false); - - doc->ensureUpToDate(); - - // Set document unit - Inkscape::XML::Node *repr = sp_document_namedview(doc, 0)->getRepr(); - Inkscape::SVGOStringStream os; - Inkscape::Util::Unit const* doc_unit = doc->getWidth().unit; - os << doc_unit->abbr; - repr->setAttribute("inkscape:document-units", os.str().c_str()); - - // Set viewBox - doc->setViewBox(Geom::Rect::from_xywh(0, 0, doc->getWidth().value(doc_unit), doc->getHeight().value(doc_unit))); - doc->ensureUpToDate(); - - // Scale and translate objects - double scale = Inkscape::Util::Quantity::convert(1, "px", doc_unit); - ShapeEditor::blockSetItem(true); - double dh; - if(SP_ACTIVE_DOCUMENT){ // for file menu open or import, or paste from clipboard - dh = SP_ACTIVE_DOCUMENT->getHeight().value("px"); - } - else { // for open via --file on command line - dh = doc->getHeight().value("px"); - } - doc->getRoot()->scaleChildItemsRec(Geom::Scale(scale), Geom::Point(0, dh)); - ShapeEditor::blockSetItem(false); - - Inkscape::DocumentUndo::setUndoSensitive(doc, saved); - } + setViewBoxIfMissing(doc); return doc; } diff --git a/src/extension/internal/emf-inout.h b/src/extension/internal/emf-inout.h index f15db5518..302d5c474 100644 --- a/src/extension/internal/emf-inout.h +++ b/src/extension/internal/emf-inout.h @@ -53,6 +53,7 @@ typedef struct emf_device_context { emf_device_context() : // SPStyle: class with constructor font_name(NULL), + clip_id(0), stroke_set(false), stroke_mode(0), stroke_idx(0), stroke_recidx(0), fill_set(false), fill_mode(0), fill_idx(0), fill_recidx(0), dirty(0), @@ -81,6 +82,7 @@ typedef struct emf_device_context { }; SPStyle style; char *font_name; + int clip_id; // 0 if none, else 1 + index into clips bool stroke_set; int stroke_mode; // enumeration from drawmode, not used if fill_set is not True int stroke_idx; // used with DRAW_PATTERN and DRAW_IMAGE to return the appropriate fill @@ -122,7 +124,7 @@ typedef struct emf_callback_data { arcdir(U_AD_COUNTERCLOCKWISE), dwRop2(U_R2_COPYPEN), dwRop3(0), MMX(0),MMY(0), - id(0), drawtype(0), + drawtype(0), pDesc(NULL), // hatches, images, gradients, struct w/ constructor tri(NULL), @@ -154,13 +156,13 @@ typedef struct emf_callback_data { float MMX; float MMY; - unsigned int id; unsigned int drawtype; // one of 0 or U_EMR_FILLPATH, U_EMR_STROKEPATH, U_EMR_STROKEANDFILLPATH char *pDesc; // both of these end up in <defs> under the names shown here. These structures allow duplicates to be avoided. EMF_STRINGS hatches; // hold pattern names, all like EMFhatch#_$$$$$$ where # is the EMF hatch code and $$$$$$ is the color EMF_STRINGS images; // hold images, all like Image#, where # is the slot the image lives. EMF_STRINGS gradients; // hold gradient names, all like EMF[HV]_$$$$$$_$$$$$$ where $$$$$$ are the colors + EMF_STRINGS clips; // hold clipping paths, referred to be the slot where the clipping path lives TR_INFO *tri; // Text Reassembly data structure @@ -199,12 +201,17 @@ protected: static int in_hatches(PEMF_CALLBACK_DATA d, char *test); static uint32_t add_hatch(PEMF_CALLBACK_DATA d, uint32_t hatchType, U_COLORREF hatchColor); static void enlarge_images(PEMF_CALLBACK_DATA d); - static int in_images(PEMF_CALLBACK_DATA d, char *test); + static int in_images(PEMF_CALLBACK_DATA d, const char *test); static uint32_t add_image(PEMF_CALLBACK_DATA d, void *pEmr, uint32_t cbBits, uint32_t cbBmi, uint32_t iUsage, uint32_t offBits, uint32_t offBmi); static void enlarge_gradients(PEMF_CALLBACK_DATA d); - static int in_gradients(PEMF_CALLBACK_DATA d, char *test); + static int in_gradients(PEMF_CALLBACK_DATA d, const char *test); static uint32_t add_gradient(PEMF_CALLBACK_DATA d, uint32_t gradientType, U_TRIVERTEX tv1, U_TRIVERTEX tv2); + + static void enlarge_clips(PEMF_CALLBACK_DATA d); + static int in_clips(PEMF_CALLBACK_DATA d, const char *test); + static void add_clips(PEMF_CALLBACK_DATA d, const char *clippath, unsigned int logic); + static void output_style(PEMF_CALLBACK_DATA d, int iType); static double _pix_x_to_point(PEMF_CALLBACK_DATA d, double px); static double _pix_y_to_point(PEMF_CALLBACK_DATA d, double py); diff --git a/src/extension/internal/emf-print.cpp b/src/extension/internal/emf-print.cpp index 8b80fec1c..9c68e40a4 100644 --- a/src/extension/internal/emf-print.cpp +++ b/src/extension/internal/emf-print.cpp @@ -34,6 +34,7 @@ #include <2geom/pathvector.h> #include <2geom/rect.h> #include <2geom/curves.h> +#include <sp-clippath.h> #include "helper/geom.h" #include "helper/geom-curves.h" @@ -975,6 +976,9 @@ unsigned int PrintEmf::fill( { using Geom::X; using Geom::Y; + + SPItem *item = SP_ITEM(style->object); + SPClipPath *scp = (item->clip_ref ? item->clip_ref->getObject() : NULL); Geom::Affine tf = m_tr_stack.top(); diff --git a/src/extension/internal/metafile-inout.cpp b/src/extension/internal/metafile-inout.cpp index 1d419a6a0..162ad8b7d 100644 --- a/src/extension/internal/metafile-inout.cpp +++ b/src/extension/internal/metafile-inout.cpp @@ -17,6 +17,7 @@ #include <glib.h> #include <glibmm/miscutils.h> +#include "sp-root.h" #include "display/curve.h" #include "extension/internal/metafile-inout.h" // picks up PNG #include "extension/print.h" @@ -27,6 +28,13 @@ #include "sp-pattern.h" #include "sp-radial-gradient.h" #include "style.h" +#include "document.h" +#include "util/units.h" +#include "shape-editor.h" +#include "sp-namedview.h" +#include "document-undo.h" +#include "inkscape.h" +#include "preferences.h" namespace Inkscape { namespace Extension { @@ -171,6 +179,88 @@ void Metafile::toPNG(PMEMPNG accum, int width, int height, const char *px){ } +/* If the viewBox is missing, set one +*/ +void Metafile::setViewBoxIfMissing(SPDocument *doc) { + + if (doc && !doc->getRoot()->viewBox_set) { + bool saved = Inkscape::DocumentUndo::getUndoSensitive(doc); + Inkscape::DocumentUndo::setUndoSensitive(doc, false); + + doc->ensureUpToDate(); + + // Set document unit + Inkscape::XML::Node *repr = sp_document_namedview(doc, 0)->getRepr(); + Inkscape::SVGOStringStream os; + Inkscape::Util::Unit const* doc_unit = doc->getWidth().unit; + os << doc_unit->abbr; + repr->setAttribute("inkscape:document-units", os.str().c_str()); + + // Set viewBox + doc->setViewBox(Geom::Rect::from_xywh(0, 0, doc->getWidth().value(doc_unit), doc->getHeight().value(doc_unit))); + doc->ensureUpToDate(); + + // Scale and translate objects + double scale = Inkscape::Util::Quantity::convert(1, "px", doc_unit); + ShapeEditor::blockSetItem(true); + double dh; + if(SP_ACTIVE_DOCUMENT){ // for file menu open or import, or paste from clipboard + dh = SP_ACTIVE_DOCUMENT->getHeight().value("px"); + } + else { // for open via --file on command line + dh = doc->getHeight().value("px"); + } + + // These should not affect input, but they do, so set them to a neutral state + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + bool transform_stroke = prefs->getBool("/options/transform/stroke", true); + bool transform_rectcorners = prefs->getBool("/options/transform/rectcorners", true); + bool transform_pattern = prefs->getBool("/options/transform/pattern", true); + bool transform_gradient = prefs->getBool("/options/transform/gradient", true); + prefs->setBool("/options/transform/stroke", true); + prefs->setBool("/options/transform/rectcorners", true); + prefs->setBool("/options/transform/pattern", true); + prefs->setBool("/options/transform/gradient", true); + + doc->getRoot()->scaleChildItemsRec(Geom::Scale(scale), Geom::Point(0, dh)); + ShapeEditor::blockSetItem(false); + + // restore options + prefs->setBool("/options/transform/stroke", transform_stroke); + prefs->setBool("/options/transform/rectcorners", transform_rectcorners); + prefs->setBool("/options/transform/pattern", transform_pattern); + prefs->setBool("/options/transform/gradient", transform_gradient); + + Inkscape::DocumentUndo::setUndoSensitive(doc, saved); + } +} + +/** + \fn Convert EMF/WMF region combining ops to livarot region combining ops + \return combination operators in livarot enumeration, or -1 on no match + \param ops (int) combination operator (Inkscape) +*/ +int Metafile::combine_ops_to_livarot(const int op) +{ + int ret = -1; + switch(op) { + case U_RGN_AND: + ret = bool_op_inters; + break; + case U_RGN_OR: + ret = bool_op_union; + break; + case U_RGN_XOR: + ret = bool_op_symdiff; + break; + case U_RGN_DIFF: + ret = bool_op_diff; + break; + } + return(ret); +} + + /* convert an EMF RGB(A) color to 0RGB inverse of gethexcolor() in emf-print.cpp diff --git a/src/extension/internal/metafile-inout.h b/src/extension/internal/metafile-inout.h index 968773a3a..b3efee2a6 100644 --- a/src/extension/internal/metafile-inout.h +++ b/src/extension/internal/metafile-inout.h @@ -71,6 +71,8 @@ protected: static void my_png_write_data(png_structp png_ptr, png_bytep data, png_size_t length); static void toPNG(PMEMPNG accum, int width, int height, const char *px); static gchar *bad_image_png(void); + static void setViewBoxIfMissing(SPDocument *doc); + static int combine_ops_to_livarot(const int op); private: diff --git a/src/extension/internal/wmf-inout.cpp b/src/extension/internal/wmf-inout.cpp index d13998d81..85060470b 100644 --- a/src/extension/internal/wmf-inout.cpp +++ b/src/extension/internal/wmf-inout.cpp @@ -33,9 +33,8 @@ #include <stdint.h> #include <libuemf/symbol_convert.h> -#include "sp-root.h" +#include "sp-root.h" // even though it is included indirectly by wmf-inout.h #include "sp-path.h" -#include "style.h" #include "print.h" #include "extension/system.h" #include "extension/print.h" @@ -44,13 +43,10 @@ #include "extension/output.h" #include "display/drawing.h" #include "display/drawing-item.h" -#include "util/units.h" #include "clear-n_.h" -#include "document.h" -#include "shape-editor.h" -#include "sp-namedview.h" -#include "document-undo.h" -#include "inkscape.h" +#include "svg/svg.h" +#include "util/units.h" // even though it is included indirectly by wmf-inout.h +#include "inkscape.h" // even though it is included indirectly by wmf-inout.h #include "wmf-inout.h" @@ -631,6 +627,67 @@ uint32_t Wmf::add_bm16_image(PWMF_CALLBACK_DATA d, U_BITMAP16 Bm16, const char * return(idx-1); } +/* Add another 100 blank slots to the clips array. +*/ +void Wmf::enlarge_clips(PWMF_CALLBACK_DATA d){ + d->clips.size += 100; + d->clips.strings = (char **) realloc(d->clips.strings,d->clips.size * sizeof(char *)); +} + +/* See if the pattern name is already in the list. If it is return its position (1->n, not 1-n-1) +*/ +int Wmf::in_clips(PWMF_CALLBACK_DATA d, const char *test){ + int i; + for(i=0; i<d->clips.count; i++){ + if(strcmp(test,d->clips.strings[i])==0)return(i+1); + } + return(0); +} + +/* (Conditionally) add a clip. + If a matching clip already exists nothing happens + If one does exist it is added to the clips list, entered into <defs>. +*/ +void Wmf::add_clips(PWMF_CALLBACK_DATA d, const char *clippath, unsigned int logic){ + int op = combine_ops_to_livarot(logic); + Geom::PathVector combined_vect; + char *combined = NULL; + if (op >= 0 && d->dc[d->level].clip_id) { + unsigned int real_idx = d->dc[d->level].clip_id - 1; + Geom::PathVector old_vect = sp_svg_read_pathv(d->clips.strings[real_idx]); + Geom::PathVector new_vect = sp_svg_read_pathv(clippath); + combined_vect = sp_pathvector_boolop(new_vect, old_vect, (bool_op) op , (FillRule) fill_oddEven, (FillRule) fill_oddEven); + combined = sp_svg_write_path(combined_vect); + } + else { + combined = strdup(clippath); // COPY operation, erases everything and starts a new one + } + + uint32_t idx = in_clips(d, combined); + if(!idx){ // add clip if not already present + if(d->clips.count == d->clips.size){ enlarge_clips(d); } + d->clips.strings[d->clips.count++]=strdup(combined); + d->dc[d->level].clip_id = d->clips.count; // one more than the slot where it is actually stored + SVGOStringStream tmp_clippath; + tmp_clippath << "\n<clipPath"; + tmp_clippath << "\n\tclipPathUnits=\"userSpaceOnUse\" "; + tmp_clippath << "\n\tid=\"clipEmfPath" << d->dc[d->level].clip_id << "\""; + tmp_clippath << " >"; + tmp_clippath << "\n\t<path d=\""; + tmp_clippath << combined; + tmp_clippath << "\""; + tmp_clippath << "\n\t/>"; + tmp_clippath << "\n</clipPath>"; + d->outdef += tmp_clippath.str().c_str(); + } + else { + d->dc[d->level].clip_id = idx; + } + free(combined); +} + + + void Wmf::output_style(PWMF_CALLBACK_DATA d) { @@ -838,9 +895,8 @@ Wmf::output_style(PWMF_CALLBACK_DATA d) tmp_style << "stroke-opacity:1;"; } tmp_style << "\" "; - if (clipset) - tmp_style << "\n\tclip-path=\"url(#clipWmfPath" << d->id << ")\" "; - clipset = false; + if (d->dc[d->level].clip_id) + tmp_style << "\n\tclip-path=\"url(#clipEmfPath" << d->dc[d->level].clip_id << ")\" "; d->outsvg += tmp_style.str().c_str(); } @@ -1918,34 +1974,51 @@ std::cout << "BEFORE DRAW" "\n\tM " << pix_to_xy( d, pt16.x, pt16.y ) << " "; break; } - case U_WMR_EXCLUDECLIPRECT: dbg_str << "<!-- U_WMR_EXCLUDECLIPRECT -->\n"; break; + case U_WMR_EXCLUDECLIPRECT: + { + dbg_str << "<!-- U_WMR_EXCLUDECLIPRECT -->\n"; + + U_RECT16 rc; + nSize = U_WMREXCLUDECLIPRECT_get(contents, &rc); + + SVGOStringStream tmp_path; + float faraway = 10000000; // hopefully well outside any real drawing! + //outer rect, clockwise + tmp_path << "M " << faraway << "," << faraway << " "; + tmp_path << "L " << faraway << "," << -faraway << " "; + tmp_path << "L " << -faraway << "," << -faraway << " "; + tmp_path << "L " << -faraway << "," << faraway << " "; + tmp_path << "z "; + //inner rect, counterclockwise (sign of Y is reversed) + tmp_path << "M " << pix_to_xy( d, rc.left , rc.top ) << " "; + tmp_path << "L " << pix_to_xy( d, rc.right, rc.top ) << " "; + tmp_path << "L " << pix_to_xy( d, rc.right, rc.bottom ) << " "; + tmp_path << "L " << pix_to_xy( d, rc.left, rc.bottom ) << " "; + tmp_path << "z"; + + add_clips(d, tmp_path.str().c_str(), U_RGN_AND); + + d->path = ""; + d->drawtype = 0; + break; + } case U_WMR_INTERSECTCLIPRECT: { dbg_str << "<!-- U_WMR_INTERSECTCLIPRECT -->\n"; nSize = U_WMRINTERSECTCLIPRECT_get(contents, &rc); - clipset = true; - if ((rc.left == rc_old.left) && (rc.top == rc_old.top) && (rc.right == rc_old.right) && (rc.bottom == rc_old.bottom)) - break; - rc_old = rc; - double dx = pix_to_x_point( d, rc.left, rc.top ); - double dy = pix_to_y_point( d, rc.left, rc.top ); - double dw = pix_to_abs_size( d, rc.right - rc.left + 1); - double dh = pix_to_abs_size( d, rc.bottom - rc.top + 1); + SVGOStringStream tmp_path; + tmp_path << "M " << pix_to_xy( d, rc.left , rc.top ) << " "; + tmp_path << "L " << pix_to_xy( d, rc.right, rc.top ) << " "; + tmp_path << "L " << pix_to_xy( d, rc.right, rc.bottom ) << " "; + tmp_path << "L " << pix_to_xy( d, rc.left, rc.bottom ) << " "; + tmp_path << "z"; + + add_clips(d, tmp_path.str().c_str(), U_RGN_AND); - SVGOStringStream tmp_rectangle; - tmp_rectangle << "\n<clipPath\n\tclipPathUnits=\"userSpaceOnUse\" "; - tmp_rectangle << "\nid=\"clipWmfPath" << ++(d->id) << "\" >"; - tmp_rectangle << "\n<rect "; - tmp_rectangle << "\n x=\"" << dx << "\" "; - tmp_rectangle << "\n y=\"" << dy << "\" "; - tmp_rectangle << "\n width=\"" << dw << "\" "; - tmp_rectangle << "\n height=\"" << dh << "\" />"; - tmp_rectangle << "\n</clipPath>"; - - d->outdef += tmp_rectangle.str().c_str(); d->path = ""; + d->drawtype = 0; break; } case U_WMR_ARC: @@ -2139,7 +2212,24 @@ std::cout << "BEFORE DRAW" break; } case U_WMR_SETPIXEL: dbg_str << "<!-- U_WMR_SETPIXEL -->\n"; break; - case U_WMR_OFFSETCLIPRGN: dbg_str << "<!-- U_WMR_OFFSETCLIPRGN/POLYLINE -->\n"; break; + case U_WMR_OFFSETCLIPRGN: + { + dbg_str << "<!-- U_EMR_OFFSETCLIPRGN -->\n"; + U_POINT16 off; + nSize = U_WMROFFSETCLIPRGN_get(contents,&off); + if (d->dc[d->level].clip_id) { // can only offset an existing clipping path + unsigned int real_idx = d->dc[d->level].clip_id - 1; + Geom::PathVector tmp_vect = sp_svg_read_pathv(d->clips.strings[real_idx]); + double ox = pix_to_x_point(d, off.x, off.y) - pix_to_x_point(d, 0, 0); // take into account all active transforms + double oy = pix_to_y_point(d, off.x, off.y) - pix_to_y_point(d, 0, 0); + Geom::Affine tf = Geom::Translate(ox,oy); + tmp_vect *= tf; + char *tmp_path = sp_svg_write_path(tmp_vect); + add_clips(d, tmp_path, U_RGN_COPY); + free(tmp_path); + } + break; + } // U_WMR_TEXTOUT should be here, but has been moved down to merge with U_WMR_EXTTEXTOUT case U_WMR_BITBLT: { @@ -2927,6 +3017,8 @@ void Wmf::free_wmf_strings(WMF_STRINGS name){ for(int i=0; i< name.count; i++){ free(name.strings[i]); } free(name.strings); } + name.count = 0; + name.size = 0; } SPDocument * @@ -2986,6 +3078,7 @@ Wmf::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri ) free_wmf_strings(d.hatches); free_wmf_strings(d.images); + free_wmf_strings(d.clips); if (d.wmf_obj) { int i; @@ -3002,39 +3095,7 @@ Wmf::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri ) d.tri = trinfo_release_except_FC(d.tri); - // Set viewBox if it doesn't exist - if (doc && !doc->getRoot()->viewBox_set) { - bool saved = Inkscape::DocumentUndo::getUndoSensitive(doc); - Inkscape::DocumentUndo::setUndoSensitive(doc, false); - - doc->ensureUpToDate(); - - // Set document unit - Inkscape::XML::Node *repr = sp_document_namedview(doc, 0)->getRepr(); - Inkscape::SVGOStringStream os; - Inkscape::Util::Unit const* doc_unit = doc->getWidth().unit; - os << doc_unit->abbr; - repr->setAttribute("inkscape:document-units", os.str().c_str()); - - // Set viewBox - doc->setViewBox(Geom::Rect::from_xywh(0, 0, doc->getWidth().value(doc_unit), doc->getHeight().value(doc_unit))); - doc->ensureUpToDate(); - - // Scale and translate objects - double scale = Inkscape::Util::Quantity::convert(1, "px", doc_unit); - ShapeEditor::blockSetItem(true); - double dh; - if(SP_ACTIVE_DOCUMENT){ // for file menu open or import, or paste from clipboard - dh = SP_ACTIVE_DOCUMENT->getHeight().value("px"); - } - else { // for open via --file on command line - dh = doc->getHeight().value("px"); - } - doc->getRoot()->scaleChildItemsRec(Geom::Scale(scale), Geom::Point(0, dh)); - ShapeEditor::blockSetItem(false); - - Inkscape::DocumentUndo::setUndoSensitive(doc, saved); - } + setViewBoxIfMissing(doc); return doc; } diff --git a/src/extension/internal/wmf-inout.h b/src/extension/internal/wmf-inout.h index 6006479c7..27ec14358 100644 --- a/src/extension/internal/wmf-inout.h +++ b/src/extension/internal/wmf-inout.h @@ -52,6 +52,7 @@ typedef struct wmf_device_context { wmf_device_context() : // SPStyle: class with constructor font_name(NULL), + clip_id(0), stroke_set(false), stroke_mode(0), stroke_idx(0), stroke_recidx(0), fill_set(false), fill_mode(0), fill_idx(0), fill_recidx(0), dirty(0), @@ -75,6 +76,7 @@ typedef struct wmf_device_context { }; SPStyle style; char *font_name; + int clip_id; // 0 if none, else 1 + index into clips bool stroke_set; int stroke_mode; // enumeration from drawmode, not used if fill_set is not True int stroke_idx; // used with DRAW_PATTERN and DRAW_IMAGE to return the appropriate fill @@ -155,6 +157,7 @@ typedef struct wmf_callback_data { // both of these end up in <defs> under the names shown here. These structures allow duplicates to be avoided. WMF_STRINGS hatches; // hold pattern names, all like WMFhatch#_$$$$$$ where # is the WMF hatch code and $$$$$$ is the color WMF_STRINGS images; // hold images, all like Image#, where # is the slot the image lives. + WMF_STRINGS clips; // hold clipping paths, referred to be the slot where the clipping path lives TR_INFO *tri; // Text Reassembly data structure @@ -195,6 +198,11 @@ protected: static int in_images(PWMF_CALLBACK_DATA d, char *test); static uint32_t add_dib_image(PWMF_CALLBACK_DATA d, const char *dib, uint32_t iUsage); static uint32_t add_bm16_image(PWMF_CALLBACK_DATA d, U_BITMAP16 Bm16, const char *px); + + static void enlarge_clips(PWMF_CALLBACK_DATA d); + static int in_clips(PWMF_CALLBACK_DATA d, const char *test); + static void add_clips(PWMF_CALLBACK_DATA d, const char *clippath, unsigned int logic); + static void output_style(PWMF_CALLBACK_DATA d); static double _pix_x_to_point(PWMF_CALLBACK_DATA d, double px); static double _pix_y_to_point(PWMF_CALLBACK_DATA d, double py); diff --git a/src/libuemf/uemf.c b/src/libuemf/uemf.c index 069cf901b..1d417dece 100644 --- a/src/libuemf/uemf.c +++ b/src/libuemf/uemf.c @@ -16,8 +16,8 @@ /* File: uemf.c -Version: 0.0.26 -Date: 24-MAR-2014 +Version: 0.0.28 +Date: 04-APR-2014 Author: David Mathog, Biology Division, Caltech email: mathog@caltech.edu Copyright: 2014 David Mathog and California Institute of Technology (Caltech) @@ -417,7 +417,7 @@ uint32_t emr_properties(uint32_t type){ table[ 64] = 0x90; // U_EMRSTROKEPATH 1 0 0 1 0 0 0 0 table[ 65] = 0xA0; // U_EMRFLATTENPATH 1 0 1 0 0 0 0 0 table[ 66] = 0xA0; // U_EMRWIDENPATH 1 0 1 0 0 0 0 0 - table[ 67] = 0xA0; // U_EMRSELECTCLIPPATH 1 0 1 0 0 0 0 0 + table[ 67] = 0x80; // U_EMRSELECTCLIPPATH 1 0 0 0 0 0 0 0 consumes the path, draws nothing table[ 68] = 0xA0; // U_EMRABORTPATH 1 0 1 0 0 0 0 0 table[ 69] = 0xA0; // U_EMRUNDEF69 1 0 1 0 0 0 0 0 table[ 70] = 0x00; // U_EMRCOMMENT 0 0 0 0 0 0 0 0 diff --git a/src/libuemf/uemf.h b/src/libuemf/uemf.h index cde1b6c85..a792a65de 100644 --- a/src/libuemf/uemf.h +++ b/src/libuemf/uemf.h @@ -95,8 +95,8 @@ these WMF enumerations is by referencing the following table: /* File: uemf.h -Version: 0.0.26 -Date: 27-MAR-2014 +Version: 0.0.27 +Date: 28-MAR-2014 Author: David Mathog, Biology Division, Caltech email: mathog@caltech.edu Copyright: 2014 David Mathog and California Institute of Technology (Caltech) @@ -1167,6 +1167,7 @@ typedef struct { EMF manual 2.1.29 @{ */ +#define U_RGN_NONE 0 //!< not part of EMF standard, may be used by others #define U_RGN_AND 1 //!< Region becomes intersection of existing region and new region. #define U_RGN_OR 2 //!< Region becomes union of existing region and new region. #define U_RGN_XOR 3 //!< Region becomes XOR of existing and new regions. diff --git a/src/libuemf/uwmf.c b/src/libuemf/uwmf.c index b4a1dfc52..b378e587f 100644 --- a/src/libuemf/uwmf.c +++ b/src/libuemf/uwmf.c @@ -19,8 +19,8 @@ /* File: uwmf.c -Version: 0.0.14 -Date: 24-MAR-2014 +Version: 0.0.15 +Date: 11-APR-2014 Author: David Mathog, Biology Division, Caltech email: mathog@caltech.edu Copyright: 2014 David Mathog and California Institute of Technology (Caltech) @@ -3019,7 +3019,7 @@ char *U_WMRESCAPE_set(uint16_t Escape, uint16_t Length, const void *Data){ \param DC Drawing Context to restore. (negative is relative to current, positive is absolute) */ char *U_WMRRESTOREDC_set(int16_t DC){ - return U_WMRCORE_1U16_set(U_WMR_SETMAPMODE, DC); + return U_WMRCORE_1U16_set(U_WMR_RESTOREDC, DC); } /** diff --git a/src/seltrans.cpp b/src/seltrans.cpp index 60a0bfa11..7708c999e 100644 --- a/src/seltrans.cpp +++ b/src/seltrans.cpp @@ -377,6 +377,12 @@ void Inkscape::SelTrans::transform(Geom::Affine const &rel_affine, Geom::Point c g_return_if_fail(_grabbed); g_return_if_fail(!_empty); + // E.g. scaling a perfectly vertical line in horizontal direction will not work, and will produce an identity affine + + if (rel_affine.isIdentity()) { + return; + } + Geom::Affine const affine( Geom::Translate(-norm) * rel_affine * Geom::Translate(norm) ); if (_show == SHOW_CONTENT) { @@ -389,6 +395,7 @@ void Inkscape::SelTrans::transform(Geom::Affine const &rel_affine, Geom::Point c } Geom::Affine const &prev_transform = _items_affines[i]; item.set_i2d_affine(prev_transform * affine); + // The new affine will only have been applied if the transformation is different from the previous one, see SPItem::set_item_transform } } else { if (_bbox) { @@ -439,21 +446,25 @@ void Inkscape::SelTrans::ungrab() _message_context.clear(); if (!_empty && _changed) { - sp_selection_apply_affine(selection, _current_relative_affine, (_show == SHOW_OUTLINE)? true : false); - if (_center) { - *_center *= _current_relative_affine; - _center_is_set = true; - } + if (!_current_relative_affine.isIdentity()) { // we can have a identity affine + // when trying to stretch a perfectly vertical line in horizontal direction, which will not be allowed by the handles; -// If dragging showed content live, sp_selection_apply_affine cannot change the centers -// appropriately - it does not know the original positions of the centers (all objects already have -// the new bboxes). So we need to reset the centers from our saved array. - if (_show != SHOW_OUTLINE && !_current_relative_affine.isTranslation()) { - for (unsigned i = 0; i < _items_centers.size(); i++) { - SPItem *currentItem = _items[i]; - if (currentItem->isCenterSet()) { // only if it's already set - currentItem->setCenter (_items_centers[i] * _current_relative_affine); - currentItem->updateRepr(); + sp_selection_apply_affine(selection, _current_relative_affine, (_show == SHOW_OUTLINE)? true : false); + if (_center) { + *_center *= _current_relative_affine; + _center_is_set = true; + } + + // If dragging showed content live, sp_selection_apply_affine cannot change the centers + // appropriately - it does not know the original positions of the centers (all objects already have + // the new bboxes). So we need to reset the centers from our saved array. + if (_show != SHOW_OUTLINE && !_current_relative_affine.isTranslation()) { + for (unsigned i = 0; i < _items_centers.size(); i++) { + SPItem *currentItem = _items[i]; + if (currentItem->isCenterSet()) { // only if it's already set + currentItem->setCenter (_items_centers[i] * _current_relative_affine); + currentItem->updateRepr(); + } } } } @@ -463,18 +474,22 @@ void Inkscape::SelTrans::ungrab() _items_affines.clear(); _items_centers.clear(); - if (_current_relative_affine.isTranslation()) { - DocumentUndo::done(sp_desktop_document(_desktop), SP_VERB_CONTEXT_SELECT, - _("Move")); - } else if (_current_relative_affine.withoutTranslation().isScale()) { - DocumentUndo::done(sp_desktop_document(_desktop), SP_VERB_CONTEXT_SELECT, - _("Scale")); - } else if (_current_relative_affine.withoutTranslation().isRotation()) { - DocumentUndo::done(sp_desktop_document(_desktop), SP_VERB_CONTEXT_SELECT, - _("Rotate")); - } else { - DocumentUndo::done(sp_desktop_document(_desktop), SP_VERB_CONTEXT_SELECT, - _("Skew")); + if (!_current_relative_affine.isIdentity()) { // we can have a identity affine + // when trying to stretch a perfectly vertical line in horizontal direction, which will not be allowed + // by the handles; this would be identified as a (zero) translation by isTranslation() + if (_current_relative_affine.isTranslation()) { + DocumentUndo::done(sp_desktop_document(_desktop), SP_VERB_CONTEXT_SELECT, + _("Move")); + } else if (_current_relative_affine.withoutTranslation().isScale()) { + DocumentUndo::done(sp_desktop_document(_desktop), SP_VERB_CONTEXT_SELECT, + _("Scale")); + } else if (_current_relative_affine.withoutTranslation().isRotation()) { + DocumentUndo::done(sp_desktop_document(_desktop), SP_VERB_CONTEXT_SELECT, + _("Rotate")); + } else { + DocumentUndo::done(sp_desktop_document(_desktop), SP_VERB_CONTEXT_SELECT, + _("Skew")); + } } } else { @@ -506,7 +521,7 @@ void Inkscape::SelTrans::stamp() bool fixup = !_grabbed; if ( fixup && _stamp_cache ) { - // TODO - give a proper fix. Simple temproary work-around for the grab() issue + // TODO - give a proper fix. Simple temporary work-around for the grab() issue g_slist_free(_stamp_cache); _stamp_cache = NULL; } @@ -565,7 +580,7 @@ void Inkscape::SelTrans::stamp() } if ( fixup && _stamp_cache ) { - // TODO - give a proper fix. Simple temproary work-around for the grab() issue + // TODO - give a proper fix. Simple temporary work-around for the grab() issue g_slist_free(_stamp_cache); _stamp_cache = NULL; } diff --git a/src/sp-ellipse.cpp b/src/sp-ellipse.cpp index 25e4c07d7..cda59e057 100644 --- a/src/sp-ellipse.cpp +++ b/src/sp-ellipse.cpp @@ -419,62 +419,35 @@ void SPGenericEllipse::set_shape() // For simplicity, we use a circle with center (0, 0) and radius 1 for our calculations. Geom::Circle circle(0, 0, 1); - if (this->_isSlice()) { - Geom::Point center(0, 0); - Geom::Point start_point = Geom::Point::polar(start); - Geom::Point end_point = Geom::Point::polar(end); - Geom::Point middle_point = make_angle_bisector_ray(Geom::Ray(center, start), Geom::Ray(center, end)).versor(); - - Geom::EllipticalArc *arc = circle.arc(start_point, middle_point, end_point); - - Geom::Path path(start_point); - path.append(*arc); - - delete arc; - - Geom::PathBuilder pb; - pb.append(path); - - if (this->_closed) { - // "pizza slice" - pb.lineTo(center); - pb.closePath(); - } else { - // arc only - pb.flush(); - } - - curve = new SPCurve(pb.peek()); - } else { - // Full ellipse - // This code converts the circle to four elliptical arcs explicitly. - // Circle::getPath currently creates cubic bezier curves, these are not suitable here - // as a circle should have four mid markers at 0, 90, 180, 270 degrees. - Geom::Path path(Geom::Point::polar(0)); - Geom::EllipticalArc* arc; - - arc = circle.arc(Geom::Point::polar(0), Geom::Point::polar(M_PI / 4.0), Geom::Point::polar(M_PI / 2.0)); - path.append(*arc); - delete arc; - - arc = circle.arc(Geom::Point::polar(M_PI / 2.0), Geom::Point::polar(3.0 * M_PI / 4.0), Geom::Point::polar(M_PI)); - path.append(*arc); - delete arc; + if (!this->_isSlice()) { + start = 0.0; + end = 2.0*M_PI; + } + double incr = end - start; // arc angle + if (incr < 0.0) incr += 2.0*M_PI; - arc = circle.arc(Geom::Point::polar(M_PI), Geom::Point::polar(5.0 * M_PI / 4.0), Geom::Point::polar(3.0 * M_PI / 2.0)); - path.append(*arc); - delete arc; + int numsegs = 1 + int(incr*2.0/M_PI); // number of arc segments + if (numsegs > 4) numsegs = 4; - arc = circle.arc(Geom::Point::polar(3.0 * M_PI / 2.0), Geom::Point::polar(7.0 * M_PI / 4.0), Geom::Point::polar(2.0 * M_PI)); + incr = incr/numsegs; // limit arc angle to less than 90 degrees + Geom::Path path(Geom::Point::polar(start)); + Geom::EllipticalArc* arc; + for (int seg = 0; seg < numsegs; seg++) { + arc = circle.arc(Geom::Point::polar(start + seg*incr), Geom::Point::polar(start + (seg + 0.5)*incr), Geom::Point::polar(start + (seg + 1.0)*incr)); path.append(*arc); delete arc; - - Geom::PathBuilder pb; - pb.append(path); + } + Geom::PathBuilder pb; + pb.append(path); + if (this->_isSlice() && this->_closed) { + pb.lineTo(Geom::Point(0, 0)); + } + if (this->_closed) { pb.closePath(); - - curve = new SPCurve(pb.peek()); + } else { + pb.flush(); } + curve = new SPCurve(pb.peek()); // gchar *str = sp_svg_write_path(curve->get_pathvector()); // std::cout << " path: " << str << std::endl; diff --git a/src/sp-lpe-item.cpp b/src/sp-lpe-item.cpp index 33359727a..762dc3191 100644 --- a/src/sp-lpe-item.cpp +++ b/src/sp-lpe-item.cpp @@ -442,6 +442,9 @@ void SPLPEItem::addPathEffect(LivePathEffectObject * new_lpeobj) g_free(hrefstr); } +/** + * If keep_path == true, the item should not be updated, effectively 'flattening' the LPE. + */ void SPLPEItem::removeCurrentPathEffect(bool keep_paths) { Inkscape::LivePathEffect::LPEObjectReference* lperef = this->getCurrentLPEReference(); @@ -456,27 +459,31 @@ void SPLPEItem::removeCurrentPathEffect(bool keep_paths) this->getRepr()->setAttribute("inkscape:path-effect", r.c_str()); } else { this->getRepr()->setAttribute("inkscape:path-effect", NULL); + } + + if (!keep_paths) { // Make sure that ellipse is stored as <svg:circle> or <svg:ellipse> if possible. if( SP_IS_GENERICELLIPSE(this)) { SP_GENERICELLIPSE(this)->write( this->getRepr()->document(), this->getRepr(), SP_OBJECT_WRITE_EXT ); } - } - if (!keep_paths) { sp_lpe_item_cleanup_original_path_recursive(this); } } +/** + * If keep_path == true, the item should not be updated, effectively 'flattening' the LPE. + */ void SPLPEItem::removeAllPathEffects(bool keep_paths) { this->getRepr()->setAttribute("inkscape:path-effect", NULL); - // Make sure that ellipse is stored as <svg:circle> or <svg:ellipse> if possible. - if( SP_IS_GENERICELLIPSE(this)) { - SP_GENERICELLIPSE(this)->write( this->getRepr()->document(), this->getRepr(), SP_OBJECT_WRITE_EXT ); - } - if (!keep_paths) { + // Make sure that ellipse is stored as <svg:circle> or <svg:ellipse> if possible. + if (SP_IS_GENERICELLIPSE(this)) { + SP_GENERICELLIPSE(this)->write(this->getRepr()->document(), this->getRepr(), SP_OBJECT_WRITE_EXT); + } + sp_lpe_item_cleanup_original_path_recursive(this); } } diff --git a/src/splivarot.cpp b/src/splivarot.cpp index 40f3f174a..8bb2a7150 100644 --- a/src/splivarot.cpp +++ b/src/splivarot.cpp @@ -1457,13 +1457,21 @@ sp_selected_path_outline(SPDesktop *desktop) g_repr, xml_doc, doc ); } } + //bug lp:1290573 : completely destroy the old object first + curve->unref(); + selection->remove(item); + item->deleteObject(false); selection->add(g_repr); Inkscape::GC::release(g_repr); - - } else { + } else + { + //lp:1290573 + curve->unref(); + selection->remove(item); + item->deleteObject(false); // add the new repr to the parent parent->appendChild(repr); @@ -1489,10 +1497,6 @@ sp_selected_path_outline(SPDesktop *desktop) Inkscape::GC::release(repr); - curve->unref(); - selection->remove(item); - item->deleteObject(false); - } if (title) { g_free(title); diff --git a/src/style-internal.cpp b/src/style-internal.cpp index b03e848ba..df08d0adf 100644 --- a/src/style-internal.cpp +++ b/src/style-internal.cpp @@ -693,6 +693,14 @@ SPIString::write( guint const flags, SPIBase const *const base) const { } void +SPIString::clear() { + SPIBase::clear(); + g_free( value ); + value = NULL; + if( value_default ) value = strdup( value_default ); +} + +void SPIString::cascade( const SPIBase* const parent ) { if( const SPIString* p = dynamic_cast<const SPIString*>(parent) ) { if( inherits && (!set || inherit) ) { diff --git a/src/style-internal.h b/src/style-internal.h index 131b77b77..e9cf6e604 100644 --- a/src/style-internal.h +++ b/src/style-internal.h @@ -128,6 +128,7 @@ class SPIBase { set = rhs.set; inherit = rhs.inherit; style_att = rhs.style_att; + style = rhs.style; return *this; } @@ -166,7 +167,7 @@ class SPIFloat : public SPIBase { SPIFloat& operator=(const SPIFloat& rhs) { SPIBase::operator=(rhs); value = rhs.value; - value_default = value_default; + value_default = rhs.value_default; return *this; } @@ -229,7 +230,8 @@ class SPIScale24 : public SPIBase { SPIScale24& operator=(const SPIScale24& rhs) { SPIBase::operator=(rhs); - value = rhs.value; + value = rhs.value; + value_default = rhs.value_default; return *this; } @@ -385,19 +387,24 @@ class SPIString : public SPIBase { public: SPIString() : SPIBase( "anonymous_string" ), value(NULL) {}; - SPIString( Glib::ustring name ) : - SPIBase( name ) , value(NULL) {}; - virtual ~SPIString() { g_free(value); }; + SPIString( Glib::ustring name, gchar* value_default_in = NULL ) : + SPIBase( name ) , value(NULL) , value_default(NULL) { + value_default = value_default_in?g_strdup(value_default_in):NULL; + }; + virtual ~SPIString() { g_free(value); g_free(value_default); }; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPIBase::clear(); g_free( value ); value = NULL; }; + virtual void clear(); virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); SPIString& operator=(const SPIString& rhs) { SPIBase::operator=(rhs); + g_free(value); + g_free(value_default); value = rhs.value?g_strdup(rhs.value):NULL; + value_default = rhs.value_default?g_strdup(rhs.value_default):NULL; return *this; } @@ -407,6 +414,7 @@ class SPIString : public SPIBase { // To do: make private, convert value to Glib::ustring public: gchar *value; + gchar *value_default; }; /// Color type interal to SPStyle, FIXME Add string value to store SVG named color. @@ -425,7 +433,8 @@ class SPIColor : public SPIBase { SPIColor& operator=(const SPIColor& rhs) { SPIBase::operator=(rhs); - value.color = rhs.value.color; + currentcolor = rhs.currentcolor; + value.color = rhs.value.color; return *this; } @@ -553,6 +562,7 @@ class SPIPaintOrder : public SPIBase { layer[i] = rhs.layer[i]; layer_set[i] = rhs.layer_set[i]; } + g_free(value); value = g_strdup(rhs.value); return *this; } diff --git a/src/style.cpp b/src/style.cpp index 2d66284d4..11b1dc440 100644 --- a/src/style.cpp +++ b/src/style.cpp @@ -112,7 +112,7 @@ SPStyle::SPStyle(SPDocument *document_in, SPObject *object_in) : font_stretch( "font-stretch", enum_font_stretch, SP_CSS_FONT_STRETCH_NORMAL ), font_size(), line_height( "line-height", 1.0 ), // SPILengthOrNormal - font_family( "font-family" ), // SPIString + font_family( "font-family", "sans-serif" ), // SPIString w/default font(), // SPIFont font_specification( "-inkscape-font-specification" ), // SPIString diff --git a/src/verbs.cpp b/src/verbs.cpp index ab6c25973..0c329cab8 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -1372,6 +1372,10 @@ void LayerVerb::perform(SPAction *action, void *data) survivor = Inkscape::previous_layer(dt->currentRoot(), old_layer); } + if (survivor == old_layer->lastChild()) { + //oops: layer_fns messed up. BADLY. + survivor = NULL; + } // Deleting the old layer before switching layers is a hack to trigger the // listeners of the deletion event (as happens when old_layer is deleted using the // xml editor). See @@ -2364,8 +2368,7 @@ Verb *Verb::_base_verbs[] = { INKSCAPE_ICON("document-cleanup") ), new FileVerb(SP_VERB_FILE_IMPORT, "FileImport", N_("_Import..."), N_("Import a bitmap or SVG image into this document"), INKSCAPE_ICON("document-import")), - new FileVerb(SP_VERB_FILE_EXPORT, "FileExport", N_("_Export Bitmap..."), - N_("Export this document or a selection as a bitmap image"), INKSCAPE_ICON("document-export")), +// new FileVerb(SP_VERB_FILE_EXPORT, "FileExport", N_("_Export Bitmap..."), N_("Export this document or a selection as a bitmap image"), INKSCAPE_ICON("document-export")), new FileVerb(SP_VERB_FILE_IMPORT_FROM_OCAL, "FileImportFromOCAL", N_("Import Clip Art..."), N_("Import clipart from Open Clip Art Library"), INKSCAPE_ICON("document-import-ocal")), // new FileVerb(SP_VERB_FILE_EXPORT_TO_OCAL, "FileExportToOCAL", N_("Export To Open Clip Art Library"), N_("Export this document to Open Clip Art Library"), INKSCAPE_ICON_DOCUMENT_EXPORT_OCAL), diff --git a/src/verbs.h b/src/verbs.h index 297ef1655..6ad4d0fee 100644 --- a/src/verbs.h +++ b/src/verbs.h @@ -52,7 +52,7 @@ enum { SP_VERB_FILE_PRINT, SP_VERB_FILE_VACUUM, SP_VERB_FILE_IMPORT, - SP_VERB_FILE_EXPORT, +// SP_VERB_FILE_EXPORT, SP_VERB_FILE_IMPORT_FROM_OCAL, /**< Import the file from Open Clip Art Library */ // SP_VERB_FILE_EXPORT_TO_OCAL, /**< Export the file to Open Clip Art Library */ SP_VERB_FILE_NEXT_DESKTOP, |
