summaryrefslogtreecommitdiffstats
path: root/src/extension
diff options
context:
space:
mode:
authormathog <>2014-04-29 00:06:10 +0000
committermathog <>2014-04-29 00:06:10 +0000
commit752397f4edd00eed397464556e845b951ff65c57 (patch)
treee07b82ef2094e0d669f92f9599c455c57565ce69 /src/extension
parentwhen removing LPE, with 'flattening' option, don't recalculate/rewrite ellips... (diff)
downloadinkscape-752397f4edd00eed397464556e845b951ff65c57.tar.gz
inkscape-752397f4edd00eed397464556e845b951ff65c57.zip
Improved support for clipping on EMF/WMF import, see bug 1302857
(bzr r13322)
Diffstat (limited to 'src/extension')
-rw-r--r--src/extension/internal/emf-inout.cpp178
-rw-r--r--src/extension/internal/emf-inout.h15
-rw-r--r--src/extension/internal/emf-print.cpp4
-rw-r--r--src/extension/internal/metafile-inout.cpp71
-rw-r--r--src/extension/internal/metafile-inout.h1
-rw-r--r--src/extension/internal/wmf-inout.cpp146
-rw-r--r--src/extension/internal/wmf-inout.h8
7 files changed, 337 insertions, 86 deletions
diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp
index 6455e7555..863d1e006 100644
--- a/src/extension/internal/emf-inout.cpp
+++ b/src/extension/internal/emf-inout.cpp
@@ -44,6 +44,7 @@
#include "display/drawing.h"
#include "display/drawing-item.h"
#include "clear-n_.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
@@ -60,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;
@@ -447,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);
@@ -618,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);
@@ -717,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
@@ -927,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();
}
@@ -1565,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;
@@ -1616,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)
@@ -1643,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);
@@ -2115,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";
@@ -2131,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;
@@ -2171,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;
}
@@ -2727,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";
@@ -2770,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:
@@ -3334,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 *
@@ -3381,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;
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 53bb86d24..162ad8b7d 100644
--- a/src/extension/internal/metafile-inout.cpp
+++ b/src/extension/internal/metafile-inout.cpp
@@ -179,28 +179,6 @@ void Metafile::toPNG(PMEMPNG accum, int width, int height, const char *px){
}
-
-/* convert an EMF RGB(A) color to 0RGB
-inverse of gethexcolor() in emf-print.cpp
-*/
-uint32_t Metafile::sethexcolor(U_COLORREF color){
-
- uint32_t out;
- out = (U_RGBAGetR(color) << 16) +
- (U_RGBAGetG(color) << 8 ) +
- (U_RGBAGetB(color) );
- return(out);
-}
-
-/* Return the base64 encoded png which is shown for all bad images.
-Currently a random 3x4 blotch.
-Caller must free.
-*/
-gchar *Metafile::bad_image_png(void){
- gchar *gstring = g_strdup("iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAAA3NCSVQICAjb4U/gAAAALElEQVQImQXBQQ2AMAAAsUJQMSWI2H8qME1yMshojwrvGB8XcHKvR1XtOTc/8HENumHCsOMAAAAASUVORK5CYII=");
- return(gstring);
-}
-
/* If the viewBox is missing, set one
*/
void Metafile::setViewBoxIfMissing(SPDocument *doc) {
@@ -257,6 +235,55 @@ void Metafile::setViewBoxIfMissing(SPDocument *doc) {
}
}
+/**
+ \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
+*/
+uint32_t Metafile::sethexcolor(U_COLORREF color){
+
+ uint32_t out;
+ out = (U_RGBAGetR(color) << 16) +
+ (U_RGBAGetG(color) << 8 ) +
+ (U_RGBAGetB(color) );
+ return(out);
+}
+
+/* Return the base64 encoded png which is shown for all bad images.
+Currently a random 3x4 blotch.
+Caller must free.
+*/
+gchar *Metafile::bad_image_png(void){
+ gchar *gstring = g_strdup("iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAAA3NCSVQICAjb4U/gAAAALElEQVQImQXBQQ2AMAAAsUJQMSWI2H8qME1yMshojwrvGB8XcHKvR1XtOTc/8HENumHCsOMAAAAASUVORK5CYII=");
+ return(gstring);
+}
+
+
} // namespace Internal
} // namespace Extension
diff --git a/src/extension/internal/metafile-inout.h b/src/extension/internal/metafile-inout.h
index 2f7001cf2..b3efee2a6 100644
--- a/src/extension/internal/metafile-inout.h
+++ b/src/extension/internal/metafile-inout.h
@@ -72,6 +72,7 @@ protected:
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 5d7fc29d0..85060470b 100644
--- a/src/extension/internal/wmf-inout.cpp
+++ b/src/extension/internal/wmf-inout.cpp
@@ -44,6 +44,7 @@
#include "display/drawing.h"
#include "display/drawing-item.h"
#include "clear-n_.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
@@ -626,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)
{
@@ -833,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();
}
@@ -1913,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:
@@ -2134,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:
{
@@ -2922,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 *
@@ -2981,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;
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);