summaryrefslogtreecommitdiffstats
path: root/src/extension/internal/emf-print.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/extension/internal/emf-print.cpp')
-rw-r--r--src/extension/internal/emf-print.cpp187
1 files changed, 163 insertions, 24 deletions
diff --git a/src/extension/internal/emf-print.cpp b/src/extension/internal/emf-print.cpp
index e054829b5..0f43fbca1 100644
--- a/src/extension/internal/emf-print.cpp
+++ b/src/extension/internal/emf-print.cpp
@@ -55,6 +55,7 @@
#include "sp-radial-gradient.h"
#include "sp-linear-gradient.h"
#include "display/cairo-utils.h"
+#include "sp-shape.h"
#include "splivarot.h" // pieces for union on shapes
#include "2geom/svg-path-parser.h" // to get from SVG text to Geom::Path
@@ -143,6 +144,7 @@ unsigned int PrintEmf::begin(Inkscape::Extension::Print *mod, SPDocument *doc)
// width and height in px
_width = doc->getWidth().value("px");
_height = doc->getHeight().value("px");
+ _doc_unit_scale = Inkscape::Util::Quantity::convert(1, (doc->getDefaultUnit()), "px");
// initialize a few global variables
hbrush = hbrushOld = hpen = 0;
@@ -298,6 +300,7 @@ unsigned int PrintEmf::begin(Inkscape::Extension::Print *mod, SPDocument *doc)
unsigned int PrintEmf::finish(Inkscape::Extension::Print * /*mod*/)
{
+ do_clip_if_present(NULL); // Terminate any open clip.
char *rec;
if (!et) {
return 0;
@@ -969,19 +972,145 @@ U_TRIVERTEX PrintEmf::make_trivertex(Geom::Point Pt, U_COLORREF uc){
return(tv);
}
+/* Examine clip. If there is a (new) one then apply it. If there is one and it is the
+ same as the preceding one, leave the preceding one active. If style is NULL
+ terminate the current clip, if any, and return.
+*/
+void PrintEmf::do_clip_if_present(SPStyle const *style){
+ char *rec;
+ static SPClipPath *scpActive = NULL;
+ if(!style){
+ if(scpActive){ // clear the existing clip
+ rec = U_EMRRESTOREDC_set(-1);
+ if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintEmf::fill at U_EMRRESTOREDC_set");
+ }
+ scpActive=NULL;
+ }
+ } else {
+ /* The current implementation converts only one level of clipping. If there were more
+ clips further up the stack they should be combined with the pathvector using "and". Since this
+ comes up rarely, and would involve a lot of searching (all the way up the stack for every
+ draw operation), it has not yet been implemented.
+
+ Note, to debug this section of code use print statements on sp_svg_write_path(combined_pathvector).
+ */
+ /* find the first clip_ref at object or up the stack. There may not be one. */
+ SPClipPath *scp = NULL;
+ SPItem *item = SP_ITEM(style->object);
+ while(1) {
+ scp = (item->clip_ref ? item->clip_ref->getObject() : NULL);
+ if(scp)break;
+ item = SP_ITEM(item->parent);
+ if(!item || SP_IS_ROOT(item))break; // this will never be a clipping path
+ }
+
+ if(scp != scpActive){ // change or remove the clipping
+ if(scpActive){ // clear the existing clip
+ rec = U_EMRRESTOREDC_set(-1);
+ if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintEmf::fill at U_EMRRESTOREDC_set");
+ }
+ scpActive = NULL;
+ }
+
+ if (scp) { // set the new clip
+ /* because of units and who knows what other transforms that might be applied above we
+ need the full transform all the way to the root.
+ */
+ Geom::Affine tf = item->transform;
+ SPItem *scan_item = item;
+ while(1) {
+ scan_item = SP_ITEM(scan_item->parent);
+ if(!scan_item)break;
+ tf *= scan_item->transform;
+ }
+ tf *= Geom::Scale(_doc_unit_scale);; // Transform must be in PIXELS, no matter what the document unit is.
+
+ /* find the clipping path */
+ Geom::PathVector combined_pathvector;
+ Geom::Affine tfc; // clipping transform, generally not the same as item transform
+ for(item = SP_ITEM(scp->firstChild()); item; item=SP_ITEM(item->getNext())){
+ if (SP_IS_GROUP(item)) { // not implemented
+ // return sp_group_render(item);
+ combined_pathvector = merge_PathVector_with_group(combined_pathvector, item, tfc);
+ } else if (SP_IS_SHAPE(item)) {
+ combined_pathvector = merge_PathVector_with_shape(combined_pathvector, item, tfc);
+ } else { // not implemented
+ }
+ }
+
+ if (!combined_pathvector.empty()) { // if clipping path isn't empty, define EMF clipping record
+ scpActive = scp; // remember for next time
+ // the sole purpose of this SAVEDC is to let us clear the clipping region later.
+ rec = U_EMRSAVEDC_set();
+ if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintEmf::image at U_EMRSAVEDC_set");
+ }
+ (void) draw_pathv_to_EMF(combined_pathvector, tf);
+ rec = U_EMRSELECTCLIPPATH_set(U_RGN_OR);
+ if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintEmf::do_clip_if_present at U_EMRSELECTCLIPPATH_set");
+ }
+ }
+ else {
+ scpActive = NULL; // no valid path available to draw, so no DC was saved, so no signal to restore
+ }
+ } // change or remove clipping
+ } // scp exists
+ } // style exists
+}
+
+Geom::PathVector PrintEmf::merge_PathVector_with_group(Geom::PathVector const &combined_pathvector, SPItem const *item, const Geom::Affine &transform)
+{
+ Geom::PathVector new_combined_pathvector;
+ if(!SP_IS_GROUP(item))return(new_combined_pathvector); // sanity test, only a group should be passed in, return empty if something else happens
+
+ new_combined_pathvector = combined_pathvector;
+ SPGroup *group = SP_GROUP(item);
+ Geom::Affine tfc = item->transform * transform;
+ for(SPItem *item = SP_ITEM(group->firstChild()); item; item=SP_ITEM(item->getNext())){
+ if (SP_IS_GROUP(item)) {
+ new_combined_pathvector = merge_PathVector_with_group(new_combined_pathvector, item, tfc); // could be endlessly recursive on a badly formed SVG
+ } else if (SP_IS_SHAPE(item)) {
+ new_combined_pathvector = merge_PathVector_with_shape(new_combined_pathvector, item, tfc);
+ } else { // not implemented
+ }
+ }
+ return new_combined_pathvector;
+}
+
+Geom::PathVector PrintEmf::merge_PathVector_with_shape(Geom::PathVector const &combined_pathvector, SPItem const *item, const Geom::Affine &transform)
+{
+ Geom::PathVector new_combined_pathvector;
+ if(!SP_IS_SHAPE(item))return(new_combined_pathvector); // sanity test, only a shape should be passed in, return empty if something else happens
+
+ Geom::Affine tfc = item->transform * transform;
+ SPShape *shape = SP_SHAPE(item);
+ if (shape->_curve) {
+ Geom::PathVector const & new_vect = shape->_curve->get_pathvector();
+ if(combined_pathvector.empty()){
+ new_combined_pathvector = new_vect * tfc;
+ }
+ else {
+ new_combined_pathvector = sp_pathvector_boolop(new_vect * tfc, combined_pathvector, bool_op_union , (FillRule) fill_oddEven, (FillRule) fill_oddEven);
+ }
+ }
+ return new_combined_pathvector;
+}
+
unsigned int PrintEmf::fill(
Inkscape::Extension::Print * /*mod*/,
Geom::PathVector const &pathv, Geom::Affine const & /*transform*/, SPStyle const *style,
Geom::OptRect const &/*pbox*/, Geom::OptRect const &/*dbox*/, Geom::OptRect const &/*bbox*/)
{
+ char *rec;
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();
+ do_clip_if_present(style); // If clipping is needed set it up
+
use_fill = true;
use_stroke = false;
@@ -1095,7 +1224,6 @@ unsigned int PrintEmf::fill(
}
} else if (gv.mode == DRAW_LINEAR_GRADIENT) {
if(is_Rect){
- char *rec;
int gMode;
Geom::Point ul, ur, lr;
Geom::Point outUL, outLR; // UL,LR corners of a stop rectangle, in OUTPUT coordinates
@@ -1284,6 +1412,7 @@ unsigned int PrintEmf::stroke(
char *rec = NULL;
Geom::Affine tf = m_tr_stack.top();
+ do_clip_if_present(style); // If clipping is needed set it up
use_stroke = true;
// use_fill was set in ::fill, if it is needed
@@ -1568,12 +1697,14 @@ unsigned int PrintEmf::image(
unsigned int h, /** height of bitmap */
unsigned int rs, /** row stride (normally w*4) */
Geom::Affine const &tf_rect, /** affine transform only used for defining location and size of rect, for all other tranforms, use the one from m_tr_stack */
- SPStyle const * /*style*/) /** provides indirect link to image object */
+ SPStyle const *style) /** provides indirect link to image object */
{
double x1, y1, dw, dh;
char *rec = NULL;
Geom::Affine tf = m_tr_stack.top();
+ do_clip_if_present(style); // If clipping is needed set it up
+
rec = U_EMRSETSTRETCHBLTMODE_set(U_COLORONCOLOR);
if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
g_error("Fatal programming error in PrintEmf::image at EMRHEADER");
@@ -1658,28 +1789,14 @@ unsigned int PrintEmf::image(
return 0;
}
-// may also be called with a simple_shape or an empty path, whereupon it just returns without doing anything
-unsigned int PrintEmf::print_pathv(Geom::PathVector const &pathv, const Geom::Affine &transform)
-{
- Geom::Affine tf = transform;
- char *rec = NULL;
-
- simple_shape = print_simple_shape(pathv, tf);
- if (simple_shape || pathv.empty()) {
- if (use_fill) {
- destroy_brush(); // these must be cleared even if nothing is drawn or hbrush,hpen fill up
- }
- if (use_stroke) {
- destroy_pen();
- }
- return TRUE;
- }
+unsigned int PrintEmf::draw_pathv_to_EMF(Geom::PathVector const &pathv, const Geom::Affine &transform) {
+ char *rec;
/* inkscape to EMF scaling is done below, but NOT the rotation/translation transform,
that is handled by the EMF MODIFYWORLDTRANSFORM record
*/
-
- Geom::PathVector pv = pathv_to_linear_and_cubic_beziers(pathv * tf);
+
+ Geom::PathVector pv = pathv_to_linear_and_cubic_beziers(pathv * transform);
rec = U_EMRBEGINPATH_set();
if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
@@ -1781,6 +1898,27 @@ unsigned int PrintEmf::print_pathv(Geom::PathVector const &pathv, const Geom::Af
if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
g_error("Fatal programming error in PrintEmf::print_pathv at U_EMRENDPATH_set");
}
+ return(0);
+}
+
+// may also be called with a simple_shape or an empty path, whereupon it just returns without doing anything
+unsigned int PrintEmf::print_pathv(Geom::PathVector const &pathv, const Geom::Affine &transform)
+{
+ Geom::Affine tf = transform;
+ char *rec = NULL;
+
+ simple_shape = print_simple_shape(pathv, tf);
+ if (simple_shape || pathv.empty()) {
+ if (use_fill) {
+ destroy_brush(); // these must be cleared even if nothing is drawn or hbrush,hpen fill up
+ }
+ if (use_stroke) {
+ destroy_pen();
+ }
+ return TRUE;
+ }
+
+ (void) draw_pathv_to_EMF(pathv, tf);
// explicit FILL/STROKE commands are needed for each sub section of the path
if (use_fill && !use_stroke) {
@@ -1819,6 +1957,7 @@ unsigned int PrintEmf::text(Inkscape::Extension::Print * /*mod*/, char const *te
return 0;
}
+ do_clip_if_present(style); // If clipping is needed set it up
char *rec = NULL;
int ccount, newfont;
int fix90n = 0;