summaryrefslogtreecommitdiffstats
path: root/src/extension/internal/emf-print.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/extension/internal/emf-print.cpp462
1 files changed, 382 insertions, 80 deletions
diff --git a/src/extension/internal/emf-print.cpp b/src/extension/internal/emf-print.cpp
index 0df643130..f4f7f08cb 100644
--- a/src/extension/internal/emf-print.cpp
+++ b/src/extension/internal/emf-print.cpp
@@ -70,9 +70,8 @@ namespace Internal {
/* globals */
-static double PX2WORLD = 20.0f;
-static U_XFORM worldTransform;
-static bool FixPPTCharPos, FixPPTDashLine, FixPPTGrad2Polys, FixPPTPatternAsHatch, FixImageRot;
+static double PX2WORLD;
+static bool FixPPTCharPos, FixPPTDashLine, FixPPTGrad2Polys, FixPPTLinGrad, FixPPTPatternAsHatch, FixImageRot;
static EMFTRACK *et = NULL;
static EMFHANDLES *eht = NULL;
@@ -125,9 +124,12 @@ unsigned int PrintEmf::begin(Inkscape::Extension::Print *mod, SPDocument *doc)
char *rec;
gchar const *utf8_fn = mod->get_param_string("destination");
+ // Typically PX2WORLD is 1200/90, using inkscape's default dpi
+ PX2WORLD = 1200.0 / Inkscape::Util::Quantity::convert(1.0, "in", "px");
FixPPTCharPos = mod->get_param_bool("FixPPTCharPos");
FixPPTDashLine = mod->get_param_bool("FixPPTDashLine");
FixPPTGrad2Polys = mod->get_param_bool("FixPPTGrad2Polys");
+ FixPPTLinGrad = mod->get_param_bool("FixPPTLinGrad");
FixPPTPatternAsHatch = mod->get_param_bool("FixPPTPatternAsHatch");
FixImageRot = mod->get_param_bool("FixImageRot");
@@ -179,12 +181,12 @@ unsigned int PrintEmf::begin(Inkscape::Extension::Print *mod, SPDocument *doc)
float dwInchesX = d.width();
float dwInchesY = d.height();
- // dwInchesX x dwInchesY in micrometer units, dpi=90 -> 3543.3 dpm
- (void) drawing_size((int) ceil(dwInchesX * 25.4), (int) ceil(dwInchesY * 25.4), 3.543307, &rclBounds, &rclFrame);
+ // dwInchesX x dwInchesY in micrometer units, 1200 dpi/25.4 -> dpmm
+ (void) drawing_size((int) ceil(dwInchesX * 25.4), (int) ceil(dwInchesY * 25.4),1200.0/25.4, &rclBounds, &rclFrame);
// set up the reference device as 100 X A4 horizontal, (1200 dpi/25.4 -> dpmm). Extra digits maintain dpi better in EMF
- int MMX = 21600;
- int MMY = 27900;
+ int MMX = 216;
+ int MMY = 279;
(void) device_size(MMX, MMY, 1200.0 / 25.4, &szlDev, &szlMm);
int PixelsX = szlDev.cx;
int PixelsY = szlDev.cy;
@@ -220,13 +222,16 @@ unsigned int PrintEmf::begin(Inkscape::Extension::Print *mod, SPDocument *doc)
}
- // Correct for dpi in EMF (1200) vs dpi in Inkscape (always 90).
- // Also correct for the scaling in PX2WORLD, which is set to 20.
+ // In earlier versions this was used to scale from inkscape's dpi of 90 to
+ // the files 1200 dpi, taking into account PX2WORLD which was 20. Now PX2WORLD
+ // is set so that this matrix is unitary. The usual value of PX2WORLD is 1200/90,
+ // but might be different if the internal dpi is changed.
- worldTransform.eM11 = 1200. / (90.0 * PX2WORLD);
+ U_XFORM worldTransform;
+ worldTransform.eM11 = 1.0;
worldTransform.eM12 = 0.0;
worldTransform.eM21 = 0.0;
- worldTransform.eM22 = 1200. / (90.0 * PX2WORLD);
+ worldTransform.eM22 = 1.0;
worldTransform.eDx = 0;
worldTransform.eDy = 0;
@@ -361,11 +366,13 @@ int PrintEmf::create_brush(SPStyle const *style, PU_COLORREF fcolor)
if (!fcolor && style) {
if (style->fill.isColor()) {
fill_mode = DRAW_PAINT;
+#if 0
+// opacity not supported by EMF
float opacity = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
if (opacity <= 0.0) {
opacity = 0.0; // basically the same as no fill
}
-
+#endif
sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
hatchColor = U_RGB(255 * rgb[0], 255 * rgb[1], 255 * rgb[2]);
@@ -420,7 +427,7 @@ int PrintEmf::create_brush(SPStyle const *style, PU_COLORREF fcolor)
hatchColor = avg_stop_color(rg);
}
} else if (lg) {
- if (FixPPTGrad2Polys) {
+ if (FixPPTGrad2Polys || FixPPTLinGrad) {
return hold_gradient(lg, fill_mode);
} else {
hatchColor = avg_stop_color(lg);
@@ -678,25 +685,24 @@ int PrintEmf::create_pen(SPStyle const *style, const Geom::Affine &transform)
linejoin = U_PS_JOIN_BEVEL;
}
- if (style->stroke_dash.n_dash &&
- style->stroke_dash.dash) {
+ if (!style->stroke_dasharray.values.empty()) {
if (FixPPTDashLine) { // will break up line into many smaller lines. Override gradient if that was set, cannot do both.
brushStyle = U_BS_SOLID;
hatchType = U_HS_HORIZONTAL;
} else {
- int i = 0;
- while ((linestyle != U_PS_USERSTYLE) && (i < style->stroke_dash.n_dash)) {
- if (style->stroke_dash.dash[i] > 0.00000001) {
+ unsigned i = 0;
+ while ((linestyle != U_PS_USERSTYLE) && (i < style->stroke_dasharray.values.size())) {
+ if (style->stroke_dasharray.values[i] > 0.00000001) {
linestyle = U_PS_USERSTYLE;
}
i++;
}
if (linestyle == U_PS_USERSTYLE) {
- n_dash = style->stroke_dash.n_dash;
+ n_dash = style->stroke_dasharray.values.size();
dash = new uint32_t[n_dash];
- for (i = 0; i < style->stroke_dash.n_dash; i++) {
- dash[i] = (uint32_t)(style->stroke_dash.dash[i]);
+ for (i = 0; i < n_dash; i++) {
+ dash[i] = (uint32_t)(Inkscape::Util::Quantity::convert(1, "mm", "px") * style->stroke_dasharray.values[i]);
}
}
}
@@ -780,6 +786,187 @@ void PrintEmf::destroy_pen()
}
}
+/* Return a Path consisting of just the corner points of the single path in a a PathVector. If the
+PathVector has more than one path, or that one path is open, or any of its segments are curved, then the
+returned PathVector is . If the input path is already just straight lines and vertices the output will be the
+same as the sole path in the input. */
+
+Geom::Path PrintEmf::pathv_to_simple_polygon(Geom::PathVector const &pathv, int *vertices)
+{
+ Geom::Point P1_trail;
+ Geom::Point P1;
+ Geom::Point P1_lead;
+ Geom::Point v1,v2;
+ Geom::Path output;
+ Geom::Path bad;
+ Geom::PathVector pv = pathv_to_linear_and_cubic_beziers(pathv);
+ Geom::PathVector::const_iterator pit = pv.begin();
+ Geom::PathVector::const_iterator pit2 = pv.begin();
+ ++pit2;
+ *vertices = 0;
+ if(pit->end_closed() != pit->end_default())return(bad); // path must be closed
+ if(pit2 != pv.end())return(bad); // there may only be one path
+ P1_trail = pit->finalPoint();
+ Geom::Path::const_iterator cit = pit->begin();
+ P1 = cit->initialPoint();
+ for(;cit != pit->end_closed();++cit) {
+ if (!is_straight_curve(*cit)) {
+ *vertices = 0;
+ return(bad);
+ }
+ P1_lead = cit->finalPoint();
+ if(Geom::are_near(P1_lead, P1, 1e-5))continue; // duplicate points at the same coordinate
+ v1 = unit_vector(P1 - P1_trail);
+ v2 = unit_vector(P1_lead - P1 );
+ if(Geom::are_near(dot(v1,v2), 1.0, 1e-5)){ // P1 is within a straight line
+ P1 = P1_lead;
+ continue;
+ }
+ // P1 is the center point of a turn of some angle
+ if(!*vertices){
+ output.start( P1 );
+ output.close( pit->closed() );
+ }
+ *vertices += 1;
+ Geom::LineSegment ls(P1_trail, P1);
+ output.append(ls);
+ P1_trail = P1;
+ P1 = P1_lead;
+ }
+ return(output);
+}
+
+/* Returns the simplified PathVector (no matter what).
+ Sets is_rect if it is a rectangle.
+ Sets angle that will rotate side closest to horizontal onto horizontal.
+*/
+Geom::Path PrintEmf::pathv_to_rect(Geom::PathVector const &pathv, bool *is_rect, double *angle)
+{
+ Geom::Point P1_trail;
+ Geom::Point P1;
+ Geom::Point P1_lead;
+ Geom::Point v1,v2;
+ int vertices;
+ Geom::Path pR = pathv_to_simple_polygon(pathv, &vertices);
+ *is_rect = false;
+ if(vertices==4){ // or else it cannot be a rectangle
+ int vertex_count=0;
+ /* Get the ends of the LAST line segment.
+ Find minimum rotation to align rectangle with X,Y axes. (Very degenerate if it is rotated 45 degrees.) */
+ *angle = 10.0; /* must be > than the actual angle in radians. */
+ for(Geom::Path::const_iterator cit = pR.begin(); cit != pR.end_open(); ++cit){
+ P1_trail = cit->initialPoint();
+ P1 = cit->finalPoint();
+ v1 = unit_vector(P1 - P1_trail);
+ if(v1[Geom::X] > 0){ // only check the 1 or 2 points on vectors aimed the same direction as unit X
+ double ang = asin(v1[Geom::Y]); // because component is rotation by ang of {1,0| vector
+ if(fabs(ang) < fabs(*angle))*angle = -ang; // y increases down, flips sign on angle
+ }
+ }
+
+ /* For increased numerical stability, snap the angle to the nearest 1/100th of a degree. */
+ double convert = 36000.0/ (2.0 * M_PI);
+ *angle = round(*angle * convert)/convert;
+
+ for(Geom::Path::const_iterator cit = pR.begin(); cit != pR.end_open();++cit) {
+ P1_lead = cit->finalPoint();
+ v1 = unit_vector(P1 - P1_trail);
+ v2 = unit_vector(P1_lead - P1 );
+ // P1 is center of a turn that is not 90 degrees. Limit comes from cos(89.9) = .001745
+ if(!Geom::are_near(dot(v1,v2), 0.0, 2e-3))break;
+ P1_trail = P1;
+ P1 = P1_lead;
+ vertex_count++;
+ }
+ if(vertex_count == 4){
+ *is_rect=true;
+ }
+ }
+ return(pR);
+}
+
+/* Compare a vector with a rectangle's orientation (angle needed to rotate side(s)
+ closest to horizontal to exactly horizontal) and return:
+ 0 none of the following
+ 1 parallel to horizontal
+ 2 parallel to vertical
+ 3 antiparallel to horizontal
+ 4 antiparallel to vertical
+*/
+int PrintEmf::vector_rect_alignment(double angle, Geom::Point vtest){
+ int stat = 0;
+ Geom::Point v1 = Geom::unit_vector(vtest); // unit vector to test alignment
+ Geom::Point v2 = Geom::Point(1,0) * Geom::Rotate(-angle); // unit horizontal side (sign change because Y increases DOWN)
+ if( Geom::are_near(dot(v1,v2), 1.0, 1e-5)){ stat = 1; }
+ else if(Geom::are_near(dot(v1,v2),-1.0, 1e-5)){ stat = 2; }
+ if(!stat){
+ v2 = Geom::Point(0,1) * Geom::Rotate(-angle); // unit vertical side
+ if( Geom::are_near(dot(v1,v2), 1.0, 1e-5)){ stat = 3; }
+ else if(Geom::are_near(dot(v1,v2),-1.0, 1e-5)){ stat = 4; }
+ }
+ return(stat);
+}
+
+/* retrieve the point at the indicated corner:
+ 0 UL (and default)
+ 1 UR
+ 2 LR
+ 3 LL
+ Needed because the start can be any point, and the direction could run either
+ clockwise or counterclockwise. This should work even if the corners of the rectangle
+ are slightly displaced.
+*/
+Geom::Point PrintEmf::get_pathrect_corner(Geom::Path pathRect, double angle, int corner){
+ Geom::Point center(0,0);
+ for(Geom::Path::const_iterator cit = pathRect.begin(); cit != pathRect.end_open(); ++cit) {
+ center += cit->initialPoint()/4.0;
+ }
+
+ int LR; // 1 if Left, 0 if Right
+ int UL; // 1 if Lower, 0 if Upper (as viewed on screen, y coordinates increase downwards)
+ switch(corner){
+ case 1: //UR
+ LR = 0;
+ UL = 0;
+ break;
+ case 2: //LR
+ LR = 0;
+ UL = 1;
+ break;
+ case 3: //LL
+ LR = 1;
+ UL = 1;
+ break;
+ default: //UL
+ LR = 1;
+ UL = 0;
+ break;
+ }
+
+ Geom::Point v1 = Geom::Point(1,0) * Geom::Rotate(-angle); // unit horizontal side (sign change because Y increases DOWN)
+ Geom::Point v2 = Geom::Point(0,1) * Geom::Rotate(-angle); // unit vertical side (sign change because Y increases DOWN)
+ Geom::Point P1;
+ for(Geom::Path::const_iterator cit = pathRect.begin(); cit != pathRect.end_open(); ++cit) {
+ P1 = cit->initialPoint();
+
+ if ( ( LR == (dot(P1 - center,v1) > 0 ? 0 : 1) )
+ && ( UL == (dot(P1 - center,v2) > 0 ? 1 : 0) ) ) break;
+ }
+ return(P1);
+}
+
+U_TRIVERTEX PrintEmf::make_trivertex(Geom::Point Pt, U_COLORREF uc){
+ U_TRIVERTEX tv;
+ using Geom::X;
+ using Geom::Y;
+ tv.x = (int32_t) round(Pt[X]);
+ tv.y = (int32_t) round(Pt[Y]);
+ tv.Red = uc.Red << 8;
+ tv.Green = uc.Green << 8;
+ tv.Blue = uc.Blue << 8;
+ tv.Alpha = uc.Reserved << 8; // EMF will ignore this
+ return(tv);
+}
unsigned int PrintEmf::fill(
Inkscape::Extension::Print * /*mod*/,
@@ -795,8 +982,28 @@ unsigned int PrintEmf::fill(
use_stroke = false;
fill_transform = tf;
+
+ int brush_stat = create_brush(style, NULL);
+
+ /* native linear gradients are only used if the object is a rectangle AND the gradient is parallel to the sides of the object */
+ bool is_Rect = false;
+ double angle;
+ int rectDir=0;
+ Geom::Path pathRect;
+ if(FixPPTLinGrad && brush_stat && gv.mode == DRAW_LINEAR_GRADIENT){
+ Geom::PathVector pvr = pathv * fill_transform;
+ pathRect = pathv_to_rect(pvr, &is_Rect, &angle);
+ if(is_Rect){
+ /* Gradientfill records can only be used if the gradient is parallel to the sides of the rectangle.
+ That must be checked here so that we can fall back to another form of gradient fill if it is not
+ the case. */
+ rectDir = vector_rect_alignment(angle, (gv.p2 - gv.p1) * fill_transform);
+ if(!rectDir)is_Rect = false;
+ }
+ if(!is_Rect && !FixPPTGrad2Polys)brush_stat=0; // fall all the way back to a solid fill
+ }
- if (create_brush(style, NULL)) { // only happens if the style is a gradient
+ if (brush_stat) { // only happens if the style is a gradient
/*
Handle gradients. Uses modified livarot as 2geom boolops is currently broken.
Can handle gradients with multiple stops.
@@ -819,10 +1026,10 @@ unsigned int PrintEmf::fill(
SPRadialGradient *tg = (SPRadialGradient *)(gv.grad); // linear/radial are the same here
nstops = tg->vector.stops.size();
sp_color_get_rgb_floatv(&tg->vector.stops[0].color, rgb);
- opa = tg->vector.stops[0].opacity;
+ opa = tg->vector.stops[0].opacity; // first stop
c1 = U_RGBA(255 * rgb[0], 255 * rgb[1], 255 * rgb[2], 255 * opa);
sp_color_get_rgb_floatv(&tg->vector.stops[nstops - 1].color, rgb);
- opa = tg->vector.stops[nstops - 1].opacity;
+ opa = tg->vector.stops[nstops - 1].opacity; // last stop
c2 = U_RGBA(255 * rgb[0], 255 * rgb[1], 255 * rgb[2], 255 * opa);
doff = 0.0;
@@ -868,9 +1075,10 @@ unsigned int PrintEmf::fill(
pathvr = sp_pathvector_boolop(pathvc, pathv, bool_op_inters, (FillRule) fill_nonZero, frb);
print_pathv(pathvr, fill_transform); // show the intersection
- if (doff >= doff_range - doff_base) {
+ if (doff >= doff_range) {
istop++;
if (istop >= nstops) {
+ istop = nstops - 1;
continue; // could happen on a rounding error
}
doff_base = doff_range;
@@ -882,56 +1090,145 @@ unsigned int PrintEmf::fill(
}
}
} else if (gv.mode == DRAW_LINEAR_GRADIENT) {
- Geom::Point uv = Geom::unit_vector(gv.p2 - gv.p1); // unit vector
- Geom::Point puv = uv.cw(); // perp. to unit vector
- double range = Geom::distance(gv.p1, gv.p2); // length along the gradient
- double step = range / divisions; // adequate approximation for gradient
- double overlap = step / 4.0; // overlap slices slightly
- double start;
- double stop;
- Geom::PathVector pathvc, pathvr;
-
- /* before lower end of gradient, overlap first slice position */
- wc = weight_opacity(c1);
- (void) create_brush(style, &wc);
- pathvc = rect_cutter(gv.p1, uv * (overlap), uv * (-50000.0), puv * 50000.0);
- pathvr = sp_pathvector_boolop(pathvc, pathv, bool_op_inters, (FillRule) fill_nonZero, frb);
- print_pathv(pathvr, fill_transform);
-
- /* after high end of gradient, overlap last slice poosition */
- wc = weight_opacity(c2);
- (void) create_brush(style, &wc);
- pathvc = rect_cutter(gv.p2, uv * (-overlap), uv * (50000.0), puv * 50000.0);
- pathvr = sp_pathvector_boolop(pathvc, pathv, bool_op_inters, (FillRule) fill_nonZero, frb);
- print_pathv(pathvr, fill_transform);
+ 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
+ U_TRIVERTEX ut[2];
+ U_GRADIENT4 ug4;
+ U_RECTL rcb;
+ U_XFORM tmpTransform;
+ double wRect, hRect;
+
+ /* coordinates: upper left, upper right, and lower right corners of the rectangle.
+ inkscape transform already applied, but needs to be scaled to EMF coordinates. */
+ ul = get_pathrect_corner(pathRect, angle, 0) * PX2WORLD;
+ ur = get_pathrect_corner(pathRect, angle, 1) * PX2WORLD;
+ lr = get_pathrect_corner(pathRect, angle, 2) * PX2WORLD;
+ wRect = Geom::distance(ul,ur);
+ hRect = Geom::distance(ur,lr);
+
+ /* The basic rectangle for all of these is placed with its UL corner at 0,0 with a size wRect,hRect.
+ Apply a world transform to place/scale it into the appropriate position on the drawing.
+ Actual gradientfill records are either this entire rectangle or slices of it as defined by the stops.
+ This rectangle has already been transformed by tf (whatever rotation/scale) Inkscape had applied to it.
+ */
+
+ Geom::Affine tf2 = Geom::Rotate(-angle); // the rectangle may be drawn skewed to the coordinate system
+ tmpTransform.eM11 = tf2[0];
+ tmpTransform.eM12 = tf2[1];
+ tmpTransform.eM21 = tf2[2];
+ tmpTransform.eM22 = tf2[3];
+ tmpTransform.eDx = round((ul)[Geom::X]); // use explicit round for better stability
+ tmpTransform.eDy = round((ul)[Geom::Y]);
+
+ 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");
+ }
- sp_color_get_rgb_floatv(&tg->vector.stops[istop].color, rgb);
- opa = tg->vector.stops[istop].opacity;
- c2 = U_RGBA(255 * rgb[0], 255 * rgb[1], 255 * rgb[2], 255 * opa);
+ rec = U_EMRMODIFYWORLDTRANSFORM_set(tmpTransform, U_MWT_LEFTMULTIPLY);
+ if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintEmf::image at EMRMODIFYWORLDTRANSFORM");
+ }
+
+ for(;istop<nstops;istop++){
+ doff_range = tg->vector.stops[istop].offset; // next or last stop
+ if(rectDir == 1 || rectDir == 2){
+ outUL = Geom::Point(doff_base *wRect, 0 );
+ outLR = Geom::Point(doff_range*wRect, hRect);
+ gMode = U_GRADIENT_FILL_RECT_H;
+ }
+ else {
+ outUL = Geom::Point(0, doff_base *hRect);
+ outLR = Geom::Point(wRect,doff_range*hRect);
+ gMode = U_GRADIENT_FILL_RECT_V;
+ }
+ doff_base = doff_range;
+ rcb.left = round(outUL[X]); // use explicit round for better stability
+ rcb.top = round(outUL[Y]);
+ rcb.right = round(outLR[X]);
+ rcb.bottom = round(outLR[Y]);
+ sp_color_get_rgb_floatv(&tg->vector.stops[istop].color, rgb);
+ opa = tg->vector.stops[istop].opacity;
+ c2 = U_RGBA(255 * rgb[0], 255 * rgb[1], 255 * rgb[2], 255 * opa);
+
+ if(rectDir == 2 || rectDir == 4){ // gradient is reversed, so swap colors
+ ut[0] = make_trivertex(outUL, c2);
+ ut[1] = make_trivertex(outLR, c1);
+ }
+ else {
+ ut[0] = make_trivertex(outUL, c1);
+ ut[1] = make_trivertex(outLR, c2);
+ }
+ c1 = c2; // for next stop
+ ug4.UpperLeft = 0;
+ ug4.LowerRight= 1;
+ rec = U_EMRGRADIENTFILL_set(rcb, 2, 1, gMode, ut, (uint32_t *) &ug4 );
+ if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintEmf::fill at U_EMRGRADIENTFILL_set");
+ }
+ }
- for (start = 0.0; start < range; start += step, doff += 1. / divisions) {
- stop = start + step + overlap;
- if (stop > range) {
- stop = range;
+ 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");
}
- pathvc = rect_cutter(gv.p1, uv * start, uv * stop, puv * 50000.0);
+ }
+ else {
+ Geom::Point uv = Geom::unit_vector(gv.p2 - gv.p1); // unit vector
+ Geom::Point puv = uv.cw(); // perp. to unit vector
+ double range = Geom::distance(gv.p1, gv.p2); // length along the gradient
+ double step = range / divisions; // adequate approximation for gradient
+ double overlap = step / 4.0; // overlap slices slightly
+ double start;
+ double stop;
+ Geom::PathVector pathvc, pathvr;
+
+ /* before lower end of gradient, overlap first slice position */
+ wc = weight_opacity(c1);
+ (void) create_brush(style, &wc);
+ pathvc = rect_cutter(gv.p1, uv * (overlap), uv * (-50000.0), puv * 50000.0);
+ pathvr = sp_pathvector_boolop(pathvc, pathv, bool_op_inters, (FillRule) fill_nonZero, frb);
+ print_pathv(pathvr, fill_transform);
- wc = weight_colors(c1, c2, (doff - doff_base) / (doff_range - doff_base));
+ /* after high end of gradient, overlap last slice position */
+ wc = weight_opacity(c2);
(void) create_brush(style, &wc);
- Geom::PathVector pathvr = sp_pathvector_boolop(pathvc, pathv, bool_op_inters, (FillRule) fill_nonZero, frb);
- print_pathv(pathvr, fill_transform); // show the intersection
+ pathvc = rect_cutter(gv.p2, uv * (-overlap), uv * (50000.0), puv * 50000.0);
+ pathvr = sp_pathvector_boolop(pathvc, pathv, bool_op_inters, (FillRule) fill_nonZero, frb);
+ print_pathv(pathvr, fill_transform);
- if (doff >= doff_range - doff_base) {
- istop++;
- if (istop >= nstops) {
- continue; // could happen on a rounding error
+ sp_color_get_rgb_floatv(&tg->vector.stops[istop].color, rgb);
+ opa = tg->vector.stops[istop].opacity;
+ c2 = U_RGBA(255 * rgb[0], 255 * rgb[1], 255 * rgb[2], 255 * opa);
+
+ for (start = 0.0; start < range; start += step, doff += 1. / divisions) {
+ stop = start + step + overlap;
+ if (stop > range) {
+ stop = range;
+ }
+ pathvc = rect_cutter(gv.p1, uv * start, uv * stop, puv * 50000.0);
+
+ wc = weight_colors(c1, c2, (doff - doff_base) / (doff_range - doff_base));
+ (void) create_brush(style, &wc);
+ Geom::PathVector pathvr = sp_pathvector_boolop(pathvc, pathv, bool_op_inters, (FillRule) fill_nonZero, frb);
+ print_pathv(pathvr, fill_transform); // show the intersection
+
+ if (doff >= doff_range) {
+ istop++;
+ if (istop >= nstops) {
+ istop = nstops - 1;
+ continue; // could happen on a rounding error
+ }
+ doff_base = doff_range;
+ doff_range = tg->vector.stops[istop].offset; // next or last stop
+ c1 = c2;
+ sp_color_get_rgb_floatv(&tg->vector.stops[istop].color, rgb);
+ opa = tg->vector.stops[istop].opacity;
+ c2 = U_RGBA(255 * rgb[0], 255 * rgb[1], 255 * rgb[2], 255 * opa);
}
- doff_base = doff_range;
- doff_range = tg->vector.stops[istop].offset; // next or last stop
- c1 = c2;
- sp_color_get_rgb_floatv(&tg->vector.stops[istop].color, rgb);
- opa = tg->vector.stops[istop].opacity;
- c2 = U_RGBA(255 * rgb[0], 255 * rgb[1], 255 * rgb[2], 255 * opa);
}
}
} else {
@@ -963,7 +1260,7 @@ unsigned int PrintEmf::fill(
}
if (
(style->stroke.isNone() || style->stroke.noneSet || style->stroke_width.computed == 0.0) ||
- (style->stroke_dash.n_dash && style->stroke_dash.dash && FixPPTDashLine) ||
+ (!style->stroke_dasharray.values.empty() && FixPPTDashLine) ||
!all_closed
) {
print_pathv(pathv, fill_transform); // do any fills. side effect: clears fill_pathv
@@ -991,13 +1288,13 @@ unsigned int PrintEmf::stroke(
return 0;
}
- if (style->stroke_dash.n_dash && style->stroke_dash.dash && FixPPTDashLine) {
+ if (!style->stroke_dasharray.values.empty() && FixPPTDashLine) {
// convert the path, gets its complete length, and then make a new path with parameter length instead of t
Geom::Piecewise<Geom::D2<Geom::SBasis> > tmp_pathpw; // pathv-> sbasis
Geom::Piecewise<Geom::D2<Geom::SBasis> > tmp_pathpw2; // sbasis using arc length parameter
Geom::Piecewise<Geom::D2<Geom::SBasis> > tmp_pathpw3; // new (discontinuous) path, composed of dots/dashes
Geom::Piecewise<Geom::D2<Geom::SBasis> > first_frag; // first fragment, will be appended at end
- int n_dash = style->stroke_dash.n_dash;
+ int n_dash = style->stroke_dasharray.values.size();
int i = 0; //dash index
double tlength; // length of tmp_pathpw
double slength = 0.0; // start of gragment
@@ -1010,7 +1307,7 @@ unsigned int PrintEmf::stroke(
// go around the dash array repeatedly until the entire path is consumed (but not beyond).
while (slength < tlength) {
- elength = slength + style->stroke_dash.dash[i++];
+ elength = slength + style->stroke_dasharray.values[i++];
if (elength > tlength) {
elength = tlength;
}
@@ -1021,7 +1318,7 @@ unsigned int PrintEmf::stroke(
first_frag = fragment;
}
slength = elength;
- slength += style->stroke_dash.dash[i++]; // the gap
+ slength += style->stroke_dasharray.values[i++]; // the gap
if (i >= n_dash) {
i = 0;
}
@@ -1266,8 +1563,8 @@ unsigned int PrintEmf::image(
unsigned int w, /** width of bitmap */
unsigned int h, /** height of bitmap */
unsigned int rs, /** row stride (normally w*4) */
- Geom::Affine const &/*tf_ignore*/, /** WRONG affine transform, use the one from m_tr_stack */
- SPStyle const *style) /** provides indirect link to image object */
+ 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 */
{
double x1, y1, dw, dh;
char *rec = NULL;
@@ -1278,10 +1575,10 @@ unsigned int PrintEmf::image(
g_error("Fatal programming error in PrintEmf::image at EMRHEADER");
}
- x1 = atof(style->object->getAttribute("x"));
- y1 = atof(style->object->getAttribute("y"));
- dw = atof(style->object->getAttribute("width"));
- dh = atof(style->object->getAttribute("height"));
+ x1 = tf_rect[4];
+ y1 = tf_rect[5];
+ dw = ((double) w) * tf_rect[0];
+ dh = ((double) h) * tf_rect[3];
Geom::Point pLL(x1, y1);
Geom::Point pLL2 = pLL * tf; //location of LL corner in Inkscape coordinates
@@ -1301,6 +1598,10 @@ unsigned int PrintEmf::image(
U_POINTL cDest = pointl_set(round(dw * PX2WORLD), round(dh * PX2WORLD));
U_POINTL Src = pointl_set(0, 0);
U_POINTL cSrc = pointl_set(w, h);
+ /* map the integer Dest coordinates back into pLL2, so that the rounded part does not destabilize the transform offset below */
+ pLL2[Geom::X] = Dest.x;
+ pLL2[Geom::Y] = Dest.y;
+ pLL2 /= PX2WORLD;
if (!FixImageRot) { /* Rotate images - some programs cannot read them in correctly if they are rotated */
tf[4] = tf[5] = 0.0; // get rid of the offset in the transform
Geom::Point pLL2prime = pLL2 * tf;
@@ -1309,7 +1610,7 @@ unsigned int PrintEmf::image(
tmpTransform.eM12 = tf[1];
tmpTransform.eM21 = tf[2];
tmpTransform.eM22 = tf[3];
- tmpTransform.eDx = (pLL2[Geom::X] - pLL2prime[Geom::X]) * PX2WORLD; //map pLL2 (now in EMF coordinates) back onto itself after the rotation
+ tmpTransform.eDx = (pLL2[Geom::X] - pLL2prime[Geom::X]) * PX2WORLD;
tmpTransform.eDy = (pLL2[Geom::Y] - pLL2prime[Geom::Y]) * PX2WORLD;
rec = U_EMRSAVEDC_set();
@@ -1734,6 +2035,7 @@ void PrintEmf::init(void)
"<param name=\"FixPPTCharPos\" type=\"boolean\">false</param>\n"
"<param name=\"FixPPTDashLine\" type=\"boolean\">false</param>\n"
"<param name=\"FixPPTGrad2Polys\" type=\"boolean\">false</param>\n"
+ "<param name=\"FixPPTLinGrad\" type=\"boolean\">false</param>\n"
"<param name=\"FixPPTPatternAsHatch\" type=\"boolean\">false</param>\n"
"<param name=\"FixImageRot\" type=\"boolean\">false</param>\n"
"<print/>\n"