From 89bb2602a15a830b7e2133307a8ec6a08ebd0f84 Mon Sep 17 00:00:00 2001 From: su_v Date: Sun, 23 Sep 2012 19:19:33 +0200 Subject: Fixes bug #988601: omnibus patch for EMF input/output support (cross-platform) (bzr r11668.1.8) --- src/extension/internal/emf-inout.cpp | 3140 ++++++++++++++++++++++++++++++++++ 1 file changed, 3140 insertions(+) create mode 100644 src/extension/internal/emf-inout.cpp (limited to 'src/extension/internal/emf-inout.cpp') diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp new file mode 100644 index 000000000..4b2767313 --- /dev/null +++ b/src/extension/internal/emf-inout.cpp @@ -0,0 +1,3140 @@ +/** @file + * @brief Windows-only Enhanced Metafile input and output. + */ +/* Authors: + * Ulf Erikson + * Jon A. Cruz + * Abhishek Sharma + * + * Copyright (C) 2006-2008 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + * + * References: + * - How to Create & Play Enhanced Metafiles in Win32 + * http://support.microsoft.com/kb/q145999/ + * - INFO: Windows Metafile Functions & Aldus Placeable Metafiles + * http://support.microsoft.com/kb/q66949/ + * - Metafile Functions + * http://msdn.microsoft.com/library/en-us/gdi/metafile_0whf.asp + * - Metafile Structures + * http://msdn.microsoft.com/library/en-us/gdi/metafile_5hkj.asp + */ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#define EMF_DRIVER +#include "sp-root.h" +#include "sp-path.h" +#include "style.h" +#include "print.h" +#include "extension/system.h" +#include "extension/print.h" +#include "extension/db.h" +#include "extension/input.h" +#include "extension/output.h" +#include "display/drawing.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" + +#define PRINT_EMF "org.inkscape.print.emf" + +#ifndef U_PS_JOIN_MASK +#define U_PS_JOIN_MASK (U_PS_JOIN_BEVEL|U_PS_JOIN_MITER|U_PS_JOIN_ROUND) +#endif + +namespace Inkscape { +namespace Extension { +namespace Internal { + + +static float device_scale = DEVICESCALE; +static U_RECTL rc_old; +static bool clipset = false; +static uint32_t ICMmode=0; +static uint32_t BLTmode=0; + +/** Construct a PNG in memory from an RGB from the EMF file + +from: +http://www.lemoda.net/c/write-png/ + +which was based on: +http://stackoverflow.com/questions/1821806/how-to-encode-png-to-buffer-using-libpng + +gcc -Wall -o testpng testpng.c -lpng +*/ + +#include +#include +#include +#include + +/* A coloured pixel. */ + +typedef struct { + uint8_t red; + uint8_t green; + uint8_t blue; + uint8_t opacity; +} pixel_t; + +/* A picture. */ + +typedef struct { + pixel_t *pixels; + size_t width; + size_t height; +} bitmap_t; + +/* structure to store PNG image bytes */ +typedef struct { + char *buffer; + size_t size; +} MEMPNG, *PMEMPNG; + +/* Given "bitmap", this returns the pixel of bitmap at the point + ("x", "y"). */ + +static pixel_t * pixel_at (bitmap_t * bitmap, int x, int y) +{ + return bitmap->pixels + bitmap->width * y + x; +} + +/* Write "bitmap" to a PNG file specified by "path"; returns 0 on + success, non-zero on error. */ + + + +void +my_png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + /* with libpng15 next line causes pointer deference error; use libpng12 */ + PMEMPNG p=(PMEMPNG)png_ptr->io_ptr; + size_t nsize = p->size + length; + + /* allocate or grow buffer */ + if(p->buffer) + p->buffer = (char *) realloc(p->buffer, nsize); + else + p->buffer = (char *) malloc(nsize); + + if(!p->buffer) + png_error(png_ptr, "Write Error"); + + /* copy new bytes to end of buffer */ + memcpy(p->buffer + p->size, data, length); + p->size += length; +} + +void toPNG(PMEMPNG accum, int width, int height, char *px, uint32_t cbPx){ + bitmap_t bmstore; + bitmap_t *bitmap=&bmstore; + accum->buffer=NULL; // PNG constructed in memory will end up here, caller must free(). + accum->size=0; + bitmap->pixels=(pixel_t *)px; + bitmap->width = width; + bitmap->height = height; + + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + size_t x, y; + png_byte ** row_pointers = NULL; + /* The following number is set by trial and error only. I cannot + see where it it is documented in the libpng manual. + */ + int pixel_size = 3; + int depth = 8; + + png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (png_ptr == NULL){ + accum->buffer=NULL; + return; + } + + info_ptr = png_create_info_struct (png_ptr); + if (info_ptr == NULL){ + png_destroy_write_struct (&png_ptr, &info_ptr); + accum->buffer=NULL; + return; + } + + /* Set up error handling. */ + + if (setjmp (png_jmpbuf (png_ptr))) { + png_destroy_write_struct (&png_ptr, &info_ptr); + accum->buffer=NULL; + return; + } + + /* Set image attributes. */ + + png_set_IHDR (png_ptr, + info_ptr, + bitmap->width, + bitmap->height, + depth, + PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + /* Initialize rows of PNG. */ + + row_pointers = (png_byte **) png_malloc (png_ptr, bitmap->height * sizeof (png_byte *)); + for (y = 0; y < bitmap->height; ++y) { + png_byte *row = + (png_byte *) png_malloc (png_ptr, sizeof (uint8_t) * bitmap->width * pixel_size); + row_pointers[bitmap->height - y - 1] = row; // Row order in EMF is reversed. + for (x = 0; x < bitmap->width; ++x) { + pixel_t * pixel = pixel_at (bitmap, x, y); + *row++ = pixel->red; // R & B channels were set correctly by DIB_to_RGB + *row++ = pixel->green; + *row++ = pixel->blue; + } + } + + /* Write the image data to memory */ + + png_set_rows (png_ptr, info_ptr, row_pointers); + + png_set_write_fn(png_ptr, accum, my_png_write_data, NULL); + + png_write_png (png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL); + + for (y = 0; y < bitmap->height; y++) { + png_free (png_ptr, row_pointers[y]); + } + png_free (png_ptr, row_pointers); + png_destroy_write_struct(&png_ptr, &info_ptr); + +} + +/* Given "value" and "max", the maximum value which we expect "value" + to take, this returns an integer between 0 and 255 proportional to + "value" divided by "max". */ + +static int pix (int value, int max) +{ + if (value < 0) + return 0; + return (int) (256.0 *((double) (value)/(double) max)); +} + +/* convert an EMF RGB(A) color to 0RGB +inverse of gethexcolor() in emf-print.cpp +*/ +uint32_t sethexcolor(U_COLORREF color){ + + uint32_t out; + out = (U_RGBAGetR(color) << 16) + + (U_RGBAGetG(color) << 8 ) + + (U_RGBAGetB(color) ); + return(out); +} + + +Emf::Emf (void) // The null constructor +{ + return; +} + + +Emf::~Emf (void) //The destructor +{ + return; +} + + +bool +Emf::check (Inkscape::Extension::Extension * /*module*/) +{ + if (NULL == Inkscape::Extension::db.get(PRINT_EMF)) + return FALSE; + return TRUE; +} + + +static void +emf_print_document_to_file(SPDocument *doc, gchar const *filename) +{ + Inkscape::Extension::Print *mod; + SPPrintContext context; + gchar const *oldconst; + gchar *oldoutput; + unsigned int ret; + + doc->ensureUpToDate(); + + mod = Inkscape::Extension::get_print(PRINT_EMF); + oldconst = mod->get_param_string("destination"); + oldoutput = g_strdup(oldconst); + mod->set_param_string("destination", filename); + +/* Start */ + context.module = mod; + /* fixme: This has to go into module constructor somehow */ + /* Create new arena */ + mod->base = doc->getRoot(); + Inkscape::Drawing drawing; + mod->dkey = SPItem::display_key_new(1); + mod->root = mod->base->invoke_show(drawing, mod->dkey, SP_ITEM_SHOW_DISPLAY); + drawing.setRoot(mod->root); + /* Print document */ + ret = mod->begin(doc); + if (ret) { + g_free(oldoutput); + throw Inkscape::Extension::Output::save_failed(); + } + mod->base->invoke_print(&context); + ret = mod->finish(); + /* Release arena */ + mod->base->invoke_hide(mod->dkey); + mod->base = NULL; + mod->root = NULL; // deleted by invoke_hide +/* end */ + + mod->set_param_string("destination", oldoutput); + g_free(oldoutput); + + return; +} + + +void +Emf::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar const *filename) +{ + Inkscape::Extension::Extension * ext; + + ext = Inkscape::Extension::db.get(PRINT_EMF); + if (ext == NULL) + return; + + bool new_val = mod->get_param_bool("textToPath"); + bool new_FixPPTCharPos = mod->get_param_bool("FixPPTCharPos"); // character position bug + // reserve FixPPT2 for opacity bug. Currently EMF does not export opacity values + bool new_FixPPTDashLine = mod->get_param_bool("FixPPTDashLine"); // dashed line bug + bool new_FixPPTGrad2Polys = mod->get_param_bool("FixPPTGrad2Polys"); // gradient bug + bool new_FixPPTPatternAsHatch = mod->get_param_bool("FixPPTPatternAsHatch"); // force all patterns as standard EMF hatch + + TableGen( //possibly regenerate the unicode-convert tables + mod->get_param_bool("TnrToSymbol"), + mod->get_param_bool("TnrToWingdings"), + mod->get_param_bool("TnrToZapfDingbats"), + mod->get_param_bool("UsePUA") + ); + + ext->set_param_bool("FixPPTCharPos",new_FixPPTCharPos); // Remember to add any new ones to PrintEmf::init or a mysterious failure will result! + ext->set_param_bool("FixPPTDashLine",new_FixPPTDashLine); + ext->set_param_bool("FixPPTGrad2Polys",new_FixPPTGrad2Polys); + ext->set_param_bool("FixPPTPatternAsHatch",new_FixPPTPatternAsHatch); + ext->set_param_bool("textToPath", new_val); + + emf_print_document_to_file(doc, filename); + + return; +} + + +enum drawmode {DRAW_PAINT, DRAW_PATTERN, DRAW_IMAGE}; // apply to either fill or stroke + +typedef struct { + int type; + int level; + char *lpEMFR; +} EMF_OBJECT, *PEMF_OBJECT; + +typedef struct { + int size; // number of slots allocated in strings + int count; // number of slots used in strings + char **strings; // place to store strings +} EMF_STRINGS, *PEMF_STRINGS; + +typedef struct emf_device_context { + struct SPStyle style; + class SPTextStyle tstyle; + 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 + bool fill_set; + int fill_mode; // enumeration from drawmode, not used if fill_set is not True + int fill_idx; // used with DRAW_PATTERN and DRAW_IMAGE to return the appropriate fill + + U_SIZEL sizeWnd; + U_SIZEL sizeView; + float PixelsInX, PixelsInY; + float PixelsOutX, PixelsOutY; + U_POINTL winorg; + U_POINTL vieworg; + double ScaleInX, ScaleInY; + double ScaleOutX, ScaleOutY; + U_COLORREF textColor; + bool textColorSet; + U_COLORREF bkColor; + bool bkColorSet; + uint32_t textAlign; + U_XFORM worldTransform; + U_POINTL cur; +} EMF_DEVICE_CONTEXT, *PEMF_DEVICE_CONTEXT; + +#define EMF_MAX_DC 128 + +typedef struct emf_callback_data { + Glib::ustring *outsvg; + Glib::ustring *path; + Glib::ustring *outdef; + Glib::ustring *defs; + + EMF_DEVICE_CONTEXT dc[EMF_MAX_DC+1]; // FIXME: This should be dynamic.. + int level; + + double xDPI, yDPI; + uint32_t mask; // Draw properties + int arcdir; //U_AD_COUNTERCLOCKWISE 1 or U_AD_CLOCKWISE 2 + + uint32_t dwRop2; // Binary raster operation, 0 if none (use brush/pen unmolested) + uint32_t dwRop3; // Ternary raster operation, 0 if none (use brush/pen unmolested) + + float MMX; + float MMY; + float dwInchesX; + float dwInchesY; + + 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 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. + + + int n_obj; + PEMF_OBJECT emf_obj; +} EMF_CALLBACK_DATA, *PEMF_CALLBACK_DATA; + +/* Add another 100 blank slots to the hatches array. +*/ +void enlarge_hatches(PEMF_CALLBACK_DATA d){ + d->hatches.size += 100; + d->hatches.strings = (char **) realloc(d->hatches.strings,d->hatches.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 in_hatches(PEMF_CALLBACK_DATA d, char *test){ + int i; + for(i=0; ihatches.count; i++){ + if(strcmp(test,d->hatches.strings[i])==0)return(i+1); + } + return(0); +} + +/* (Conditionally) add a hatch. If a matching hatch already exists nothing happens. If one + does not exist it is added to the hatches list and also entered into . +*/ +uint32_t add_hatch(PEMF_CALLBACK_DATA d, uint32_t hatchType, U_COLORREF hatchColor){ + char hatchname[64]; // big enough + char tmpcolor[8]; + uint32_t idx; + + if(hatchType==U_HS_DIAGCROSS){ // This is the only one with dependencies on others + (void) add_hatch(d,U_HS_FDIAGONAL,hatchColor); + (void) add_hatch(d,U_HS_BDIAGONAL,hatchColor); + } + + sprintf(tmpcolor,"%6.6X",sethexcolor(hatchColor)); + switch(hatchType){ + case U_HS_SOLIDTEXTCLR: + case U_HS_DITHEREDTEXTCLR: + if(d->dc[d->level].textColorSet){ + sprintf(tmpcolor,"%6.6X",sethexcolor(d->dc[d->level].textColor)); + } + break; + case U_HS_SOLIDBKCLR: + case U_HS_DITHEREDBKCLR: + if(d->dc[d->level].bkColorSet){ + sprintf(tmpcolor,"%6.6X",sethexcolor(d->dc[d->level].bkColor)); + } + break; + default: + break; + } + + // 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. + if(hatchType > U_HS_SOLIDCLR)hatchType = U_HS_SOLIDCLR; + + sprintf(hatchname,"EMFhatch%d_%s",hatchType,tmpcolor); + idx = in_hatches(d,hatchname); + if(!idx){ // add it if not already present + if(d->hatches.count == d->hatches.size){ enlarge_hatches(d); } + d->hatches.strings[d->hatches.count++]=strdup(hatchname); + + *(d->defs) += "\n"; + *(d->defs) += " defs) += hatchname; + *(d->defs) += "\"\n"; + switch(hatchType){ + case U_HS_HORIZONTAL: + *(d->defs) += " patternUnits=\"userSpaceOnUse\" width=\"6\" height=\"6\" x=\"0\" y=\"0\" >\n"; + *(d->defs) += " defs) += tmpcolor; + *(d->defs) += "\" />\n"; + *(d->defs) += " \n"; + break; + case U_HS_VERTICAL: + *(d->defs) += " patternUnits=\"userSpaceOnUse\" width=\"6\" height=\"6\" x=\"0\" y=\"0\" >\n"; + *(d->defs) += " defs) += tmpcolor; + *(d->defs) += "\" />\n"; + *(d->defs) += " \n"; + break; + case U_HS_FDIAGONAL: + *(d->defs) += " patternUnits=\"userSpaceOnUse\" width=\"6\" height=\"6\" x=\"0\" y=\"0\" viewBox=\"0 0 6 6\" preserveAspectRatio=\"none\" >\n"; + *(d->defs) += " defs) += tmpcolor; + *(d->defs) += "\" id=\"sub"; + *(d->defs) += hatchname; + *(d->defs) += "\"/>\n"; + *(d->defs) += " defs) += hatchname; + *(d->defs) += "\" transform=\"translate(6,0)\"/>\n"; + *(d->defs) += " defs) += hatchname; + *(d->defs) += "\" transform=\"translate(-6,0)\"/>\n"; + *(d->defs) += " \n"; + break; + case U_HS_BDIAGONAL: + *(d->defs) += " patternUnits=\"userSpaceOnUse\" width=\"6\" height=\"6\" x=\"0\" y=\"0\" viewBox=\"0 0 6 6\" preserveAspectRatio=\"none\" >\n"; + *(d->defs) += " defs) += tmpcolor; + *(d->defs) += "\" id=\"sub"; + *(d->defs) += hatchname; + *(d->defs) += "\"/>\n"; + *(d->defs) += " defs) += hatchname; + *(d->defs) += "\" transform=\"translate(6,0)\"/>\n"; + *(d->defs) += " defs) += hatchname; + *(d->defs) += "\" transform=\"translate(-6,0)\"/>\n"; + *(d->defs) += " \n"; + break; + case U_HS_CROSS: + *(d->defs) += " patternUnits=\"userSpaceOnUse\" width=\"6\" height=\"6\" x=\"0\" y=\"0\" >\n"; + *(d->defs) += " defs) += tmpcolor; + *(d->defs) += "\" />\n"; + *(d->defs) += " \n"; + break; + case U_HS_DIAGCROSS: + *(d->defs) += " patternUnits=\"userSpaceOnUse\" width=\"6\" height=\"6\" x=\"0\" y=\"0\" viewBox=\"0 0 6 6\" preserveAspectRatio=\"none\" >\n"; + *(d->defs) += " defs) += hatchname; + *(d->defs) += "\" transform=\"translate(0,0)\"/>\n"; + *(d->defs) += " defs) += hatchname; + *(d->defs) += "\" transform=\"translate(0,0)\"/>\n"; + *(d->defs) += " \n"; + break; + case U_HS_SOLIDCLR: + case U_HS_DITHEREDCLR: + case U_HS_SOLIDTEXTCLR: + case U_HS_DITHEREDTEXTCLR: + case U_HS_SOLIDBKCLR: + case U_HS_DITHEREDBKCLR: + default: + *(d->defs) += " patternUnits=\"userSpaceOnUse\" width=\"6\" height=\"6\" x=\"0\" y=\"0\" >\n"; + *(d->defs) += " defs) += tmpcolor; + *(d->defs) += ";stroke:none"; + *(d->defs) += "\" />\n"; + *(d->defs) += " \n"; + break; + } + idx = d->hatches.count; + } + return(idx-1); +} + +/* Add another 100 blank slots to the images array. +*/ +void enlarge_images(PEMF_CALLBACK_DATA d){ + d->images.size += 100; + d->images.strings = (char **) realloc(d->images.strings,d->images.size + sizeof(char *)); +} + +/* See if the image string is already in the list. If it is return its position (1->n, not 1-n-1) +*/ +int in_images(PEMF_CALLBACK_DATA d, char *test){ + int i; + for(i=0; iimages.count; i++){ + if(strcmp(test,d->images.strings[i])==0)return(i+1); + } + return(0); +} + +/* (Conditionally) add an image. If a matching image already exists nothing happens. If one + does not exist it is added to the images list and also entered into . + + U_EMRCREATEMONOBRUSH records only work when the bitmap is monochrome. If we hit one that isn't + set idx to 2^32-1 and let the caller handle it. +*/ +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){ + + uint32_t idx; + char imagename[64]; // big enough + char xywh[64]; // big enough + + MEMPNG mempng; // PNG in memory comes back in this + mempng.buffer = NULL; + + char *rgba_px=NULL; // RGBA pixels + char *px=NULL; // DIB pixels + uint32_t width, height, colortype, numCt, invert; + PU_RGBQUAD ct = NULL; + if(!cbBits || + !cbBmi || + (iUsage != U_DIB_RGB_COLORS) || + !get_DIB_params( // this returns pointers and values, but allocates no memory + pEmr, + offBits, + offBmi, + &px, + &ct, + &numCt, + &width, + &height, + &colortype, + &invert + )){ + + // U_EMRCREATEMONOBRUSH uses text/bk colors instead of what is in the color map. + if(((PU_EMR)pEmr)->iType == U_EMR_CREATEMONOBRUSH){ + if(numCt==2){ + ct[0] = U_RGB2BGR(d->dc[d->level].textColor); + ct[1] = U_RGB2BGR(d->dc[d->level].bkColor); + } + else { // createmonobrush renders on other platforms this way + return(0xFFFFFFFF); + } + } + + if(!DIB_to_RGBA( + px, // DIB pixel array + ct, // DIB color table + numCt, // DIB color table number of entries + &rgba_px, // U_RGBA pixel array (32 bits), created by this routine, caller must free. + width, // Width of pixel array + height, // Height of pixel array + colortype, // DIB BitCount Enumeration + numCt, // Color table used if not 0 + invert // If DIB rows are in opposite order from RGBA rows + ) && + rgba_px) + { + toPNG( // Get the image from the RGBA px into mempng + &mempng, + width, height, + rgba_px, + 4 * width * height); + free(rgba_px); + } + } + gchar *base64String; + if(mempng.buffer){ + base64String = g_base64_encode((guchar*) mempng.buffer, mempng.size ); + free(mempng.buffer); + idx = in_images(d, (char *) base64String); + } + else { + // insert a random 3x4 blotch otherwise + width = 3; + height = 4; + base64String = strdup("iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAAA3NCSVQICAjb4U/gAAAALElEQVQImQXBQQ2AMAAAsUJQMSWI2H8qME1yMshojwrvGB8XcHKvR1XtOTc/8HENumHCsOMAAAAASUVORK5CYII="); + idx = in_images(d, (char *) base64String); + } + if(!idx){ // add it if not already present + if(d->images.count == d->images.size){ enlarge_images(d); } + idx = d->images.count; + d->images.strings[d->images.count++]=strdup(base64String); + + sprintf(imagename,"EMFimage%d",idx++); + sprintf(xywh," x=\"0\" y=\"0\" width=\"%d\" height=\"%d\" ",width,height); // reuse this buffer + + *(d->defs) += "\n"; + *(d->defs) += " defs) += imagename; + *(d->defs) += "\"\n "; + *(d->defs) += xywh; + *(d->defs) += "\n"; + *(d->defs) += " xlink:href=\"data:image/png;base64,"; + *(d->defs) += base64String; + *(d->defs) += "\"\n"; + *(d->defs) += " />\n"; + + + *(d->defs) += "\n"; + *(d->defs) += " defs) += imagename; + *(d->defs) += "_ref\"\n "; + *(d->defs) += xywh; + *(d->defs) += "\n patternUnits=\"userSpaceOnUse\""; + *(d->defs) += " >\n"; + *(d->defs) += " defs) += imagename; + *(d->defs) += "_ign\" "; + *(d->defs) += " xlink:href=\"#"; + *(d->defs) += imagename; + *(d->defs) += "\" />\n"; + *(d->defs) += " \n"; + } + g_free(base64String); + return(idx-1); +} + + +static void +output_style(PEMF_CALLBACK_DATA d, int iType) +{ +// SVGOStringStream tmp_id; + SVGOStringStream tmp_style; + char tmp[1024] = {0}; + + float fill_rgb[3]; + sp_color_get_rgb_floatv( &(d->dc[d->level].style.fill.value.color), fill_rgb ); + float stroke_rgb[3]; + sp_color_get_rgb_floatv(&(d->dc[d->level].style.stroke.value.color), stroke_rgb); + + // for U_EMR_BITBLT with no image, try to approximate some of these operations/ + // Assume src color is "white" + if(d->dwRop3){ + switch(d->dwRop3){ + case U_PATINVERT: // treat all of these as black + case U_SRCINVERT: + case U_DSTINVERT: + case U_BLACKNESS: + case U_SRCERASE: + case U_NOTSRCCOPY: + fill_rgb[0]=fill_rgb[1]=fill_rgb[2]=0.0; + break; + case U_SRCCOPY: // treat all of these as white + case U_NOTSRCERASE: + case U_PATCOPY: + case U_WHITENESS: + fill_rgb[0]=fill_rgb[1]=fill_rgb[2]=1.0; + break; + case U_SRCPAINT: // use the existing color + case U_SRCAND: + case U_MERGECOPY: + case U_MERGEPAINT: + case U_PATPAINT: + default: + break; + } + d->dwRop3 = 0; // might as well reset it here, it must be set for each BITBLT + } + + // Implement some of these, the ones where the original screen color does not matter. + // The options that merge screen and pen colors cannot be done correctly because we + // have no way of knowing what color is already on the screen. For those just pass the + // pen color through. + switch(d->dwRop2){ + case U_R2_BLACK: + fill_rgb[0] = fill_rgb[1] = fill_rgb[2] = 0.0; + stroke_rgb[0]= stroke_rgb[1]= stroke_rgb[2] = 0.0; + break; + case U_R2_NOTMERGEPEN: + case U_R2_MASKNOTPEN: + break; + case U_R2_NOTCOPYPEN: + fill_rgb[0] = 1.0 - fill_rgb[0]; + fill_rgb[1] = 1.0 - fill_rgb[1]; + fill_rgb[2] = 1.0 - fill_rgb[2]; + stroke_rgb[0] = 1.0 - stroke_rgb[0]; + stroke_rgb[1] = 1.0 - stroke_rgb[1]; + stroke_rgb[2] = 1.0 - stroke_rgb[2]; + break; + 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: + break; + case U_R2_WHITE: + fill_rgb[0] = fill_rgb[1] = fill_rgb[2] = 1.0; + stroke_rgb[0]= stroke_rgb[1]= stroke_rgb[2] = 1.0; + break; + default: + break; + } + + +// tmp_id << "\n\tid=\"" << (d->id++) << "\""; +// *(d->outsvg) += tmp_id.str().c_str(); + *(d->outsvg) += "\n\tstyle=\""; + if (iType == U_EMR_STROKEPATH || !d->dc[d->level].fill_set) { + tmp_style << "fill:none;"; + } else { + switch(d->dc[d->level].fill_mode){ + // both of these use the url(#) method + case DRAW_PATTERN: + snprintf(tmp, 1023, "fill:url(#%s); ",d->hatches.strings[d->dc[d->level].fill_idx]); + tmp_style << tmp; + break; + case DRAW_IMAGE: + snprintf(tmp, 1023, "fill:url(#EMFimage%d_ref); ",d->dc[d->level].fill_idx); + tmp_style << tmp; + break; + case DRAW_PAINT: + default: // <-- this should never happen, but just in case... + snprintf(tmp, 1023, + "fill:#%02x%02x%02x;", + SP_COLOR_F_TO_U(fill_rgb[0]), + SP_COLOR_F_TO_U(fill_rgb[1]), + SP_COLOR_F_TO_U(fill_rgb[2])); + tmp_style << tmp; + break; + } + snprintf(tmp, 1023, + "fill-rule:%s;", + d->dc[d->level].style.fill_rule.value == 0 ? "evenodd" : "nonzero"); + tmp_style << tmp; + tmp_style << "fill-opacity:1;"; + + if (d->dc[d->level].fill_set && d->dc[d->level].stroke_set && d->dc[d->level].style.stroke_width.value == 1 && + fill_rgb[0]==stroke_rgb[0] && fill_rgb[1]==stroke_rgb[1] && fill_rgb[2]==stroke_rgb[2]) + { + d->dc[d->level].stroke_set = false; + } + } + + if (iType == U_EMR_FILLPATH || !d->dc[d->level].stroke_set) { + tmp_style << "stroke:none;"; + } else { + switch(d->dc[d->level].stroke_mode){ + // both of these use the url(#) method + case DRAW_PATTERN: + snprintf(tmp, 1023, "stroke:url(#%s); ",d->hatches.strings[d->dc[d->level].stroke_idx]); + tmp_style << tmp; + break; + case DRAW_IMAGE: + snprintf(tmp, 1023, "stroke:url(#EMFimage%d_ref); ",d->dc[d->level].stroke_idx); + tmp_style << tmp; + break; + case DRAW_PAINT: + default: // <-- this should never happen, but just in case... + snprintf(tmp, 1023, + "stroke:#%02x%02x%02x;", + SP_COLOR_F_TO_U(stroke_rgb[0]), + SP_COLOR_F_TO_U(stroke_rgb[1]), + SP_COLOR_F_TO_U(stroke_rgb[2])); + tmp_style << tmp; + break; + } + tmp_style << "stroke-width:" << + MAX( 0.001, d->dc[d->level].style.stroke_width.value ) << "px;"; + + tmp_style << "stroke-linecap:" << + (d->dc[d->level].style.stroke_linecap.computed == 0 ? "butt" : + d->dc[d->level].style.stroke_linecap.computed == 1 ? "round" : + d->dc[d->level].style.stroke_linecap.computed == 2 ? "square" : + "unknown") << ";"; + + tmp_style << "stroke-linejoin:" << + (d->dc[d->level].style.stroke_linejoin.computed == 0 ? "miter" : + d->dc[d->level].style.stroke_linejoin.computed == 1 ? "round" : + d->dc[d->level].style.stroke_linejoin.computed == 2 ? "bevel" : + "unknown") << ";"; + + // Set miter limit if known, even if it is not needed immediately (not miter) + tmp_style << "stroke-miterlimit:" << + MAX( 2.0, d->dc[d->level].style.stroke_miterlimit.value ) << ";"; + + if (d->dc[d->level].style.stroke_dasharray_set && + d->dc[d->level].style.stroke_dash.n_dash && d->dc[d->level].style.stroke_dash.dash) + { + tmp_style << "stroke-dasharray:"; + for (int i=0; idc[d->level].style.stroke_dash.n_dash; i++) { + if (i) + tmp_style << ","; + tmp_style << d->dc[d->level].style.stroke_dash.dash[i]; + } + tmp_style << ";"; + tmp_style << "stroke-dashoffset:0;"; + } else { + tmp_style << "stroke-dasharray:none;"; + } + tmp_style << "stroke-opacity:1;"; + } + tmp_style << "\" "; + if (clipset) + tmp_style << "\n\tclip-path=\"url(#clipEmfPath" << d->id << ")\" "; + clipset = false; + + *(d->outsvg) += tmp_style.str().c_str(); +} + + +static double +_pix_x_to_point(PEMF_CALLBACK_DATA d, double px) +{ + double tmp = px - d->dc[d->level].winorg.x; + tmp *= d->dc[d->level].ScaleInX ? d->dc[d->level].ScaleInX : 1.0; + tmp += d->dc[d->level].vieworg.x; + return tmp; +} + +static double +_pix_y_to_point(PEMF_CALLBACK_DATA d, double px) +{ + double tmp = px - d->dc[d->level].winorg.y; + tmp *= d->dc[d->level].ScaleInY ? d->dc[d->level].ScaleInY : 1.0; + tmp += d->dc[d->level].vieworg.y; + return tmp; +} + + +static double +pix_to_x_point(PEMF_CALLBACK_DATA d, double px, double py) +{ + double ppx = _pix_x_to_point(d, px); + double ppy = _pix_y_to_point(d, py); + + 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; + + return x; +} + +static double +pix_to_y_point(PEMF_CALLBACK_DATA d, double px, double py) +{ + double ppx = _pix_x_to_point(d, px); + double ppy = _pix_y_to_point(d, py); + + 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; + + return y; +} + +static double +pix_to_size_point(PEMF_CALLBACK_DATA d, double px) +{ + double ppx = px * (d->dc[d->level].ScaleInX ? d->dc[d->level].ScaleInX : 1.0); + double ppy = 0; + + double dx = ppx * d->dc[d->level].worldTransform.eM11 + ppy * d->dc[d->level].worldTransform.eM21; + dx *= device_scale; + double dy = ppx * d->dc[d->level].worldTransform.eM12 + ppy * d->dc[d->level].worldTransform.eM22; + dy *= device_scale; + + double tmp = sqrt(dx * dx + dy * dy); + return tmp; +} + + +static void +select_pen(PEMF_CALLBACK_DATA d, int index) +{ + PU_EMRCREATEPEN pEmr = NULL; + + if (index >= 0 && index < d->n_obj) + pEmr = (PU_EMRCREATEPEN) d->emf_obj[index].lpEMFR; + + if (!pEmr) + return; + + switch (pEmr->lopn.lopnStyle & U_PS_STYLE_MASK) { + case U_PS_DASH: + case U_PS_DOT: + case U_PS_DASHDOT: + case U_PS_DASHDOTDOT: + { + int i = 0; + int penstyle = (pEmr->lopn.lopnStyle & U_PS_STYLE_MASK); + d->dc[d->level].style.stroke_dash.n_dash = + penstyle == U_PS_DASHDOTDOT ? 6 : penstyle == U_PS_DASHDOT ? 4 : 2; + 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))) + delete[] d->dc[d->level].style.stroke_dash.dash; + d->dc[d->level].style.stroke_dash.dash = new double[d->dc[d->level].style.stroke_dash.n_dash]; + if (penstyle==U_PS_DASH || penstyle==U_PS_DASHDOT || penstyle==U_PS_DASHDOTDOT) { + d->dc[d->level].style.stroke_dash.dash[i++] = 3; + d->dc[d->level].style.stroke_dash.dash[i++] = 1; + } + if (penstyle==U_PS_DOT || penstyle==U_PS_DASHDOT || penstyle==U_PS_DASHDOTDOT) { + d->dc[d->level].style.stroke_dash.dash[i++] = 1; + d->dc[d->level].style.stroke_dash.dash[i++] = 1; + } + if (penstyle==U_PS_DASHDOTDOT) { + d->dc[d->level].style.stroke_dash.dash[i++] = 1; + d->dc[d->level].style.stroke_dash.dash[i++] = 1; + } + + d->dc[d->level].style.stroke_dasharray_set = 1; + break; + } + + case U_PS_SOLID: + default: + { + d->dc[d->level].style.stroke_dasharray_set = 0; + break; + } + } + + switch (pEmr->lopn.lopnStyle & U_PS_ENDCAP_MASK) { + case U_PS_ENDCAP_ROUND: + { + d->dc[d->level].style.stroke_linecap.computed = 1; + break; + } + case U_PS_ENDCAP_SQUARE: + { + d->dc[d->level].style.stroke_linecap.computed = 2; + break; + } + case U_PS_ENDCAP_FLAT: + default: + { + d->dc[d->level].style.stroke_linecap.computed = 0; + break; + } + } + + switch (pEmr->lopn.lopnStyle & U_PS_JOIN_MASK) { + case U_PS_JOIN_BEVEL: + { + d->dc[d->level].style.stroke_linejoin.computed = 2; + break; + } + case U_PS_JOIN_MITER: + { + d->dc[d->level].style.stroke_linejoin.computed = 0; + break; + } + case U_PS_JOIN_ROUND: + default: + { + d->dc[d->level].style.stroke_linejoin.computed = 1; + break; + } + } + + d->dc[d->level].stroke_set = true; + + if (pEmr->lopn.lopnStyle == U_PS_NULL) { + d->dc[d->level].style.stroke_width.value = 0; + d->dc[d->level].stroke_set = false; + } else if (pEmr->lopn.lopnWidth.x) { + int cur_level = d->level; + d->level = d->emf_obj[index].level; + double pen_width = pix_to_size_point( d, pEmr->lopn.lopnWidth.x ); + d->level = cur_level; + d->dc[d->level].style.stroke_width.value = pen_width; + } 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; + int cur_level = d->level; + d->level = d->emf_obj[index].level; + double pen_width = pix_to_size_point( d, 1 ); + d->level = cur_level; + d->dc[d->level].style.stroke_width.value = pen_width; + } + + double r, g, b; + r = SP_COLOR_U_TO_F( U_RGBAGetR(pEmr->lopn.lopnColor) ); + g = SP_COLOR_U_TO_F( U_RGBAGetG(pEmr->lopn.lopnColor) ); + b = SP_COLOR_U_TO_F( U_RGBAGetB(pEmr->lopn.lopnColor) ); + d->dc[d->level].style.stroke.value.color.set( r, g, b ); +} + + +static void +select_extpen(PEMF_CALLBACK_DATA d, int index) +{ + PU_EMREXTCREATEPEN pEmr = NULL; + + if (index >= 0 && index < d->n_obj) + pEmr = (PU_EMREXTCREATEPEN) d->emf_obj[index].lpEMFR; + + if (!pEmr) + return; + + switch (pEmr->elp.elpPenStyle & U_PS_STYLE_MASK) { + case U_PS_USERSTYLE: + { + if (pEmr->elp.elpNumEntries) { + d->dc[d->level].style.stroke_dash.n_dash = pEmr->elp.elpNumEntries; + 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))) + delete[] d->dc[d->level].style.stroke_dash.dash; + d->dc[d->level].style.stroke_dash.dash = new double[pEmr->elp.elpNumEntries]; + for (unsigned int i=0; ielp.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_size_point( d, pEmr->elp.elpStyleEntry[i] ); + double dash_length = pEmr->elp.elpStyleEntry[i]; + d->level = cur_level; + d->dc[d->level].style.stroke_dash.dash[i] = dash_length; + } + d->dc[d->level].style.stroke_dasharray_set = 1; + } else { + d->dc[d->level].style.stroke_dasharray_set = 0; + } + break; + } + + case U_PS_DASH: + case U_PS_DOT: + case U_PS_DASHDOT: + case U_PS_DASHDOTDOT: + { + int i = 0; + int penstyle = (pEmr->elp.elpPenStyle & U_PS_STYLE_MASK); + d->dc[d->level].style.stroke_dash.n_dash = + penstyle == U_PS_DASHDOTDOT ? 6 : penstyle == U_PS_DASHDOT ? 4 : 2; + 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))) + delete[] d->dc[d->level].style.stroke_dash.dash; + d->dc[d->level].style.stroke_dash.dash = new double[d->dc[d->level].style.stroke_dash.n_dash]; + if (penstyle==U_PS_DASH || penstyle==U_PS_DASHDOT || penstyle==U_PS_DASHDOTDOT) { + d->dc[d->level].style.stroke_dash.dash[i++] = 3; + d->dc[d->level].style.stroke_dash.dash[i++] = 2; + } + if (penstyle==U_PS_DOT || penstyle==U_PS_DASHDOT || penstyle==U_PS_DASHDOTDOT) { + d->dc[d->level].style.stroke_dash.dash[i++] = 1; + d->dc[d->level].style.stroke_dash.dash[i++] = 2; + } + if (penstyle==U_PS_DASHDOTDOT) { + d->dc[d->level].style.stroke_dash.dash[i++] = 1; + d->dc[d->level].style.stroke_dash.dash[i++] = 2; + } + + d->dc[d->level].style.stroke_dasharray_set = 1; + break; + } + + case U_PS_SOLID: + default: + { + d->dc[d->level].style.stroke_dasharray_set = 0; + break; + } + } + + switch (pEmr->elp.elpPenStyle & U_PS_ENDCAP_MASK) { + case U_PS_ENDCAP_ROUND: + { + d->dc[d->level].style.stroke_linecap.computed = 1; + break; + } + case U_PS_ENDCAP_SQUARE: + { + d->dc[d->level].style.stroke_linecap.computed = 2; + break; + } + case U_PS_ENDCAP_FLAT: + default: + { + d->dc[d->level].style.stroke_linecap.computed = 0; + break; + } + } + + switch (pEmr->elp.elpPenStyle & U_PS_JOIN_MASK) { + case U_PS_JOIN_BEVEL: + { + d->dc[d->level].style.stroke_linejoin.computed = 2; + break; + } + case U_PS_JOIN_MITER: + { + d->dc[d->level].style.stroke_linejoin.computed = 0; + break; + } + case U_PS_JOIN_ROUND: + default: + { + d->dc[d->level].style.stroke_linejoin.computed = 1; + break; + } + } + + d->dc[d->level].stroke_set = true; + + if (pEmr->elp.elpPenStyle == U_PS_NULL) { + d->dc[d->level].style.stroke_width.value = 0; + d->dc[d->level].stroke_set = false; + } else if (pEmr->elp.elpWidth) { + int cur_level = d->level; + d->level = d->emf_obj[index].level; + double pen_width = pix_to_size_point( d, pEmr->elp.elpWidth ); + d->level = cur_level; + d->dc[d->level].style.stroke_width.value = pen_width; + } 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; + int cur_level = d->level; + d->level = d->emf_obj[index].level; + double pen_width = pix_to_size_point( d, 1 ); + d->level = cur_level; + d->dc[d->level].style.stroke_width.value = pen_width; + } + + if( pEmr->elp.elpBrushStyle == U_BS_SOLID){ + double r, g, b; + r = SP_COLOR_U_TO_F( U_RGBAGetR(pEmr->elp.elpColor) ); + g = SP_COLOR_U_TO_F( U_RGBAGetG(pEmr->elp.elpColor) ); + b = SP_COLOR_U_TO_F( U_RGBAGetB(pEmr->elp.elpColor) ); + d->dc[d->level].style.stroke.value.color.set( r, g, b ); + d->dc[d->level].stroke_mode = DRAW_PAINT; + d->dc[d->level].stroke_set = true; + } + else if(pEmr->elp.elpBrushStyle == U_BS_HATCHED){ + d->dc[d->level].stroke_idx = add_hatch(d, pEmr->elp.elpHatch, pEmr->elp.elpColor); + d->dc[d->level].stroke_mode = DRAW_PATTERN; + d->dc[d->level].stroke_set = true; + } + 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); + d->dc[d->level].stroke_mode = DRAW_IMAGE; + d->dc[d->level].stroke_set = true; + } + else { // U_BS_PATTERN and anything strange that falls in, stroke is solid textColor + double r, g, b; + r = SP_COLOR_U_TO_F( U_RGBAGetR(d->dc[d->level].textColor)); + g = SP_COLOR_U_TO_F( U_RGBAGetG(d->dc[d->level].textColor)); + b = SP_COLOR_U_TO_F( U_RGBAGetB(d->dc[d->level].textColor)); + d->dc[d->level].style.stroke.value.color.set( r, g, b ); + d->dc[d->level].stroke_mode = DRAW_PAINT; + d->dc[d->level].stroke_set = true; + } +} + + +static void +select_brush(PEMF_CALLBACK_DATA d, int index) +{ + uint32_t tidx; + uint32_t iType; + + if (index >= 0 && index < d->n_obj){ + iType = ((PU_EMR) (d->emf_obj[index].lpEMFR))->iType; + if(iType == U_EMR_CREATEBRUSHINDIRECT){ + PU_EMRCREATEBRUSHINDIRECT pEmr = (PU_EMRCREATEBRUSHINDIRECT) d->emf_obj[index].lpEMFR; + if( pEmr->lb.lbStyle == U_BS_SOLID){ + double r, g, b; + r = SP_COLOR_U_TO_F( U_RGBAGetR(pEmr->lb.lbColor) ); + g = SP_COLOR_U_TO_F( U_RGBAGetG(pEmr->lb.lbColor) ); + b = SP_COLOR_U_TO_F( U_RGBAGetB(pEmr->lb.lbColor) ); + d->dc[d->level].style.fill.value.color.set( r, g, b ); + d->dc[d->level].fill_mode = DRAW_PAINT; + d->dc[d->level].fill_set = true; + } + else if(pEmr->lb.lbStyle == U_BS_HATCHED){ + d->dc[d->level].fill_idx = add_hatch(d, pEmr->lb.lbHatch, pEmr->lb.lbColor); + d->dc[d->level].fill_mode = DRAW_PATTERN; + d->dc[d->level].fill_set = true; + } + } + else if(iType == U_EMR_CREATEDIBPATTERNBRUSHPT || iType == U_EMR_CREATEMONOBRUSH){ + PU_EMRCREATEDIBPATTERNBRUSHPT pEmr = (PU_EMRCREATEDIBPATTERNBRUSHPT) d->emf_obj[index].lpEMFR; + tidx = add_image(d, (void *) pEmr, pEmr->cbBits, pEmr->cbBmi, pEmr->iUsage, pEmr->offBits, pEmr->offBmi); + if(tidx == 0xFFFFFFFF){ // This happens if createmonobrush has a DIB that isn't monochrome + double r, g, b; + r = SP_COLOR_U_TO_F( U_RGBAGetR(d->dc[d->level].textColor)); + g = SP_COLOR_U_TO_F( U_RGBAGetG(d->dc[d->level].textColor)); + b = SP_COLOR_U_TO_F( U_RGBAGetB(d->dc[d->level].textColor)); + d->dc[d->level].style.fill.value.color.set( r, g, b ); + d->dc[d->level].fill_mode = DRAW_PAINT; + } + else { + d->dc[d->level].fill_idx = tidx; + d->dc[d->level].fill_mode = DRAW_IMAGE; + } + d->dc[d->level].fill_set = true; + } + } +} + + +static void +select_font(PEMF_CALLBACK_DATA d, int index) +{ + PU_EMREXTCREATEFONTINDIRECTW pEmr = NULL; + + if (index >= 0 && index < d->n_obj) + pEmr = (PU_EMREXTCREATEFONTINDIRECTW) d->emf_obj[index].lpEMFR; + + 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 + is supported, and the remainder, it it really is a U_LOGFONT_PANOSE record, is ignored + */ + int cur_level = d->level; + d->level = d->emf_obj[index].level; + double font_size = pix_to_size_point( d, pEmr->elfw.elfLogFont.lfHeight ); + /* snap the font_size to the nearest .01. + See the notes where device_scale is set for the reason why. + Typically this will set the font to the desired exact size. If some peculiar size + was intended this will, at worst, make it 1% off, which is unlikely to be a problem. */ + font_size = round(100.0 * font_size)/100.0; + d->level = cur_level; + d->dc[d->level].style.font_size.computed = font_size; + d->dc[d->level].style.font_weight.value = + pEmr->elfw.elfLogFont.lfWeight == U_FW_THIN ? SP_CSS_FONT_WEIGHT_100 : + pEmr->elfw.elfLogFont.lfWeight == U_FW_EXTRALIGHT ? SP_CSS_FONT_WEIGHT_200 : + pEmr->elfw.elfLogFont.lfWeight == U_FW_LIGHT ? SP_CSS_FONT_WEIGHT_300 : + pEmr->elfw.elfLogFont.lfWeight == U_FW_NORMAL ? SP_CSS_FONT_WEIGHT_400 : + pEmr->elfw.elfLogFont.lfWeight == U_FW_MEDIUM ? SP_CSS_FONT_WEIGHT_500 : + pEmr->elfw.elfLogFont.lfWeight == U_FW_SEMIBOLD ? SP_CSS_FONT_WEIGHT_600 : + pEmr->elfw.elfLogFont.lfWeight == U_FW_BOLD ? SP_CSS_FONT_WEIGHT_700 : + pEmr->elfw.elfLogFont.lfWeight == U_FW_EXTRABOLD ? SP_CSS_FONT_WEIGHT_800 : + pEmr->elfw.elfLogFont.lfWeight == U_FW_HEAVY ? SP_CSS_FONT_WEIGHT_900 : + pEmr->elfw.elfLogFont.lfWeight == U_FW_NORMAL ? SP_CSS_FONT_WEIGHT_NORMAL : + pEmr->elfw.elfLogFont.lfWeight == U_FW_BOLD ? SP_CSS_FONT_WEIGHT_BOLD : + pEmr->elfw.elfLogFont.lfWeight == U_FW_EXTRALIGHT ? SP_CSS_FONT_WEIGHT_LIGHTER : + pEmr->elfw.elfLogFont.lfWeight == U_FW_EXTRABOLD ? SP_CSS_FONT_WEIGHT_BOLDER : + U_FW_NORMAL; + 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.text_decoration.underline = pEmr->elfw.elfLogFont.lfUnderline; + d->dc[d->level].style.text_decoration.line_through = pEmr->elfw.elfLogFont.lfStrikeOut; + if (d->dc[d->level].tstyle.font_family.value){ free(d->dc[d->level].tstyle.font_family.value); } + d->dc[d->level].tstyle.font_family.value = + U_Utf16leToUtf8((uint16_t *) (pEmr->elfw.elfLogFont.lfFaceName), U_LF_FACESIZE, NULL); + 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 +} + +static void +delete_object(PEMF_CALLBACK_DATA d, int index) +{ + if (index >= 0 && index < d->n_obj) { + d->emf_obj[index].type = 0; +// We are keeping a copy of the EMR rather than just a structure. Currently that is not necessary as the entire +// EMF is read in at once and is stored in a big malloc. However, in past versions it was handled +// reord by record, and we might need to do that again at some point in the future if we start running into EMF +// files too big to fit into memory. + if (d->emf_obj[index].lpEMFR) + free(d->emf_obj[index].lpEMFR); + d->emf_obj[index].lpEMFR = NULL; + } +} + + +static void +insert_object(PEMF_CALLBACK_DATA d, int index, int type, PU_ENHMETARECORD pObj) +{ + if (index >= 0 && index < d->n_obj) { + delete_object(d, index); + d->emf_obj[index].type = type; + d->emf_obj[index].level = d->level; + d->emf_obj[index].lpEMFR = emr_dup((char *) pObj); + } +} + +/** + \fn create a UTF-32LE buffer and fill it with UNICODE unknown character + \param count number of copies of the Unicode unknown character to fill with +*/ +uint32_t *unknown_chars(size_t count){ + uint32_t *res = (uint32_t *) malloc(sizeof(uint32_t) * (count + 1)); + if(!res)throw "Inkscape fatal memory allocation error - cannot continue"; + for(uint32_t i=0; i=length)return(0); //normally should exit from while after EMREOF sets OK to false. + + lpEMFR = (PU_ENHMETARECORD)(contents + off); +// std::cout << "record type: " << lpEMFR->iType << " length: " << lpEMFR->nSize << "offset: " << off <nSize; + + SVGOStringStream tmp_outsvg; + SVGOStringStream tmp_path; + SVGOStringStream tmp_str; + SVGOStringStream dbg_str; + + emr_mask = emr_properties(lpEMFR->iType); + +// 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) + << " test1 " << ( d->mask & U_DRAW_FORCE) + << " test2 " << (emr_mask & U_DRAW_ALTERS) + << " test3 " << (emr_mask & U_DRAW_VISIBLE) + << " test4 " << !(d->mask & U_DRAW_ONLYTO) + << " test5 " << ((d->mask & U_DRAW_ONLYTO) && !(emr_mask & U_DRAW_ONLYTO) ) + << std::endl; +*/ + if ( (emr_mask != 0xFFFFFFFF) && // next record is valid type + (d->mask & U_DRAW_VISIBLE) && // This record is drawable + ( (d->mask & U_DRAW_FORCE) || // This draw is forced by STROKE/FILL/STROKEANDFILL PATH + (emr_mask & U_DRAW_ALTERS) || // Next record would alter the drawing environment in some way + ( (emr_mask & U_DRAW_VISIBLE) // Next record is visible... + && + ( + ( !(d->mask & U_DRAW_ONLYTO) ) // Non *TO records cannot be followed by any Visible + || + ((d->mask & U_DRAW_ONLYTO) && !(emr_mask & U_DRAW_ONLYTO) ) // *TO records can only be followed by other *TO records + ) + ) + ) + ){ +// std::cout << "PATH DRAW at TOP" << std::endl; + *(d->outsvg) += " drawtype){ // explicit draw type EMR record + output_style(d, d->drawtype); + } + else if(d->mask & U_DRAW_CLOSED){ // implicit draw type + output_style(d, U_EMR_STROKEANDFILLPATH); + } + else { + output_style(d, U_EMR_STROKEPATH); + } + *(d->outsvg) += "\n\t"; + *(d->outsvg) += "\n\td=\""; // this is the ONLY place d=" should be used!!!! + *(d->outsvg) += *(d->path); + *(d->outsvg) += " \" /> \n"; + *(d->path) = ""; + // reset the flags + 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; + + switch (lpEMFR->iType) + { + case U_EMR_HEADER: + { + dbg_str << "\n"; + + *(d->outdef) += "\n"; + + if (d->pDesc) { + *(d->outdef) += "\n"; + } + + PU_EMRHEADER pEmr = (PU_EMRHEADER) lpEMFR; + SVGOStringStream tmp_outdef; + tmp_outdef << "xDPI = 2540; + d->yDPI = 2540; + + d->dc[d->level].PixelsInX = pEmr->rclFrame.right; // - pEmr->rclFrame.left; + d->dc[d->level].PixelsInY = pEmr->rclFrame.bottom; // - pEmr->rclFrame.top; + + d->MMX = d->dc[d->level].PixelsInX / 100.0; + d->MMY = d->dc[d->level].PixelsInY / 100.0; + + d->dc[d->level].PixelsOutX = d->MMX * PX_PER_MM; + d->dc[d->level].PixelsOutY = d->MMY * PX_PER_MM; + + /* + calculate ratio of Inkscape dpi/device dpi + This can cause problems later due to accuracy limits in the EMF. A super high resolution + EMF might have a final device_scale of 0.074998, and adjusting the (integer) device size + by 1 will still not get it exactly to 0.075. Later when the font size is calculated it + can end up as 29.9992 or 22.4994 instead of the intended 30 or 22.5. This is handled by + snapping font sizes to the nearest .01. + */ + if (pEmr->szlMillimeters.cx && pEmr->szlDevice.cx) + device_scale = PX_PER_MM*pEmr->szlMillimeters.cx/pEmr->szlDevice.cx; + + tmp_outdef << + " width=\"" << d->MMX << "mm\"\n" << + " height=\"" << d->MMY << "mm\">\n"; + *(d->outdef) += tmp_outdef.str().c_str(); + *(d->outdef) += ""; // temporary end of header + + // d->defs holds any defines which are read in. + + tmp_outsvg << "\n\n\n"; // start of main body + + if (pEmr->nHandles) { + d->n_obj = pEmr->nHandles; + d->emf_obj = new EMF_OBJECT[d->n_obj]; + + // Init the new emf_obj list elements to null, provided the + // dynamic allocation succeeded. + if ( d->emf_obj != NULL ) + { + for( int i=0; i < d->n_obj; ++i ) + d->emf_obj[i].lpEMFR = NULL; + } //if + + } else { + d->emf_obj = NULL; + } + + break; + } + case U_EMR_POLYBEZIER: + { + dbg_str << "\n"; + + PU_EMRPOLYBEZIER pEmr = (PU_EMRPOLYBEZIER) lpEMFR; + uint32_t i,j; + + if (pEmr->cptl<4) + break; + + d->mask |= emr_mask; + + tmp_str << + "\n\tM " << + pix_to_x_point( d, pEmr->aptl[0].x, pEmr->aptl[0].y ) << " " << + pix_to_y_point( d, pEmr->aptl[0].x, pEmr->aptl[0].y) << " "; + + for (i=1; icptl; ) { + tmp_str << "\n\tC "; + for (j=0; j<3 && icptl; j++,i++) { + tmp_str << + pix_to_x_point( d, pEmr->aptl[i].x, pEmr->aptl[i].y ) << " " << + pix_to_y_point( d, pEmr->aptl[i].x, pEmr->aptl[i].y ) << " "; + } + } + + tmp_path << tmp_str.str().c_str(); + + break; + } + case U_EMR_POLYGON: + { + dbg_str << "\n"; + + PU_EMRPOLYGON pEmr = (PU_EMRPOLYGON) lpEMFR; + uint32_t i; + + if (pEmr->cptl < 2) + break; + + d->mask |= emr_mask; + + tmp_str << + "\n\tM " << + pix_to_x_point( d, pEmr->aptl[0].x, pEmr->aptl[0].y ) << " " << + pix_to_y_point( d, pEmr->aptl[0].x, pEmr->aptl[0].y ) << " "; + + for (i=1; icptl; i++) { + tmp_str << + "\n\tL " << + pix_to_x_point( d, pEmr->aptl[i].x, pEmr->aptl[i].y ) << " " << + pix_to_y_point( d, pEmr->aptl[i].x, pEmr->aptl[i].y ) << " "; + } + + tmp_path << tmp_str.str().c_str(); + tmp_path << " z"; + + break; + } + case U_EMR_POLYLINE: + { + dbg_str << "\n"; + + PU_EMRPOLYLINE pEmr = (PU_EMRPOLYLINE) lpEMFR; + uint32_t i; + + if (pEmr->cptl<2) + break; + + d->mask |= emr_mask; + + tmp_str << + "\n\tM " << + pix_to_x_point( d, pEmr->aptl[0].x, pEmr->aptl[0].y ) << " " << + pix_to_y_point( d, pEmr->aptl[0].x, pEmr->aptl[0].y ) << " "; + + for (i=1; icptl; i++) { + tmp_str << + "\n\tL " << + pix_to_x_point( d, pEmr->aptl[i].x, pEmr->aptl[i].y ) << " " << + pix_to_y_point( d, pEmr->aptl[i].x, pEmr->aptl[i].y ) << " "; + } + + tmp_path << tmp_str.str().c_str(); + + break; + } + case U_EMR_POLYBEZIERTO: + { + dbg_str << "\n"; + + PU_EMRPOLYBEZIERTO pEmr = (PU_EMRPOLYBEZIERTO) lpEMFR; + uint32_t i,j; + + d->mask |= emr_mask; + + for (i=0; icptl;) { + tmp_path << "\n\tC "; + for (j=0; j<3 && icptl; j++,i++) { + tmp_path << + pix_to_x_point( d, pEmr->aptl[i].x, pEmr->aptl[i].y ) << " " << + pix_to_y_point( d, pEmr->aptl[i].x, pEmr->aptl[i].y ) << " "; + } + } + + break; + } + case U_EMR_POLYLINETO: + { + dbg_str << "\n"; + + PU_EMRPOLYLINETO pEmr = (PU_EMRPOLYLINETO) lpEMFR; + uint32_t i; + + d->mask |= emr_mask; + + for (i=0; icptl;i++) { + tmp_path << + "\n\tL " << + pix_to_x_point( d, pEmr->aptl[i].x, pEmr->aptl[i].y ) << " " << + pix_to_y_point( d, pEmr->aptl[i].x, pEmr->aptl[i].y ) << " "; + } + + break; + } + case U_EMR_POLYPOLYLINE: + case U_EMR_POLYPOLYGON: + { + if (lpEMFR->iType == U_EMR_POLYPOLYLINE) + dbg_str << "\n"; + if (lpEMFR->iType == U_EMR_POLYPOLYGON) + dbg_str << "\n"; + + PU_EMRPOLYPOLYGON pEmr = (PU_EMRPOLYPOLYGON) lpEMFR; + unsigned int n, i, j; + + d->mask |= emr_mask; + + U_POINTL *aptl = (PU_POINTL) &pEmr->aPolyCounts[pEmr->nPolys]; + + i = 0; + for (n=0; nnPolys && icptl; n++) { + SVGOStringStream poly_path; + + poly_path << "\n\tM " << + pix_to_x_point( d, aptl[i].x, aptl[i].y ) << " " << + pix_to_y_point( d, aptl[i].x, aptl[i].y ) << " "; + i++; + + for (j=1; jaPolyCounts[n] && icptl; j++) { + poly_path << "\n\tL " << + pix_to_x_point( d, aptl[i].x, aptl[i].y ) << " " << + pix_to_y_point( d, aptl[i].x, aptl[i].y ) << " "; + i++; + } + + tmp_str << poly_path.str().c_str(); + if (lpEMFR->iType == U_EMR_POLYPOLYGON) + tmp_str << " z"; + tmp_str << " \n"; + } + + tmp_path << tmp_str.str().c_str(); + + break; + } + case U_EMR_SETWINDOWEXTEX: + { + dbg_str << "\n"; + + PU_EMRSETWINDOWEXTEX pEmr = (PU_EMRSETWINDOWEXTEX) lpEMFR; + + d->dc[d->level].sizeWnd = pEmr->szlExtent; + + if (!d->dc[d->level].sizeWnd.cx || !d->dc[d->level].sizeWnd.cy) { + d->dc[d->level].sizeWnd = d->dc[d->level].sizeView; + if (!d->dc[d->level].sizeWnd.cx || !d->dc[d->level].sizeWnd.cy) { + d->dc[d->level].sizeWnd.cx = d->dc[d->level].PixelsOutX; + d->dc[d->level].sizeWnd.cy = d->dc[d->level].PixelsOutY; + } + } + + if (!d->dc[d->level].sizeView.cx || !d->dc[d->level].sizeView.cy) { + d->dc[d->level].sizeView = d->dc[d->level].sizeWnd; + } + + d->dc[d->level].PixelsInX = d->dc[d->level].sizeWnd.cx; + d->dc[d->level].PixelsInY = d->dc[d->level].sizeWnd.cy; + + if (d->dc[d->level].PixelsInX && d->dc[d->level].PixelsInY) { + 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; + } + else { + d->dc[d->level].ScaleInX = 1; + d->dc[d->level].ScaleInY = 1; + } + + break; + } + case U_EMR_SETWINDOWORGEX: + { + dbg_str << "\n"; + + PU_EMRSETWINDOWORGEX pEmr = (PU_EMRSETWINDOWORGEX) lpEMFR; + d->dc[d->level].winorg = pEmr->ptlOrigin; + break; + } + case U_EMR_SETVIEWPORTEXTEX: + { + dbg_str << "\n"; + + PU_EMRSETVIEWPORTEXTEX pEmr = (PU_EMRSETVIEWPORTEXTEX) lpEMFR; + + d->dc[d->level].sizeView = pEmr->szlExtent; + + if (!d->dc[d->level].sizeView.cx || !d->dc[d->level].sizeView.cy) { + d->dc[d->level].sizeView = d->dc[d->level].sizeWnd; + if (!d->dc[d->level].sizeView.cx || !d->dc[d->level].sizeView.cy) { + d->dc[d->level].sizeView.cx = d->dc[d->level].PixelsOutX; + d->dc[d->level].sizeView.cy = d->dc[d->level].PixelsOutY; + } + } + + if (!d->dc[d->level].sizeWnd.cx || !d->dc[d->level].sizeWnd.cy) { + d->dc[d->level].sizeWnd = d->dc[d->level].sizeView; + } + + d->dc[d->level].PixelsInX = d->dc[d->level].sizeWnd.cx; + d->dc[d->level].PixelsInY = d->dc[d->level].sizeWnd.cy; + + if (d->dc[d->level].PixelsInX && d->dc[d->level].PixelsInY) { + 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; + } + else { + d->dc[d->level].ScaleInX = 1; + d->dc[d->level].ScaleInY = 1; + } + + break; + } + case U_EMR_SETVIEWPORTORGEX: + { + dbg_str << "\n"; + + PU_EMRSETVIEWPORTORGEX pEmr = (PU_EMRSETVIEWPORTORGEX) lpEMFR; + d->dc[d->level].vieworg = pEmr->ptlOrigin; + break; + } + case U_EMR_SETBRUSHORGEX: dbg_str << "\n"; break; + case U_EMR_EOF: + { + dbg_str << "\n"; + + tmp_outsvg << "\n"; + tmp_outsvg << "\n"; + *(d->outsvg) = *(d->outdef) + *(d->defs) + *(d->outsvg); + OK=0; + break; + } + case U_EMR_SETPIXELV: dbg_str << "\n"; break; + case U_EMR_SETMAPPERFLAGS: dbg_str << "\n"; break; + case U_EMR_SETMAPMODE: dbg_str << "\n"; break; + case U_EMR_SETBKMODE: dbg_str << "\n"; break; + case U_EMR_SETPOLYFILLMODE: + { + dbg_str << "\n"; + + PU_EMRSETPOLYFILLMODE pEmr = (PU_EMRSETPOLYFILLMODE) lpEMFR; + d->dc[d->level].style.fill_rule.value = + (pEmr->iMode == U_ALTERNATE ? 0 : + pEmr->iMode == U_WINDING ? 1 : 0); + break; + } + case U_EMR_SETROP2: + { + dbg_str << "\n"; + PU_EMRSETROP2 pEmr = (PU_EMRSETROP2) lpEMFR; + d->dwRop2 = pEmr->iMode; + break; + } + case U_EMR_SETSTRETCHBLTMODE: + { + PU_EMRSETSTRETCHBLTMODE pEmr = (PU_EMRSETSTRETCHBLTMODE) lpEMFR; // from wingdi.h + BLTmode = pEmr->iMode; + dbg_str << "\n"; + break; + } + case U_EMR_SETTEXTALIGN: + { + dbg_str << "\n"; + + PU_EMRSETTEXTALIGN pEmr = (PU_EMRSETTEXTALIGN) lpEMFR; + d->dc[d->level].textAlign = pEmr->iMode; + break; + } + case U_EMR_SETCOLORADJUSTMENT: + dbg_str << "\n"; + break; + case U_EMR_SETTEXTCOLOR: + { + dbg_str << "\n"; + + PU_EMRSETTEXTCOLOR pEmr = (PU_EMRSETTEXTCOLOR) lpEMFR; + d->dc[d->level].textColor = pEmr->crColor; + d->dc[d->level].textColorSet = true; + break; + } + case U_EMR_SETBKCOLOR: + { + dbg_str << "\n"; + + PU_EMRSETBKCOLOR pEmr = (PU_EMRSETBKCOLOR) lpEMFR; + d->dc[d->level].bkColor = pEmr->crColor; + d->dc[d->level].bkColorSet = true; + break; + } + case U_EMR_OFFSETCLIPRGN: dbg_str << "\n"; break; + case U_EMR_MOVETOEX: + { + dbg_str << "\n"; + + PU_EMRMOVETOEX pEmr = (PU_EMRMOVETOEX) lpEMFR; + + d->mask |= emr_mask; + + d->dc[d->level].cur = pEmr->ptl; + + tmp_path << + "\n\tM " << + pix_to_x_point( d, pEmr->ptl.x, pEmr->ptl.y ) << " " << + pix_to_y_point( d, pEmr->ptl.x, pEmr->ptl.y ) << " "; + break; + } + case U_EMR_SETMETARGN: dbg_str << "\n"; break; + case U_EMR_EXCLUDECLIPRECT: dbg_str << "\n"; break; + case U_EMR_INTERSECTCLIPRECT: + { + dbg_str << "\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 l = pix_to_x_point( d, rc.left, rc.top ); + double t = pix_to_y_point( d, rc.left, rc.top ); + double r = pix_to_x_point( d, rc.right, rc.bottom ); + double b = pix_to_y_point( d, rc.right, rc.bottom ); + + SVGOStringStream tmp_rectangle; + tmp_rectangle << "\nid) << "\" >"; + tmp_rectangle << "\n"; + tmp_rectangle << "\n"; + + *(d->outdef) += tmp_rectangle.str().c_str(); + *(d->path) = ""; + break; + } + case U_EMR_SCALEVIEWPORTEXTEX: dbg_str << "\n"; break; + case U_EMR_SCALEWINDOWEXTEX: dbg_str << "\n"; break; + case U_EMR_SAVEDC: + dbg_str << "\n"; + + if (d->level < EMF_MAX_DC) { + d->dc[d->level + 1] = d->dc[d->level]; + d->level = d->level + 1; + } + break; + case U_EMR_RESTOREDC: + { + dbg_str << "\n"; + + PU_EMRRESTOREDC pEmr = (PU_EMRRESTOREDC) lpEMFR; + int old_level = d->level; + if (pEmr->iRelative >= 0) { + if (pEmr->iRelative < d->level) + d->level = pEmr->iRelative; + } + else { + if (d->level + pEmr->iRelative >= 0) + d->level = d->level + pEmr->iRelative; + } + while (old_level > d->level) { + 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))) + delete[] d->dc[old_level].style.stroke_dash.dash; + old_level--; + } + break; + } + case U_EMR_SETWORLDTRANSFORM: + { + dbg_str << "\n"; + + PU_EMRSETWORLDTRANSFORM pEmr = (PU_EMRSETWORLDTRANSFORM) lpEMFR; + d->dc[d->level].worldTransform = pEmr->xform; + break; + } + case U_EMR_MODIFYWORLDTRANSFORM: + { + dbg_str << "\n"; + + PU_EMRMODIFYWORLDTRANSFORM pEmr = (PU_EMRMODIFYWORLDTRANSFORM) lpEMFR; + switch (pEmr->iMode) + { + case U_MWT_IDENTITY: + d->dc[d->level].worldTransform.eM11 = 1.0; + d->dc[d->level].worldTransform.eM12 = 0.0; + d->dc[d->level].worldTransform.eM21 = 0.0; + d->dc[d->level].worldTransform.eM22 = 1.0; + d->dc[d->level].worldTransform.eDx = 0.0; + d->dc[d->level].worldTransform.eDy = 0.0; + break; + case U_MWT_LEFTMULTIPLY: + { +// d->dc[d->level].worldTransform = pEmr->xform * worldTransform; + + float a11 = pEmr->xform.eM11; + float a12 = pEmr->xform.eM12; + float a13 = 0.0; + float a21 = pEmr->xform.eM21; + float a22 = pEmr->xform.eM22; + float a23 = 0.0; + float a31 = pEmr->xform.eDx; + float a32 = pEmr->xform.eDy; + float a33 = 1.0; + + float b11 = d->dc[d->level].worldTransform.eM11; + float b12 = d->dc[d->level].worldTransform.eM12; + //float b13 = 0.0; + float b21 = d->dc[d->level].worldTransform.eM21; + float b22 = d->dc[d->level].worldTransform.eM22; + //float b23 = 0.0; + float b31 = d->dc[d->level].worldTransform.eDx; + float b32 = d->dc[d->level].worldTransform.eDy; + //float b33 = 1.0; + + float c11 = a11*b11 + a12*b21 + a13*b31;; + float c12 = a11*b12 + a12*b22 + a13*b32;; + //float c13 = a11*b13 + a12*b23 + a13*b33;; + float c21 = a21*b11 + a22*b21 + a23*b31;; + float c22 = a21*b12 + a22*b22 + a23*b32;; + //float c23 = a21*b13 + a22*b23 + a23*b33;; + float c31 = a31*b11 + a32*b21 + a33*b31;; + float c32 = a31*b12 + a32*b22 + a33*b32;; + //float c33 = a31*b13 + a32*b23 + a33*b33;; + + d->dc[d->level].worldTransform.eM11 = c11;; + d->dc[d->level].worldTransform.eM12 = c12;; + d->dc[d->level].worldTransform.eM21 = c21;; + d->dc[d->level].worldTransform.eM22 = c22;; + d->dc[d->level].worldTransform.eDx = c31; + d->dc[d->level].worldTransform.eDy = c32; + + break; + } + case U_MWT_RIGHTMULTIPLY: + { +// d->dc[d->level].worldTransform = worldTransform * pEmr->xform; + + float a11 = d->dc[d->level].worldTransform.eM11; + float a12 = d->dc[d->level].worldTransform.eM12; + float a13 = 0.0; + float a21 = d->dc[d->level].worldTransform.eM21; + float a22 = d->dc[d->level].worldTransform.eM22; + float a23 = 0.0; + float a31 = d->dc[d->level].worldTransform.eDx; + float a32 = d->dc[d->level].worldTransform.eDy; + float a33 = 1.0; + + float b11 = pEmr->xform.eM11; + float b12 = pEmr->xform.eM12; + //float b13 = 0.0; + float b21 = pEmr->xform.eM21; + float b22 = pEmr->xform.eM22; + //float b23 = 0.0; + float b31 = pEmr->xform.eDx; + float b32 = pEmr->xform.eDy; + //float b33 = 1.0; + + float c11 = a11*b11 + a12*b21 + a13*b31;; + float c12 = a11*b12 + a12*b22 + a13*b32;; + //float c13 = a11*b13 + a12*b23 + a13*b33;; + float c21 = a21*b11 + a22*b21 + a23*b31;; + float c22 = a21*b12 + a22*b22 + a23*b32;; + //float c23 = a21*b13 + a22*b23 + a23*b33;; + float c31 = a31*b11 + a32*b21 + a33*b31;; + float c32 = a31*b12 + a32*b22 + a33*b32;; + //float c33 = a31*b13 + a32*b23 + a33*b33;; + + d->dc[d->level].worldTransform.eM11 = c11;; + d->dc[d->level].worldTransform.eM12 = c12;; + d->dc[d->level].worldTransform.eM21 = c21;; + d->dc[d->level].worldTransform.eM22 = c22;; + d->dc[d->level].worldTransform.eDx = c31; + d->dc[d->level].worldTransform.eDy = c32; + + break; + } +// case MWT_SET: + default: + d->dc[d->level].worldTransform = pEmr->xform; + break; + } + break; + } + case U_EMR_SELECTOBJECT: + { + dbg_str << "\n"; + + PU_EMRSELECTOBJECT pEmr = (PU_EMRSELECTOBJECT) lpEMFR; + unsigned int index = pEmr->ihObject; + + if (index & U_STOCK_OBJECT) { + switch (index) { + case U_NULL_BRUSH: + d->dc[d->level].fill_mode = DRAW_PAINT; + d->dc[d->level].fill_set = false; + break; + 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: + val = 0.0 / 255.0; + break; + case U_DKGRAY_BRUSH: + val = 64.0 / 255.0; + break; + case U_GRAY_BRUSH: + val = 128.0 / 255.0; + break; + case U_LTGRAY_BRUSH: + val = 192.0 / 255.0; + break; + case U_WHITE_BRUSH: + val = 255.0 / 255.0; + break; + } + d->dc[d->level].style.fill.value.color.set( val, val, val ); + + d->dc[d->level].fill_mode = DRAW_PAINT; + d->dc[d->level].fill_set = true; + break; + } + case U_NULL_PEN: + d->dc[d->level].stroke_mode = DRAW_PAINT; + d->dc[d->level].stroke_set = false; + break; + case U_BLACK_PEN: + case U_WHITE_PEN: + { + float val = index == U_BLACK_PEN ? 0 : 1; + d->dc[d->level].style.stroke_dasharray_set = 0; + d->dc[d->level].style.stroke_width.value = 1.0; + d->dc[d->level].style.stroke.value.color.set( val, val, val ); + + d->dc[d->level].stroke_mode = DRAW_PAINT; + d->dc[d->level].stroke_set = true; + + break; + } + } + } else { + if ( /*index >= 0 &&*/ index < (unsigned int) d->n_obj) { + switch (d->emf_obj[index].type) + { + case U_EMR_CREATEPEN: + select_pen(d, index); + break; + case U_EMR_CREATEBRUSHINDIRECT: + case U_EMR_CREATEDIBPATTERNBRUSHPT: + case U_EMR_CREATEMONOBRUSH: + select_brush(d, index); + break; + case U_EMR_EXTCREATEPEN: + select_extpen(d, index); + break; + case U_EMR_EXTCREATEFONTINDIRECTW: + select_font(d, index); + break; + } + } + } + break; + } + case U_EMR_CREATEPEN: + { + dbg_str << "\n"; + + PU_EMRCREATEPEN pEmr = (PU_EMRCREATEPEN) lpEMFR; + insert_object(d, pEmr->ihPen, U_EMR_CREATEPEN, lpEMFR); + break; + } + case U_EMR_CREATEBRUSHINDIRECT: + { + dbg_str << "\n"; + + PU_EMRCREATEBRUSHINDIRECT pEmr = (PU_EMRCREATEBRUSHINDIRECT) lpEMFR; + insert_object(d, pEmr->ihBrush, U_EMR_CREATEBRUSHINDIRECT, lpEMFR); + break; + } + case U_EMR_DELETEOBJECT: + dbg_str << "\n"; + break; + case U_EMR_ANGLEARC: + dbg_str << "\n"; + break; + case U_EMR_ELLIPSE: + { + dbg_str << "\n"; + + PU_EMRELLIPSE pEmr = (PU_EMRELLIPSE) lpEMFR; + U_RECTL rclBox = pEmr->rclBox; + + double l = pix_to_x_point( d, rclBox.left, rclBox.top ); + double t = pix_to_y_point( d, rclBox.left, rclBox.top ); + double r = pix_to_x_point( d, rclBox.right, rclBox.bottom ); + double b = pix_to_y_point( d, rclBox.right, rclBox.bottom ); + + double cx = (l + r) / 2.0; + double cy = (t + b) / 2.0; + double rx = fabs(l - r) / 2.0; + double ry = fabs(t - b) / 2.0; + + SVGOStringStream tmp_ellipse; + tmp_ellipse << "cx=\"" << cx << "\" "; + tmp_ellipse << "cy=\"" << cy << "\" "; + tmp_ellipse << "rx=\"" << rx << "\" "; + tmp_ellipse << "ry=\"" << ry << "\" "; + + d->mask |= emr_mask; + + *(d->outsvg) += " iType); // + *(d->outsvg) += "\n\t"; + *(d->outsvg) += tmp_ellipse.str().c_str(); + *(d->outsvg) += "/> \n"; + *(d->path) = ""; + break; + } + case U_EMR_RECTANGLE: + { + dbg_str << "\n"; + + PU_EMRRECTANGLE pEmr = (PU_EMRRECTANGLE) lpEMFR; + U_RECTL rc = pEmr->rclBox; + + double l = pix_to_x_point( d, rc.left, rc.top ); + double t = pix_to_y_point( d, rc.left, rc.top ); + double r = pix_to_x_point( d, rc.right, rc.bottom ); + double b = pix_to_y_point( d, rc.right, rc.bottom ); + + SVGOStringStream tmp_rectangle; + tmp_rectangle << "\n\tM " << l << " " << t << " "; + tmp_rectangle << "\n\tL " << r << " " << t << " "; + tmp_rectangle << "\n\tL " << r << " " << b << " "; + tmp_rectangle << "\n\tL " << l << " " << b << " "; + tmp_rectangle << "\n\tz"; + + d->mask |= emr_mask; + + tmp_path << tmp_rectangle.str().c_str(); + break; + } + case U_EMR_ROUNDRECT: + { + dbg_str << "\n"; + + PU_EMRROUNDRECT pEmr = (PU_EMRROUNDRECT) lpEMFR; + U_RECTL rc = pEmr->rclBox; + U_SIZEL corner = pEmr->szlCorner; + double f = 4.*(sqrt(2) - 1)/3; + + double l = pix_to_x_point(d, rc.left, rc.top); + double t = pix_to_y_point(d, rc.left, rc.top); + double r = pix_to_x_point(d, rc.right, rc.bottom); + double b = pix_to_y_point(d, rc.right, rc.bottom); + double cnx = pix_to_size_point(d, corner.cx/2); + double cny = pix_to_size_point(d, corner.cy/2); + + SVGOStringStream tmp_rectangle; + tmp_rectangle << "\n\tM " << l << ", " << t + cny << " "; + tmp_rectangle << "\n\tC " << l << ", " << t + (1-f)*cny << " " << l + (1-f)*cnx << ", " << t << " " << l + cnx << ", " << t << " "; + tmp_rectangle << "\n\tL " << r - cnx << ", " << t << " "; + tmp_rectangle << "\n\tC " << r - (1-f)*cnx << ", " << t << " " << r << ", " << t + (1-f)*cny << " " << r << ", " << t + cny << " "; + tmp_rectangle << "\n\tL " << r << ", " << b - cny << " "; + tmp_rectangle << "\n\tC " << r << ", " << b - (1-f)*cny << " " << r - (1-f)*cnx << ", " << b << " " << r - cnx << ", " << b << " "; + tmp_rectangle << "\n\tL " << l + cnx << ", " << b << " "; + tmp_rectangle << "\n\tC " << l + (1-f)*cnx << ", " << b << " " << l << ", " << b - (1-f)*cny << " " << l << ", " << b - cny << " "; + tmp_rectangle << "\n\tz"; + + d->mask |= emr_mask; + + tmp_path << tmp_rectangle.str().c_str(); + break; + } + case U_EMR_ARC: + { + dbg_str << "\n"; + U_PAIRF center,start,end,size; + int f1; + int f2 = (d->arcdir == U_AD_COUNTERCLOCKWISE ? 0 : 1); + if(!emr_arc_points( lpEMFR, &f1, f2, ¢er, &start, &end, &size)){ + 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 ; + tmp_path << " 0 "; + tmp_path << " " << f1 << "," << f2 << " "; + tmp_path << pix_to_x_point(d, end.x, end.y) << "," << pix_to_y_point(d, end.x, end.y)<< " "; + + d->mask |= emr_mask; + } + else { + dbg_str << "\n"; + } + break; + } + case U_EMR_CHORD: + { + dbg_str << "\n"; + U_PAIRF center,start,end,size; + int f1; + int f2 = (d->arcdir == U_AD_COUNTERCLOCKWISE ? 0 : 1); + if(!emr_arc_points( lpEMFR, &f1, f2, ¢er, &start, &end, &size)){ + 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 ; + tmp_path << " 0 "; + tmp_path << " " << f1 << "," << f2 << " "; + tmp_path << pix_to_x_point(d, end.x, end.y) << "," << pix_to_y_point(d, end.x, end.y); + tmp_path << " z "; + d->mask |= emr_mask; + } + else { + dbg_str << "\n"; + } + break; + } + case U_EMR_PIE: + { + dbg_str << "\n"; + U_PAIRF center,start,end,size; + int f1; + int f2 = (d->arcdir == U_AD_COUNTERCLOCKWISE ? 0 : 1); + if(!emr_arc_points( lpEMFR, &f1, f2, ¢er, &start, &end, &size)){ + 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; + tmp_path << " 0 "; + tmp_path << " " << f1 << "," << f2 << " "; + tmp_path << pix_to_x_point(d, end.x, end.y) << "," << pix_to_y_point(d, end.x, end.y); + tmp_path << " z "; + d->mask |= emr_mask; + } + else { + dbg_str << "\n"; + } + break; + } + case U_EMR_SELECTPALETTE: dbg_str << "\n"; break; + case U_EMR_CREATEPALETTE: dbg_str << "\n"; break; + case U_EMR_SETPALETTEENTRIES: dbg_str << "\n"; break; + case U_EMR_RESIZEPALETTE: dbg_str << "\n"; break; + case U_EMR_REALIZEPALETTE: dbg_str << "\n"; break; + case U_EMR_EXTFLOODFILL: dbg_str << "\n"; break; + case U_EMR_LINETO: + { + dbg_str << "\n"; + + PU_EMRLINETO pEmr = (PU_EMRLINETO) lpEMFR; + + d->mask |= emr_mask; + + tmp_path << + "\n\tL " << + pix_to_x_point( d, pEmr->ptl.x, pEmr->ptl.y ) << " " << + pix_to_y_point( d, pEmr->ptl.x, pEmr->ptl.y ) << " "; + break; + } + case U_EMR_ARCTO: + { + dbg_str << "\n"; + U_PAIRF center,start,end,size; + int f1; + int f2 = (d->arcdir == U_AD_COUNTERCLOCKWISE ? 0 : 1); + if(!emr_arc_points( lpEMFR, &f1, f2, ¢er, &start, &end, &size)){ + // draw a line from current position to start + 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 ; + tmp_path << " 0 "; + tmp_path << " " << f1 << "," << f2 << " "; + tmp_path << pix_to_x_point(d, end.x, end.y) << "," << pix_to_y_point(d, end.x, end.y)<< " "; + + d->mask |= emr_mask; + } + else { + dbg_str << "\n"; + } + break; + } + case U_EMR_POLYDRAW: dbg_str << "\n"; break; + case U_EMR_SETARCDIRECTION: + { + dbg_str << "\n"; + PU_EMRSETARCDIRECTION pEmr = (PU_EMRSETARCDIRECTION) lpEMFR; + if(d->arcdir == U_AD_CLOCKWISE || d->arcdir == U_AD_COUNTERCLOCKWISE){ // EMF file could be corrupt + d->arcdir = pEmr->iArcDirection; + } + break; + } + case U_EMR_SETMITERLIMIT: + { + dbg_str << "\n"; + + PU_EMRSETMITERLIMIT pEmr = (PU_EMRSETMITERLIMIT) lpEMFR; + + //The function takes a float but saves a 32 bit int in the U_EMR_SETMITERLIMIT record. + float miterlimit = *((int32_t *) &(pEmr->eMiterLimit)); + d->dc[d->level].style.stroke_miterlimit.value = miterlimit; //ratio, not a pt size + if (d->dc[d->level].style.stroke_miterlimit.value < 2) + d->dc[d->level].style.stroke_miterlimit.value = 2.0; + break; + } + case U_EMR_BEGINPATH: + { + dbg_str << "\n"; + // The next line should never be needed, should have been handled before main switch + *(d->path) = ""; + d->mask |= emr_mask; + break; + } + case U_EMR_ENDPATH: + { + dbg_str << "\n"; + d->mask &= (0xFFFFFFFF - U_DRAW_ONLYTO); // clear the OnlyTo bit (it might not have been set), prevents any further path extension + break; + } + case U_EMR_CLOSEFIGURE: + { + dbg_str << "\n"; + // EMF may contain multiple closefigures on one path + tmp_path << "\n\tz"; + d->mask |= U_DRAW_CLOSED; + break; + } + case U_EMR_FILLPATH: + { + dbg_str << "\n"; + if(d->mask & U_DRAW_PATH){ // Operation only effects declared paths + if(!(d->mask & U_DRAW_CLOSED)){ // Close a path not explicitly closed by an EMRCLOSEFIGURE, otherwise fill makes no sense + tmp_path << "\n\tz"; + d->mask |= U_DRAW_CLOSED; + } + d->mask |= emr_mask; + d->drawtype = U_EMR_FILLPATH; + } + break; + } + case U_EMR_STROKEANDFILLPATH: + { + dbg_str << "\n"; + if(d->mask & U_DRAW_PATH){ // Operation only effects declared paths + if(!(d->mask & U_DRAW_CLOSED)){ // Close a path not explicitly closed by an EMRCLOSEFIGURE, otherwise fill makes no sense + tmp_path << "\n\tz"; + d->mask |= U_DRAW_CLOSED; + } + d->mask |= emr_mask; + d->drawtype = U_EMR_STROKEANDFILLPATH; + } + break; + } + case U_EMR_STROKEPATH: + { + dbg_str << "\n"; + if(d->mask & U_DRAW_PATH){ // Operation only effects declared paths + d->mask |= emr_mask; + d->drawtype = U_EMR_STROKEPATH; + } + break; + } + case U_EMR_FLATTENPATH: dbg_str << "\n"; break; + case U_EMR_WIDENPATH: dbg_str << "\n"; break; + case U_EMR_SELECTCLIPPATH: dbg_str << "\n"; break; + case U_EMR_ABORTPATH: + { + dbg_str << "\n"; + *(d->path) = ""; + d->drawtype = 0; + break; + } + case U_EMR_UNDEF69: dbg_str << "\n"; break; + case U_EMR_COMMENT: + { + dbg_str << "\n"; + + PU_EMRCOMMENT pEmr = (PU_EMRCOMMENT) lpEMFR; + + char *szTxt = (char *) pEmr->Data; + + for (uint32_t i = 0; i < pEmr->cbData; i++) { + if ( *szTxt) { + if ( *szTxt >= ' ' && *szTxt < 'z' && *szTxt != '<' && *szTxt != '>' ) { + tmp_str << *szTxt; + } + szTxt++; + } + } + + if (0 && strlen(tmp_str.str().c_str())) { + tmp_outsvg << " \n"; + } + + break; + } + case U_EMR_FILLRGN: dbg_str << "\n"; break; + case U_EMR_FRAMERGN: dbg_str << "\n"; break; + case U_EMR_INVERTRGN: dbg_str << "\n"; break; + case U_EMR_PAINTRGN: dbg_str << "\n"; break; + case U_EMR_EXTSELECTCLIPRGN: + { + dbg_str << "\n"; + + PU_EMREXTSELECTCLIPRGN pEmr = (PU_EMREXTSELECTCLIPRGN) lpEMFR; + if (pEmr->iMode == U_RGN_COPY) + clipset = false; + break; + } + case U_EMR_BITBLT: + { + dbg_str << "\n"; + + PU_EMRBITBLT pEmr = (PU_EMRBITBLT) lpEMFR; + // Treat all nonImage bitblts as a rectangular write. Definitely not correct, but at + // least it leaves objects where the operations should have been. + if (!pEmr->cbBmiSrc) { + // should be an application of a DIBPATTERNBRUSHPT, use a solid color instead + double l = pix_to_x_point( d, pEmr->Dest.x, pEmr->Dest.y); + double t = pix_to_y_point( d, pEmr->Dest.x, pEmr->Dest.y); + double r = pix_to_x_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y); + double b = pix_to_y_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y); + + SVGOStringStream tmp_rectangle; + tmp_rectangle << "\n\tM " << l << " " << t << " "; + tmp_rectangle << "\n\tL " << r << " " << t << " "; + tmp_rectangle << "\n\tL " << r << " " << b << " "; + tmp_rectangle << "\n\tL " << l << " " << b << " "; + tmp_rectangle << "\n\tz"; + + d->mask |= emr_mask; + d->dwRop3 = pEmr->dwRop; // we will try to approximate SOME of these + 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 + + tmp_path << tmp_rectangle.str().c_str(); + } + break; + } + case U_EMR_STRETCHBLT: dbg_str << "\n"; break; + case U_EMR_MASKBLT: dbg_str << "\n"; break; + case U_EMR_PLGBLT: dbg_str << "\n"; break; + case U_EMR_SETDIBITSTODEVICE: dbg_str << "\n"; break; + case U_EMR_STRETCHDIBITS: + { + // Some applications use multiple EMF operations, including multiple STRETCHDIBITS to create + // images with transparent regions. PowerPoint does this with rotated images, for instance. + // Parsing all of that to derive a single resultant image object is left for a later version + // of this code. In the meantime, every STRETCHDIBITS goes directly to an image. The Inkscape + // user can sort out transparency later using Gimp, if need be. + + PU_EMRSTRETCHDIBITS pEmr = (PU_EMRSTRETCHDIBITS) lpEMFR; + double l = pix_to_x_point( d, pEmr->Dest.x, pEmr->Dest.y ); + double t = pix_to_y_point( d, pEmr->Dest.x, pEmr->Dest.y ); + double r = pix_to_x_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y ); + double b = pix_to_y_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y ); + SVGOStringStream tmp_image; + tmp_image << " y=\"" << t << "\"\n x=\"" << l <<"\"\n "; + + // The image ID is filled in much later when tmp_image is converted + + tmp_image << " xlink:href=\"data:image/png;base64,"; + + MEMPNG mempng; // PNG in memory comes back in this + mempng.buffer = NULL; + + char *rgba_px=NULL; // RGBA pixels + char *px=NULL; // DIB pixels + uint32_t width, height, colortype, numCt, invert; + PU_RGBQUAD ct = NULL; + if(!pEmr->cbBitsSrc || + !pEmr->cbBmiSrc || + (pEmr->iUsageSrc != U_DIB_RGB_COLORS) || + !get_DIB_params( // this returns pointers and values, but allocates no memory + pEmr, + pEmr->offBitsSrc, + pEmr->offBmiSrc, + &px, + &ct, + &numCt, + &width, + &height, + &colortype, + &invert + )){ + + if(!DIB_to_RGBA( + px, // DIB pixel array + ct, // DIB color table + numCt, // DIB color table number of entries + &rgba_px, // U_RGBA pixel array (32 bits), created by this routine, caller must free. + width, // Width of pixel array + height, // Height of pixel array + colortype, // DIB BitCount Enumeration + numCt, // Color table used if not 0 + invert // If DIB rows are in opposite order from RGBA rows + ) && + rgba_px) + { + toPNG( // Get the image from the RGBA px into mempng + &mempng, + width, height, + rgba_px, + 4 * width * height); + free(rgba_px); + } + } + if(mempng.buffer){ + gchar *base64String = g_base64_encode((guchar*) mempng.buffer, mempng.size ); + free(mempng.buffer); + tmp_image << base64String ; + g_free(base64String); + } + else { + // insert a random 3x4 blotch otherwise + tmp_image << "iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAAA3NCSVQICAjb4U/gAAAALElEQVQImQXBQQ2AMAAAsUJQMSWI2H8qME1yMshojwrvGB8XcHKvR1XtOTc/8HENumHCsOMAAAAASUVORK5CYII="; + } + + tmp_image << "\"\n height=\"" << b-t+1 << "\"\n width=\"" << r-l+1 << "\"\n"; + + *(d->outsvg) += "\n\t outsvg) += tmp_image.str().c_str(); + *(d->outsvg) += "/> \n"; + *(d->path) = ""; + + dbg_str << "\n"; + break; + } + case U_EMR_EXTCREATEFONTINDIRECTW: + { + dbg_str << "\n"; + + PU_EMREXTCREATEFONTINDIRECTW pEmr = (PU_EMREXTCREATEFONTINDIRECTW) lpEMFR; + insert_object(d, pEmr->ihFont, U_EMR_EXTCREATEFONTINDIRECTW, lpEMFR); + break; + } + case U_EMR_EXTTEXTOUTA: + case U_EMR_EXTTEXTOUTW: + case U_EMR_SMALLTEXTOUT: + { + dbg_str << "\n"; + + PU_EMREXTTEXTOUTW pEmr = (PU_EMREXTTEXTOUTW) lpEMFR; + PU_EMRSMALLTEXTOUT pEmrS = (PU_EMRSMALLTEXTOUT) lpEMFR; + + double x1,y1; + int roff = sizeof(U_EMRSMALLTEXTOUT); //offset to the start of the variable fields, only used with U_EMR_SMALLTEXTOUT + int cChars; + if(lpEMFR->iType==U_EMR_SMALLTEXTOUT){ + x1 = pEmrS->Dest.x; + y1 = pEmrS->Dest.y; + cChars = pEmrS->cChars; + if(!(pEmrS->fuOptions & U_ETO_NO_RECT)){ roff += sizeof(U_RECTL); } + } + else { + x1 = pEmr->emrtext.ptlReference.x; + y1 = pEmr->emrtext.ptlReference.y; + cChars = 0; + } + + if (d->dc[d->level].textAlign & U_TA_UPDATECP) { + x1 = d->dc[d->level].cur.x; + y1 = d->dc[d->level].cur.y; + } + + double x = pix_to_x_point(d, x1, y1); + double y = pix_to_y_point(d, x1, y1); + + 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 + else { dfact = 0.85; } // alignments U_TA_TOP 0x00 to 0x07, factor is approximate + if (d->dc[d->level].style.baseline_shift.value) { + 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); + } + else { + y += dfact * fabs(d->dc[d->level].style.font_size.computed); + } + + uint32_t *dup_wt = NULL; + + if( lpEMFR->iType==U_EMR_EXTTEXTOUTA){ + /* These should be JUST ASCII, but they might not be... + If it holds Utf-8 or plain ASCII the first call will succeed. + If not, assume that it holds Latin1. + If that fails then someting is really screwed up! + */ + dup_wt = U_Utf8ToUtf32le((char *) pEmr + pEmr->emrtext.offString, pEmr->emrtext.nChars, NULL); + if(!dup_wt)dup_wt = U_Latin1ToUtf32le((char *) pEmr + pEmr->emrtext.offString, pEmr->emrtext.nChars, NULL); + if(!dup_wt)dup_wt = unknown_chars(pEmr->emrtext.nChars); + } + else if( lpEMFR->iType==U_EMR_EXTTEXTOUTW){ + dup_wt = U_Utf16leToUtf32le((uint16_t *)((char *) pEmr + pEmr->emrtext.offString), pEmr->emrtext.nChars, NULL); + if(!dup_wt)dup_wt = unknown_chars(pEmr->emrtext.nChars); + } + else { // U_EMR_SMALLTEXTOUT + if(pEmrS->fuOptions & U_ETO_SMALL_CHARS){ + dup_wt = U_Utf8ToUtf32le((char *) pEmrS + roff, cChars, NULL); + } + else { + dup_wt = U_Utf16leToUtf32le((uint16_t *)((char *) pEmrS + roff), cChars, NULL); + } + if(!dup_wt)dup_wt = unknown_chars(cChars); + } + + msdepua(dup_wt); //convert everything in Microsoft's private use area. For Symbol, Wingdings, Dingbats + + if(NonToUnicode(dup_wt, d->dc[d->level].tstyle.font_family.value)){ + g_free(d->dc[d->level].tstyle.font_family.value); + d->dc[d->level].tstyle.font_family.value = g_strdup("Times New Roman"); + } + + char *ansi_text; + ansi_text = (char *) U_Utf32leToUtf8((uint32_t *)dup_wt, 0, NULL); + free(dup_wt); + + if (ansi_text) { +// gchar *p = ansi_text; +// while (*p) { +// if (*p < 32 || *p >= 127) { +// g_free(ansi_text); +// ansi_text = g_strdup(""); +// break; +// } +// p++; +// } + + SVGOStringStream ts; + + gchar *escaped_text = g_markup_escape_text(ansi_text, -1); + +// float text_rgb[3]; +// sp_color_get_rgb_floatv( &(d->dc[d->level].style.fill.value.color), text_rgb ); + +// if (!d->dc[d->level].textColorSet) { +// d->dc[d->level].textColor = RGB(SP_COLOR_F_TO_U(text_rgb[0]), +// SP_COLOR_F_TO_U(text_rgb[1]), +// SP_COLOR_F_TO_U(text_rgb[2])); +// } + + char tmp[128]; + snprintf(tmp, 127, + "fill:#%02x%02x%02x;", + U_RGBAGetR(d->dc[d->level].textColor), + U_RGBAGetG(d->dc[d->level].textColor), + U_RGBAGetB(d->dc[d->level].textColor)); + + bool i = (d->dc[d->level].style.font_style.value == SP_CSS_FONT_STYLE_ITALIC); + //bool o = (d->dc[d->level].style.font_style.value == SP_CSS_FONT_STYLE_OBLIQUE); + bool b = (d->dc[d->level].style.font_weight.value == SP_CSS_FONT_WEIGHT_BOLD) || + (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; + + ts << " id++) << "\"\n"; + ts << " xml:space=\"preserve\"\n"; + ts << " x=\"" << x << "\"\n"; + ts << " y=\"" << y << "\"\n"; + if (d->dc[d->level].style.baseline_shift.value) { + ts << " transform=\"" + << "rotate(-" << d->dc[d->level].style.baseline_shift.value + << " " << x << " " << y << ")" + << "\"\n"; + } + ts << " style=\"" + << "font-size:" << fabs(d->dc[d->level].style.font_size.computed) << "px;" + << tmp + << "font-style:" << (i ? "italic" : "normal") << ";" + << "font-weight:" << (b ? "bold" : "normal") << ";" + << "text-align:" << (lcr==2 ? "center" : lcr==1 ? "end" : "start") << ";" + << "text-anchor:" << (lcr==2 ? "middle" : lcr==1 ? "end" : "start") << ";" + << "font-family:" << d->dc[d->level].tstyle.font_family.value << ";" + << "\"\n"; + ts << " >"; + ts << escaped_text; + ts << "\n"; + + *(d->outsvg) += ts.str().c_str(); + + g_free(escaped_text); + free(ansi_text); + } + + break; + } + case U_EMR_POLYBEZIER16: + { + dbg_str << "\n"; + + PU_EMRPOLYBEZIER16 pEmr = (PU_EMRPOLYBEZIER16) lpEMFR; + PU_POINT16 apts = (PU_POINT16) pEmr->apts; // Bug in MinGW wingdi.h ? + uint32_t i,j; + + if (pEmr->cpts<4) + break; + + d->mask |= emr_mask; + + tmp_str << + "\n\tM " << + pix_to_x_point( d, apts[0].x, apts[0].y ) << " " << + pix_to_y_point( d, apts[0].x, apts[0].y ) << " "; + + for (i=1; icpts; ) { + tmp_str << "\n\tC "; + for (j=0; j<3 && icpts; j++,i++) { + tmp_str << + pix_to_x_point( d, apts[i].x, apts[i].y ) << " " << + pix_to_y_point( d, apts[i].x, apts[i].y ) << " "; + } + } + + tmp_path << tmp_str.str().c_str(); + + break; + } + case U_EMR_POLYGON16: + { + dbg_str << "\n"; + + PU_EMRPOLYGON16 pEmr = (PU_EMRPOLYGON16) lpEMFR; + PU_POINT16 apts = (PU_POINT16) pEmr->apts; // Bug in MinGW wingdi.h ? + SVGOStringStream tmp_poly; + unsigned int i; + unsigned int first = 0; + + d->mask |= emr_mask; + + // skip the first point? + tmp_poly << "\n\tM " << + pix_to_x_point( d, apts[first].x, apts[first].y ) << " " << + pix_to_y_point( d, apts[first].x, apts[first].y ) << " "; + + for (i=first+1; icpts; i++) { + tmp_poly << "\n\tL " << + pix_to_x_point( d, apts[i].x, apts[i].y ) << " " << + pix_to_y_point( d, apts[i].x, apts[i].y ) << " "; + } + + tmp_path << tmp_poly.str().c_str(); + tmp_path << "\n\tz"; + d->mask |= U_DRAW_CLOSED; + + break; + } + case U_EMR_POLYLINE16: + { + dbg_str << "\n"; + + PU_EMRPOLYLINE16 pEmr = (PU_EMRPOLYLINE16) lpEMFR; + PU_POINT16 apts = (PU_POINT16) pEmr->apts; // Bug in MinGW wingdi.h ? + uint32_t i; + + if (pEmr->cpts<2) + break; + + d->mask |= emr_mask; + + tmp_str << + "\n\tM " << + pix_to_x_point( d, apts[0].x, apts[0].y ) << " " << + pix_to_y_point( d, apts[0].x, apts[0].y ) << " "; + + for (i=1; icpts; i++) { + tmp_str << + "\n\tL " << + pix_to_x_point( d, apts[i].x, apts[i].y ) << " " << + pix_to_y_point( d, apts[i].x, apts[i].y ) << " "; + } + + tmp_path << tmp_str.str().c_str(); + + break; + } + case U_EMR_POLYBEZIERTO16: + { + dbg_str << "\n"; + + PU_EMRPOLYBEZIERTO16 pEmr = (PU_EMRPOLYBEZIERTO16) lpEMFR; + PU_POINT16 apts = (PU_POINT16) pEmr->apts; // Bug in MinGW wingdi.h ? + uint32_t i,j; + + d->mask |= emr_mask; + + for (i=0; icpts;) { + tmp_path << "\n\tC "; + for (j=0; j<3 && icpts; j++,i++) { + tmp_path << + pix_to_x_point( d, apts[i].x, apts[i].y ) << " " << + pix_to_y_point( d, apts[i].x, apts[i].y ) << " "; + } + } + + break; + } + case U_EMR_POLYLINETO16: + { + dbg_str << "\n"; + + PU_EMRPOLYLINETO16 pEmr = (PU_EMRPOLYLINETO16) lpEMFR; + PU_POINT16 apts = (PU_POINT16) pEmr->apts; // Bug in MinGW wingdi.h ? + uint32_t i; + + d->mask |= emr_mask; + + for (i=0; icpts;i++) { + tmp_path << + "\n\tL " << + pix_to_x_point( d, apts[i].x, apts[i].y ) << " " << + pix_to_y_point( d, apts[i].x, apts[i].y ) << " "; + } + + break; + } + case U_EMR_POLYPOLYLINE16: + case U_EMR_POLYPOLYGON16: + { + if (lpEMFR->iType == U_EMR_POLYPOLYLINE16) + dbg_str << "\n"; + if (lpEMFR->iType == U_EMR_POLYPOLYGON16) + dbg_str << "\n"; + + PU_EMRPOLYPOLYGON16 pEmr = (PU_EMRPOLYPOLYGON16) lpEMFR; + unsigned int n, i, j; + + d->mask |= emr_mask; + + PU_POINT16 apts = (PU_POINT16) &pEmr->aPolyCounts[pEmr->nPolys]; + + i = 0; + for (n=0; nnPolys && icpts; n++) { + SVGOStringStream poly_path; + + poly_path << "\n\tM " << + pix_to_x_point( d, apts[i].x, apts[i].y ) << " " << + pix_to_y_point( d, apts[i].x, apts[i].y ) << " "; + i++; + + for (j=1; jaPolyCounts[n] && icpts; j++) { + poly_path << "\n\tL " << + pix_to_x_point( d, apts[i].x, apts[i].y ) << " " << + pix_to_y_point( d, apts[i].x, apts[i].y ) << " "; + i++; + } + + tmp_str << poly_path.str().c_str(); + if (lpEMFR->iType == U_EMR_POLYPOLYGON16) + tmp_str << " z"; + tmp_str << " \n"; + } + + tmp_path << tmp_str.str().c_str(); + + break; + } + case U_EMR_POLYDRAW16: dbg_str << "\n"; break; + case U_EMR_CREATEMONOBRUSH: + { + dbg_str << "\n"; + + PU_EMRCREATEMONOBRUSH pEmr = (PU_EMRCREATEMONOBRUSH) lpEMFR; + insert_object(d, pEmr->ihBrush, U_EMR_CREATEMONOBRUSH, lpEMFR); + break; + } + case U_EMR_CREATEDIBPATTERNBRUSHPT: + { + dbg_str << "\n"; + + PU_EMRCREATEDIBPATTERNBRUSHPT pEmr = (PU_EMRCREATEDIBPATTERNBRUSHPT) lpEMFR; + insert_object(d, pEmr->ihBrush, U_EMR_CREATEDIBPATTERNBRUSHPT, lpEMFR); + break; + } + case U_EMR_EXTCREATEPEN: + { + dbg_str << "\n"; + + PU_EMREXTCREATEPEN pEmr = (PU_EMREXTCREATEPEN) lpEMFR; + insert_object(d, pEmr->ihPen, U_EMR_EXTCREATEPEN, lpEMFR); + break; + } + case U_EMR_POLYTEXTOUTA: dbg_str << "\n"; break; + case U_EMR_POLYTEXTOUTW: dbg_str << "\n"; break; + case U_EMR_SETICMMODE: + { + dbg_str << "\n"; +#if 0 + PU_EMRENABLEICM pEmr = (PU_EMRENABLEICM) lpEMFR; + ICMmode= pEmr->iMode; +#endif //0 + break; + } + case U_EMR_CREATECOLORSPACE: dbg_str << "\n"; break; + case U_EMR_SETCOLORSPACE: dbg_str << "\n"; break; + case U_EMR_DELETECOLORSPACE: dbg_str << "\n"; break; + case U_EMR_GLSRECORD: dbg_str << "\n"; break; + case U_EMR_GLSBOUNDEDRECORD: dbg_str << "\n"; break; + case U_EMR_PIXELFORMAT: dbg_str << "\n"; break; + case U_EMR_DRAWESCAPE: dbg_str << "\n"; break; + case U_EMR_EXTESCAPE: dbg_str << "\n"; break; + case U_EMR_UNDEF107: dbg_str << "\n"; break; + // U_EMR_SMALLTEXTOUT is handled with U_EMR_EXTTEXTOUTA/W above + case U_EMR_FORCEUFIMAPPING: dbg_str << "\n"; break; + case U_EMR_NAMEDESCAPE: dbg_str << "\n"; break; + case U_EMR_COLORCORRECTPALETTE: dbg_str << "\n"; break; + case U_EMR_SETICMPROFILEA: dbg_str << "\n"; break; + case U_EMR_SETICMPROFILEW: dbg_str << "\n"; break; + case U_EMR_ALPHABLEND: dbg_str << "\n"; break; + case U_EMR_SETLAYOUT: dbg_str << "\n"; break; + case U_EMR_TRANSPARENTBLT: dbg_str << "\n"; break; + case U_EMR_UNDEF117: dbg_str << "\n"; break; + case U_EMR_GRADIENTFILL: dbg_str << "\n"; break; + /* Gradient fill is doable for rectangles because those correspond to linear gradients. However, + the general case for the triangle fill, with a different color in each corner of the triangle, + has no SVG equivalent and cannot be easily emulated with SVG gradients. Except that so far + I (DM) have not been able to make an EMF with a rectangular gradientfill record which is not + completely toxic to other EMF readers. So far now, do nothing. + */ + case U_EMR_SETLINKEDUFIS: dbg_str << "\n"; break; + case U_EMR_SETTEXTJUSTIFICATION: dbg_str << "\n"; break; + case U_EMR_COLORMATCHTOTARGETW: dbg_str << "\n"; break; + case U_EMR_CREATECOLORSPACEW: dbg_str << "\n"; break; + default: + dbg_str << "\n"; + break; + } //end of switch +// When testing, uncomment the following to place a comment for each processed EMR record in the SVG +// *(d->outsvg) += dbg_str.str().c_str(); + *(d->outsvg) += tmp_outsvg.str().c_str(); + *(d->path) += tmp_path.str().c_str(); + + } //end of while +// When testing, uncomment the following to show the final SVG derived from the EMF +// std::cout << *(d->outsvg) << std::endl; + + return 1; +} + + +// Aldus Placeable Header =================================================== +// Since we are a 32bit app, we have to be sure this structure compiles to +// be identical to a 16 bit app's version. To do this, we use the #pragma +// to adjust packing, we use a uint16_t for the hmf handle, and a SMALL_RECT +// for the bbox rectangle. +#pragma pack( push ) +#pragma pack( 2 ) +typedef struct _SMALL_RECT { + int16_t Left; + int16_t Top; + int16_t Right; + int16_t Bottom; +} SMALL_RECT, *PSMALL_RECT; +typedef struct +{ + uint32_t dwKey; + uint16_t hmf; + SMALL_RECT bbox; + uint16_t wInch; + uint32_t dwReserved; + uint16_t wCheckSum; +} APMHEADER, *PAPMHEADER; +#pragma pack( pop ) + + +SPDocument * +Emf::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri ) +{ + EMF_CALLBACK_DATA d; + + memset(&d, 0, sizeof(d)); + + d.dc[0].worldTransform.eM11 = 1.0; + d.dc[0].worldTransform.eM12 = 0.0; + d.dc[0].worldTransform.eM21 = 0.0; + d.dc[0].worldTransform.eM22 = 1.0; + d.dc[0].worldTransform.eDx = 0.0; + d.dc[0].worldTransform.eDy = 0.0; + + if (uri == NULL) { + return NULL; + } + + d.outsvg = new Glib::ustring(""); + d.path = new Glib::ustring(""); + d.outdef = new Glib::ustring(""); + d.defs = new Glib::ustring(""); + d.mask = 0; + d.drawtype = 0; + d.arcdir = U_AD_COUNTERCLOCKWISE; + d.dwRop2 = U_R2_COPYPEN; + d.dwRop3 = 0; + d.hatches.size = 0; + d.hatches.count = 0; + d.hatches.strings = NULL; + d.images.size = 0; + d.images.count = 0; + d.images.strings = NULL; + + size_t length; + char *contents; + if(emf_readdata(uri, &contents, &length))return(NULL); + + d.pDesc = NULL; + + + (void) myEnhMetaFileProc(contents,length, &d); + free(contents); + + + if (d.pDesc) + free( d.pDesc ); + +// std::cout << "SVG Output: " << std::endl << *(d.outsvg) << std::endl; + + 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.hatches.count){ free(d.hatches.strings); } + if(d.images.count){ free(d.images.strings); } + + if (d.emf_obj) { + int i; + for (i=0; i\n" + "" N_("EMF Input") "\n" + "org.inkscape.input.emf\n" + "\n" + ".emf\n" + "image/x-emf\n" + "" N_("Enhanced Metafiles (*.emf)") "\n" + "" N_("Enhanced Metafiles") "\n" + "org.inkscape.output.emf\n" + "\n" + "", new Emf()); + + /* EMF out */ + Inkscape::Extension::build_from_mem( + "\n" + "" N_("EMF Output") "\n" + "org.inkscape.output.emf\n" + "true\n" + "true\n" + "true\n" + "true\n" + "false\n" + "false\n" + "false\n" + "false\n" + "false\n" + "\n" + ".emf\n" + "image/x-emf\n" + "" N_("Enhanced Metafile (*.emf)") "\n" + "" N_("Enhanced Metafile") "\n" + "\n" + "", new Emf()); + + return; +} + + +} } } /* namespace Inkscape, Extension, Implementation */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : -- cgit v1.2.3 From a5df19880fa6819861053ba8e351af9e8df4b796 Mon Sep 17 00:00:00 2001 From: su_v Date: Sun, 23 Sep 2012 19:26:54 +0200 Subject: Fix pointer issue in EMF code (bzr r11668.1.11) --- src/extension/internal/emf-inout.cpp | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) (limited to 'src/extension/internal/emf-inout.cpp') diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp index 4b2767313..9fef38333 100644 --- a/src/extension/internal/emf-inout.cpp +++ b/src/extension/internal/emf-inout.cpp @@ -363,7 +363,7 @@ typedef struct { typedef struct emf_device_context { struct SPStyle style; - class SPTextStyle tstyle; + char *font_name; 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 @@ -390,6 +390,7 @@ typedef struct emf_device_context { #define EMF_MAX_DC 128 + typedef struct emf_callback_data { Glib::ustring *outsvg; Glib::ustring *path; @@ -1320,8 +1321,8 @@ select_font(PEMF_CALLBACK_DATA d, int index) 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.text_decoration.underline = pEmr->elfw.elfLogFont.lfUnderline; d->dc[d->level].style.text_decoration.line_through = pEmr->elfw.elfLogFont.lfStrikeOut; - if (d->dc[d->level].tstyle.font_family.value){ free(d->dc[d->level].tstyle.font_family.value); } - d->dc[d->level].tstyle.font_family.value = + if (d->dc[d->level].font_name){ free(d->dc[d->level].font_name); } + d->dc[d->level].font_name = U_Utf16leToUtf8((uint16_t *) (pEmr->elfw.elfLogFont.lfFaceName), U_LF_FACESIZE, NULL); 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 } @@ -1384,7 +1385,8 @@ int myEnhMetaFileProc(char *contents, unsigned int length, PEMF_CALLBACK_DATA d) if(off>=length)return(0); //normally should exit from while after EMREOF sets OK to false. lpEMFR = (PU_ENHMETARECORD)(contents + off); -// std::cout << "record type: " << lpEMFR->iType << " length: " << lpEMFR->nSize << "offset: " << off <iType << " length: " << lpEMFR->nSize << " offset: " << off <nSize; SVGOStringStream tmp_outsvg; @@ -1888,6 +1890,9 @@ 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->level = d->level + 1; } break; @@ -1906,8 +1911,13 @@ std::cout << "BEFORE DRAW" d->level = d->level + pEmr->iRelative; } while (old_level > d->level) { - 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))) + 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))){ delete[] d->dc[old_level].style.stroke_dash.dash; + } + if(d->dc[old_level].font_name){ + free(d->dc[old_level].font_name); // else memory leak + d->dc[old_level].font_name = NULL; + } old_level--; } break; @@ -2653,9 +2663,9 @@ std::cout << "BEFORE DRAW" msdepua(dup_wt); //convert everything in Microsoft's private use area. For Symbol, Wingdings, Dingbats - if(NonToUnicode(dup_wt, d->dc[d->level].tstyle.font_family.value)){ - g_free(d->dc[d->level].tstyle.font_family.value); - d->dc[d->level].tstyle.font_family.value = g_strdup("Times New Roman"); + if(NonToUnicode(dup_wt, d->dc[d->level].font_name)){ + g_free(d->dc[d->level].font_name); + d->dc[d->level].font_name = g_strdup("Times New Roman"); } char *ansi_text; @@ -2718,7 +2728,7 @@ std::cout << "BEFORE DRAW" << "font-weight:" << (b ? "bold" : "normal") << ";" << "text-align:" << (lcr==2 ? "center" : lcr==1 ? "end" : "start") << ";" << "text-anchor:" << (lcr==2 ? "middle" : lcr==1 ? "end" : "start") << ";" - << "font-family:" << d->dc[d->level].tstyle.font_family.value << ";" + << "font-family:" << d->dc[d->level].font_name << ";" << "\"\n"; ts << " >"; ts << escaped_text; @@ -3017,6 +3027,10 @@ Emf::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri ) memset(&d, 0, sizeof(d)); + for(int i = 0; i < EMF_MAX_DC+1; i++){ // be sure all values and pointers are empty to start with + memset(&(d.dc[i]),0,sizeof(EMF_DEVICE_CONTEXT)); + } + d.dc[0].worldTransform.eM11 = 1.0; d.dc[0].worldTransform.eM12 = 0.0; d.dc[0].worldTransform.eM21 = 0.0; @@ -3078,6 +3092,10 @@ Emf::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri ) if (d.dc[0].style.stroke_dash.dash) delete[] d.dc[0].style.stroke_dash.dash; + + for(int i=0; i<=d.level;i++){ + if(d.dc[i].font_name)free(d.dc[i].font_name); + } return doc; } -- cgit v1.2.3 From db010468005eb279d242a698ed5b89b05bc0d817 Mon Sep 17 00:00:00 2001 From: David Mathog <> Date: Thu, 27 Sep 2012 08:40:44 +0200 Subject: fix compiler warnings with old GCC on Mac OS X and Solaris (bzr r11668.1.16) --- src/extension/internal/emf-inout.cpp | 42 +++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 17 deletions(-) (limited to 'src/extension/internal/emf-inout.cpp') diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp index 9fef38333..44f7d0465 100644 --- a/src/extension/internal/emf-inout.cpp +++ b/src/extension/internal/emf-inout.cpp @@ -138,7 +138,7 @@ my_png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) p->size += length; } -void toPNG(PMEMPNG accum, int width, int height, char *px, uint32_t cbPx){ +void toPNG(PMEMPNG accum, int width, int height, char *px){ bitmap_t bmstore; bitmap_t *bitmap=&bmstore; accum->buffer=NULL; // PNG constructed in memory will end up here, caller must free(). @@ -650,8 +650,7 @@ uint32_t add_image(PEMF_CALLBACK_DATA d, void *pEmr, uint32_t cbBits, uint32_t toPNG( // Get the image from the RGBA px into mempng &mempng, width, height, - rgba_px, - 4 * width * height); + rgba_px); free(rgba_px); } } @@ -1462,7 +1461,8 @@ std::cout << "BEFORE DRAW" tmp_outdef << "xDPI = 2540; @@ -2557,8 +2557,7 @@ std::cout << "BEFORE DRAW" toPNG( // Get the image from the RGBA px into mempng &mempng, width, height, - rgba_px, - 4 * width * height); + rgba_px); free(rgba_px); } } @@ -2710,18 +2709,20 @@ std::cout << "BEFORE DRAW" // 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; - ts << " id++) << "\"\n"; - ts << " xml:space=\"preserve\"\n"; - ts << " x=\"" << x << "\"\n"; - ts << " y=\"" << y << "\"\n"; + ts << "dc[d->level].style.baseline_shift.value) { - ts << " transform=\"" + ts << " transform=\"" << "rotate(-" << d->dc[d->level].style.baseline_shift.value << " " << x << " " << y << ")" << "\"\n"; } - ts << " style=\"" + ts << ">dc[d->level].style.font_size.computed) << "px;" << tmp << "font-style:" << (i ? "italic" : "normal") << ";" @@ -2732,6 +2733,7 @@ std::cout << "BEFORE DRAW" << "\"\n"; ts << " >"; ts << escaped_text; + ts << " "; ts << "\n"; *(d->outsvg) += ts.str().c_str(); @@ -2989,7 +2991,7 @@ std::cout << "BEFORE DRAW" } //end of while // When testing, uncomment the following to show the final SVG derived from the EMF -// std::cout << *(d->outsvg) << std::endl; +std::cout << *(d->outsvg) << std::endl; return 1; } @@ -3019,6 +3021,12 @@ typedef struct } APMHEADER, *PAPMHEADER; #pragma pack( pop ) +void free_emf_strings(EMF_STRINGS name){ + if(name.count){ + for(int i=0; i< name.count; i++){ free(name.strings[i]); } + free(name.strings); + } +} SPDocument * Emf::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri ) @@ -3080,9 +3088,9 @@ Emf::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri ) delete d.path; delete d.outdef; delete d.defs; - if(d.hatches.count){ free(d.hatches.strings); } - if(d.images.count){ free(d.images.strings); } - + free_emf_strings(d.hatches); + free_emf_strings(d.images); + if (d.emf_obj) { int i; for (i=0; i Date: Thu, 4 Oct 2012 10:04:48 +0200 Subject: update based on patch 'changes_2012_09_27b.patch' (bzr r11668.1.21) --- src/extension/internal/emf-inout.cpp | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) (limited to 'src/extension/internal/emf-inout.cpp') diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp index 44f7d0465..2f682d5da 100644 --- a/src/extension/internal/emf-inout.cpp +++ b/src/extension/internal/emf-inout.cpp @@ -62,7 +62,7 @@ namespace Internal { static float device_scale = DEVICESCALE; static U_RECTL rc_old; static bool clipset = false; -static uint32_t ICMmode=0; +static uint32_t ICMmode=0; // not used yet, but code to read it from EMF implemented static uint32_t BLTmode=0; /** Construct a PNG in memory from an RGB from the EMF file @@ -120,8 +120,8 @@ static pixel_t * pixel_at (bitmap_t * bitmap, int x, int y) void my_png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) { - /* with libpng15 next line causes pointer deference error; use libpng12 */ - PMEMPNG p=(PMEMPNG)png_ptr->io_ptr; + PMEMPNG p=(PMEMPNG)png_get_io_ptr(png_ptr); + size_t nsize = p->size + length; /* allocate or grow buffer */ @@ -221,16 +221,6 @@ void toPNG(PMEMPNG accum, int width, int height, char *px){ } -/* Given "value" and "max", the maximum value which we expect "value" - to take, this returns an integer between 0 and 255 proportional to - "value" divided by "max". */ - -static int pix (int value, int max) -{ - if (value < 0) - return 0; - return (int) (256.0 *((double) (value)/(double) max)); -} /* convert an EMF RGB(A) color to 0RGB inverse of gethexcolor() in emf-print.cpp @@ -2944,10 +2934,8 @@ std::cout << "BEFORE DRAW" case U_EMR_SETICMMODE: { dbg_str << "\n"; -#if 0 - PU_EMRENABLEICM pEmr = (PU_EMRENABLEICM) lpEMFR; + PU_EMRSETICMMODE pEmr = (PU_EMRSETICMMODE) lpEMFR; ICMmode= pEmr->iMode; -#endif //0 break; } case U_EMR_CREATECOLORSPACE: dbg_str << "\n"; break; @@ -2991,7 +2979,7 @@ std::cout << "BEFORE DRAW" } //end of while // When testing, uncomment the following to show the final SVG derived from the EMF -std::cout << *(d->outsvg) << std::endl; +// std::cout << *(d->outsvg) << std::endl; return 1; } -- cgit v1.2.3 From 2016ceee470cdebba2aa48f963369e53602f3211 Mon Sep 17 00:00:00 2001 From: David Mathog <> Date: Fri, 5 Oct 2012 01:44:26 +0200 Subject: changes_2012_10_04b.patch: fixes one small memory issue (bytes allocated and never deallocated, not a leak per se) (bzr r11668.1.24) --- src/extension/internal/emf-inout.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/extension/internal/emf-inout.cpp') diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp index 2f682d5da..550f05cf3 100644 --- a/src/extension/internal/emf-inout.cpp +++ b/src/extension/internal/emf-inout.cpp @@ -1383,7 +1383,8 @@ int myEnhMetaFileProc(char *contents, unsigned int length, PEMF_CALLBACK_DATA d) SVGOStringStream tmp_str; SVGOStringStream dbg_str; - emr_mask = emr_properties(lpEMFR->iType); + emr_mask = emr_properties(lpEMFR->iType); + if(emr_mask == U_EMR_INVALID){ throw "Inkscape fatal memory allocation error - cannot continue"; } // std::cout << "BEFORE DRAW logic d->mask: " << std::hex << d->mask << " emr_mask: " << emr_mask << std::dec << std::endl; /* @@ -2980,6 +2981,7 @@ std::cout << "BEFORE DRAW" } //end of while // When testing, uncomment the following to show the final SVG derived from the EMF // std::cout << *(d->outsvg) << std::endl; + (void) emr_properties(U_EMR_INVALID); // force the release of the lookup table memory, returned value is irrelevant return 1; } -- cgit v1.2.3 From 3e19ae183993f58ae665839f556a02cde4814396 Mon Sep 17 00:00:00 2001 From: David Mathog <> Date: Tue, 9 Oct 2012 01:22:53 +0200 Subject: changes_2012_10_08a.patch: - image import from EMR_BITBLT, EMR_STRETCHBLT, and EMR_MASKBLT records - sets a default font name (Arial) (for EMFs which specify for a font) - drops text that starts with any of the characters 0x0-0x1F (bzr r11668.1.26) --- src/extension/internal/emf-inout.cpp | 208 ++++++++++++++++++++++------------- 1 file changed, 130 insertions(+), 78 deletions(-) (limited to 'src/extension/internal/emf-inout.cpp') diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp index 550f05cf3..e1b20ee23 100644 --- a/src/extension/internal/emf-inout.cpp +++ b/src/extension/internal/emf-inout.cpp @@ -1310,9 +1310,18 @@ select_font(PEMF_CALLBACK_DATA d, int index) 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.text_decoration.underline = pEmr->elfw.elfLogFont.lfUnderline; d->dc[d->level].style.text_decoration.line_through = pEmr->elfw.elfLogFont.lfStrikeOut; - if (d->dc[d->level].font_name){ free(d->dc[d->level].font_name); } - d->dc[d->level].font_name = - U_Utf16leToUtf8((uint16_t *) (pEmr->elfw.elfLogFont.lfFaceName), U_LF_FACESIZE, NULL); + // malformed EMF with empty filename may exist, ignore font change if encountered + char *ctmp = U_Utf16leToUtf8((uint16_t *) (pEmr->elfw.elfLogFont.lfFaceName), U_LF_FACESIZE, NULL); + if(ctmp){ + if (d->dc[d->level].font_name){ free(d->dc[d->level].font_name); } + if(*ctmp){ + d->dc[d->level].font_name = ctmp; + } + else { // Malformed EMF might specify an empty font name + free(ctmp); + d->dc[d->level].font_name = strdup("Arial"); // Default font, EMF spec says device can pick whatever it wants + } + } 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 } @@ -1355,6 +1364,76 @@ uint32_t *unknown_chars(size_t count){ return res; } +void common_image_extraction(PEMF_CALLBACK_DATA d, void *pEmr, double l, double t, double r, double b, + uint32_t iUsage, uint32_t offBits, uint32_t cbBits, uint32_t offBmi, uint32_t cbBmi){ + SVGOStringStream tmp_image; + tmp_image << " y=\"" << t << "\"\n x=\"" << l <<"\"\n "; + + // The image ID is filled in much later when tmp_image is converted + + tmp_image << " xlink:href=\"data:image/png;base64,"; + + MEMPNG mempng; // PNG in memory comes back in this + mempng.buffer = NULL; + + char *rgba_px=NULL; // RGBA pixels + char *px=NULL; // DIB pixels + uint32_t width, height, colortype, numCt, invert; + PU_RGBQUAD ct = NULL; + if(!cbBits || + !cbBmi || + (iUsage != U_DIB_RGB_COLORS) || + !get_DIB_params( // this returns pointers and values, but allocates no memory + pEmr, + offBits, + offBmi, + &px, + &ct, + &numCt, + &width, + &height, + &colortype, + &invert + )){ + + if(!DIB_to_RGBA( + px, // DIB pixel array + ct, // DIB color table + numCt, // DIB color table number of entries + &rgba_px, // U_RGBA pixel array (32 bits), created by this routine, caller must free. + width, // Width of pixel array + height, // Height of pixel array + colortype, // DIB BitCount Enumeration + numCt, // Color table used if not 0 + invert // If DIB rows are in opposite order from RGBA rows + ) && + rgba_px) + { + toPNG( // Get the image from the RGBA px into mempng + &mempng, + width, height, + rgba_px); + free(rgba_px); + } + } + if(mempng.buffer){ + gchar *base64String = g_base64_encode((guchar*) mempng.buffer, mempng.size ); + free(mempng.buffer); + tmp_image << base64String ; + g_free(base64String); + } + else { + // insert a random 3x4 blotch otherwise + tmp_image << "iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAAA3NCSVQICAjb4U/gAAAALElEQVQImQXBQQ2AMAAAsUJQMSWI2H8qME1yMshojwrvGB8XcHKvR1XtOTc/8HENumHCsOMAAAAASUVORK5CYII="; + } + + tmp_image << "\"\n height=\"" << b-t+1 << "\"\n width=\"" << r-l+1 << "\"\n"; + + *(d->outsvg) += "\n\t outsvg) += tmp_image.str().c_str(); + *(d->outsvg) += "/> \n"; + *(d->path) = ""; +} /** \fn myEnhMetaFileProc(char *contents, unsigned int length, PEMF_CALLBACK_DATA lpData) @@ -2461,14 +2540,14 @@ std::cout << "BEFORE DRAW" dbg_str << "\n"; PU_EMRBITBLT pEmr = (PU_EMRBITBLT) lpEMFR; + double l = pix_to_x_point( d, pEmr->Dest.x, pEmr->Dest.y); + double t = pix_to_y_point( d, pEmr->Dest.x, pEmr->Dest.y); + double r = pix_to_x_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y); + double b = pix_to_y_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y); // Treat all nonImage bitblts as a rectangular write. Definitely not correct, but at // least it leaves objects where the operations should have been. if (!pEmr->cbBmiSrc) { // should be an application of a DIBPATTERNBRUSHPT, use a solid color instead - double l = pix_to_x_point( d, pEmr->Dest.x, pEmr->Dest.y); - double t = pix_to_y_point( d, pEmr->Dest.x, pEmr->Dest.y); - double r = pix_to_x_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y); - double b = pix_to_y_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y); SVGOStringStream tmp_rectangle; tmp_rectangle << "\n\tM " << l << " " << t << " "; @@ -2483,10 +2562,42 @@ std::cout << "BEFORE DRAW" tmp_path << tmp_rectangle.str().c_str(); } + else { + common_image_extraction(d,pEmr,l,t,r,b, + pEmr->iUsageSrc, pEmr->offBitsSrc, pEmr->cbBitsSrc, pEmr->offBmiSrc, pEmr->cbBmiSrc); + } + break; + } + case U_EMR_STRETCHBLT: + { + dbg_str << "\n"; + PU_EMRSTRETCHBLT pEmr = (PU_EMRSTRETCHBLT) lpEMFR; + // Always grab image, ignore modes. + if (pEmr->cbBmiSrc) { + double l = pix_to_x_point( d, pEmr->Dest.x, pEmr->Dest.y); + double t = pix_to_y_point( d, pEmr->Dest.x, pEmr->Dest.y); + double r = pix_to_x_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y); + double b = pix_to_y_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y); + common_image_extraction(d,pEmr,l,t,r,b, + pEmr->iUsageSrc, pEmr->offBitsSrc, pEmr->cbBitsSrc, pEmr->offBmiSrc, pEmr->cbBmiSrc); + } + break; + } + case U_EMR_MASKBLT: + { + dbg_str << "\n"; + PU_EMRMASKBLT pEmr = (PU_EMRMASKBLT) lpEMFR; + // Always grab image, ignore masks and modes. + if (pEmr->cbBmiSrc) { + double l = pix_to_x_point( d, pEmr->Dest.x, pEmr->Dest.y); + double t = pix_to_y_point( d, pEmr->Dest.x, pEmr->Dest.y); + double r = pix_to_x_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y); + double b = pix_to_y_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y); + common_image_extraction(d,pEmr,l,t,r,b, + pEmr->iUsageSrc, pEmr->offBitsSrc, pEmr->cbBitsSrc, pEmr->offBmiSrc, pEmr->cbBmiSrc); + } break; } - case U_EMR_STRETCHBLT: dbg_str << "\n"; break; - case U_EMR_MASKBLT: dbg_str << "\n"; break; case U_EMR_PLGBLT: dbg_str << "\n"; break; case U_EMR_SETDIBITSTODEVICE: dbg_str << "\n"; break; case U_EMR_STRETCHDIBITS: @@ -2502,73 +2613,8 @@ std::cout << "BEFORE DRAW" double t = pix_to_y_point( d, pEmr->Dest.x, pEmr->Dest.y ); double r = pix_to_x_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y ); double b = pix_to_y_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y ); - SVGOStringStream tmp_image; - tmp_image << " y=\"" << t << "\"\n x=\"" << l <<"\"\n "; - - // The image ID is filled in much later when tmp_image is converted - - tmp_image << " xlink:href=\"data:image/png;base64,"; - - MEMPNG mempng; // PNG in memory comes back in this - mempng.buffer = NULL; - - char *rgba_px=NULL; // RGBA pixels - char *px=NULL; // DIB pixels - uint32_t width, height, colortype, numCt, invert; - PU_RGBQUAD ct = NULL; - if(!pEmr->cbBitsSrc || - !pEmr->cbBmiSrc || - (pEmr->iUsageSrc != U_DIB_RGB_COLORS) || - !get_DIB_params( // this returns pointers and values, but allocates no memory - pEmr, - pEmr->offBitsSrc, - pEmr->offBmiSrc, - &px, - &ct, - &numCt, - &width, - &height, - &colortype, - &invert - )){ - - if(!DIB_to_RGBA( - px, // DIB pixel array - ct, // DIB color table - numCt, // DIB color table number of entries - &rgba_px, // U_RGBA pixel array (32 bits), created by this routine, caller must free. - width, // Width of pixel array - height, // Height of pixel array - colortype, // DIB BitCount Enumeration - numCt, // Color table used if not 0 - invert // If DIB rows are in opposite order from RGBA rows - ) && - rgba_px) - { - toPNG( // Get the image from the RGBA px into mempng - &mempng, - width, height, - rgba_px); - free(rgba_px); - } - } - if(mempng.buffer){ - gchar *base64String = g_base64_encode((guchar*) mempng.buffer, mempng.size ); - free(mempng.buffer); - tmp_image << base64String ; - g_free(base64String); - } - else { - // insert a random 3x4 blotch otherwise - tmp_image << "iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAAA3NCSVQICAjb4U/gAAAALElEQVQImQXBQQ2AMAAAsUJQMSWI2H8qME1yMshojwrvGB8XcHKvR1XtOTc/8HENumHCsOMAAAAASUVORK5CYII="; - } - - tmp_image << "\"\n height=\"" << b-t+1 << "\"\n width=\"" << r-l+1 << "\"\n"; - - *(d->outsvg) += "\n\t outsvg) += tmp_image.str().c_str(); - *(d->outsvg) += "/> \n"; - *(d->path) = ""; + common_image_extraction(d,pEmr,l,t,r,b, + pEmr->iUsageSrc, pEmr->offBitsSrc, pEmr->cbBitsSrc, pEmr->offBmiSrc, pEmr->cbBmiSrc); dbg_str << "\n"; break; @@ -2661,6 +2707,11 @@ std::cout << "BEFORE DRAW" char *ansi_text; ansi_text = (char *) U_Utf32leToUtf8((uint32_t *)dup_wt, 0, NULL); free(dup_wt); + // Empty string or starts with an invalid escape/control sequence, which is bogus text. Throw it out before g_markup_escape_text can make things worse + if(*ansi_text <= 0x1F){ + free(ansi_text); + ansi_text=NULL; + } if (ansi_text) { // gchar *p = ansi_text; @@ -2980,7 +3031,7 @@ std::cout << "BEFORE DRAW" } //end of while // When testing, uncomment the following to show the final SVG derived from the EMF -// std::cout << *(d->outsvg) << std::endl; +//std::cout << *(d->outsvg) << std::endl; (void) emr_properties(U_EMR_INVALID); // force the release of the lookup table memory, returned value is irrelevant return 1; @@ -3035,7 +3086,8 @@ Emf::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri ) d.dc[0].worldTransform.eM22 = 1.0; d.dc[0].worldTransform.eDx = 0.0; d.dc[0].worldTransform.eDy = 0.0; - + d.dc[0].font_name = strdup("Arial"); // Default font, EMF spec says device can pick whatever it wants + if (uri == NULL) { return NULL; } -- cgit v1.2.3 From 4584fa234ef3d7fbc42532031fb5eb7e26405836 Mon Sep 17 00:00:00 2001 From: David Mathog <> Date: Sat, 13 Oct 2012 01:00:15 +0200 Subject: Fix SVG formatting (EMF import): A typo caused two extra spaces to be appended on the end of any text which was read in from an EMF file. (bzr r11668.1.29) --- src/extension/internal/emf-inout.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/extension/internal/emf-inout.cpp') diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp index e1b20ee23..295bbd5ea 100644 --- a/src/extension/internal/emf-inout.cpp +++ b/src/extension/internal/emf-inout.cpp @@ -2775,7 +2775,7 @@ std::cout << "BEFORE DRAW" << "\"\n"; ts << " >"; ts << escaped_text; - ts << " "; + ts << ""; ts << "\n"; *(d->outsvg) += ts.str().c_str(); -- cgit v1.2.3 From 647afe1aa1b2da82f39a70868c370997c22bb696 Mon Sep 17 00:00:00 2001 From: David Mathog <> Date: Thu, 18 Oct 2012 20:20:34 +0200 Subject: changes_2012_10_18b.patch: Additional fix for text that starts with any of the characters 0x0-0x1F cast the tested character to unsigned to prevent dropping of some strings (follow-up to r11984) (bzr r11668.1.31) --- src/extension/internal/emf-inout.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/extension/internal/emf-inout.cpp') diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp index 295bbd5ea..8be4e998e 100644 --- a/src/extension/internal/emf-inout.cpp +++ b/src/extension/internal/emf-inout.cpp @@ -2708,7 +2708,7 @@ std::cout << "BEFORE DRAW" ansi_text = (char *) U_Utf32leToUtf8((uint32_t *)dup_wt, 0, NULL); free(dup_wt); // Empty string or starts with an invalid escape/control sequence, which is bogus text. Throw it out before g_markup_escape_text can make things worse - if(*ansi_text <= 0x1F){ + if(*((uint8_t *)ansi_text) <= 0x1F){ free(ansi_text); ansi_text=NULL; } -- cgit v1.2.3 From 8fae5ea0ef11d8a447d0a7e93e045cc2d67b2cc7 Mon Sep 17 00:00:00 2001 From: David Mathog <> Date: Thu, 25 Oct 2012 10:08:38 +0200 Subject: changes_2012_10_22b.patch, changes_2012_10_24a.patch EMF import (Adobe Illustrator EMF files): - workaround for issue with page scaling ('MM_ANISOTROPIC', wrong units) - fix SETWORLDTRANSFORM operation - fix libUEMF to support older/shorter EMF header forms EMF import (general): - fix import of shapes (rectangles) without borders - handle EMF bitmap modes where a subsection of the image is extracted EMF export/import: - increased size in mm of the reference device by 100X on EMF export (significant when the dpi is calculated on reading the EMF back in) - changed dpi calculation: (sum of pixels ref device)/(sum of millimeter ref device) (bzr r11668.1.34) --- src/extension/internal/emf-inout.cpp | 356 ++++++++++++++++++++++++----------- 1 file changed, 251 insertions(+), 105 deletions(-) (limited to 'src/extension/internal/emf-inout.cpp') diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp index 8be4e998e..acc3443d5 100644 --- a/src/extension/internal/emf-inout.cpp +++ b/src/extension/internal/emf-inout.cpp @@ -390,17 +390,16 @@ typedef struct emf_callback_data { EMF_DEVICE_CONTEXT dc[EMF_MAX_DC+1]; // FIXME: This should be dynamic.. int level; - double xDPI, yDPI; - uint32_t mask; // Draw properties - int arcdir; //U_AD_COUNTERCLOCKWISE 1 or U_AD_CLOCKWISE 2 + double ulCornerX,ulCornerY; // Upper left corner, from header rclBounds, in logical units + double ydir; // 1.0 if y is positive DOWN (usual case), -1.0 if y is negative DOWN + uint32_t mask; // Draw properties + int arcdir; //U_AD_COUNTERCLOCKWISE 1 or U_AD_CLOCKWISE 2 - uint32_t dwRop2; // Binary raster operation, 0 if none (use brush/pen unmolested) - uint32_t dwRop3; // Ternary raster operation, 0 if none (use brush/pen unmolested) + uint32_t dwRop2; // Binary raster operation, 0 if none (use brush/pen unmolested) + uint32_t dwRop3; // Ternary raster operation, 0 if none (use brush/pen unmolested) float MMX; float MMY; - float dwInchesX; - float dwInchesY; unsigned int id; unsigned int drawtype; // one of 0 or U_EMR_FILLPATH, U_EMR_STROKEPATH, U_EMR_STROKEANDFILLPATH @@ -629,17 +628,20 @@ uint32_t add_image(PEMF_CALLBACK_DATA d, void *pEmr, uint32_t cbBits, uint32_t ct, // DIB color table numCt, // DIB color table number of entries &rgba_px, // U_RGBA pixel array (32 bits), created by this routine, caller must free. - width, // Width of pixel array - height, // Height of pixel array + width, // Width of pixel array in record + height, // Height of pixel array in record colortype, // DIB BitCount Enumeration numCt, // Color table used if not 0 - invert // If DIB rows are in opposite order from RGBA rows + invert, // If DIB rows are in opposite order from RGBA rows + 0,0, // start position in pixel array in record + width, // Width of extracted pixel array + height // Height of extracted pixel array ) && rgba_px) { toPNG( // Get the image from the RGBA px into mempng &mempng, - width, height, + width, height, // of the SRC bitmap rgba_px); free(rgba_px); } @@ -888,17 +890,23 @@ output_style(PEMF_CALLBACK_DATA d, int iType) static double _pix_x_to_point(PEMF_CALLBACK_DATA d, double px) { - double tmp = px - d->dc[d->level].winorg.x; - tmp *= d->dc[d->level].ScaleInX ? d->dc[d->level].ScaleInX : 1.0; + double scale = (d->dc[d->level].ScaleInX ? d->dc[d->level].ScaleInX : 1.0); + double tmp = px; + tmp -= d->dc[d->level].winorg.x; + tmp *= scale; + tmp -= d->ulCornerX; tmp += d->dc[d->level].vieworg.x; return tmp; } static double -_pix_y_to_point(PEMF_CALLBACK_DATA d, double px) +_pix_y_to_point(PEMF_CALLBACK_DATA d, double py) { - double tmp = px - d->dc[d->level].winorg.y; - tmp *= d->dc[d->level].ScaleInY ? d->dc[d->level].ScaleInY : 1.0; + double scale = (d->dc[d->level].ScaleInY ? d->dc[d->level].ScaleInY : 1.0); + double tmp = py; + tmp -= d->dc[d->level].winorg.y; + tmp *= scale; + tmp += d->ydir*d->ulCornerY; tmp += d->dc[d->level].vieworg.y; return tmp; } @@ -907,10 +915,13 @@ _pix_y_to_point(PEMF_CALLBACK_DATA d, double px) static double pix_to_x_point(PEMF_CALLBACK_DATA d, double px, double py) { + double wpx = px * d->dc[d->level].worldTransform.eM11 + py * d->dc[d->level].worldTransform.eM21 + d->dc[d->level].worldTransform.eDx; + double x = _pix_x_to_point(d, wpx); +/* double ppx = _pix_x_to_point(d, px); double ppy = _pix_y_to_point(d, py); - 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; return x; @@ -919,13 +930,18 @@ pix_to_x_point(PEMF_CALLBACK_DATA d, double px, double py) static double pix_to_y_point(PEMF_CALLBACK_DATA d, double px, double py) { + + double wpy = px * d->dc[d->level].worldTransform.eM12 + py * d->dc[d->level].worldTransform.eM22 + d->dc[d->level].worldTransform.eDy; + double y = _pix_y_to_point(d, wpy); +/* double ppx = _pix_x_to_point(d, px); double ppy = _pix_y_to_point(d, py); - 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; return y; + } static double @@ -1123,8 +1139,13 @@ select_extpen(PEMF_CALLBACK_DATA d, int index) d->dc[d->level].style.stroke_dasharray_set = 1; break; } - case U_PS_SOLID: +/* includes these for now, some should maybe not be in here + case U_PS_NULL: + case U_PS_INSIDEFRAME: + case U_PS_ALTERNATE: + case U_PS_STYLE_MASK: +*/ default: { d->dc[d->level].style.stroke_dasharray_set = 0; @@ -1172,51 +1193,60 @@ select_extpen(PEMF_CALLBACK_DATA d, int index) d->dc[d->level].stroke_set = true; - if (pEmr->elp.elpPenStyle == U_PS_NULL) { + if (pEmr->elp.elpPenStyle == U_PS_NULL) { // draw nothing, but fill out all the values with something + double r, g, b; + r = SP_COLOR_U_TO_F( U_RGBAGetR(d->dc[d->level].textColor)); + g = SP_COLOR_U_TO_F( U_RGBAGetG(d->dc[d->level].textColor)); + b = SP_COLOR_U_TO_F( U_RGBAGetB(d->dc[d->level].textColor)); + d->dc[d->level].style.stroke.value.color.set( r, g, b ); d->dc[d->level].style.stroke_width.value = 0; d->dc[d->level].stroke_set = false; - } else if (pEmr->elp.elpWidth) { - int cur_level = d->level; - d->level = d->emf_obj[index].level; - double pen_width = pix_to_size_point( d, pEmr->elp.elpWidth ); - d->level = cur_level; - d->dc[d->level].style.stroke_width.value = pen_width; - } 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; - int cur_level = d->level; - d->level = d->emf_obj[index].level; - double pen_width = pix_to_size_point( d, 1 ); - d->level = cur_level; - d->dc[d->level].style.stroke_width.value = pen_width; + d->dc[d->level].stroke_mode = DRAW_PAINT; } + else { + if (pEmr->elp.elpWidth) { + int cur_level = d->level; + d->level = d->emf_obj[index].level; + double pen_width = pix_to_size_point( d, pEmr->elp.elpWidth ); + d->level = cur_level; + d->dc[d->level].style.stroke_width.value = pen_width; + } 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; + int cur_level = d->level; + d->level = d->emf_obj[index].level; + double pen_width = pix_to_size_point( d, 1 ); + d->level = cur_level; + d->dc[d->level].style.stroke_width.value = pen_width; + } - if( pEmr->elp.elpBrushStyle == U_BS_SOLID){ - double r, g, b; - r = SP_COLOR_U_TO_F( U_RGBAGetR(pEmr->elp.elpColor) ); - g = SP_COLOR_U_TO_F( U_RGBAGetG(pEmr->elp.elpColor) ); - b = SP_COLOR_U_TO_F( U_RGBAGetB(pEmr->elp.elpColor) ); - d->dc[d->level].style.stroke.value.color.set( r, g, b ); - d->dc[d->level].stroke_mode = DRAW_PAINT; - d->dc[d->level].stroke_set = true; - } - else if(pEmr->elp.elpBrushStyle == U_BS_HATCHED){ - d->dc[d->level].stroke_idx = add_hatch(d, pEmr->elp.elpHatch, pEmr->elp.elpColor); - d->dc[d->level].stroke_mode = DRAW_PATTERN; - d->dc[d->level].stroke_set = true; - } - 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); - d->dc[d->level].stroke_mode = DRAW_IMAGE; - d->dc[d->level].stroke_set = true; - } - else { // U_BS_PATTERN and anything strange that falls in, stroke is solid textColor - double r, g, b; - r = SP_COLOR_U_TO_F( U_RGBAGetR(d->dc[d->level].textColor)); - g = SP_COLOR_U_TO_F( U_RGBAGetG(d->dc[d->level].textColor)); - b = SP_COLOR_U_TO_F( U_RGBAGetB(d->dc[d->level].textColor)); - d->dc[d->level].style.stroke.value.color.set( r, g, b ); - d->dc[d->level].stroke_mode = DRAW_PAINT; - d->dc[d->level].stroke_set = true; + if( pEmr->elp.elpBrushStyle == U_BS_SOLID){ + double r, g, b; + r = SP_COLOR_U_TO_F( U_RGBAGetR(pEmr->elp.elpColor) ); + g = SP_COLOR_U_TO_F( U_RGBAGetG(pEmr->elp.elpColor) ); + b = SP_COLOR_U_TO_F( U_RGBAGetB(pEmr->elp.elpColor) ); + d->dc[d->level].style.stroke.value.color.set( r, g, b ); + d->dc[d->level].stroke_mode = DRAW_PAINT; + d->dc[d->level].stroke_set = true; + } + else if(pEmr->elp.elpBrushStyle == U_BS_HATCHED){ + d->dc[d->level].stroke_idx = add_hatch(d, pEmr->elp.elpHatch, pEmr->elp.elpColor); + d->dc[d->level].stroke_mode = DRAW_PATTERN; + d->dc[d->level].stroke_set = true; + } + 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); + d->dc[d->level].stroke_mode = DRAW_IMAGE; + d->dc[d->level].stroke_set = true; + } + else { // U_BS_PATTERN and anything strange that falls in, stroke is solid textColor + double r, g, b; + r = SP_COLOR_U_TO_F( U_RGBAGetR(d->dc[d->level].textColor)); + g = SP_COLOR_U_TO_F( U_RGBAGetG(d->dc[d->level].textColor)); + b = SP_COLOR_U_TO_F( U_RGBAGetB(d->dc[d->level].textColor)); + d->dc[d->level].style.stroke.value.color.set( r, g, b ); + d->dc[d->level].stroke_mode = DRAW_PAINT; + d->dc[d->level].stroke_set = true; + } } } @@ -1352,6 +1382,24 @@ insert_object(PEMF_CALLBACK_DATA d, int index, int type, PU_ENHMETARECORD pObj) } } +/* Identify probable Adobe Illustrator produced EMF files, which do strange things with the scaling. + The few so far observed all had this format. +*/ +int AI_hack(PU_EMRHEADER pEmr){ + int ret=0; + char *ptr; + ptr = (char *)pEmr; + PU_EMRSETMAPMODE nEmr = (PU_EMRSETMAPMODE) (ptr + pEmr->emr.nSize); + char *string = NULL; + if(pEmr->nDescription)string = U_Utf16leToUtf8((uint16_t *)((char *) pEmr + pEmr->offDescription), pEmr->nDescription, NULL); + if((pEmr->nDescription >= 13) && + (0==strcmp("Adobe Systems",string)) && + (nEmr->emr.iType == U_EMR_SETMAPMODE) && + (nEmr->iMode == U_MM_ANISOTROPIC)){ ret=1; } + if(string)free(string); + return(ret); +} + /** \fn create a UTF-32LE buffer and fill it with UNICODE unknown character \param count number of copies of the Unicode unknown character to fill with @@ -1364,10 +1412,27 @@ uint32_t *unknown_chars(size_t count){ return res; } -void common_image_extraction(PEMF_CALLBACK_DATA d, void *pEmr, double l, double t, double r, double b, +/** + \fn store SVG for an image given the pixmap and various coordinate information + \param d + \param pEmr + \param dl (double) destination left in inkscape pixels + \param dt (double) destination top in inkscape pixels + \param dr (double) destination right in inkscape pixels + \param db (double) destination bottom in inkscape pixels + \param sl (int) source left in pixels in the src image + \param st (int) source top in pixels in the src image + \param iUsage + \param offBits + \param cbBits + \param offBmi + \param cbBmi +*/ +void common_image_extraction(PEMF_CALLBACK_DATA d, void *pEmr, + double dl, double dt, double dr, double db, int sl, int st, int sw, int sh, uint32_t iUsage, uint32_t offBits, uint32_t cbBits, uint32_t offBmi, uint32_t cbBmi){ SVGOStringStream tmp_image; - tmp_image << " y=\"" << t << "\"\n x=\"" << l <<"\"\n "; + tmp_image << " y=\"" << dt << "\"\n x=\"" << dl <<"\"\n "; // The image ID is filled in much later when tmp_image is converted @@ -1395,6 +1460,10 @@ void common_image_extraction(PEMF_CALLBACK_DATA d, void *pEmr, double l, double &colortype, &invert )){ + if(sw == 0 || sl == 0){ + sw = width; + sh = height; + } if(!DIB_to_RGBA( px, // DIB pixel array @@ -1405,13 +1474,15 @@ void common_image_extraction(PEMF_CALLBACK_DATA d, void *pEmr, double l, double height, // Height of pixel array colortype, // DIB BitCount Enumeration numCt, // Color table used if not 0 - invert // If DIB rows are in opposite order from RGBA rows + invert, // If DIB rows are in opposite order from RGBA rows + sl,st, // starting point in pixel array + sw,sh // columns/rows to extract from the pixel array (output array size) ) && rgba_px) { toPNG( // Get the image from the RGBA px into mempng &mempng, - width, height, + sw, sh, // size of the extracted pixel array rgba_px); free(rgba_px); } @@ -1427,7 +1498,7 @@ void common_image_extraction(PEMF_CALLBACK_DATA d, void *pEmr, double l, double tmp_image << "iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAAA3NCSVQICAjb4U/gAAAALElEQVQImQXBQQ2AMAAAsUJQMSWI2H8qME1yMshojwrvGB8XcHKvR1XtOTc/8HENumHCsOMAAAAASUVORK5CYII="; } - tmp_image << "\"\n height=\"" << b-t+1 << "\"\n width=\"" << r-l+1 << "\"\n"; + tmp_image << "\"\n height=\"" << db-dt+1 << "\"\n width=\"" << dr-dl+1 << "\"\n"; *(d->outsvg) += "\n\t outsvg) += tmp_image.str().c_str(); @@ -1535,29 +1606,42 @@ std::cout << "BEFORE DRAW" tmp_outdef << " xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\"\n"; // needed for sodipodi:role tmp_outdef << " version=\"1.0\"\n"; - d->xDPI = 2540; - d->yDPI = 2540; + d->ulCornerX = pEmr->rclBounds.left; // Upper left corner, from header rclBounds, in logical units, usually both 0, but not always + d->ulCornerY = pEmr->rclBounds.top;; - d->dc[d->level].PixelsInX = pEmr->rclFrame.right; // - pEmr->rclFrame.left; - d->dc[d->level].PixelsInY = pEmr->rclFrame.bottom; // - pEmr->rclFrame.top; - - d->MMX = d->dc[d->level].PixelsInX / 100.0; - d->MMY = d->dc[d->level].PixelsInY / 100.0; - - d->dc[d->level].PixelsOutX = d->MMX * PX_PER_MM; - d->dc[d->level].PixelsOutY = d->MMY * PX_PER_MM; + if(pEmr->rclFrame.bottom < 0 || pEmr->rclFrame.top < 0){ d->ydir = -1.0; } + else { d->ydir = 1.0; } + /* inclusive-inclusive, so the size is 1 more than the difference */ + d->dc[d->level].PixelsInX = pEmr->rclFrame.right - pEmr->rclFrame.left + 1; + d->dc[d->level].PixelsInY = pEmr->rclFrame.bottom - pEmr->rclFrame.top + 1; /* calculate ratio of Inkscape dpi/device dpi - This can cause problems later due to accuracy limits in the EMF. A super high resolution + This can cause problems later due to accuracy limits in the EMF. A high resolution EMF might have a final device_scale of 0.074998, and adjusting the (integer) device size by 1 will still not get it exactly to 0.075. Later when the font size is calculated it can end up as 29.9992 or 22.4994 instead of the intended 30 or 22.5. This is handled by - snapping font sizes to the nearest .01. + snapping font sizes to the nearest .01. The best estimate is made by using both values. */ - if (pEmr->szlMillimeters.cx && pEmr->szlDevice.cx) - device_scale = PX_PER_MM*pEmr->szlMillimeters.cx/pEmr->szlDevice.cx; - + if (pEmr->szlMillimeters.cx && pEmr->szlDevice.cx) device_scale = PX_PER_MM * + (pEmr->szlMillimeters.cx + pEmr->szlMillimeters.cy)/( pEmr->szlDevice.cx + pEmr->szlDevice.cy); + + /* Adobe Illustrator files set mapmode to MM_ANISOTROPIC and somehow or other this + converts the rclFrame values from MM_HIMETRIC to MM_HIENGLISH, with another factor of 3 thrown + in for good measure. Ours not to question why... + */ + if(AI_hack(pEmr)){ + d->dc[d->level].PixelsInX *= 25.4/(10.0*3.0); + d->dc[d->level].PixelsInY *= 25.4/(10.0*3.0); + device_scale *= 25.4/(10.0*3.0); + } + + d->MMX = d->dc[d->level].PixelsInX / 100.0; + d->MMY = d->dc[d->level].PixelsInY / 100.0; + + d->dc[d->level].PixelsOutX = d->MMX * PX_PER_MM; + d->dc[d->level].PixelsOutY = d->MMY * PX_PER_MM; + tmp_outdef << " width=\"" << d->MMX << "mm\"\n" << " height=\"" << d->MMY << "mm\">\n"; @@ -1849,7 +1933,43 @@ std::cout << "BEFORE DRAW" } case U_EMR_SETPIXELV: dbg_str << "\n"; break; case U_EMR_SETMAPPERFLAGS: dbg_str << "\n"; break; - case U_EMR_SETMAPMODE: dbg_str << "\n"; break; + case U_EMR_SETMAPMODE: + { + dbg_str << "\n"; + PU_EMRSETMAPMODE pEmr = (PU_EMRSETMAPMODE) lpEMFR; + switch (pEmr->iMode){ + case U_MM_TEXT: + default: + d->ydir = 1.0; + // leave device_scale as is, device_scale maps LU pixels to inkscape pixels as set in the EMF header + break; + case U_MM_LOMETRIC: // 1 LU = 0.1 mm + d->ydir = -1.0; + device_scale = 0.1 * PX_PER_MM; + break; + case U_MM_HIMETRIC: // 1 LU = 0.01 mm + d->ydir = -1.0; + device_scale = 0.01 * PX_PER_MM; + break; + case U_MM_LOENGLISH: // 1 LU = 0.1 in + d->ydir = -1.0; + device_scale = 0.1 * PX_PER_IN; + break; + case U_MM_HIENGLISH: // 1 LU = 0.01 in + d->ydir = -1.0; + device_scale = 0.01 * PX_PER_IN; + break; + case U_MM_TWIPS: // 1 LU = 1/1440 in + d->ydir = -1.0; + device_scale = (1.0/1440.0) * PX_PER_IN; + break; + case U_MM_ISOTROPIC: // let scaleX etc. handle it, as set by SETVIEWPORTEXTEX and SETWINDOWEXTEX + break; + case U_MM_ANISOTROPIC: + break; + } + break; + } case U_EMR_SETBKMODE: dbg_str << "\n"; break; case U_EMR_SETPOLYFILLMODE: { @@ -2540,20 +2660,27 @@ std::cout << "BEFORE DRAW" dbg_str << "\n"; PU_EMRBITBLT pEmr = (PU_EMRBITBLT) lpEMFR; - double l = pix_to_x_point( d, pEmr->Dest.x, pEmr->Dest.y); - double t = pix_to_y_point( d, pEmr->Dest.x, pEmr->Dest.y); - double r = pix_to_x_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y); - double b = pix_to_y_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y); + double dl = pix_to_x_point( d, pEmr->Dest.x, pEmr->Dest.y); + double dt = pix_to_y_point( d, pEmr->Dest.x, pEmr->Dest.y); + double dr = pix_to_x_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y); + double db = pix_to_y_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y); + //source position within the bitmap, in pixels + int sl = pEmr->Src.x + pEmr->xformSrc.eDx; + int st = pEmr->Src.y + pEmr->xformSrc.eDy; + int sw = 0; // extract all of the image + int sh = 0; + if(sl<0)sl=0; + if(st<0)st=0; // Treat all nonImage bitblts as a rectangular write. Definitely not correct, but at // least it leaves objects where the operations should have been. if (!pEmr->cbBmiSrc) { // should be an application of a DIBPATTERNBRUSHPT, use a solid color instead SVGOStringStream tmp_rectangle; - tmp_rectangle << "\n\tM " << l << " " << t << " "; - tmp_rectangle << "\n\tL " << r << " " << t << " "; - tmp_rectangle << "\n\tL " << r << " " << b << " "; - tmp_rectangle << "\n\tL " << l << " " << b << " "; + tmp_rectangle << "\n\tM " << dl << " " << dt << " "; + tmp_rectangle << "\n\tL " << dr << " " << dt << " "; + tmp_rectangle << "\n\tL " << dr << " " << db << " "; + tmp_rectangle << "\n\tL " << dl << " " << db << " "; tmp_rectangle << "\n\tz"; d->mask |= emr_mask; @@ -2563,7 +2690,7 @@ std::cout << "BEFORE DRAW" tmp_path << tmp_rectangle.str().c_str(); } else { - common_image_extraction(d,pEmr,l,t,r,b, + common_image_extraction(d,pEmr,dl,dt,dr,db,sl,st,sw,sh, pEmr->iUsageSrc, pEmr->offBitsSrc, pEmr->cbBitsSrc, pEmr->offBmiSrc, pEmr->cbBmiSrc); } break; @@ -2574,11 +2701,18 @@ std::cout << "BEFORE DRAW" PU_EMRSTRETCHBLT pEmr = (PU_EMRSTRETCHBLT) lpEMFR; // Always grab image, ignore modes. if (pEmr->cbBmiSrc) { - double l = pix_to_x_point( d, pEmr->Dest.x, pEmr->Dest.y); - double t = pix_to_y_point( d, pEmr->Dest.x, pEmr->Dest.y); - double r = pix_to_x_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y); - double b = pix_to_y_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y); - common_image_extraction(d,pEmr,l,t,r,b, + double dl = pix_to_x_point( d, pEmr->Dest.x, pEmr->Dest.y); + double dt = pix_to_y_point( d, pEmr->Dest.x, pEmr->Dest.y); + double dr = pix_to_x_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y); + double db = pix_to_y_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y); + //source position within the bitmap, in pixels + int sl = pEmr->Src.x + pEmr->xformSrc.eDx; + int st = pEmr->Src.y + pEmr->xformSrc.eDy; + int sw = pEmr->cSrc.x; // extract the specified amount of the image + int sh = pEmr->cSrc.y; + if(sl<0)sl=0; + if(st<0)st=0; + common_image_extraction(d,pEmr,dl,dt,dr,db,sl,st,sw,sh, pEmr->iUsageSrc, pEmr->offBitsSrc, pEmr->cbBitsSrc, pEmr->offBmiSrc, pEmr->cbBmiSrc); } break; @@ -2589,11 +2723,17 @@ std::cout << "BEFORE DRAW" PU_EMRMASKBLT pEmr = (PU_EMRMASKBLT) lpEMFR; // Always grab image, ignore masks and modes. if (pEmr->cbBmiSrc) { - double l = pix_to_x_point( d, pEmr->Dest.x, pEmr->Dest.y); - double t = pix_to_y_point( d, pEmr->Dest.x, pEmr->Dest.y); - double r = pix_to_x_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y); - double b = pix_to_y_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y); - common_image_extraction(d,pEmr,l,t,r,b, + double dl = pix_to_x_point( d, pEmr->Dest.x, pEmr->Dest.y); + double dt = pix_to_y_point( d, pEmr->Dest.x, pEmr->Dest.y); + double dr = pix_to_x_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y); + double db = pix_to_y_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y); + int sl = pEmr->Src.x + pEmr->xformSrc.eDx; //source position within the bitmap, in pixels + int st = pEmr->Src.y + pEmr->xformSrc.eDy; + int sw = 0; // extract all of the image + int sh = 0; + if(sl<0)sl=0; + if(st<0)st=0; + common_image_extraction(d,pEmr,dl,dt,dr,db,sl,st,sw,sh, pEmr->iUsageSrc, pEmr->offBitsSrc, pEmr->cbBitsSrc, pEmr->offBmiSrc, pEmr->cbBmiSrc); } break; @@ -2609,11 +2749,17 @@ std::cout << "BEFORE DRAW" // user can sort out transparency later using Gimp, if need be. PU_EMRSTRETCHDIBITS pEmr = (PU_EMRSTRETCHDIBITS) lpEMFR; - double l = pix_to_x_point( d, pEmr->Dest.x, pEmr->Dest.y ); - double t = pix_to_y_point( d, pEmr->Dest.x, pEmr->Dest.y ); - double r = pix_to_x_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y ); - double b = pix_to_y_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y ); - common_image_extraction(d,pEmr,l,t,r,b, + double dl = pix_to_x_point( d, pEmr->Dest.x, pEmr->Dest.y ); + double dt = pix_to_y_point( d, pEmr->Dest.x, pEmr->Dest.y ); + double dr = pix_to_x_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y ); + double db = pix_to_y_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y ); + int sl = pEmr->Src.x; //source position within the bitmap, in pixels + int st = pEmr->Src.y; + int sw = pEmr->cSrc.x; // extract the specified amount of the image + int sh = pEmr->cSrc.y; + if(sl<0)sl=0; + if(st<0)st=0; + common_image_extraction(d,pEmr,dl,dt,dr,db,sl,st,sw,sh, pEmr->iUsageSrc, pEmr->offBitsSrc, pEmr->cbBitsSrc, pEmr->offBmiSrc, pEmr->cbBmiSrc); dbg_str << "\n"; -- cgit v1.2.3 From 67c802d29eb3810a434992a457c35d887051b0f9 Mon Sep 17 00:00:00 2001 From: David Mathog <> Date: Thu, 1 Nov 2012 13:04:33 +0100 Subject: changes_2012_10_31b.patch EMF import: - better handling of aberrant bitmap records - support for reading rarely encountered map modes (MM_LOENGLISH etc.) (Output is still always MM_TEXT) - better handling of EMF files with offset EMR_HEADER bounds fields - improved text import (snap font sizes to the nearest whole multiple of 1/32 points) (bzr r11668.1.37) --- src/extension/internal/emf-inout.cpp | 213 +++++++++++++++++------------------ 1 file changed, 104 insertions(+), 109 deletions(-) (limited to 'src/extension/internal/emf-inout.cpp') diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp index acc3443d5..5a832f9aa 100644 --- a/src/extension/internal/emf-inout.cpp +++ b/src/extension/internal/emf-inout.cpp @@ -59,7 +59,6 @@ namespace Extension { namespace Internal { -static float device_scale = DEVICESCALE; static U_RECTL rc_old; static bool clipset = false; static uint32_t ICMmode=0; // not used yet, but code to read it from EMF implemented @@ -363,8 +362,6 @@ typedef struct emf_device_context { U_SIZEL sizeWnd; U_SIZEL sizeView; - float PixelsInX, PixelsInY; - float PixelsOutX, PixelsOutY; U_POINTL winorg; U_POINTL vieworg; double ScaleInX, ScaleInY; @@ -390,8 +387,12 @@ typedef struct emf_callback_data { EMF_DEVICE_CONTEXT dc[EMF_MAX_DC+1]; // FIXME: This should be dynamic.. int level; - double ulCornerX,ulCornerY; // Upper left corner, from header rclBounds, in logical units - double ydir; // 1.0 if y is positive DOWN (usual case), -1.0 if y is negative DOWN + double W2PscaleX,W2PscaleY; // World to Page scale. Y may be negative for MM_LOMETRIC etc. + float MM100InX, MM100InY; // size of the drawing in hundredths of a millimeter + float PixelsInX, PixelsInY; // size of the drawing, in EMF device pixels + float PixelsOutX, PixelsOutY;// size of the drawing, in Inkscape pixels + double ulCornerInX,ulCornerInY; // Upper left corner, from header rclBounds, in logical units + double ulCornerOutX,ulCornerOutY; // Upper left corner, in Inkscape pixels uint32_t mask; // Draw properties int arcdir; //U_AD_COUNTERCLOCKWISE 1 or U_AD_CLOCKWISE 2 @@ -632,10 +633,7 @@ uint32_t add_image(PEMF_CALLBACK_DATA d, void *pEmr, uint32_t cbBits, uint32_t height, // Height of pixel array in record colortype, // DIB BitCount Enumeration numCt, // Color table used if not 0 - invert, // If DIB rows are in opposite order from RGBA rows - 0,0, // start position in pixel array in record - width, // Width of extracted pixel array - height // Height of extracted pixel array + invert // If DIB rows are in opposite order from RGBA rows ) && rgba_px) { @@ -890,25 +888,21 @@ output_style(PEMF_CALLBACK_DATA d, int iType) static double _pix_x_to_point(PEMF_CALLBACK_DATA d, double px) { - double scale = (d->dc[d->level].ScaleInX ? d->dc[d->level].ScaleInX : 1.0); - double tmp = px; - tmp -= d->dc[d->level].winorg.x; - tmp *= scale; - tmp -= d->ulCornerX; - tmp += d->dc[d->level].vieworg.x; - return tmp; + double scale = (d->dc[d->level].ScaleInX ? d->dc[d->level].ScaleInX : d->W2PscaleX); + double tmp; + tmp = ((double) (px - d->dc[d->level].winorg.x))*scale + + d->dc[d->level].vieworg.x - d->ulCornerOutX; + return(tmp); } static double _pix_y_to_point(PEMF_CALLBACK_DATA d, double py) { - double scale = (d->dc[d->level].ScaleInY ? d->dc[d->level].ScaleInY : 1.0); - double tmp = py; - tmp -= d->dc[d->level].winorg.y; - tmp *= scale; - tmp += d->ydir*d->ulCornerY; - tmp += d->dc[d->level].vieworg.y; - return tmp; + double scale = (d->dc[d->level].ScaleInY ? d->dc[d->level].ScaleInY : d->W2PscaleY); + double tmp; + tmp = ((double) (py - d->dc[d->level].winorg.y))*scale + + d->dc[d->level].vieworg.y - d->ulCornerOutY; + return(tmp); } @@ -917,13 +911,7 @@ pix_to_x_point(PEMF_CALLBACK_DATA d, double px, double py) { double wpx = px * d->dc[d->level].worldTransform.eM11 + py * d->dc[d->level].worldTransform.eM21 + d->dc[d->level].worldTransform.eDx; double x = _pix_x_to_point(d, wpx); -/* - double ppx = _pix_x_to_point(d, px); - double ppy = _pix_y_to_point(d, py); - 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; - + return x; } @@ -933,12 +921,6 @@ pix_to_y_point(PEMF_CALLBACK_DATA d, double px, double py) double wpy = px * d->dc[d->level].worldTransform.eM12 + py * d->dc[d->level].worldTransform.eM22 + d->dc[d->level].worldTransform.eDy; double y = _pix_y_to_point(d, wpy); -/* - double ppx = _pix_x_to_point(d, px); - double ppy = _pix_y_to_point(d, py); - 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; return y; @@ -947,13 +929,11 @@ pix_to_y_point(PEMF_CALLBACK_DATA d, double px, double py) static double pix_to_size_point(PEMF_CALLBACK_DATA d, double px) { - double ppx = px * (d->dc[d->level].ScaleInX ? d->dc[d->level].ScaleInX : 1.0); - double ppy = 0; + double ppx = px * (d->dc[d->level].ScaleInX ? d->dc[d->level].ScaleInX : d->W2PscaleX); + // double ppy = 0; - double dx = ppx * d->dc[d->level].worldTransform.eM11 + ppy * d->dc[d->level].worldTransform.eM21; - dx *= device_scale; - double dy = ppx * d->dc[d->level].worldTransform.eM12 + ppy * d->dc[d->level].worldTransform.eM22; - dy *= device_scale; + double dx = ppx * d->dc[d->level].worldTransform.eM11; // + ppy * d->dc[d->level].worldTransform.eM21 + double dy = ppx * d->dc[d->level].worldTransform.eM12; // + ppy * d->dc[d->level].worldTransform.eM22 double tmp = sqrt(dx * dx + dy * dy); return tmp; @@ -1315,11 +1295,12 @@ select_font(PEMF_CALLBACK_DATA d, int index) int cur_level = d->level; d->level = d->emf_obj[index].level; double font_size = pix_to_size_point( d, pEmr->elfw.elfLogFont.lfHeight ); - /* snap the font_size to the nearest .01. - See the notes where device_scale is set for the reason why. + /* snap the font_size to the nearest 1/32nd of a point. + (The size is converted from Pixels to points, snapped, and converted back.) + See the notes where d->W2Pscale[XY] are set for the reason why. Typically this will set the font to the desired exact size. If some peculiar size - was intended this will, at worst, make it 1% off, which is unlikely to be a problem. */ - font_size = round(100.0 * font_size)/100.0; + was intended this will, at worst, make it .03125 off, which is unlikely to be a problem. */ + font_size = round(20.0 * 0.8 * font_size)/(20.0 * 0.8); d->level = cur_level; d->dc[d->level].style.font_size.computed = font_size; d->dc[d->level].style.font_weight.value = @@ -1392,11 +1373,13 @@ int AI_hack(PU_EMRHEADER pEmr){ PU_EMRSETMAPMODE nEmr = (PU_EMRSETMAPMODE) (ptr + pEmr->emr.nSize); char *string = NULL; if(pEmr->nDescription)string = U_Utf16leToUtf8((uint16_t *)((char *) pEmr + pEmr->offDescription), pEmr->nDescription, NULL); - if((pEmr->nDescription >= 13) && - (0==strcmp("Adobe Systems",string)) && - (nEmr->emr.iType == U_EMR_SETMAPMODE) && - (nEmr->iMode == U_MM_ANISOTROPIC)){ ret=1; } - if(string)free(string); + if(string){ + if((pEmr->nDescription >= 13) && + (0==strcmp("Adobe Systems",string)) && + (nEmr->emr.iType == U_EMR_SETMAPMODE) && + (nEmr->iMode == U_MM_ANISOTROPIC)){ ret=1; } + free(string); + } return(ret); } @@ -1442,6 +1425,7 @@ void common_image_extraction(PEMF_CALLBACK_DATA d, void *pEmr, mempng.buffer = NULL; char *rgba_px=NULL; // RGBA pixels + char *sub_px=NULL; // RGBA pixels, subarray char *px=NULL; // DIB pixels uint32_t width, height, colortype, numCt, invert; PU_RGBQUAD ct = NULL; @@ -1474,17 +1458,24 @@ void common_image_extraction(PEMF_CALLBACK_DATA d, void *pEmr, height, // Height of pixel array colortype, // DIB BitCount Enumeration numCt, // Color table used if not 0 - invert, // If DIB rows are in opposite order from RGBA rows - sl,st, // starting point in pixel array - sw,sh // columns/rows to extract from the pixel array (output array size) + invert // If DIB rows are in opposite order from RGBA rows ) && rgba_px) { + sub_px = RGBA_to_RGBA( + rgba_px, // full pixel array from DIB + width, // Width of pixel array + height, // Height of pixel array + sl,st, // starting point in pixel array + &sw,&sh // columns/rows to extract from the pixel array (output array size) + ); + + if(!sub_px)sub_px=rgba_px; toPNG( // Get the image from the RGBA px into mempng &mempng, sw, sh, // size of the extracted pixel array - rgba_px); - free(rgba_px); + sub_px); + free(sub_px); } } if(mempng.buffer){ @@ -1606,41 +1597,50 @@ std::cout << "BEFORE DRAW" tmp_outdef << " xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\"\n"; // needed for sodipodi:role tmp_outdef << " version=\"1.0\"\n"; - d->ulCornerX = pEmr->rclBounds.left; // Upper left corner, from header rclBounds, in logical units, usually both 0, but not always - d->ulCornerY = pEmr->rclBounds.top;; - - if(pEmr->rclFrame.bottom < 0 || pEmr->rclFrame.top < 0){ d->ydir = -1.0; } - else { d->ydir = 1.0; } /* inclusive-inclusive, so the size is 1 more than the difference */ - d->dc[d->level].PixelsInX = pEmr->rclFrame.right - pEmr->rclFrame.left + 1; - d->dc[d->level].PixelsInY = pEmr->rclFrame.bottom - pEmr->rclFrame.top + 1; + d->MM100InX = pEmr->rclFrame.right - pEmr->rclFrame.left + 1; + d->MM100InY = pEmr->rclFrame.bottom - pEmr->rclFrame.top + 1; + d->PixelsInX = pEmr->rclBounds.right - pEmr->rclBounds.left + 1; + d->PixelsInY = pEmr->rclBounds.bottom - pEmr->rclBounds.top + 1; + /* calculate ratio of Inkscape dpi/device dpi This can cause problems later due to accuracy limits in the EMF. A high resolution - EMF might have a final device_scale of 0.074998, and adjusting the (integer) device size + EMF might have a final W2Pscale[XY] of 0.074998, and adjusting the (integer) device size by 1 will still not get it exactly to 0.075. Later when the font size is calculated it can end up as 29.9992 or 22.4994 instead of the intended 30 or 22.5. This is handled by snapping font sizes to the nearest .01. The best estimate is made by using both values. */ - if (pEmr->szlMillimeters.cx && pEmr->szlDevice.cx) device_scale = PX_PER_MM * - (pEmr->szlMillimeters.cx + pEmr->szlMillimeters.cy)/( pEmr->szlDevice.cx + pEmr->szlDevice.cy); + if (pEmr->szlMillimeters.cx && pEmr->szlDevice.cy){ + d->W2PscaleX = d->W2PscaleY = + PX_PER_MM * + (pEmr->szlMillimeters.cx + pEmr->szlMillimeters.cy)/ + ( pEmr->szlDevice.cx + pEmr->szlDevice.cy); + } /* Adobe Illustrator files set mapmode to MM_ANISOTROPIC and somehow or other this converts the rclFrame values from MM_HIMETRIC to MM_HIENGLISH, with another factor of 3 thrown in for good measure. Ours not to question why... */ if(AI_hack(pEmr)){ - d->dc[d->level].PixelsInX *= 25.4/(10.0*3.0); - d->dc[d->level].PixelsInY *= 25.4/(10.0*3.0); - device_scale *= 25.4/(10.0*3.0); + d->MM100InX *= 25.4/(10.0*3.0); + d->MM100InY *= 25.4/(10.0*3.0); + d->W2PscaleX *= 25.4/(10.0*3.0); + d->W2PscaleY *= 25.4/(10.0*3.0); } - d->MMX = d->dc[d->level].PixelsInX / 100.0; - d->MMY = d->dc[d->level].PixelsInY / 100.0; + d->MMX = d->MM100InX / 100.0; + d->MMY = d->MM100InY / 100.0; + + d->PixelsOutX = d->MMX * PX_PER_MM; + d->PixelsOutY = d->MMY * PX_PER_MM; - d->dc[d->level].PixelsOutX = d->MMX * PX_PER_MM; - d->dc[d->level].PixelsOutY = d->MMY * PX_PER_MM; + // Upper left corner, from header rclBounds, in device units, usually both 0, but not always + d->ulCornerInX = pEmr->rclBounds.left; + d->ulCornerInY = pEmr->rclBounds.top; + d->ulCornerOutX = d->ulCornerInX * d->W2PscaleX; + d->ulCornerOutY = d->ulCornerInY * d->W2PscaleY; tmp_outdef << " width=\"" << d->MMX << "mm\"\n" << @@ -1847,8 +1847,8 @@ std::cout << "BEFORE DRAW" if (!d->dc[d->level].sizeWnd.cx || !d->dc[d->level].sizeWnd.cy) { d->dc[d->level].sizeWnd = d->dc[d->level].sizeView; if (!d->dc[d->level].sizeWnd.cx || !d->dc[d->level].sizeWnd.cy) { - d->dc[d->level].sizeWnd.cx = d->dc[d->level].PixelsOutX; - d->dc[d->level].sizeWnd.cy = d->dc[d->level].PixelsOutY; + d->dc[d->level].sizeWnd.cx = d->PixelsOutX; + d->dc[d->level].sizeWnd.cy = d->PixelsOutY; } } @@ -1856,17 +1856,17 @@ std::cout << "BEFORE DRAW" d->dc[d->level].sizeView = d->dc[d->level].sizeWnd; } - d->dc[d->level].PixelsInX = d->dc[d->level].sizeWnd.cx; - d->dc[d->level].PixelsInY = d->dc[d->level].sizeWnd.cy; - - if (d->dc[d->level].PixelsInX && d->dc[d->level].PixelsInY) { - 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; + if (d->dc[d->level].sizeWnd.cx && d->dc[d->level].sizeWnd.cy) { + d->dc[d->level].ScaleInX = (double) d->dc[d->level].sizeView.cx / (double) d->dc[d->level].sizeWnd.cx; + d->dc[d->level].ScaleInY = (double) d->dc[d->level].sizeView.cy / (double) d->dc[d->level].sizeWnd.cy; } else { d->dc[d->level].ScaleInX = 1; d->dc[d->level].ScaleInY = 1; } + /* scales logical to EMF pixels, but we need logical to Inkscape pixels */ + d->dc[d->level].ScaleInX *= d->PixelsOutX / d->PixelsInX; + d->dc[d->level].ScaleInY *= d->PixelsOutY / d->PixelsInY; break; } @@ -1889,27 +1889,27 @@ std::cout << "BEFORE DRAW" if (!d->dc[d->level].sizeView.cx || !d->dc[d->level].sizeView.cy) { d->dc[d->level].sizeView = d->dc[d->level].sizeWnd; if (!d->dc[d->level].sizeView.cx || !d->dc[d->level].sizeView.cy) { - d->dc[d->level].sizeView.cx = d->dc[d->level].PixelsOutX; - d->dc[d->level].sizeView.cy = d->dc[d->level].PixelsOutY; + d->dc[d->level].sizeView.cx = d->PixelsOutX; + d->dc[d->level].sizeView.cy = d->PixelsOutY; } } if (!d->dc[d->level].sizeWnd.cx || !d->dc[d->level].sizeWnd.cy) { d->dc[d->level].sizeWnd = d->dc[d->level].sizeView; } - - d->dc[d->level].PixelsInX = d->dc[d->level].sizeWnd.cx; - d->dc[d->level].PixelsInY = d->dc[d->level].sizeWnd.cy; - if (d->dc[d->level].PixelsInX && d->dc[d->level].PixelsInY) { - 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; + if (d->dc[d->level].sizeWnd.cx && d->dc[d->level].sizeWnd.cy) { + d->dc[d->level].ScaleInX = (double) d->dc[d->level].sizeView.cx / (double) d->dc[d->level].sizeWnd.cx; + d->dc[d->level].ScaleInY = (double) d->dc[d->level].sizeView.cy / (double) d->dc[d->level].sizeWnd.cy; } else { d->dc[d->level].ScaleInX = 1; d->dc[d->level].ScaleInY = 1; } - + /* scales logical to EMF pixels, but we need logical to Inkscape pixels */ + d->dc[d->level].ScaleInX *= d->PixelsOutX / d->PixelsInX; + d->dc[d->level].ScaleInY *= d->PixelsOutY / d->PixelsInY; + break; } case U_EMR_SETVIEWPORTORGEX: @@ -1940,30 +1940,29 @@ std::cout << "BEFORE DRAW" switch (pEmr->iMode){ case U_MM_TEXT: default: - d->ydir = 1.0; - // leave device_scale as is, device_scale maps LU pixels to inkscape pixels as set in the EMF header + // Use values from the header. break; case U_MM_LOMETRIC: // 1 LU = 0.1 mm - d->ydir = -1.0; - device_scale = 0.1 * PX_PER_MM; + d->W2PscaleX = 0.1 * PX_PER_MM; + d->W2PscaleY = -d->W2PscaleX; break; case U_MM_HIMETRIC: // 1 LU = 0.01 mm - d->ydir = -1.0; - device_scale = 0.01 * PX_PER_MM; + d->W2PscaleX = 0.01 * PX_PER_MM; + d->W2PscaleY = -d->W2PscaleX; break; case U_MM_LOENGLISH: // 1 LU = 0.1 in - d->ydir = -1.0; - device_scale = 0.1 * PX_PER_IN; + d->W2PscaleX = 0.01 * PX_PER_IN; + d->W2PscaleY = -d->W2PscaleX; break; case U_MM_HIENGLISH: // 1 LU = 0.01 in - d->ydir = -1.0; - device_scale = 0.01 * PX_PER_IN; + d->W2PscaleX = 0.001 * PX_PER_IN; + d->W2PscaleY = -d->W2PscaleX; break; case U_MM_TWIPS: // 1 LU = 1/1440 in - d->ydir = -1.0; - device_scale = (1.0/1440.0) * PX_PER_IN; + d->W2PscaleX = (1.0/1440.0) * PX_PER_IN; + d->W2PscaleY = -d->W2PscaleX; break; - case U_MM_ISOTROPIC: // let scaleX etc. handle it, as set by SETVIEWPORTEXTEX and SETWINDOWEXTEX + case U_MM_ISOTROPIC: // ScaleIn[XY] should be set elsewhere by SETVIEWPORTEXTEX and SETWINDOWEXTEX break; case U_MM_ANISOTROPIC: break; @@ -2710,8 +2709,6 @@ std::cout << "BEFORE DRAW" int st = pEmr->Src.y + pEmr->xformSrc.eDy; int sw = pEmr->cSrc.x; // extract the specified amount of the image int sh = pEmr->cSrc.y; - if(sl<0)sl=0; - if(st<0)st=0; common_image_extraction(d,pEmr,dl,dt,dr,db,sl,st,sw,sh, pEmr->iUsageSrc, pEmr->offBitsSrc, pEmr->cbBitsSrc, pEmr->offBmiSrc, pEmr->cbBmiSrc); } @@ -2731,8 +2728,6 @@ std::cout << "BEFORE DRAW" int st = pEmr->Src.y + pEmr->xformSrc.eDy; int sw = 0; // extract all of the image int sh = 0; - if(sl<0)sl=0; - if(st<0)st=0; common_image_extraction(d,pEmr,dl,dt,dr,db,sl,st,sw,sh, pEmr->iUsageSrc, pEmr->offBitsSrc, pEmr->cbBitsSrc, pEmr->offBmiSrc, pEmr->cbBmiSrc); } @@ -2757,8 +2752,6 @@ std::cout << "BEFORE DRAW" int st = pEmr->Src.y; int sw = pEmr->cSrc.x; // extract the specified amount of the image int sh = pEmr->cSrc.y; - if(sl<0)sl=0; - if(st<0)st=0; common_image_extraction(d,pEmr,dl,dt,dr,db,sl,st,sw,sh, pEmr->iUsageSrc, pEmr->offBitsSrc, pEmr->cbBitsSrc, pEmr->offBmiSrc, pEmr->cbBmiSrc); @@ -3247,6 +3240,8 @@ Emf::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri ) d.arcdir = U_AD_COUNTERCLOCKWISE; d.dwRop2 = U_R2_COPYPEN; d.dwRop3 = 0; + d.W2PscaleX = 1.0; + d.W2PscaleY = 1.0; d.hatches.size = 0; d.hatches.count = 0; d.hatches.strings = NULL; -- cgit v1.2.3 From 4360b6499cc35657523513390e56d5dd263a3e0c Mon Sep 17 00:00:00 2001 From: David Mathog <> Date: Wed, 14 Nov 2012 16:57:12 +0100 Subject: changes_2012_11_13a.patch EMF import: - More changes to the MAPMODES behavior. (bzr r11668.1.40) --- src/extension/internal/emf-inout.cpp | 94 +++++++++++++++++------------------- 1 file changed, 44 insertions(+), 50 deletions(-) (limited to 'src/extension/internal/emf-inout.cpp') diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp index 5a832f9aa..ee593b62e 100644 --- a/src/extension/internal/emf-inout.cpp +++ b/src/extension/internal/emf-inout.cpp @@ -387,7 +387,8 @@ typedef struct emf_callback_data { EMF_DEVICE_CONTEXT dc[EMF_MAX_DC+1]; // FIXME: This should be dynamic.. int level; - double W2PscaleX,W2PscaleY; // World to Page scale. Y may be negative for MM_LOMETRIC etc. + double E2IdirY; // EMF Y direction relative to Inkscape Y direction. Will be negative for MM_LOMETRIC etc. + double D2PscaleX,D2PscaleY; // EMF device to Inkscape Page scale. float MM100InX, MM100InY; // size of the drawing in hundredths of a millimeter float PixelsInX, PixelsInY; // size of the drawing, in EMF device pixels float PixelsOutX, PixelsOutY;// size of the drawing, in Inkscape pixels @@ -888,20 +889,20 @@ output_style(PEMF_CALLBACK_DATA d, int iType) static double _pix_x_to_point(PEMF_CALLBACK_DATA d, double px) { - double scale = (d->dc[d->level].ScaleInX ? d->dc[d->level].ScaleInX : d->W2PscaleX); + double scale = (d->dc[d->level].ScaleInX ? d->dc[d->level].ScaleInX : 1.0); double tmp; - tmp = ((double) (px - d->dc[d->level].winorg.x))*scale - + d->dc[d->level].vieworg.x - d->ulCornerOutX; + tmp = ((((double) (px - d->dc[d->level].winorg.x))*scale) + d->dc[d->level].vieworg.x) * d->D2PscaleX; + tmp -= d->ulCornerOutX; //The EMF boundary rectangle can be anywhere, place its upper left corner in the Inkscape upper left corner return(tmp); } static double _pix_y_to_point(PEMF_CALLBACK_DATA d, double py) { - double scale = (d->dc[d->level].ScaleInY ? d->dc[d->level].ScaleInY : d->W2PscaleY); + double scale = (d->dc[d->level].ScaleInY ? d->dc[d->level].ScaleInY : 1.0); double tmp; - tmp = ((double) (py - d->dc[d->level].winorg.y))*scale - + d->dc[d->level].vieworg.y - d->ulCornerOutY; + tmp = ((((double) (py - d->dc[d->level].winorg.y))*scale) * d->E2IdirY + d->dc[d->level].vieworg.y) * d->D2PscaleY; + tmp -= d->ulCornerOutY; //The EMF boundary rectangle can be anywhere, place its upper left corner in the Inkscape upper left corner return(tmp); } @@ -929,13 +930,13 @@ pix_to_y_point(PEMF_CALLBACK_DATA d, double px, double py) static double pix_to_size_point(PEMF_CALLBACK_DATA d, double px) { - double ppx = px * (d->dc[d->level].ScaleInX ? d->dc[d->level].ScaleInX : d->W2PscaleX); + double ppx = px * (d->dc[d->level].ScaleInX ? d->dc[d->level].ScaleInX : 1.0) * d->D2PscaleX; // double ppy = 0; double dx = ppx * d->dc[d->level].worldTransform.eM11; // + ppy * d->dc[d->level].worldTransform.eM21 double dy = ppx * d->dc[d->level].worldTransform.eM12; // + ppy * d->dc[d->level].worldTransform.eM22 - double tmp = sqrt(dx * dx + dy * dy); + double tmp = sqrt(dx * dx + dy * dy); return tmp; } @@ -1297,7 +1298,7 @@ select_font(PEMF_CALLBACK_DATA d, int index) double font_size = pix_to_size_point( d, pEmr->elfw.elfLogFont.lfHeight ); /* snap the font_size to the nearest 1/32nd of a point. (The size is converted from Pixels to points, snapped, and converted back.) - See the notes where d->W2Pscale[XY] are set for the reason why. + See the notes where d->D2Pscale[XY] are set for the reason why. Typically this will set the font to the desired exact size. If some peculiar size was intended this will, at worst, make it .03125 off, which is unlikely to be a problem. */ font_size = round(20.0 * 0.8 * font_size)/(20.0 * 0.8); @@ -1605,18 +1606,18 @@ std::cout << "BEFORE DRAW" d->PixelsInY = pEmr->rclBounds.bottom - pEmr->rclBounds.top + 1; /* - calculate ratio of Inkscape dpi/device dpi + calculate ratio of Inkscape dpi/EMF device dpi This can cause problems later due to accuracy limits in the EMF. A high resolution - EMF might have a final W2Pscale[XY] of 0.074998, and adjusting the (integer) device size + EMF might have a final D2Pscale[XY] of 0.074998, and adjusting the (integer) device size by 1 will still not get it exactly to 0.075. Later when the font size is calculated it can end up as 29.9992 or 22.4994 instead of the intended 30 or 22.5. This is handled by snapping font sizes to the nearest .01. The best estimate is made by using both values. */ - if (pEmr->szlMillimeters.cx && pEmr->szlDevice.cy){ - d->W2PscaleX = d->W2PscaleY = - PX_PER_MM * - (pEmr->szlMillimeters.cx + pEmr->szlMillimeters.cy)/ - ( pEmr->szlDevice.cx + pEmr->szlDevice.cy); + if ((pEmr->szlMillimeters.cx + pEmr->szlMillimeters.cy) && ( pEmr->szlDevice.cx + pEmr->szlDevice.cy)){ + d->E2IdirY = 1.0; // assume MM_TEXT, if not, this will be changed later + d->D2PscaleX = d->D2PscaleY = PX_PER_MM * + (double)(pEmr->szlMillimeters.cx + pEmr->szlMillimeters.cy)/ + (double)( pEmr->szlDevice.cx + pEmr->szlDevice.cy); } /* Adobe Illustrator files set mapmode to MM_ANISOTROPIC and somehow or other this @@ -1626,8 +1627,8 @@ std::cout << "BEFORE DRAW" if(AI_hack(pEmr)){ d->MM100InX *= 25.4/(10.0*3.0); d->MM100InY *= 25.4/(10.0*3.0); - d->W2PscaleX *= 25.4/(10.0*3.0); - d->W2PscaleY *= 25.4/(10.0*3.0); + d->D2PscaleX *= 25.4/(10.0*3.0); + d->D2PscaleY *= 25.4/(10.0*3.0); } d->MMX = d->MM100InX / 100.0; @@ -1639,8 +1640,8 @@ std::cout << "BEFORE DRAW" // Upper left corner, from header rclBounds, in device units, usually both 0, but not always d->ulCornerInX = pEmr->rclBounds.left; d->ulCornerInY = pEmr->rclBounds.top; - d->ulCornerOutX = d->ulCornerInX * d->W2PscaleX; - d->ulCornerOutY = d->ulCornerInY * d->W2PscaleY; + d->ulCornerOutX = d->ulCornerInX * d->D2PscaleX; + d->ulCornerOutY = d->ulCornerInY * d->E2IdirY * d->D2PscaleY; tmp_outdef << " width=\"" << d->MMX << "mm\"\n" << @@ -1856,18 +1857,19 @@ std::cout << "BEFORE DRAW" d->dc[d->level].sizeView = d->dc[d->level].sizeWnd; } + /* scales logical to EMF pixels, transfer a negative sign on Y, if any */ if (d->dc[d->level].sizeWnd.cx && d->dc[d->level].sizeWnd.cy) { d->dc[d->level].ScaleInX = (double) d->dc[d->level].sizeView.cx / (double) d->dc[d->level].sizeWnd.cx; d->dc[d->level].ScaleInY = (double) d->dc[d->level].sizeView.cy / (double) d->dc[d->level].sizeWnd.cy; + if(d->dc[d->level].ScaleInY < 0){ + d->dc[d->level].ScaleInY *= -1.0; + d->E2IdirY = -1.0; + } } else { d->dc[d->level].ScaleInX = 1; d->dc[d->level].ScaleInY = 1; } - /* scales logical to EMF pixels, but we need logical to Inkscape pixels */ - d->dc[d->level].ScaleInX *= d->PixelsOutX / d->PixelsInX; - d->dc[d->level].ScaleInY *= d->PixelsOutY / d->PixelsInY; - break; } case U_EMR_SETWINDOWORGEX: @@ -1898,18 +1900,19 @@ std::cout << "BEFORE DRAW" d->dc[d->level].sizeWnd = d->dc[d->level].sizeView; } + /* scales logical to EMF pixels, transfer a negative sign on Y, if any */ if (d->dc[d->level].sizeWnd.cx && d->dc[d->level].sizeWnd.cy) { d->dc[d->level].ScaleInX = (double) d->dc[d->level].sizeView.cx / (double) d->dc[d->level].sizeWnd.cx; d->dc[d->level].ScaleInY = (double) d->dc[d->level].sizeView.cy / (double) d->dc[d->level].sizeWnd.cy; + if(d->dc[d->level].ScaleInY < 0){ + d->dc[d->level].ScaleInY *= -1.0; + d->E2IdirY = -1.0; + } } else { d->dc[d->level].ScaleInX = 1; d->dc[d->level].ScaleInY = 1; } - /* scales logical to EMF pixels, but we need logical to Inkscape pixels */ - d->dc[d->level].ScaleInX *= d->PixelsOutX / d->PixelsInX; - d->dc[d->level].ScaleInY *= d->PixelsOutY / d->PixelsInY; - break; } case U_EMR_SETVIEWPORTORGEX: @@ -1940,31 +1943,21 @@ std::cout << "BEFORE DRAW" switch (pEmr->iMode){ case U_MM_TEXT: default: - // Use values from the header. - break; - case U_MM_LOMETRIC: // 1 LU = 0.1 mm - d->W2PscaleX = 0.1 * PX_PER_MM; - d->W2PscaleY = -d->W2PscaleX; + // Use all values from the header. break; + /* For all of the following the indicated scale this will be encoded in WindowExtEx/ViewportExtex + and show up in ScaleIn[XY] + */ + case U_MM_LOMETRIC: // 1 LU = 0.1 mm, case U_MM_HIMETRIC: // 1 LU = 0.01 mm - d->W2PscaleX = 0.01 * PX_PER_MM; - d->W2PscaleY = -d->W2PscaleX; - break; case U_MM_LOENGLISH: // 1 LU = 0.1 in - d->W2PscaleX = 0.01 * PX_PER_IN; - d->W2PscaleY = -d->W2PscaleX; - break; case U_MM_HIENGLISH: // 1 LU = 0.01 in - d->W2PscaleX = 0.001 * PX_PER_IN; - d->W2PscaleY = -d->W2PscaleX; - break; case U_MM_TWIPS: // 1 LU = 1/1440 in - d->W2PscaleX = (1.0/1440.0) * PX_PER_IN; - d->W2PscaleY = -d->W2PscaleX; - break; - case U_MM_ISOTROPIC: // ScaleIn[XY] should be set elsewhere by SETVIEWPORTEXTEX and SETWINDOWEXTEX + d->E2IdirY = -1.0; + // Use d->D2Pscale[XY] values from the header. break; - case U_MM_ANISOTROPIC: + case U_MM_ISOTROPIC: // ScaleIn[XY] should be set elsewhere by SETVIEWPORTEXTEX and SETWINDOWEXTEX + case U_MM_ANISOTROPIC: break; } break; @@ -3240,8 +3233,9 @@ Emf::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri ) d.arcdir = U_AD_COUNTERCLOCKWISE; d.dwRop2 = U_R2_COPYPEN; d.dwRop3 = 0; - d.W2PscaleX = 1.0; - d.W2PscaleY = 1.0; + d.E2IdirY = 1.0; + d.D2PscaleX = 1.0; + d.D2PscaleY = 1.0; d.hatches.size = 0; d.hatches.count = 0; d.hatches.strings = NULL; -- cgit v1.2.3 From 583d0895ad53ea5257099ab997527ed44649ff71 Mon Sep 17 00:00:00 2001 From: David Mathog <> Date: Thu, 13 Dec 2012 05:10:54 +0100 Subject: preliminary release of the EMF import text reassembly feature. (based on libTERE (TExt REassembly), not yet published) libTERE examines all the text in an EMF file, which consists only of little chunks in different formats, and attempts to reassemble it into an editable SVG text object, with color, different fonts and so forth. (only tested with English) (bzr r11668.1.45) --- src/extension/internal/emf-inout.cpp | 184 ++++++++++++++++++++--------------- 1 file changed, 108 insertions(+), 76 deletions(-) (limited to 'src/extension/internal/emf-inout.cpp') diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp index ee593b62e..06a64f875 100644 --- a/src/extension/internal/emf-inout.cpp +++ b/src/extension/internal/emf-inout.cpp @@ -43,10 +43,15 @@ #include "document.h" #include "libunicode-convert/unicode-convert.h" +#include //This must precede text_reassemble.h or it blows up in pngconf.h when compiling +#include +#include +#include #include "emf-print.h" #include "emf-inout.h" #include "uemf.h" +#include "text_reassemble.h" #define PRINT_EMF "org.inkscape.print.emf" @@ -73,12 +78,14 @@ which was based on: http://stackoverflow.com/questions/1821806/how-to-encode-png-to-buffer-using-libpng gcc -Wall -o testpng testpng.c -lpng -*/ + +Originally here, but moved up #include #include #include #include +*/ /* A coloured pixel. */ @@ -409,6 +416,7 @@ typedef struct emf_callback_data { // both of these end up in 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. + TR_INFO *tri; // Text Reassembly data structure int n_obj; @@ -1507,11 +1515,29 @@ void common_image_extraction(PEMF_CALLBACK_DATA d, void *pEmr, //THis was a callback, just build it into a normal function int myEnhMetaFileProc(char *contents, unsigned int length, PEMF_CALLBACK_DATA d) { - uint32_t off=0; - uint32_t emr_mask; - int OK =1; + uint32_t off=0; + uint32_t emr_mask; + int OK =1; PU_ENHMETARECORD lpEMFR; - + TCHUNK_SPECS tsp; + + /* initialize the tsp for text reassembly */ + tsp.string = NULL; + tsp.ori = 0.0; /* degrees */ + tsp.fs = 12.0; /* font size */ + tsp.x = 0.0; + tsp.y = 0.0; + tsp.boff = 0.0; /* offset to baseline from LL corner of bounding rectangle, changes with fs and taln*/ + tsp.vadvance = 0.0; /* meaningful only when a complex contains two or more lines */ + tsp.taln = ALILEFT + ALIBASE; + tsp.ldir = LDIR_LR; + tsp.color = 0; /* RGBA Black */ + tsp.italics = 0; + tsp.weight = 80; + tsp.condensed = 100; + tsp.co = 0; + tsp.fi_idx = -1; /* set to an invalid */ + while(OK){ if(off>=length)return(0); //normally should exit from while after EMREOF sets OK to false. @@ -1528,7 +1554,18 @@ int myEnhMetaFileProc(char *contents, unsigned int length, PEMF_CALLBACK_DATA d) emr_mask = emr_properties(lpEMFR->iType); if(emr_mask == U_EMR_INVALID){ throw "Inkscape fatal memory allocation error - cannot continue"; } -// std::cout << "BEFORE DRAW logic d->mask: " << std::hex << d->mask << " emr_mask: " << emr_mask << std::dec << std::endl; +/* Uncomment the following to track down text problems */ +//std::cout << "tri->dirty:"<< d->tri->dirty << " emr_mask: " << std::hex << emr_mask << std::dec << std::endl; + if ( (emr_mask != 0xFFFFFFFF) && (emr_mask & U_DRAW_TEXT) && d->tri->dirty){ // next record is valid type and forces pending text to be drawn immediately + TR_layout_analyze(d->tri); + TR_layout_2_svg(d->tri); + SVGOStringStream ts; + ts << d->tri->out; + *(d->outsvg) += ts.str().c_str(); + d->tri = trinfo_clear(d->tri); + } + +//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) @@ -1539,6 +1576,7 @@ std::cout << "BEFORE DRAW" << " test5 " << ((d->mask & U_DRAW_ONLYTO) && !(emr_mask & U_DRAW_ONLYTO) ) << std::endl; */ + if ( (emr_mask != 0xFFFFFFFF) && // next record is valid type (d->mask & U_DRAW_VISIBLE) && // This record is drawable ( (d->mask & U_DRAW_FORCE) || // This draw is forced by STROKE/FILL/STROKEANDFILL PATH @@ -1619,6 +1657,7 @@ std::cout << "BEFORE DRAW" (double)(pEmr->szlMillimeters.cx + pEmr->szlMillimeters.cy)/ (double)( pEmr->szlDevice.cx + pEmr->szlDevice.cy); } + trinfo_load_qe(d->tri, d->D2PscaleX); /* quantization error that will affect text positions */ /* Adobe Illustrator files set mapmode to MM_ANISOTROPIC and somehow or other this converts the rclFrame values from MM_HIMETRIC to MM_HIENGLISH, with another factor of 3 thrown @@ -2791,17 +2830,7 @@ std::cout << "BEFORE DRAW" double x = pix_to_x_point(d, x1, y1); double y = pix_to_y_point(d, x1, y1); - 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 - else { dfact = 0.85; } // alignments U_TA_TOP 0x00 to 0x07, factor is approximate - if (d->dc[d->level].style.baseline_shift.value) { - 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); - } - else { - y += dfact * fabs(d->dc[d->level].style.font_size.computed); - } + /* Rotation issues are handled entirely in libTERE now */ uint32_t *dup_wt = NULL; @@ -2846,71 +2875,67 @@ std::cout << "BEFORE DRAW" } if (ansi_text) { -// gchar *p = ansi_text; -// while (*p) { -// if (*p < 32 || *p >= 127) { -// g_free(ansi_text); -// ansi_text = g_strdup(""); -// break; -// } -// p++; -// } SVGOStringStream ts; gchar *escaped_text = g_markup_escape_text(ansi_text, -1); -// float text_rgb[3]; -// sp_color_get_rgb_floatv( &(d->dc[d->level].style.fill.value.color), text_rgb ); - -// if (!d->dc[d->level].textColorSet) { -// d->dc[d->level].textColor = RGB(SP_COLOR_F_TO_U(text_rgb[0]), -// SP_COLOR_F_TO_U(text_rgb[1]), -// SP_COLOR_F_TO_U(text_rgb[2])); -// } - - char tmp[128]; - snprintf(tmp, 127, - "fill:#%02x%02x%02x;", - U_RGBAGetR(d->dc[d->level].textColor), - U_RGBAGetG(d->dc[d->level].textColor), - U_RGBAGetB(d->dc[d->level].textColor)); - - bool i = (d->dc[d->level].style.font_style.value == SP_CSS_FONT_STYLE_ITALIC); - //bool o = (d->dc[d->level].style.font_style.value == SP_CSS_FONT_STYLE_OBLIQUE); - bool b = (d->dc[d->level].style.font_weight.value == SP_CSS_FONT_WEIGHT_BOLD) || - (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); + tsp.x = x*0.8; // TERE expects sizes in points. + tsp.y = y*0.8; + memcpy(&tsp.color, &d->dc[d->level].textColor, sizeof(uint32_t)); //It is already an RGBA binary value, but compiler is picky about types + switch(d->dc[d->level].style.font_style.value){ + case SP_CSS_FONT_STYLE_OBLIQUE: + tsp.italics = FC_SLANT_OBLIQUE; break; + case SP_CSS_FONT_STYLE_ITALIC: + tsp.italics = FC_SLANT_ITALIC; break; + default: + case SP_CSS_FONT_STYLE_NORMAL: + tsp.italics = FC_SLANT_ROMAN; break; + } + switch(d->dc[d->level].style.font_weight.value){ + case SP_CSS_FONT_WEIGHT_100: tsp.weight = FC_WEIGHT_THIN ; break; + case SP_CSS_FONT_WEIGHT_200: tsp.weight = FC_WEIGHT_EXTRALIGHT ; break; + case SP_CSS_FONT_WEIGHT_300: tsp.weight = FC_WEIGHT_LIGHT ; break; + case SP_CSS_FONT_WEIGHT_400: tsp.weight = FC_WEIGHT_NORMAL ; break; + case SP_CSS_FONT_WEIGHT_500: tsp.weight = FC_WEIGHT_MEDIUM ; break; + case SP_CSS_FONT_WEIGHT_600: tsp.weight = FC_WEIGHT_SEMIBOLD ; break; + case SP_CSS_FONT_WEIGHT_700: tsp.weight = FC_WEIGHT_BOLD ; break; + case SP_CSS_FONT_WEIGHT_800: tsp.weight = FC_WEIGHT_EXTRABOLD ; break; + case SP_CSS_FONT_WEIGHT_900: tsp.weight = FC_WEIGHT_HEAVY ; break; + case SP_CSS_FONT_WEIGHT_NORMAL: tsp.weight = FC_WEIGHT_NORMAL ; break; + case SP_CSS_FONT_WEIGHT_BOLD: tsp.weight = FC_WEIGHT_BOLD ; break; + case SP_CSS_FONT_WEIGHT_LIGHTER: tsp.weight = FC_WEIGHT_EXTRALIGHT ; break; + case SP_CSS_FONT_WEIGHT_BOLDER: tsp.weight = FC_WEIGHT_EXTRABOLD ; break; + default: tsp.weight = FC_WEIGHT_NORMAL ; break; + } + // 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; - - ts << "dc[d->level].style.baseline_shift.value) { - ts << " transform=\"" - << "rotate(-" << d->dc[d->level].style.baseline_shift.value - << " " << x << " " << y << ")" - << "\"\n"; + tsp.taln = ((d->dc[d->level].textAlign & U_TA_CENTER) == U_TA_CENTER) ? ALICENTER : + (((d->dc[d->level].textAlign & U_TA_CENTER) == U_TA_LEFT) ? ALILEFT : + ALIRIGHT); + tsp.taln |= ((d->dc[d->level].textAlign & U_TA_BASEBIT) ? ALIBASE : + ((d->dc[d->level].textAlign & U_TA_BOTTOM) ? ALIBOT : + ALITOP)); + tsp.ldir = (d->dc[d->level].textAlign & U_TA_RTLREADING ? LDIR_RL : LDIR_LR); // language direction + tsp.condensed = FC_WIDTH_NORMAL; // Not implemented well in libTERE (yet) + tsp.ori = d->dc[d->level].style.baseline_shift.value; // For now orientation is always the same as escapement + tsp.string = (uint8_t *) U_strdup(escaped_text); // this will be free'd much later at a trinfo_clear(). + tsp.fs = d->dc[d->level].style.font_size.computed * 0.8; // Font size in points + (void) trinfo_load_fontname(d->tri, (uint8_t *)d->dc[d->level].font_name, &tsp); + // when font name includes narrow it may not be set to "condensed". Narrow fonts do not work well anyway though + // as the metrics from fontconfig may not match, or the font may not be present. + if(0<= TR_findcasesub(d->dc[d->level].font_name, (char *) "Narrow")){ tsp.co=1; } + else { tsp.co=0; } + + int status = trinfo_load_textrec(d->tri, &tsp, tsp.ori,TR_EMFBOT); // ori is actually escapement + if(status==-1){ // change of escapement, emit what we have and reset + TR_layout_analyze(d->tri); + TR_layout_2_svg(d->tri); + ts << d->tri->out; + *(d->outsvg) += ts.str().c_str(); + d->tri = trinfo_clear(d->tri); + (void) trinfo_load_textrec(d->tri, &tsp, tsp.ori,TR_EMFBOT); // ignore return status, it must work } - ts << ">dc[d->level].style.font_size.computed) << "px;" - << tmp - << "font-style:" << (i ? "italic" : "normal") << ";" - << "font-weight:" << (b ? "bold" : "normal") << ";" - << "text-align:" << (lcr==2 ? "center" : lcr==1 ? "end" : "start") << ";" - << "text-anchor:" << (lcr==2 ? "middle" : lcr==1 ? "end" : "start") << ";" - << "font-family:" << d->dc[d->level].font_name << ";" - << "\"\n"; - ts << " >"; - ts << escaped_text; - ts << ""; - ts << "\n"; - - *(d->outsvg) += ts.str().c_str(); g_free(escaped_text); free(ansi_text); @@ -3248,7 +3273,12 @@ Emf::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri ) if(emf_readdata(uri, &contents, &length))return(NULL); d.pDesc = NULL; - + + // set up the text reassembly system + if(!(d.tri = trinfo_init(NULL)))return(NULL); + (void) trinfo_load_ft_opts(d.tri, 1, + FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP, + FT_KERNING_UNSCALED); (void) myEnhMetaFileProc(contents,length, &d); free(contents); @@ -3282,6 +3312,8 @@ Emf::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri ) if(d.dc[i].font_name)free(d.dc[i].font_name); } + d.tri = trinfo_release_except_FC(d.tri); + return doc; } -- cgit v1.2.3 From 9f839a0f779d0cf4ad81fd93a5fbeb395cc41ec7 Mon Sep 17 00:00:00 2001 From: David Mathog <> Date: Wed, 13 Feb 2013 19:13:49 +0100 Subject: changes_2013_02_01b.patch EMF import: - Fix for EMF files with MODIFYWINDOWTRANSFORM records containing rotations - Add support for embedded PNG and JPG images - Fix for PowerPoint patterns EMF export: - Rotated images are exported using MODIFYWINDOWTRANSFORM records - Add output option to allow unrotated output (e.g. for PowerPoint 2003) - Don't rotate hatch and image fills on export libTERE: - Fix bug with misplaced words in LTR text (fix for RTL untested) (bzr r11668.1.50) --- src/extension/internal/emf-inout.cpp | 716 +++++++++++++++++++++-------------- 1 file changed, 422 insertions(+), 294 deletions(-) (limited to 'src/extension/internal/emf-inout.cpp') diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp index 06a64f875..eab47c5c8 100644 --- a/src/extension/internal/emf-inout.cpp +++ b/src/extension/internal/emf-inout.cpp @@ -117,11 +117,10 @@ static pixel_t * pixel_at (bitmap_t * bitmap, int x, int y) { return bitmap->pixels + bitmap->width * y + x; } - -/* Write "bitmap" to a PNG file specified by "path"; returns 0 on - success, non-zero on error. */ +/* Write "bitmap" to a PNG file specified by "path"; returns 0 on + success, non-zero on error. */ void my_png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) @@ -323,6 +322,7 @@ Emf::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar const *filena bool new_FixPPTDashLine = mod->get_param_bool("FixPPTDashLine"); // dashed line bug bool new_FixPPTGrad2Polys = mod->get_param_bool("FixPPTGrad2Polys"); // gradient bug bool new_FixPPTPatternAsHatch = mod->get_param_bool("FixPPTPatternAsHatch"); // force all patterns as standard EMF hatch + bool new_FixImageRot = mod->get_param_bool("FixImageRot"); // remove rotations on images TableGen( //possibly regenerate the unicode-convert tables mod->get_param_bool("TnrToSymbol"), @@ -335,6 +335,7 @@ Emf::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar const *filena ext->set_param_bool("FixPPTDashLine",new_FixPPTDashLine); ext->set_param_bool("FixPPTGrad2Polys",new_FixPPTGrad2Polys); ext->set_param_bool("FixPPTPatternAsHatch",new_FixPPTPatternAsHatch); + ext->set_param_bool("FixImageRot",new_FixImageRot); ext->set_param_bool("textToPath", new_val); emf_print_document_to_file(doc, filename); @@ -374,9 +375,7 @@ typedef struct emf_device_context { double ScaleInX, ScaleInY; double ScaleOutX, ScaleOutY; U_COLORREF textColor; - bool textColorSet; U_COLORREF bkColor; - bool bkColorSet; uint32_t textAlign; U_XFORM worldTransform; U_POINTL cur; @@ -423,6 +422,49 @@ typedef struct emf_callback_data { PEMF_OBJECT emf_obj; } EMF_CALLBACK_DATA, *PEMF_CALLBACK_DATA; +/* given the transformation matrix from worldTranform return the scale in the matrix part. Assumes that the + matrix is not used to skew, invert, or make another distorting transformation. */ +double current_scale(PEMF_CALLBACK_DATA d){ + double scale = d->dc[d->level].worldTransform.eM11 * d->dc[d->level].worldTransform.eM22 - + d->dc[d->level].worldTransform.eM12 * d->dc[d->level].worldTransform.eM21; + if(scale <= 0.0)scale=1.0; /* something is dreadfully wrong with the matrix, but do not crash over it */ + scale=sqrt(scale); + return(scale); +} + +/* given the transformation matrix from worldTranform and the current x,y position in inkscape coordinates, + generate an SVG transform that gives the same amount of rotation, no scaling, and maps x,y back onto x,y. This is used for + rotating objects when the location of at least one point in that object is known. Returns: + "matrix(a,b,c,d,e,f)" (WITH the double quotes) +*/ +static std::string current_matrix(PEMF_CALLBACK_DATA d, double x, double y, int useoffset){ + std::stringstream cxform; + double scale = current_scale(d); + cxform << "\"matrix("; + cxform << d->dc[d->level].worldTransform.eM11/scale; cxform << ","; + cxform << d->dc[d->level].worldTransform.eM12/scale; cxform << ","; + cxform << d->dc[d->level].worldTransform.eM21/scale; cxform << ","; + cxform << d->dc[d->level].worldTransform.eM22/scale; cxform << ","; + if(useoffset){ + /* for the "new" coordinates drop the worldtransform translations, not used here */ + double newx = x * d->dc[d->level].worldTransform.eM11/scale + y * d->dc[d->level].worldTransform.eM21/scale; + double newy = x * d->dc[d->level].worldTransform.eM12/scale + y * d->dc[d->level].worldTransform.eM22/scale; + cxform << x - newx; cxform << ","; + cxform << y - newy; + } + else { + cxform << "0,0"; + } + cxform << ")\""; + return(cxform.str()); +} + +/* given the transformation matrix from worldTranform return the rotation angle in radians. + counter clocwise from the x axis. */ +double current_rotation(PEMF_CALLBACK_DATA d){ + return -std::atan2(d->dc[d->level].worldTransform.eM12, d->dc[d->level].worldTransform.eM11); +} + /* Add another 100 blank slots to the hatches array. */ void enlarge_hatches(PEMF_CALLBACK_DATA d){ @@ -445,6 +487,7 @@ int in_hatches(PEMF_CALLBACK_DATA d, char *test){ */ uint32_t add_hatch(PEMF_CALLBACK_DATA d, uint32_t hatchType, U_COLORREF hatchColor){ char hatchname[64]; // big enough + char hrotname[64]; // big enough char tmpcolor[8]; uint32_t idx; @@ -457,15 +500,11 @@ uint32_t add_hatch(PEMF_CALLBACK_DATA d, uint32_t hatchType, U_COLORREF hatchCol switch(hatchType){ case U_HS_SOLIDTEXTCLR: case U_HS_DITHEREDTEXTCLR: - if(d->dc[d->level].textColorSet){ - sprintf(tmpcolor,"%6.6X",sethexcolor(d->dc[d->level].textColor)); - } + sprintf(tmpcolor,"%6.6X",sethexcolor(d->dc[d->level].textColor)); break; case U_HS_SOLIDBKCLR: case U_HS_DITHEREDBKCLR: - if(d->dc[d->level].bkColorSet){ - sprintf(tmpcolor,"%6.6X",sethexcolor(d->dc[d->level].bkColor)); - } + sprintf(tmpcolor,"%6.6X",sethexcolor(d->dc[d->level].bkColor)); break; default: break; @@ -476,7 +515,8 @@ uint32_t add_hatch(PEMF_CALLBACK_DATA d, uint32_t hatchType, U_COLORREF hatchCol // on export the background/text might not match at the time this is written, and the colors will shift. if(hatchType > U_HS_SOLIDCLR)hatchType = U_HS_SOLIDCLR; - sprintf(hatchname,"EMFhatch%d_%s",hatchType,tmpcolor); + // pattern defines hatch when there is no rotation. Load this one first. + sprintf(hatchname,"EMFhatch%d_%s",hatchType,tmpcolor); /* name of pattern BEFORE rotation*/ idx = in_hatches(d,hatchname); if(!idx){ // add it if not already present if(d->hatches.count == d->hatches.size){ enlarge_hatches(d); } @@ -492,14 +532,12 @@ uint32_t add_hatch(PEMF_CALLBACK_DATA d, uint32_t hatchType, U_COLORREF hatchCol *(d->defs) += " defs) += tmpcolor; *(d->defs) += "\" />\n"; - *(d->defs) += " \n"; break; case U_HS_VERTICAL: *(d->defs) += " patternUnits=\"userSpaceOnUse\" width=\"6\" height=\"6\" x=\"0\" y=\"0\" >\n"; *(d->defs) += " defs) += tmpcolor; *(d->defs) += "\" />\n"; - *(d->defs) += " \n"; break; case U_HS_FDIAGONAL: *(d->defs) += " patternUnits=\"userSpaceOnUse\" width=\"6\" height=\"6\" x=\"0\" y=\"0\" viewBox=\"0 0 6 6\" preserveAspectRatio=\"none\" >\n"; @@ -514,7 +552,6 @@ uint32_t add_hatch(PEMF_CALLBACK_DATA d, uint32_t hatchType, U_COLORREF hatchCol *(d->defs) += " defs) += hatchname; *(d->defs) += "\" transform=\"translate(-6,0)\"/>\n"; - *(d->defs) += " \n"; break; case U_HS_BDIAGONAL: *(d->defs) += " patternUnits=\"userSpaceOnUse\" width=\"6\" height=\"6\" x=\"0\" y=\"0\" viewBox=\"0 0 6 6\" preserveAspectRatio=\"none\" >\n"; @@ -529,26 +566,23 @@ uint32_t add_hatch(PEMF_CALLBACK_DATA d, uint32_t hatchType, U_COLORREF hatchCol *(d->defs) += " defs) += hatchname; *(d->defs) += "\" transform=\"translate(-6,0)\"/>\n"; - *(d->defs) += " \n"; break; case U_HS_CROSS: *(d->defs) += " patternUnits=\"userSpaceOnUse\" width=\"6\" height=\"6\" x=\"0\" y=\"0\" >\n"; *(d->defs) += " defs) += tmpcolor; *(d->defs) += "\" />\n"; - *(d->defs) += " \n"; break; case U_HS_DIAGCROSS: *(d->defs) += " patternUnits=\"userSpaceOnUse\" width=\"6\" height=\"6\" x=\"0\" y=\"0\" viewBox=\"0 0 6 6\" preserveAspectRatio=\"none\" >\n"; *(d->defs) += " defs) += hatchname; + sprintf(hrotname,"EMFhatch%d_%6.6X",U_HS_FDIAGONAL,sethexcolor(hatchColor)); // keep hatchname intact for later, hrotname will overwrite this + *(d->defs) += hrotname; *(d->defs) += "\" transform=\"translate(0,0)\"/>\n"; *(d->defs) += " defs) += hatchname; + sprintf(hrotname,"EMFhatch%d_%6.6X",U_HS_BDIAGONAL,sethexcolor(hatchColor)); + *(d->defs) += hrotname; *(d->defs) += "\" transform=\"translate(0,0)\"/>\n"; - *(d->defs) += " \n"; break; case U_HS_SOLIDCLR: case U_HS_DITHEREDCLR: @@ -562,11 +596,38 @@ uint32_t add_hatch(PEMF_CALLBACK_DATA d, uint32_t hatchType, U_COLORREF hatchCol *(d->defs) += tmpcolor; *(d->defs) += ";stroke:none"; *(d->defs) += "\" />\n"; - *(d->defs) += " \n"; break; } + *(d->defs) += " "; + *(d->defs) += " \n"; idx = d->hatches.count; } + + + // pattern allows the inner pattern to be rotated nicely, load this one second only if needed + // hatchname retained from above + sprintf(hrotname,"EMFrothatch%d_%s",hatchType,tmpcolor); /* name of pattern AFTER rotation*/ + if(current_rotation(d) >= 0.00001 || current_rotation(d) <= -0.00001){ /* some rotation, allow a little rounding error around 0 degrees */ + idx = in_hatches(d,hrotname); + if(!idx){ + if(d->hatches.count == d->hatches.size){ enlarge_hatches(d); } + d->hatches.strings[d->hatches.count++]=strdup(hrotname); + + *(d->defs) += "\n"; + *(d->defs) += " defs) += " id=\""; + *(d->defs) += hrotname; + *(d->defs) += "\"\n"; + *(d->defs) += " xlink:href=\"#"; + *(d->defs) += hatchname; + *(d->defs) += "\"\n"; + *(d->defs) += " patternTransform="; + *(d->defs) += current_matrix(d, 0.0, 0.0, 0); //j use offset 0,0 + *(d->defs) += " />\n"; + idx = d->hatches.count; + } + } + return(idx-1); } @@ -597,7 +658,9 @@ uint32_t add_image(PEMF_CALLBACK_DATA d, void *pEmr, uint32_t cbBits, uint32_t uint32_t idx; char imagename[64]; // big enough + char imrotname[64]; // big enough char xywh[64]; // big enough + int dibparams; MEMPNG mempng; // PNG in memory comes back in this mempng.buffer = NULL; @@ -609,7 +672,7 @@ uint32_t add_image(PEMF_CALLBACK_DATA d, void *pEmr, uint32_t cbBits, uint32_t if(!cbBits || !cbBmi || (iUsage != U_DIB_RGB_COLORS) || - !get_DIB_params( // this returns pointers and values, but allocates no memory + !(dibparams = get_DIB_params( // this returns pointers and values, but allocates no memory pEmr, offBits, offBmi, @@ -620,13 +683,14 @@ uint32_t add_image(PEMF_CALLBACK_DATA d, void *pEmr, uint32_t cbBits, uint32_t &height, &colortype, &invert - )){ + )) + ){ // U_EMRCREATEMONOBRUSH uses text/bk colors instead of what is in the color map. if(((PU_EMR)pEmr)->iType == U_EMR_CREATEMONOBRUSH){ if(numCt==2){ ct[0] = U_RGB2BGR(d->dc[d->level].textColor); - ct[1] = U_RGB2BGR(d->dc[d->level].bkColor); + ct[1] = U_RGB2BGR(d->dc[d->level].bkColor); } else { // createmonobrush renders on other platforms this way return(0xFFFFFFFF); @@ -654,7 +718,11 @@ uint32_t add_image(PEMF_CALLBACK_DATA d, void *pEmr, uint32_t cbBits, uint32_t } } gchar *base64String; - if(mempng.buffer){ + if(dibparams == U_BI_JPEG || dibparams==U_BI_PNG){ + base64String = g_base64_encode((guchar*) px, numCt ); + idx = in_images(d, (char *) base64String); + } + else if(mempng.buffer){ base64String = g_base64_encode((guchar*) mempng.buffer, mempng.size ); free(mempng.buffer); idx = in_images(d, (char *) base64String); @@ -666,7 +734,7 @@ uint32_t add_image(PEMF_CALLBACK_DATA d, void *pEmr, uint32_t cbBits, uint32_t base64String = strdup("iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAAA3NCSVQICAjb4U/gAAAALElEQVQImQXBQQ2AMAAAsUJQMSWI2H8qME1yMshojwrvGB8XcHKvR1XtOTc/8HENumHCsOMAAAAASUVORK5CYII="); idx = in_images(d, (char *) base64String); } - if(!idx){ // add it if not already present + if(!idx){ // add it if not already present - we looked at the actual data for comparison if(d->images.count == d->images.size){ enlarge_images(d); } idx = d->images.count; d->images.strings[d->images.count++]=strdup(base64String); @@ -680,7 +748,8 @@ uint32_t add_image(PEMF_CALLBACK_DATA d, void *pEmr, uint32_t cbBits, uint32_t *(d->defs) += "\"\n "; *(d->defs) += xywh; *(d->defs) += "\n"; - *(d->defs) += " xlink:href=\"data:image/png;base64,"; + if(dibparams == U_BI_JPEG){ *(d->defs) += " xlink:href=\"data:image/jpeg;base64,"; } + else { *(d->defs) += " xlink:href=\"data:image/png;base64,"; } *(d->defs) += base64String; *(d->defs) += "\"\n"; *(d->defs) += " />\n"; @@ -699,9 +768,46 @@ uint32_t add_image(PEMF_CALLBACK_DATA d, void *pEmr, uint32_t cbBits, uint32_t *(d->defs) += " xlink:href=\"#"; *(d->defs) += imagename; *(d->defs) += "\" />\n"; + *(d->defs) += " "; *(d->defs) += " \n"; } g_free(base64String); + + /* image allows the inner image to be rotated nicely, load this one second only if needed + imagename retained from above + Here comes a dreadful hack. How do we determine if this rotation of the base image has already + been loaded? The image names contain no identifying information, they are just numbered sequentially. + So the rotated name is EMFrotimage###_XXXXXX, where ### is the number of the referred to image, and + XXXX is the rotation in radians x 1000000 and truncated. That is then stored in BASE64 as the "image". + The corresponding SVG generated though is not for an image, but a reference to an image. + The name of the pattern MUST stil be EMFimage###_ref or output_style() will not be able to use it. + */ + if(current_rotation(d) >= 0.00001 || current_rotation(d) <= -0.00001){ /* some rotation, allow a little rounding error around 0 degrees */ + int tangle = round(current_rotation(d)*1000000.0); + sprintf(imrotname,"EMFrotimage%d_%d",idx-1,tangle); + base64String = g_base64_encode((guchar*) imrotname, strlen(imrotname) ); + idx = in_images(d, (char *) base64String); // scan for this "image" + if(!idx){ + if(d->images.count == d->images.size){ enlarge_images(d); } + idx = d->images.count; + d->images.strings[d->images.count++]=strdup(base64String); + sprintf(imrotname,"EMFimage%d",idx++); + + *(d->defs) += "\n"; + *(d->defs) += " defs) += " id=\""; + *(d->defs) += imrotname; + *(d->defs) += "_ref\"\n"; + *(d->defs) += " xlink:href=\"#"; + *(d->defs) += imagename; + *(d->defs) += "_ref\"\n"; + *(d->defs) += " patternTransform="; + *(d->defs) += current_matrix(d, 0.0, 0.0, 0); //j use offset 0,0 + *(d->defs) += " />\n"; + } + g_free(base64String); + } + return(idx-1); } @@ -936,16 +1042,20 @@ pix_to_y_point(PEMF_CALLBACK_DATA d, double px, double py) } static double -pix_to_size_point(PEMF_CALLBACK_DATA d, double px) +pix_to_abs_size(PEMF_CALLBACK_DATA d, double px) { - double ppx = px * (d->dc[d->level].ScaleInX ? d->dc[d->level].ScaleInX : 1.0) * d->D2PscaleX; - // double ppy = 0; - - double dx = ppx * d->dc[d->level].worldTransform.eM11; // + ppy * d->dc[d->level].worldTransform.eM21 - double dy = ppx * d->dc[d->level].worldTransform.eM12; // + ppy * d->dc[d->level].worldTransform.eM22 + double ppx = fabs(px * (d->dc[d->level].ScaleInX ? d->dc[d->level].ScaleInX : 1.0) * d->D2PscaleX * current_scale(d)); + return ppx; +} - double tmp = sqrt(dx * dx + dy * dy); - return tmp; +/* returns "x,y" (without the quotes) in inkscape coordinates for a pair of EMF x,y coordinates +*/ +static std::string pix_to_xy(PEMF_CALLBACK_DATA d, double x, double y){ + std::stringstream cxform; + cxform << pix_to_x_point(d,x,y); + cxform << ","; + cxform << pix_to_y_point(d,x,y); + return(cxform.str()); } @@ -1044,14 +1154,14 @@ select_pen(PEMF_CALLBACK_DATA d, int index) } else if (pEmr->lopn.lopnWidth.x) { int cur_level = d->level; d->level = d->emf_obj[index].level; - double pen_width = pix_to_size_point( d, pEmr->lopn.lopnWidth.x ); + double pen_width = pix_to_abs_size( d, pEmr->lopn.lopnWidth.x ); d->level = cur_level; d->dc[d->level].style.stroke_width.value = pen_width; } 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; int cur_level = d->level; d->level = d->emf_obj[index].level; - double pen_width = pix_to_size_point( d, 1 ); + double pen_width = pix_to_abs_size( d, 1 ); d->level = cur_level; d->dc[d->level].style.stroke_width.value = pen_width; } @@ -1088,7 +1198,7 @@ select_extpen(PEMF_CALLBACK_DATA d, int index) 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_size_point( d, pEmr->elp.elpStyleEntry[i] ); +// 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_dash.dash[i] = dash_length; @@ -1196,14 +1306,14 @@ select_extpen(PEMF_CALLBACK_DATA d, int index) if (pEmr->elp.elpWidth) { int cur_level = d->level; d->level = d->emf_obj[index].level; - double pen_width = pix_to_size_point( d, pEmr->elp.elpWidth ); + double pen_width = pix_to_abs_size( d, pEmr->elp.elpWidth ); d->level = cur_level; d->dc[d->level].style.stroke_width.value = pen_width; } 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; int cur_level = d->level; d->level = d->emf_obj[index].level; - double pen_width = pix_to_size_point( d, 1 ); + double pen_width = pix_to_abs_size( d, 1 ); d->level = cur_level; d->dc[d->level].style.stroke_width.value = pen_width; } @@ -1303,7 +1413,7 @@ select_font(PEMF_CALLBACK_DATA d, int index) */ int cur_level = d->level; d->level = d->emf_obj[index].level; - double font_size = pix_to_size_point( d, pEmr->elfw.elfLogFont.lfHeight ); + double font_size = pix_to_abs_size( d, pEmr->elfw.elfLogFont.lfHeight ); /* snap the font_size to the nearest 1/32nd of a point. (The size is converted from Pixels to points, snapped, and converted back.) See the notes where d->D2Pscale[XY] are set for the reason why. @@ -1408,12 +1518,12 @@ uint32_t *unknown_chars(size_t count){ \fn store SVG for an image given the pixmap and various coordinate information \param d \param pEmr - \param dl (double) destination left in inkscape pixels - \param dt (double) destination top in inkscape pixels - \param dr (double) destination right in inkscape pixels - \param db (double) destination bottom in inkscape pixels - \param sl (int) source left in pixels in the src image - \param st (int) source top in pixels in the src image + \param dx (double) destination x in inkscape pixels + \param dy (double) destination y in inkscape pixels + \param dw (double) destination width in inkscape pixels + \param dh (double) destination height in inkscape pixels + \param sx (int) source x in src image pixels + \param sy (int) source y in src image pixels \param iUsage \param offBits \param cbBits @@ -1421,89 +1531,109 @@ uint32_t *unknown_chars(size_t count){ \param cbBmi */ void common_image_extraction(PEMF_CALLBACK_DATA d, void *pEmr, - double dl, double dt, double dr, double db, int sl, int st, int sw, int sh, - uint32_t iUsage, uint32_t offBits, uint32_t cbBits, uint32_t offBmi, uint32_t cbBmi){ - SVGOStringStream tmp_image; - tmp_image << " y=\"" << dt << "\"\n x=\"" << dl <<"\"\n "; - - // The image ID is filled in much later when tmp_image is converted - - tmp_image << " xlink:href=\"data:image/png;base64,"; - - MEMPNG mempng; // PNG in memory comes back in this - mempng.buffer = NULL; - - char *rgba_px=NULL; // RGBA pixels - char *sub_px=NULL; // RGBA pixels, subarray - char *px=NULL; // DIB pixels - uint32_t width, height, colortype, numCt, invert; - PU_RGBQUAD ct = NULL; - if(!cbBits || - !cbBmi || - (iUsage != U_DIB_RGB_COLORS) || - !get_DIB_params( // this returns pointers and values, but allocates no memory - pEmr, - offBits, - offBmi, - &px, - &ct, - &numCt, - &width, - &height, - &colortype, - &invert - )){ - if(sw == 0 || sl == 0){ - sw = width; - sh = height; - } + double dx, double dy, double dw, double dh, int sx, int sy, int sw, int sh, + uint32_t iUsage, uint32_t offBits, uint32_t cbBits, uint32_t offBmi, uint32_t cbBmi){ - if(!DIB_to_RGBA( - px, // DIB pixel array - ct, // DIB color table - numCt, // DIB color table number of entries - &rgba_px, // U_RGBA pixel array (32 bits), created by this routine, caller must free. - width, // Width of pixel array - height, // Height of pixel array - colortype, // DIB BitCount Enumeration - numCt, // Color table used if not 0 - invert // If DIB rows are in opposite order from RGBA rows - ) && - rgba_px) - { - sub_px = RGBA_to_RGBA( - rgba_px, // full pixel array from DIB - width, // Width of pixel array - height, // Height of pixel array - sl,st, // starting point in pixel array - &sw,&sh // columns/rows to extract from the pixel array (output array size) - ); - - if(!sub_px)sub_px=rgba_px; - toPNG( // Get the image from the RGBA px into mempng - &mempng, - sw, sh, // size of the extracted pixel array - sub_px); - free(sub_px); - } - } - if(mempng.buffer){ - gchar *base64String = g_base64_encode((guchar*) mempng.buffer, mempng.size ); - free(mempng.buffer); - tmp_image << base64String ; - g_free(base64String); - } - else { - // insert a random 3x4 blotch otherwise - tmp_image << "iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAAA3NCSVQICAjb4U/gAAAALElEQVQImQXBQQ2AMAAAsUJQMSWI2H8qME1yMshojwrvGB8XcHKvR1XtOTc/8HENumHCsOMAAAAASUVORK5CYII="; - } - - tmp_image << "\"\n height=\"" << db-dt+1 << "\"\n width=\"" << dr-dl+1 << "\"\n"; + SVGOStringStream tmp_image; + int dibparams; - *(d->outsvg) += "\n\t outsvg) += tmp_image.str().c_str(); - *(d->outsvg) += "/> \n"; - *(d->path) = ""; + tmp_image << " y=\"" << dy << "\"\n x=\"" << dx <<"\"\n "; + + // The image ID is filled in much later when tmp_image is converted + + + MEMPNG mempng; // PNG in memory comes back in this + mempng.buffer = NULL; + + char *rgba_px=NULL; // RGBA pixels + char *sub_px=NULL; // RGBA pixels, subarray + char *px=NULL; // DIB pixels + uint32_t width, height, colortype, numCt, invert; + PU_RGBQUAD ct = NULL; + if(!cbBits || + !cbBmi || + (iUsage != U_DIB_RGB_COLORS) || + !(dibparams = get_DIB_params( // this returns pointers and values, but allocates no memory + pEmr, + offBits, + offBmi, + &px, + &ct, + &numCt, + &width, + &height, + &colortype, + &invert + )) + ){ + if(sw == 0 || sh == 0){ + sw = width; + sh = height; + } + + if(!DIB_to_RGBA( + px, // DIB pixel array + ct, // DIB color table + numCt, // DIB color table number of entries + &rgba_px, // U_RGBA pixel array (32 bits), created by this routine, caller must free. + width, // Width of pixel array + height, // Height of pixel array + colortype, // DIB BitCount Enumeration + numCt, // Color table used if not 0 + invert // If DIB rows are in opposite order from RGBA rows + ) && + rgba_px) + { + sub_px = RGBA_to_RGBA( + rgba_px, // full pixel array from DIB + width, // Width of pixel array + height, // Height of pixel array + sx,sy, // starting point in pixel array + &sw,&sh // columns/rows to extract from the pixel array (output array size) + ); + + if(!sub_px)sub_px=rgba_px; + toPNG( // Get the image from the RGBA px into mempng + &mempng, + sw, sh, // size of the extracted pixel array + sub_px); + free(sub_px); + } + } + gchar *base64String; + if(dibparams == U_BI_JPEG){ + tmp_image << " xlink:href=\"data:image/jpeg;base64,"; + base64String = g_base64_encode((guchar*) px, numCt ); + tmp_image << base64String ; + g_free(base64String); + } + else if(dibparams==U_BI_PNG){ + tmp_image << " xlink:href=\"data:image/png;base64,"; + base64String = g_base64_encode((guchar*) px, numCt ); + tmp_image << base64String ; + g_free(base64String); + } + else if(mempng.buffer){ + tmp_image << " xlink:href=\"data:image/png;base64,"; + gchar *base64String = g_base64_encode((guchar*) mempng.buffer, mempng.size ); + free(mempng.buffer); + tmp_image << base64String ; + g_free(base64String); + } + else { + tmp_image << " xlink:href=\"data:image/png;base64,"; + // insert a random 3x4 blotch otherwise + tmp_image << "iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAAA3NCSVQICAjb4U/gAAAALElEQVQImQXBQQ2AMAAAsUJQMSWI2H8qME1yMshojwrvGB8XcHKvR1XtOTc/8HENumHCsOMAAAAASUVORK5CYII="; + } + + tmp_image << "\"\n height=\"" << dh << "\"\n width=\"" << dw << "\"\n"; + + tmp_image << " transform=" << current_matrix(d, dx, dy, 1); // calculate appropriate offset + *(d->outsvg) += "\n\t outsvg) += tmp_image.str().c_str(); + + *(d->outsvg) += "/> \n"; + *(d->path) = ""; } /** @@ -1690,7 +1820,7 @@ std::cout << "BEFORE DRAW" // d->defs holds any defines which are read in. - tmp_outsvg << "\n\n\n"; // start of main body + tmp_outsvg << "\n\n\n"; // start of main body if (pEmr->nHandles) { d->n_obj = pEmr->nHandles; @@ -1724,15 +1854,13 @@ std::cout << "BEFORE DRAW" tmp_str << "\n\tM " << - pix_to_x_point( d, pEmr->aptl[0].x, pEmr->aptl[0].y ) << " " << - pix_to_y_point( d, pEmr->aptl[0].x, pEmr->aptl[0].y) << " "; + pix_to_xy( d, pEmr->aptl[0].x, pEmr->aptl[0].y) << " "; for (i=1; icptl; ) { tmp_str << "\n\tC "; for (j=0; j<3 && icptl; j++,i++) { tmp_str << - pix_to_x_point( d, pEmr->aptl[i].x, pEmr->aptl[i].y ) << " " << - pix_to_y_point( d, pEmr->aptl[i].x, pEmr->aptl[i].y ) << " "; + pix_to_xy( d, pEmr->aptl[i].x, pEmr->aptl[i].y ) << " "; } } @@ -1754,14 +1882,12 @@ std::cout << "BEFORE DRAW" tmp_str << "\n\tM " << - pix_to_x_point( d, pEmr->aptl[0].x, pEmr->aptl[0].y ) << " " << - pix_to_y_point( d, pEmr->aptl[0].x, pEmr->aptl[0].y ) << " "; + pix_to_xy( d, pEmr->aptl[0].x, pEmr->aptl[0].y ) << " "; for (i=1; icptl; i++) { tmp_str << "\n\tL " << - pix_to_x_point( d, pEmr->aptl[i].x, pEmr->aptl[i].y ) << " " << - pix_to_y_point( d, pEmr->aptl[i].x, pEmr->aptl[i].y ) << " "; + pix_to_xy( d, pEmr->aptl[i].x, pEmr->aptl[i].y ) << " "; } tmp_path << tmp_str.str().c_str(); @@ -1783,14 +1909,12 @@ std::cout << "BEFORE DRAW" tmp_str << "\n\tM " << - pix_to_x_point( d, pEmr->aptl[0].x, pEmr->aptl[0].y ) << " " << - pix_to_y_point( d, pEmr->aptl[0].x, pEmr->aptl[0].y ) << " "; + pix_to_xy( d, pEmr->aptl[0].x, pEmr->aptl[0].y ) << " "; for (i=1; icptl; i++) { tmp_str << "\n\tL " << - pix_to_x_point( d, pEmr->aptl[i].x, pEmr->aptl[i].y ) << " " << - pix_to_y_point( d, pEmr->aptl[i].x, pEmr->aptl[i].y ) << " "; + pix_to_xy( d, pEmr->aptl[i].x, pEmr->aptl[i].y ) << " "; } tmp_path << tmp_str.str().c_str(); @@ -1810,8 +1934,7 @@ std::cout << "BEFORE DRAW" tmp_path << "\n\tC "; for (j=0; j<3 && icptl; j++,i++) { tmp_path << - pix_to_x_point( d, pEmr->aptl[i].x, pEmr->aptl[i].y ) << " " << - pix_to_y_point( d, pEmr->aptl[i].x, pEmr->aptl[i].y ) << " "; + pix_to_xy( d, pEmr->aptl[i].x, pEmr->aptl[i].y ) << " "; } } @@ -1829,8 +1952,7 @@ std::cout << "BEFORE DRAW" for (i=0; icptl;i++) { tmp_path << "\n\tL " << - pix_to_x_point( d, pEmr->aptl[i].x, pEmr->aptl[i].y ) << " " << - pix_to_y_point( d, pEmr->aptl[i].x, pEmr->aptl[i].y ) << " "; + pix_to_xy( d, pEmr->aptl[i].x, pEmr->aptl[i].y ) << " "; } break; @@ -1855,14 +1977,12 @@ std::cout << "BEFORE DRAW" SVGOStringStream poly_path; poly_path << "\n\tM " << - pix_to_x_point( d, aptl[i].x, aptl[i].y ) << " " << - pix_to_y_point( d, aptl[i].x, aptl[i].y ) << " "; + pix_to_xy( d, aptl[i].x, aptl[i].y ) << " "; i++; for (j=1; jaPolyCounts[n] && icptl; j++) { poly_path << "\n\tL " << - pix_to_x_point( d, aptl[i].x, aptl[i].y ) << " " << - pix_to_y_point( d, aptl[i].x, aptl[i].y ) << " "; + pix_to_xy( d, aptl[i].x, aptl[i].y ) << " "; i++; } @@ -1967,7 +2087,6 @@ std::cout << "BEFORE DRAW" { dbg_str << "\n"; - tmp_outsvg << "\n"; tmp_outsvg << "\n"; *(d->outsvg) = *(d->outdef) + *(d->defs) + *(d->outsvg); OK=0; @@ -2043,7 +2162,6 @@ std::cout << "BEFORE DRAW" PU_EMRSETTEXTCOLOR pEmr = (PU_EMRSETTEXTCOLOR) lpEMFR; d->dc[d->level].textColor = pEmr->crColor; - d->dc[d->level].textColorSet = true; break; } case U_EMR_SETBKCOLOR: @@ -2052,7 +2170,6 @@ std::cout << "BEFORE DRAW" PU_EMRSETBKCOLOR pEmr = (PU_EMRSETBKCOLOR) lpEMFR; d->dc[d->level].bkColor = pEmr->crColor; - d->dc[d->level].bkColorSet = true; break; } case U_EMR_OFFSETCLIPRGN: dbg_str << "\n"; break; @@ -2068,8 +2185,7 @@ std::cout << "BEFORE DRAW" tmp_path << "\n\tM " << - pix_to_x_point( d, pEmr->ptl.x, pEmr->ptl.y ) << " " << - pix_to_y_point( d, pEmr->ptl.x, pEmr->ptl.y ) << " "; + pix_to_xy( d, pEmr->ptl.x, pEmr->ptl.y ) << " "; break; } case U_EMR_SETMETARGN: dbg_str << "\n"; break; @@ -2085,19 +2201,20 @@ std::cout << "BEFORE DRAW" break; rc_old = rc; - double l = pix_to_x_point( d, rc.left, rc.top ); - double t = pix_to_y_point( d, rc.left, rc.top ); - double r = pix_to_x_point( d, rc.right, rc.bottom ); - double b = pix_to_y_point( d, rc.right, rc.bottom ); + 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_rectangle; tmp_rectangle << "\nid) << "\" >"; + tmp_rectangle << "\nid=\"clipEmfPath" << ++(d->id) << "\" >"; tmp_rectangle << "\n"; + 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"; *(d->outdef) += tmp_rectangle.str().c_str(); @@ -2372,15 +2489,10 @@ std::cout << "BEFORE DRAW" PU_EMRELLIPSE pEmr = (PU_EMRELLIPSE) lpEMFR; U_RECTL rclBox = pEmr->rclBox; - double l = pix_to_x_point( d, rclBox.left, rclBox.top ); - double t = pix_to_y_point( d, rclBox.left, rclBox.top ); - double r = pix_to_x_point( d, rclBox.right, rclBox.bottom ); - double b = pix_to_y_point( d, rclBox.right, rclBox.bottom ); - - double cx = (l + r) / 2.0; - double cy = (t + b) / 2.0; - double rx = fabs(l - r) / 2.0; - double ry = fabs(t - b) / 2.0; + double cx = pix_to_x_point( d, (rclBox.left + rclBox.right)/2.0, (rclBox.bottom + rclBox.top)/2.0 ); + double cy = pix_to_y_point( d, (rclBox.left + rclBox.right)/2.0, (rclBox.bottom + rclBox.top)/2.0 ); + double rx = pix_to_abs_size( d, fabs(rclBox.right - rclBox.left )/2.0 ); + double ry = pix_to_abs_size( d, fabs(rclBox.top - rclBox.bottom)/2.0 ); SVGOStringStream tmp_ellipse; tmp_ellipse << "cx=\"" << cx << "\" "; @@ -2405,16 +2517,11 @@ std::cout << "BEFORE DRAW" PU_EMRRECTANGLE pEmr = (PU_EMRRECTANGLE) lpEMFR; U_RECTL rc = pEmr->rclBox; - double l = pix_to_x_point( d, rc.left, rc.top ); - double t = pix_to_y_point( d, rc.left, rc.top ); - double r = pix_to_x_point( d, rc.right, rc.bottom ); - double b = pix_to_y_point( d, rc.right, rc.bottom ); - SVGOStringStream tmp_rectangle; - tmp_rectangle << "\n\tM " << l << " " << t << " "; - tmp_rectangle << "\n\tL " << r << " " << t << " "; - tmp_rectangle << "\n\tL " << r << " " << b << " "; - tmp_rectangle << "\n\tL " << l << " " << b << " "; + tmp_rectangle << "\n\tM " << pix_to_xy( d, rc.left , rc.top ) << " "; + tmp_rectangle << "\n\tL " << pix_to_xy( d, rc.right, rc.top ) << " "; + tmp_rectangle << "\n\tL " << pix_to_xy( d, rc.right, rc.bottom ) << " "; + tmp_rectangle << "\n\tL " << pix_to_xy( d, rc.left, rc.bottom ) << " "; tmp_rectangle << "\n\tz"; d->mask |= emr_mask; @@ -2430,24 +2537,54 @@ std::cout << "BEFORE DRAW" U_RECTL rc = pEmr->rclBox; U_SIZEL corner = pEmr->szlCorner; double f = 4.*(sqrt(2) - 1)/3; - - double l = pix_to_x_point(d, rc.left, rc.top); - double t = pix_to_y_point(d, rc.left, rc.top); - double r = pix_to_x_point(d, rc.right, rc.bottom); - double b = pix_to_y_point(d, rc.right, rc.bottom); - double cnx = pix_to_size_point(d, corner.cx/2); - double cny = pix_to_size_point(d, corner.cy/2); - + double f1 = 1.0 - f; + double cnx = corner.cx/2; + double cny = corner.cy/2; + SVGOStringStream tmp_rectangle; - tmp_rectangle << "\n\tM " << l << ", " << t + cny << " "; - tmp_rectangle << "\n\tC " << l << ", " << t + (1-f)*cny << " " << l + (1-f)*cnx << ", " << t << " " << l + cnx << ", " << t << " "; - tmp_rectangle << "\n\tL " << r - cnx << ", " << t << " "; - tmp_rectangle << "\n\tC " << r - (1-f)*cnx << ", " << t << " " << r << ", " << t + (1-f)*cny << " " << r << ", " << t + cny << " "; - tmp_rectangle << "\n\tL " << r << ", " << b - cny << " "; - tmp_rectangle << "\n\tC " << r << ", " << b - (1-f)*cny << " " << r - (1-f)*cnx << ", " << b << " " << r - cnx << ", " << b << " "; - tmp_rectangle << "\n\tL " << l + cnx << ", " << b << " "; - tmp_rectangle << "\n\tC " << l + (1-f)*cnx << ", " << b << " " << l << ", " << b - (1-f)*cny << " " << l << ", " << b - cny << " "; - tmp_rectangle << "\n\tz"; + tmp_rectangle << "\n" + << " M " + << pix_to_xy(d, rc.left , rc.top + cny ) + << "\n"; + tmp_rectangle << " C " + << pix_to_xy(d, rc.left , rc.top + cny*f1 ) + << " " + << pix_to_xy(d, rc.left + cnx*f1 , rc.top ) + << " " + << pix_to_xy(d, rc.left + cnx , rc.top ) + << "\n"; + tmp_rectangle << " L " + << pix_to_xy(d, rc.right - cnx , rc.top ) + << "\n"; + tmp_rectangle << " C " + << pix_to_xy(d, rc.right - cnx*f1 , rc.top ) + << " " + << pix_to_xy(d, rc.right , rc.top + cny*f1 ) + << " " + << pix_to_xy(d, rc.right , rc.top + cny ) + << "\n"; + tmp_rectangle << " L " + << pix_to_xy(d, rc.right , rc.bottom - cny ) + << "\n"; + tmp_rectangle << " C " + << pix_to_xy(d, rc.right , rc.bottom - cny*f1 ) + << " " + << pix_to_xy(d, rc.right - cnx*f1 , rc.bottom ) + << " " + << pix_to_xy(d, rc.right - cnx , rc.bottom ) + << "\n"; + tmp_rectangle << " L " + << pix_to_xy(d, rc.left + cnx , rc.bottom ) + << "\n"; + tmp_rectangle << " C " + << pix_to_xy(d, rc.left + cnx*f1 , rc.bottom ) + << " " + << pix_to_xy(d, rc.left , rc.bottom - cny*f1 ) + << " " + << pix_to_xy(d, rc.left , rc.bottom - cny ) + << "\n"; + tmp_rectangle << " z\n"; + d->mask |= emr_mask; @@ -2460,13 +2597,15 @@ std::cout << "BEFORE DRAW" U_PAIRF center,start,end,size; int f1; int f2 = (d->arcdir == U_AD_COUNTERCLOCKWISE ? 0 : 1); - if(!emr_arc_points( lpEMFR, &f1, f2, ¢er, &start, &end, &size)){ - 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 ; - tmp_path << " 0 "; + int stat = emr_arc_points( lpEMFR, &f1, f2, ¢er, &start, &end, &size); + if(!stat){ + tmp_path << "\n\tM " << pix_to_xy(d, start.x, start.y); + tmp_path << " A " << pix_to_abs_size(d, size.x)/2.0 << "," << pix_to_abs_size(d, size.y)/2.0 ; + tmp_path << " "; + tmp_path << 180.0 * current_rotation(d)/M_PI; + tmp_path << " "; tmp_path << " " << f1 << "," << f2 << " "; - tmp_path << pix_to_x_point(d, end.x, end.y) << "," << pix_to_y_point(d, end.x, end.y)<< " "; - + tmp_path << pix_to_xy(d, end.x, end.y) << " \n"; d->mask |= emr_mask; } else { @@ -2481,11 +2620,13 @@ std::cout << "BEFORE DRAW" int f1; int f2 = (d->arcdir == U_AD_COUNTERCLOCKWISE ? 0 : 1); if(!emr_arc_points( lpEMFR, &f1, f2, ¢er, &start, &end, &size)){ - 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 ; - tmp_path << " 0 "; + tmp_path << "\n\tM " << pix_to_xy(d, start.x, start.y); + tmp_path << " A " << pix_to_abs_size(d, size.x)/2.0 << "," << pix_to_abs_size(d, size.y)/2.0 ; + tmp_path << " "; + tmp_path << 180.0 * current_rotation(d)/M_PI; + tmp_path << " "; tmp_path << " " << f1 << "," << f2 << " "; - tmp_path << pix_to_x_point(d, end.x, end.y) << "," << pix_to_y_point(d, end.x, end.y); + tmp_path << pix_to_xy(d, end.x, end.y) << " \n"; tmp_path << " z "; d->mask |= emr_mask; } @@ -2501,12 +2642,14 @@ std::cout << "BEFORE DRAW" int f1; int f2 = (d->arcdir == U_AD_COUNTERCLOCKWISE ? 0 : 1); if(!emr_arc_points( lpEMFR, &f1, f2, ¢er, &start, &end, &size)){ - 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; - tmp_path << " 0 "; + tmp_path << "\n\tM " << pix_to_xy(d, center.x, center.y); + tmp_path << "\n\tL " << pix_to_xy(d, start.x, start.y); + tmp_path << " A " << pix_to_abs_size(d, size.x)/2.0 << "," << pix_to_abs_size(d, size.y)/2.0 ; + tmp_path << " "; + tmp_path << 180.0 * current_rotation(d)/M_PI; + tmp_path << " "; tmp_path << " " << f1 << "," << f2 << " "; - tmp_path << pix_to_x_point(d, end.x, end.y) << "," << pix_to_y_point(d, end.x, end.y); + tmp_path << pix_to_xy(d, end.x, end.y) << " \n"; tmp_path << " z "; d->mask |= emr_mask; } @@ -2531,8 +2674,7 @@ std::cout << "BEFORE DRAW" tmp_path << "\n\tL " << - pix_to_x_point( d, pEmr->ptl.x, pEmr->ptl.y ) << " " << - pix_to_y_point( d, pEmr->ptl.x, pEmr->ptl.y ) << " "; + pix_to_xy( d, pEmr->ptl.x, pEmr->ptl.y ) << " "; break; } case U_EMR_ARCTO: @@ -2543,12 +2685,14 @@ std::cout << "BEFORE DRAW" int f2 = (d->arcdir == U_AD_COUNTERCLOCKWISE ? 0 : 1); if(!emr_arc_points( lpEMFR, &f1, f2, ¢er, &start, &end, &size)){ // draw a line from current position to start - 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 ; - tmp_path << " 0 "; + tmp_path << "\n\tL " << pix_to_xy(d, start.x, start.y); + tmp_path << "\n\tM " << pix_to_xy(d, start.x, start.y); + tmp_path << " A " << pix_to_abs_size(d, size.x)/2.0 << "," << pix_to_abs_size(d, size.y)/2.0 ; + tmp_path << " "; + tmp_path << 180.0 * current_rotation(d)/M_PI; + tmp_path << " "; tmp_path << " " << f1 << "," << f2 << " "; - tmp_path << pix_to_x_point(d, end.x, end.y) << "," << pix_to_y_point(d, end.x, end.y)<< " "; + tmp_path << pix_to_xy(d, end.x, end.y)<< " "; d->mask |= emr_mask; } @@ -2691,27 +2835,20 @@ std::cout << "BEFORE DRAW" dbg_str << "\n"; PU_EMRBITBLT pEmr = (PU_EMRBITBLT) lpEMFR; - double dl = pix_to_x_point( d, pEmr->Dest.x, pEmr->Dest.y); - double dt = pix_to_y_point( d, pEmr->Dest.x, pEmr->Dest.y); - double dr = pix_to_x_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y); - double db = pix_to_y_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y); - //source position within the bitmap, in pixels - int sl = pEmr->Src.x + pEmr->xformSrc.eDx; - int st = pEmr->Src.y + pEmr->xformSrc.eDy; - int sw = 0; // extract all of the image - int sh = 0; - if(sl<0)sl=0; - if(st<0)st=0; - // Treat all nonImage bitblts as a rectangular write. Definitely not correct, but at + // Treat all nonImage bitblts as a rectangular write. Definitely not correct, but at // least it leaves objects where the operations should have been. if (!pEmr->cbBmiSrc) { // should be an application of a DIBPATTERNBRUSHPT, use a solid color instead + int32_t dx = pEmr->Dest.x; + int32_t dy = pEmr->Dest.y; + int32_t dw = pEmr->cDest.x; + int32_t dh = pEmr->cDest.y; SVGOStringStream tmp_rectangle; - tmp_rectangle << "\n\tM " << dl << " " << dt << " "; - tmp_rectangle << "\n\tL " << dr << " " << dt << " "; - tmp_rectangle << "\n\tL " << dr << " " << db << " "; - tmp_rectangle << "\n\tL " << dl << " " << db << " "; + tmp_rectangle << "\n\tM " << pix_to_xy( d, dx, dy ) << " "; + tmp_rectangle << "\n\tL " << pix_to_xy( d, dx + dw, dy ) << " "; + tmp_rectangle << "\n\tL " << pix_to_xy( d, dx + dw, dy + dh ) << " "; + tmp_rectangle << "\n\tL " << pix_to_xy( d, dx, dy + dh ) << " "; tmp_rectangle << "\n\tz"; d->mask |= emr_mask; @@ -2721,7 +2858,18 @@ std::cout << "BEFORE DRAW" tmp_path << tmp_rectangle.str().c_str(); } else { - common_image_extraction(d,pEmr,dl,dt,dr,db,sl,st,sw,sh, + double dx = pix_to_x_point( d, pEmr->Dest.x, pEmr->Dest.y); + double dy = pix_to_y_point( d, pEmr->Dest.x, pEmr->Dest.y); + double dw = pix_to_abs_size( d, pEmr->cDest.x); + double dh = pix_to_abs_size( d, pEmr->cDest.y); + //source position within the bitmap, in pixels + int sx = pEmr->Src.x + pEmr->xformSrc.eDx; + int sy = pEmr->Src.y + pEmr->xformSrc.eDy; + int sw = 0; // extract all of the image + int sh = 0; + if(sx<0)sx=0; + if(sy<0)sy=0; + common_image_extraction(d,pEmr,dx,dy,dw,dh,sx,sy,sw,sh, pEmr->iUsageSrc, pEmr->offBitsSrc, pEmr->cbBitsSrc, pEmr->offBmiSrc, pEmr->cbBmiSrc); } break; @@ -2732,16 +2880,16 @@ std::cout << "BEFORE DRAW" PU_EMRSTRETCHBLT pEmr = (PU_EMRSTRETCHBLT) lpEMFR; // Always grab image, ignore modes. if (pEmr->cbBmiSrc) { - double dl = pix_to_x_point( d, pEmr->Dest.x, pEmr->Dest.y); - double dt = pix_to_y_point( d, pEmr->Dest.x, pEmr->Dest.y); - double dr = pix_to_x_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y); - double db = pix_to_y_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y); + double dx = pix_to_x_point( d, pEmr->Dest.x, pEmr->Dest.y); + double dy = pix_to_y_point( d, pEmr->Dest.x, pEmr->Dest.y); + double dw = pix_to_abs_size( d, pEmr->cDest.x); + double dh = pix_to_abs_size( d, pEmr->cDest.y); //source position within the bitmap, in pixels - int sl = pEmr->Src.x + pEmr->xformSrc.eDx; - int st = pEmr->Src.y + pEmr->xformSrc.eDy; + int sx = pEmr->Src.x + pEmr->xformSrc.eDx; + int sy = pEmr->Src.y + pEmr->xformSrc.eDy; int sw = pEmr->cSrc.x; // extract the specified amount of the image int sh = pEmr->cSrc.y; - common_image_extraction(d,pEmr,dl,dt,dr,db,sl,st,sw,sh, + common_image_extraction(d,pEmr,dx,dy,dw,dh,sx,sy,sw,sh, pEmr->iUsageSrc, pEmr->offBitsSrc, pEmr->cbBitsSrc, pEmr->offBmiSrc, pEmr->cbBmiSrc); } break; @@ -2752,15 +2900,15 @@ std::cout << "BEFORE DRAW" PU_EMRMASKBLT pEmr = (PU_EMRMASKBLT) lpEMFR; // Always grab image, ignore masks and modes. if (pEmr->cbBmiSrc) { - double dl = pix_to_x_point( d, pEmr->Dest.x, pEmr->Dest.y); - double dt = pix_to_y_point( d, pEmr->Dest.x, pEmr->Dest.y); - double dr = pix_to_x_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y); - double db = pix_to_y_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y); - int sl = pEmr->Src.x + pEmr->xformSrc.eDx; //source position within the bitmap, in pixels - int st = pEmr->Src.y + pEmr->xformSrc.eDy; + double dx = pix_to_x_point( d, pEmr->Dest.x, pEmr->Dest.y); + double dy = pix_to_y_point( d, pEmr->Dest.x, pEmr->Dest.y); + double dw = pix_to_abs_size( d, pEmr->cDest.x); + double dh = pix_to_abs_size( d, pEmr->cDest.y); + int sx = pEmr->Src.x + pEmr->xformSrc.eDx; //source position within the bitmap, in pixels + int sy = pEmr->Src.y + pEmr->xformSrc.eDy; int sw = 0; // extract all of the image int sh = 0; - common_image_extraction(d,pEmr,dl,dt,dr,db,sl,st,sw,sh, + common_image_extraction(d,pEmr,dx,dy,dw,dh,sx,sy,sw,sh, pEmr->iUsageSrc, pEmr->offBitsSrc, pEmr->cbBitsSrc, pEmr->offBmiSrc, pEmr->cbBmiSrc); } break; @@ -2776,15 +2924,15 @@ std::cout << "BEFORE DRAW" // user can sort out transparency later using Gimp, if need be. PU_EMRSTRETCHDIBITS pEmr = (PU_EMRSTRETCHDIBITS) lpEMFR; - double dl = pix_to_x_point( d, pEmr->Dest.x, pEmr->Dest.y ); - double dt = pix_to_y_point( d, pEmr->Dest.x, pEmr->Dest.y ); - double dr = pix_to_x_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y ); - double db = pix_to_y_point( d, pEmr->Dest.x + pEmr->cDest.x, pEmr->Dest.y + pEmr->cDest.y ); - int sl = pEmr->Src.x; //source position within the bitmap, in pixels - int st = pEmr->Src.y; + double dx = pix_to_x_point( d, pEmr->Dest.x, pEmr->Dest.y ); + double dy = pix_to_y_point( d, pEmr->Dest.x, pEmr->Dest.y ); + double dw = pix_to_abs_size( d, pEmr->cDest.x); + double dh = pix_to_abs_size( d, pEmr->cDest.y); + int sx = pEmr->Src.x; //source position within the bitmap, in pixels + int sy = pEmr->Src.y; int sw = pEmr->cSrc.x; // extract the specified amount of the image int sh = pEmr->cSrc.y; - common_image_extraction(d,pEmr,dl,dt,dr,db,sl,st,sw,sh, + common_image_extraction(d,pEmr,dx,dy,dw,dh,sx,sy,sw,sh, pEmr->iUsageSrc, pEmr->offBitsSrc, pEmr->cbBitsSrc, pEmr->offBmiSrc, pEmr->cbBmiSrc); dbg_str << "\n"; @@ -2919,6 +3067,7 @@ std::cout << "BEFORE DRAW" tsp.ldir = (d->dc[d->level].textAlign & U_TA_RTLREADING ? LDIR_RL : LDIR_LR); // language direction tsp.condensed = FC_WIDTH_NORMAL; // Not implemented well in libTERE (yet) tsp.ori = d->dc[d->level].style.baseline_shift.value; // For now orientation is always the same as escapement + tsp.ori += 180.0 * current_rotation(d)/ M_PI; // radians to degrees tsp.string = (uint8_t *) U_strdup(escaped_text); // this will be free'd much later at a trinfo_clear(). tsp.fs = d->dc[d->level].style.font_size.computed * 0.8; // Font size in points (void) trinfo_load_fontname(d->tri, (uint8_t *)d->dc[d->level].font_name, &tsp); @@ -2956,17 +3105,12 @@ std::cout << "BEFORE DRAW" d->mask |= emr_mask; - tmp_str << - "\n\tM " << - pix_to_x_point( d, apts[0].x, apts[0].y ) << " " << - pix_to_y_point( d, apts[0].x, apts[0].y ) << " "; + tmp_str << "\n\tM " << pix_to_xy( d, apts[0].x, apts[0].y ) << " "; for (i=1; icpts; ) { tmp_str << "\n\tC "; for (j=0; j<3 && icpts; j++,i++) { - tmp_str << - pix_to_x_point( d, apts[i].x, apts[i].y ) << " " << - pix_to_y_point( d, apts[i].x, apts[i].y ) << " "; + tmp_str << pix_to_xy( d, apts[i].x, apts[i].y ) << " "; } } @@ -2987,14 +3131,10 @@ std::cout << "BEFORE DRAW" d->mask |= emr_mask; // skip the first point? - tmp_poly << "\n\tM " << - pix_to_x_point( d, apts[first].x, apts[first].y ) << " " << - pix_to_y_point( d, apts[first].x, apts[first].y ) << " "; + tmp_poly << "\n\tM " << pix_to_xy( d, apts[first].x, apts[first].y ) << " "; for (i=first+1; icpts; i++) { - tmp_poly << "\n\tL " << - pix_to_x_point( d, apts[i].x, apts[i].y ) << " " << - pix_to_y_point( d, apts[i].x, apts[i].y ) << " "; + tmp_poly << "\n\tL " << pix_to_xy( d, apts[i].x, apts[i].y ) << " "; } tmp_path << tmp_poly.str().c_str(); @@ -3016,16 +3156,10 @@ std::cout << "BEFORE DRAW" d->mask |= emr_mask; - tmp_str << - "\n\tM " << - pix_to_x_point( d, apts[0].x, apts[0].y ) << " " << - pix_to_y_point( d, apts[0].x, apts[0].y ) << " "; + tmp_str << "\n\tM " << pix_to_xy( d, apts[0].x, apts[0].y ) << " "; for (i=1; icpts; i++) { - tmp_str << - "\n\tL " << - pix_to_x_point( d, apts[i].x, apts[i].y ) << " " << - pix_to_y_point( d, apts[i].x, apts[i].y ) << " "; + tmp_str << "\n\tL " << pix_to_xy( d, apts[i].x, apts[i].y ) << " "; } tmp_path << tmp_str.str().c_str(); @@ -3045,9 +3179,7 @@ std::cout << "BEFORE DRAW" for (i=0; icpts;) { tmp_path << "\n\tC "; for (j=0; j<3 && icpts; j++,i++) { - tmp_path << - pix_to_x_point( d, apts[i].x, apts[i].y ) << " " << - pix_to_y_point( d, apts[i].x, apts[i].y ) << " "; + tmp_path << pix_to_xy( d, apts[i].x, apts[i].y ) << " "; } } @@ -3064,10 +3196,7 @@ std::cout << "BEFORE DRAW" d->mask |= emr_mask; for (i=0; icpts;i++) { - tmp_path << - "\n\tL " << - pix_to_x_point( d, apts[i].x, apts[i].y ) << " " << - pix_to_y_point( d, apts[i].x, apts[i].y ) << " "; + tmp_path << "\n\tL " << pix_to_xy( d, apts[i].x, apts[i].y ) << " "; } break; @@ -3091,15 +3220,11 @@ std::cout << "BEFORE DRAW" for (n=0; nnPolys && icpts; n++) { SVGOStringStream poly_path; - poly_path << "\n\tM " << - pix_to_x_point( d, apts[i].x, apts[i].y ) << " " << - pix_to_y_point( d, apts[i].x, apts[i].y ) << " "; + poly_path << "\n\tM " << pix_to_xy( d, apts[i].x, apts[i].y ) << " "; i++; for (j=1; jaPolyCounts[n] && icpts; j++) { - poly_path << "\n\tL " << - pix_to_x_point( d, apts[i].x, apts[i].y ) << " " << - pix_to_y_point( d, apts[i].x, apts[i].y ) << " "; + poly_path << "\n\tL " << pix_to_xy( d, apts[i].x, apts[i].y ) << " "; i++; } @@ -3244,6 +3369,8 @@ Emf::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri ) d.dc[0].worldTransform.eDx = 0.0; d.dc[0].worldTransform.eDy = 0.0; d.dc[0].font_name = strdup("Arial"); // Default font, EMF spec says device can pick whatever it wants + d.dc[0].textColor = U_RGB(0, 0, 0); // default foreground color (black) + d.dc[0].bkColor = U_RGB(255, 255, 255); // default background color (white) if (uri == NULL) { return NULL; @@ -3349,6 +3476,7 @@ Emf::init (void) "false\n" "false\n" "false\n" + "false\n" "\n" ".emf\n" "image/x-emf\n" -- cgit v1.2.3 From e298aeb969151e2a523d3aaef737ac04b6e5b0e8 Mon Sep 17 00:00:00 2001 From: David Mathog <> Date: Fri, 8 Mar 2013 09:27:50 +0100 Subject: changes_2013_02_25a.patch New: WMF import/export implements WMF (Windows Metafile) read and write. Inkscape previously supported that through uniconverter, which was not very good with WMF files. The new version now has a complete wmf-print/wmf-inout implementation, analogous to the previous emf-print/emf-inout. This handles images, patterns, and various other goodies to the extent that WMF does. WMF is a bit primitive, many fields are only 16 bits, so it even more resolution sapping issues than does EMF. Given the choice, always use the latter format. (bzr r11668.1.52) --- src/extension/internal/emf-inout.cpp | 271 ++++++++++++----------------------- 1 file changed, 89 insertions(+), 182 deletions(-) (limited to 'src/extension/internal/emf-inout.cpp') diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp index eab47c5c8..d1587a5b6 100644 --- a/src/extension/internal/emf-inout.cpp +++ b/src/extension/internal/emf-inout.cpp @@ -26,7 +26,11 @@ # include "config.h" #endif -#define EMF_DRIVER +#include //This must precede text_reassemble.h or it blows up in pngconf.h when compiling +#include +#include +#include +#define EMF_DRIVER // work around for SPStyle issue #include "sp-root.h" #include "sp-path.h" #include "style.h" @@ -43,15 +47,9 @@ #include "document.h" #include "libunicode-convert/unicode-convert.h" -#include //This must precede text_reassemble.h or it blows up in pngconf.h when compiling -#include -#include -#include #include "emf-print.h" #include "emf-inout.h" -#include "uemf.h" -#include "text_reassemble.h" #define PRINT_EMF "org.inkscape.print.emf" @@ -87,33 +85,11 @@ Originally here, but moved up #include */ -/* A coloured pixel. */ - -typedef struct { - uint8_t red; - uint8_t green; - uint8_t blue; - uint8_t opacity; -} pixel_t; - -/* A picture. */ - -typedef struct { - pixel_t *pixels; - size_t width; - size_t height; -} bitmap_t; - -/* structure to store PNG image bytes */ -typedef struct { - char *buffer; - size_t size; -} MEMPNG, *PMEMPNG; /* Given "bitmap", this returns the pixel of bitmap at the point ("x", "y"). */ -static pixel_t * pixel_at (bitmap_t * bitmap, int x, int y) +pixel_t * Emf::pixel_at (bitmap_t * bitmap, int x, int y) { return bitmap->pixels + bitmap->width * y + x; } @@ -123,7 +99,7 @@ static pixel_t * pixel_at (bitmap_t * bitmap, int x, int y) success, non-zero on error. */ void -my_png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +Emf::my_png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) { PMEMPNG p=(PMEMPNG)png_get_io_ptr(png_ptr); @@ -143,7 +119,7 @@ my_png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) p->size += length; } -void toPNG(PMEMPNG accum, int width, int height, char *px){ +void Emf::toPNG(PMEMPNG accum, int width, int height, char *px){ bitmap_t bmstore; bitmap_t *bitmap=&bmstore; accum->buffer=NULL; // PNG constructed in memory will end up here, caller must free(). @@ -230,7 +206,7 @@ void toPNG(PMEMPNG accum, int width, int height, char *px){ /* convert an EMF RGB(A) color to 0RGB inverse of gethexcolor() in emf-print.cpp */ -uint32_t sethexcolor(U_COLORREF color){ +uint32_t Emf::sethexcolor(U_COLORREF color){ uint32_t out; out = (U_RGBAGetR(color) << 16) + @@ -261,8 +237,8 @@ Emf::check (Inkscape::Extension::Extension * /*module*/) } -static void -emf_print_document_to_file(SPDocument *doc, gchar const *filename) +void +Emf::print_document_to_file(SPDocument *doc, gchar const *filename) { Inkscape::Extension::Print *mod; SPPrintContext context; @@ -338,7 +314,7 @@ Emf::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar const *filena ext->set_param_bool("FixImageRot",new_FixImageRot); ext->set_param_bool("textToPath", new_val); - emf_print_document_to_file(doc, filename); + print_document_to_file(doc, filename); return; } @@ -346,85 +322,11 @@ Emf::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar const *filena enum drawmode {DRAW_PAINT, DRAW_PATTERN, DRAW_IMAGE}; // apply to either fill or stroke -typedef struct { - int type; - int level; - char *lpEMFR; -} EMF_OBJECT, *PEMF_OBJECT; - -typedef struct { - int size; // number of slots allocated in strings - int count; // number of slots used in strings - char **strings; // place to store strings -} EMF_STRINGS, *PEMF_STRINGS; - -typedef struct emf_device_context { - struct SPStyle style; - char *font_name; - 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 - bool fill_set; - int fill_mode; // enumeration from drawmode, not used if fill_set is not True - int fill_idx; // used with DRAW_PATTERN and DRAW_IMAGE to return the appropriate fill - - U_SIZEL sizeWnd; - U_SIZEL sizeView; - U_POINTL winorg; - U_POINTL vieworg; - double ScaleInX, ScaleInY; - double ScaleOutX, ScaleOutY; - U_COLORREF textColor; - U_COLORREF bkColor; - uint32_t textAlign; - U_XFORM worldTransform; - U_POINTL cur; -} EMF_DEVICE_CONTEXT, *PEMF_DEVICE_CONTEXT; - -#define EMF_MAX_DC 128 - - -typedef struct emf_callback_data { - Glib::ustring *outsvg; - Glib::ustring *path; - Glib::ustring *outdef; - Glib::ustring *defs; - - EMF_DEVICE_CONTEXT dc[EMF_MAX_DC+1]; // FIXME: This should be dynamic.. - int level; - - double E2IdirY; // EMF Y direction relative to Inkscape Y direction. Will be negative for MM_LOMETRIC etc. - double D2PscaleX,D2PscaleY; // EMF device to Inkscape Page scale. - float MM100InX, MM100InY; // size of the drawing in hundredths of a millimeter - float PixelsInX, PixelsInY; // size of the drawing, in EMF device pixels - float PixelsOutX, PixelsOutY;// size of the drawing, in Inkscape pixels - double ulCornerInX,ulCornerInY; // Upper left corner, from header rclBounds, in logical units - double ulCornerOutX,ulCornerOutY; // Upper left corner, in Inkscape pixels - uint32_t mask; // Draw properties - int arcdir; //U_AD_COUNTERCLOCKWISE 1 or U_AD_CLOCKWISE 2 - - uint32_t dwRop2; // Binary raster operation, 0 if none (use brush/pen unmolested) - uint32_t dwRop3; // Ternary raster operation, 0 if none (use brush/pen unmolested) - - 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 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. - TR_INFO *tri; // Text Reassembly data structure - - int n_obj; - PEMF_OBJECT emf_obj; -} EMF_CALLBACK_DATA, *PEMF_CALLBACK_DATA; /* given the transformation matrix from worldTranform return the scale in the matrix part. Assumes that the matrix is not used to skew, invert, or make another distorting transformation. */ -double current_scale(PEMF_CALLBACK_DATA d){ +double Emf::current_scale(PEMF_CALLBACK_DATA d){ double scale = d->dc[d->level].worldTransform.eM11 * d->dc[d->level].worldTransform.eM22 - d->dc[d->level].worldTransform.eM12 * d->dc[d->level].worldTransform.eM21; if(scale <= 0.0)scale=1.0; /* something is dreadfully wrong with the matrix, but do not crash over it */ @@ -437,7 +339,7 @@ double current_scale(PEMF_CALLBACK_DATA d){ rotating objects when the location of at least one point in that object is known. Returns: "matrix(a,b,c,d,e,f)" (WITH the double quotes) */ -static std::string current_matrix(PEMF_CALLBACK_DATA d, double x, double y, int useoffset){ +std::string Emf::current_matrix(PEMF_CALLBACK_DATA d, double x, double y, int useoffset){ std::stringstream cxform; double scale = current_scale(d); cxform << "\"matrix("; @@ -461,20 +363,20 @@ static std::string current_matrix(PEMF_CALLBACK_DATA d, double x, double y, int /* given the transformation matrix from worldTranform return the rotation angle in radians. counter clocwise from the x axis. */ -double current_rotation(PEMF_CALLBACK_DATA d){ +double Emf::current_rotation(PEMF_CALLBACK_DATA d){ return -std::atan2(d->dc[d->level].worldTransform.eM12, d->dc[d->level].worldTransform.eM11); } /* Add another 100 blank slots to the hatches array. */ -void enlarge_hatches(PEMF_CALLBACK_DATA d){ +void Emf::enlarge_hatches(PEMF_CALLBACK_DATA d){ d->hatches.size += 100; d->hatches.strings = (char **) realloc(d->hatches.strings,d->hatches.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 in_hatches(PEMF_CALLBACK_DATA d, char *test){ +int Emf::in_hatches(PEMF_CALLBACK_DATA d, char *test){ int i; for(i=0; ihatches.count; i++){ if(strcmp(test,d->hatches.strings[i])==0)return(i+1); @@ -485,7 +387,7 @@ int in_hatches(PEMF_CALLBACK_DATA d, char *test){ /* (Conditionally) add a hatch. If a matching hatch already exists nothing happens. If one does not exist it is added to the hatches list and also entered into . */ -uint32_t add_hatch(PEMF_CALLBACK_DATA d, uint32_t hatchType, U_COLORREF hatchColor){ +uint32_t Emf::add_hatch(PEMF_CALLBACK_DATA d, uint32_t hatchType, U_COLORREF hatchColor){ char hatchname[64]; // big enough char hrotname[64]; // big enough char tmpcolor[8]; @@ -633,14 +535,14 @@ uint32_t add_hatch(PEMF_CALLBACK_DATA d, uint32_t hatchType, U_COLORREF hatchCol /* Add another 100 blank slots to the images array. */ -void enlarge_images(PEMF_CALLBACK_DATA d){ +void Emf::enlarge_images(PEMF_CALLBACK_DATA d){ d->images.size += 100; d->images.strings = (char **) realloc(d->images.strings,d->images.size + sizeof(char *)); } /* See if the image string is already in the list. If it is return its position (1->n, not 1-n-1) */ -int in_images(PEMF_CALLBACK_DATA d, char *test){ +int Emf::in_images(PEMF_CALLBACK_DATA d, char *test){ int i; for(i=0; iimages.count; i++){ if(strcmp(test,d->images.strings[i])==0)return(i+1); @@ -654,7 +556,8 @@ int in_images(PEMF_CALLBACK_DATA d, char *test){ U_EMRCREATEMONOBRUSH records only work when the bitmap is monochrome. If we hit one that isn't set idx to 2^32-1 and let the caller handle it. */ -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){ +uint32_t Emf::add_image(PEMF_CALLBACK_DATA d, void *pEmr, uint32_t cbBits, uint32_t cbBmi, + uint32_t iUsage, uint32_t offBits, uint32_t offBmi){ uint32_t idx; char imagename[64]; // big enough @@ -665,10 +568,11 @@ uint32_t add_image(PEMF_CALLBACK_DATA d, void *pEmr, uint32_t cbBits, uint32_t MEMPNG mempng; // PNG in memory comes back in this mempng.buffer = NULL; - char *rgba_px=NULL; // RGBA pixels - char *px=NULL; // DIB pixels + char *rgba_px = NULL; // RGBA pixels + const char *px = NULL; // DIB pixels + const U_RGBQUAD *ct = NULL; // DIB color table + U_RGBQUAD ct2[2]; uint32_t width, height, colortype, numCt, invert; - PU_RGBQUAD ct = NULL; if(!cbBits || !cbBmi || (iUsage != U_DIB_RGB_COLORS) || @@ -677,7 +581,7 @@ uint32_t add_image(PEMF_CALLBACK_DATA d, void *pEmr, uint32_t cbBits, uint32_t offBits, offBmi, &px, - &ct, + (const U_RGBQUAD **) &ct, &numCt, &width, &height, @@ -688,9 +592,10 @@ uint32_t add_image(PEMF_CALLBACK_DATA d, void *pEmr, uint32_t cbBits, uint32_t // U_EMRCREATEMONOBRUSH uses text/bk colors instead of what is in the color map. if(((PU_EMR)pEmr)->iType == U_EMR_CREATEMONOBRUSH){ - if(numCt==2){ - ct[0] = U_RGB2BGR(d->dc[d->level].textColor); - ct[1] = U_RGB2BGR(d->dc[d->level].bkColor); + if(numCt==2){ + ct2[0] = U_RGB2BGR(d->dc[d->level].textColor); + ct2[1] = U_RGB2BGR(d->dc[d->level].bkColor); + ct = &ct2[0]; } else { // createmonobrush renders on other platforms this way return(0xFFFFFFFF); @@ -812,8 +717,8 @@ uint32_t add_image(PEMF_CALLBACK_DATA d, void *pEmr, uint32_t cbBits, uint32_t } -static void -output_style(PEMF_CALLBACK_DATA d, int iType) +void +Emf::output_style(PEMF_CALLBACK_DATA d, int iType) { // SVGOStringStream tmp_id; SVGOStringStream tmp_style; @@ -1000,8 +905,8 @@ output_style(PEMF_CALLBACK_DATA d, int iType) } -static double -_pix_x_to_point(PEMF_CALLBACK_DATA d, double px) +double +Emf::_pix_x_to_point(PEMF_CALLBACK_DATA d, double px) { double scale = (d->dc[d->level].ScaleInX ? d->dc[d->level].ScaleInX : 1.0); double tmp; @@ -1010,8 +915,8 @@ _pix_x_to_point(PEMF_CALLBACK_DATA d, double px) return(tmp); } -static double -_pix_y_to_point(PEMF_CALLBACK_DATA d, double py) +double +Emf::_pix_y_to_point(PEMF_CALLBACK_DATA d, double py) { double scale = (d->dc[d->level].ScaleInY ? d->dc[d->level].ScaleInY : 1.0); double tmp; @@ -1021,8 +926,8 @@ _pix_y_to_point(PEMF_CALLBACK_DATA d, double py) } -static double -pix_to_x_point(PEMF_CALLBACK_DATA d, double px, double py) +double +Emf::pix_to_x_point(PEMF_CALLBACK_DATA d, double px, double py) { double wpx = px * d->dc[d->level].worldTransform.eM11 + py * d->dc[d->level].worldTransform.eM21 + d->dc[d->level].worldTransform.eDx; double x = _pix_x_to_point(d, wpx); @@ -1030,8 +935,8 @@ pix_to_x_point(PEMF_CALLBACK_DATA d, double px, double py) return x; } -static double -pix_to_y_point(PEMF_CALLBACK_DATA d, double px, double py) +double +Emf::pix_to_y_point(PEMF_CALLBACK_DATA d, double px, double py) { double wpy = px * d->dc[d->level].worldTransform.eM12 + py * d->dc[d->level].worldTransform.eM22 + d->dc[d->level].worldTransform.eDy; @@ -1041,8 +946,8 @@ pix_to_y_point(PEMF_CALLBACK_DATA d, double px, double py) } -static double -pix_to_abs_size(PEMF_CALLBACK_DATA d, double px) +double +Emf::pix_to_abs_size(PEMF_CALLBACK_DATA d, double px) { double ppx = fabs(px * (d->dc[d->level].ScaleInX ? d->dc[d->level].ScaleInX : 1.0) * d->D2PscaleX * current_scale(d)); return ppx; @@ -1050,7 +955,7 @@ pix_to_abs_size(PEMF_CALLBACK_DATA d, double px) /* returns "x,y" (without the quotes) in inkscape coordinates for a pair of EMF x,y coordinates */ -static std::string pix_to_xy(PEMF_CALLBACK_DATA d, double x, double y){ +std::string Emf::pix_to_xy(PEMF_CALLBACK_DATA d, double x, double y){ std::stringstream cxform; cxform << pix_to_x_point(d,x,y); cxform << ","; @@ -1059,8 +964,8 @@ static std::string pix_to_xy(PEMF_CALLBACK_DATA d, double x, double y){ } -static void -select_pen(PEMF_CALLBACK_DATA d, int index) +void +Emf::select_pen(PEMF_CALLBACK_DATA d, int index) { PU_EMRCREATEPEN pEmr = NULL; @@ -1174,8 +1079,8 @@ select_pen(PEMF_CALLBACK_DATA d, int index) } -static void -select_extpen(PEMF_CALLBACK_DATA d, int index) +void +Emf::select_extpen(PEMF_CALLBACK_DATA d, int index) { PU_EMREXTCREATEPEN pEmr = NULL; @@ -1350,8 +1255,8 @@ select_extpen(PEMF_CALLBACK_DATA d, int index) } -static void -select_brush(PEMF_CALLBACK_DATA d, int index) +void +Emf::select_brush(PEMF_CALLBACK_DATA d, int index) { uint32_t tidx; uint32_t iType; @@ -1396,8 +1301,8 @@ select_brush(PEMF_CALLBACK_DATA d, int index) } -static void -select_font(PEMF_CALLBACK_DATA d, int index) +void +Emf::select_font(PEMF_CALLBACK_DATA d, int index) { PU_EMREXTCREATEFONTINDIRECTW pEmr = NULL; @@ -1455,8 +1360,8 @@ select_font(PEMF_CALLBACK_DATA d, int index) 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 } -static void -delete_object(PEMF_CALLBACK_DATA d, int index) +void +Emf::delete_object(PEMF_CALLBACK_DATA d, int index) { if (index >= 0 && index < d->n_obj) { d->emf_obj[index].type = 0; @@ -1471,8 +1376,8 @@ delete_object(PEMF_CALLBACK_DATA d, int index) } -static void -insert_object(PEMF_CALLBACK_DATA d, int index, int type, PU_ENHMETARECORD pObj) +void +Emf::insert_object(PEMF_CALLBACK_DATA d, int index, int type, PU_ENHMETARECORD pObj) { if (index >= 0 && index < d->n_obj) { delete_object(d, index); @@ -1485,7 +1390,7 @@ insert_object(PEMF_CALLBACK_DATA d, int index, int type, PU_ENHMETARECORD pObj) /* Identify probable Adobe Illustrator produced EMF files, which do strange things with the scaling. The few so far observed all had this format. */ -int AI_hack(PU_EMRHEADER pEmr){ +int Emf::AI_hack(PU_EMRHEADER pEmr){ int ret=0; char *ptr; ptr = (char *)pEmr; @@ -1506,7 +1411,7 @@ int AI_hack(PU_EMRHEADER pEmr){ \fn create a UTF-32LE buffer and fill it with UNICODE unknown character \param count number of copies of the Unicode unknown character to fill with */ -uint32_t *unknown_chars(size_t count){ +uint32_t *Emf::unknown_chars(size_t count){ uint32_t *res = (uint32_t *) malloc(sizeof(uint32_t) * (count + 1)); if(!res)throw "Inkscape fatal memory allocation error - cannot continue"; for(uint32_t i=0; icptl; ) { tmp_str << "\n\tC "; for (j=0; j<3 && icptl; j++,i++) { - tmp_str << - pix_to_xy( d, pEmr->aptl[i].x, pEmr->aptl[i].y ) << " "; + tmp_str << pix_to_xy( d, pEmr->aptl[i].x, pEmr->aptl[i].y) << " "; } } @@ -1976,13 +1883,11 @@ std::cout << "BEFORE DRAW" for (n=0; nnPolys && icptl; n++) { SVGOStringStream poly_path; - poly_path << "\n\tM " << - pix_to_xy( d, aptl[i].x, aptl[i].y ) << " "; + poly_path << "\n\tM " << pix_to_xy( d, aptl[i].x, aptl[i].y) << " "; i++; for (j=1; jaPolyCounts[n] && icptl; j++) { - poly_path << "\n\tL " << - pix_to_xy( d, aptl[i].x, aptl[i].y ) << " "; + poly_path << "\n\tL " << pix_to_xy( d, aptl[i].x, aptl[i].y) << " "; i++; } @@ -2184,8 +2089,7 @@ std::cout << "BEFORE DRAW" d->dc[d->level].cur = pEmr->ptl; tmp_path << - "\n\tM " << - pix_to_xy( d, pEmr->ptl.x, pEmr->ptl.y ) << " "; + "\n\tM " << pix_to_xy( d, pEmr->ptl.x, pEmr->ptl.y ) << " "; break; } case U_EMR_SETMETARGN: dbg_str << "\n"; break; @@ -2673,8 +2577,7 @@ std::cout << "BEFORE DRAW" d->mask |= emr_mask; tmp_path << - "\n\tL " << - pix_to_xy( d, pEmr->ptl.x, pEmr->ptl.y ) << " "; + "\n\tL " << pix_to_xy( d, pEmr->ptl.x, pEmr->ptl.y) << " "; break; } case U_EMR_ARCTO: @@ -3009,8 +2912,8 @@ std::cout << "BEFORE DRAW" msdepua(dup_wt); //convert everything in Microsoft's private use area. For Symbol, Wingdings, Dingbats if(NonToUnicode(dup_wt, d->dc[d->level].font_name)){ - g_free(d->dc[d->level].font_name); - d->dc[d->level].font_name = g_strdup("Times New Roman"); + free(d->dc[d->level].font_name); + d->dc[d->level].font_name = strdup("Times New Roman"); } char *ansi_text; @@ -3028,9 +2931,12 @@ std::cout << "BEFORE DRAW" gchar *escaped_text = g_markup_escape_text(ansi_text, -1); - tsp.x = x*0.8; // TERE expects sizes in points. - tsp.y = y*0.8; - memcpy(&tsp.color, &d->dc[d->level].textColor, sizeof(uint32_t)); //It is already an RGBA binary value, but compiler is picky about types + tsp.x = x*0.8; // TERE expects sizes in points. + tsp.y = y*0.8; + tsp.color.Red = d->dc[d->level].textColor.Red; + tsp.color.Green = d->dc[d->level].textColor.Green; + tsp.color.Blue = d->dc[d->level].textColor.Blue; + tsp.color.Reserved = 0; switch(d->dc[d->level].style.font_style.value){ case SP_CSS_FONT_STYLE_OBLIQUE: tsp.italics = FC_SLANT_OBLIQUE; break; @@ -3179,7 +3085,7 @@ std::cout << "BEFORE DRAW" for (i=0; icpts;) { tmp_path << "\n\tC "; for (j=0; j<3 && icpts; j++,i++) { - tmp_path << pix_to_xy( d, apts[i].x, apts[i].y ) << " "; + tmp_path << pix_to_xy( d, apts[i].x, apts[i].y) << " "; } } @@ -3196,7 +3102,7 @@ std::cout << "BEFORE DRAW" d->mask |= emr_mask; for (i=0; icpts;i++) { - tmp_path << "\n\tL " << pix_to_xy( d, apts[i].x, apts[i].y ) << " "; + tmp_path << "\n\tL " << pix_to_xy( d, apts[i].x, apts[i].y) << " "; } break; @@ -3220,11 +3126,11 @@ std::cout << "BEFORE DRAW" for (n=0; nnPolys && icpts; n++) { SVGOStringStream poly_path; - poly_path << "\n\tM " << pix_to_xy( d, apts[i].x, apts[i].y ) << " "; + poly_path << "\n\tM " << pix_to_xy( d, apts[i].x, apts[i].y) << " "; i++; for (j=1; jaPolyCounts[n] && icpts; j++) { - poly_path << "\n\tL " << pix_to_xy( d, apts[i].x, apts[i].y ) << " "; + poly_path << "\n\tL " << pix_to_xy( d, apts[i].x, apts[i].y) << " "; i++; } @@ -3344,7 +3250,7 @@ typedef struct } APMHEADER, *PAPMHEADER; #pragma pack( pop ) -void free_emf_strings(EMF_STRINGS name){ +void Emf::free_emf_strings(EMF_STRINGS name){ if(name.count){ for(int i=0; i< name.count; i++){ free(name.strings[i]); } free(name.strings); @@ -3356,7 +3262,8 @@ Emf::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri ) { EMF_CALLBACK_DATA d; - memset(&d, 0, sizeof(d)); +// memset(&d, 0, sizeof(d)); + memset(&d, 0, sizeof(EMF_CALLBACK_DATA)); for(int i = 0; i < EMF_MAX_DC+1; i++){ // be sure all values and pointers are empty to start with memset(&(d.dc[i]),0,sizeof(EMF_DEVICE_CONTEXT)); -- cgit v1.2.3 From 2bddd4ed1da6759f2ee741f47261b9f431cba7f8 Mon Sep 17 00:00:00 2001 From: David Mathog <> Date: Tue, 19 Mar 2013 03:22:04 +0100 Subject: changes_2013_03_18c.patch This set of patches does the following: 1. Fixed a typo ( where "+ sizeof()" should have been "* sizeof()") which caused a memory problem for EMF/WMF files with very large numbers of hatches. 2. Added support for background mode, background color, and textcolor in hatches. EMF/WMF files change these parameters but the change may be silent until many records later. This has the odd effect that a stroke or fill may be defined (in SVG) and then it is ignored later and replaced with one with a different background color. 3. Fixed WMF output so that it wasn't adding +1 to the number of pixels for Width and Height. (Allows WMF files to go through several cycles of save as, open without changing sizes.) 4. Cleaned up indenting of [ew]mf-{print|inout}.* files, to make them compliant with the inkscape standard. All indents are (I hope) now 4*i deep. 5. Added underline/strikeout support for text read from EMF/WMF files. Inkscape itself cannot currently render this, but it makes it into the SVG, and it shows up correctly when that SVG is read by Opera. 6. Took out all the "throw" calls, replacing them with g_error(). If this comes up again in debugging a define can be used to remap the g_error to temporarily reintroduce the throw so that gdb can catch them. 7. Took out the "%6lf" format changes from patch 11724, retained the "127" length limit in the same sscanf. 8. Put the C type casts back in, reversing those changes from patch 11724. This is a style issue, and I could not find clear guidance for which way to go. (Nor a good rationale for keeping the lengthier C++ syntax.) So I reviewed a large swath of other inkscape code to see if there was a trend and found a very large number of other sections that were using C style casts instead of the more verbose C++ forms. So I kept it the way it has been. 9. The locale changes from 11724 were of course retained. (bzr r11668.1.59) --- src/extension/internal/emf-inout.cpp | 2143 ++++++++++++++++++---------------- 1 file changed, 1136 insertions(+), 1007 deletions(-) (limited to 'src/extension/internal/emf-inout.cpp') diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp index d1587a5b6..7a5757235 100644 --- a/src/extension/internal/emf-inout.cpp +++ b/src/extension/internal/emf-inout.cpp @@ -67,7 +67,7 @@ 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; -/** Construct a PNG in memory from an RGB from the EMF file +/** Construct a PNG in memory from an RGB from the EMF file from: http://www.lemoda.net/c/write-png/ @@ -84,10 +84,10 @@ Originally here, but moved up #include #include */ - -/* Given "bitmap", this returns the pixel of bitmap at the point - ("x", "y"). */ + +/* Given "bitmap", this returns the pixel of bitmap at the point + ("x", "y"). */ pixel_t * Emf::pixel_at (bitmap_t * bitmap, int x, int y) { @@ -95,87 +95,86 @@ pixel_t * Emf::pixel_at (bitmap_t * bitmap, int x, int y) } -/* Write "bitmap" to a PNG file specified by "path"; returns 0 on - success, non-zero on error. */ +/* Write "bitmap" to a PNG file specified by "path"; returns 0 on + success, non-zero on error. */ void Emf::my_png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) { - PMEMPNG p=(PMEMPNG)png_get_io_ptr(png_ptr); - - size_t nsize = p->size + length; - - /* allocate or grow buffer */ - if(p->buffer) - p->buffer = (char *) realloc(p->buffer, nsize); - else - p->buffer = (char *) malloc(nsize); - - if(!p->buffer) - png_error(png_ptr, "Write Error"); - - /* copy new bytes to end of buffer */ - memcpy(p->buffer + p->size, data, length); - p->size += length; + PMEMPNG p=(PMEMPNG)png_get_io_ptr(png_ptr); + + size_t nsize = p->size + length; + + /* allocate or grow buffer */ + if(p->buffer){ p->buffer = (char *) realloc(p->buffer, nsize); } + else{ p->buffer = (char *) malloc(nsize); } + + if(!p->buffer){ png_error(png_ptr, "Write Error"); } + + /* copy new bytes to end of buffer */ + memcpy(p->buffer + p->size, data, length); + p->size += length; } -void Emf::toPNG(PMEMPNG accum, int width, int height, char *px){ - bitmap_t bmstore; - bitmap_t *bitmap=&bmstore; +void Emf::toPNG(PMEMPNG accum, int width, int height, const char *px){ + bitmap_t bmStore; + bitmap_t *bitmap = &bmStore; accum->buffer=NULL; // PNG constructed in memory will end up here, caller must free(). accum->size=0; bitmap->pixels=(pixel_t *)px; bitmap->width = width; bitmap->height = height; - + png_structp png_ptr = NULL; png_infop info_ptr = NULL; size_t x, y; png_byte ** row_pointers = NULL; - /* The following number is set by trial and error only. I cannot - see where it it is documented in the libpng manual. + /* The following number is set by trial and error only. I cannot + see where it it is documented in the libpng manual. */ int pixel_size = 3; int depth = 8; - + png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (png_ptr == NULL){ + if (png_ptr == NULL){ accum->buffer=NULL; return; } - + info_ptr = png_create_info_struct (png_ptr); if (info_ptr == NULL){ png_destroy_write_struct (&png_ptr, &info_ptr); - accum->buffer=NULL; + accum->buffer=NULL; return; } - + /* Set up error handling. */ if (setjmp (png_jmpbuf (png_ptr))) { png_destroy_write_struct (&png_ptr, &info_ptr); - accum->buffer=NULL; + accum->buffer=NULL; return; } - + /* Set image attributes. */ - png_set_IHDR (png_ptr, - info_ptr, - bitmap->width, - bitmap->height, - depth, - PNG_COLOR_TYPE_RGB, - PNG_INTERLACE_NONE, - PNG_COMPRESSION_TYPE_DEFAULT, - PNG_FILTER_TYPE_DEFAULT); - + png_set_IHDR ( + png_ptr, + info_ptr, + bitmap->width, + bitmap->height, + depth, + PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT + ); + /* Initialize rows of PNG. */ row_pointers = (png_byte **) png_malloc (png_ptr, bitmap->height * sizeof (png_byte *)); for (y = 0; y < bitmap->height; ++y) { - png_byte *row = + png_byte *row = (png_byte *) png_malloc (png_ptr, sizeof (uint8_t) * bitmap->width * pixel_size); row_pointers[bitmap->height - y - 1] = row; // Row order in EMF is reversed. for (x = 0; x < bitmap->width; ++x) { @@ -185,21 +184,21 @@ void Emf::toPNG(PMEMPNG accum, int width, int height, char *px){ *row++ = pixel->blue; } } - + /* Write the image data to memory */ png_set_rows (png_ptr, info_ptr, row_pointers); png_set_write_fn(png_ptr, accum, my_png_write_data, NULL); - + png_write_png (png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL); - + for (y = 0; y < bitmap->height; y++) { png_free (png_ptr, row_pointers[y]); } png_free (png_ptr, row_pointers); png_destroy_write_struct(&png_ptr, &info_ptr); - + } @@ -209,7 +208,7 @@ inverse of gethexcolor() in emf-print.cpp uint32_t Emf::sethexcolor(U_COLORREF color){ uint32_t out; - out = (U_RGBAGetR(color) << 16) + + out = (U_RGBAGetR(color) << 16) + (U_RGBAGetG(color) << 8 ) + (U_RGBAGetB(color) ); return(out); @@ -238,11 +237,11 @@ Emf::check (Inkscape::Extension::Extension * /*module*/) void -Emf::print_document_to_file(SPDocument *doc, gchar const *filename) +Emf::print_document_to_file(SPDocument *doc, const gchar *filename) { Inkscape::Extension::Print *mod; SPPrintContext context; - gchar const *oldconst; + const gchar *oldconst; gchar *oldoutput; unsigned int ret; @@ -301,10 +300,10 @@ Emf::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar const *filena bool new_FixImageRot = mod->get_param_bool("FixImageRot"); // remove rotations on images TableGen( //possibly regenerate the unicode-convert tables - mod->get_param_bool("TnrToSymbol"), - mod->get_param_bool("TnrToWingdings"), - mod->get_param_bool("TnrToZapfDingbats"), - mod->get_param_bool("UsePUA") + mod->get_param_bool("TnrToSymbol"), + mod->get_param_bool("TnrToWingdings"), + mod->get_param_bool("TnrToZapfDingbats"), + mod->get_param_bool("UsePUA") ); ext->set_param_bool("FixPPTCharPos",new_FixPPTCharPos); // Remember to add any new ones to PrintEmf::init or a mysterious failure will result! @@ -324,45 +323,46 @@ enum drawmode {DRAW_PAINT, DRAW_PATTERN, DRAW_IMAGE}; // apply to either fill o -/* given the transformation matrix from worldTranform return the scale in the matrix part. Assumes that the - matrix is not used to skew, invert, or make another distorting transformation. */ +/* given the transformation matrix from worldTranform return the scale in the matrix part. Assumes that the + matrix is not used to skew, invert, or make another distorting transformation. */ double Emf::current_scale(PEMF_CALLBACK_DATA d){ - double scale = d->dc[d->level].worldTransform.eM11 * d->dc[d->level].worldTransform.eM22 - - d->dc[d->level].worldTransform.eM12 * d->dc[d->level].worldTransform.eM21; - if(scale <= 0.0)scale=1.0; /* something is dreadfully wrong with the matrix, but do not crash over it */ - scale=sqrt(scale); - return(scale); + double scale = + d->dc[d->level].worldTransform.eM11 * d->dc[d->level].worldTransform.eM22 - + d->dc[d->level].worldTransform.eM12 * d->dc[d->level].worldTransform.eM21; + if(scale <= 0.0)scale=1.0; /* something is dreadfully wrong with the matrix, but do not crash over it */ + scale=sqrt(scale); + return(scale); } -/* given the transformation matrix from worldTranform and the current x,y position in inkscape coordinates, - generate an SVG transform that gives the same amount of rotation, no scaling, and maps x,y back onto x,y. This is used for - rotating objects when the location of at least one point in that object is known. Returns: - "matrix(a,b,c,d,e,f)" (WITH the double quotes) +/* given the transformation matrix from worldTranform and the current x,y position in inkscape coordinates, + generate an SVG transform that gives the same amount of rotation, no scaling, and maps x,y back onto x,y. This is used for + rotating objects when the location of at least one point in that object is known. Returns: + "matrix(a,b,c,d,e,f)" (WITH the double quotes) */ std::string Emf::current_matrix(PEMF_CALLBACK_DATA d, double x, double y, int useoffset){ - std::stringstream cxform; - double scale = current_scale(d); - cxform << "\"matrix("; - cxform << d->dc[d->level].worldTransform.eM11/scale; cxform << ","; - cxform << d->dc[d->level].worldTransform.eM12/scale; cxform << ","; - cxform << d->dc[d->level].worldTransform.eM21/scale; cxform << ","; - cxform << d->dc[d->level].worldTransform.eM22/scale; cxform << ","; - if(useoffset){ - /* for the "new" coordinates drop the worldtransform translations, not used here */ - double newx = x * d->dc[d->level].worldTransform.eM11/scale + y * d->dc[d->level].worldTransform.eM21/scale; - double newy = x * d->dc[d->level].worldTransform.eM12/scale + y * d->dc[d->level].worldTransform.eM22/scale; - cxform << x - newx; cxform << ","; - cxform << y - newy; - } - else { - cxform << "0,0"; - } - cxform << ")\""; - return(cxform.str()); + std::stringstream cxform; + double scale = current_scale(d); + cxform << "\"matrix("; + cxform << d->dc[d->level].worldTransform.eM11/scale; cxform << ","; + cxform << d->dc[d->level].worldTransform.eM12/scale; cxform << ","; + cxform << d->dc[d->level].worldTransform.eM21/scale; cxform << ","; + cxform << d->dc[d->level].worldTransform.eM22/scale; cxform << ","; + if(useoffset){ + /* for the "new" coordinates drop the worldtransform translations, not used here */ + double newx = x * d->dc[d->level].worldTransform.eM11/scale + y * d->dc[d->level].worldTransform.eM21/scale; + double newy = x * d->dc[d->level].worldTransform.eM12/scale + y * d->dc[d->level].worldTransform.eM22/scale; + cxform << x - newx; cxform << ","; + cxform << y - newy; + } + else { + cxform << "0,0"; + } + cxform << ")\""; + return(cxform.str()); } -/* given the transformation matrix from worldTranform return the rotation angle in radians. - counter clocwise from the x axis. */ +/* given the transformation matrix from worldTranform return the rotation angle in radians. + counter clocwise from the x axis. */ double Emf::current_rotation(PEMF_CALLBACK_DATA d){ return -std::atan2(d->dc[d->level].worldTransform.eM12, d->dc[d->level].worldTransform.eM11); } @@ -370,350 +370,408 @@ double Emf::current_rotation(PEMF_CALLBACK_DATA d){ /* Add another 100 blank slots to the hatches array. */ void Emf::enlarge_hatches(PEMF_CALLBACK_DATA d){ - d->hatches.size += 100; - d->hatches.strings = (char **) realloc(d->hatches.strings,d->hatches.size + sizeof(char *)); + d->hatches.size += 100; + d->hatches.strings = (char **) realloc(d->hatches.strings,d->hatches.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_hatches(PEMF_CALLBACK_DATA d, char *test){ - int i; - for(i=0; ihatches.count; i++){ - if(strcmp(test,d->hatches.strings[i])==0)return(i+1); - } - return(0); + int i; + for(i=0; ihatches.count; i++){ + if(strcmp(test,d->hatches.strings[i])==0)return(i+1); + } + return(0); } /* (Conditionally) add a hatch. If a matching hatch already exists nothing happens. If one - does not exist it is added to the hatches list and also entered into . + does not exist it is added to the hatches list and also entered into . + This is also used to add the path part of the hatches, which they reference with a xlink:href */ uint32_t Emf::add_hatch(PEMF_CALLBACK_DATA d, uint32_t hatchType, U_COLORREF hatchColor){ - char hatchname[64]; // big enough - char hrotname[64]; // big enough - char tmpcolor[8]; - uint32_t idx; - - if(hatchType==U_HS_DIAGCROSS){ // This is the only one with dependencies on others - (void) add_hatch(d,U_HS_FDIAGONAL,hatchColor); - (void) add_hatch(d,U_HS_BDIAGONAL,hatchColor); - } - - sprintf(tmpcolor,"%6.6X",sethexcolor(hatchColor)); - switch(hatchType){ - case U_HS_SOLIDTEXTCLR: - case U_HS_DITHEREDTEXTCLR: - sprintf(tmpcolor,"%6.6X",sethexcolor(d->dc[d->level].textColor)); - break; - case U_HS_SOLIDBKCLR: - case U_HS_DITHEREDBKCLR: - sprintf(tmpcolor,"%6.6X",sethexcolor(d->dc[d->level].bkColor)); - break; - default: - break; - } - - // 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. - if(hatchType > U_HS_SOLIDCLR)hatchType = U_HS_SOLIDCLR; - - // pattern defines hatch when there is no rotation. Load this one first. - sprintf(hatchname,"EMFhatch%d_%s",hatchType,tmpcolor); /* name of pattern BEFORE rotation*/ - idx = in_hatches(d,hatchname); - if(!idx){ // add it if not already present - if(d->hatches.count == d->hatches.size){ enlarge_hatches(d); } - d->hatches.strings[d->hatches.count++]=strdup(hatchname); - - *(d->defs) += "\n"; - *(d->defs) += " defs) += hatchname; - *(d->defs) += "\"\n"; - switch(hatchType){ - case U_HS_HORIZONTAL: - *(d->defs) += " patternUnits=\"userSpaceOnUse\" width=\"6\" height=\"6\" x=\"0\" y=\"0\" >\n"; - *(d->defs) += " defs) += tmpcolor; - *(d->defs) += "\" />\n"; + char hatchname[64]; // big enough + char hpathname[64]; // big enough + char hbkname[64]; // big enough + char tmpcolor[8]; + char bkcolor[8]; + uint32_t idx; + + switch(hatchType){ + case U_HS_SOLIDTEXTCLR: + case U_HS_DITHEREDTEXTCLR: + sprintf(tmpcolor,"%6.6X",sethexcolor(d->dc[d->level].textColor)); + break; + case U_HS_SOLIDBKCLR: + case U_HS_DITHEREDBKCLR: + sprintf(tmpcolor,"%6.6X",sethexcolor(d->dc[d->level].bkColor)); break; - case U_HS_VERTICAL: - *(d->defs) += " patternUnits=\"userSpaceOnUse\" width=\"6\" height=\"6\" x=\"0\" y=\"0\" >\n"; - *(d->defs) += " defs) += tmpcolor; - *(d->defs) += "\" />\n"; + default: + sprintf(tmpcolor,"%6.6X",sethexcolor(hatchColor)); break; - case U_HS_FDIAGONAL: - *(d->defs) += " patternUnits=\"userSpaceOnUse\" width=\"6\" height=\"6\" x=\"0\" y=\"0\" viewBox=\"0 0 6 6\" preserveAspectRatio=\"none\" >\n"; - *(d->defs) += " defs) += tmpcolor; - *(d->defs) += "\" id=\"sub"; - *(d->defs) += hatchname; - *(d->defs) += "\"/>\n"; - *(d->defs) += " defs) += hatchname; - *(d->defs) += "\" transform=\"translate(6,0)\"/>\n"; - *(d->defs) += " defs) += hatchname; - *(d->defs) += "\" transform=\"translate(-6,0)\"/>\n"; + } + + /* For both bkMode types set the PATH + FOREGROUND COLOR for the indicated standard hatch. + This will be used late to compose, or recompose the transparent or opaque final hatch.*/ + + std::string refpath; // used to reference later the path pieces which are about to be created + sprintf(hpathname,"EMFhpath%d_%s",hatchType,tmpcolor); + idx = in_hatches(d,hpathname); + if(!idx){ // add path/color if not already present + if(d->hatches.count == d->hatches.size){ enlarge_hatches(d); } + d->hatches.strings[d->hatches.count++]=strdup(hpathname); + + *(d->defs) += "\n"; + switch(hatchType){ + case U_HS_HORIZONTAL: + *(d->defs) += " defs) += hpathname; + *(d->defs) += "\" d=\"M 0 0 6 0\" style=\"fill:none;stroke:#"; + *(d->defs) += tmpcolor; + *(d->defs) += "\" />\n"; + break; + case U_HS_VERTICAL: + *(d->defs) += " defs) += hpathname; + *(d->defs) += "\" d=\"M 0 0 0 6\" style=\"fill:none;stroke:#"; + *(d->defs) += tmpcolor; + *(d->defs) += "\" />\n"; + break; + case U_HS_FDIAGONAL: + *(d->defs) += " defs) += hpathname; + *(d->defs) += "\" x1=\"-1\" y1=\"-1\" x2=\"7\" y2=\"7\" stroke=\"#"; + *(d->defs) += tmpcolor; + *(d->defs) += "\"/>\n"; + break; + case U_HS_BDIAGONAL: + *(d->defs) += " defs) += hpathname; + *(d->defs) += "\" x1=\"-1\" y1=\"7\" x2=\"7\" y2=\"-1\" stroke=\"#"; + *(d->defs) += tmpcolor; + *(d->defs) += "\"/>\n"; + break; + case U_HS_CROSS: + *(d->defs) += " defs) += hpathname; + *(d->defs) += "\" d=\"M 0 0 6 0 M 0 0 0 6\" style=\"fill:none;stroke:#"; + *(d->defs) += tmpcolor; + *(d->defs) += "\" />\n"; + break; + case U_HS_DIAGCROSS: + *(d->defs) += " defs) += hpathname; + *(d->defs) += "\" x1=\"-1\" y1=\"-1\" x2=\"7\" y2=\"7\" stroke=\"#"; + *(d->defs) += tmpcolor; + *(d->defs) += "\"/>\n"; + *(d->defs) += " defs) += hpathname; + *(d->defs) += "\" x1=\"-1\" y1=\"7\" x2=\"7\" y2=\"-1\" stroke=\"#"; + *(d->defs) += tmpcolor; + *(d->defs) += "\"/>\n"; + break; + case U_HS_SOLIDCLR: + case U_HS_DITHEREDCLR: + case U_HS_SOLIDTEXTCLR: + case U_HS_DITHEREDTEXTCLR: + case U_HS_SOLIDBKCLR: + case U_HS_DITHEREDBKCLR: + default: + *(d->defs) += " defs) += hpathname; + *(d->defs) += "\" d=\"M 0 0 6 0 6 6 0 6 z\" style=\"fill:#"; + *(d->defs) += tmpcolor; + *(d->defs) += ";stroke:none"; + *(d->defs) += "\" />\n"; + break; + } + } + + // References to paths possibly just created above. These will be used in the actual patterns. + switch(hatchType){ + case U_HS_HORIZONTAL: + case U_HS_VERTICAL: + case U_HS_CROSS: + case U_HS_SOLIDCLR: + case U_HS_DITHEREDCLR: + case U_HS_SOLIDTEXTCLR: + case U_HS_DITHEREDTEXTCLR: + case U_HS_SOLIDBKCLR: + case U_HS_DITHEREDBKCLR: + default: + refpath += " \n"; + break; + case U_HS_FDIAGONAL: + case U_HS_BDIAGONAL: + refpath += " \n"; + refpath += " \n"; + refpath += " \n"; + break; + case U_HS_DIAGCROSS: + refpath += " \n"; + refpath += " \n"; + refpath += " \n"; + refpath += " \n"; + refpath += " \n"; + refpath += " \n"; break; - case U_HS_BDIAGONAL: - *(d->defs) += " patternUnits=\"userSpaceOnUse\" width=\"6\" height=\"6\" x=\"0\" y=\"0\" viewBox=\"0 0 6 6\" preserveAspectRatio=\"none\" >\n"; - *(d->defs) += " defs) += tmpcolor; - *(d->defs) += "\" id=\"sub"; - *(d->defs) += hatchname; - *(d->defs) += "\"/>\n"; - *(d->defs) += " defs) += hatchname; - *(d->defs) += "\" transform=\"translate(6,0)\"/>\n"; - *(d->defs) += " dc[d->level].bkMode == U_TRANSPARENT || hatchType >= U_HS_SOLIDCLR){ + sprintf(hatchname,"EMFhatch%d_%s",hatchType,tmpcolor); + sprintf(hpathname,"EMFhpath%d_%s",hatchType,tmpcolor); + idx = in_hatches(d,hatchname); + if(!idx){ // add it if not already present + if(d->hatches.count == d->hatches.size){ enlarge_hatches(d); } + d->hatches.strings[d->hatches.count++]=strdup(hatchname); + *(d->defs) += "\n"; + *(d->defs) += " defs) += hatchname; - *(d->defs) += "\" transform=\"translate(-6,0)\"/>\n"; - break; - case U_HS_CROSS: - *(d->defs) += " patternUnits=\"userSpaceOnUse\" width=\"6\" height=\"6\" x=\"0\" y=\"0\" >\n"; - *(d->defs) += " defs) += tmpcolor; + *(d->defs) += "\" xlink:href=\"#EMFhbasepattern\">\n"; + *(d->defs) += refpath; + *(d->defs) += " \n"; + idx = d->hatches.count; + } + } + else { // bkMode==U_OPAQUE + /* Set up an object in the defs for this background, if there is not one already there */ + sprintf(bkcolor,"%6.6X",sethexcolor(d->dc[d->level].bkColor)); + sprintf(hbkname,"EMFhbkclr_%s",bkcolor); + idx = in_hatches(d,hbkname); + if(!idx){ // add path/color if not already present. Hatchtype is not needed in the name. + if(d->hatches.count == d->hatches.size){ enlarge_hatches(d); } + d->hatches.strings[d->hatches.count++]=strdup(hbkname); + + *(d->defs) += "\n"; + *(d->defs) += " defs) += hbkname; + *(d->defs) += "\" x=\"0\" y=\"0\" width=\"6\" height=\"6\" fill=\"#"; + *(d->defs) += bkcolor; *(d->defs) += "\" />\n"; - break; - case U_HS_DIAGCROSS: - *(d->defs) += " patternUnits=\"userSpaceOnUse\" width=\"6\" height=\"6\" x=\"0\" y=\"0\" viewBox=\"0 0 6 6\" preserveAspectRatio=\"none\" >\n"; - *(d->defs) += " defs) += hrotname; - *(d->defs) += "\" transform=\"translate(0,0)\"/>\n"; - *(d->defs) += " defs) += hrotname; - *(d->defs) += "\" transform=\"translate(0,0)\"/>\n"; - break; - case U_HS_SOLIDCLR: - case U_HS_DITHEREDCLR: - case U_HS_SOLIDTEXTCLR: - case U_HS_DITHEREDTEXTCLR: - case U_HS_SOLIDBKCLR: - case U_HS_DITHEREDBKCLR: - default: - *(d->defs) += " patternUnits=\"userSpaceOnUse\" width=\"6\" height=\"6\" x=\"0\" y=\"0\" >\n"; - *(d->defs) += " defs) += tmpcolor; - *(d->defs) += ";stroke:none"; + } + + // this is the pattern, its name will show up in Inkscape's pattern selector + sprintf(hatchname,"EMFhatch%d_%s_%s",hatchType,tmpcolor,bkcolor); + idx = in_hatches(d,hatchname); + if(!idx){ // add it if not already present + if(d->hatches.count == d->hatches.size){ enlarge_hatches(d); } + d->hatches.strings[d->hatches.count++]=strdup(hatchname); + *(d->defs) += "\n"; + *(d->defs) += " defs) += hatchname; + *(d->defs) += "\" xlink:href=\"#EMFhbasepattern\">\n"; + *(d->defs) += " defs) += hbkname; *(d->defs) += "\" />\n"; - break; - } - *(d->defs) += " "; - *(d->defs) += " \n"; - idx = d->hatches.count; - } - - - // pattern allows the inner pattern to be rotated nicely, load this one second only if needed - // hatchname retained from above - sprintf(hrotname,"EMFrothatch%d_%s",hatchType,tmpcolor); /* name of pattern AFTER rotation*/ - if(current_rotation(d) >= 0.00001 || current_rotation(d) <= -0.00001){ /* some rotation, allow a little rounding error around 0 degrees */ - idx = in_hatches(d,hrotname); - if(!idx){ - if(d->hatches.count == d->hatches.size){ enlarge_hatches(d); } - d->hatches.strings[d->hatches.count++]=strdup(hrotname); - - *(d->defs) += "\n"; - *(d->defs) += " defs) += " id=\""; - *(d->defs) += hrotname; - *(d->defs) += "\"\n"; - *(d->defs) += " xlink:href=\"#"; - *(d->defs) += hatchname; - *(d->defs) += "\"\n"; - *(d->defs) += " patternTransform="; - *(d->defs) += current_matrix(d, 0.0, 0.0, 0); //j use offset 0,0 - *(d->defs) += " />\n"; - idx = d->hatches.count; - } - } - - return(idx-1); + *(d->defs) += refpath; + *(d->defs) += " \n"; + idx = d->hatches.count; + } + } + return(idx-1); } /* Add another 100 blank slots to the images array. */ void Emf::enlarge_images(PEMF_CALLBACK_DATA d){ - d->images.size += 100; - d->images.strings = (char **) realloc(d->images.strings,d->images.size + sizeof(char *)); + d->images.size += 100; + d->images.strings = (char **) realloc(d->images.strings,d->images.size * sizeof(char *)); } /* 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 i; - for(i=0; iimages.count; i++){ - if(strcmp(test,d->images.strings[i])==0)return(i+1); - } - return(0); + int i; + for(i=0; iimages.count; i++){ + if(strcmp(test,d->images.strings[i])==0)return(i+1); + } + return(0); } /* (Conditionally) add an image. If a matching image already exists nothing happens. If one - does not exist it is added to the images list and also entered into . - + does not exist it is added to the images list and also entered into . + U_EMRCREATEMONOBRUSH records only work when the bitmap is monochrome. If we hit one that isn't set idx to 2^32-1 and let the caller handle it. */ -uint32_t Emf::add_image(PEMF_CALLBACK_DATA d, void *pEmr, uint32_t cbBits, uint32_t cbBmi, - uint32_t iUsage, uint32_t offBits, uint32_t offBmi){ - - uint32_t idx; - char imagename[64]; // big enough - char imrotname[64]; // big enough - char xywh[64]; // big enough - int dibparams; - - MEMPNG mempng; // PNG in memory comes back in this - mempng.buffer = NULL; - - char *rgba_px = NULL; // RGBA pixels - const char *px = NULL; // DIB pixels - const U_RGBQUAD *ct = NULL; // DIB color table - U_RGBQUAD ct2[2]; - uint32_t width, height, colortype, numCt, invert; - if(!cbBits || - !cbBmi || - (iUsage != U_DIB_RGB_COLORS) || - !(dibparams = get_DIB_params( // this returns pointers and values, but allocates no memory - pEmr, - offBits, - offBmi, - &px, - (const U_RGBQUAD **) &ct, - &numCt, - &width, - &height, - &colortype, - &invert - )) - ){ - - // U_EMRCREATEMONOBRUSH uses text/bk colors instead of what is in the color map. - if(((PU_EMR)pEmr)->iType == U_EMR_CREATEMONOBRUSH){ - if(numCt==2){ - ct2[0] = U_RGB2BGR(d->dc[d->level].textColor); - ct2[1] = U_RGB2BGR(d->dc[d->level].bkColor); - ct = &ct2[0]; - } - else { // createmonobrush renders on other platforms this way - return(0xFFFFFFFF); - } - } - - if(!DIB_to_RGBA( - px, // DIB pixel array - ct, // DIB color table - numCt, // DIB color table number of entries - &rgba_px, // U_RGBA pixel array (32 bits), created by this routine, caller must free. - width, // Width of pixel array in record - height, // Height of pixel array in record - colortype, // DIB BitCount Enumeration - numCt, // Color table used if not 0 - invert // If DIB rows are in opposite order from RGBA rows - ) && - rgba_px) - { - toPNG( // Get the image from the RGBA px into mempng - &mempng, - width, height, // of the SRC bitmap - rgba_px); - free(rgba_px); - } - } - gchar *base64String; - if(dibparams == U_BI_JPEG || dibparams==U_BI_PNG){ - base64String = g_base64_encode((guchar*) px, numCt ); - idx = in_images(d, (char *) base64String); - } - else if(mempng.buffer){ - base64String = g_base64_encode((guchar*) mempng.buffer, mempng.size ); - free(mempng.buffer); - idx = in_images(d, (char *) base64String); - } - else { - // insert a random 3x4 blotch otherwise - width = 3; - height = 4; - base64String = strdup("iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAAA3NCSVQICAjb4U/gAAAALElEQVQImQXBQQ2AMAAAsUJQMSWI2H8qME1yMshojwrvGB8XcHKvR1XtOTc/8HENumHCsOMAAAAASUVORK5CYII="); - idx = in_images(d, (char *) base64String); - } - if(!idx){ // add it if not already present - we looked at the actual data for comparison - if(d->images.count == d->images.size){ enlarge_images(d); } - idx = d->images.count; - d->images.strings[d->images.count++]=strdup(base64String); - - sprintf(imagename,"EMFimage%d",idx++); - sprintf(xywh," x=\"0\" y=\"0\" width=\"%d\" height=\"%d\" ",width,height); // reuse this buffer - - *(d->defs) += "\n"; - *(d->defs) += " defs) += imagename; - *(d->defs) += "\"\n "; - *(d->defs) += xywh; - *(d->defs) += "\n"; - if(dibparams == U_BI_JPEG){ *(d->defs) += " xlink:href=\"data:image/jpeg;base64,"; } - else { *(d->defs) += " xlink:href=\"data:image/png;base64,"; } - *(d->defs) += base64String; - *(d->defs) += "\"\n"; - *(d->defs) += " />\n"; - - - *(d->defs) += "\n"; - *(d->defs) += " defs) += imagename; - *(d->defs) += "_ref\"\n "; - *(d->defs) += xywh; - *(d->defs) += "\n patternUnits=\"userSpaceOnUse\""; - *(d->defs) += " >\n"; - *(d->defs) += " defs) += imagename; - *(d->defs) += "_ign\" "; - *(d->defs) += " xlink:href=\"#"; - *(d->defs) += imagename; - *(d->defs) += "\" />\n"; - *(d->defs) += " "; - *(d->defs) += " \n"; - } - g_free(base64String); - - /* image allows the inner image to be rotated nicely, load this one second only if needed - imagename retained from above - Here comes a dreadful hack. How do we determine if this rotation of the base image has already - been loaded? The image names contain no identifying information, they are just numbered sequentially. - So the rotated name is EMFrotimage###_XXXXXX, where ### is the number of the referred to image, and - XXXX is the rotation in radians x 1000000 and truncated. That is then stored in BASE64 as the "image". - The corresponding SVG generated though is not for an image, but a reference to an image. - The name of the pattern MUST stil be EMFimage###_ref or output_style() will not be able to use it. - */ - if(current_rotation(d) >= 0.00001 || current_rotation(d) <= -0.00001){ /* some rotation, allow a little rounding error around 0 degrees */ - int tangle = round(current_rotation(d)*1000000.0); - sprintf(imrotname,"EMFrotimage%d_%d",idx-1,tangle); - base64String = g_base64_encode((guchar*) imrotname, strlen(imrotname) ); - idx = in_images(d, (char *) base64String); // scan for this "image" - if(!idx){ - if(d->images.count == d->images.size){ enlarge_images(d); } - idx = d->images.count; - d->images.strings[d->images.count++]=strdup(base64String); - sprintf(imrotname,"EMFimage%d",idx++); - - *(d->defs) += "\n"; - *(d->defs) += " defs) += " id=\""; - *(d->defs) += imrotname; - *(d->defs) += "_ref\"\n"; - *(d->defs) += " xlink:href=\"#"; - *(d->defs) += imagename; - *(d->defs) += "_ref\"\n"; - *(d->defs) += " patternTransform="; - *(d->defs) += current_matrix(d, 0.0, 0.0, 0); //j use offset 0,0 - *(d->defs) += " />\n"; - } - g_free(base64String); - } - - return(idx-1); +uint32_t Emf::add_image(PEMF_CALLBACK_DATA d, void *pEmr, uint32_t cbBits, uint32_t cbBmi, + uint32_t iUsage, uint32_t offBits, uint32_t offBmi){ + + uint32_t idx; + char imagename[64]; // big enough + char imrotname[64]; // big enough + char xywh[64]; // big enough + int dibparams; + + MEMPNG mempng; // PNG in memory comes back in this + mempng.buffer = NULL; + + char *rgba_px = NULL; // RGBA pixels + const char *px = NULL; // DIB pixels + const U_RGBQUAD *ct = NULL; // DIB color table + U_RGBQUAD ct2[2]; + uint32_t width, height, colortype, numCt, invert; + if( !cbBits || + !cbBmi || + (iUsage != U_DIB_RGB_COLORS) || + !(dibparams = get_DIB_params( // this returns pointers and values, but allocates no memory + pEmr, + offBits, + offBmi, + &px, + (const U_RGBQUAD **) &ct, + &numCt, + &width, + &height, + &colortype, + &invert + )) + ){ + + // U_EMRCREATEMONOBRUSH uses text/bk colors instead of what is in the color map. + if(((PU_EMR)pEmr)->iType == U_EMR_CREATEMONOBRUSH){ + if(numCt==2){ + ct2[0] = U_RGB2BGR(d->dc[d->level].textColor); + ct2[1] = U_RGB2BGR(d->dc[d->level].bkColor); + ct = &ct2[0]; + } + else { // createmonobrush renders on other platforms this way + return(0xFFFFFFFF); + } + } + + if(!DIB_to_RGBA( + px, // DIB pixel array + ct, // DIB color table + numCt, // DIB color table number of entries + &rgba_px, // U_RGBA pixel array (32 bits), created by this routine, caller must free. + width, // Width of pixel array in record + height, // Height of pixel array in record + colortype, // DIB BitCount Enumeration + numCt, // Color table used if not 0 + invert // If DIB rows are in opposite order from RGBA rows + ) && + rgba_px + ){ + toPNG( // Get the image from the RGBA px into mempng + &mempng, + width, height, // of the SRC bitmap + rgba_px + ); + free(rgba_px); + } + } + gchar *base64String; + if(dibparams == U_BI_JPEG || dibparams==U_BI_PNG){ + base64String = g_base64_encode((guchar*) px, numCt ); + idx = in_images(d, (char *) base64String); + } + else if(mempng.buffer){ + base64String = g_base64_encode((guchar*) mempng.buffer, mempng.size ); + free(mempng.buffer); + idx = in_images(d, (char *) base64String); + } + else { + // insert a random 3x4 blotch otherwise + width = 3; + height = 4; + base64String = g_strdup("iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAAA3NCSVQICAjb4U/gAAAALElEQVQImQXBQQ2AMAAAsUJQMSWI2H8qME1yMshojwrvGB8XcHKvR1XtOTc/8HENumHCsOMAAAAASUVORK5CYII="); + idx = in_images(d, (char *) base64String); + } + if(!idx){ // add it if not already present - we looked at the actual data for comparison + if(d->images.count == d->images.size){ enlarge_images(d); } + idx = d->images.count; + d->images.strings[d->images.count++]=strdup(base64String); + + sprintf(imagename,"EMFimage%d",idx++); + sprintf(xywh," x=\"0\" y=\"0\" width=\"%d\" height=\"%d\" ",width,height); // reuse this buffer + + *(d->defs) += "\n"; + *(d->defs) += " defs) += imagename; + *(d->defs) += "\"\n "; + *(d->defs) += xywh; + *(d->defs) += "\n"; + if(dibparams == U_BI_JPEG){ *(d->defs) += " xlink:href=\"data:image/jpeg;base64,"; } + else { *(d->defs) += " xlink:href=\"data:image/png;base64,"; } + *(d->defs) += base64String; + *(d->defs) += "\"\n"; + *(d->defs) += " />\n"; + + + *(d->defs) += "\n"; + *(d->defs) += " defs) += imagename; + *(d->defs) += "_ref\"\n "; + *(d->defs) += xywh; + *(d->defs) += "\n patternUnits=\"userSpaceOnUse\""; + *(d->defs) += " >\n"; + *(d->defs) += " defs) += imagename; + *(d->defs) += "_ign\" "; + *(d->defs) += " xlink:href=\"#"; + *(d->defs) += imagename; + *(d->defs) += "\" />\n"; + *(d->defs) += " "; + *(d->defs) += " \n"; + } + g_free(base64String); + + /* image allows the inner image to be rotated nicely, load this one second only if needed + imagename retained from above + Here comes a dreadful hack. How do we determine if this rotation of the base image has already + been loaded? The image names contain no identifying information, they are just numbered sequentially. + So the rotated name is EMFrotimage###_XXXXXX, where ### is the number of the referred to image, and + XXXX is the rotation in radians x 1000000 and truncated. That is then stored in BASE64 as the "image". + The corresponding SVG generated though is not for an image, but a reference to an image. + The name of the pattern MUST stil be EMFimage###_ref or output_style() will not be able to use it. + */ + if(current_rotation(d) >= 0.00001 || current_rotation(d) <= -0.00001){ /* some rotation, allow a little rounding error around 0 degrees */ + int tangle = round(current_rotation(d)*1000000.0); + sprintf(imrotname,"EMFrotimage%d_%d",idx-1,tangle); + base64String = g_base64_encode((guchar*) imrotname, strlen(imrotname) ); + idx = in_images(d, (char *) base64String); // scan for this "image" + if(!idx){ + if(d->images.count == d->images.size){ enlarge_images(d); } + idx = d->images.count; + d->images.strings[d->images.count++]=strdup(base64String); + sprintf(imrotname,"EMFimage%d",idx++); + + *(d->defs) += "\n"; + *(d->defs) += " defs) += " id=\""; + *(d->defs) += imrotname; + *(d->defs) += "_ref\"\n"; + *(d->defs) += " xlink:href=\"#"; + *(d->defs) += imagename; + *(d->defs) += "_ref\"\n"; + *(d->defs) += " patternTransform="; + *(d->defs) += current_matrix(d, 0.0, 0.0, 0); //j use offset 0,0 + *(d->defs) += " />\n"; + } + g_free(base64String); + } + + return(idx-1); } @@ -728,74 +786,74 @@ Emf::output_style(PEMF_CALLBACK_DATA d, int iType) sp_color_get_rgb_floatv( &(d->dc[d->level].style.fill.value.color), fill_rgb ); float stroke_rgb[3]; sp_color_get_rgb_floatv(&(d->dc[d->level].style.stroke.value.color), stroke_rgb); - + // for U_EMR_BITBLT with no image, try to approximate some of these operations/ // Assume src color is "white" if(d->dwRop3){ - switch(d->dwRop3){ - case U_PATINVERT: // treat all of these as black - case U_SRCINVERT: - case U_DSTINVERT: - case U_BLACKNESS: - case U_SRCERASE: - case U_NOTSRCCOPY: - fill_rgb[0]=fill_rgb[1]=fill_rgb[2]=0.0; - break; - case U_SRCCOPY: // treat all of these as white - case U_NOTSRCERASE: - case U_PATCOPY: - case U_WHITENESS: - fill_rgb[0]=fill_rgb[1]=fill_rgb[2]=1.0; - break; - case U_SRCPAINT: // use the existing color - case U_SRCAND: - case U_MERGECOPY: - case U_MERGEPAINT: - case U_PATPAINT: - default: - break; - } - d->dwRop3 = 0; // might as well reset it here, it must be set for each BITBLT + switch(d->dwRop3){ + case U_PATINVERT: // treat all of these as black + case U_SRCINVERT: + case U_DSTINVERT: + case U_BLACKNESS: + case U_SRCERASE: + case U_NOTSRCCOPY: + fill_rgb[0]=fill_rgb[1]=fill_rgb[2]=0.0; + break; + case U_SRCCOPY: // treat all of these as white + case U_NOTSRCERASE: + case U_PATCOPY: + case U_WHITENESS: + fill_rgb[0]=fill_rgb[1]=fill_rgb[2]=1.0; + break; + case U_SRCPAINT: // use the existing color + case U_SRCAND: + case U_MERGECOPY: + case U_MERGEPAINT: + case U_PATPAINT: + default: + break; + } + d->dwRop3 = 0; // might as well reset it here, it must be set for each BITBLT } // Implement some of these, the ones where the original screen color does not matter. - // The options that merge screen and pen colors cannot be done correctly because we + // The options that merge screen and pen colors cannot be done correctly because we // have no way of knowing what color is already on the screen. For those just pass the - // pen color through. + // pen color through. switch(d->dwRop2){ - case U_R2_BLACK: - fill_rgb[0] = fill_rgb[1] = fill_rgb[2] = 0.0; - stroke_rgb[0]= stroke_rgb[1]= stroke_rgb[2] = 0.0; - break; - case U_R2_NOTMERGEPEN: - case U_R2_MASKNOTPEN: - break; - case U_R2_NOTCOPYPEN: - fill_rgb[0] = 1.0 - fill_rgb[0]; - fill_rgb[1] = 1.0 - fill_rgb[1]; - fill_rgb[2] = 1.0 - fill_rgb[2]; - stroke_rgb[0] = 1.0 - stroke_rgb[0]; - stroke_rgb[1] = 1.0 - stroke_rgb[1]; - stroke_rgb[2] = 1.0 - stroke_rgb[2]; - break; - 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: - break; - case U_R2_WHITE: - fill_rgb[0] = fill_rgb[1] = fill_rgb[2] = 1.0; - stroke_rgb[0]= stroke_rgb[1]= stroke_rgb[2] = 1.0; - break; - default: - break; + case U_R2_BLACK: + fill_rgb[0] = fill_rgb[1] = fill_rgb[2] = 0.0; + stroke_rgb[0]= stroke_rgb[1]= stroke_rgb[2] = 0.0; + break; + case U_R2_NOTMERGEPEN: + case U_R2_MASKNOTPEN: + break; + case U_R2_NOTCOPYPEN: + fill_rgb[0] = 1.0 - fill_rgb[0]; + fill_rgb[1] = 1.0 - fill_rgb[1]; + fill_rgb[2] = 1.0 - fill_rgb[2]; + stroke_rgb[0] = 1.0 - stroke_rgb[0]; + stroke_rgb[1] = 1.0 - stroke_rgb[1]; + stroke_rgb[2] = 1.0 - stroke_rgb[2]; + break; + 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: + break; + case U_R2_WHITE: + fill_rgb[0] = fill_rgb[1] = fill_rgb[2] = 1.0; + stroke_rgb[0]= stroke_rgb[1]= stroke_rgb[2] = 1.0; + break; + default: + break; } @@ -808,32 +866,48 @@ Emf::output_style(PEMF_CALLBACK_DATA d, int iType) switch(d->dc[d->level].fill_mode){ // both of these use the url(#) method case DRAW_PATTERN: - snprintf(tmp, 1023, "fill:url(#%s); ",d->hatches.strings[d->dc[d->level].fill_idx]); - tmp_style << tmp; - break; + snprintf(tmp, 1023, "fill:url(#%s); ",d->hatches.strings[d->dc[d->level].fill_idx]); + tmp_style << tmp; + break; case DRAW_IMAGE: - snprintf(tmp, 1023, "fill:url(#EMFimage%d_ref); ",d->dc[d->level].fill_idx); - tmp_style << tmp; - break; + snprintf(tmp, 1023, "fill:url(#EMFimage%d_ref); ",d->dc[d->level].fill_idx); + tmp_style << tmp; + break; case DRAW_PAINT: default: // <-- this should never happen, but just in case... - snprintf(tmp, 1023, - "fill:#%02x%02x%02x;", - SP_COLOR_F_TO_U(fill_rgb[0]), - SP_COLOR_F_TO_U(fill_rgb[1]), - SP_COLOR_F_TO_U(fill_rgb[2])); - tmp_style << tmp; - break; - } - snprintf(tmp, 1023, - "fill-rule:%s;", - d->dc[d->level].style.fill_rule.value == 0 ? "evenodd" : "nonzero"); + snprintf( + tmp, 1023, + "fill:#%02x%02x%02x;", + SP_COLOR_F_TO_U(fill_rgb[0]), + SP_COLOR_F_TO_U(fill_rgb[1]), + SP_COLOR_F_TO_U(fill_rgb[2]) + ); + tmp_style << tmp; + break; + } + snprintf( + tmp, 1023, + "fill-rule:%s;", + (d->dc[d->level].style.fill_rule.value == 0 ? "evenodd" : "nonzero") + ); tmp_style << tmp; tmp_style << "fill-opacity:1;"; - if (d->dc[d->level].fill_set && d->dc[d->level].stroke_set && d->dc[d->level].style.stroke_width.value == 1 && - fill_rgb[0]==stroke_rgb[0] && fill_rgb[1]==stroke_rgb[1] && fill_rgb[2]==stroke_rgb[2]) - { + // if the stroke is the same as the fill, and the right size not to change the end size of the object, do not do it separately + if( + (d->dc[d->level].fill_set ) && + (d->dc[d->level].stroke_set ) && + (d->dc[d->level].style.stroke_width.value == 1 ) && + (d->dc[d->level].fill_mode == d->dc[d->level].stroke_mode) && + ( + (d->dc[d->level].fill_mode != DRAW_PAINT) || + ( + (fill_rgb[0]==stroke_rgb[0]) && + (fill_rgb[1]==stroke_rgb[1]) && + (fill_rgb[2]==stroke_rgb[2]) + ) + ) + ){ d->dc[d->level].stroke_set = false; } } @@ -844,37 +918,43 @@ Emf::output_style(PEMF_CALLBACK_DATA d, int iType) switch(d->dc[d->level].stroke_mode){ // both of these use the url(#) method case DRAW_PATTERN: - snprintf(tmp, 1023, "stroke:url(#%s); ",d->hatches.strings[d->dc[d->level].stroke_idx]); - tmp_style << tmp; - break; + snprintf(tmp, 1023, "stroke:url(#%s); ",d->hatches.strings[d->dc[d->level].stroke_idx]); + tmp_style << tmp; + break; case DRAW_IMAGE: - snprintf(tmp, 1023, "stroke:url(#EMFimage%d_ref); ",d->dc[d->level].stroke_idx); - tmp_style << tmp; - break; + snprintf(tmp, 1023, "stroke:url(#EMFimage%d_ref); ",d->dc[d->level].stroke_idx); + tmp_style << tmp; + break; case DRAW_PAINT: default: // <-- this should never happen, but just in case... - snprintf(tmp, 1023, - "stroke:#%02x%02x%02x;", - SP_COLOR_F_TO_U(stroke_rgb[0]), - SP_COLOR_F_TO_U(stroke_rgb[1]), - SP_COLOR_F_TO_U(stroke_rgb[2])); - tmp_style << tmp; - break; + snprintf( + tmp, 1023, + "stroke:#%02x%02x%02x;", + SP_COLOR_F_TO_U(stroke_rgb[0]), + SP_COLOR_F_TO_U(stroke_rgb[1]), + SP_COLOR_F_TO_U(stroke_rgb[2]) + ); + tmp_style << tmp; + break; } tmp_style << "stroke-width:" << MAX( 0.001, d->dc[d->level].style.stroke_width.value ) << "px;"; tmp_style << "stroke-linecap:" << - (d->dc[d->level].style.stroke_linecap.computed == 0 ? "butt" : - d->dc[d->level].style.stroke_linecap.computed == 1 ? "round" : - d->dc[d->level].style.stroke_linecap.computed == 2 ? "square" : - "unknown") << ";"; + ( + d->dc[d->level].style.stroke_linecap.computed == 0 ? "butt" : + d->dc[d->level].style.stroke_linecap.computed == 1 ? "round" : + d->dc[d->level].style.stroke_linecap.computed == 2 ? "square" : + "unknown" + ) << ";"; tmp_style << "stroke-linejoin:" << - (d->dc[d->level].style.stroke_linejoin.computed == 0 ? "miter" : - d->dc[d->level].style.stroke_linejoin.computed == 1 ? "round" : - d->dc[d->level].style.stroke_linejoin.computed == 2 ? "bevel" : - "unknown") << ";"; + ( + d->dc[d->level].style.stroke_linejoin.computed == 0 ? "miter" : + d->dc[d->level].style.stroke_linejoin.computed == 1 ? "round" : + d->dc[d->level].style.stroke_linejoin.computed == 2 ? "bevel" : + "unknown" + ) << ";"; // Set miter limit if known, even if it is not needed immediately (not miter) tmp_style << "stroke-miterlimit:" << @@ -941,7 +1021,7 @@ Emf::pix_to_y_point(PEMF_CALLBACK_DATA d, double px, double py) double wpy = px * d->dc[d->level].worldTransform.eM12 + py * d->dc[d->level].worldTransform.eM22 + d->dc[d->level].worldTransform.eDy; double y = _pix_y_to_point(d, wpy); - + return y; } @@ -956,11 +1036,11 @@ Emf::pix_to_abs_size(PEMF_CALLBACK_DATA d, double px) /* returns "x,y" (without the quotes) in inkscape coordinates for a pair of EMF x,y coordinates */ std::string Emf::pix_to_xy(PEMF_CALLBACK_DATA d, double x, double y){ - std::stringstream cxform; - cxform << pix_to_x_point(d,x,y); - cxform << ","; - cxform << pix_to_y_point(d,x,y); - return(cxform.str()); + std::stringstream cxform; + cxform << pix_to_x_point(d,x,y); + cxform << ","; + cxform << pix_to_y_point(d,x,y); + return(cxform.str()); } @@ -969,11 +1049,11 @@ Emf::select_pen(PEMF_CALLBACK_DATA d, int index) { PU_EMRCREATEPEN pEmr = NULL; - if (index >= 0 && index < d->n_obj) + if (index >= 0 && index < d->n_obj){ pEmr = (PU_EMRCREATEPEN) d->emf_obj[index].lpEMFR; + } - if (!pEmr) - return; + if (!pEmr){ return; } switch (pEmr->lopn.lopnStyle & U_PS_STYLE_MASK) { case U_PS_DASH: @@ -1000,11 +1080,11 @@ Emf::select_pen(PEMF_CALLBACK_DATA d, int index) d->dc[d->level].style.stroke_dash.dash[i++] = 1; d->dc[d->level].style.stroke_dash.dash[i++] = 1; } - + d->dc[d->level].style.stroke_dasharray_set = 1; break; } - + case U_PS_SOLID: default: { @@ -1014,41 +1094,17 @@ Emf::select_pen(PEMF_CALLBACK_DATA d, int index) } switch (pEmr->lopn.lopnStyle & U_PS_ENDCAP_MASK) { - case U_PS_ENDCAP_ROUND: - { - d->dc[d->level].style.stroke_linecap.computed = 1; - break; - } - case U_PS_ENDCAP_SQUARE: - { - d->dc[d->level].style.stroke_linecap.computed = 2; - break; - } + case U_PS_ENDCAP_ROUND: { d->dc[d->level].style.stroke_linecap.computed = 1; break; } + case U_PS_ENDCAP_SQUARE: { d->dc[d->level].style.stroke_linecap.computed = 2; break; } case U_PS_ENDCAP_FLAT: - default: - { - d->dc[d->level].style.stroke_linecap.computed = 0; - break; - } + default: { d->dc[d->level].style.stroke_linecap.computed = 0; break; } } switch (pEmr->lopn.lopnStyle & U_PS_JOIN_MASK) { - case U_PS_JOIN_BEVEL: - { - d->dc[d->level].style.stroke_linejoin.computed = 2; - break; - } - case U_PS_JOIN_MITER: - { - d->dc[d->level].style.stroke_linejoin.computed = 0; - break; - } + case U_PS_JOIN_BEVEL: { d->dc[d->level].style.stroke_linejoin.computed = 2; break; } + case U_PS_JOIN_MITER: { d->dc[d->level].style.stroke_linejoin.computed = 0; break; } case U_PS_JOIN_ROUND: - default: - { - d->dc[d->level].style.stroke_linejoin.computed = 1; - break; - } + default: { d->dc[d->level].style.stroke_linejoin.computed = 1; break; } } d->dc[d->level].stroke_set = true; @@ -1139,7 +1195,7 @@ Emf::select_extpen(PEMF_CALLBACK_DATA d, int index) d->dc[d->level].style.stroke_dash.dash[i++] = 1; d->dc[d->level].style.stroke_dash.dash[i++] = 2; } - + d->dc[d->level].style.stroke_dasharray_set = 1; break; } @@ -1208,49 +1264,50 @@ Emf::select_extpen(PEMF_CALLBACK_DATA d, int index) d->dc[d->level].stroke_mode = DRAW_PAINT; } else { - if (pEmr->elp.elpWidth) { - int cur_level = d->level; - d->level = d->emf_obj[index].level; - double pen_width = pix_to_abs_size( d, pEmr->elp.elpWidth ); - d->level = cur_level; - d->dc[d->level].style.stroke_width.value = pen_width; - } 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; - int cur_level = d->level; - d->level = d->emf_obj[index].level; - double pen_width = pix_to_abs_size( d, 1 ); - d->level = cur_level; - d->dc[d->level].style.stroke_width.value = pen_width; - } - - if( pEmr->elp.elpBrushStyle == U_BS_SOLID){ - double r, g, b; - r = SP_COLOR_U_TO_F( U_RGBAGetR(pEmr->elp.elpColor) ); - g = SP_COLOR_U_TO_F( U_RGBAGetG(pEmr->elp.elpColor) ); - b = SP_COLOR_U_TO_F( U_RGBAGetB(pEmr->elp.elpColor) ); - d->dc[d->level].style.stroke.value.color.set( r, g, b ); - d->dc[d->level].stroke_mode = DRAW_PAINT; - d->dc[d->level].stroke_set = true; - } - else if(pEmr->elp.elpBrushStyle == U_BS_HATCHED){ - d->dc[d->level].stroke_idx = add_hatch(d, pEmr->elp.elpHatch, pEmr->elp.elpColor); - d->dc[d->level].stroke_mode = DRAW_PATTERN; - d->dc[d->level].stroke_set = true; - } - 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); - d->dc[d->level].stroke_mode = DRAW_IMAGE; - d->dc[d->level].stroke_set = true; - } - else { // U_BS_PATTERN and anything strange that falls in, stroke is solid textColor - double r, g, b; - r = SP_COLOR_U_TO_F( U_RGBAGetR(d->dc[d->level].textColor)); - g = SP_COLOR_U_TO_F( U_RGBAGetG(d->dc[d->level].textColor)); - b = SP_COLOR_U_TO_F( U_RGBAGetB(d->dc[d->level].textColor)); - d->dc[d->level].style.stroke.value.color.set( r, g, b ); - d->dc[d->level].stroke_mode = DRAW_PAINT; - d->dc[d->level].stroke_set = true; - } + if (pEmr->elp.elpWidth) { + int cur_level = d->level; + d->level = d->emf_obj[index].level; + double pen_width = pix_to_abs_size( d, pEmr->elp.elpWidth ); + d->level = cur_level; + d->dc[d->level].style.stroke_width.value = pen_width; + } 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; + int cur_level = d->level; + d->level = d->emf_obj[index].level; + double pen_width = pix_to_abs_size( d, 1 ); + d->level = cur_level; + d->dc[d->level].style.stroke_width.value = pen_width; + } + + if( pEmr->elp.elpBrushStyle == U_BS_SOLID){ + double r, g, b; + r = SP_COLOR_U_TO_F( U_RGBAGetR(pEmr->elp.elpColor) ); + g = SP_COLOR_U_TO_F( U_RGBAGetG(pEmr->elp.elpColor) ); + b = SP_COLOR_U_TO_F( U_RGBAGetB(pEmr->elp.elpColor) ); + d->dc[d->level].style.stroke.value.color.set( r, g, b ); + d->dc[d->level].stroke_mode = DRAW_PAINT; + d->dc[d->level].stroke_set = true; + } + else if(pEmr->elp.elpBrushStyle == U_BS_HATCHED){ + d->dc[d->level].stroke_idx = add_hatch(d, pEmr->elp.elpHatch, pEmr->elp.elpColor); + d->dc[d->level].stroke_recidx = index; // used if the hatch needs to be redone due to bkMode, textmode, etc. changes + d->dc[d->level].stroke_mode = DRAW_PATTERN; + d->dc[d->level].stroke_set = true; + } + 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); + d->dc[d->level].stroke_mode = DRAW_IMAGE; + d->dc[d->level].stroke_set = true; + } + else { // U_BS_PATTERN and anything strange that falls in, stroke is solid textColor + double r, g, b; + r = SP_COLOR_U_TO_F( U_RGBAGetR(d->dc[d->level].textColor)); + g = SP_COLOR_U_TO_F( U_RGBAGetG(d->dc[d->level].textColor)); + b = SP_COLOR_U_TO_F( U_RGBAGetB(d->dc[d->level].textColor)); + d->dc[d->level].style.stroke.value.color.set( r, g, b ); + d->dc[d->level].stroke_mode = DRAW_PAINT; + d->dc[d->level].stroke_set = true; + } } } @@ -1264,38 +1321,39 @@ Emf::select_brush(PEMF_CALLBACK_DATA d, int index) if (index >= 0 && index < d->n_obj){ iType = ((PU_EMR) (d->emf_obj[index].lpEMFR))->iType; if(iType == U_EMR_CREATEBRUSHINDIRECT){ - PU_EMRCREATEBRUSHINDIRECT pEmr = (PU_EMRCREATEBRUSHINDIRECT) d->emf_obj[index].lpEMFR; - if( pEmr->lb.lbStyle == U_BS_SOLID){ - double r, g, b; - r = SP_COLOR_U_TO_F( U_RGBAGetR(pEmr->lb.lbColor) ); - g = SP_COLOR_U_TO_F( U_RGBAGetG(pEmr->lb.lbColor) ); - b = SP_COLOR_U_TO_F( U_RGBAGetB(pEmr->lb.lbColor) ); - d->dc[d->level].style.fill.value.color.set( r, g, b ); - d->dc[d->level].fill_mode = DRAW_PAINT; - d->dc[d->level].fill_set = true; - } - else if(pEmr->lb.lbStyle == U_BS_HATCHED){ - d->dc[d->level].fill_idx = add_hatch(d, pEmr->lb.lbHatch, pEmr->lb.lbColor); - d->dc[d->level].fill_mode = DRAW_PATTERN; - d->dc[d->level].fill_set = true; - } + PU_EMRCREATEBRUSHINDIRECT pEmr = (PU_EMRCREATEBRUSHINDIRECT) d->emf_obj[index].lpEMFR; + if( pEmr->lb.lbStyle == U_BS_SOLID){ + double r, g, b; + r = SP_COLOR_U_TO_F( U_RGBAGetR(pEmr->lb.lbColor) ); + g = SP_COLOR_U_TO_F( U_RGBAGetG(pEmr->lb.lbColor) ); + b = SP_COLOR_U_TO_F( U_RGBAGetB(pEmr->lb.lbColor) ); + d->dc[d->level].style.fill.value.color.set( r, g, b ); + d->dc[d->level].fill_mode = DRAW_PAINT; + d->dc[d->level].fill_set = true; + } + else if(pEmr->lb.lbStyle == U_BS_HATCHED){ + d->dc[d->level].fill_idx = add_hatch(d, pEmr->lb.lbHatch, pEmr->lb.lbColor); + d->dc[d->level].fill_recidx = index; // used if the hatch needs to be redone due to bkMode, textmode, etc. changes + d->dc[d->level].fill_mode = DRAW_PATTERN; + d->dc[d->level].fill_set = true; + } } else if(iType == U_EMR_CREATEDIBPATTERNBRUSHPT || iType == U_EMR_CREATEMONOBRUSH){ - PU_EMRCREATEDIBPATTERNBRUSHPT pEmr = (PU_EMRCREATEDIBPATTERNBRUSHPT) d->emf_obj[index].lpEMFR; - tidx = add_image(d, (void *) pEmr, pEmr->cbBits, pEmr->cbBmi, pEmr->iUsage, pEmr->offBits, pEmr->offBmi); - if(tidx == 0xFFFFFFFF){ // This happens if createmonobrush has a DIB that isn't monochrome - double r, g, b; - r = SP_COLOR_U_TO_F( U_RGBAGetR(d->dc[d->level].textColor)); - g = SP_COLOR_U_TO_F( U_RGBAGetG(d->dc[d->level].textColor)); - b = SP_COLOR_U_TO_F( U_RGBAGetB(d->dc[d->level].textColor)); - d->dc[d->level].style.fill.value.color.set( r, g, b ); - d->dc[d->level].fill_mode = DRAW_PAINT; - } - else { - d->dc[d->level].fill_idx = tidx; - d->dc[d->level].fill_mode = DRAW_IMAGE; - } - d->dc[d->level].fill_set = true; + PU_EMRCREATEDIBPATTERNBRUSHPT pEmr = (PU_EMRCREATEDIBPATTERNBRUSHPT) d->emf_obj[index].lpEMFR; + tidx = add_image(d, (void *) pEmr, pEmr->cbBits, pEmr->cbBmi, pEmr->iUsage, pEmr->offBits, pEmr->offBmi); + if(tidx == 0xFFFFFFFF){ // This happens if createmonobrush has a DIB that isn't monochrome + double r, g, b; + r = SP_COLOR_U_TO_F( U_RGBAGetR(d->dc[d->level].textColor)); + g = SP_COLOR_U_TO_F( U_RGBAGetG(d->dc[d->level].textColor)); + b = SP_COLOR_U_TO_F( U_RGBAGetB(d->dc[d->level].textColor)); + d->dc[d->level].style.fill.value.color.set( r, g, b ); + d->dc[d->level].fill_mode = DRAW_PAINT; + } + else { + d->dc[d->level].fill_idx = tidx; + d->dc[d->level].fill_mode = DRAW_IMAGE; + } + d->dc[d->level].fill_set = true; } } } @@ -1312,18 +1370,18 @@ Emf::select_font(PEMF_CALLBACK_DATA d, int index) 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 - is supported, and the remainder, it it really is a U_LOGFONT_PANOSE record, is ignored + /* 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 + is supported, and the remainder, it it really is a U_LOGFONT_PANOSE record, is ignored */ int cur_level = d->level; d->level = d->emf_obj[index].level; double font_size = pix_to_abs_size( d, pEmr->elfw.elfLogFont.lfHeight ); - /* snap the font_size to the nearest 1/32nd of a point. - (The size is converted from Pixels to points, snapped, and converted back.) - See the notes where d->D2Pscale[XY] are set for the reason why. - Typically this will set the font to the desired exact size. If some peculiar size - was intended this will, at worst, make it .03125 off, which is unlikely to be a problem. */ + /* snap the font_size to the nearest 1/32nd of a point. + (The size is converted from Pixels to points, snapped, and converted back.) + See the notes where d->D2Pscale[XY] are set for the reason why. + Typically this will set the font to the desired exact size. If some peculiar size + was intended this will, at worst, make it .03125 off, which is unlikely to be a problem. */ font_size = round(20.0 * 0.8 * font_size)/(20.0 * 0.8); d->level = cur_level; d->dc[d->level].style.font_size.computed = font_size; @@ -1348,14 +1406,14 @@ Emf::select_font(PEMF_CALLBACK_DATA d, int index) // malformed EMF with empty filename may exist, ignore font change if encountered char *ctmp = U_Utf16leToUtf8((uint16_t *) (pEmr->elfw.elfLogFont.lfFaceName), U_LF_FACESIZE, NULL); if(ctmp){ - if (d->dc[d->level].font_name){ free(d->dc[d->level].font_name); } - if(*ctmp){ - d->dc[d->level].font_name = ctmp; - } - else { // Malformed EMF might specify an empty font name - free(ctmp); - d->dc[d->level].font_name = strdup("Arial"); // Default font, EMF spec says device can pick whatever it wants - } + if (d->dc[d->level].font_name){ free(d->dc[d->level].font_name); } + if(*ctmp){ + d->dc[d->level].font_name = ctmp; + } + else { // Malformed EMF might specify an empty font name + free(ctmp); + d->dc[d->level].font_name = strdup("Arial"); // Default font, EMF spec says device can pick whatever it wants + } } 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 } @@ -1387,8 +1445,8 @@ Emf::insert_object(PEMF_CALLBACK_DATA d, int index, int type, PU_ENHMETARECORD p } } -/* Identify probable Adobe Illustrator produced EMF files, which do strange things with the scaling. - The few so far observed all had this format. +/* Identify probable Adobe Illustrator produced EMF files, which do strange things with the scaling. + The few so far observed all had this format. */ int Emf::AI_hack(PU_EMRHEADER pEmr){ int ret=0; @@ -1398,9 +1456,9 @@ int Emf::AI_hack(PU_EMRHEADER pEmr){ char *string = NULL; if(pEmr->nDescription)string = U_Utf16leToUtf8((uint16_t *)((char *) pEmr + pEmr->offDescription), pEmr->nDescription, NULL); if(string){ - if((pEmr->nDescription >= 13) && + if((pEmr->nDescription >= 13) && (0==strcmp("Adobe Systems",string)) && - (nEmr->emr.iType == U_EMR_SETMAPMODE) && + (nEmr->emr.iType == U_EMR_SETMAPMODE) && (nEmr->iMode == U_MM_ANISOTROPIC)){ ret=1; } free(string); } @@ -1408,75 +1466,75 @@ int Emf::AI_hack(PU_EMRHEADER pEmr){ } /** - \fn create a UTF-32LE buffer and fill it with UNICODE unknown character - \param count number of copies of the Unicode unknown character to fill with + \fn create a UTF-32LE buffer and fill it with UNICODE unknown character + \param count number of copies of the Unicode unknown character to fill with */ uint32_t *Emf::unknown_chars(size_t count){ - uint32_t *res = (uint32_t *) malloc(sizeof(uint32_t) * (count + 1)); - if(!res)throw "Inkscape fatal memory allocation error - cannot continue"; - for(uint32_t i=0; ioutsvg) += tmp_image.str().c_str(); - - *(d->outsvg) += "/> \n"; - *(d->path) = ""; + ) && + rgba_px + ){ + sub_px = RGBA_to_RGBA( + rgba_px, // full pixel array from DIB + width, // Width of pixel array + height, // Height of pixel array + sx,sy, // starting point in pixel array + &sw,&sh // columns/rows to extract from the pixel array (output array size) + ); + + if(!sub_px)sub_px=rgba_px; + toPNG( // Get the image from the RGBA px into mempng + &mempng, + sw, sh, // size of the extracted pixel array + sub_px + ); + free(sub_px); + } + } + gchar *base64String; + if(dibparams == U_BI_JPEG){ + tmp_image << " xlink:href=\"data:image/jpeg;base64,"; + base64String = g_base64_encode((guchar*) px, numCt ); + tmp_image << base64String ; + g_free(base64String); + } + else if(dibparams==U_BI_PNG){ + tmp_image << " xlink:href=\"data:image/png;base64,"; + base64String = g_base64_encode((guchar*) px, numCt ); + tmp_image << base64String ; + g_free(base64String); + } + else if(mempng.buffer){ + tmp_image << " xlink:href=\"data:image/png;base64,"; + gchar *base64String = g_base64_encode((guchar*) mempng.buffer, mempng.size ); + free(mempng.buffer); + tmp_image << base64String ; + g_free(base64String); + } + else { + tmp_image << " xlink:href=\"data:image/png;base64,"; + // insert a random 3x4 blotch otherwise + tmp_image << "iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAAA3NCSVQICAjb4U/gAAAALElEQVQImQXBQQ2AMAAAsUJQMSWI2H8qME1yMshojwrvGB8XcHKvR1XtOTc/8HENumHCsOMAAAAASUVORK5CYII="; + } + + tmp_image << "\"\n height=\"" << dh << "\"\n width=\"" << dw << "\"\n"; + + tmp_image << " transform=" << current_matrix(d, dx, dy, 1); // calculate appropriate offset + *(d->outsvg) += "\n\t outsvg) += tmp_image.str().c_str(); + + *(d->outsvg) += "/> \n"; + *(d->path) = ""; } /** - \fn myEnhMetaFileProc(char *contents, unsigned int length, PEMF_CALLBACK_DATA lpData) - \param contents binary contents of an EMF file - \param length length in bytes of contents - \param d Inkscape data structures returned by this call + \fn myEnhMetaFileProc(char *contents, unsigned int length, PEMF_CALLBACK_DATA lpData) + \param contents binary contents of an EMF file + \param length length in bytes of contents + \param d Inkscape data structures returned by this call */ //THis was a callback, just build it into a normal function int Emf::myEnhMetaFileProc(char *contents, unsigned int length, PEMF_CALLBACK_DATA d) @@ -1555,6 +1614,8 @@ int Emf::myEnhMetaFileProc(char *contents, unsigned int length, PEMF_CALLBACK_DA int OK =1; PU_ENHMETARECORD lpEMFR; TCHUNK_SPECS tsp; + uint32_t tbkMode = U_TRANSPARENT; // holds proposed change to bkMode, if text is involved saving these to the DC must wait until the text is written + U_COLORREF tbkColor = U_RGB(255, 255, 255); // holds proposed change to bkColor /* initialize the tsp for text reassembly */ tsp.string = NULL; @@ -1572,29 +1633,34 @@ int Emf::myEnhMetaFileProc(char *contents, unsigned int length, PEMF_CALLBACK_DA tsp.color.Reserved = 0; /* not used */ tsp.italics = 0; tsp.weight = 80; + tsp.decoration = TXTDECOR_NONE; tsp.condensed = 100; tsp.co = 0; tsp.fi_idx = -1; /* set to an invalid */ - + while(OK){ - if(off>=length)return(0); //normally should exit from while after EMREOF sets OK to false. + if(off>=length)return(0); //normally should exit from while after EMREOF sets OK to false. lpEMFR = (PU_ENHMETARECORD)(contents + off); // Uncomment the following to track down toxic records //std::cout << "record type: " << lpEMFR->iType << " length: " << lpEMFR->nSize << " offset: " << off <nSize; - + SVGOStringStream tmp_outsvg; SVGOStringStream tmp_path; SVGOStringStream tmp_str; SVGOStringStream dbg_str; - - emr_mask = emr_properties(lpEMFR->iType); + + emr_mask = emr_properties(lpEMFR->iType); if(emr_mask == U_EMR_INVALID){ throw "Inkscape fatal memory allocation error - cannot continue"; } /* Uncomment the following to track down text problems */ //std::cout << "tri->dirty:"<< d->tri->dirty << " emr_mask: " << std::hex << emr_mask << std::dec << std::endl; - if ( (emr_mask != 0xFFFFFFFF) && (emr_mask & U_DRAW_TEXT) && d->tri->dirty){ // next record is valid type and forces pending text to be drawn immediately + + // incompatible change to text drawing detected (color or background change) forces out existing text + // OR + // next record is valid type and forces pending text to be drawn immediately + if ((d->dc[d->level].dirty & DIRTY_TEXT) || ((emr_mask != 0xFFFFFFFF) && (emr_mask & U_DRAW_TEXT) && d->tri->dirty)){ TR_layout_analyze(d->tri); TR_layout_2_svg(d->tri); SVGOStringStream ts; @@ -1602,52 +1668,78 @@ int Emf::myEnhMetaFileProc(char *contents, unsigned int length, PEMF_CALLBACK_DA *(d->outsvg) += ts.str().c_str(); d->tri = trinfo_clear(d->tri); } + if(d->dc[d->level].dirty){ //Apply the delayed background changes, clear the flag + d->dc[d->level].bkMode = tbkMode; + memcpy(&(d->dc[d->level].bkColor),&tbkColor, sizeof(U_COLORREF)); + + if(d->dc[d->level].dirty & DIRTY_TEXT){ + // U_COLORREF and TRCOLORREF are exactly the same in memory, but the compiler needs some convincing... + if(tbkMode == U_TRANSPARENT){ (void) trinfo_load_bk(d->tri, BKCLR_NONE, *(TRCOLORREF *) &tbkColor); } + else { (void) trinfo_load_bk(d->tri, BKCLR_LINE, *(TRCOLORREF *) &tbkColor); } // Opaque + } + + /* It is possible to have a series of EMF records that would result in + the following creating hash patterns which are never used. For instance, if + there were a series of records that changed the background color but did nothing + else. + */ + if((d->dc[d->level].stroke_mode == DRAW_PATTERN) && (d->dc[d->level].dirty & DIRTY_STROKE)){ + select_extpen(d, d->dc[d->level].stroke_recidx); + } + + if((d->dc[d->level].fill_mode == DRAW_PATTERN) && (d->dc[d->level].dirty & DIRTY_FILL)){ + select_brush(d, d->dc[d->level].fill_recidx); + } + + 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" - << " test0 " << ( d->mask & U_DRAW_VISIBLE) - << " test1 " << ( d->mask & U_DRAW_FORCE) - << " test2 " << (emr_mask & U_DRAW_ALTERS) + << " test0 " << ( d->mask & U_DRAW_VISIBLE) + << " test1 " << ( d->mask & U_DRAW_FORCE) + << " test2 " << (emr_mask & U_DRAW_ALTERS) << " test3 " << (emr_mask & U_DRAW_VISIBLE) << " test4 " << !(d->mask & U_DRAW_ONLYTO) << " test5 " << ((d->mask & U_DRAW_ONLYTO) && !(emr_mask & U_DRAW_ONLYTO) ) << std::endl; */ - if ( (emr_mask != 0xFFFFFFFF) && // next record is valid type - (d->mask & U_DRAW_VISIBLE) && // This record is drawable - ( (d->mask & U_DRAW_FORCE) || // This draw is forced by STROKE/FILL/STROKEANDFILL PATH - (emr_mask & U_DRAW_ALTERS) || // Next record would alter the drawing environment in some way - ( (emr_mask & U_DRAW_VISIBLE) // Next record is visible... - && - ( - ( !(d->mask & U_DRAW_ONLYTO) ) // Non *TO records cannot be followed by any Visible - || - ((d->mask & U_DRAW_ONLYTO) && !(emr_mask & U_DRAW_ONLYTO) ) // *TO records can only be followed by other *TO records - ) + if( + (emr_mask != 0xFFFFFFFF) && // next record is valid type + (d->mask & U_DRAW_VISIBLE) && // Current set of objects are drawable + ( + (d->mask & U_DRAW_FORCE) || // This draw is forced by STROKE/FILL/STROKEANDFILL PATH + (emr_mask & U_DRAW_ALTERS) || // Next record would alter the drawing environment in some way + ( + (emr_mask & U_DRAW_VISIBLE) && // Next record is visible... + ( + ( !(d->mask & U_DRAW_ONLYTO) ) || // Non *TO records cannot be followed by any Visible + ((d->mask & U_DRAW_ONLYTO) && !(emr_mask & U_DRAW_ONLYTO) )// *TO records can only be followed by other *TO records + ) ) - ) - ){ + ) + ){ // std::cout << "PATH DRAW at TOP" << std::endl; - *(d->outsvg) += " drawtype){ // explicit draw type EMR record - output_style(d, d->drawtype); - } - else if(d->mask & U_DRAW_CLOSED){ // implicit draw type - output_style(d, U_EMR_STROKEANDFILLPATH); - } - else { - output_style(d, U_EMR_STROKEPATH); - } - *(d->outsvg) += "\n\t"; - *(d->outsvg) += "\n\td=\""; // this is the ONLY place d=" should be used!!!! - *(d->outsvg) += *(d->path); - *(d->outsvg) += " \" /> \n"; - *(d->path) = ""; - // reset the flags - d->mask = 0; - d->drawtype = 0; + *(d->outsvg) += " drawtype){ // explicit draw type EMR record + output_style(d, d->drawtype); + } + else if(d->mask & U_DRAW_CLOSED){ // implicit draw type + output_style(d, U_EMR_STROKEANDFILLPATH); + } + else { + output_style(d, U_EMR_STROKEPATH); + } + *(d->outsvg) += "\n\t"; + *(d->outsvg) += "\n\td=\""; // this is the ONLY place d=" should be used!!!! + *(d->outsvg) += *(d->path); + *(d->outsvg) += " \" /> \n"; + *(d->path) = ""; + // reset the flags + 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; @@ -1681,31 +1773,31 @@ std::cout << "BEFORE DRAW" d->PixelsInX = pEmr->rclBounds.right - pEmr->rclBounds.left + 1; d->PixelsInY = pEmr->rclBounds.bottom - pEmr->rclBounds.top + 1; - /* - calculate ratio of Inkscape dpi/EMF device dpi - This can cause problems later due to accuracy limits in the EMF. A high resolution - EMF might have a final D2Pscale[XY] of 0.074998, and adjusting the (integer) device size - by 1 will still not get it exactly to 0.075. Later when the font size is calculated it - can end up as 29.9992 or 22.4994 instead of the intended 30 or 22.5. This is handled by - snapping font sizes to the nearest .01. The best estimate is made by using both values. + /* + calculate ratio of Inkscape dpi/EMF device dpi + This can cause problems later due to accuracy limits in the EMF. A high resolution + EMF might have a final D2Pscale[XY] of 0.074998, and adjusting the (integer) device size + by 1 will still not get it exactly to 0.075. Later when the font size is calculated it + can end up as 29.9992 or 22.4994 instead of the intended 30 or 22.5. This is handled by + snapping font sizes to the nearest .01. The best estimate is made by using both values. */ if ((pEmr->szlMillimeters.cx + pEmr->szlMillimeters.cy) && ( pEmr->szlDevice.cx + pEmr->szlDevice.cy)){ - d->E2IdirY = 1.0; // assume MM_TEXT, if not, this will be changed later - d->D2PscaleX = d->D2PscaleY = PX_PER_MM * - (double)(pEmr->szlMillimeters.cx + pEmr->szlMillimeters.cy)/ - (double)( pEmr->szlDevice.cx + pEmr->szlDevice.cy); + d->E2IdirY = 1.0; // assume MM_TEXT, if not, this will be changed later + d->D2PscaleX = d->D2PscaleY = PX_PER_MM * + (double)(pEmr->szlMillimeters.cx + pEmr->szlMillimeters.cy)/ + (double)( pEmr->szlDevice.cx + pEmr->szlDevice.cy); } trinfo_load_qe(d->tri, d->D2PscaleX); /* quantization error that will affect text positions */ - /* Adobe Illustrator files set mapmode to MM_ANISOTROPIC and somehow or other this + /* Adobe Illustrator files set mapmode to MM_ANISOTROPIC and somehow or other this converts the rclFrame values from MM_HIMETRIC to MM_HIENGLISH, with another factor of 3 thrown in for good measure. Ours not to question why... */ if(AI_hack(pEmr)){ - d->MM100InX *= 25.4/(10.0*3.0); - d->MM100InY *= 25.4/(10.0*3.0); - d->D2PscaleX *= 25.4/(10.0*3.0); - d->D2PscaleY *= 25.4/(10.0*3.0); + d->MM100InX *= 25.4/(10.0*3.0); + d->MM100InY *= 25.4/(10.0*3.0); + d->D2PscaleX *= 25.4/(10.0*3.0); + d->D2PscaleY *= 25.4/(10.0*3.0); } d->MMX = d->MM100InX / 100.0; @@ -1718,7 +1810,7 @@ std::cout << "BEFORE DRAW" d->ulCornerInX = pEmr->rclBounds.left; d->ulCornerInY = pEmr->rclBounds.top; d->ulCornerOutX = d->ulCornerInX * d->D2PscaleX; - d->ulCornerOutY = d->ulCornerInY * d->E2IdirY * d->D2PscaleY; + d->ulCornerOutY = d->ulCornerInY * d->E2IdirY * d->D2PscaleY; tmp_outdef << " width=\"" << d->MMX << "mm\"\n" << @@ -1733,7 +1825,7 @@ std::cout << "BEFORE DRAW" if (pEmr->nHandles) { d->n_obj = pEmr->nHandles; d->emf_obj = new EMF_OBJECT[d->n_obj]; - + // Init the new emf_obj list elements to null, provided the // dynamic allocation succeeded. if ( d->emf_obj != NULL ) @@ -1825,7 +1917,7 @@ std::cout << "BEFORE DRAW" } tmp_path << tmp_str.str().c_str(); - + break; } case U_EMR_POLYBEZIERTO: @@ -1926,8 +2018,8 @@ std::cout << "BEFORE DRAW" d->dc[d->level].ScaleInX = (double) d->dc[d->level].sizeView.cx / (double) d->dc[d->level].sizeWnd.cx; d->dc[d->level].ScaleInY = (double) d->dc[d->level].sizeView.cy / (double) d->dc[d->level].sizeWnd.cy; if(d->dc[d->level].ScaleInY < 0){ - d->dc[d->level].ScaleInY *= -1.0; - d->E2IdirY = -1.0; + d->dc[d->level].ScaleInY *= -1.0; + d->E2IdirY = -1.0; } } else { @@ -1963,14 +2055,14 @@ std::cout << "BEFORE DRAW" if (!d->dc[d->level].sizeWnd.cx || !d->dc[d->level].sizeWnd.cy) { d->dc[d->level].sizeWnd = d->dc[d->level].sizeView; } - + /* scales logical to EMF pixels, transfer a negative sign on Y, if any */ if (d->dc[d->level].sizeWnd.cx && d->dc[d->level].sizeWnd.cy) { d->dc[d->level].ScaleInX = (double) d->dc[d->level].sizeView.cx / (double) d->dc[d->level].sizeWnd.cx; d->dc[d->level].ScaleInY = (double) d->dc[d->level].sizeView.cy / (double) d->dc[d->level].sizeWnd.cy; - if(d->dc[d->level].ScaleInY < 0){ - d->dc[d->level].ScaleInY *= -1.0; - d->E2IdirY = -1.0; + if( d->dc[d->level].ScaleInY < 0){ + d->dc[d->level].ScaleInY *= -1.0; + d->E2IdirY = -1.0; } } else { @@ -2004,36 +2096,49 @@ std::cout << "BEFORE DRAW" dbg_str << "\n"; PU_EMRSETMAPMODE pEmr = (PU_EMRSETMAPMODE) lpEMFR; switch (pEmr->iMode){ - case U_MM_TEXT: - default: - // Use all values from the header. - break; - /* For all of the following the indicated scale this will be encoded in WindowExtEx/ViewportExtex - and show up in ScaleIn[XY] - */ - case U_MM_LOMETRIC: // 1 LU = 0.1 mm, - case U_MM_HIMETRIC: // 1 LU = 0.01 mm - case U_MM_LOENGLISH: // 1 LU = 0.1 in - case U_MM_HIENGLISH: // 1 LU = 0.01 in - case U_MM_TWIPS: // 1 LU = 1/1440 in - d->E2IdirY = -1.0; - // Use d->D2Pscale[XY] values from the header. - break; - case U_MM_ISOTROPIC: // ScaleIn[XY] should be set elsewhere by SETVIEWPORTEXTEX and SETWINDOWEXTEX - case U_MM_ANISOTROPIC: - break; - } - break; - } - case U_EMR_SETBKMODE: dbg_str << "\n"; break; + case U_MM_TEXT: + default: + // Use all values from the header. + break; + /* For all of the following the indicated scale this will be encoded in WindowExtEx/ViewportExtex + and show up in ScaleIn[XY] + */ + case U_MM_LOMETRIC: // 1 LU = 0.1 mm, + case U_MM_HIMETRIC: // 1 LU = 0.01 mm + case U_MM_LOENGLISH: // 1 LU = 0.1 in + case U_MM_HIENGLISH: // 1 LU = 0.01 in + case U_MM_TWIPS: // 1 LU = 1/1440 in + d->E2IdirY = -1.0; + // Use d->D2Pscale[XY] values from the header. + break; + case U_MM_ISOTROPIC: // ScaleIn[XY] should be set elsewhere by SETVIEWPORTEXTEX and SETWINDOWEXTEX + case U_MM_ANISOTROPIC: + break; + } + break; + } + case U_EMR_SETBKMODE: + { + dbg_str << "\n"; + PU_EMRSETBKMODE pEmr = (PU_EMRSETBKMODE) lpEMFR; + tbkMode = pEmr->iMode; + if(tbkMode != d->dc[d->level].bkMode){ + d->dc[d->level].dirty |= DIRTY_TEXT; + if(tbkMode != d->dc[d->level].bkMode){ + if(d->dc[d->level].fill_mode == DRAW_PATTERN){ d->dc[d->level].dirty |= DIRTY_FILL; } + if(d->dc[d->level].stroke_mode == DRAW_PATTERN){ d->dc[d->level].dirty |= DIRTY_STROKE; } + } + memcpy(&tbkColor,&(d->dc[d->level].bkColor),sizeof(U_COLORREF)); + } + break; + } case U_EMR_SETPOLYFILLMODE: { dbg_str << "\n"; PU_EMRSETPOLYFILLMODE pEmr = (PU_EMRSETPOLYFILLMODE) lpEMFR; d->dc[d->level].style.fill_rule.value = - (pEmr->iMode == U_ALTERNATE ? 0 : - pEmr->iMode == U_WINDING ? 1 : 0); + (pEmr->iMode == U_ALTERNATE ? 0 : (pEmr->iMode == U_WINDING ? 1 : 0)); break; } case U_EMR_SETROP2: @@ -2067,6 +2172,11 @@ std::cout << "BEFORE DRAW" PU_EMRSETTEXTCOLOR pEmr = (PU_EMRSETTEXTCOLOR) lpEMFR; d->dc[d->level].textColor = pEmr->crColor; + if(tbkMode != d->dc[d->level].bkMode){ + if(d->dc[d->level].fill_mode == DRAW_PATTERN){ d->dc[d->level].dirty |= DIRTY_FILL; } + if(d->dc[d->level].stroke_mode == DRAW_PATTERN){ d->dc[d->level].dirty |= DIRTY_STROKE; } + } + // not text_dirty, because multicolored complex text is supported in libTERE break; } case U_EMR_SETBKCOLOR: @@ -2074,7 +2184,13 @@ std::cout << "BEFORE DRAW" dbg_str << "\n"; PU_EMRSETBKCOLOR pEmr = (PU_EMRSETBKCOLOR) lpEMFR; - d->dc[d->level].bkColor = pEmr->crColor; + tbkColor = pEmr->crColor; + if(memcmp(&tbkColor, &(d->dc[d->level].bkColor), sizeof(U_COLORREF))){ + d->dc[d->level].dirty |= DIRTY_TEXT; + if(d->dc[d->level].fill_mode == DRAW_PATTERN){ d->dc[d->level].dirty |= DIRTY_FILL; } + if(d->dc[d->level].stroke_mode == DRAW_PATTERN){ d->dc[d->level].dirty |= DIRTY_STROKE; } + tbkMode = d->dc[d->level].bkMode; + } break; } case U_EMR_OFFSETCLIPRGN: dbg_str << "\n"; break; @@ -2141,7 +2257,7 @@ std::cout << "BEFORE DRAW" case U_EMR_RESTOREDC: { dbg_str << "\n"; - + PU_EMRRESTOREDC pEmr = (PU_EMRRESTOREDC) lpEMFR; int old_level = d->level; if (pEmr->iRelative >= 0) { @@ -2157,8 +2273,8 @@ std::cout << "BEFORE DRAW" delete[] d->dc[old_level].style.stroke_dash.dash; } if(d->dc[old_level].font_name){ - free(d->dc[old_level].font_name); // else memory leak - d->dc[old_level].font_name = NULL; + free(d->dc[old_level].font_name); // else memory leak + d->dc[old_level].font_name = NULL; } old_level--; } @@ -2227,7 +2343,7 @@ std::cout << "BEFORE DRAW" d->dc[d->level].worldTransform.eM22 = c22;; d->dc[d->level].worldTransform.eDx = c31; d->dc[d->level].worldTransform.eDy = c32; - + break; } case U_MWT_RIGHTMULTIPLY: @@ -2382,6 +2498,7 @@ std::cout << "BEFORE DRAW" } case U_EMR_DELETEOBJECT: dbg_str << "\n"; + // Objects here are not deleted until the draw completes, new ones may write over an existing one. break; case U_EMR_ANGLEARC: dbg_str << "\n"; @@ -2406,7 +2523,7 @@ std::cout << "BEFORE DRAW" d->mask |= emr_mask; - *(d->outsvg) += " outsvg) += " iType); // *(d->outsvg) += "\n\t"; *(d->outsvg) += tmp_ellipse.str().c_str(); @@ -2444,40 +2561,40 @@ std::cout << "BEFORE DRAW" double f1 = 1.0 - f; double cnx = corner.cx/2; double cny = corner.cy/2; - + SVGOStringStream tmp_rectangle; tmp_rectangle << "\n" - << " M " + << " M " << pix_to_xy(d, rc.left , rc.top + cny ) << "\n"; - tmp_rectangle << " C " + tmp_rectangle << " C " << pix_to_xy(d, rc.left , rc.top + cny*f1 ) - << " " + << " " << pix_to_xy(d, rc.left + cnx*f1 , rc.top ) - << " " + << " " << pix_to_xy(d, rc.left + cnx , rc.top ) << "\n"; - tmp_rectangle << " L " + tmp_rectangle << " L " << pix_to_xy(d, rc.right - cnx , rc.top ) << "\n"; - tmp_rectangle << " C " + tmp_rectangle << " C " << pix_to_xy(d, rc.right - cnx*f1 , rc.top ) - << " " + << " " << pix_to_xy(d, rc.right , rc.top + cny*f1 ) - << " " + << " " << pix_to_xy(d, rc.right , rc.top + cny ) << "\n"; tmp_rectangle << " L " << pix_to_xy(d, rc.right , rc.bottom - cny ) << "\n"; - tmp_rectangle << " C " + tmp_rectangle << " C " << pix_to_xy(d, rc.right , rc.bottom - cny*f1 ) << " " << pix_to_xy(d, rc.right - cnx*f1 , rc.bottom ) << " " << pix_to_xy(d, rc.right - cnx , rc.bottom ) << "\n"; - tmp_rectangle << " L " + tmp_rectangle << " L " << pix_to_xy(d, rc.left + cnx , rc.bottom ) << "\n"; tmp_rectangle << " C " @@ -2503,17 +2620,17 @@ std::cout << "BEFORE DRAW" int f2 = (d->arcdir == U_AD_COUNTERCLOCKWISE ? 0 : 1); int stat = emr_arc_points( lpEMFR, &f1, f2, ¢er, &start, &end, &size); if(!stat){ - tmp_path << "\n\tM " << pix_to_xy(d, start.x, start.y); - tmp_path << " A " << pix_to_abs_size(d, size.x)/2.0 << "," << pix_to_abs_size(d, size.y)/2.0 ; - tmp_path << " "; - tmp_path << 180.0 * current_rotation(d)/M_PI; - tmp_path << " "; - tmp_path << " " << f1 << "," << f2 << " "; - tmp_path << pix_to_xy(d, end.x, end.y) << " \n"; - d->mask |= emr_mask; + tmp_path << "\n\tM " << pix_to_xy(d, start.x, start.y); + tmp_path << " A " << pix_to_abs_size(d, size.x)/2.0 << "," << pix_to_abs_size(d, size.y)/2.0 ; + tmp_path << " "; + tmp_path << 180.0 * current_rotation(d)/M_PI; + tmp_path << " "; + tmp_path << " " << f1 << "," << f2 << " "; + tmp_path << pix_to_xy(d, end.x, end.y) << " \n"; + d->mask |= emr_mask; } else { - dbg_str << "\n"; + dbg_str << "\n"; } break; } @@ -2524,18 +2641,18 @@ std::cout << "BEFORE DRAW" int f1; int f2 = (d->arcdir == U_AD_COUNTERCLOCKWISE ? 0 : 1); if(!emr_arc_points( lpEMFR, &f1, f2, ¢er, &start, &end, &size)){ - tmp_path << "\n\tM " << pix_to_xy(d, start.x, start.y); - tmp_path << " A " << pix_to_abs_size(d, size.x)/2.0 << "," << pix_to_abs_size(d, size.y)/2.0 ; - tmp_path << " "; - tmp_path << 180.0 * current_rotation(d)/M_PI; - tmp_path << " "; - tmp_path << " " << f1 << "," << f2 << " "; - tmp_path << pix_to_xy(d, end.x, end.y) << " \n"; - tmp_path << " z "; - d->mask |= emr_mask; + tmp_path << "\n\tM " << pix_to_xy(d, start.x, start.y); + tmp_path << " A " << pix_to_abs_size(d, size.x)/2.0 << "," << pix_to_abs_size(d, size.y)/2.0 ; + tmp_path << " "; + tmp_path << 180.0 * current_rotation(d)/M_PI; + tmp_path << " "; + tmp_path << " " << f1 << "," << f2 << " "; + tmp_path << pix_to_xy(d, end.x, end.y) << " \n"; + tmp_path << " z "; + d->mask |= emr_mask; } else { - dbg_str << "\n"; + dbg_str << "\n"; } break; } @@ -2546,19 +2663,19 @@ std::cout << "BEFORE DRAW" int f1; int f2 = (d->arcdir == U_AD_COUNTERCLOCKWISE ? 0 : 1); if(!emr_arc_points( lpEMFR, &f1, f2, ¢er, &start, &end, &size)){ - tmp_path << "\n\tM " << pix_to_xy(d, center.x, center.y); - tmp_path << "\n\tL " << pix_to_xy(d, start.x, start.y); - tmp_path << " A " << pix_to_abs_size(d, size.x)/2.0 << "," << pix_to_abs_size(d, size.y)/2.0 ; - tmp_path << " "; - tmp_path << 180.0 * current_rotation(d)/M_PI; - tmp_path << " "; - tmp_path << " " << f1 << "," << f2 << " "; - tmp_path << pix_to_xy(d, end.x, end.y) << " \n"; - tmp_path << " z "; - d->mask |= emr_mask; + tmp_path << "\n\tM " << pix_to_xy(d, center.x, center.y); + tmp_path << "\n\tL " << pix_to_xy(d, start.x, start.y); + tmp_path << " A " << pix_to_abs_size(d, size.x)/2.0 << "," << pix_to_abs_size(d, size.y)/2.0 ; + tmp_path << " "; + tmp_path << 180.0 * current_rotation(d)/M_PI; + tmp_path << " "; + tmp_path << " " << f1 << "," << f2 << " "; + tmp_path << pix_to_xy(d, end.x, end.y) << " \n"; + tmp_path << " z "; + d->mask |= emr_mask; } else { - dbg_str << "\n"; + dbg_str << "\n"; } break; } @@ -2587,20 +2704,20 @@ std::cout << "BEFORE DRAW" int f1; int f2 = (d->arcdir == U_AD_COUNTERCLOCKWISE ? 0 : 1); if(!emr_arc_points( lpEMFR, &f1, f2, ¢er, &start, &end, &size)){ - // draw a line from current position to start - tmp_path << "\n\tL " << pix_to_xy(d, start.x, start.y); - tmp_path << "\n\tM " << pix_to_xy(d, start.x, start.y); - tmp_path << " A " << pix_to_abs_size(d, size.x)/2.0 << "," << pix_to_abs_size(d, size.y)/2.0 ; - tmp_path << " "; - tmp_path << 180.0 * current_rotation(d)/M_PI; - tmp_path << " "; - tmp_path << " " << f1 << "," << f2 << " "; - tmp_path << pix_to_xy(d, end.x, end.y)<< " "; - - d->mask |= emr_mask; + // draw a line from current position to start + tmp_path << "\n\tL " << pix_to_xy(d, start.x, start.y); + tmp_path << "\n\tM " << pix_to_xy(d, start.x, start.y); + tmp_path << " A " << pix_to_abs_size(d, size.x)/2.0 << "," << pix_to_abs_size(d, size.y)/2.0 ; + tmp_path << " "; + tmp_path << 180.0 * current_rotation(d)/M_PI; + tmp_path << " "; + tmp_path << " " << f1 << "," << f2 << " "; + tmp_path << pix_to_xy(d, end.x, end.y)<< " "; + + d->mask |= emr_mask; } else { - dbg_str << "\n"; + dbg_str << "\n"; } break; } @@ -2653,12 +2770,12 @@ std::cout << "BEFORE DRAW" { dbg_str << "\n"; if(d->mask & U_DRAW_PATH){ // Operation only effects declared paths - if(!(d->mask & U_DRAW_CLOSED)){ // Close a path not explicitly closed by an EMRCLOSEFIGURE, otherwise fill makes no sense - tmp_path << "\n\tz"; - d->mask |= U_DRAW_CLOSED; - } - d->mask |= emr_mask; - d->drawtype = U_EMR_FILLPATH; + if(!(d->mask & U_DRAW_CLOSED)){ // Close a path not explicitly closed by an EMRCLOSEFIGURE, otherwise fill makes no sense + tmp_path << "\n\tz"; + d->mask |= U_DRAW_CLOSED; + } + d->mask |= emr_mask; + d->drawtype = U_EMR_FILLPATH; } break; } @@ -2666,12 +2783,12 @@ std::cout << "BEFORE DRAW" { dbg_str << "\n"; if(d->mask & U_DRAW_PATH){ // Operation only effects declared paths - if(!(d->mask & U_DRAW_CLOSED)){ // Close a path not explicitly closed by an EMRCLOSEFIGURE, otherwise fill makes no sense - tmp_path << "\n\tz"; - d->mask |= U_DRAW_CLOSED; - } - d->mask |= emr_mask; - d->drawtype = U_EMR_STROKEANDFILLPATH; + if(!(d->mask & U_DRAW_CLOSED)){ // Close a path not explicitly closed by an EMRCLOSEFIGURE, otherwise fill makes no sense + tmp_path << "\n\tz"; + d->mask |= U_DRAW_CLOSED; + } + d->mask |= emr_mask; + d->drawtype = U_EMR_STROKEANDFILLPATH; } break; } @@ -2679,8 +2796,8 @@ std::cout << "BEFORE DRAW" { dbg_str << "\n"; if(d->mask & U_DRAW_PATH){ // Operation only effects declared paths - d->mask |= emr_mask; - d->drawtype = U_EMR_STROKEPATH; + d->mask |= emr_mask; + d->drawtype = U_EMR_STROKEPATH; } break; } @@ -2698,7 +2815,7 @@ std::cout << "BEFORE DRAW" case U_EMR_COMMENT: { dbg_str << "\n"; - + PU_EMRCOMMENT pEmr = (PU_EMRCOMMENT) lpEMFR; char *szTxt = (char *) pEmr->Data; @@ -2713,13 +2830,13 @@ std::cout << "BEFORE DRAW" } if (0 && strlen(tmp_str.str().c_str())) { - tmp_outsvg << " \n"; } - + break; - } + } case U_EMR_FILLRGN: dbg_str << "\n"; break; case U_EMR_FRAMERGN: dbg_str << "\n"; break; case U_EMR_INVERTRGN: dbg_str << "\n"; break; @@ -2738,8 +2855,8 @@ std::cout << "BEFORE DRAW" dbg_str << "\n"; PU_EMRBITBLT pEmr = (PU_EMRBITBLT) lpEMFR; - // Treat all nonImage bitblts as a rectangular write. Definitely not correct, but at - // least it leaves objects where the operations should have been. + // Treat all nonImage bitblts as a rectangular write. Definitely not correct, but at + // least it leaves objects where the operations should have been. if (!pEmr->cbBmiSrc) { // should be an application of a DIBPATTERNBRUSHPT, use a solid color instead @@ -2768,12 +2885,12 @@ std::cout << "BEFORE DRAW" //source position within the bitmap, in pixels int sx = pEmr->Src.x + pEmr->xformSrc.eDx; int sy = pEmr->Src.y + pEmr->xformSrc.eDy; - int sw = 0; // extract all of the image + int sw = 0; // extract all of the image int sh = 0; if(sx<0)sx=0; if(sy<0)sy=0; common_image_extraction(d,pEmr,dx,dy,dw,dh,sx,sy,sw,sh, - pEmr->iUsageSrc, pEmr->offBitsSrc, pEmr->cbBitsSrc, pEmr->offBmiSrc, pEmr->cbBmiSrc); + pEmr->iUsageSrc, pEmr->offBitsSrc, pEmr->cbBitsSrc, pEmr->offBmiSrc, pEmr->cbBmiSrc); } break; } @@ -2790,10 +2907,10 @@ std::cout << "BEFORE DRAW" //source position within the bitmap, in pixels int sx = pEmr->Src.x + pEmr->xformSrc.eDx; int sy = pEmr->Src.y + pEmr->xformSrc.eDy; - int sw = pEmr->cSrc.x; // extract the specified amount of the image + int sw = pEmr->cSrc.x; // extract the specified amount of the image int sh = pEmr->cSrc.y; common_image_extraction(d,pEmr,dx,dy,dw,dh,sx,sy,sw,sh, - pEmr->iUsageSrc, pEmr->offBitsSrc, pEmr->cbBitsSrc, pEmr->offBmiSrc, pEmr->cbBmiSrc); + pEmr->iUsageSrc, pEmr->offBitsSrc, pEmr->cbBitsSrc, pEmr->offBmiSrc, pEmr->cbBmiSrc); } break; } @@ -2812,7 +2929,7 @@ std::cout << "BEFORE DRAW" int sw = 0; // extract all of the image int sh = 0; common_image_extraction(d,pEmr,dx,dy,dw,dh,sx,sy,sw,sh, - pEmr->iUsageSrc, pEmr->offBitsSrc, pEmr->cbBitsSrc, pEmr->offBmiSrc, pEmr->cbBmiSrc); + pEmr->iUsageSrc, pEmr->offBitsSrc, pEmr->cbBitsSrc, pEmr->offBmiSrc, pEmr->cbBmiSrc); } break; } @@ -2833,10 +2950,10 @@ std::cout << "BEFORE DRAW" double dh = pix_to_abs_size( d, pEmr->cDest.y); int sx = pEmr->Src.x; //source position within the bitmap, in pixels int sy = pEmr->Src.y; - int sw = pEmr->cSrc.x; // extract the specified amount of the image + int sw = pEmr->cSrc.x; // extract the specified amount of the image int sh = pEmr->cSrc.y; common_image_extraction(d,pEmr,dx,dy,dw,dh,sx,sy,sw,sh, - pEmr->iUsageSrc, pEmr->offBitsSrc, pEmr->cbBitsSrc, pEmr->offBmiSrc, pEmr->cbBmiSrc); + pEmr->iUsageSrc, pEmr->offBitsSrc, pEmr->cbBitsSrc, pEmr->offBmiSrc, pEmr->cbBmiSrc); dbg_str << "\n"; break; @@ -2872,7 +2989,7 @@ std::cout << "BEFORE DRAW" y1 = pEmr->emrtext.ptlReference.y; cChars = 0; } - + if (d->dc[d->level].textAlign & U_TA_UPDATECP) { x1 = d->dc[d->level].cur.x; y1 = d->dc[d->level].cur.y; @@ -2885,35 +3002,35 @@ std::cout << "BEFORE DRAW" uint32_t *dup_wt = NULL; - if( lpEMFR->iType==U_EMR_EXTTEXTOUTA){ - /* These should be JUST ASCII, but they might not be... - If it holds Utf-8 or plain ASCII the first call will succeed. - If not, assume that it holds Latin1. - If that fails then someting is really screwed up! - */ - dup_wt = U_Utf8ToUtf32le((char *) pEmr + pEmr->emrtext.offString, pEmr->emrtext.nChars, NULL); - if(!dup_wt)dup_wt = U_Latin1ToUtf32le((char *) pEmr + pEmr->emrtext.offString, pEmr->emrtext.nChars, NULL); - if(!dup_wt)dup_wt = unknown_chars(pEmr->emrtext.nChars); + if( lpEMFR->iType==U_EMR_EXTTEXTOUTA){ + /* These should be JUST ASCII, but they might not be... + If it holds Utf-8 or plain ASCII the first call will succeed. + If not, assume that it holds Latin1. + If that fails then someting is really screwed up! + */ + dup_wt = U_Utf8ToUtf32le((char *) pEmr + pEmr->emrtext.offString, pEmr->emrtext.nChars, NULL); + if(!dup_wt)dup_wt = U_Latin1ToUtf32le((char *) pEmr + pEmr->emrtext.offString, pEmr->emrtext.nChars, NULL); + if(!dup_wt)dup_wt = unknown_chars(pEmr->emrtext.nChars); } else if( lpEMFR->iType==U_EMR_EXTTEXTOUTW){ - dup_wt = U_Utf16leToUtf32le((uint16_t *)((char *) pEmr + pEmr->emrtext.offString), pEmr->emrtext.nChars, NULL); - if(!dup_wt)dup_wt = unknown_chars(pEmr->emrtext.nChars); + dup_wt = U_Utf16leToUtf32le((uint16_t *)((char *) pEmr + pEmr->emrtext.offString), pEmr->emrtext.nChars, NULL); + if(!dup_wt)dup_wt = unknown_chars(pEmr->emrtext.nChars); } else { // U_EMR_SMALLTEXTOUT - if(pEmrS->fuOptions & U_ETO_SMALL_CHARS){ - dup_wt = U_Utf8ToUtf32le((char *) pEmrS + roff, cChars, NULL); - } - else { - dup_wt = U_Utf16leToUtf32le((uint16_t *)((char *) pEmrS + roff), cChars, NULL); - } - if(!dup_wt)dup_wt = unknown_chars(cChars); + if(pEmrS->fuOptions & U_ETO_SMALL_CHARS){ + dup_wt = U_Utf8ToUtf32le((char *) pEmrS + roff, cChars, NULL); + } + else { + dup_wt = U_Utf16leToUtf32le((uint16_t *)((char *) pEmrS + roff), cChars, NULL); + } + if(!dup_wt)dup_wt = unknown_chars(cChars); } msdepua(dup_wt); //convert everything in Microsoft's private use area. For Symbol, Wingdings, Dingbats if(NonToUnicode(dup_wt, d->dc[d->level].font_name)){ - free(d->dc[d->level].font_name); - d->dc[d->level].font_name = strdup("Times New Roman"); + free(d->dc[d->level].font_name); + d->dc[d->level].font_name = strdup("Times New Roman"); } char *ansi_text; @@ -2921,8 +3038,8 @@ std::cout << "BEFORE DRAW" free(dup_wt); // Empty string or starts with an invalid escape/control sequence, which is bogus text. Throw it out before g_markup_escape_text can make things worse if(*((uint8_t *)ansi_text) <= 0x1F){ - free(ansi_text); - ansi_text=NULL; + free(ansi_text); + ansi_text=NULL; } if (ansi_text) { @@ -2947,29 +3064,29 @@ std::cout << "BEFORE DRAW" tsp.italics = FC_SLANT_ROMAN; break; } switch(d->dc[d->level].style.font_weight.value){ - case SP_CSS_FONT_WEIGHT_100: tsp.weight = FC_WEIGHT_THIN ; break; - case SP_CSS_FONT_WEIGHT_200: tsp.weight = FC_WEIGHT_EXTRALIGHT ; break; - case SP_CSS_FONT_WEIGHT_300: tsp.weight = FC_WEIGHT_LIGHT ; break; - case SP_CSS_FONT_WEIGHT_400: tsp.weight = FC_WEIGHT_NORMAL ; break; - case SP_CSS_FONT_WEIGHT_500: tsp.weight = FC_WEIGHT_MEDIUM ; break; - case SP_CSS_FONT_WEIGHT_600: tsp.weight = FC_WEIGHT_SEMIBOLD ; break; - case SP_CSS_FONT_WEIGHT_700: tsp.weight = FC_WEIGHT_BOLD ; break; - case SP_CSS_FONT_WEIGHT_800: tsp.weight = FC_WEIGHT_EXTRABOLD ; break; - case SP_CSS_FONT_WEIGHT_900: tsp.weight = FC_WEIGHT_HEAVY ; break; - case SP_CSS_FONT_WEIGHT_NORMAL: tsp.weight = FC_WEIGHT_NORMAL ; break; - case SP_CSS_FONT_WEIGHT_BOLD: tsp.weight = FC_WEIGHT_BOLD ; break; - case SP_CSS_FONT_WEIGHT_LIGHTER: tsp.weight = FC_WEIGHT_EXTRALIGHT ; break; - case SP_CSS_FONT_WEIGHT_BOLDER: tsp.weight = FC_WEIGHT_EXTRABOLD ; break; - default: tsp.weight = FC_WEIGHT_NORMAL ; break; + case SP_CSS_FONT_WEIGHT_100: tsp.weight = FC_WEIGHT_THIN ; break; + case SP_CSS_FONT_WEIGHT_200: tsp.weight = FC_WEIGHT_EXTRALIGHT ; break; + case SP_CSS_FONT_WEIGHT_300: tsp.weight = FC_WEIGHT_LIGHT ; break; + case SP_CSS_FONT_WEIGHT_400: tsp.weight = FC_WEIGHT_NORMAL ; break; + case SP_CSS_FONT_WEIGHT_500: tsp.weight = FC_WEIGHT_MEDIUM ; break; + case SP_CSS_FONT_WEIGHT_600: tsp.weight = FC_WEIGHT_SEMIBOLD ; break; + case SP_CSS_FONT_WEIGHT_700: tsp.weight = FC_WEIGHT_BOLD ; break; + case SP_CSS_FONT_WEIGHT_800: tsp.weight = FC_WEIGHT_EXTRABOLD ; break; + case SP_CSS_FONT_WEIGHT_900: tsp.weight = FC_WEIGHT_HEAVY ; break; + case SP_CSS_FONT_WEIGHT_NORMAL: tsp.weight = FC_WEIGHT_NORMAL ; break; + case SP_CSS_FONT_WEIGHT_BOLD: tsp.weight = FC_WEIGHT_BOLD ; break; + case SP_CSS_FONT_WEIGHT_LIGHTER: tsp.weight = FC_WEIGHT_EXTRALIGHT ; break; + case SP_CSS_FONT_WEIGHT_BOLDER: tsp.weight = FC_WEIGHT_EXTRABOLD ; break; + default: tsp.weight = FC_WEIGHT_NORMAL ; break; } // EMF textalignment is a bit strange: 0x6 is center, 0x2 is right, 0x0 is left, the value 0x4 is also drawn left - tsp.taln = ((d->dc[d->level].textAlign & U_TA_CENTER) == U_TA_CENTER) ? ALICENTER : - (((d->dc[d->level].textAlign & U_TA_CENTER) == U_TA_LEFT) ? ALILEFT : - ALIRIGHT); - tsp.taln |= ((d->dc[d->level].textAlign & U_TA_BASEBIT) ? ALIBASE : - ((d->dc[d->level].textAlign & U_TA_BOTTOM) ? ALIBOT : - ALITOP)); + tsp.taln = ((d->dc[d->level].textAlign & U_TA_CENTER) == U_TA_CENTER) ? ALICENTER : + (((d->dc[d->level].textAlign & U_TA_CENTER) == U_TA_LEFT) ? ALILEFT : + ALIRIGHT); + tsp.taln |= ((d->dc[d->level].textAlign & U_TA_BASEBIT) ? ALIBASE : + ((d->dc[d->level].textAlign & U_TA_BOTTOM) ? ALIBOT : + ALITOP)); tsp.ldir = (d->dc[d->level].textAlign & U_TA_RTLREADING ? LDIR_RL : LDIR_LR); // language direction tsp.condensed = FC_WIDTH_NORMAL; // Not implemented well in libTERE (yet) tsp.ori = d->dc[d->level].style.baseline_shift.value; // For now orientation is always the same as escapement @@ -2983,19 +3100,19 @@ std::cout << "BEFORE DRAW" else { tsp.co=0; } int status = trinfo_load_textrec(d->tri, &tsp, tsp.ori,TR_EMFBOT); // ori is actually escapement - if(status==-1){ // change of escapement, emit what we have and reset - TR_layout_analyze(d->tri); - TR_layout_2_svg(d->tri); - ts << d->tri->out; - *(d->outsvg) += ts.str().c_str(); - d->tri = trinfo_clear(d->tri); - (void) trinfo_load_textrec(d->tri, &tsp, tsp.ori,TR_EMFBOT); // ignore return status, it must work + if(status==-1){ // change of escapement, emit what we have and reset + TR_layout_analyze(d->tri); + TR_layout_2_svg(d->tri); + ts << d->tri->out; + *(d->outsvg) += ts.str().c_str(); + d->tri = trinfo_clear(d->tri); + (void) trinfo_load_textrec(d->tri, &tsp, tsp.ori,TR_EMFBOT); // ignore return status, it must work } - + g_free(escaped_text); free(ansi_text); } - + break; } case U_EMR_POLYBEZIER16: @@ -3035,7 +3152,7 @@ std::cout << "BEFORE DRAW" unsigned int first = 0; d->mask |= emr_mask; - + // skip the first point? tmp_poly << "\n\tM " << pix_to_xy( d, apts[first].x, apts[first].y ) << " "; @@ -3198,11 +3315,11 @@ std::cout << "BEFORE DRAW" case U_EMR_TRANSPARENTBLT: dbg_str << "\n"; break; case U_EMR_UNDEF117: dbg_str << "\n"; break; case U_EMR_GRADIENTFILL: dbg_str << "\n"; break; - /* Gradient fill is doable for rectangles because those correspond to linear gradients. However, - the general case for the triangle fill, with a different color in each corner of the triangle, - has no SVG equivalent and cannot be easily emulated with SVG gradients. Except that so far - I (DM) have not been able to make an EMF with a rectangular gradientfill record which is not - completely toxic to other EMF readers. So far now, do nothing. + /* Gradient fill is doable for rectangles because those correspond to linear gradients. However, + the general case for the triangle fill, with a different color in each corner of the triangle, + has no SVG equivalent and cannot be easily emulated with SVG gradients. Except that so far + I (DM) have not been able to make an EMF with a rectangular gradientfill record which is not + completely toxic to other EMF readers. So far now, do nothing. */ case U_EMR_SETLINKEDUFIS: dbg_str << "\n"; break; case U_EMR_SETTEXTJUSTIFICATION: dbg_str << "\n"; break; @@ -3219,7 +3336,7 @@ std::cout << "BEFORE DRAW" } //end of while // When testing, uncomment the following to show the final SVG derived from the EMF -//std::cout << *(d->outsvg) << std::endl; +//std::cout << *(d->outsvg) << std::endl; (void) emr_properties(U_EMR_INVALID); // force the release of the lookup table memory, returned value is irrelevant return 1; @@ -3251,10 +3368,10 @@ typedef struct #pragma pack( pop ) void Emf::free_emf_strings(EMF_STRINGS name){ - if(name.count){ - for(int i=0; i< name.count; i++){ free(name.strings[i]); } - free(name.strings); - } + if(name.count){ + for(int i=0; i< name.count; i++){ free(name.strings[i]); } + free(name.strings); + } } SPDocument * @@ -3266,9 +3383,9 @@ Emf::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri ) memset(&d, 0, sizeof(EMF_CALLBACK_DATA)); for(int i = 0; i < EMF_MAX_DC+1; i++){ // be sure all values and pointers are empty to start with - memset(&(d.dc[i]),0,sizeof(EMF_DEVICE_CONTEXT)); + memset(&(d.dc[i]),0,sizeof(EMF_DEVICE_CONTEXT)); } - + d.dc[0].worldTransform.eM11 = 1.0; d.dc[0].worldTransform.eM12 = 0.0; d.dc[0].worldTransform.eM21 = 0.0; @@ -3278,7 +3395,9 @@ Emf::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri ) d.dc[0].font_name = strdup("Arial"); // Default font, EMF spec says device can pick whatever it wants d.dc[0].textColor = U_RGB(0, 0, 0); // default foreground color (black) d.dc[0].bkColor = U_RGB(255, 255, 255); // default background color (white) - + d.dc[0].bkMode = U_TRANSPARENT; + d.dc[0].dirty = 0; + if (uri == NULL) { return NULL; } @@ -3302,24 +3421,34 @@ Emf::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri ) d.images.count = 0; d.images.strings = NULL; + // set up the size default for patterns in defs. This might not be referenced if there are no patterns defined in the drawing. + + *(d.defs) += "\n"; + *(d.defs) += " \n"; + *(d.defs) += " \n"; + + size_t length; char *contents; - if(emf_readdata(uri, &contents, &length))return(NULL); + if(emf_readdata(uri, &contents, &length))return(NULL); d.pDesc = NULL; - + // set up the text reassembly system if(!(d.tri = trinfo_init(NULL)))return(NULL); (void) trinfo_load_ft_opts(d.tri, 1, - FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP, + FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP, FT_KERNING_UNSCALED); - + (void) myEnhMetaFileProc(contents,length, &d); free(contents); - - if (d.pDesc) - free( d.pDesc ); + if (d.pDesc){ free( d.pDesc ); } // std::cout << "SVG Output: " << std::endl << *(d.outsvg) << std::endl; @@ -3331,17 +3460,17 @@ Emf::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri ) delete d.defs; free_emf_strings(d.hatches); free_emf_strings(d.images); - + if (d.emf_obj) { int i; for (i=0; i Date: Thu, 21 Mar 2013 15:07:06 +0100 Subject: 988601-changes_2013_03_20a.patch 1. Fixes the clang warnings noted in a post above, other than those associated with alignment caused by casting. 2. Fixes some minor rounding errors in both WMF and EMF input/output. Round trip open/save cycles are conservative for EMF and WMF files (excluding any features that are not full supported in inkscape or the target file format, for instance, gradients, which must be emulated.) 3. Fixed a missing break in the input WMF LINETO record handling, which was falling through into the MOVETO and generating a harmless extra "M" operation in a path. 4. WMF has no POLYPOLYLINE record. However input that maps into essentially a polypolyline record in SVG is common, for instance dashed lines that have been converted to line segments. These end up in SVG as a series of M L M L draw commands in the path. Earlier each M L pair was going out as a polyline record, now they go as a series of MOVETO/LINETO records. The primary reason for this change is that without this change the behavior described in (2) does not occur. 5. Fixed an issue where polyline and polygon records in some instances ended up with an extra copy of their last point. (bzr r11668.1.62) --- src/extension/internal/emf-inout.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'src/extension/internal/emf-inout.cpp') diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp index 7a5757235..aaa817c61 100644 --- a/src/extension/internal/emf-inout.cpp +++ b/src/extension/internal/emf-inout.cpp @@ -61,11 +61,10 @@ namespace Inkscape { namespace Extension { namespace Internal { - -static U_RECTL rc_old; -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; +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; /** Construct a PNG in memory from an RGB from the EMF file @@ -291,8 +290,8 @@ Emf::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar const *filena if (ext == NULL) return; - bool new_val = mod->get_param_bool("textToPath"); - bool new_FixPPTCharPos = mod->get_param_bool("FixPPTCharPos"); // character position bug + bool new_val = mod->get_param_bool("textToPath"); + bool new_FixPPTCharPos = mod->get_param_bool("FixPPTCharPos"); // character position bug // reserve FixPPT2 for opacity bug. Currently EMF does not export opacity values bool new_FixPPTDashLine = mod->get_param_bool("FixPPTDashLine"); // dashed line bug bool new_FixPPTGrad2Polys = mod->get_param_bool("FixPPTGrad2Polys"); // gradient bug @@ -1415,7 +1414,7 @@ Emf::select_font(PEMF_CALLBACK_DATA d, int index) d->dc[d->level].font_name = strdup("Arial"); // Default font, EMF spec says device can pick whatever it wants } } - 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 + d->dc[d->level].style.baseline_shift.value = round((double)((pEmr->elfw.elfLogFont.lfEscapement + 3600) % 3600)) / 10.0; // use baseline_shift instead of text_transform to avoid overflow } void -- cgit v1.2.3 From d19db89e2e22d4e09c539bd42823d511aebb669f Mon Sep 17 00:00:00 2001 From: David Mathog <> Date: Wed, 19 Jun 2013 19:20:33 +0200 Subject: changes_2013_05_22a.patch: 1. Resolves issue of bug #988601 message 170 (Support of 'Unset' styles in EMF export). 2. Implements CSS 3 (and CSS 2) text-decoration support. Note that it does not yet provide any method of adding these features - at present it just shows whatever is in the SVG. This new code is also used to display EMF/WMF strike-through and underline text decorations when these files are read in. Those decorations may also be written out to EMF/WMF. Other text decoration features, like overline, or dotted lines, are dropped. For SVG text-decoration -line, -style, -color are all implemented. CSS3 provides two ways to represent the same state, this code uses the compound text-decoration method rather than the 3 fields method. Also it leaves out keywords that are not needed and would break backwards compatibility. For instance: text-decoration: underline solid is valid, but would break CSS2. Solid is the default, so that sort of case is written as: text-decoration: underline If the state is CSS3 specific all of the needed fields are of course include, like text-decoration: underline wavy red 3. It incorporates the fix for bug 1181326 (Text edit mishandles span of just colored spaces) 4. It incorporates further changes to text editing so that style can be changed on spans consisting of only spaces when text decorations are present in the span. 5. It incorporates code to disable text decorations when text so marked is mapped onto a path. 6. Fixed more bugs in Hebrew language support than I can remember. Hebrew language export/import to EMF now works quite well. (See the examples in libTERE v 0.7.) WMF does not support unicode and for all intents and purposes Inkscape has no way to read or write Hebrew to it. Some of more important things that now work that didn't (or didn't always): Hebrew diacritical marks, R/L/center justification, and bidirectional text. The Hebrew fonts "Ezra SIL" and "EZRA SIL SR" should be installed before viewing the libTERE examples, otherwise font substitutions will cause some text shifts. 7. Implemented font failover in Text Reassemble, which makes the process more robust. (Again, see the examples in libTERE. ) (bzr r11668.1.71) --- src/extension/internal/emf-inout.cpp | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) (limited to 'src/extension/internal/emf-inout.cpp') diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp index aaa817c61..c14393cc2 100644 --- a/src/extension/internal/emf-inout.cpp +++ b/src/extension/internal/emf-inout.cpp @@ -1400,8 +1400,10 @@ Emf::select_font(PEMF_CALLBACK_DATA d, int index) pEmr->elfw.elfLogFont.lfWeight == U_FW_EXTRABOLD ? SP_CSS_FONT_WEIGHT_BOLDER : U_FW_NORMAL; 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.text_decoration.underline = pEmr->elfw.elfLogFont.lfUnderline; - d->dc[d->level].style.text_decoration.line_through = pEmr->elfw.elfLogFont.lfStrikeOut; + d->dc[d->level].style.text_decoration_line.underline = pEmr->elfw.elfLogFont.lfUnderline; + d->dc[d->level].style.text_decoration_line.line_through = pEmr->elfw.elfLogFont.lfStrikeOut; + d->dc[d->level].style.text_decoration_line.set = true; + d->dc[d->level].style.text_decoration_line.inherit = false; // malformed EMF with empty filename may exist, ignore font change if encountered char *ctmp = U_Utf16leToUtf8((uint16_t *) (pEmr->elfw.elfLogFont.lfFaceName), U_LF_FACESIZE, NULL); if(ctmp){ @@ -2988,6 +2990,7 @@ std::cout << "BEFORE DRAW" y1 = pEmr->emrtext.ptlReference.y; cChars = 0; } + uint32_t fOptions = pEmr->emrtext.fOptions; if (d->dc[d->level].textAlign & U_TA_UPDATECP) { x1 = d->dc[d->level].cur.x; @@ -3078,6 +3081,10 @@ std::cout << "BEFORE DRAW" case SP_CSS_FONT_WEIGHT_BOLDER: tsp.weight = FC_WEIGHT_EXTRABOLD ; break; default: tsp.weight = FC_WEIGHT_NORMAL ; break; } + // EMF only supports two types of text decoration + tsp.decoration = TXTDECOR_NONE; + if(d->dc[d->level].style.text_decoration_line.underline){ tsp.decoration |= TXTDECOR_UNDER; } + if(d->dc[d->level].style.text_decoration_line.line_through){ tsp.decoration |= TXTDECOR_STRIKE;} // EMF textalignment is a bit strange: 0x6 is center, 0x2 is right, 0x0 is left, the value 0x4 is also drawn left tsp.taln = ((d->dc[d->level].textAlign & U_TA_CENTER) == U_TA_CENTER) ? ALICENTER : @@ -3086,13 +3093,19 @@ std::cout << "BEFORE DRAW" tsp.taln |= ((d->dc[d->level].textAlign & U_TA_BASEBIT) ? ALIBASE : ((d->dc[d->level].textAlign & U_TA_BOTTOM) ? ALIBOT : ALITOP)); - tsp.ldir = (d->dc[d->level].textAlign & U_TA_RTLREADING ? LDIR_RL : LDIR_LR); // language direction + + // language direction can be encoded two ways, U_TA_RTLREADING is preferred + if( (fOptions & U_ETO_RTLREADING) || (d->dc[d->level].textAlign & U_TA_RTLREADING) ){ tsp.ldir = LDIR_RL; } + else{ tsp.ldir = LDIR_LR; } + tsp.condensed = FC_WIDTH_NORMAL; // Not implemented well in libTERE (yet) tsp.ori = d->dc[d->level].style.baseline_shift.value; // For now orientation is always the same as escapement tsp.ori += 180.0 * current_rotation(d)/ M_PI; // radians to degrees tsp.string = (uint8_t *) U_strdup(escaped_text); // this will be free'd much later at a trinfo_clear(). tsp.fs = d->dc[d->level].style.font_size.computed * 0.8; // Font size in points - (void) trinfo_load_fontname(d->tri, (uint8_t *)d->dc[d->level].font_name, &tsp); + char *fontspec = TR_construct_fontspec(&tsp, d->dc[d->level].font_name); + tsp.fi_idx = ftinfo_load_fontname(d->tri->fti,fontspec); + free(fontspec); // when font name includes narrow it may not be set to "condensed". Narrow fonts do not work well anyway though // as the metrics from fontconfig may not match, or the font may not be present. if(0<= TR_findcasesub(d->dc[d->level].font_name, (char *) "Narrow")){ tsp.co=1; } @@ -3101,7 +3114,7 @@ std::cout << "BEFORE DRAW" int status = trinfo_load_textrec(d->tri, &tsp, tsp.ori,TR_EMFBOT); // ori is actually escapement if(status==-1){ // change of escapement, emit what we have and reset TR_layout_analyze(d->tri); - TR_layout_2_svg(d->tri); + TR_layout_2_svg(d->tri); ts << d->tri->out; *(d->outsvg) += ts.str().c_str(); d->tri = trinfo_clear(d->tri); -- cgit v1.2.3 From 1f8f2ee3fe058a03b06f24dda950795f2639be6c Mon Sep 17 00:00:00 2001 From: Kris De Gussem Date: Thu, 15 Aug 2013 22:38:20 +0200 Subject: patch of David Mathog in bugtread 988601 comment 186 (bzr r11668.1.73) --- src/extension/internal/emf-inout.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/extension/internal/emf-inout.cpp') diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp index c14393cc2..7dc0ee314 100644 --- a/src/extension/internal/emf-inout.cpp +++ b/src/extension/internal/emf-inout.cpp @@ -2705,9 +2705,8 @@ std::cout << "BEFORE DRAW" int f1; int f2 = (d->arcdir == U_AD_COUNTERCLOCKWISE ? 0 : 1); if(!emr_arc_points( lpEMFR, &f1, f2, ¢er, &start, &end, &size)){ - // draw a line from current position to start + // draw a line from current position to start, arc from there tmp_path << "\n\tL " << pix_to_xy(d, start.x, start.y); - tmp_path << "\n\tM " << pix_to_xy(d, start.x, start.y); tmp_path << " A " << pix_to_abs_size(d, size.x)/2.0 << "," << pix_to_abs_size(d, size.y)/2.0 ; tmp_path << " "; tmp_path << 180.0 * current_rotation(d)/M_PI; -- cgit v1.2.3 From 3c4acd93fbc00c466d24b74f05d874dc2d7d6b95 Mon Sep 17 00:00:00 2001 From: su_v Date: Thu, 29 Aug 2013 23:28:08 +0200 Subject: adapt to changes in r12471 (unit refactoring) (bzr r11668.1.76) --- src/extension/internal/emf-inout.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'src/extension/internal/emf-inout.cpp') diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp index 7dc0ee314..9dba3b77c 100644 --- a/src/extension/internal/emf-inout.cpp +++ b/src/extension/internal/emf-inout.cpp @@ -42,11 +42,10 @@ #include "extension/output.h" #include "display/drawing.h" #include "display/drawing-item.h" -#include "unit-constants.h" #include "clear-n_.h" #include "document.h" #include "libunicode-convert/unicode-convert.h" - +#include "util/units.h" #include "emf-print.h" #include "emf-inout.h" @@ -1784,7 +1783,7 @@ std::cout << "BEFORE DRAW" */ if ((pEmr->szlMillimeters.cx + pEmr->szlMillimeters.cy) && ( pEmr->szlDevice.cx + pEmr->szlDevice.cy)){ d->E2IdirY = 1.0; // assume MM_TEXT, if not, this will be changed later - d->D2PscaleX = d->D2PscaleY = PX_PER_MM * + d->D2PscaleX = d->D2PscaleY = Inkscape::Util::Quantity::convert(1, "mm", "px") * (double)(pEmr->szlMillimeters.cx + pEmr->szlMillimeters.cy)/ (double)( pEmr->szlDevice.cx + pEmr->szlDevice.cy); } @@ -1804,8 +1803,8 @@ std::cout << "BEFORE DRAW" d->MMX = d->MM100InX / 100.0; d->MMY = d->MM100InY / 100.0; - d->PixelsOutX = d->MMX * PX_PER_MM; - d->PixelsOutY = d->MMY * PX_PER_MM; + d->PixelsOutX = d->MMX * Inkscape::Util::Quantity::convert(1, "mm", "px"); + d->PixelsOutY = d->MMY * Inkscape::Util::Quantity::convert(1, "mm", "px"); // Upper left corner, from header rclBounds, in device units, usually both 0, but not always d->ulCornerInX = pEmr->rclBounds.left; -- cgit v1.2.3 From 14f607efe6cb318756d74604c3cd6810799b5434 Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Sat, 31 Aug 2013 18:05:13 +0200 Subject: Move libuemf to a separate directory. Rename libunicode-convert to symbol_convert and put it in libuemf. (bzr r12490) --- src/extension/internal/emf-inout.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/extension/internal/emf-inout.cpp') diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp index 9dba3b77c..eeecc8e59 100644 --- a/src/extension/internal/emf-inout.cpp +++ b/src/extension/internal/emf-inout.cpp @@ -30,7 +30,8 @@ #include #include #include -#define EMF_DRIVER // work around for SPStyle issue +#include + #include "sp-root.h" #include "sp-path.h" #include "style.h" @@ -44,7 +45,6 @@ #include "display/drawing-item.h" #include "clear-n_.h" #include "document.h" -#include "libunicode-convert/unicode-convert.h" #include "util/units.h" #include "emf-print.h" -- cgit v1.2.3 From 2549a383386efa10d4298daef2494927ebdc5ca8 Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Mon, 2 Sep 2013 01:39:00 +0200 Subject: Unduplicate some code in the metafile printing extensions (bzr r12499) --- src/extension/internal/emf-inout.cpp | 4 ---- 1 file changed, 4 deletions(-) (limited to 'src/extension/internal/emf-inout.cpp') diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp index eeecc8e59..b185d3348 100644 --- a/src/extension/internal/emf-inout.cpp +++ b/src/extension/internal/emf-inout.cpp @@ -317,10 +317,6 @@ Emf::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar const *filena } -enum drawmode {DRAW_PAINT, DRAW_PATTERN, DRAW_IMAGE}; // apply to either fill or stroke - - - /* given the transformation matrix from worldTranform return the scale in the matrix part. Assumes that the matrix is not used to skew, invert, or make another distorting transformation. */ double Emf::current_scale(PEMF_CALLBACK_DATA d){ -- cgit v1.2.3