emf-inout.cpp revision 00d0240eaf3986ebaeedcbeabbdb727b73a5934b
#ifdef HAVE_CONFIG_H
# include "config.h"
#define EMF_DRIVER
#include "sp-root.h"
#include "sp-path.h"
#include "style.h"
#include "print.h"
#include "display/drawing-item.h"
#include "unit-constants.h"
#include "clear-n_.h"
#include "document.h"
#include "libunicode-convert/unicode-convert.h"
#include "emf-print.h"
#include "emf-inout.h"
#include "uemf.h"
#ifndef U_PS_JOIN_MASK
namespace Inkscape {
namespace Extension {
namespace Internal {
static bool clipset = false;
gcc -Wall -o testpng testpng.c -lpng
#include <png.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
} pixel_t;
} bitmap_t;
char *buffer;
if(p->buffer)
if(!p->buffer)
size_t x, y;
if (value < 0)
inverse of gethexcolor() in emf-print.cpp
return(out);
return FALSE;
return TRUE;
unsigned int ret;
if (ret) {
bool new_FixPPTPatternAsHatch = mod->get_param_bool("FixPPTPatternAsHatch"); // force all patterns as standard EMF hatch
ext->set_param_bool("FixPPTCharPos",new_FixPPTCharPos); // Remember to add any new ones to PrintEmf::init or a mysterious failure will result!
int type;
int level;
char *lpEMFR;
typedef struct emf_device_context {
bool stroke_set;
bool fill_set;
bool textColorSet;
bool bkColorSet;
typedef struct emf_callback_data {
int level;
float MMX;
float MMY;
float dwInchesX;
float dwInchesY;
unsigned int id;
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
int n_obj;
switch(hatchType){
case U_HS_SOLIDTEXTCLR:
case U_HS_DITHEREDTEXTCLR:
case U_HS_SOLIDBKCLR:
case U_HS_DITHEREDBKCLR:
// EMF can take solid colors from background or the default text color but on conversion to inkscape
// these need to go to a defined color. Consequently the hatchType also has to go to a solid color, otherwise
// on export the background/text might not match at the time this is written, and the colors will shift.
switch(hatchType){
case U_HS_HORIZONTAL:
case U_HS_VERTICAL:
case U_HS_FDIAGONAL:
*(d->defs) += " patternUnits=\"userSpaceOnUse\" width=\"6\" height=\"6\" x=\"0\" y=\"0\" viewBox=\"0 0 6 6\" preserveAspectRatio=\"none\" >\n";
case U_HS_BDIAGONAL:
*(d->defs) += " patternUnits=\"userSpaceOnUse\" width=\"6\" height=\"6\" x=\"0\" y=\"0\" viewBox=\"0 0 6 6\" preserveAspectRatio=\"none\" >\n";
case U_HS_CROSS:
case U_HS_DIAGCROSS:
*(d->defs) += " patternUnits=\"userSpaceOnUse\" width=\"6\" height=\"6\" x=\"0\" y=\"0\" viewBox=\"0 0 6 6\" preserveAspectRatio=\"none\" >\n";
case U_HS_SOLIDCLR:
case U_HS_DITHEREDCLR:
case U_HS_SOLIDTEXTCLR:
case U_HS_DITHEREDTEXTCLR:
case U_HS_SOLIDBKCLR:
case U_HS_DITHEREDBKCLR:
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){
if(!cbBits ||
!cbBmi ||
pEmr,
&px,
&ct,
&numCt,
&width,
&height,
if(!DIB_to_RGBA(
&mempng,
base64String = strdup("iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAAA3NCSVQICAjb4U/gAAAALElEQVQImQXBQQ2AMAAAsUJQMSWI2H8qME1yMshojwrvGB8XcHKvR1XtOTc/8HENumHCsOMAAAAASUVORK5CYII=");
if(d->dwRop3){
switch(d->dwRop3){
case U_SRCINVERT:
case U_DSTINVERT:
case U_BLACKNESS:
case U_SRCERASE:
case U_NOTSRCCOPY:
case U_NOTSRCERASE:
case U_PATCOPY:
case U_WHITENESS:
case U_SRCAND:
case U_MERGECOPY:
case U_MERGEPAINT:
case U_PATPAINT:
switch(d->dwRop2){
case U_R2_BLACK:
case U_R2_NOTMERGEPEN:
case U_R2_MASKNOTPEN:
case U_R2_NOTCOPYPEN:
case U_R2_MASKPENNOT:
case U_R2_NOT:
case U_R2_XORPEN:
case U_R2_NOTMASKPEN:
case U_R2_NOTXORPEN:
case U_R2_NOP:
case U_R2_MERGENOTPEN:
case U_R2_COPYPEN:
case U_R2_MASKPEN:
case U_R2_MERGEPENNOT:
case U_R2_MERGEPEN:
case U_R2_WHITE:
// *(d->outsvg) += tmp_id.str().c_str();
case DRAW_PATTERN:
case DRAW_IMAGE:
case DRAW_PAINT:
if (d->dc[d->level].fill_set && d->dc[d->level].stroke_set && d->dc[d->level].style.stroke_width.value == 1 &&
case DRAW_PATTERN:
case DRAW_IMAGE:
case DRAW_PAINT:
if (clipset)
clipset = false;
return tmp;
return tmp;
double x = ppx * d->dc[d->level].worldTransform.eM11 + ppy * d->dc[d->level].worldTransform.eM21 + d->dc[d->level].worldTransform.eDx;
x *= device_scale;
double y = ppx * d->dc[d->level].worldTransform.eM12 + ppy * d->dc[d->level].worldTransform.eM22 + d->dc[d->level].worldTransform.eDy;
y *= device_scale;
double ppy = 0;
return tmp;
if (!pEmr)
case U_PS_DASH:
case U_PS_DOT:
case U_PS_DASHDOT:
case U_PS_DASHDOTDOT:
if (d->dc[d->level].style.stroke_dash.dash && (d->level==0 || (d->level>0 && d->dc[d->level].style.stroke_dash.dash!=d->dc[d->level-1].style.stroke_dash.dash)))
case U_PS_SOLID:
case U_PS_ENDCAP_ROUND:
case U_PS_ENDCAP_SQUARE:
case U_PS_ENDCAP_FLAT:
case U_PS_JOIN_BEVEL:
case U_PS_JOIN_MITER:
case U_PS_JOIN_ROUND:
} else { // this stroke should always be rendered as 1 pixel wide, independent of zoom level (can that be done in SVG?)
//d->dc[d->level].style.stroke_width.value = 1.0;
if (!pEmr)
case U_PS_USERSTYLE:
if (d->dc[d->level].style.stroke_dash.dash && (d->level==0 || (d->level>0 && d->dc[d->level].style.stroke_dash.dash!=d->dc[d->level-1].style.stroke_dash.dash)))
case U_PS_DASH:
case U_PS_DOT:
case U_PS_DASHDOT:
case U_PS_DASHDOTDOT:
if (d->dc[d->level].style.stroke_dash.dash && (d->level==0 || (d->level>0 && d->dc[d->level].style.stroke_dash.dash!=d->dc[d->level-1].style.stroke_dash.dash)))
case U_PS_SOLID:
case U_PS_ENDCAP_ROUND:
case U_PS_ENDCAP_SQUARE:
case U_PS_ENDCAP_FLAT:
case U_PS_JOIN_BEVEL:
case U_PS_JOIN_MITER:
case U_PS_JOIN_ROUND:
} else { // this stroke should always be rendered as 1 pixel wide, independent of zoom level (can that be done in SVG?)
//d->dc[d->level].style.stroke_width.value = 1.0;
else if(pEmr->elp.elpBrushStyle == U_BS_DIBPATTERN || pEmr->elp.elpBrushStyle == U_BS_DIBPATTERNPT){
d->dc[d->level].stroke_idx = add_image(d, pEmr, pEmr->cbBits, pEmr->cbBmi, *(uint32_t *) &(pEmr->elp.elpColor), pEmr->offBits, pEmr->offBmi);
tidx = add_image(d, (void *) pEmr, pEmr->cbBits, pEmr->cbBmi, pEmr->iUsage, pEmr->offBits, pEmr->offBmi);
if (!pEmr)return;
/* The logfont information always starts with a U_LOGFONT structure but the U_EMREXTCREATEFONTINDIRECTW
is defined as U_LOGFONT_PANOSE so it can handle one of those if that is actually present. Currently only logfont
d->dc[d->level].style.font_style.value = (pEmr->elfw.elfLogFont.lfItalic ? SP_CSS_FONT_STYLE_ITALIC : SP_CSS_FONT_STYLE_NORMAL);
d->dc[d->level].style.baseline_shift.value = ((pEmr->elfw.elfLogFont.lfEscapement + 3600) % 3600) / 10; // use baseline_shift instead of text_transform to avoid overflow
// We are keeping a copy of the EMR rather than just a structure. Currently that is not necessary as the entire
// reord by record, and we might need to do that again at some point in the future if we start running into EMF
return res;
while(OK){
// std::cout << "record type: " << lpEMFR->iType << " length: " << lpEMFR->nSize << "offset: " << off <<std::endl;
// std::cout << "BEFORE DRAW logic d->mask: " << std::hex << d->mask << " emr_mask: " << emr_mask << std::dec << std::endl;
((d->mask & U_DRAW_ONLYTO) && !(emr_mask & U_DRAW_ONLYTO) ) // *TO records can only be followed by other *TO records
d->mask = 0;
d->drawtype = 0;
// std::cout << "AFTER DRAW logic d->mask: " << std::hex << d->mask << " emr_mask: " << emr_mask << std::dec << std::endl;
case U_EMR_HEADER:
if (d->pDesc) {
tmp_outdef <<
for( int i=0; i < d->n_obj; ++i )
case U_EMR_POLYBEZIER:
uint32_t i,j;
tmp_str <<
tmp_str <<
case U_EMR_POLYGON:
uint32_t i;
tmp_str <<
tmp_str <<
case U_EMR_POLYLINE:
uint32_t i;
tmp_str <<
tmp_str <<
case U_EMR_POLYBEZIERTO:
uint32_t i,j;
tmp_path <<
case U_EMR_POLYLINETO:
uint32_t i;
tmp_path <<
case U_EMR_POLYPOLYLINE:
case U_EMR_POLYPOLYGON:
case U_EMR_SETWINDOWEXTEX:
d->dc[d->level].ScaleInX = (double) d->dc[d->level].sizeView.cx / (double) d->dc[d->level].PixelsInX;
d->dc[d->level].ScaleInY = (double) d->dc[d->level].sizeView.cy / (double) d->dc[d->level].PixelsInY;
case U_EMR_SETWINDOWORGEX:
case U_EMR_SETVIEWPORTEXTEX:
d->dc[d->level].ScaleInX = (double) d->dc[d->level].sizeView.cx / (double) d->dc[d->level].PixelsInX;
d->dc[d->level].ScaleInY = (double) d->dc[d->level].sizeView.cy / (double) d->dc[d->level].PixelsInY;
case U_EMR_SETVIEWPORTORGEX:
case U_EMR_EOF:
OK=0;
case U_EMR_SETPOLYFILLMODE:
case U_EMR_SETROP2:
case U_EMR_SETSTRETCHBLTMODE:
case U_EMR_SETTEXTALIGN:
case U_EMR_SETCOLORADJUSTMENT:
case U_EMR_SETTEXTCOLOR:
case U_EMR_SETBKCOLOR:
case U_EMR_MOVETOEX:
tmp_path <<
case U_EMR_INTERSECTCLIPRECT:
clipset = true;
if ((rc.left == rc_old.left) && (rc.top == rc_old.top) && (rc.right == rc_old.right) && (rc.bottom == rc_old.bottom))
case U_EMR_SAVEDC:
case U_EMR_RESTOREDC:
if (d->dc[old_level].style.stroke_dash.dash && (old_level==0 || (old_level>0 && d->dc[old_level].style.stroke_dash.dash!=d->dc[old_level-1].style.stroke_dash.dash)))
old_level--;
case U_EMR_SETWORLDTRANSFORM:
case U_MWT_IDENTITY:
case U_MWT_LEFTMULTIPLY:
case U_MWT_RIGHTMULTIPLY:
case U_EMR_SELECTOBJECT:
switch (index) {
case U_NULL_BRUSH:
case U_BLACK_BRUSH:
case U_DKGRAY_BRUSH:
case U_GRAY_BRUSH:
case U_LTGRAY_BRUSH:
case U_WHITE_BRUSH:
float val = 0;
switch (index) {
case U_BLACK_BRUSH:
case U_DKGRAY_BRUSH:
case U_GRAY_BRUSH:
case U_LTGRAY_BRUSH:
case U_WHITE_BRUSH:
case U_NULL_PEN:
case U_BLACK_PEN:
case U_WHITE_PEN:
case U_EMR_CREATEPEN:
case U_EMR_CREATEMONOBRUSH:
case U_EMR_EXTCREATEPEN:
case U_EMR_CREATEPEN:
case U_EMR_DELETEOBJECT:
case U_EMR_ANGLEARC:
case U_EMR_ELLIPSE:
case U_EMR_RECTANGLE:
case U_EMR_ROUNDRECT:
tmp_rectangle << "\n\tC " << l << ", " << t + (1-f)*cny << " " << l + (1-f)*cnx << ", " << t << " " << l + cnx << ", " << t << " ";
tmp_rectangle << "\n\tC " << r - (1-f)*cnx << ", " << t << " " << r << ", " << t + (1-f)*cny << " " << r << ", " << t + cny << " ";
tmp_rectangle << "\n\tC " << r << ", " << b - (1-f)*cny << " " << r - (1-f)*cnx << ", " << b << " " << r - cnx << ", " << b << " ";
tmp_rectangle << "\n\tC " << l + (1-f)*cnx << ", " << b << " " << l << ", " << b - (1-f)*cny << " " << l << ", " << b - cny << " ";
case U_EMR_ARC:
int f1;
tmp_path << "\n\tM " << pix_to_x_point(d, start.x, start.y) << "," << pix_to_y_point(d, start.x, start.y);
tmp_path << " A " << pix_to_x_point(d, size.x, size.y)/2.0 << "," << pix_to_y_point(d, size.x, size.y)/2.0 ;
case U_EMR_CHORD:
int f1;
tmp_path << "\n\tM " << pix_to_x_point(d, start.x, start.y) << "," << pix_to_y_point(d, start.x, start.y);
tmp_path << " A " << pix_to_x_point(d, size.x, size.y)/2.0 << "," << pix_to_y_point(d, size.x, size.y)/2.0 ;
case U_EMR_PIE:
int f1;
tmp_path << "\n\tM " << pix_to_x_point(d, center.x, center.y) << "," << pix_to_y_point(d, center.x, center.y);
tmp_path << "\n\tL " << pix_to_x_point(d, start.x, start.y) << "," << pix_to_y_point(d, start.x, start.y);
tmp_path << " A " << pix_to_x_point(d, size.x, size.y)/2.0 << "," << pix_to_y_point(d, size.x, size.y)/2.0;
case U_EMR_LINETO:
tmp_path <<
case U_EMR_ARCTO:
int f1;
tmp_path << "\n\tL " << pix_to_x_point(d, start.x, start.y) << "," << pix_to_y_point(d, start.x, start.y);
tmp_path << "\n\tM " << pix_to_x_point(d, start.x, start.y) << "," << pix_to_y_point(d, start.x, start.y);
tmp_path << " A " << pix_to_x_point(d, size.x, size.y)/2.0 << "," << pix_to_y_point(d, size.x, size.y)/2.0 ;
case U_EMR_SETARCDIRECTION:
case U_EMR_SETMITERLIMIT:
case U_EMR_BEGINPATH:
case U_EMR_ENDPATH:
d->mask &= (0xFFFFFFFF - U_DRAW_ONLYTO); // clear the OnlyTo bit (it might not have been set), prevents any further path extension
case U_EMR_CLOSEFIGURE:
case U_EMR_FILLPATH:
if(!(d->mask & U_DRAW_CLOSED)){ // Close a path not explicitly closed by an EMRCLOSEFIGURE, otherwise fill makes no sense
case U_EMR_STROKEANDFILLPATH:
if(!(d->mask & U_DRAW_CLOSED)){ // Close a path not explicitly closed by an EMRCLOSEFIGURE, otherwise fill makes no sense
case U_EMR_STROKEPATH:
case U_EMR_ABORTPATH:
d->drawtype = 0;
case U_EMR_COMMENT:
if ( *szTxt) {
szTxt++;
case U_EMR_EXTSELECTCLIPRGN:
clipset = false;
case U_EMR_BITBLT:
d->mask |= U_DRAW_CLOSED; // Bitblit is not really open or closed, but we need it to fill, and this is the flag for that
case U_EMR_STRETCHDIBITS:
pEmr,
&px,
&ct,
&numCt,
&width,
&height,
if(!DIB_to_RGBA(
&mempng,
tmp_image << "iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAAA3NCSVQICAjb4U/gAAAALElEQVQImQXBQQ2AMAAAsUJQMSWI2H8qME1yMshojwrvGB8XcHKvR1XtOTc/8HENumHCsOMAAAAASUVORK5CYII=";
case U_EMR_EXTTEXTOUTA:
case U_EMR_EXTTEXTOUTW:
case U_EMR_SMALLTEXTOUT:
int roff = sizeof(U_EMRSMALLTEXTOUT); //offset to the start of the variable fields, only used with U_EMR_SMALLTEXTOUT
int cChars;
cChars = 0;
double dfact;
if (d->dc[d->level].textAlign & U_TA_BASEBIT){ dfact = 0.00; } // alignments 0x10 to U_TA_BASELINE 0x18
else if(d->dc[d->level].textAlign & U_TA_BOTTOM){ dfact = -0.35; } // alignments U_TA_BOTTOM 0x08 to 0x0E, factor is approximate
x += dfact * std::sin(d->dc[d->level].style.baseline_shift.value*M_PI/180.0)*fabs(d->dc[d->level].style.font_size.computed);
y += dfact * std::cos(d->dc[d->level].style.baseline_shift.value*M_PI/180.0)*fabs(d->dc[d->level].style.font_size.computed);
if(!dup_wt)dup_wt = U_Latin1ToUtf32le((char *) pEmr + pEmr->emrtext.offString, pEmr->emrtext.nChars, NULL);
dup_wt = U_Utf16leToUtf32le((uint16_t *)((char *) pEmr + pEmr->emrtext.offString), pEmr->emrtext.nChars, NULL);
msdepua(dup_wt); //convert everything in Microsoft's private use area. For Symbol, Wingdings, Dingbats
char *ansi_text;
if (ansi_text) {
// sp_color_get_rgb_floatv( &(d->dc[d->level].style.fill.value.color), text_rgb );
(d->dc[d->level].style.font_weight.value >= SP_CSS_FONT_WEIGHT_500 && d->dc[d->level].style.font_weight.value <= SP_CSS_FONT_WEIGHT_900);
// EMF textalignment is a bit strange: 0x6 is center, 0x2 is right, 0x0 is left, the value 0x4 is also drawn left
int lcr = ((d->dc[d->level].textAlign & U_TA_CENTER) == U_TA_CENTER) ? 2 : ((d->dc[d->level].textAlign & U_TA_CENTER) == U_TA_LEFT) ? 0 : 1;
<< tmp
case U_EMR_POLYBEZIER16:
uint32_t i,j;
tmp_str <<
tmp_str <<
case U_EMR_POLYGON16:
unsigned int first = 0;
case U_EMR_POLYLINE16:
uint32_t i;
tmp_str <<
tmp_str <<
case U_EMR_POLYBEZIERTO16:
uint32_t i,j;
tmp_path <<
case U_EMR_POLYLINETO16:
uint32_t i;
tmp_path <<
case U_EMR_POLYPOLYLINE16:
case U_EMR_POLYPOLYGON16:
case U_EMR_CREATEMONOBRUSH:
case U_EMR_EXTCREATEPEN:
case U_EMR_SETICMMODE:
// When testing, uncomment the following to place a comment for each processed EMR record in the SVG
// *(d->outsvg) += dbg_str.str().c_str();
typedef struct _SMALL_RECT {
memset(&d, 0, sizeof(d));
return NULL;
d.mask = 0;
d.drawtype = 0;
d.dwRop3 = 0;
char *contents;
if (d.pDesc)
SPDocument *doc = SPDocument::createNewDocFromMem(d.outsvg->c_str(), strlen(d.outsvg->c_str()), TRUE);
delete d.outsvg;
delete d.path;
delete d.outdef;
delete d.defs;
if (d.emf_obj) {
for (i=0; i<d.n_obj; i++)
delete_object(&d, i);
delete[] d.emf_obj;
return doc;
"<param name=\"textToPath\" gui-text=\"" N_("Convert texts to paths") "\" type=\"boolean\">true</param>\n"
"<param name=\"TnrToSymbol\" gui-text=\"" N_("Map Unicode to Symbol font") "\" type=\"boolean\">true</param>\n"
"<param name=\"TnrToWingdings\" gui-text=\"" N_("Map Unicode to Wingdings") "\" type=\"boolean\">true</param>\n"
"<param name=\"TnrToZapfDingbats\" gui-text=\"" N_("Map Unicode to Zapf Dingbats") "\" type=\"boolean\">true</param>\n"
"<param name=\"UsePUA\" gui-text=\"" N_("Use MS Unicode PUA (0xF020-0xF0FF) for converted characters") "\" type=\"boolean\">false</param>\n"
"<param name=\"FixPPTCharPos\" gui-text=\"" N_("Compensate for PPT font bug") "\" type=\"boolean\">false</param>\n"
"<param name=\"FixPPTDashLine\" gui-text=\"" N_("Convert dashed/dotted lines to single lines") "\" type=\"boolean\">false</param>\n"
"<param name=\"FixPPTGrad2Polys\" gui-text=\"" N_("Convert gradients to colored polygon series") "\" type=\"boolean\">false</param>\n"
"<param name=\"FixPPTPatternAsHatch\" gui-text=\"" N_("Map all fill patterns to standard EMF hatches") "\" type=\"boolean\">false</param>\n"