diff -rNu3 build.xml build.xml --- build.xml 2012-09-13 10:24:25.000000000 -0700 +++ build.xml 2012-09-13 13:15:09.000000000 -0700 @@ -453,7 +453,7 @@ stripcommand="${archutil}strip" objcopycommand="${archutil}objcopy"> - -mwindows + -mconsole -mthreads @@ -527,7 +527,7 @@ stripcommand="${archutil}strip" objcopycommand="${archutil}objcopy"> - -mwindows + -mconsole -mthreads diff -rNu3 configure.ac configure.ac --- configure.ac 2012-09-13 10:24:25.000000000 -0700 +++ configure.ac 2012-09-13 13:15:09.000000000 -0700 @@ -11,6 +11,7 @@ AC_CANONICAL_HOST AC_CONFIG_SRCDIR([src/main.cpp]) +AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([dist-zip dist-bzip2 tar-pax]) AC_ARG_ENABLE([lsb], AS_HELP_STRING([--enable-lsb], [LSB-compatible build configuration]), [ @@ -69,7 +70,7 @@ AC_MSG_CHECKING([compiler support for -Werror=format-security]) ink_svd_CPPFLAGS="$CPPFLAGS" CPPFLAGS="-Werror=format-security $CPPFLAGS" - AC_COMPILE_IFELSE(AC_LANG_PROGRAM([]), [ink_opt_ok=yes], [ink_opt_ok=no]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([])], [ink_opt_ok=yes], [ink_opt_ok=no]) AC_MSG_RESULT([$ink_opt_ok]) if test "x$ink_opt_ok" != "xyes"; then CPPFLAGS="$ink_svd_CPPFLAGS" @@ -83,7 +84,7 @@ AC_MSG_CHECKING([compiler support for -Wno-pointer-sign]) ink_svd_CFLAGS="$CFLAGS" CFLAGS="-Wno-pointer-sign $CFLAGS" - AC_COMPILE_IFELSE(AC_LANG_PROGRAM([]), [ink_opt_ok=yes], [ink_opt_ok=no]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([])], [ink_opt_ok=yes], [ink_opt_ok=no]) AC_MSG_RESULT([$ink_opt_ok]) if test "x$ink_opt_ok" != "xyes"; then CFLAGS="$ink_svd_CFLAGS" @@ -96,7 +97,7 @@ AC_MSG_CHECKING([linker tolerates -z relro]) ink_svd_LDFLAGS="$LDFLAGS" LDFLAGS="-Wl,-z,relro $LDFLAGS" - AC_LINK_IFELSE(AC_LANG_PROGRAM([]), [ink_opt_ok=yes], [ink_opt_ok=no]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([])], [ink_opt_ok=yes], [ink_opt_ok=no]) AC_MSG_RESULT([$ink_opt_ok]) if test "x$ink_opt_ok" != "xyes"; then LDFLAGS="$ink_svd_LDFLAGS" @@ -137,14 +138,14 @@ # Detect a working version of unordered containers. AC_MSG_CHECKING([TR1 unordered_set usability]) -AC_COMPILE_IFELSE([ +AC_COMPILE_IFELSE([AC_LANG_SOURCE([ #include int main() { std::tr1::unordered_set i, j; i = j; return 0; } -], [unordered_set_works=yes], [unordered_set_works=no]) +])], [unordered_set_works=yes], [unordered_set_works=no]) if test "x$unordered_set_works" = "xyes"; then AC_MSG_RESULT([ok]) AC_DEFINE(HAVE_TR1_UNORDERED_SET, 1, [Has working standard TR1 unordered_set]) @@ -161,13 +162,13 @@ ignore_strict_aliasing=no CXXFLAGS_SAVE=$CXXFLAGS CXXFLAGS="$CXXFLAGS -Werror=strict-aliasing" -AC_COMPILE_IFELSE([ +AC_COMPILE_IFELSE([AC_LANG_SOURCE([ #include boost::optional x; int func() { return *x; } -], [ignore_strict_aliasing=no], [ignore_strict_aliasing=yes]) +])], [ignore_strict_aliasing=no], [ignore_strict_aliasing=yes]) AC_MSG_RESULT($ignore_strict_aliasing) CXXFLAGS=$CXXFLAGS_SAVE if test "x$ignore_strict_aliasing" = "xyes"; then @@ -609,7 +610,7 @@ AC_MSG_CHECKING(for new color space API in Poppler) popplercolor="no" -AC_COMPILE_IFELSE([ +AC_COMPILE_IFELSE([AC_LANG_SOURCE([ #include int main() { @@ -618,7 +619,7 @@ p = &GfxColorSpace::parse; return 0; } -], [popplercolor=yes]) +])], [popplercolor=yes]) if test "x$popplercolor" = "xyes"; then AC_DEFINE(POPPLER_NEW_COLOR_SPACE_API, 1, [Use color space API from Poppler >= 0.12.2]) AC_MSG_RESULT(yes) @@ -629,7 +630,7 @@ # Poppler's b604a008 commit changes this AC_MSG_CHECKING([whether Poppler's GfxPatch no longer uses GfxColor]) popplergfxcolor="no" -AC_COMPILE_IFELSE([ +AC_COMPILE_IFELSE([AC_LANG_SOURCE([ #include int main() { @@ -638,7 +639,7 @@ patch.color[[0]][[0]] = color; return 0; } -], [popplergfxcolor=yes]) +])], [popplergfxcolor=yes]) if test "x$popplergfxcolor" = "xyes"; then AC_DEFINE(POPPLER_NEW_GFXPATCH, 1, [GfxPatch no longer uses GfxColor in >= 0.15.1]) AC_MSG_RESULT(yes) @@ -843,10 +844,10 @@ # Check for Apple Mac OS X Carbon framework carbon_ok=no AC_MSG_CHECKING([for Mac OS X Carbon support]) -AC_COMPILE_IFELSE([ +AC_COMPILE_IFELSE([AC_LANG_SOURCE([ #include #include -], [carbon_ok=yes]) +])], [carbon_ok=yes]) AC_MSG_RESULT($carbon_ok) if test "x$carbon_ok" = "xyes"; then AC_DEFINE(HAVE_CARBON, 1, [define to 1 if Carbon is available]) @@ -1013,8 +1014,8 @@ ink_svd_CXXFLAGS="$CXXFLAGS" CXXFLAGS="-Wno-unused-parameter $CXXFLAGS" # -Wno-unused-parameter isn't accepted by gcc 2.95. - AC_COMPILE_IFELSE([int dummy; -], , CXXFLAGS="-Wno-unused $ink_svd_CXXFLAGS",) + AC_COMPILE_IFELSE([AC_LANG_SOURCE([int dummy; +])], , CXXFLAGS="-Wno-unused $ink_svd_CXXFLAGS",) # Note: At least one bug has been caught from unused parameter warnings, # so it might be worth trying not to disable it. # One way of selectively disabling the warnings (i.e. only where the @@ -1066,6 +1067,7 @@ src/libgdl/makefile src/libnrtype/makefile src/libavoid/makefile +src/libunicode-convert/makefile src/livarot/makefile src/live_effects/makefile src/live_effects/parameter/makefile diff -rNu3 share/extensions/fontfix.conf share/extensions/fontfix.conf --- share/extensions/fontfix.conf 1969-12-31 16:00:00.000000000 -0800 +++ share/extensions/fontfix.conf 2012-09-13 13:15:09.000000000 -0700 @@ -0,0 +1,55 @@ +# This file contains correction factors for the PowerPoint compensation method +# when files are saved to EMF. PowerPoint applies some odd offsets when it ungroups +# fonts from within an EMF. This file contains compensating factors so that the +# imported, ungrouped characters end up in the desired location. +# +# Format(s) +# +# # a comment +# +# f1 f2 f3 FontName +# where +# f1: vertical (rotating) correction factor (a double) +# f2: vertical (nonrotating) correction factor (a double) +# f3: horizontal (nonrotating) correction factor (a double) +# FontName: Case sensitive, may contain spaces. Example: Times New Roman +# +# The first font will listed will be used as the default for any font which +# is later requested but was not explicitly listed. +# +# If f1 specifies a multiplicative correction factor. It is multiplied by the font size +# and then the character is offset parallel to the (original) vertical direction of the character, +# that is, along the long part of a capital L. +# +# If f2 or f3 is nonzero then for angles <1 degree from 0,90,180,270 degrees +# the angle is snapped to n*90 and f2 is used for the vertical displacements, f3 +# for the horizontal displacements (that is, for 90 degrees, f3 is used). +# +# There is are one special type of fontname: Convert To FontName, +# for instance "Convert To Symbol". It is used when EMF print converts +# from unicode to Symbol, Wingdings, or Zapf Dingbats. +# +# Positive values lower the letter, negative raise it +# +# File history: +# 1.0.0 03/26/2012, David Mathog, initial values +##################################################################### +0.05 -0.055 -0.065 Arial +0.05 -0.055 -0.065 Times New Roman +-0.025 -0.055 -0.065 Lucida Sans +0.05 -0.055 -0.065 Sans +-0.05 -0.055 -0.065 Microsoft Sans Serif +0.05 -0.055 -0.065 Serif +0.05 -0.055 -0.065 Garamond +0.25 0.025 0.025 Century Schoolbook +0.025 0.0 0.0 Verdana +0.045 0.025 0.025 Tahoma +0.025 0.0 0.0 Symbol +0.05 0.0 0.0 Wingdings +0.025 0.0 0.0 Zapf Dingbats +0.025 0.0 0.0 Convert To Symbol +0.05 0.0 0.0 Convert To Wingdings +0.025 0.0 0.0 Convert To Zapf Dingbats +0.1 0.0 0.0 Sylfaen +0.175 0.125 0.125 Palatino Linotype +0.1 0.0 0.0 Segoe UI diff -rNu3 share/extensions/Makefile.am share/extensions/Makefile.am --- share/extensions/Makefile.am 2012-09-13 10:24:25.000000000 -0700 +++ share/extensions/Makefile.am 2012-09-13 13:15:09.000000000 -0700 @@ -178,6 +178,7 @@ yocto_css.py otherstuff = \ + fontfix.conf \ inkweb.js \ jessyInk.js \ jessyInk_core_mouseHandler_noclick.js \ diff -rNu3 src/extension/init.cpp src/extension/init.cpp --- src/extension/init.cpp 2012-09-13 10:24:25.000000000 -0700 +++ src/extension/init.cpp 2012-09-13 13:15:09.000000000 -0700 @@ -32,10 +32,8 @@ #include "system.h" #include "db.h" #include "internal/svgz.h" -#ifdef WIN32 -# include "internal/emf-win32-inout.h" -# include "internal/emf-win32-print.h" -#endif +# include "internal/emf-inout.h" +# include "internal/emf-print.h" #ifdef HAVE_CAIRO_PDF # include "internal/cairo-renderer-pdf-out.h" # include "internal/cairo-png-out.h" @@ -171,10 +169,8 @@ Internal::PdfInputCairo::init(); } #endif -#ifdef WIN32 - Internal::PrintEmfWin32::init(); - Internal::EmfWin32::init(); -#endif + Internal::PrintEmf::init(); + Internal::Emf::init(); Internal::PovOutput::init(); Internal::JavaFXOutput::init(); Internal::OdfOutput::init(); @@ -308,7 +304,7 @@ continue; } - gchar *pathname = g_build_filename(dirname, filename, NULL); + gchar *pathname = g_build_filename(dirname, filename, (char *) NULL); build_from_file(pathname); g_free(pathname); } diff -rNu3 src/extension/internal/emf-inout.cpp src/extension/internal/emf-inout.cpp --- src/extension/internal/emf-inout.cpp 1969-12-31 16:00:00.000000000 -0800 +++ src/extension/internal/emf-inout.cpp 2012-09-14 14:31:52.000000000 -0700 @@ -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 : diff -rNu3 src/extension/internal/emf-inout.h src/extension/internal/emf-inout.h --- src/extension/internal/emf-inout.h 1969-12-31 16:00:00.000000000 -0800 +++ src/extension/internal/emf-inout.h 2012-09-13 13:15:09.000000000 -0700 @@ -0,0 +1,55 @@ +/** @file + * @brief Enhanced Metafile Input/Output + */ +/* Authors: + * Ulf Erikson + * + * Copyright (C) 2006-2008 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ +#ifndef SEEN_EXTENSION_INTERNAL_EMF_H +#define SEEN_EXTENSION_INTERNAL_EMF_H + +#include "extension/implementation/implementation.h" + +namespace Inkscape { +namespace Extension { +namespace Internal { + +class Emf : Inkscape::Extension::Implementation::Implementation { //This is a derived class + +public: + Emf(); // Empty constructor + + virtual ~Emf();//Destructor + + bool check(Inkscape::Extension::Extension *module); //Can this module load (always yes for now) + + void save(Inkscape::Extension::Output *mod, // Save the given document to the given filename + SPDocument *doc, + gchar const *filename); + + virtual SPDocument *open( Inkscape::Extension::Input *mod, + const gchar *uri ); + + static void init(void);//Initialize the class + +private: +}; + +} } } /* namespace Inkscape, Extension, Implementation */ + + +#endif /* EXTENSION_INTERNAL_EMF_H */ + +/* + 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 : diff -rNu3 src/extension/internal/emf-print.cpp src/extension/internal/emf-print.cpp --- src/extension/internal/emf-print.cpp 1969-12-31 16:00:00.000000000 -0800 +++ src/extension/internal/emf-print.cpp 2012-09-14 11:55:03.000000000 -0700 @@ -0,0 +1,2146 @@ +/** @file + * @brief Enhanced Metafile printing + */ +/* Authors: + * Ulf Erikson + * Jon A. Cruz + * Abhishek Sharma + * David Mathog + * + * Copyright (C) 2006-2009 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 + + +#include "2geom/sbasis-to-bezier.h" +#include "2geom/svg-elliptical-arc.h" + +#include "2geom/path.h" +#include "2geom/pathvector.h" +#include "2geom/rect.h" +#include "2geom/bezier-curve.h" +#include "2geom/hvlinesegment.h" +#include "helper/geom.h" +#include "helper/geom-curves.h" +#include "sp-item.h" + +#include "style.h" +#include "inkscape-version.h" +#include "sp-root.h" + +#include "emf-print.h" + +#include "unit-constants.h" + +#include "extension/system.h" +#include "extension/print.h" +#include "document.h" +#include "path-prefix.h" +#include "sp-pattern.h" +#include "sp-image.h" +#include "sp-gradient.h" +#include "sp-radial-gradient.h" +#include "sp-linear-gradient.h" + +#include "splivarot.h" // pieces for union on shapes +#include "2geom/svg-path-parser.h" // to get from SVG text to Geom::Path +#include "display/canvas-bpath.h" // for SPWindRule + +#include +extern "C" { +#include "libunicode-convert/unicode-convert.h" +} + + +namespace Inkscape { +namespace Extension { +namespace Internal { + +#define PXPERMETER 2835 + + +enum drawmode {DRAW_PAINT, DRAW_PATTERN, DRAW_IMAGE, DRAW_LINEAR_GRADIENT, DRAW_RADIAL_GRADIENT}; + +struct FFNEXUS { + char *fontname; //Font name + FFNEXUS *next; //link to next nexus, NULL if this is the last + double f1; //Vertical (rotating) offset factor (* font height) + double f2; //Vertical (nonrotating) offset factor (* font height) + double f3; //Horizontal (nonrotating) offset factor (* font height) + }; + +struct GRADVALUES{ + Geom::Point p1; // center or start + Geom::Point p2; // xhandle or end + Geom::Point p3; // yhandle or unused + double r; // radius or unused + void *grad; // to access the stops information + int mode; // DRAW_LINEAR_GRADIENT or DRAW_RADIAL_GRADIENT, if GRADVALUES is valid, else any value + U_COLORREF bgc; // document background color, this is as good a place as any to keep it + float rgb[3]; // also background color, but as 0-1 float. + }; + +/* globals */ +static double PX2WORLD = 20.0f; +static U_XFORM worldTransform; +static bool FixPPTCharPos, FixPPTDashLine, FixPPTGrad2Polys, FixPPTPatternAsHatch; +static FFNEXUS *short_fflist = NULL; //only those fonts so far encountered +static FFNEXUS *long_fflist = NULL; //all the fonts described in ...\share\extensions\fontfix.conf +static EMFTRACK *et = NULL; +static EMFHANDLES *eht = NULL; +static GRADVALUES gv; + +void read_system_fflist(void){ //this is not called by any other source files +FFNEXUS *temp=NULL; +FFNEXUS *ptr=NULL; +std::fstream fffile; +std::string instr; +char fontname[128]; +double f1,f2,f3; +std::string path_to_ffconf; + + if(long_fflist)return; + path_to_ffconf=INKSCAPE_EXTENSIONDIR; +#ifdef WIN32 + path_to_ffconf.append("\\fontfix.conf"); //Windows path syntax +#else + path_to_ffconf.append("/fontfix.conf"); //Unix/linx path syntax +#endif + //open the input + fffile.open(path_to_ffconf.c_str(), std::ios::in); + if(!fffile.is_open()){ + g_message("Unable to open file: %s\n", path_to_ffconf.c_str()); + throw "boom"; + } + while (std::getline(fffile,instr)){ + if(instr[0]=='#')continue; + // not a comment, get the 4 values from the line + int elements=sscanf(instr.c_str(),"%lf %lf %lf %[^\n]",&f1,&f2,&f3, &fontname[0]); + if(elements!=4){ + g_message("Expected \"f1 f2 f3 Fontname\" but did not find it in file: %s\n", path_to_ffconf.c_str()); + throw "boom"; + } + temp=(FFNEXUS *) calloc(1,sizeof(FFNEXUS)); //This will never be freed + temp->f1=f1; + temp->f2=f2; + temp->f3=f3; + temp->fontname=strdup(fontname); //This will never be freed + temp->next=NULL; //just to be explicit, it is already 0 + if(ptr){ + ptr->next=temp; + ptr=temp; + } + else { + long_fflist=ptr=temp; + } + } + fffile.close(); +} + +/* Looks for the fontname in the long list. If it does not find it, it adds the default values +to the short list with this fontname. If it does find it, then it adds the specified values. +*/ +void search_long_fflist(const char *fontname, double *f1, double *f2, double *f3){ //this is not called by any other source files +FFNEXUS *ptr=NULL; +FFNEXUS *tmp=long_fflist; + if(!long_fflist){ + g_message("Programming error search_long_fflist called before read_system_fflist\n"); + throw "boom"; + } + ptr=long_fflist; + while(ptr){ + if(!strcmp(ptr->fontname,fontname)){ tmp=ptr; break; } + ptr=ptr->next; + } + //tmp points at either the found name, or the default, the first entry in long_fflist + if(!short_fflist){ + ptr=short_fflist=(FFNEXUS *) malloc(sizeof(FFNEXUS)); + } + else { + ptr=short_fflist; + while(ptr->next){ ptr=ptr->next; } + ptr->next=(FFNEXUS *) malloc(sizeof(FFNEXUS)); + ptr=ptr->next; + } + ptr->fontname=strdup(tmp->fontname); + *f1 = ptr->f1 = tmp->f1; + *f2 = ptr->f2 = tmp->f2; + *f3 = ptr->f3 = tmp->f3; + ptr->next=NULL; +} + +/* Looks for the fontname in the short list. If it does not find it, it looks in the long_fflist. +Either way it returns the f1, f2, f3 parameters for the font, even if these are for the default. +*/ +void search_short_fflist(const char *fontname, double *f1, double *f2, double *f3){ //this is not called by any other source files +FFNEXUS *ptr=NULL; +static FFNEXUS *last=NULL; + if(!long_fflist){ + g_message("Programming error search_short_fflist called before read_system_fflist\n"); + throw "boom"; + } + // This speeds things up a lot - if the same font is called twice in a row, pull it out immediately + if(last && !strcmp(last->fontname,fontname)){ ptr=last; } + else { ptr=short_fflist; } // short_fflist may still be NULL + while(ptr){ + if(!strcmp(ptr->fontname,fontname)){ *f1=ptr->f1; *f2=ptr->f2; *f3=ptr->f3; last=ptr; return; } + ptr=ptr->next; + } + //reach this point only if there is no match + search_long_fflist(fontname, f1, f2, f3); +} + +void smuggle_adx_out(const char *string, uint32_t **adx, int *ndx, float scale){ + float fdx; + int i; + uint32_t *ladx; + const char *cptr=&string[strlen(string)+1]; + + *adx=NULL; + sscanf(cptr,"%7d",ndx); + if(!*ndx)return; // this could happen with an empty string + cptr += 7; + ladx = (uint32_t *) malloc(*ndx * sizeof(uint32_t) ); + *adx=ladx; + for(i=0; i<*ndx; i++,cptr+=7, ladx++){ + sscanf(cptr,"%7f",&fdx); + *ladx=(uint32_t) round(fdx * scale); + } +} + +/* convert an 0RGB color to EMF U_COLORREF. +inverse of sethexcolor() in emf-inout.cpp +*/ +U_COLORREF gethexcolor(uint32_t color){ + + U_COLORREF out; + out = U_RGB( + (color >> 16) & 0xFF, + (color >> 8) & 0xFF, + (color >> 0) & 0xFF + ); + return(out); +} + + +/* Translate inkscape weights to EMF weights. +*/ +uint32_t transweight(const unsigned int inkweight){ + if(inkweight == SP_CSS_FONT_WEIGHT_400)return(U_FW_NORMAL); + if(inkweight == SP_CSS_FONT_WEIGHT_100)return(U_FW_THIN); + if(inkweight == SP_CSS_FONT_WEIGHT_200)return(U_FW_EXTRALIGHT); + if(inkweight == SP_CSS_FONT_WEIGHT_300)return(U_FW_LIGHT); + // 400 is tested first, as it is the most common case + if(inkweight == SP_CSS_FONT_WEIGHT_500)return(U_FW_MEDIUM); + if(inkweight == SP_CSS_FONT_WEIGHT_600)return(U_FW_SEMIBOLD); + if(inkweight == SP_CSS_FONT_WEIGHT_700)return(U_FW_BOLD); + if(inkweight == SP_CSS_FONT_WEIGHT_800)return(U_FW_EXTRABOLD); + if(inkweight == SP_CSS_FONT_WEIGHT_900)return(U_FW_HEAVY); + return(U_FW_NORMAL); +} + +PrintEmf::PrintEmf (void): + _width(0), + _height(0), + hbrush(0), + hbrushOld(0), + hpen(0), + use_stroke(false), + use_fill(false), + simple_shape(false) +{ +} + + +PrintEmf::~PrintEmf (void) +{ + + /* restore default signal handling for SIGPIPE */ +#if !defined(_WIN32) && !defined(__WIN32__) + (void) signal(SIGPIPE, SIG_DFL); +#endif + return; +} + + +unsigned int PrintEmf::setup (Inkscape::Extension::Print * /*mod*/) +{ + return TRUE; +} + + +unsigned int PrintEmf::begin (Inkscape::Extension::Print *mod, SPDocument *doc) +{ + U_SIZEL szlDev, szlMm; + U_RECTL rclBounds, rclFrame; + char *rec; + + gchar const *utf8_fn = mod->get_param_string("destination"); + FixPPTCharPos = mod->get_param_bool("FixPPTCharPos"); + FixPPTDashLine = mod->get_param_bool("FixPPTDashLine"); + FixPPTGrad2Polys = mod->get_param_bool("FixPPTGrad2Polys"); + FixPPTPatternAsHatch = mod->get_param_bool("FixPPTPatternAsHatch"); + + (void) emf_start(utf8_fn, 1000000, 250000, &et); // Initialize the et structure + (void) htable_create(128, 128, &eht); // Initialize the eht structure + + char *ansi_uri = (char *) utf8_fn; + + // width and height in px + _width = doc->getWidth(); + _height = doc->getHeight(); + + Inkscape::XML::Node *nv = sp_repr_lookup_name (doc->rroot, "sodipodi:namedview"); + if(nv){ + const char *p1 = nv->attribute("pagecolor"); + char *p2; + uint32_t lc = strtoul( &p1[1], &p2, 16 ); // it looks like "#ABC123" + if(*p2)lc=0; + gv.bgc = gethexcolor(lc); + gv.rgb[0] = (float) U_RGBAGetR(gv.bgc)/255.0; + gv.rgb[1] = (float) U_RGBAGetG(gv.bgc)/255.0; + gv.rgb[2] = (float) U_RGBAGetB(gv.bgc)/255.0; + } + + bool pageBoundingBox; + pageBoundingBox = mod->get_param_bool("pageBoundingBox"); + + Geom::Rect d; + if (pageBoundingBox) { + d = Geom::Rect::from_xywh(0, 0, _width, _height); + } else { + SPItem* doc_item = doc->getRoot(); + Geom::OptRect bbox = doc_item->desktopVisualBounds(); + if (bbox) d = *bbox; + } + + d *= Geom::Scale(IN_PER_PX); + + float dwInchesX = d.width(); + float dwInchesY = d.height(); + + // dwInchesX x dwInchesY in micrometer units, dpi=90 -> 3543.3 dpm + (void) drawing_size((int) ceil(dwInchesX*25.4), (int) ceil(dwInchesY*25.4), 3.543307, &rclBounds, &rclFrame); + + // set up the device as A4 horizontal, 47.244094 dpmm (1200 dpi) + int MMX = 216; + int MMY = 279; + (void) device_size(MMX, MMY, 47.244094, &szlDev, &szlMm); // Drawing: A4 horizontal, 42744 dpm (1200 dpi) + int PixelsX = szlDev.cx; + int PixelsY = szlDev.cy; + + // set up the description: (version string)0(file)00 + char buff[1024]; + memset(buff,0, sizeof(buff)); + char *p1 = strrchr(ansi_uri, '\\'); + char *p2 = strrchr(ansi_uri, '/'); + char *p = MAX(p1, p2); + if (p) + p++; + else + p = ansi_uri; + snprintf(buff, sizeof(buff)-1, "Inkscape %s (%s)\1%s\1", Inkscape::version_string, __DATE__,p); + uint16_t *Description = U_Utf8ToUtf16le(buff, 0, NULL); + int cbDesc = 2 + wchar16len(Description); // also count the final terminator + (void) U_Utf16leEdit(Description, '\1', '\0'); // swap the temporary \1 characters for nulls + + // construct the EMRHEADER record and append it to the EMF in memory + rec = U_EMRHEADER_set( rclBounds, rclFrame, NULL, cbDesc, Description, szlDev, szlMm, 0); + free(Description); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::begin at EMRHEADER"; + } + + + // Simplest mapping mode, supply all coordinates in pixels + rec = U_EMRSETMAPMODE_set(U_MM_TEXT); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::begin at EMRSETMAPMODE"; + } + + + // Correct for dpi in EMF vs dpi in Inkscape (always 90?) + // Also correct for the scaling in PX2WORLD, which is set to 20. Doesn't hurt for high resolution, + // helps prevent rounding errors for low resolution EMF. Low resolution EMF is possible if there + // are no print devices and the screen resolution is low. + + worldTransform.eM11 = ((float)PixelsX * 25.4f)/((float)MMX*90.0f*PX2WORLD); + worldTransform.eM12 = 0.0f; + worldTransform.eM21 = 0.0f; + worldTransform.eM22 = ((float)PixelsY * 25.4f)/((float)MMY*90.0f*PX2WORLD); + worldTransform.eDx = 0; + worldTransform.eDy = 0; + + rec = U_EMRMODIFYWORLDTRANSFORM_set(worldTransform, U_MWT_LEFTMULTIPLY); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::begin at EMRMODIFYWORLDTRANSFORM"; + } + + + if (1) { + snprintf(buff, sizeof(buff)-1, "Screen=%dx%dpx, %dx%dmm", PixelsX, PixelsY, MMX, MMY); + rec = textcomment_set(buff); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::begin at textcomment_set 1"; + } + + snprintf(buff, sizeof(buff)-1, "Drawing=%.1lfx%.1lfpx, %.1lfx%.1lfmm", _width, _height, dwInchesX * MM_PER_IN, dwInchesY * MM_PER_IN); + rec = textcomment_set(buff); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::begin at textcomment_set 1"; + } + } + + return 0; +} + + +unsigned int PrintEmf::finish (Inkscape::Extension::Print * /*mod*/) +{ +// std::cout << "finish " << std::endl; + char *rec; + if (!et) return 0; + + + // earlier versions had flush of fill here, but it never executed and was removed + + rec = U_EMREOF_set(0,NULL,et); // generate the EOF record + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::finish"; + } + (void) emf_finish(et, eht); // Finalize and write out the EMF + emf_free(&et); // clean up + htable_free(&eht); // clean up + +// std::cout << "end finish" << std::endl; + return 0; +} + + +unsigned int PrintEmf::comment (Inkscape::Extension::Print * /*module*/, + const char * /*comment*/) +{ +// std::cout << "comment " << std::endl; + if (!et) return 0; + + // earlier versions had flush of fill here, but it never executed and was removed + +// std::cout << "end comment" << std::endl; + return 0; +} + +// Extracth hatchType, hatchColor from a name like +// EMFhatch_ +// Where the first one is a number and the second a color in hex. +// hatchType and hatchColor have been set with defaults before this is called. +// +void hatch_classify(char *name, int *hatchType, U_COLORREF *hatchColor){ + int val; + uint32_t hcolor=0; + if(0!=strncmp(name,"EMFhatch",8)){ return; } // not anything we can parse + name+=8; // EMFhatch already detected + val = 0; + while(*name && isdigit(*name)){ + val = 10*val + *name - '0'; + name++; + } + *hatchType = val; + if(*name != '_' || val > U_HS_DITHEREDBKCLR){ // wrong syntax, cannot classify + *hatchType = -1; + } + else { + name++; + if(1 != sscanf(name,"%X",&hcolor)){ *hatchType = -1; } // again wrong syntax, cannot classify + *hatchColor = gethexcolor(hcolor); + } + if(*hatchType > U_HS_SOLIDCLR)*hatchType = U_HS_SOLIDCLR; +} + +// +// Recurse down from a brush pattern, try to figure out what it is. +// If an image is found set a pointer to the epixbuf, else set that to NULL +// If a pattern is found with a name like EMFhatch3_3F7FFF return hatchType=3, hatchColor=3F7FFF (as a uint32_t), +// otherwise hatchType is set to -1 and hatchColor is not defined. +// + +void brush_classify(SPObject *parent, int depth, GdkPixbuf **epixbuf, int *hatchType, U_COLORREF *hatchColor){ + if(depth==0){ + *epixbuf = NULL; + *hatchType = -1; + *hatchColor = U_RGB(0,0,0); + } + depth++; + // first look along the pattern chain, if there is one + if(SP_IS_PATTERN(parent)){ + for (SPPattern *pat_i = SP_PATTERN(parent); pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if(SP_IS_IMAGE(pat_i)){ + *epixbuf = ((SPImage *)pat_i)->pixbuf; + return; + } + char temp[32]; // large enough + temp[31]='\0'; + strncpy(temp,pat_i->getAttribute("id"),31); // Some names may be longer than EMFhatch#_###### + hatch_classify(temp,hatchType,hatchColor); + if(*hatchType != -1)return; + + // still looking? Look at this pattern's children, if there are any + SPObject *child = pat_i->firstChild(); + while(child && !(*epixbuf) && (*hatchType == -1)){ + brush_classify(child, depth, epixbuf, hatchType, hatchColor); + child = child->getNext(); + } + } + } + else if(SP_IS_IMAGE(parent)){ + *epixbuf = ((SPImage *)parent)->pixbuf; + return; + } + else { // some inkscape rearrangements pass through nodes between pattern and image which are not classified as either. + SPObject *child = parent->firstChild(); + while(child && !(*epixbuf) && (*hatchType == -1)){ + brush_classify(child, depth, epixbuf, hatchType, hatchColor); + child = child->getNext(); + } + } +} + +//swap R/B in 4 byte pixel +void swapRBinRGBA(char *px, int pixels){ + char tmp; + for(int i=0;ivector.stops.size() -1; + if(last>=1){ + float rgbs[3]; + float rgbe[3]; + float ops,ope; + + ops = gr->vector.stops[0 ].opacity; + ope = gr->vector.stops[last].opacity; + sp_color_get_rgb_floatv(&gr->vector.stops[0 ].color, rgbs); + sp_color_get_rgb_floatv(&gr->vector.stops[last].color, rgbe); + + /* Replace opacity at start & stop with that fraction background color, then average those two for final color. */ + cr = U_RGB( + 255*(( opweight(rgbs[0],gv.rgb[0],ops) + opweight(rgbe[0],gv.rgb[0],ope) )/2.0), + 255*(( opweight(rgbs[1],gv.rgb[1],ops) + opweight(rgbe[1],gv.rgb[1],ope) )/2.0), + 255*(( opweight(rgbs[2],gv.rgb[2],ops) + opweight(rgbe[2],gv.rgb[2],ope) )/2.0) + ); + } + else { + cr = U_RGB(0, 0, 0); // The default fill + } + return cr; +} + +int hold_gradient(void *gr, int mode){ + gv.mode = mode; + gv.grad = gr; + if(mode==DRAW_RADIAL_GRADIENT){ + SPRadialGradient *rg = (SPRadialGradient *) gr; + gv.r = rg->r.computed; // radius, but of what??? + gv.p1 = Geom::Point(rg->cx.computed, rg->cy.computed); // center + gv.p2 = Geom::Point(gv.r, 0) + gv.p1; // xhandle + gv.p3 = Geom::Point(0, -gv.r) + gv.p1; // yhandle + if (rg->gradientTransform_set) { + gv.p1 = gv.p1 * rg->gradientTransform; + gv.p2 = gv.p2 * rg->gradientTransform; + gv.p3 = gv.p3 * rg->gradientTransform; + } + } + else if(mode==DRAW_LINEAR_GRADIENT){ + SPLinearGradient *lg = (SPLinearGradient *) gr; + gv.r = 0; // unused + gv.p1 = Geom::Point (lg->x1.computed, lg->y1.computed); // start + gv.p2 = Geom::Point (lg->x2.computed, lg->y2.computed); // end + gv.p3 = Geom::Point (0, 0); // unused + if (lg->gradientTransform_set) { + gv.p1 = gv.p1 * lg->gradientTransform; + gv.p2 = gv.p2 * lg->gradientTransform; + } + } + else { + throw "Fatal programming error, hold_gradient() in emf-print.cpp called with invalid draw mode"; + } + return 1; +} + +// fcolor is defined when gradients are being expanded, it is the color of one stripe or ring. +int PrintEmf::create_brush(SPStyle const *style, PU_COLORREF fcolor) +{ +// std::cout << "create_brush " << std::endl; + float rgb[3]; + char *rec; + U_LOGBRUSH lb; + uint32_t brush, fmode; + enum drawmode fill_mode; + GdkPixbuf *pixbuf; + uint32_t brushStyle; + int hatchType; + U_COLORREF hatchColor; + uint32_t width = 0; // quiets a harmless compiler warning, initialization not otherwise required. + uint32_t height = 0; + + if (!et) return 0; + + // set a default fill in case we can't figure out a better way to do it + fmode = U_ALTERNATE; + fill_mode = DRAW_PAINT; + brushStyle = U_BS_SOLID; + hatchType = U_HS_SOLIDCLR; + if(fcolor){ hatchColor = *fcolor; } + else { hatchColor = U_RGB(0, 0, 0); } + + if (!fcolor && style) { + if(style->fill.isColor()){ + fill_mode = DRAW_PAINT; + float opacity = SP_SCALE24_TO_FLOAT(style->fill_opacity.value); + if (opacity <= 0.0) return 1; // opacity isn't used here beyond this + + sp_color_get_rgb_floatv( &style->fill.value.color, rgb ); + hatchColor = U_RGB(255*rgb[0], 255*rgb[1], 255*rgb[2]); + + fmode = style->fill_rule.computed == 0 ? U_WINDING : (style->fill_rule.computed == 2 ? U_ALTERNATE : U_ALTERNATE); + } + else if(SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style))){ // must be paint-server + SPPaintServer *paintserver = style->fill.value.href->getObject(); + SPPattern *pat = SP_PATTERN (paintserver); + double dwidth = pattern_width(pat); + double dheight = pattern_height(pat); + width = dwidth; + height = dheight; + brush_classify(pat,0,&pixbuf,&hatchType,&hatchColor); + if(pixbuf){ fill_mode = DRAW_IMAGE; } + else { // pattern + fill_mode = DRAW_PATTERN; + if(hatchType == -1){ // Not a standard hatch, so force it to something + hatchType = U_HS_CROSS; + hatchColor = U_RGB(0xFF,0xC3,0xC3); + } + } + if(FixPPTPatternAsHatch){ + if(hatchType == -1){ // image or unclassified + fill_mode = DRAW_PATTERN; + hatchType = U_HS_DIAGCROSS; + hatchColor = U_RGB(0xFF,0xC3,0xC3); + } + } + brushStyle = U_BS_HATCHED; + } + else if(SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))){ // must be a gradient + // currently we do not do anything with gradients, the code below just sets the color to the average of the stops + SPPaintServer *paintserver = style->fill.value.href->getObject(); + SPLinearGradient *lg = NULL; + SPRadialGradient *rg = NULL; + + if (SP_IS_LINEARGRADIENT (paintserver)) { + lg = SP_LINEARGRADIENT(paintserver); + SP_GRADIENT(lg)->ensureVector(); // when exporting from commandline, vector is not built + fill_mode = DRAW_LINEAR_GRADIENT; + } + else if (SP_IS_RADIALGRADIENT (paintserver)) { + rg = SP_RADIALGRADIENT(paintserver); + SP_GRADIENT(rg)->ensureVector(); // when exporting from commandline, vector is not built + fill_mode = DRAW_RADIAL_GRADIENT; + } + else { + // default fill + } + + if(rg){ + if(FixPPTGrad2Polys){ return hold_gradient(rg, fill_mode); } + else { hatchColor = avg_stop_color(rg); } + } + else if(lg){ + if(FixPPTGrad2Polys){ return hold_gradient(lg, fill_mode); } + else { hatchColor = avg_stop_color(lg); } + } + } + } + else { // if (!style) + // default fill + } + + lb = logbrush_set(brushStyle, hatchColor, hatchType); + + switch(fill_mode){ + case DRAW_LINEAR_GRADIENT: // fill with average color unless gradients are converted to slices + case DRAW_RADIAL_GRADIENT: // ditto + case DRAW_PAINT: + case DRAW_PATTERN: + rec = createbrushindirect_set(&brush, eht, lb); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::create_brush at createbrushindirect_set"; + } + hbrush = brush; // need this later for destroy_brush + + rec = selectobject_set(brush, eht); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::create_brush at selectobject_set"; + } + + break; + case DRAW_IMAGE: + char *px; + char *rgba_px; + uint32_t cbPx; + uint32_t colortype; + PU_RGBQUAD ct; + int numCt; + U_BITMAPINFOHEADER Bmih; + PU_BITMAPINFO Bmi; + rgba_px = (char *) gdk_pixbuf_get_pixels(pixbuf); // Do NOT free this!!! + colortype = U_BCBM_COLOR32; + (void) RGBA_to_DIB(&px, &cbPx, &ct, &numCt, rgba_px, width, height, width*4, colortype, 0, 1); + // Not sure why the next swap is needed because the preceding does it, and the code is identical + // to that in stretchdibits_set, which does not need this. + swapRBinRGBA(px, width*height); + Bmih = bitmapinfoheader_set(width, height, 1, colortype, U_BI_RGB, 0, PXPERMETER, PXPERMETER, numCt, 0); + Bmi = bitmapinfo_set(Bmih, ct); + rec = createdibpatternbrushpt_set(&brush, eht, U_DIB_RGB_COLORS, Bmi, cbPx, px); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::create_brush at createdibpatternbrushpt_set"; + } + free(px); + free(Bmi); // ct will be NULL because of colortype + + rec = selectobject_set(brush, eht); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::create_brush at selectobject_set"; + } + break; + } + rec = U_EMRSETPOLYFILLMODE_set(fmode); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::create_brush at U_EMRSETPOLYdrawmode_set"; + } +// std::cout << "end create_brush " << std::endl; + return 0; +} + + +void PrintEmf::destroy_brush() +{ +// std::cout << "destroy_brush " << std::endl; + char *rec; + // before an object may be safely deleted it must no longer be selected + // select in a stock object to deselect this one, the stock object should + // never be used because we always select in a new one before drawing anythingrestore previous brush, necessary??? Would using a default stock object not work? + rec = selectobject_set(U_NULL_BRUSH, eht); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::destroy_brush at selectobject_set"; + } + if (hbrush){ + rec = deleteobject_set(&hbrush, eht); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::destroy_brush"; + } + hbrush = 0; + } +// std::cout << "end destroy_brush" << std::endl; +} + + +int PrintEmf::create_pen(SPStyle const *style, const Geom::Affine &transform) +{ + U_EXTLOGPEN *elp; + U_NUM_STYLEENTRY n_dash = 0; + U_STYLEENTRY *dash = NULL; + char *rec = NULL; + int linestyle = U_PS_SOLID; + int linecap = 0; + int linejoin = 0; + uint32_t pen; + uint32_t penStyle; + GdkPixbuf *pixbuf; + int hatchType; + U_COLORREF hatchColor; + uint32_t width,height; + char *px=NULL; + char *rgba_px; + uint32_t cbPx=0; + uint32_t colortype; + PU_RGBQUAD ct=NULL; + int numCt=0; + U_BITMAPINFOHEADER Bmih; + PU_BITMAPINFO Bmi=NULL; +// std::cout << "create_pen " << std::endl; + + if (!et) return 0; + + // set a default stroke in case we can't figure out a better way to do it + penStyle = U_BS_SOLID; + hatchColor = U_RGB(0, 0, 0); + hatchType = U_HS_HORIZONTAL; + + if (style) { + float rgb[3]; + + if(SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style))){ // must be paint-server + SPPaintServer *paintserver = style->stroke.value.href->getObject(); + SPPattern *pat = SP_PATTERN (paintserver); + double dwidth = pattern_width(pat); + double dheight = pattern_height(pat); + width = dwidth; + height = dheight; + brush_classify(pat,0,&pixbuf,&hatchType,&hatchColor); + if(pixbuf){ + penStyle = U_BS_DIBPATTERN; + rgba_px = (char *) gdk_pixbuf_get_pixels(pixbuf); // Do NOT free this!!! + colortype = U_BCBM_COLOR32; + (void) RGBA_to_DIB(&px, &cbPx, &ct, &numCt, rgba_px, width, height, width*4, colortype, 0, 1); + // Not sure why the next swap is needed because the preceding does it, and the code is identical + // to that in stretchdibits_set, which does not need this. + swapRBinRGBA(px, width*height); + Bmih = bitmapinfoheader_set(width, height, 1, colortype, U_BI_RGB, 0, PXPERMETER, PXPERMETER, numCt, 0); + Bmi = bitmapinfo_set(Bmih, ct); + } + else { // pattern + penStyle = U_BS_HATCHED; + if(hatchType == -1){ // Not a standard hatch, so force it to something + hatchType = U_HS_CROSS; + hatchColor = U_RGB(0xFF,0xC3,0xC3); + } + } + if(FixPPTPatternAsHatch){ + if(hatchType == -1){ // image or unclassified + penStyle = U_BS_HATCHED; + hatchType = U_HS_DIAGCROSS; + hatchColor = U_RGB(0xFF,0xC3,0xC3); + } + } + } + else if(SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))){ // must be a gradient + // currently we do not do anything with gradients, the code below has no net effect. + + SPPaintServer *paintserver = style->stroke.value.href->getObject(); + if (SP_IS_LINEARGRADIENT (paintserver)) { + SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver); + + SP_GRADIENT(lg)->ensureVector(); // when exporting from commandline, vector is not built + + Geom::Point p1 (lg->x1.computed, lg->y1.computed); + Geom::Point p2 (lg->x2.computed, lg->y2.computed); + + if (lg->gradientTransform_set) { + p1 = p1 * lg->gradientTransform; + p2 = p2 * lg->gradientTransform; + } + hatchColor = avg_stop_color(lg); + } + else if (SP_IS_RADIALGRADIENT (paintserver)) { + SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver); + + SP_GRADIENT(rg)->ensureVector(); // when exporting from commandline, vector is not built + double r = rg->r.computed; + + Geom::Point c (rg->cx.computed, rg->cy.computed); + Geom::Point xhandle_point(r, 0); + Geom::Point yhandle_point(0, -r); + yhandle_point += c; + xhandle_point += c; + if (rg->gradientTransform_set) { + c = c * rg->gradientTransform; + yhandle_point = yhandle_point * rg->gradientTransform; + xhandle_point = xhandle_point * rg->gradientTransform; + } + hatchColor = avg_stop_color(rg); + } + else { + // default fill + } + } + else if(style->stroke.isColor()){ // test last, always seems to be set, even for other types above + sp_color_get_rgb_floatv( &style->stroke.value.color, rgb ); + penStyle = U_BS_SOLID; + hatchColor = U_RGB(255*rgb[0], 255*rgb[1], 255*rgb[2]); + hatchType = U_HS_SOLIDCLR; + } + else { + // default fill + } + + + + using Geom::X; + using Geom::Y; + + Geom::Point zero(0, 0); + Geom::Point one(1, 1); + Geom::Point p0(zero * transform); + Geom::Point p1(one * transform); + Geom::Point p(p1 - p0); + + double scale = sqrt( (p[X]*p[X]) + (p[Y]*p[Y]) ) / sqrt(2); + + if(!style->stroke_width.computed){return 0;} //if width is 0 do not (reset) the pen, it should already be NULL_PEN + uint32_t linewidth = MAX( 1, (uint32_t) (scale * style->stroke_width.computed * PX2WORLD) ); + + if (style->stroke_linecap.computed == 0) { + linecap = U_PS_ENDCAP_FLAT; + } + else if (style->stroke_linecap.computed == 1) { + linecap = U_PS_ENDCAP_ROUND; + } + else if (style->stroke_linecap.computed == 2) { + linecap = U_PS_ENDCAP_SQUARE; + } + + if (style->stroke_linejoin.computed == 0) { + linejoin = U_PS_JOIN_MITER; + } + else if (style->stroke_linejoin.computed == 1) { + linejoin = U_PS_JOIN_ROUND; + } + else if (style->stroke_linejoin.computed == 2) { + linejoin = U_PS_JOIN_BEVEL; + } + + if (style->stroke_dash.n_dash && + style->stroke_dash.dash ) + { + if(FixPPTDashLine){ // will break up line into many smaller lines. Override gradient if that was set, cannot do both. + penStyle = U_BS_SOLID; + hatchType = U_HS_HORIZONTAL; + } + else { + int i = 0; + while (linestyle != U_PS_USERSTYLE && + (i < style->stroke_dash.n_dash)) { + if (style->stroke_dash.dash[i] > 0.00000001) + linestyle = U_PS_USERSTYLE; + i++; + } + + if (linestyle == U_PS_USERSTYLE) { + n_dash = style->stroke_dash.n_dash; + dash = new uint32_t[n_dash]; + for (i = 0; i < style->stroke_dash.n_dash; i++) { + dash[i] = (uint32_t) (style->stroke_dash.dash[i]); + } + } + } + } + + elp = extlogpen_set( + U_PS_GEOMETRIC | linestyle | linecap | linejoin, + linewidth, + penStyle, + hatchColor, + hatchType, + n_dash, + dash); + + } + else { // if (!style) + linejoin=0; + elp = extlogpen_set( + linestyle, + 1, + U_BS_SOLID, + U_RGB(0,0,0), + U_HS_HORIZONTAL, + 0, + NULL); + } + + rec = extcreatepen_set(&pen, eht, Bmi, cbPx, px, elp ); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::create_pen at extcreatepen_set"; + } + free(elp); + if(Bmi)free(Bmi); + if(px)free(px); // ct will always be NULL + + rec = selectobject_set(pen, eht); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::create_pen at selectobject_set"; + } + hpen = pen; // need this later for destroy_pen + + if (linejoin == U_PS_JOIN_MITER) { + float miterlimit = style->stroke_miterlimit.value; // This is a ratio. + + if (miterlimit < 1)miterlimit = 1; + + rec = U_EMRSETMITERLIMIT_set((uint32_t) miterlimit); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::create_pen at U_EMRSETMITERLIMIT_set"; + } + } + + if (n_dash) { + delete[] dash; + } + return 0; +// std::cout << "end create_pen" << std::endl; +} + +// set the current pen to the stock object NULL_PEN and then delete the defined pen object, if there is one. +void PrintEmf::destroy_pen() +{ +// std::cout << "destroy_pen hpen: " << hpen<< std::endl; + char *rec = NULL; + // before an object may be safely deleted it must no longer be selected + // select in a stock object to deselect this one, the stock object should + // never be used because we always select in a new one before drawing anythingrestore previous brush, necessary??? Would using a default stock object not work? + rec = selectobject_set(U_NULL_PEN, eht); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::destroy_pen at selectobject_set"; + } + if (hpen){ + rec = deleteobject_set(&hpen, eht); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::destroy_pen"; + } + hpen = 0; + } +// std::cout << "end destroy_pen " << std::endl; +} + + + +unsigned int PrintEmf::bind(Inkscape::Extension::Print * /*mod*/, Geom::Affine const &transform, float /*opacity*/) +{ +// std::cout << "bind " << std::endl; + if (!m_tr_stack.empty()) { + Geom::Affine tr_top = m_tr_stack.top(); + m_tr_stack.push(transform * tr_top); + } else { + m_tr_stack.push(transform); + } + +// std::cout << "end bind" << std::endl; + return 1; +} + +unsigned int PrintEmf::release(Inkscape::Extension::Print * /*mod*/) +{ +// std::cout << "release " << std::endl; + m_tr_stack.pop(); +// std::cout << "end release" << std::endl; + return 1; +} + +#define clrweight(a,b,t) ((1-t)*((double) a) + (t)*((double) b)) +inline U_COLORREF weight_opacity(U_COLORREF c1){ + float opa = c1.Reserved/255.0; + U_COLORREF result = U_RGB( + 255*opweight((float)c1.Red /255.0, gv.rgb[0], opa), + 255*opweight((float)c1.Green/255.0, gv.rgb[1], opa), + 255*opweight((float)c1.Blue /255.0, gv.rgb[2], opa) + ); + return result; +} + + +// return the color between c1 and c2, c1 for t=0, c2 for t=1.0 +U_COLORREF weight_colors(U_COLORREF c1, U_COLORREF c2, double t){ + U_COLORREF result; + result.Red = clrweight(c1.Red, c2.Red, t); + result.Green = clrweight(c1.Green, c2.Green, t); + result.Blue = clrweight(c1.Blue, c2.Blue, t); + result.Reserved = clrweight(c1.Reserved, c2.Reserved, t); + + // now handle the opacity, mix the RGB with background at the weighted opacity + + if(result.Reserved != 255)result = weight_opacity(result); + + return result; +} + +/* convert from center ellipse to SVGEllipticalArc ellipse + + From: + http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter + A point (x,y) on the arc can be found by: + + {x,y} = {cx,cy} + {cosF,-sinF,sinF,cosF} x {rxcosT,rysinT} + + where + {cx,cy} is the center of the ellipse + F is the rotation angle of the X axis of the ellipse from the true X axis + T is the rotation angle around the ellipse + {,,,} is the rotation matrix + rx,ry are the radii of the ellipse's axes + + For SVG parameterization need two points. + Arbitrarily we can use T=0 and T=pi + Since the sweep is 180 the flags are always 0: + + F is in RADIANS, but the SVGEllipticalArc needs degrees! + +*/ +Geom::PathVector center_ellipse_as_SVG_PathV(Geom::Point ctr, double rx, double ry, double F){ + using Geom::X; + using Geom::Y; + double x1,y1,x2,y2; + Geom::Path SVGep; + + x1 = ctr[X] + cos(F) * rx * cos(0) + sin(-F) * ry * sin(0); + y1 = ctr[Y] + sin(F) * rx * cos(0) + cos(F) * ry * sin(0); + x2 = ctr[X] + cos(F) * rx * cos(M_PI) + sin(-F) * ry * sin(M_PI); + y2 = ctr[Y] + sin(F) * rx * cos(M_PI) + cos(F) * ry * sin(M_PI); + + char text[256]; + sprintf(text," M %f,%f A %f %f %f 0 0 %f %f A %f %f %f 0 0 %f %f z",x1,y1, rx,ry,F*360./(2.*M_PI),x2,y2, rx,ry,F*360./(2.*M_PI),x1,y1); + std::vector outres = Geom::parse_svg_path(text); + return outres; +} + + +/* rx2,ry2 must be larger than rx1,ry1! + angle is in RADIANS +*/ +Geom::PathVector center_elliptical_ring_as_SVG_PathV(Geom::Point ctr, double rx1, double ry1, double rx2, double ry2, double F){ + using Geom::X; + using Geom::Y; + double x11,y11,x12,y12; + double x21,y21,x22,y22; + double degrot = F*360./(2.*M_PI); + + x11 = ctr[X] + cos(F) * rx1 * cos(0) + sin(-F) * ry1 * sin(0); + y11 = ctr[Y] + sin(F) * rx1 * cos(0) + cos(F) * ry1 * sin(0); + x12 = ctr[X] + cos(F) * rx1 * cos(M_PI) + sin(-F) * ry1 * sin(M_PI); + y12 = ctr[Y] + sin(F) * rx1 * cos(M_PI) + cos(F) * ry1 * sin(M_PI); + + x21 = ctr[X] + cos(F) * rx2 * cos(0) + sin(-F) * ry2 * sin(0); + y21 = ctr[Y] + sin(F) * rx2 * cos(0) + cos(F) * ry2 * sin(0); + x22 = ctr[X] + cos(F) * rx2 * cos(M_PI) + sin(-F) * ry2 * sin(M_PI); + y22 = ctr[Y] + sin(F) * rx2 * cos(M_PI) + cos(F) * ry2 * sin(M_PI); + + char text[512]; + sprintf(text," M %f,%f A %f %f %f 0 1 %f %f A %f %f %f 0 1 %f %f z M %f,%f A %f %f %f 0 0 %f %f A %f %f %f 0 0 %f %f z", + x11,y11, rx1,ry1,degrot,x12,y12, rx1,ry1,degrot,x11,y11, + x21,y21, rx2,ry2,degrot,x22,y22, rx2,ry2,degrot,x21,y21); + std::vector outres = Geom::parse_svg_path(text); + + return outres; +} + +/* Elliptical hole in a large square extending from -50k to +50k */ +Geom::PathVector center_elliptical_hole_as_SVG_PathV(Geom::Point ctr, double rx, double ry, double F){ + using Geom::X; + using Geom::Y; + double x1,y1,x2,y2; + Geom::Path SVGep; + + x1 = ctr[X] + cos(F) * rx * cos(0) + sin(-F) * ry * sin(0); + y1 = ctr[Y] + sin(F) * rx * cos(0) + cos(F) * ry * sin(0); + x2 = ctr[X] + cos(F) * rx * cos(M_PI) + sin(-F) * ry * sin(M_PI); + y2 = ctr[Y] + sin(F) * rx * cos(M_PI) + cos(F) * ry * sin(M_PI); + + char text[256]; + sprintf(text," M %f,%f A %f %f %f 0 0 %f %f A %f %f %f 0 0 %f %f z M 50000,50000 50000,-50000 -50000,-50000 -50000,50000 z", + x1,y1, rx,ry,F*360./(2.*M_PI),x2,y2, rx,ry,F*360./(2.*M_PI),x1,y1); + std::vector outres = Geom::parse_svg_path(text); + return outres; +} + +/* rectangular cutter. +ctr "center" of rectangle (might not actually be in the center with respect to leading/trailing edges +pos vector from center to leading edge +neg vector from center to trailing edge +width vector to side edge +*/ +Geom::PathVector rect_cutter(Geom::Point ctr, Geom::Point pos, Geom::Point neg, Geom::Point width){ + std::vector outres; + Geom::Path cutter; + cutter.start( ctr + pos - width); + cutter.appendNew(ctr + pos + width); + cutter.appendNew(ctr + neg + width); + cutter.appendNew(ctr + neg - width); + cutter.close(); + outres.push_back(cutter); + return outres; +} + +/* Convert from SPWindRule to livarot's FillRule + This is similar to what sp_selected_path_boolop() does +*/ +FillRule SPWR_to_LVFR(SPWindRule wr){ + FillRule fr; + if(wr == SP_WIND_RULE_EVENODD){ + fr = fill_oddEven; + } + else { + fr = fill_nonZero; + } + return fr; +} + + +unsigned int PrintEmf::fill(Inkscape::Extension::Print * /*mod*/, + Geom::PathVector const &pathv, Geom::Affine const & /*transform*/, SPStyle const *style, + Geom::OptRect const &/*pbox*/, Geom::OptRect const &/*dbox*/, Geom::OptRect const &/*bbox*/) +{ +// std::cout << "fill " << std::endl; + using Geom::X; + using Geom::Y; + + Geom::Affine tf = m_tr_stack.top(); + + use_fill = true; + use_stroke = false; + + // earlier versions had flush of fill here, but it never executed and was removed + + fill_transform = tf; + + if (create_brush(style, NULL)){ + /* + Handle gradients. Uses modified livarot as 2geom boolops is currently broken. + Can handle gradients with multiple stops. + + The overlap is needed to avoid antialiasing artifacts when edges are not strictly aligned on pixel boundaries. + There is an inevitable loss of accuracy saving through an EMF file because of the integer coordinate system. + Keep the overlap quite large so that loss of accuracy does not remove an overlap. + */ + destroy_pen(); //this sets the NULL_PEN, otherwise gradient slices may display with boundaries, see longer explanation below + Geom::Path cutter; + float rgb[3]; + U_COLORREF wc,c1,c2; + FillRule frb = SPWR_to_LVFR( (SPWindRule) style->fill_rule.computed); + double doff,doff_base,doff_range; + double divisions= 128.0; + int nstops; + int istop = 1; + float opa; // opacity at stop + + SPRadialGradient *tg = (SPRadialGradient *) (gv.grad); // linear/radial are the same here + nstops = tg->vector.stops.size(); + sp_color_get_rgb_floatv(&tg->vector.stops[0].color, rgb); + opa = tg->vector.stops[0].opacity; + c1 = U_RGBA( 255*rgb[0], 255*rgb[1], 255*rgb[2], 255*opa ); + sp_color_get_rgb_floatv(&tg->vector.stops[nstops-1].color, rgb); + opa = tg->vector.stops[nstops-1].opacity; + c2 = U_RGBA( 255*rgb[0], 255*rgb[1], 255*rgb[2], 255*opa ); + + doff = 0.0; + doff_base = 0.0; + doff_range = tg->vector.stops[1].offset; // next or last stop + + if(gv.mode==DRAW_RADIAL_GRADIENT){ + Geom::Point xv = gv.p2 - gv.p1; // X' vector + Geom::Point yv = gv.p3 - gv.p1; // Y' vector + Geom::Point xuv = Geom::unit_vector(xv); // X' unit vector + double rx = hypot(xv[X],xv[Y]); + double ry = hypot(yv[X],yv[Y]); + double range = fmax(rx,ry); // length along the gradient + double step = range/divisions; // adequate approximation for gradient + double overlap = step/4.0; // overlap slices slightly + double start; + double stop; + Geom::PathVector pathvc, pathvr; + + /* radial gradient might stop part way through the shape, fill with outer color from there to "infinity". + Do this first so that outer colored ring will overlay it. + */ + pathvc = center_elliptical_hole_as_SVG_PathV(gv.p1, rx*(1.0 - overlap/range), ry*(1.0 - overlap/range), asin(xuv[Y])); + pathvr = sp_pathvector_boolop(pathvc, pathv, bool_op_inters, (FillRule) fill_oddEven, frb); + wc = weight_opacity(c2); + (void) create_brush(style, &wc); + print_pathv(pathvr, fill_transform); + + sp_color_get_rgb_floatv(&tg->vector.stops[istop].color, rgb); + opa = tg->vector.stops[istop].opacity; + c2 = U_RGBA( 255*rgb[0], 255*rgb[1], 255*rgb[2], 255*opa ); + + for(start = 0.0; start < range; start += step, doff += 1./divisions){ + stop = start + step + overlap; + if(stop > range)stop=range; + wc = weight_colors(c1, c2, (doff - doff_base)/(doff_range-doff_base) ); + (void) create_brush(style, &wc); + + pathvc = center_elliptical_ring_as_SVG_PathV(gv.p1, rx*start/range, ry*start/range, rx*stop/range, ry*stop/range, asin(xuv[Y])); + + pathvr = sp_pathvector_boolop(pathvc, pathv, bool_op_inters, (FillRule) fill_nonZero, frb); + print_pathv(pathvr, fill_transform); // show the intersection + + if(doff >= doff_range - doff_base){ + istop++; + if(istop >= nstops)continue; // could happen on a rounding error + doff_base = doff_range; + doff_range = tg->vector.stops[istop].offset; // next or last stop + c1=c2; + sp_color_get_rgb_floatv(&tg->vector.stops[istop].color, rgb); + opa = tg->vector.stops[istop].opacity; + c2 = U_RGBA( 255*rgb[0], 255*rgb[1], 255*rgb[2], 255*opa ); + } + } + + } + else if(gv.mode == DRAW_LINEAR_GRADIENT){ + Geom::Point uv = Geom::unit_vector(gv.p2 - gv.p1); // unit vector + Geom::Point puv = uv.cw(); // perp. to unit vector + double range = Geom::distance(gv.p1,gv.p2); // length along the gradient + double step = range/divisions; // adequate approximation for gradient + double overlap = step/4.0; // overlap slices slightly + double start; + double stop; + Geom::PathVector pathvc, pathvr; + + /* before lower end of gradient, overlap first slice position */ + wc = weight_opacity(c1); + (void) create_brush(style, &wc); + pathvc = rect_cutter(gv.p1, uv*(overlap), uv*(-50000.0), puv*50000.0); + pathvr = sp_pathvector_boolop(pathvc, pathv, bool_op_inters, (FillRule) fill_nonZero, frb); + print_pathv(pathvr, fill_transform); + + /* after high end of gradient, overlap last slice poosition */ + wc = weight_opacity(c2); + (void) create_brush(style, &wc); + pathvc = rect_cutter(gv.p2, uv*(-overlap), uv*(50000.0), puv*50000.0); + pathvr = sp_pathvector_boolop(pathvc, pathv, bool_op_inters, (FillRule) fill_nonZero, frb); + print_pathv(pathvr, fill_transform); + + sp_color_get_rgb_floatv(&tg->vector.stops[istop].color, rgb); + opa = tg->vector.stops[istop].opacity; + c2 = U_RGBA( 255*rgb[0], 255*rgb[1], 255*rgb[2], 255*opa ); + + for(start = 0.0; start < range; start += step, doff += 1./divisions){ + stop = start + step + overlap; + if(stop > range)stop=range; + pathvc = rect_cutter(gv.p1, uv*start, uv*stop, puv*50000.0); + + wc = weight_colors(c1, c2, (doff - doff_base)/(doff_range-doff_base) ); + (void) create_brush(style, &wc); + Geom::PathVector pathvr = sp_pathvector_boolop(pathvc, pathv, bool_op_inters, (FillRule) fill_nonZero, frb); + print_pathv(pathvr, fill_transform); // show the intersection + + if(doff >= doff_range - doff_base){ + istop++; + if(istop >= nstops)continue; // could happen on a rounding error + doff_base = doff_range; + doff_range = tg->vector.stops[istop].offset; // next or last stop + c1=c2; + sp_color_get_rgb_floatv(&tg->vector.stops[istop].color, rgb); + opa = tg->vector.stops[istop].opacity; + c2 = U_RGBA( 255*rgb[0], 255*rgb[1], 255*rgb[2], 255*opa ); + } + } + } + else { + throw "Fatal programming error in PrintEmf::fill, invalid gradient type detected"; + } + use_fill = false; // gradients handled, be sure stroke does not use stroke and fill + } + else { + /* + Inkscape was not calling create_pen for objects with no border. + This was because it never called stroke() (next method). + PPT, and presumably others, pick whatever they want for the border if it is not specified, so no border can + become a visible border. + To avoid this force the pen to NULL_PEN if we can determine that no pen will be needed after the fill. + */ + if (style->stroke.noneSet || style->stroke_width.computed == 0.0){ + destroy_pen(); //this sets the NULL_PEN + } + + /* postpone fill in case stroke also required AND all stroke paths closed + Dashes converted to line segments will "open" a closed path. + */ + bool all_closed = true; + for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit){ + for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_open(); ++cit){ + if (pit->end_default() != pit->end_closed()) { all_closed=false; } + } + } + if ( + (style->stroke.noneSet || style->stroke_width.computed == 0.0) || + (style->stroke_dash.n_dash && style->stroke_dash.dash && FixPPTDashLine) || + !all_closed + ) + { + print_pathv(pathv, fill_transform); // do any fills. side effect: clears fill_pathv + use_fill = false; + } + } + + + +// std::cout << "end fill" << std::endl; + return 0; +} + + +unsigned int PrintEmf::stroke (Inkscape::Extension::Print * /*mod*/, + Geom::PathVector const &pathv, const Geom::Affine &/*transform*/, const SPStyle *style, + Geom::OptRect const &/*pbox*/, Geom::OptRect const &/*dbox*/, Geom::OptRect const &/*bbox*/) +{ +// std::cout << "stroke " << std::endl; + + Geom::Affine tf = m_tr_stack.top(); + + use_stroke = true; +// use_fill was set in ::fill, if it is needed + + if (create_pen(style, tf))return 0; + + if (style->stroke_dash.n_dash && style->stroke_dash.dash && FixPPTDashLine ){ + // convert the path, gets its complete length, and then make a new path with parameter length instead of t + Geom::Piecewise > tmp_pathpw; // pathv-> sbasis + Geom::Piecewise > tmp_pathpw2; // sbasis using arc length parameter + Geom::Piecewise > tmp_pathpw3; // new (discontinuous) path, composed of dots/dashes + Geom::Piecewise > first_frag; // first fragment, will be appended at end + int n_dash = style->stroke_dash.n_dash; + int i=0; //dash index + double tlength; // length of tmp_pathpw + double slength=0.0; // start of gragment + double elength; // end of gragment + for (unsigned int i=0; i < pathv.size(); i++) { + tmp_pathpw.concat(pathv[i].toPwSb()); + } + tlength = length(tmp_pathpw,0.1); + tmp_pathpw2 = arc_length_parametrization(tmp_pathpw); + + // go around the dash array repeatedly until the entire path is consumed (but not beyond). + while(slength < tlength){ + elength = slength + style->stroke_dash.dash[i++]; + if(elength > tlength)elength = tlength; + Geom::Piecewise > fragment(portion(tmp_pathpw2, slength, elength)); + if(slength){ tmp_pathpw3.concat(fragment); } + else { first_frag = fragment; } + slength = elength; + slength += style->stroke_dash.dash[i++]; // the gap + if(i>=n_dash)i=0; + } + tmp_pathpw3.concat(first_frag); // may merge line around start point + Geom::PathVector out_pathv = Geom::path_from_piecewise(tmp_pathpw3, 0.01); + print_pathv(out_pathv, tf); + } + else { + print_pathv(pathv, tf); + } + + use_stroke = false; + use_fill = false; + + +// std::cout << "end stroke " << std::endl; + return 0; +} + + +// Draws simple_shapes, those with closed EMR_* primitives, like polygons, rectangles and ellipses. +// These use whatever the current pen/brush are and need not be followed by a FILLPATH or STROKEPATH. +// For other paths it sets a few flags and returns. +bool PrintEmf::print_simple_shape(Geom::PathVector const &pathv, const Geom::Affine &transform) +{ +// std::cout << "print_simple_shape " << std::endl <begin(); cit != pit->end_open(); ++cit) + { + nodes++; + + if ( is_straight_curve(*cit) ) { + lines++; + } + else if (Geom::CubicBezier const *cubic = dynamic_cast(&*cit)) { + cubic = cubic; + curves++; + } + } + } + + if (!nodes) + return false; + + U_POINT *lpPoints = new U_POINT[moves + lines + curves*3]; + int i = 0; + + /** + * For all Subpaths in the + */ + for (Geom::PathVector::const_iterator pit = pv.begin(); pit != pv.end(); ++pit) + { + using Geom::X; + using Geom::Y; + + Geom::Point p0 = pit->initialPoint(); + + p0[X] = (p0[X] * PX2WORLD); + p0[Y] = (p0[Y] * PX2WORLD); + + int32_t const x0 = (int32_t) round(p0[X]); + int32_t const y0 = (int32_t) round(p0[Y]); + + lpPoints[i].x = x0; + lpPoints[i].y = y0; + i = i + 1; + + /** + * For all segments in the subpath + */ + for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_open(); ++cit) + { + if ( is_straight_curve(*cit) ) + { + //Geom::Point p0 = cit->initialPoint(); + Geom::Point p1 = cit->finalPoint(); + + //p0[X] = (p0[X] * PX2WORLD); + p1[X] = (p1[X] * PX2WORLD); + //p0[Y] = (p0[Y] * PX2WORLD); + p1[Y] = (p1[Y] * PX2WORLD); + + //int32_t const x0 = (int32_t) round(p0[X]); + //int32_t const y0 = (int32_t) round(p0[Y]); + int32_t const x1 = (int32_t) round(p1[X]); + int32_t const y1 = (int32_t) round(p1[Y]); + + lpPoints[i].x = x1; + lpPoints[i].y = y1; + i = i + 1; + } + else if (Geom::CubicBezier const *cubic = dynamic_cast(&*cit)) + { + std::vector points = cubic->points(); + //Geom::Point p0 = points[0]; + Geom::Point p1 = points[1]; + Geom::Point p2 = points[2]; + Geom::Point p3 = points[3]; + + //p0[X] = (p0[X] * PX2WORLD); + p1[X] = (p1[X] * PX2WORLD); + p2[X] = (p2[X] * PX2WORLD); + p3[X] = (p3[X] * PX2WORLD); + //p0[Y] = (p0[Y] * PX2WORLD); + p1[Y] = (p1[Y] * PX2WORLD); + p2[Y] = (p2[Y] * PX2WORLD); + p3[Y] = (p3[Y] * PX2WORLD); + + //int32_t const x0 = (int32_t) round(p0[X]); + //int32_t const y0 = (int32_t) round(p0[Y]); + int32_t const x1 = (int32_t) round(p1[X]); + int32_t const y1 = (int32_t) round(p1[Y]); + int32_t const x2 = (int32_t) round(p2[X]); + int32_t const y2 = (int32_t) round(p2[Y]); + int32_t const x3 = (int32_t) round(p3[X]); + int32_t const y3 = (int32_t) round(p3[Y]); + + lpPoints[i].x = x1; + lpPoints[i].y = y1; + lpPoints[i+1].x = x2; + lpPoints[i+1].y = y2; + lpPoints[i+2].x = x3; + lpPoints[i+2].y = y3; + i = i + 3; + } + } + } + + bool done = false; + bool closed = (lpPoints[0].x == lpPoints[i-1].x) && (lpPoints[0].y == lpPoints[i-1].y); + bool polygon = false; + bool rectangle = false; + bool ellipse = false; + + if (moves == 1 && moves+lines == nodes && closed) { + polygon = true; +// if (nodes==5) { // disable due to LP Bug 407394 +// if (lpPoints[0].x == lpPoints[3].x && lpPoints[1].x == lpPoints[2].x && +// lpPoints[0].y == lpPoints[1].y && lpPoints[2].y == lpPoints[3].y) +// { +// rectangle = true; +// } +// } + } + else if (moves == 1 && nodes == 5 && moves+curves == nodes && closed) { +// if (lpPoints[0].x == lpPoints[1].x && lpPoints[1].x == lpPoints[11].x && +// lpPoints[5].x == lpPoints[6].x && lpPoints[6].x == lpPoints[7].x && +// lpPoints[2].x == lpPoints[10].x && lpPoints[3].x == lpPoints[9].x && lpPoints[4].x == lpPoints[8].x && +// lpPoints[2].y == lpPoints[3].y && lpPoints[3].y == lpPoints[4].y && +// lpPoints[8].y == lpPoints[9].y && lpPoints[9].y == lpPoints[10].y && +// lpPoints[5].y == lpPoints[1].y && lpPoints[6].y == lpPoints[0].y && lpPoints[7].y == lpPoints[11].y) +// { // disable due to LP Bug 407394 +// ellipse = true; +// } + } + + if (polygon || ellipse) { + + if (use_fill && !use_stroke) { // only fill + rec = selectobject_set(U_NULL_PEN, eht); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::print_simple_shape at selectobject_set pen"; + } + } + else if(!use_fill && use_stroke) { // only stroke + rec = selectobject_set(U_NULL_BRUSH, eht); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::print_simple_shape at selectobject_set brush"; + } + } + + if (polygon) { + if (rectangle){ + U_RECTL rcl = rectl_set((U_POINTL) {lpPoints[0].x, lpPoints[0].y}, (U_POINTL) {lpPoints[2].x, lpPoints[2].y}); + rec = U_EMRRECTANGLE_set(rcl); + } + else { + rec = U_EMRPOLYGON_set(U_RCL_DEF, nodes, lpPoints); + } + } + else if (ellipse) { + U_RECTL rcl = rectl_set((U_POINTL) {lpPoints[6].x, lpPoints[3].y}, (U_POINTL) {lpPoints[0].x, lpPoints[9].y}); + rec = U_EMRELLIPSE_set(rcl); + } + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::print_simple_shape at retangle/ellipse/polygon"; + } + + done = true; + + // replace the handle we moved above, assuming there was something set already + if (use_fill && !use_stroke && hpen) { // only fill + rec = selectobject_set(hpen, eht); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::print_simple_shape at selectobject_set pen"; + } + } + else if (!use_fill && use_stroke && hbrush){ // only stroke + rec = selectobject_set(hbrush, eht); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::print_simple_shape at selectobject_set brush"; + } + } + + } + + delete[] lpPoints; + +// std::cout << "end simple_shape " << std::endl; + return done; +} + +/** Some parts based on win32.cpp by Lauris Kaplinski . Was a part of Inkscape + in the past (or will be in the future?) Not in current trunk. (4/19/2012) + + Limitations of this code: + 1. rotated images are mangled. They stay in their original orientation and are stretched + along X or Y. + 2. Transparency is lost on export. (Apparently a limitation of the EMF format.) + 3. Probably messes up if row stride != w*4 + 4. There is still a small memory leak somewhere, possibly in a pixbuf created in a routine + that calls this one and passes px, but never removes the rest of the pixbuf. The first time + this is called it leaked 5M (in one test) and each subsequent call leaked around 200K more. + If this routine is reduced to + if(1)return(0); + and called for a single 1280 x 1024 image then the program leaks 11M per call, or roughly the + size of two bitmaps. +*/ + +unsigned int PrintEmf::image(Inkscape::Extension::Print * /* module */, /** not used */ + unsigned char *rgba_px, /** array of pixel values, Gdk::Pixbuf bitmap format */ + unsigned int w, /** width of bitmap */ + unsigned int h, /** height of bitmap */ + unsigned int rs, /** row stride (normally w*4) */ + Geom::Affine const &tf_ignore, /** WRONG affine transform, use the one from m_tr_stack */ + SPStyle const *style) /** provides indirect link to image object */ +{ +// std::cout << "image " << std::endl; + double x1,x2,y1,y2; + char *rec = NULL; + Geom::Affine tf = m_tr_stack.top(); + + rec = U_EMRSETSTRETCHBLTMODE_set(U_COLORONCOLOR); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::image at EMRHEADER"; + } + + x1= atof(style->object->getAttribute("x")); + y1= atof(style->object->getAttribute("y")); + x2=x1 + atof(style->object->getAttribute("width")); + y2=y1 + atof(style->object->getAttribute("height")); + Geom::Point pLL(x1,y1); + Geom::Point pUR(x2,y2); + Geom::Point p2LL = pLL * tf; + Geom::Point p2UR = pUR * tf; + + char *px; + uint32_t cbPx; + uint32_t colortype; + PU_RGBQUAD ct; + int numCt; + U_BITMAPINFOHEADER Bmih; + PU_BITMAPINFO Bmi; + colortype = U_BCBM_COLOR32; + (void) RGBA_to_DIB(&px, &cbPx, &ct, &numCt, (char *) rgba_px, w, h, w*4, colortype, 0, 1); + Bmih = bitmapinfoheader_set(w, h, 1, colortype, U_BI_RGB, 0, PXPERMETER, PXPERMETER, numCt, 0); + Bmi = bitmapinfo_set(Bmih, ct); + + U_POINTL Dest = pointl_set(round(p2LL[Geom::X] * PX2WORLD), round(p2LL[Geom::Y] * PX2WORLD)); + U_POINTL cDest = pointl_set(round((p2UR[Geom::X]-p2LL[Geom::X]) * PX2WORLD), round((p2UR[Geom::Y]-p2LL[Geom::Y]) * PX2WORLD)); + U_POINTL Src = pointl_set(0,0); + U_POINTL cSrc = pointl_set(w,h); + rec = U_EMRSTRETCHDIBITS_set( + U_RCL_DEF, //! Bounding rectangle in device units + Dest, //! Destination UL corner in logical units + cDest, //! Destination W & H in logical units + Src, //! Source UL corner in logical units + cSrc, //! Source W & H in logical units + U_DIB_RGB_COLORS, //! DIBColors Enumeration + U_SRCCOPY, //! RasterOPeration Enumeration + Bmi, //! (Optional) bitmapbuffer (U_BITMAPINFO section) + h*rs, //! size in bytes of px + px //! (Optional) bitmapbuffer (U_BITMAPINFO section) + ); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::image at U_EMRSTRETCHDIBITS_set"; + } + free(px); + free(Bmi); + if(numCt)free(ct); + +// std::cout << "end image" << std::endl; + return 0; +} + +// may also be called with a simple_shape or an empty path, whereupon it just returns without doing anything +unsigned int PrintEmf::print_pathv(Geom::PathVector const &pathv, const Geom::Affine &transform) +{ +// std::cout << "print_pathv " << std::endl << std::flush; + char *rec = NULL; + + simple_shape = print_simple_shape(pathv, transform); + if (simple_shape || pathv.empty()){ + if (use_fill){ destroy_brush(); } // these must be cleared even if nothing is drawn or hbrush,hpen fill up + if (use_stroke){ destroy_pen(); } + return TRUE; + } + + Geom::PathVector pv = pathv_to_linear_and_cubic_beziers( pathv * transform ); + + rec = U_EMRBEGINPATH_set(); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::print_pathv at U_EMRBEGINPATH_set"; + } + + /** + * For all Subpaths in the + */ + for (Geom::PathVector::const_iterator pit = pv.begin(); pit != pv.end(); ++pit) + { + using Geom::X; + using Geom::Y; + + + Geom::Point p0 = pit->initialPoint(); + + p0[X] = (p0[X] * PX2WORLD); + p0[Y] = (p0[Y] * PX2WORLD); + + U_POINTL ptl = pointl_set((int32_t) round(p0[X]), (int32_t) round(p0[Y])); + rec = U_EMRMOVETOEX_set(ptl); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::print_pathv at U_EMRMOVETOEX_set"; + } + + /** + * For all segments in the subpath + */ + for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_open(); ++cit) + { + if ( is_straight_curve(*cit) ) + { + //Geom::Point p0 = cit->initialPoint(); + Geom::Point p1 = cit->finalPoint(); + + //p0[X] = (p0[X] * PX2WORLD); + p1[X] = (p1[X] * PX2WORLD); + //p0[Y] = (p0[Y] * PX2WORLD); + p1[Y] = (p1[Y] * PX2WORLD); + + //int32_t const x0 = (int32_t) round(p0[X]); + //int32_t const y0 = (int32_t) round(p0[Y]); + + ptl = pointl_set((int32_t) round(p1[X]), (int32_t) round(p1[Y])); + rec = U_EMRLINETO_set(ptl); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::print_pathv at U_EMRLINETO_set"; + } + } + else if (Geom::CubicBezier const *cubic = dynamic_cast(&*cit)) + { + std::vector points = cubic->points(); + //Geom::Point p0 = points[0]; + Geom::Point p1 = points[1]; + Geom::Point p2 = points[2]; + Geom::Point p3 = points[3]; + + //p0[X] = (p0[X] * PX2WORLD); + p1[X] = (p1[X] * PX2WORLD); + p2[X] = (p2[X] * PX2WORLD); + p3[X] = (p3[X] * PX2WORLD); + //p0[Y] = (p0[Y] * PX2WORLD); + p1[Y] = (p1[Y] * PX2WORLD); + p2[Y] = (p2[Y] * PX2WORLD); + p3[Y] = (p3[Y] * PX2WORLD); + + //int32_t const x0 = (int32_t) round(p0[X]); + //int32_t const y0 = (int32_t) round(p0[Y]); + int32_t const x1 = (int32_t) round(p1[X]); + int32_t const y1 = (int32_t) round(p1[Y]); + int32_t const x2 = (int32_t) round(p2[X]); + int32_t const y2 = (int32_t) round(p2[Y]); + int32_t const x3 = (int32_t) round(p3[X]); + int32_t const y3 = (int32_t) round(p3[Y]); + + U_POINTL pt[3]; + pt[0].x = x1; + pt[0].y = y1; + pt[1].x = x2; + pt[1].y = y2; + pt[2].x = x3; + pt[2].y = y3; + + rec = U_EMRPOLYBEZIERTO_set(U_RCL_DEF, 3, pt); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::print_pathv at U_EMRPOLYBEZIERTO_set"; + } + } + else + { + g_warning("logical error, because pathv_to_linear_and_cubic_beziers was used"); + } + } + + if (pit->end_default() == pit->end_closed()) { // there may be multiples of this on a single path + rec = U_EMRCLOSEFIGURE_set(); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::print_pathv at U_EMRCLOSEFIGURE_set"; + } + } + + } + + rec = U_EMRENDPATH_set(); // there may be only be one of these on a single path + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::print_pathv at U_EMRENDPATH_set"; + } + + // explicit FILL/STROKE commands are needed for each sub section of the path + if (use_fill && !use_stroke){ + rec = U_EMRFILLPATH_set(U_RCL_DEF); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::fill at U_EMRFILLPATH_set"; + } + } + else if (use_fill && use_stroke) { + rec = U_EMRSTROKEANDFILLPATH_set(U_RCL_DEF); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::stroke at U_EMRSTROKEANDFILLPATH_set"; + } + } + else if (!use_fill && use_stroke){ + rec = U_EMRSTROKEPATH_set(U_RCL_DEF); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::stroke at U_EMRSTROKEPATH_set"; + } + } + + // clean out brush and pen, but only after all parts of the draw complete + if (use_fill){ + destroy_brush(); + } + if (use_stroke){ + destroy_pen(); + } +// std::cout << "end pathv" << std::endl; + + return TRUE; +} + + +bool PrintEmf::textToPath(Inkscape::Extension::Print * ext) +{ + return ext->get_param_bool("textToPath"); +} + +unsigned int PrintEmf::text(Inkscape::Extension::Print * /*mod*/, char const *text, Geom::Point const &p, + SPStyle const *const style) +{ +// std::cout << "text " << std::endl; + if (!et) return 0; + + char *rec = NULL; + int ccount,newfont; + int fix90n=0; + uint32_t hfont = 0; + Geom::Affine tf = m_tr_stack.top(); + double rot = -1800.0*std::atan2(tf[1], tf[0])/M_PI; // 0.1 degree rotation, - sign for MM_TEXT + double rotb = -std::atan2(tf[1], tf[0]); // rotation for baseline offset for superscript/subscript, used below + double dx,dy; + double f1,f2,f3; + +#ifdef USE_PANGO_WIN32 +/* + font_instance *tf = (font_factory::Default())->Face(style->text->font_family.value, font_style_to_pos(*style)); + if (tf) { + LOGFONT *lf = pango_win32_font_logfont(tf->pFont); + tf->Unref(); + hfont = CreateFontIndirect(lf); + g_free(lf); + } +*/ +#endif + + // the dx array is smuggled in like: textw1 w2 w3 ...wn, where the widths are floats 7 characters wide, including the space + int ndx; + uint32_t *adx; + smuggle_adx_out(text, &adx, &ndx, PX2WORLD * std::min(tf.expansionX(),tf.expansionY())); // side effect: free() adx + + char *text2 = strdup(text); // because U_Utf8ToUtf16le calls iconv which does not like a const char * + uint16_t *unicode_text = U_Utf8ToUtf16le( text2, 0, NULL ); + free(text2); + //translates Unicode to NonUnicode, if possible. If any translate, all will, and all to + //the same font, because of code in Layout::print + UnicodeToNon(unicode_text, &ccount, &newfont); + + //PPT gets funky with text within +-1 degree of a multiple of 90, but only for SOME fonts.Snap those to the central value + //Some funky ones: Arial, Times New Roman + //Some not funky ones: Symbol and Verdana. + //Without a huge table we cannot catch them all, so just the most common problem ones. + if(FixPPTCharPos){ + switch(newfont){ + case CVTSYM: + search_short_fflist("Convert To Symbol", &f1, &f2, &f3); + break; + case CVTZDG: + search_short_fflist("Convert To Zapf Dingbats", &f1, &f2, &f3); + break; + case CVTWDG: + search_short_fflist("Convert To Wingdings", &f1, &f2, &f3); + break; + default: //also CVTNON + search_short_fflist(style->text->font_family.value, &f1, &f2, &f3); + break; + } + if(f2 || f3){ + int irem = ((int) round(rot)) % 900 ; + if(irem <=9 && irem >= -9){ + fix90n=1; //assume vertical + rot = (double) (((int) round(rot)) - irem); + rotb = rot*M_PI/1800.0; + if( abs(rot) == 900.0 ){ fix90n = 2; } + } + } + } + + /* Note that text font sizes are stored into the EMF as fairly small integers and that limits their precision. + The EMF output files produced here have been designed so that the integer valued pt sizes + land right on an integer value in the EMF file, so those are exact. However, something like 18.1 pt will be + somewhat off, so that when it is read back in it becomes 18.11 pt. (For instance.) + */ + int textheight = round(-style->font_size.computed * PX2WORLD * std::min(tf.expansionX(),tf.expansionY())); + if (!hfont) { + + // Get font face name. Use changed font name if unicode mapped to one + // of the special fonts. + uint16_t *wfacename; + if(!newfont){ + wfacename = U_Utf8ToUtf16le(style->text->font_family.value, 0, NULL); + } + else { + wfacename = U_Utf8ToUtf16le(FontName(newfont), 0, NULL); + } + + // Scale the text to the minimum stretch. (It tends to stay within bounding rectangles even if + // it was streteched asymmetrically.) Few applications support text from EMF which is scaled + // differently by height/width, so leave lfWidth alone. + + U_LOGFONT lf = logfont_set( + textheight, + 0, + rot, + rot, + transweight(style->font_weight.computed), + (style->font_style.computed == SP_CSS_FONT_STYLE_ITALIC), + style->text_decoration.underline, + style->text_decoration.line_through, + U_DEFAULT_CHARSET, + U_OUT_DEFAULT_PRECIS, + U_CLIP_DEFAULT_PRECIS, + U_DEFAULT_QUALITY, + U_DEFAULT_PITCH | U_FF_DONTCARE, + wfacename); + free(wfacename); + + rec = extcreatefontindirectw_set(&hfont, eht, (char *) &lf, NULL); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::text at extcreatefontindirectw_set"; + } + } + + rec = selectobject_set(hfont, eht); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::text at selectobject_set"; + } + + float rgb[3]; + sp_color_get_rgb_floatv( &style->fill.value.color, rgb ); + rec = U_EMRSETTEXTCOLOR_set(U_RGB(255*rgb[0], 255*rgb[1], 255*rgb[2])); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::text at U_EMRSETTEXTCOLOR_set"; + } + + // Text alignment: + // - (x,y) coordinates received by this filter are those of the point where the text + // actually starts, and already takes into account the text object's alignment; + // - for this reason, the EMF text alignment must always be TA_BASELINE|TA_LEFT. + rec = U_EMRSETTEXTALIGN_set(U_TA_BASELINE | U_TA_LEFT); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::text at U_EMRSETTEXTALIGN_set"; + } + + // Transparent text background + rec = U_EMRSETBKMODE_set(U_TRANSPARENT); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::text at U_EMRSETBKMODE_set"; + } + + Geom::Point p2 = p * tf; + + //Handle super/subscripts. Negative sign because of geometry of MM_TEXT. + p2[Geom::X] -= style->baseline_shift.computed * std::sin( rotb ); + p2[Geom::Y] -= style->baseline_shift.computed * std::cos( rotb ); + + //Conditionally handle compensation for PPT EMF import bug (affects PPT 2003-2010, at least) + if(FixPPTCharPos){ + if(fix90n==1){ //vertical + dx= 0.0; + dy= f3 * style->font_size.computed * std::cos( rotb ); + } + else if(fix90n==2){ //horizontal + dx= f2 * style->font_size.computed * std::sin( rotb ); + dy= 0.0; + } + else { + dx= f1 * style->font_size.computed * std::sin( rotb ); + dy= f1 * style->font_size.computed * std::cos( rotb ); + } + p2[Geom::X] += dx; + p2[Geom::Y] += dy; + } + + p2[Geom::X] = (p2[Geom::X] * PX2WORLD); + p2[Geom::Y] = (p2[Geom::Y] * PX2WORLD); + + int32_t const xpos = (int32_t) round(p2[Geom::X]); + int32_t const ypos = (int32_t) round(p2[Geom::Y]); + + // The number of characters in the string is a bit fuzzy. ndx, the number of entries in adx is + // the number of VISIBLE characters, since some may combine from the UTF (8 originally, + // now 16) encoding. Conversely strlen() or wchar16len() would give the absolute number of + // encoding characters. Unclear if emrtext wants the former or the latter but for now assume the former. + +// This is currently being smuggled in from caller as part of text, works +// MUCH better than the fallback hack below +// uint32_t *adx = dx_set(textheight, U_FW_NORMAL, slen); // dx is needed, this makes one up + char *rec2 = emrtext_set( (U_POINTL) {xpos, ypos}, ndx, 2, unicode_text, U_ETO_NONE, U_RCL_DEF, adx); + free(unicode_text); + free(adx); + rec = U_EMREXTTEXTOUTW_set(U_RCL_DEF,U_GM_COMPATIBLE,1.0,1.0,(PU_EMRTEXT)rec2); + free(rec2); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::text at U_EMREXTTEXTOUTW_set"; + } + + // Must deselect an object before deleting it. Put the default font (back) in. + rec = selectobject_set(U_DEVICE_DEFAULT_FONT, eht); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::text at selectobject_set"; + } + + if(hfont){ + rec = deleteobject_set(&hfont, eht); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + throw "Fatal programming error in PrintEmf::text at deleteobject_set"; + } + } + +// std::cout << "end text" << std::endl; + return 0; +} + +void PrintEmf::init (void) +{ +// std::cout << "init " << std::endl; + read_system_fflist(); + + /* EMF print */ + Inkscape::Extension::build_from_mem( + "\n" + "Enhanced Metafile Print\n" + "org.inkscape.print.emf\n" + "\n" + "true\n" + "true\n" + "false\n" + "false\n" + "false\n" + "false\n" + "\n" + "", new PrintEmf()); + + return; +} + +} /* namespace Internal */ +} /* namespace Extension */ +} /* namespace Inkscape */ + + +/* + 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 : diff -rNu3 src/extension/internal/emf-print.h src/extension/internal/emf-print.h --- src/extension/internal/emf-print.h 1969-12-31 16:00:00.000000000 -0800 +++ src/extension/internal/emf-print.h 2012-09-13 13:15:09.000000000 -0700 @@ -0,0 +1,113 @@ +/** @file + * @brief Enhanced Metafile printing - implementation + */ +/* Author: + * Ulf Erikson + * + * Copyright (C) 2006-2008 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ +#ifndef __INKSCAPE_EXTENSION_INTERNAL_PRINT_EMF_H__ +#define __INKSCAPE_EXTENSION_INTERNAL_PRINT_EMF_H__ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "uemf.h" + +#include "extension/implementation/implementation.h" +//#include "extension/extension.h" + +#include <2geom/pathvector.h> + +#include + +namespace Inkscape { +namespace Extension { +namespace Internal { + +class PrintEmf : public Inkscape::Extension::Implementation::Implementation +{ + double _width; + double _height; + U_RECTL rc; + + uint32_t hbrush, hbrushOld, hpen, hpenOld; + + std::stack m_tr_stack; + Geom::PathVector fill_pathv; + Geom::Affine fill_transform; + bool use_stroke; + bool use_fill; + bool simple_shape; + + unsigned int print_pathv (Geom::PathVector const &pathv, const Geom::Affine &transform); + bool print_simple_shape (Geom::PathVector const &pathv, const Geom::Affine &transform); + +public: + PrintEmf (void); + virtual ~PrintEmf (void); + + /* Print functions */ + virtual unsigned int setup (Inkscape::Extension::Print * module); + + virtual unsigned int begin (Inkscape::Extension::Print * module, SPDocument *doc); + virtual unsigned int finish (Inkscape::Extension::Print * module); + + /* Rendering methods */ + virtual unsigned int bind(Inkscape::Extension::Print *module, Geom::Affine const &transform, float opacity); + virtual unsigned int release(Inkscape::Extension::Print *module); + virtual unsigned int fill (Inkscape::Extension::Print *module, + Geom::PathVector const &pathv, + Geom::Affine const &ctm, SPStyle const *style, + Geom::OptRect const &pbox, Geom::OptRect const &dbox, + Geom::OptRect const &bbox); + virtual unsigned int stroke (Inkscape::Extension::Print * module, + Geom::PathVector const &pathv, + Geom::Affine const &ctm, SPStyle const *style, + Geom::OptRect const &pbox, Geom::OptRect const &dbox, + Geom::OptRect const &bbox); + virtual unsigned int image(Inkscape::Extension::Print *module, + unsigned char *px, + unsigned int w, + unsigned int h, + unsigned int rs, + Geom::Affine const &transform, + SPStyle const *style); + virtual unsigned int comment(Inkscape::Extension::Print *module, const char * comment); + virtual unsigned int text(Inkscape::Extension::Print *module, char const *text, + Geom::Point const &p, SPStyle const *style); + bool textToPath (Inkscape::Extension::Print * ext); + + static void init (void); +protected: + int create_brush(SPStyle const *style, PU_COLORREF fcolor); + + void destroy_brush(); + + int create_pen(SPStyle const *style, const Geom::Affine &transform); + + void destroy_pen(); + +}; + +} /* namespace Internal */ +} /* namespace Extension */ +} /* namespace Inkscape */ + + +#endif /* __INKSCAPE_EXTENSION_INTERNAL_PRINT_EMF_H__ */ + +/* + 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 : diff -rNu3 src/extension/internal/emf-win32-inout.cpp src/extension/internal/emf-win32-inout.cpp --- src/extension/internal/emf-win32-inout.cpp 2012-09-13 10:24:25.000000000 -0700 +++ src/extension/internal/emf-win32-inout.cpp 1969-12-31 16:00:00.000000000 -0800 @@ -1,2569 +0,0 @@ -/** @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 WIN32 - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#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/output.h" -#include "display/drawing.h" -#include "display/drawing-item.h" -#include "unit-constants.h" -#include "clear-n_.h" -#include "document.h" - -#define WIN32_LEAN_AND_MEAN -#include - -#include "emf-win32-print.h" -#include "emf-win32-inout.h" - - -#define PRINT_EMF_WIN32 "org.inkscape.print.emf.win32" - -#ifndef PS_JOIN_MASK -#define PS_JOIN_MASK (PS_JOIN_BEVEL|PS_JOIN_MITER|PS_JOIN_ROUND) -#endif - -#define DPA 0x00A000C9 // TernaryRasterOperation - -namespace Inkscape { -namespace Extension { -namespace Internal { - -static float device_scale = DEVICESCALE; -static RECTL rc_old; -static bool clipset = false; - -EmfWin32::EmfWin32 (void) // The null constructor -{ - return; -} - - -EmfWin32::~EmfWin32 (void) //The destructor -{ - return; -} - - -bool -EmfWin32::check (Inkscape::Extension::Extension * /*module*/) -{ - if (NULL == Inkscape::Extension::db.get(PRINT_EMF_WIN32)) - 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_WIN32); - 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 -EmfWin32::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar const *filename) -{ - Inkscape::Extension::Extension * ext; - - ext = Inkscape::Extension::db.get(PRINT_EMF_WIN32); - if (ext == NULL) - return; - - bool old_textToPath = ext->get_param_bool("textToPath"); - bool new_val = mod->get_param_bool("textToPath"); - ext->set_param_bool("textToPath", new_val); - - emf_print_document_to_file(doc, filename); - - ext->set_param_bool("textToPath", old_textToPath); - - return; -} - - - -typedef struct { - int type; - int level; - ENHMETARECORD *lpEMFR; -} EMF_OBJECT, *PEMF_OBJECT; - -typedef struct emf_device_context { - struct SPStyle style; - class SPTextStyle tstyle; - bool stroke_set; - bool fill_set; - - SIZEL sizeWnd; - SIZEL sizeView; - float PixelsInX, PixelsInY; - float PixelsOutX, PixelsOutY; - POINTL winorg; - POINTL vieworg; - double ScaleInX, ScaleInY; - double ScaleOutX, ScaleOutY; - COLORREF textColor; - bool textColorSet; - DWORD textAlign; - XFORM worldTransform; - 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; - - EMF_DEVICE_CONTEXT dc[EMF_MAX_DC+1]; // FIXME: This should be dynamic.. - int level; - - double xDPI, yDPI; - bool pathless_stroke; - bool inpath; - - float MMX; - float MMY; - float dwInchesX; - float dwInchesY; - - unsigned int id; - CHAR *pDesc; - - int n_obj; - PEMF_OBJECT emf_obj; -} EMF_CALLBACK_DATA, *PEMF_CALLBACK_DATA; - - -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); - -// tmp_id << "\n\tid=\"" << (d->id++) << "\""; -// *(d->outsvg) += tmp_id.str().c_str(); - *(d->outsvg) += "\n\tstyle=\""; - if (iType == EMR_STROKEPATH || !d->dc[d->level].fill_set) { - tmp_style << "fill:none;"; - } else { - 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; - 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 == EMR_FILLPATH || !d->dc[d->level].stroke_set) { - tmp_style << "stroke:none;"; - } else { - 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; - - 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") << ";"; - - if (d->dc[d->level].style.stroke_linejoin.computed == 0) { - tmp_style << "stroke-miterlimit:" << - MAX( 0.01, 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) -{ - PEMRCREATEPEN pEmr = NULL; - - if (index >= 0 && index < d->n_obj) - pEmr = (PEMRCREATEPEN) d->emf_obj[index].lpEMFR; - - if (!pEmr) - return; - - switch (pEmr->lopn.lopnStyle & PS_STYLE_MASK) { - case PS_DASH: - case PS_DOT: - case PS_DASHDOT: - case PS_DASHDOTDOT: - { - int i = 0; - int penstyle = (pEmr->lopn.lopnStyle & PS_STYLE_MASK); - d->dc[d->level].style.stroke_dash.n_dash = - penstyle == PS_DASHDOTDOT ? 6 : penstyle == 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==PS_DASH || penstyle==PS_DASHDOT || penstyle==PS_DASHDOTDOT) { - d->dc[d->level].style.stroke_dash.dash[i++] = 3; - d->dc[d->level].style.stroke_dash.dash[i++] = 1; - } - if (penstyle==PS_DOT || penstyle==PS_DASHDOT || penstyle==PS_DASHDOTDOT) { - d->dc[d->level].style.stroke_dash.dash[i++] = 1; - d->dc[d->level].style.stroke_dash.dash[i++] = 1; - } - if (penstyle==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 PS_SOLID: - default: - { - d->dc[d->level].style.stroke_dasharray_set = 0; - break; - } - } - - switch (pEmr->lopn.lopnStyle & PS_ENDCAP_MASK) { - case PS_ENDCAP_ROUND: - { - d->dc[d->level].style.stroke_linecap.computed = 1; - break; - } - case PS_ENDCAP_SQUARE: - { - d->dc[d->level].style.stroke_linecap.computed = 2; - break; - } - case PS_ENDCAP_FLAT: - default: - { - d->dc[d->level].style.stroke_linecap.computed = 0; - break; - } - } - - switch (pEmr->lopn.lopnStyle & PS_JOIN_MASK) { - case PS_JOIN_BEVEL: - { - d->dc[d->level].style.stroke_linejoin.computed = 2; - break; - } - case PS_JOIN_MITER: - { - d->dc[d->level].style.stroke_linejoin.computed = 0; - break; - } - case 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 == 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( GetRValue(pEmr->lopn.lopnColor) ); - g = SP_COLOR_U_TO_F( GetGValue(pEmr->lopn.lopnColor) ); - b = SP_COLOR_U_TO_F( GetBValue(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) -{ - PEMREXTCREATEPEN pEmr = NULL; - - if (index >= 0 && index < d->n_obj) - pEmr = (PEMREXTCREATEPEN) d->emf_obj[index].lpEMFR; - - if (!pEmr) - return; - - switch (pEmr->elp.elpPenStyle & PS_STYLE_MASK) { - case 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; - double dash_length = pix_to_size_point( d, 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 PS_DASH: - case PS_DOT: - case PS_DASHDOT: - case PS_DASHDOTDOT: - { - int i = 0; - int penstyle = (pEmr->elp.elpPenStyle & PS_STYLE_MASK); - d->dc[d->level].style.stroke_dash.n_dash = - penstyle == PS_DASHDOTDOT ? 6 : penstyle == 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==PS_DASH || penstyle==PS_DASHDOT || penstyle==PS_DASHDOTDOT) { - d->dc[d->level].style.stroke_dash.dash[i++] = 3; - d->dc[d->level].style.stroke_dash.dash[i++] = 2; - } - if (penstyle==PS_DOT || penstyle==PS_DASHDOT || penstyle==PS_DASHDOTDOT) { - d->dc[d->level].style.stroke_dash.dash[i++] = 1; - d->dc[d->level].style.stroke_dash.dash[i++] = 2; - } - if (penstyle==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 PS_SOLID: - default: - { - d->dc[d->level].style.stroke_dasharray_set = 0; - break; - } - } - - switch (pEmr->elp.elpPenStyle & PS_ENDCAP_MASK) { - case PS_ENDCAP_ROUND: - { - d->dc[d->level].style.stroke_linecap.computed = 1; - break; - } - case PS_ENDCAP_SQUARE: - { - d->dc[d->level].style.stroke_linecap.computed = 2; - break; - } - case PS_ENDCAP_FLAT: - default: - { - d->dc[d->level].style.stroke_linecap.computed = 0; - break; - } - } - - switch (pEmr->elp.elpPenStyle & PS_JOIN_MASK) { - case PS_JOIN_BEVEL: - { - d->dc[d->level].style.stroke_linejoin.computed = 2; - break; - } - case PS_JOIN_MITER: - { - d->dc[d->level].style.stroke_linejoin.computed = 0; - break; - } - case 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 == 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; - } - - double r, g, b; - r = SP_COLOR_U_TO_F( GetRValue(pEmr->elp.elpColor) ); - g = SP_COLOR_U_TO_F( GetGValue(pEmr->elp.elpColor) ); - b = SP_COLOR_U_TO_F( GetBValue(pEmr->elp.elpColor) ); - - d->dc[d->level].style.stroke.value.color.set( r, g, b ); -} - - -static void -select_brush(PEMF_CALLBACK_DATA d, int index) -{ - PEMRCREATEBRUSHINDIRECT pEmr = NULL; - - if (index >= 0 && index < d->n_obj) - pEmr = (PEMRCREATEBRUSHINDIRECT) d->emf_obj[index].lpEMFR; - - if (!pEmr) - return; - - if (pEmr->lb.lbStyle == BS_SOLID) { - double r, g, b; - r = SP_COLOR_U_TO_F( GetRValue(pEmr->lb.lbColor) ); - g = SP_COLOR_U_TO_F( GetGValue(pEmr->lb.lbColor) ); - b = SP_COLOR_U_TO_F( GetBValue(pEmr->lb.lbColor) ); - d->dc[d->level].style.fill.value.color.set( r, g, b ); - } - - d->dc[d->level].fill_set = true; -} - - -static void -select_font(PEMF_CALLBACK_DATA d, int index) -{ - PEMREXTCREATEFONTINDIRECTW pEmr = NULL; - - if (index >= 0 && index < d->n_obj) - pEmr = (PEMREXTCREATEFONTINDIRECTW) d->emf_obj[index].lpEMFR; - - if (!pEmr) - return; - - int cur_level = d->level; - d->level = d->emf_obj[index].level; - double font_size = pix_to_size_point( d, pEmr->elfw.elfLogFont.lfHeight ); - 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 == FW_THIN ? SP_CSS_FONT_WEIGHT_100 : - pEmr->elfw.elfLogFont.lfWeight == FW_EXTRALIGHT ? SP_CSS_FONT_WEIGHT_200 : - pEmr->elfw.elfLogFont.lfWeight == FW_LIGHT ? SP_CSS_FONT_WEIGHT_300 : - pEmr->elfw.elfLogFont.lfWeight == FW_NORMAL ? SP_CSS_FONT_WEIGHT_400 : - pEmr->elfw.elfLogFont.lfWeight == FW_MEDIUM ? SP_CSS_FONT_WEIGHT_500 : - pEmr->elfw.elfLogFont.lfWeight == FW_SEMIBOLD ? SP_CSS_FONT_WEIGHT_600 : - pEmr->elfw.elfLogFont.lfWeight == FW_BOLD ? SP_CSS_FONT_WEIGHT_700 : - pEmr->elfw.elfLogFont.lfWeight == FW_EXTRABOLD ? SP_CSS_FONT_WEIGHT_800 : - pEmr->elfw.elfLogFont.lfWeight == FW_HEAVY ? SP_CSS_FONT_WEIGHT_900 : - pEmr->elfw.elfLogFont.lfWeight == FW_NORMAL ? SP_CSS_FONT_WEIGHT_NORMAL : - pEmr->elfw.elfLogFont.lfWeight == FW_BOLD ? SP_CSS_FONT_WEIGHT_BOLD : - pEmr->elfw.elfLogFont.lfWeight == FW_EXTRALIGHT ? SP_CSS_FONT_WEIGHT_LIGHTER : - pEmr->elfw.elfLogFont.lfWeight == FW_EXTRABOLD ? SP_CSS_FONT_WEIGHT_BOLDER : - 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) - g_free(d->dc[d->level].tstyle.font_family.value); - d->dc[d->level].tstyle.font_family.value = - (gchar *) g_utf16_to_utf8( (gunichar2*) pEmr->elfw.elfLogFont.lfFaceName, -1, NULL, NULL, 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; - 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, 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 = pObj; - } -} - -static void -assert_empty_path(PEMF_CALLBACK_DATA d, const char * /*fun*/) -{ - if (!d->path->empty()) { - // g_debug("emf-win32-inout: assert_empty_path failed for %s\n", fun); - - *(d->outsvg) += "\n"; - - *(d->path) = ""; - } -} - - -static int CALLBACK -myEnhMetaFileProc(HDC /*hDC*/, HANDLETABLE * /*lpHTable*/, ENHMETARECORD const *lpEMFR, int /*nObj*/, LPARAM lpData) -{ - PEMF_CALLBACK_DATA d; - SVGOStringStream tmp_outsvg; - SVGOStringStream tmp_path; - SVGOStringStream tmp_str; - SVGOStringStream dbg_str; - - d = (PEMF_CALLBACK_DATA) lpData; - - if (d->pathless_stroke) { - if (lpEMFR->iType!=EMR_POLYBEZIERTO && lpEMFR->iType!=EMR_POLYBEZIERTO16 && - lpEMFR->iType!=EMR_POLYLINETO && lpEMFR->iType!=EMR_POLYLINETO16 && - lpEMFR->iType!=EMR_LINETO && lpEMFR->iType!=EMR_ARCTO && - lpEMFR->iType!=EMR_SETBKCOLOR && lpEMFR->iType!=EMR_SETROP2 && - lpEMFR->iType!=EMR_SETBKMODE && lpEMFR->iType!=EMR_SELECTOBJECT && - lpEMFR->iType!=EMR_BEGINPATH) - { - *(d->outsvg) += " outsvg) += "\n\t"; - *(d->outsvg) += *(d->path); - *(d->outsvg) += " \" /> \n"; - *(d->path) = ""; - d->pathless_stroke = false; - } - } - - switch (lpEMFR->iType) - { - case EMR_HEADER: - { - dbg_str << "\n"; - - *(d->outdef) += "\n"; - - if (d->pDesc) { - *(d->outdef) += "\n"; - } - - ENHMETAHEADER *pEmr = (ENHMETAHEADER *) 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 - 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 - - 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 EMR_POLYBEZIER: - { - dbg_str << "\n"; - - PEMRPOLYBEZIER pEmr = (PEMRPOLYBEZIER) lpEMFR; - DWORD i,j; - - if (pEmr->cptl<4) - break; - - if (!d->inpath) { - assert_empty_path(d, "EMR_POLYBEZIER"); - - *(d->outsvg) += " outsvg) += "\n\td=\""; - } - - 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 ) << " "; - } - } - - if (d->inpath) { - tmp_path << tmp_str.str().c_str(); - } - else { - *(d->outsvg) += tmp_str.str().c_str(); - *(d->outsvg) += " \" /> \n"; - } - - break; - } - case EMR_POLYGON: - { - dbg_str << "\n"; - - EMRPOLYGON *pEmr = (EMRPOLYGON *) lpEMFR; - DWORD i; - - if (pEmr->cptl < 2) - break; - - assert_empty_path(d, "EMR_POLYGON"); - - *(d->outsvg) += " outsvg) += "\n\td=\""; - - 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 ) << " "; - } - - *(d->outsvg) += tmp_str.str().c_str(); - *(d->outsvg) += " z \" /> \n"; - - break; - } - case EMR_POLYLINE: - { - dbg_str << "\n"; - - EMRPOLYLINE *pEmr = (EMRPOLYLINE *) lpEMFR; - DWORD i; - - if (pEmr->cptl<2) - break; - - if (!d->inpath) { - assert_empty_path(d, "EMR_POLYLINE"); - - *(d->outsvg) += " outsvg) += "\n\td=\""; - } - - 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 ) << " "; - } - - if (d->inpath) { - tmp_path << tmp_str.str().c_str(); - } - else { - *(d->outsvg) += tmp_str.str().c_str(); - *(d->outsvg) += " \" /> \n"; - } - - break; - } - case EMR_POLYBEZIERTO: - { - dbg_str << "\n"; - - PEMRPOLYBEZIERTO pEmr = (PEMRPOLYBEZIERTO) lpEMFR; - DWORD i,j; - - if (d->path->empty()) { - d->pathless_stroke = true; - *(d->path) = "d=\""; - } - - 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 EMR_POLYLINETO: - { - dbg_str << "\n"; - - PEMRPOLYLINETO pEmr = (PEMRPOLYLINETO) lpEMFR; - DWORD i; - - if (d->path->empty()) { - d->pathless_stroke = true; - *(d->path) = "d=\""; - } - - 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 EMR_POLYPOLYLINE: - case EMR_POLYPOLYGON: - { - if (lpEMFR->iType == EMR_POLYPOLYLINE) - dbg_str << "\n"; - if (lpEMFR->iType == EMR_POLYPOLYGON) - dbg_str << "\n"; - - PEMRPOLYPOLYGON pEmr = (PEMRPOLYPOLYGON) lpEMFR; - unsigned int n, i, j; - - if (!d->inpath) { - assert_empty_path(d, lpEMFR->iType == EMR_POLYPOLYGON ? "EMR_POLYPOLYGON" : "EMR_POLYPOLYLINE"); - - *(d->outsvg) += " iType==EMR_POLYPOLYGON ? EMR_STROKEANDFILLPATH : EMR_STROKEPATH); - *(d->outsvg) += "\n\td=\""; - } - - POINTL *aptl = (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 == EMR_POLYPOLYGON) - tmp_str << " z"; - tmp_str << " \n"; - } - - if (d->inpath) { - tmp_path << tmp_str.str().c_str(); - } - else { - *(d->outsvg) += tmp_str.str().c_str(); - *(d->outsvg) += " \" /> \n"; - } - - break; - } - case EMR_SETWINDOWEXTEX: - { - dbg_str << "\n"; - - PEMRSETWINDOWEXTEX pEmr = (PEMRSETWINDOWEXTEX) 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 EMR_SETWINDOWORGEX: - { - dbg_str << "\n"; - - PEMRSETWINDOWORGEX pEmr = (PEMRSETWINDOWORGEX) lpEMFR; - d->dc[d->level].winorg = pEmr->ptlOrigin; - break; - } - case EMR_SETVIEWPORTEXTEX: - { - dbg_str << "\n"; - - PEMRSETVIEWPORTEXTEX pEmr = (PEMRSETVIEWPORTEXTEX) 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 EMR_SETVIEWPORTORGEX: - { - dbg_str << "\n"; - - PEMRSETVIEWPORTORGEX pEmr = (PEMRSETVIEWPORTORGEX) lpEMFR; - d->dc[d->level].vieworg = pEmr->ptlOrigin; - break; - } - case EMR_SETBRUSHORGEX: - dbg_str << "\n"; - break; - case EMR_EOF: - { - dbg_str << "\n"; - - assert_empty_path(d, "EMR_EOF"); - tmp_outsvg << "\n"; - tmp_outsvg << "\n"; - *(d->outsvg) = *(d->outdef) + *(d->outsvg); - break; - } - case EMR_SETPIXELV: - dbg_str << "\n"; - break; - case EMR_SETMAPPERFLAGS: - dbg_str << "\n"; - break; - case EMR_SETMAPMODE: - dbg_str << "\n"; - break; - case EMR_SETBKMODE: - dbg_str << "\n"; - break; - case EMR_SETPOLYFILLMODE: - { - dbg_str << "\n"; - - PEMRSETPOLYFILLMODE pEmr = (PEMRSETPOLYFILLMODE) lpEMFR; - d->dc[d->level].style.fill_rule.value = - (pEmr->iMode == ALTERNATE ? 0 : - pEmr->iMode == WINDING ? 1 : 0); - break; - } - case EMR_SETROP2: - dbg_str << "\n"; - break; - case EMR_SETSTRETCHBLTMODE: - dbg_str << "\n"; - break; - case EMR_SETTEXTALIGN: - { - dbg_str << "\n"; - - PEMRSETTEXTALIGN pEmr = (PEMRSETTEXTALIGN) lpEMFR; - d->dc[d->level].textAlign = pEmr->iMode; - break; - } - case EMR_SETCOLORADJUSTMENT: - dbg_str << "\n"; - break; - case EMR_SETTEXTCOLOR: - { - dbg_str << "\n"; - - PEMRSETTEXTCOLOR pEmr = (PEMRSETTEXTCOLOR) lpEMFR; - d->dc[d->level].textColor = pEmr->crColor; - d->dc[d->level].textColorSet = true; - break; - } - case EMR_SETBKCOLOR: - dbg_str << "\n"; - break; - case EMR_OFFSETCLIPRGN: - dbg_str << "\n"; - break; - case EMR_MOVETOEX: - { - dbg_str << "\n"; - - PEMRMOVETOEX pEmr = (PEMRMOVETOEX) lpEMFR; - - if (d->path->empty()) { - d->pathless_stroke = true; - *(d->path) = "d=\""; - } - - 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 EMR_SETMETARGN: - dbg_str << "\n"; - break; - case EMR_EXCLUDECLIPRECT: - dbg_str << "\n"; - break; - case EMR_INTERSECTCLIPRECT: - { - dbg_str << "\n"; - - PEMRINTERSECTCLIPRECT pEmr = (PEMRINTERSECTCLIPRECT) lpEMFR; - 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"; - - assert_empty_path(d, "EMR_RECTANGLE"); - - *(d->outdef) += tmp_rectangle.str().c_str(); - *(d->path) = ""; - break; - } - case EMR_SCALEVIEWPORTEXTEX: - dbg_str << "\n"; - break; - case EMR_SCALEWINDOWEXTEX: - dbg_str << "\n"; - break; - case 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 EMR_RESTOREDC: - { - dbg_str << "\n"; - - PEMRRESTOREDC pEmr = (PEMRRESTOREDC) 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 EMR_SETWORLDTRANSFORM: - { - dbg_str << "\n"; - - PEMRSETWORLDTRANSFORM pEmr = (PEMRSETWORLDTRANSFORM) lpEMFR; - d->dc[d->level].worldTransform = pEmr->xform; - break; - } - case EMR_MODIFYWORLDTRANSFORM: - { - dbg_str << "\n"; - - PEMRMODIFYWORLDTRANSFORM pEmr = (PEMRMODIFYWORLDTRANSFORM) lpEMFR; - switch (pEmr->iMode) - { - case 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 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 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 EMR_SELECTOBJECT: - { - dbg_str << "\n"; - - PEMRSELECTOBJECT pEmr = (PEMRSELECTOBJECT) lpEMFR; - unsigned int index = pEmr->ihObject; - - if (index >= ENHMETA_STOCK_OBJECT) { - index -= ENHMETA_STOCK_OBJECT; - switch (index) { - case NULL_BRUSH: - d->dc[d->level].fill_set = false; - break; - case BLACK_BRUSH: - case DKGRAY_BRUSH: - case GRAY_BRUSH: - case LTGRAY_BRUSH: - case WHITE_BRUSH: - { - float val = 0; - switch (index) { - case BLACK_BRUSH: - val = 0.0 / 255.0; - break; - case DKGRAY_BRUSH: - val = 64.0 / 255.0; - break; - case GRAY_BRUSH: - val = 128.0 / 255.0; - break; - case LTGRAY_BRUSH: - val = 192.0 / 255.0; - break; - case 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_set = true; - break; - } - case NULL_PEN: - d->dc[d->level].stroke_set = false; - break; - case BLACK_PEN: - case WHITE_PEN: - { - float val = index == 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_set = true; - - break; - } - } - } else { - if ( /*index >= 0 &&*/ index < (unsigned int) d->n_obj) { - switch (d->emf_obj[index].type) - { - case EMR_CREATEPEN: - select_pen(d, index); - break; - case EMR_CREATEBRUSHINDIRECT: - select_brush(d, index); - break; - case EMR_EXTCREATEPEN: - select_extpen(d, index); - break; - case EMR_EXTCREATEFONTINDIRECTW: - select_font(d, index); - break; - } - } - } - break; - } - case EMR_CREATEPEN: - { - dbg_str << "\n"; - - PEMRCREATEPEN pEmr = (PEMRCREATEPEN) lpEMFR; - int index = pEmr->ihPen; - - EMRCREATEPEN *pPen = - (EMRCREATEPEN *) malloc( sizeof(EMRCREATEPEN) ); - pPen->lopn = pEmr->lopn; - insert_object(d, index, EMR_CREATEPEN, (ENHMETARECORD *) pPen); - - break; - } - case EMR_CREATEBRUSHINDIRECT: - { - dbg_str << "\n"; - - PEMRCREATEBRUSHINDIRECT pEmr = (PEMRCREATEBRUSHINDIRECT) lpEMFR; - int index = pEmr->ihBrush; - - EMRCREATEBRUSHINDIRECT *pBrush = - (EMRCREATEBRUSHINDIRECT *) malloc( sizeof(EMRCREATEBRUSHINDIRECT) ); - pBrush->lb = pEmr->lb; - insert_object(d, index, EMR_CREATEBRUSHINDIRECT, (ENHMETARECORD *) pBrush); - - break; - } - case EMR_DELETEOBJECT: - dbg_str << "\n"; - break; - case EMR_ANGLEARC: - dbg_str << "\n"; - break; - case EMR_ELLIPSE: - { - dbg_str << "\n"; - - PEMRELLIPSE pEmr = (PEMRELLIPSE) lpEMFR; - RECTL rclBox = pEmr->rclBox; - - double l = pix_to_x_point( d, pEmr->rclBox.left, pEmr->rclBox.top ); - double t = pix_to_y_point( d, pEmr->rclBox.left, pEmr->rclBox.top ); - double r = pix_to_x_point( d, pEmr->rclBox.right, pEmr->rclBox.bottom ); - double b = pix_to_y_point( d, pEmr->rclBox.right, pEmr->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 << "\" "; - - assert_empty_path(d, "EMR_ELLIPSE"); - - *(d->outsvg) += " iType); - *(d->outsvg) += "\n\t"; - *(d->outsvg) += tmp_ellipse.str().c_str(); - *(d->outsvg) += "/> \n"; - *(d->path) = ""; - break; - } - case EMR_RECTANGLE: - { - dbg_str << "\n"; - - PEMRRECTANGLE pEmr = (PEMRRECTANGLE) lpEMFR; - 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 << "d=\""; - 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"; - - assert_empty_path(d, "EMR_RECTANGLE"); - - *(d->outsvg) += " iType); - *(d->outsvg) += "\n\t"; - *(d->outsvg) += tmp_rectangle.str().c_str(); - *(d->outsvg) += " \" /> \n"; - *(d->path) = ""; - break; - } - case EMR_ROUNDRECT: - { - dbg_str << "\n"; - - PEMRROUNDRECT pEmr = (PEMRROUNDRECT) lpEMFR; - RECTL rc = pEmr->rclBox; - 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 << "d=\""; - 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"; - assert_empty_path(d, "EMR_ROUNDRECT"); - - *(d->outsvg) += " iType); - *(d->outsvg) += "\n\t"; - *(d->outsvg) += tmp_rectangle.str().c_str(); - *(d->outsvg) += " \" /> \n"; - *(d->path) = ""; - break; - } - case EMR_ARC: - dbg_str << "\n"; - break; - case EMR_CHORD: - dbg_str << "\n"; - break; - case EMR_PIE: - { - dbg_str << "\n"; - - PEMRPIE pEmr = (PEMRPIE) lpEMFR; - 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 ); - double x1 = pix_to_x_point( d, pEmr->ptlStart.x, pEmr->ptlStart.y ); - double y1 = pix_to_y_point( d, pEmr->ptlStart.x, pEmr->ptlStart.y ); - double x2 = pix_to_x_point( d, pEmr->ptlEnd.x, pEmr->ptlEnd.y ); - double y2 = pix_to_y_point( d, pEmr->ptlEnd.x, pEmr->ptlEnd.y ); - - SVGOStringStream tmp_pie; - tmp_pie << "d=\"" << - "\n\tM " << x1 << " " << y1 << " "; - - double angle1 = -atan2(y1 - (t + b) / 2.0, x1 - (l + r) / 2.0); - double angle2 = -atan2(y2 - (t + b) / 2.0, x2 - (l + r) / 2.0); - double angle = angle2 - angle1; - if (angle < 0) angle += 2*M_PI; - - bool large_arc_flag = false; - if (angle > M_PI) large_arc_flag = true; - - tmp_pie << "\n\tA " << - fabs(l - r) / 2.0 << " " << - fabs(t - b) / 2.0 << " " << - "0 " << large_arc_flag << " 0 " << - x2 << " " << y2 << " " << - "\n\tL " << (l + r) / 2.0 << " " << (t + b) / 2.0 << " " << - "\n\tz"; - - assert_empty_path(d, "EMR_PIE"); - - *(d->outsvg) += " iType); - *(d->outsvg) += "\n\t"; - *(d->outsvg) += tmp_pie.str().c_str(); - *(d->outsvg) += " \" /> \n"; - *(d->path) = ""; - break; - } - case EMR_SELECTPALETTE: - dbg_str << "\n"; - break; - case EMR_CREATEPALETTE: - dbg_str << "\n"; - break; - case EMR_SETPALETTEENTRIES: - dbg_str << "\n"; - break; - case EMR_RESIZEPALETTE: - dbg_str << "\n"; - break; - case EMR_REALIZEPALETTE: - dbg_str << "\n"; - break; - case EMR_EXTFLOODFILL: - dbg_str << "\n"; - break; - case EMR_LINETO: - { - dbg_str << "\n"; - - PEMRLINETO pEmr = (PEMRLINETO) lpEMFR; - - if (d->path->empty()) { - d->pathless_stroke = true; - *(d->path) = "d=\""; - } - - 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 EMR_ARCTO: - dbg_str << "\n"; - break; - case EMR_POLYDRAW: - dbg_str << "\n"; - break; - case EMR_SETARCDIRECTION: - dbg_str << "\n"; - break; - case EMR_SETMITERLIMIT: - { - dbg_str << "\n"; - - PEMRSETMITERLIMIT pEmr = (PEMRSETMITERLIMIT) lpEMFR; - - //BUG in SetMiterLimit() on mingw, possibly in underlying Windows(at least on XP?) - //The function takes a float but saves a 32 bit int in the 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 < 1.0) - d->dc[d->level].style.stroke_miterlimit.value = 1.0; - break; - } - case EMR_BEGINPATH: - { - dbg_str << "\n"; - - if (!d->pathless_stroke) { - tmp_path << "d=\""; - *(d->path) = ""; - } - d->pathless_stroke = false; - d->inpath = true; - break; - } - case EMR_ENDPATH: - { - dbg_str << "\n"; - - tmp_path << "\""; - d->inpath = false; - break; - } - case EMR_CLOSEFIGURE: - { - dbg_str << "\n"; - - tmp_path << "\n\tz"; - break; - } - case EMR_FILLPATH: - case EMR_STROKEANDFILLPATH: - case EMR_STROKEPATH: - { - if (lpEMFR->iType == EMR_FILLPATH) - dbg_str << "\n"; - if (lpEMFR->iType == EMR_STROKEANDFILLPATH) - dbg_str << "\n"; - if (lpEMFR->iType == EMR_STROKEPATH) - dbg_str << "\n"; - - *(d->outsvg) += " iType); - *(d->outsvg) += "\n\t"; - *(d->outsvg) += *(d->path); - *(d->outsvg) += " /> \n"; - *(d->path) = ""; - break; - } - case EMR_FLATTENPATH: - dbg_str << "\n"; - break; - case EMR_WIDENPATH: - dbg_str << "\n"; - break; - case EMR_SELECTCLIPPATH: - dbg_str << "\n"; - break; - case EMR_ABORTPATH: - dbg_str << "\n"; - break; - case EMR_GDICOMMENT: - { - dbg_str << "\n"; - - PEMRGDICOMMENT pEmr = (PEMRGDICOMMENT) lpEMFR; - - CHAR *szTxt = (CHAR *) pEmr->Data; - - for (DWORD 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 EMR_FILLRGN: - dbg_str << "\n"; - break; - case EMR_FRAMERGN: - dbg_str << "\n"; - break; - case EMR_INVERTRGN: - dbg_str << "\n"; - break; - case EMR_PAINTRGN: - dbg_str << "\n"; - break; - case EMR_EXTSELECTCLIPRGN: - { - dbg_str << "\n"; - - PEMREXTSELECTCLIPRGN pEmr = (PEMREXTSELECTCLIPRGN) lpEMFR; - if (pEmr->iMode == RGN_COPY) - clipset = false; - break; - } - case EMR_BITBLT: - { - dbg_str << "\n"; - - PEMRBITBLT pEmr = (PEMRBITBLT) lpEMFR; - if (pEmr->dwRop == DPA) { - // should be an application of a DIBPATTERNBRUSHPT, use a solid color instead - double l = pix_to_x_point( d, pEmr->xDest, pEmr->yDest); - double t = pix_to_y_point( d, pEmr->xDest, pEmr->yDest); - double r = pix_to_x_point( d, pEmr->xDest + pEmr->cxDest, pEmr->yDest + pEmr->cyDest); - double b = pix_to_y_point( d, pEmr->xDest + pEmr->cxDest, pEmr->yDest + pEmr->cyDest); - - SVGOStringStream tmp_rectangle; - tmp_rectangle << "d=\""; - 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"; - - assert_empty_path(d, "EMR_BITBLT"); - - *(d->outsvg) += " iType); - *(d->outsvg) += "\n\t"; - *(d->outsvg) += tmp_rectangle.str().c_str(); - *(d->outsvg) += " \" /> \n"; - *(d->path) = ""; - } - break; - } - case EMR_STRETCHBLT: - dbg_str << "\n"; - break; - case EMR_MASKBLT: - dbg_str << "\n"; - break; - case EMR_PLGBLT: - dbg_str << "\n"; - break; - case EMR_SETDIBITSTODEVICE: - dbg_str << "\n"; - break; - case EMR_STRETCHDIBITS: - dbg_str << "\n"; - break; - case EMR_EXTCREATEFONTINDIRECTW: - { - dbg_str << "\n"; - - PEMREXTCREATEFONTINDIRECTW pEmr = (PEMREXTCREATEFONTINDIRECTW) lpEMFR; - int index = pEmr->ihFont; - - EMREXTCREATEFONTINDIRECTW *pFont = - (EMREXTCREATEFONTINDIRECTW *) malloc( sizeof(EMREXTCREATEFONTINDIRECTW) ); - pFont->elfw = pEmr->elfw; - insert_object(d, index, EMR_EXTCREATEFONTINDIRECTW, (ENHMETARECORD *) pFont); - break; - } - case EMR_EXTTEXTOUTA: - { - dbg_str << "\n"; - break; - } - case EMR_EXTTEXTOUTW: - { - dbg_str << "\n"; - - PEMREXTTEXTOUTW pEmr = (PEMREXTTEXTOUTW) lpEMFR; - - double x1 = pEmr->emrtext.ptlReference.x; - double y1 = pEmr->emrtext.ptlReference.y; - - if (d->dc[d->level].textAlign & 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); - - if (!(d->dc[d->level].textAlign & TA_BOTTOM)) - if (d->dc[d->level].style.baseline_shift.value) { - x += std::sin(d->dc[d->level].style.baseline_shift.value*M_PI/180.0)*fabs(d->dc[d->level].style.font_size.computed); - y += 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 += fabs(d->dc[d->level].style.font_size.computed); - - wchar_t *wide_text = (wchar_t *) ((char *) pEmr + pEmr->emrtext.offString); - - gchar *ansi_text = - (gchar *) g_utf16_to_utf8( (gunichar2 *) wide_text, pEmr->emrtext.nChars, NULL, NULL, NULL ); - - 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;", - GetRValue(d->dc[d->level].textColor), - GetGValue(d->dc[d->level].textColor), - GetBValue(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); - int lcr = ((d->dc[d->level].textAlign & TA_CENTER) == TA_CENTER) ? 2 : ((d->dc[d->level].textAlign & TA_RIGHT) == TA_RIGHT) ? 1 : 0; - - assert_empty_path(d, "EMR_EXTTEXTOUTW"); - - 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); - g_free(ansi_text); - } - - break; - } - case EMR_POLYBEZIER16: - { - dbg_str << "\n"; - - PEMRPOLYBEZIER16 pEmr = (PEMRPOLYBEZIER16) lpEMFR; - POINTS *apts = (POINTS *) pEmr->apts; // Bug in MinGW wingdi.h ? - DWORD i,j; - - if (pEmr->cpts<4) - break; - - if (!d->inpath) { - assert_empty_path(d, "EMR_POLYBEZIER16"); - - *(d->outsvg) += " outsvg) += "\n\td=\""; - } - - 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 ) << " "; - } - } - - if (d->inpath) { - tmp_path << tmp_str.str().c_str(); - } - else { - *(d->outsvg) += tmp_str.str().c_str(); - *(d->outsvg) += " \" /> \n"; - } - - break; - } - case EMR_POLYGON16: - { - dbg_str << "\n"; - - PEMRPOLYGON16 pEmr = (PEMRPOLYGON16) lpEMFR; - POINTS *apts = (POINTS *) pEmr->apts; // Bug in MinGW wingdi.h ? - SVGOStringStream tmp_poly; - unsigned int i; - unsigned int first = 0; - - assert_empty_path(d, "EMR_POLYGON16"); - - *(d->outsvg) += " outsvg) += "\n\td=\""; - - // 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 ) << " "; - } - - *(d->outsvg) += tmp_poly.str().c_str(); - *(d->outsvg) += " z \" /> \n"; - - break; - } - case EMR_POLYLINE16: - { - dbg_str << "\n"; - - EMRPOLYLINE16 *pEmr = (EMRPOLYLINE16 *) lpEMFR; - POINTS *apts = (POINTS *) pEmr->apts; // Bug in MinGW wingdi.h ? - DWORD i; - - if (pEmr->cpts<2) - break; - - if (!d->inpath) { - assert_empty_path(d, "EMR_POLYLINE16"); - - *(d->outsvg) += " outsvg) += "\n\td=\""; - } - - 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 ) << " "; - } - - if (d->inpath) { - tmp_path << tmp_str.str().c_str(); - } - else { - *(d->outsvg) += tmp_str.str().c_str(); - *(d->outsvg) += " \" /> \n"; - } - - break; - } - case EMR_POLYBEZIERTO16: - { - dbg_str << "\n"; - - PEMRPOLYBEZIERTO16 pEmr = (PEMRPOLYBEZIERTO16) lpEMFR; - POINTS *apts = (POINTS *) pEmr->apts; // Bug in MinGW wingdi.h ? - DWORD i,j; - - if (d->path->empty()) { - d->pathless_stroke = true; - *(d->path) = "d=\""; - } - - 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 EMR_POLYLINETO16: - { - dbg_str << "\n"; - - PEMRPOLYLINETO16 pEmr = (PEMRPOLYLINETO16) lpEMFR; - POINTS *apts = (POINTS *) pEmr->apts; // Bug in MinGW wingdi.h ? - DWORD i; - - if (d->path->empty()) { - d->pathless_stroke = true; - *(d->path) = "d=\""; - } - - 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 EMR_POLYPOLYLINE16: - case EMR_POLYPOLYGON16: - { - if (lpEMFR->iType == EMR_POLYPOLYLINE16) - dbg_str << "\n"; - if (lpEMFR->iType == EMR_POLYPOLYGON16) - dbg_str << "\n"; - - PEMRPOLYPOLYGON16 pEmr = (PEMRPOLYPOLYGON16) lpEMFR; - unsigned int n, i, j; - - if (!d->inpath) { - assert_empty_path(d, lpEMFR->iType == EMR_POLYPOLYGON16 ? "EMR_POLYPOLYGON16" : "EMR_POLYPOLYLINE16"); - - *(d->outsvg) += " iType==EMR_POLYPOLYGON16 ? EMR_STROKEANDFILLPATH : EMR_STROKEPATH); - *(d->outsvg) += "\n\td=\""; - } - - POINTS *apts = (POINTS *) &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 == EMR_POLYPOLYGON16) - tmp_str << " z"; - tmp_str << " \n"; - } - - if (d->inpath) { - tmp_path << tmp_str.str().c_str(); - } - else { - *(d->outsvg) += tmp_str.str().c_str(); - *(d->outsvg) += " \" /> \n"; - } - - break; - } - case EMR_POLYDRAW16: - dbg_str << "\n"; - break; - case EMR_CREATEMONOBRUSH: - dbg_str << "\n"; - break; - case EMR_CREATEDIBPATTERNBRUSHPT: - { - dbg_str << "\n"; - - PEMRCREATEDIBPATTERNBRUSHPT pEmr = (PEMRCREATEDIBPATTERNBRUSHPT) lpEMFR; - int index = pEmr->ihBrush; - - EMRCREATEDIBPATTERNBRUSHPT *pBrush = - (EMRCREATEDIBPATTERNBRUSHPT *) malloc( sizeof(EMRCREATEDIBPATTERNBRUSHPT) ); - insert_object(d, index, EMR_CREATEDIBPATTERNBRUSHPT, (ENHMETARECORD *) pBrush); - break; - } - case EMR_EXTCREATEPEN: - { - dbg_str << "\n"; - - PEMREXTCREATEPEN pEmr = (PEMREXTCREATEPEN) lpEMFR; - int index = pEmr->ihPen; - - EMREXTCREATEPEN *pPen = - (EMREXTCREATEPEN *) malloc( sizeof(EMREXTCREATEPEN) + - sizeof(DWORD) * pEmr->elp.elpNumEntries ); - pPen->ihPen = pEmr->ihPen; - pPen->offBmi = pEmr->offBmi; - pPen->cbBmi = pEmr->cbBmi; - pPen->offBits = pEmr->offBits; - pPen->cbBits = pEmr->cbBits; - pPen->elp = pEmr->elp; - for (unsigned int i=0; ielp.elpNumEntries; i++) { - pPen->elp.elpStyleEntry[i] = pEmr->elp.elpStyleEntry[i]; - } - insert_object(d, index, EMR_EXTCREATEPEN, (ENHMETARECORD *) pPen); - - break; - } - case EMR_POLYTEXTOUTA: - dbg_str << "\n"; - break; - case EMR_POLYTEXTOUTW: - dbg_str << "\n"; - break; - case EMR_SETICMMODE: - dbg_str << "\n"; - break; - case EMR_CREATECOLORSPACE: - dbg_str << "\n"; - break; - case EMR_SETCOLORSPACE: - dbg_str << "\n"; - break; - case EMR_DELETECOLORSPACE: - dbg_str << "\n"; - break; - case EMR_GLSRECORD: - dbg_str << "\n"; - break; - case EMR_GLSBOUNDEDRECORD: - dbg_str << "\n"; - break; - case EMR_PIXELFORMAT: - dbg_str << "\n"; - break; - default: - dbg_str << "\n"; - break; - } - -// *(d->outsvg) += dbg_str.str().c_str(); - *(d->outsvg) += tmp_outsvg.str().c_str(); - *(d->path) += tmp_path.str().c_str(); - - return 1; -} - -static int CALLBACK -myMetaFileProc(HDC /*hDC*/, HANDLETABLE * /*lpHTable*/, METARECORD * /*lpMFR*/, int /*nObj*/, LPARAM /*lpData*/) -{ - g_warning("Unable to import Windows Meta File.\n"); - return 0; -} - -// 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 WORD for the hmf handle, and a SMALL_RECT -// for the bbox rectangle. -#pragma pack( push ) -#pragma pack( 2 ) -typedef struct -{ - DWORD dwKey; - WORD hmf; - SMALL_RECT bbox; - WORD wInch; - DWORD dwReserved; - WORD wCheckSum; -} APMHEADER, *PAPMHEADER; -#pragma pack( pop ) - - -SPDocument * -EmfWin32::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; - - gsize bytesRead = 0; - gsize bytesWritten = 0; - GError* error = NULL; - gchar *local_fn = - g_filename_from_utf8( uri, -1, &bytesRead, &bytesWritten, &error ); - - if (local_fn == NULL) { - return NULL; - } - - d.outsvg = new Glib::ustring(""); - d.path = new Glib::ustring(""); - d.outdef = new Glib::ustring(""); - - CHAR *ansi_uri = (CHAR *) local_fn; - gunichar2 *unicode_fn = g_utf8_to_utf16( local_fn, -1, NULL, NULL, NULL ); - WCHAR *unicode_uri = (WCHAR *) unicode_fn; - - DWORD filesize = 0; - HANDLE fp = NULL; - - HMETAFILE hmf; - HENHMETAFILE hemf; - - fp = CreateFileW(unicode_uri, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - - if ( fp != INVALID_HANDLE_VALUE ) { - filesize = GetFileSize(fp, NULL); - CloseHandle(fp); - } - - // Try open as Enhanced Metafile - hemf = GetEnhMetaFileW(unicode_uri); - - if (!hemf) { - // Try open as Windows Metafile - hmf = GetMetaFileW(unicode_uri); - - METAFILEPICT mp; - HDC hDC; - - if (!hmf) { - WCHAR szTemp[MAX_PATH]; - - DWORD dw = GetShortPathNameW( unicode_uri, szTemp, MAX_PATH ); - if (dw) { - hmf = GetMetaFileW( szTemp ); - } - } - - if (hmf) { - DWORD nSize = GetMetaFileBitsEx( hmf, 0, NULL ); - - if (!nSize) - nSize = filesize; - - if (nSize) { - BYTE *lpvData = new BYTE[nSize]; - if (lpvData) { - DWORD dw = GetMetaFileBitsEx( hmf, nSize, lpvData ); - if (dw) { - // Fill out a METAFILEPICT structure - mp.mm = MM_ANISOTROPIC; - mp.xExt = 1000; - mp.yExt = 1000; - mp.hMF = NULL; - // Get a reference DC - hDC = GetDC( NULL ); - // Make an enhanced metafile from the windows metafile - hemf = SetWinMetaFileBits( nSize, lpvData, hDC, &mp ); - // Clean up - ReleaseDC( NULL, hDC ); - DeleteMetaFile( hmf ); - hmf = NULL; - } - else { - // EnumMetaFile - } - delete[] lpvData; - } - else { - DeleteMetaFile( hmf ); - hmf = NULL; - } - } - else { - DeleteMetaFile( hmf ); - hmf = NULL; - } - } - else { - // Try open as Aldus Placeable Metafile - HANDLE hFile; - hFile = CreateFileW( unicode_uri, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); - - if (hFile != INVALID_HANDLE_VALUE) { - DWORD nSize = GetFileSize( hFile, NULL ); - if (nSize) { - BYTE *lpvData = new BYTE[nSize]; - if (lpvData) { - DWORD dw = ReadFile( hFile, lpvData, nSize, &nSize, NULL ); - if (dw) { - if ( ((PAPMHEADER)lpvData)->dwKey == 0x9ac6cdd7l ) { - // Fill out a METAFILEPICT structure - mp.mm = MM_ANISOTROPIC; - mp.xExt = ((PAPMHEADER)lpvData)->bbox.Right - ((PAPMHEADER)lpvData)->bbox.Left; - mp.xExt = ( mp.xExt * 2540l ) / (DWORD)(((PAPMHEADER)lpvData)->wInch); - mp.yExt = ((PAPMHEADER)lpvData)->bbox.Bottom - ((PAPMHEADER)lpvData)->bbox.Top; - mp.yExt = ( mp.yExt * 2540l ) / (DWORD)(((PAPMHEADER)lpvData)->wInch); - mp.hMF = NULL; - // Get a reference DC - hDC = GetDC( NULL ); - // Create an enhanced metafile from the bits - hemf = SetWinMetaFileBits( nSize, lpvData+sizeof(APMHEADER), hDC, &mp ); - // Clean up - ReleaseDC( NULL, hDC ); - } - } - delete[] lpvData; - } - } - CloseHandle( hFile ); - } - } - } - - if ((!hemf && !hmf) || !d.outsvg || !d.path) { - if (d.outsvg) - delete d.outsvg; - if (d.path) - delete d.path; - if (d.outdef) - delete d.outdef; - if (local_fn) - g_free(local_fn); - if (unicode_fn) - g_free(unicode_fn); - if (hemf) - DeleteEnhMetaFile(hemf); - if (hmf) - DeleteMetaFile(hmf); - return NULL; - } - - d.pDesc = NULL; - - if (hemf) { - DWORD dwNeeded = GetEnhMetaFileDescriptionA( hemf, 0, NULL ); - if ( dwNeeded > 0 ) { - d.pDesc = (CHAR *) malloc( dwNeeded + 1 ); - if ( GetEnhMetaFileDescription( hemf, dwNeeded, d.pDesc ) == 0 ) - lstrcpy( d.pDesc, "" ); - if ( lstrlen( d.pDesc ) > 1 ) - d.pDesc[lstrlen(d.pDesc)] = '#'; - } - - // This ugly reinterpret_cast is to prevent old versions of gcc from whining about a mismatch in the const-ness of the arguments - EnumEnhMetaFile(NULL, hemf, reinterpret_cast(myEnhMetaFileProc), (LPVOID) &d, NULL); - DeleteEnhMetaFile(hemf); - } - else { - EnumMetaFile(NULL, hmf, myMetaFileProc, (LPARAM) &d); - DeleteMetaFile(hmf); - } - - 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; - - if (d.emf_obj) { - int i; - for (i=0; i\n" - "" N_("EMF Input") "\n" - "org.inkscape.input.emf.win32\n" - "\n" - ".emf\n" - "image/x-emf\n" - "" N_("Enhanced Metafiles (*.emf)") "\n" - "" N_("Enhanced Metafiles") "\n" - "org.inkscape.output.emf.win32\n" - "\n" - "", new EmfWin32()); - - /* WMF in */ - Inkscape::Extension::build_from_mem( - "\n" - "" N_("WMF Input") "\n" - "org.inkscape.input.wmf.win32\n" - "\n" - ".wmf\n" - "image/x-wmf\n" - "" N_("Windows Metafiles (*.wmf)") "\n" - "" N_("Windows Metafiles") "\n" - "org.inkscape.output.emf.win32\n" - "\n" - "", new EmfWin32()); - - /* EMF out */ - Inkscape::Extension::build_from_mem( - "\n" - "" N_("EMF Output") "\n" - "org.inkscape.output.emf.win32\n" - "true\n" - "\n" - ".emf\n" - "image/x-emf\n" - "" N_("Enhanced Metafile (*.emf)") "\n" - "" N_("Enhanced Metafile") "\n" - "\n" - "", new EmfWin32()); - - return; -} - - -} } } /* namespace Inkscape, Extension, Implementation */ - -#endif /* WIN32 */ -/* - 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 : diff -rNu3 src/extension/internal/emf-win32-inout.h src/extension/internal/emf-win32-inout.h --- src/extension/internal/emf-win32-inout.h 2012-09-13 10:24:25.000000000 -0700 +++ src/extension/internal/emf-win32-inout.h 1969-12-31 16:00:00.000000000 -0800 @@ -1,57 +0,0 @@ -/** @file - * @brief Enhanced Metafile Input/Output - */ -/* Authors: - * Ulf Erikson - * - * Copyright (C) 2006-2008 Authors - * - * Released under GNU GPL, read the file 'COPYING' for more information - */ -#ifndef SEEN_EXTENSION_INTERNAL_EMF_WIN32_H -#define SEEN_EXTENSION_INTERNAL_EMF_WIN32_H -#ifdef WIN32 - -#include "extension/implementation/implementation.h" - -namespace Inkscape { -namespace Extension { -namespace Internal { - -class EmfWin32 : Inkscape::Extension::Implementation::Implementation { //This is a derived class - -public: - EmfWin32(); // Empty constructor - - virtual ~EmfWin32();//Destructor - - bool check(Inkscape::Extension::Extension *module); //Can this module load (always yes for now) - - void save(Inkscape::Extension::Output *mod, // Save the given document to the given filename - SPDocument *doc, - gchar const *filename); - - virtual SPDocument *open( Inkscape::Extension::Input *mod, - const gchar *uri ); - - static void init(void);//Initialize the class - -private: -}; - -} } } /* namespace Inkscape, Extension, Implementation */ - -#endif /* WIN32 */ - -#endif /* EXTENSION_INTERNAL_EMF_WIN32_H */ - -/* - 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 : diff -rNu3 src/extension/internal/emf-win32-print.cpp src/extension/internal/emf-win32-print.cpp --- src/extension/internal/emf-win32-print.cpp 2012-09-13 10:24:25.000000000 -0700 +++ src/extension/internal/emf-win32-print.cpp 1969-12-31 16:00:00.000000000 -0800 @@ -1,942 +0,0 @@ -/** @file - * @brief Enhanced Metafile printing - */ -/* Authors: - * Ulf Erikson - * Jon A. Cruz - * Abhishek Sharma - * - * Copyright (C) 2006-2009 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 WIN32 - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - - -#include <2geom/pathvector.h> -#include <2geom/rect.h> -#include <2geom/bezier-curve.h> -#include <2geom/hvlinesegment.h> -#include "helper/geom.h" -#include "helper/geom-curves.h" -#include "sp-item.h" - -#include "style.h" -#include "inkscape-version.h" -#include "sp-root.h" - -#include "emf-win32-print.h" - -#include "unit-constants.h" - -#include "extension/system.h" -#include "extension/print.h" -#include "document.h" - - -namespace Inkscape { -namespace Extension { -namespace Internal { - -static float dwDPI = 2540; - - -PrintEmfWin32::PrintEmfWin32 (void): - _width(0), - _height(0), - hdc(NULL), - hbrush(NULL), - hbrushOld(NULL), - hpen(NULL), - stroke_and_fill(false), - fill_only(false), - simple_shape(false) -{ -} - - -PrintEmfWin32::~PrintEmfWin32 (void) -{ - if (hdc) { - HENHMETAFILE metafile = CloseEnhMetaFile( hdc ); - if ( metafile ) { - DeleteEnhMetaFile( metafile ); - } - DeleteDC( hdc ); - } - - /* restore default signal handling for SIGPIPE */ -#if !defined(_WIN32) && !defined(__WIN32__) - (void) signal(SIGPIPE, SIG_DFL); -#endif - return; -} - - -unsigned int PrintEmfWin32::setup (Inkscape::Extension::Print * /*mod*/) -{ - return TRUE; -} - - -unsigned int PrintEmfWin32::begin (Inkscape::Extension::Print *mod, SPDocument *doc) -{ - gchar const *utf8_fn = mod->get_param_string("destination"); - - gsize bytesRead = 0; - gsize bytesWritten = 0; - GError* error = NULL; - gchar *local_fn = - g_filename_from_utf8( utf8_fn, -1, &bytesRead, &bytesWritten, &error ); - - if (local_fn == NULL) { - return 1; - } - - CHAR *ansi_uri = (CHAR *) local_fn; - gunichar2 *unicode_fn = g_utf8_to_utf16( local_fn, -1, NULL, NULL, NULL ); - WCHAR *unicode_uri = (WCHAR *) unicode_fn; - - // width and height in px - _width = doc->getWidth(); - _height = doc->getHeight(); - - bool pageBoundingBox; - pageBoundingBox = mod->get_param_bool("pageBoundingBox"); - - Geom::Rect d; - if (pageBoundingBox) { - d = Geom::Rect::from_xywh(0, 0, _width, _height); - } else { - SPItem* doc_item = doc->getRoot(); - Geom::OptRect bbox = doc_item->desktopVisualBounds(); - if (bbox) d = *bbox; - } - - d *= Geom::Scale(IN_PER_PX); - - float dwInchesX = d.width(); - float dwInchesY = d.height(); - - // dwInchesX x dwInchesY in .01mm units - SetRect( &rc, 0, 0, (int) ceil(dwInchesX*2540), (int) ceil(dwInchesY*2540) ); - - // Get a Reference DC - HDC hScreenDC = GetDC( NULL ); - - // Get the physical characteristics of the reference DC - int PixelsX = GetDeviceCaps( hScreenDC, HORZRES ); - int PixelsY = GetDeviceCaps( hScreenDC, VERTRES ); - int MMX = GetDeviceCaps( hScreenDC, HORZSIZE ); - int MMY = GetDeviceCaps( hScreenDC, VERTSIZE ); - - CHAR buff[1024]; - ZeroMemory(buff, sizeof(buff)); - snprintf(buff, sizeof(buff)-1, "Inkscape %s (%s)", Inkscape::version_string, __DATE__); - INT len = strlen(buff); - CHAR *p1 = strrchr(ansi_uri, '\\'); - CHAR *p2 = strrchr(ansi_uri, '/'); - CHAR *p = MAX(p1, p2); - if (p) - p++; - else - p = ansi_uri; - snprintf(buff+len+1, sizeof(buff)-len-2, "%s", p); - - // Create the Metafile - { - WCHAR wbuff[1024]; - ZeroMemory(wbuff, sizeof(wbuff)); - MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, buff, sizeof(buff)/sizeof(buff[0]), wbuff, sizeof(wbuff)/sizeof(wbuff[0])); - hdc = CreateEnhMetaFileW( hScreenDC, unicode_uri, &rc, wbuff ); - } - - // Release the reference DC - ReleaseDC( NULL, hScreenDC ); - - // Did we get a good metafile? - if (hdc == NULL) - { - g_free(local_fn); - g_free(unicode_fn); - return 1; - } - - // Anisotropic mapping mode - SetMapMode( hdc, MM_ANISOTROPIC ); - - // Set the Windows extent - int windowextX = (int) ceil(dwInchesX*dwDPI); - int windowextY = (int) ceil(dwInchesY*dwDPI); - SetWindowExtEx( hdc, windowextX, windowextY, NULL ); - - // Set the viewport extent to reflect - // dwInchesX" x dwInchesY" in device units - int viewportextX = (int)((float)dwInchesX*25.4f*(float)PixelsX/(float)MMX); - int viewportextY = (int)((float)dwInchesY*25.4f*(float)PixelsY/(float)MMY); - SetViewportExtEx( hdc, viewportextX, viewportextY, NULL ); - - if (1) { - snprintf(buff, sizeof(buff)-1, "Screen=%dx%dpx, %dx%dmm", PixelsX, PixelsY, MMX, MMY); - GdiComment(hdc, strlen(buff), (BYTE*) buff); - - snprintf(buff, sizeof(buff)-1, "Drawing=%.1lfx%.1lfpx, %.1lfx%.1lfmm", _width, _height, dwInchesX * MM_PER_IN, dwInchesY * MM_PER_IN); - GdiComment(hdc, strlen(buff), (BYTE*) buff); - } - - SetRect( &rc, 0, 0, (int) ceil(dwInchesX*dwDPI), (int) ceil(dwInchesY*dwDPI) ); - - g_free(local_fn); - g_free(unicode_fn); - - m_tr_stack.push( Geom::Scale(1, -1) * Geom::Translate(0, doc->getHeight())); /// @fixme hardcoded doc2dt transform - - return 0; -} - - -unsigned int PrintEmfWin32::finish (Inkscape::Extension::Print * /*mod*/) -{ - if (!hdc) return 0; - - flush_fill(); // flush any pending fills - - HENHMETAFILE metafile = CloseEnhMetaFile( hdc ); - if ( metafile ) { - DeleteEnhMetaFile( metafile ); - } - DeleteDC( hdc ); - - hdc = NULL; - - return 0; -} - - -unsigned int PrintEmfWin32::comment (Inkscape::Extension::Print * /*module*/, - const char * /*comment*/) -{ - if (!hdc) return 0; - - flush_fill(); // flush any pending fills - - return 0; -} - - -int PrintEmfWin32::create_brush(SPStyle const *style) -{ - float rgb[3]; - - if (style) { - float opacity = SP_SCALE24_TO_FLOAT(style->fill_opacity.value); - if (opacity <= 0.0) - return 1; - - sp_color_get_rgb_floatv( &style->fill.value.color, rgb ); - hbrush = CreateSolidBrush( RGB(255*rgb[0], 255*rgb[1], 255*rgb[2]) ); - hbrushOld = (HBRUSH) SelectObject( hdc, hbrush ); - - SetPolyFillMode( hdc, - style->fill_rule.computed == 0 ? WINDING : - style->fill_rule.computed == 2 ? ALTERNATE : ALTERNATE ); - } else { // if (!style) - hbrush = CreateSolidBrush( RGB(255, 255, 255) ); - hbrushOld = (HBRUSH) SelectObject( hdc, hbrush ); - SetPolyFillMode( hdc, ALTERNATE ); - } - - return 0; -} - - -void PrintEmfWin32::destroy_brush() -{ - SelectObject( hdc, hbrushOld ); - if (hbrush) - DeleteObject( hbrush ); - hbrush = NULL; - hbrushOld = NULL; -} - - -void PrintEmfWin32::create_pen(SPStyle const *style, const Geom::Affine &transform) -{ - if (style) { - float rgb[3]; - - sp_color_get_rgb_floatv(&style->stroke.value.color, rgb); - - LOGBRUSH lb; - ZeroMemory(&lb, sizeof(lb)); - lb.lbStyle = BS_SOLID; - lb.lbColor = RGB( 255*rgb[0], 255*rgb[1], 255*rgb[2] ); - - int linestyle = PS_SOLID; - int linecap = 0; - int linejoin = 0; - DWORD n_dash = 0; - DWORD *dash = NULL; - - using Geom::X; - using Geom::Y; - - Geom::Point zero(0, 0); - Geom::Point one(1, 1); - Geom::Point p0(zero * transform); - Geom::Point p1(one * transform); - Geom::Point p(p1 - p0); - - double scale = sqrt( (p[X]*p[X]) + (p[Y]*p[Y]) ) / sqrt(2); - - DWORD linewidth = MAX( 1, (DWORD) (scale * style->stroke_width.computed * IN_PER_PX * dwDPI) ); - - if (style->stroke_linecap.computed == 0) { - linecap = PS_ENDCAP_FLAT; - } - else if (style->stroke_linecap.computed == 1) { - linecap = PS_ENDCAP_ROUND; - } - else if (style->stroke_linecap.computed == 2) { - linecap = PS_ENDCAP_SQUARE; - } - - if (style->stroke_linejoin.computed == 0) { - linejoin = PS_JOIN_MITER; - } - else if (style->stroke_linejoin.computed == 1) { - linejoin = PS_JOIN_ROUND; - } - else if (style->stroke_linejoin.computed == 2) { - linejoin = PS_JOIN_BEVEL; - } - - if (style->stroke_dash.n_dash && - style->stroke_dash.dash ) - { - int i = 0; - while (linestyle != PS_USERSTYLE && - (i < style->stroke_dash.n_dash)) { - if (style->stroke_dash.dash[i] > 0.00000001) - linestyle = PS_USERSTYLE; - i++; - } - - if (linestyle == PS_USERSTYLE) { - n_dash = style->stroke_dash.n_dash; - dash = new DWORD[n_dash]; - for (i = 0; i < style->stroke_dash.n_dash; i++) { - dash[i] = (DWORD) (style->stroke_dash.dash[i] * IN_PER_PX * dwDPI); - } - } - } - - hpen = ExtCreatePen( - PS_GEOMETRIC | linestyle | linecap | linejoin, - linewidth, - &lb, - n_dash, - dash ); - - if ( !hpen && linestyle == PS_USERSTYLE ) { - hpen = ExtCreatePen( - PS_GEOMETRIC | PS_SOLID | linecap | linejoin, - linewidth, - &lb, - 0, - NULL ); - } - - if ( !hpen ) { - hpen = CreatePen( - PS_SOLID, - linewidth, - lb.lbColor ); - } - - hpenOld = (HPEN) SelectObject( hdc, hpen ); - - if (linejoin == PS_JOIN_MITER) { - float oldmiterlimit; - float miterlimit = style->stroke_miterlimit.value; //ratio, not a pt size - - SetMiterLimit( - hdc, - miterlimit, - &oldmiterlimit ); - } - - if (n_dash) { - delete[] dash; - } - } - else { // if (!style) - hpen = CreatePen( PS_SOLID, 1, RGB(0, 0, 0) ); - hpenOld = (HPEN) SelectObject( hdc, hpen ); - } -} - - -void PrintEmfWin32::destroy_pen() -{ - SelectObject( hdc, hpenOld ); - if (hpen) - DeleteObject( hpen ); - hpen = NULL; -} - - -void PrintEmfWin32::flush_fill() -{ - if (!fill_pathv.empty()) { - stroke_and_fill = false; - fill_only = true; - print_pathv(fill_pathv, fill_transform); - fill_only = false; - if (!simple_shape) - FillPath( hdc ); - destroy_brush(); - fill_pathv.clear(); - } -} - -unsigned int PrintEmfWin32::bind(Inkscape::Extension::Print * /*mod*/, Geom::Affine const &transform, float /*opacity*/) -{ - if (!m_tr_stack.empty()) { - Geom::Affine tr_top = m_tr_stack.top(); - m_tr_stack.push(transform * tr_top); - } else { - m_tr_stack.push(transform); - } - - return 1; -} - -unsigned int PrintEmfWin32::release(Inkscape::Extension::Print * /*mod*/) -{ - m_tr_stack.pop(); - return 1; -} - -unsigned int PrintEmfWin32::fill(Inkscape::Extension::Print * /*mod*/, - Geom::PathVector const &pathv, Geom::Affine const & /*transform*/, SPStyle const *style, - Geom::OptRect const &/*pbox*/, Geom::OptRect const &/*dbox*/, Geom::OptRect const &/*bbox*/) -{ - if (!hdc) return 0; - - Geom::Affine tf = m_tr_stack.top(); - - flush_fill(); // flush any pending fills - - if (style->fill.isColor()) { - if (create_brush(style)) - return 0; - } else { - // create_brush(NULL); - return 0; - } - - fill_pathv.clear(); - std::copy(pathv.begin(), pathv.end(), std::back_inserter(fill_pathv)); - fill_transform = tf; - - // postpone fill in case of stroke-and-fill - - return 0; -} - - -unsigned int PrintEmfWin32::stroke (Inkscape::Extension::Print * /*mod*/, - Geom::PathVector const &pathv, const Geom::Affine &/*transform*/, const SPStyle *style, - Geom::OptRect const &/*pbox*/, Geom::OptRect const &/*dbox*/, Geom::OptRect const &/*bbox*/) -{ - if (!hdc) return 0; - - Geom::Affine tf = m_tr_stack.top(); - - stroke_and_fill = ( pathv == fill_pathv ); - - if (!stroke_and_fill) { - flush_fill(); // flush any pending fills - } - - if (style->stroke.isColor()) { - create_pen(style, tf); - } else { - // create_pen(NULL, tf); - return 0; - } - - print_pathv(pathv, tf); - - if (stroke_and_fill) { - if (!simple_shape) - StrokeAndFillPath( hdc ); - destroy_brush(); - fill_pathv.clear(); - } else { - if (!simple_shape) - StrokePath( hdc ); - } - - destroy_pen(); - - return 0; -} - - -bool PrintEmfWin32::print_simple_shape(Geom::PathVector const &pathv, const Geom::Affine &transform) -{ - Geom::PathVector pv = pathv_to_linear_and_cubic_beziers( pathv * transform ); - - int nodes = 0; - int moves = 0; - int lines = 0; - int curves = 0; - - for (Geom::PathVector::const_iterator pit = pv.begin(); pit != pv.end(); ++pit) - { - moves++; - nodes++; - - for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_open(); ++cit) - { - nodes++; - - if ( is_straight_curve(*cit) ) { - lines++; - } - else if (Geom::CubicBezier const *cubic = dynamic_cast(&*cit)) { - cubic = cubic; - curves++; - } - } - } - - if (!nodes) - return false; - - POINT *lpPoints = new POINT[moves + lines + curves*3]; - int i = 0; - - /** - * For all Subpaths in the - */ - for (Geom::PathVector::const_iterator pit = pv.begin(); pit != pv.end(); ++pit) - { - using Geom::X; - using Geom::Y; - - Geom::Point p0 = pit->initialPoint(); - - p0[X] = (p0[X] * IN_PER_PX * dwDPI); - p0[Y] = (p0[Y] * IN_PER_PX * dwDPI); - - LONG const x0 = (LONG) round(p0[X]); - LONG const y0 = (LONG) round(rc.bottom-p0[Y]); - - lpPoints[i].x = x0; - lpPoints[i].y = y0; - i = i + 1; - - /** - * For all segments in the subpath - */ - for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_open(); ++cit) - { - if ( is_straight_curve(*cit) ) - { - //Geom::Point p0 = cit->initialPoint(); - Geom::Point p1 = cit->finalPoint(); - - //p0[X] = (p0[X] * IN_PER_PX * dwDPI); - p1[X] = (p1[X] * IN_PER_PX * dwDPI); - //p0[Y] = (p0[Y] * IN_PER_PX * dwDPI); - p1[Y] = (p1[Y] * IN_PER_PX * dwDPI); - - //LONG const x0 = (LONG) round(p0[X]); - //LONG const y0 = (LONG) round(rc.bottom-p0[Y]); - LONG const x1 = (LONG) round(p1[X]); - LONG const y1 = (LONG) round(rc.bottom-p1[Y]); - - lpPoints[i].x = x1; - lpPoints[i].y = y1; - i = i + 1; - } - else if (Geom::CubicBezier const *cubic = dynamic_cast(&*cit)) - { - std::vector points = cubic->points(); - //Geom::Point p0 = points[0]; - Geom::Point p1 = points[1]; - Geom::Point p2 = points[2]; - Geom::Point p3 = points[3]; - - //p0[X] = (p0[X] * IN_PER_PX * dwDPI); - p1[X] = (p1[X] * IN_PER_PX * dwDPI); - p2[X] = (p2[X] * IN_PER_PX * dwDPI); - p3[X] = (p3[X] * IN_PER_PX * dwDPI); - //p0[Y] = (p0[Y] * IN_PER_PX * dwDPI); - p1[Y] = (p1[Y] * IN_PER_PX * dwDPI); - p2[Y] = (p2[Y] * IN_PER_PX * dwDPI); - p3[Y] = (p3[Y] * IN_PER_PX * dwDPI); - - //LONG const x0 = (LONG) round(p0[X]); - //LONG const y0 = (LONG) round(rc.bottom-p0[Y]); - LONG const x1 = (LONG) round(p1[X]); - LONG const y1 = (LONG) round(rc.bottom-p1[Y]); - LONG const x2 = (LONG) round(p2[X]); - LONG const y2 = (LONG) round(rc.bottom-p2[Y]); - LONG const x3 = (LONG) round(p3[X]); - LONG const y3 = (LONG) round(rc.bottom-p3[Y]); - - POINT pt[3]; - pt[0].x = x1; - pt[0].y = y1; - pt[1].x = x2; - pt[1].y = y2; - pt[2].x = x3; - pt[2].y = y3; - - lpPoints[i].x = x1; - lpPoints[i].y = y1; - lpPoints[i+1].x = x2; - lpPoints[i+1].y = y2; - lpPoints[i+2].x = x3; - lpPoints[i+2].y = y3; - i = i + 3; - } - } - } - - bool done = false; - bool closed = (lpPoints[0].x == lpPoints[i-1].x) && (lpPoints[0].y == lpPoints[i-1].y); - bool polygon = false; - bool rectangle = false; - bool ellipse = false; - - if (moves == 1 && moves+lines == nodes && closed) { - polygon = true; -// if (nodes==5) { // disable due to LP Bug 407394 -// if (lpPoints[0].x == lpPoints[3].x && lpPoints[1].x == lpPoints[2].x && -// lpPoints[0].y == lpPoints[1].y && lpPoints[2].y == lpPoints[3].y) -// { -// rectangle = true; -// } -// } - } - else if (moves == 1 && nodes == 5 && moves+curves == nodes && closed) { -// if (lpPoints[0].x == lpPoints[1].x && lpPoints[1].x == lpPoints[11].x && -// lpPoints[5].x == lpPoints[6].x && lpPoints[6].x == lpPoints[7].x && -// lpPoints[2].x == lpPoints[10].x && lpPoints[3].x == lpPoints[9].x && lpPoints[4].x == lpPoints[8].x && -// lpPoints[2].y == lpPoints[3].y && lpPoints[3].y == lpPoints[4].y && -// lpPoints[8].y == lpPoints[9].y && lpPoints[9].y == lpPoints[10].y && -// lpPoints[5].y == lpPoints[1].y && lpPoints[6].y == lpPoints[0].y && lpPoints[7].y == lpPoints[11].y) -// { // disable due to LP Bug 407394 -// ellipse = true; -// } - } - - if (polygon || ellipse) { - HPEN hpenTmp = NULL; - HPEN hpenOld = NULL; - HBRUSH hbrushTmp = NULL; - HBRUSH hbrushOld = NULL; - - if (!stroke_and_fill) { - if (fill_only) { - hpenTmp = (HPEN) GetStockObject(NULL_PEN); - hpenOld = (HPEN) SelectObject( hdc, hpenTmp ); - } - else { // if (stroke_only) - hbrushTmp = (HBRUSH) GetStockObject(NULL_BRUSH); - hbrushOld = (HBRUSH) SelectObject( hdc, hbrushTmp ); - } - } - - if (polygon) { - if (rectangle) - Rectangle( hdc, lpPoints[0].x, lpPoints[0].y, lpPoints[2].x, lpPoints[2].y ); - else - Polygon( hdc, lpPoints, nodes ); - } - else if (ellipse) { - Ellipse( hdc, lpPoints[6].x, lpPoints[3].y, lpPoints[0].x, lpPoints[9].y); - } - - done = true; - - if (hpenOld) - SelectObject( hdc, hpenOld ); - if (hpenTmp) - DeleteObject( hpenTmp ); - if (hbrushOld) - SelectObject( hdc, hbrushOld ); - if (hbrushTmp) - DeleteObject( hbrushTmp ); - } - - delete[] lpPoints; - - return done; -} - -unsigned int PrintEmfWin32::print_pathv(Geom::PathVector const &pathv, const Geom::Affine &transform) -{ - simple_shape = print_simple_shape(pathv, transform); - - if (simple_shape) - return TRUE; - - Geom::PathVector pv = pathv_to_linear_and_cubic_beziers( pathv * transform ); - - BeginPath( hdc ); - - /** - * For all Subpaths in the - */ - for (Geom::PathVector::const_iterator pit = pv.begin(); pit != pv.end(); ++pit) - { - using Geom::X; - using Geom::Y; - - Geom::Point p0 = pit->initialPoint(); - - p0[X] = (p0[X] * IN_PER_PX * dwDPI); - p0[Y] = (p0[Y] * IN_PER_PX * dwDPI); - - LONG const x0 = (LONG) round(p0[X]); - LONG const y0 = (LONG) round(rc.bottom-p0[Y]); - - MoveToEx( hdc, x0, y0, NULL ); - - /** - * For all segments in the subpath - */ - for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_open(); ++cit) - { - if ( is_straight_curve(*cit) ) - { - //Geom::Point p0 = cit->initialPoint(); - Geom::Point p1 = cit->finalPoint(); - - //p0[X] = (p0[X] * IN_PER_PX * dwDPI); - p1[X] = (p1[X] * IN_PER_PX * dwDPI); - //p0[Y] = (p0[Y] * IN_PER_PX * dwDPI); - p1[Y] = (p1[Y] * IN_PER_PX * dwDPI); - - //LONG const x0 = (LONG) round(p0[X]); - //LONG const y0 = (LONG) round(rc.bottom-p0[Y]); - LONG const x1 = (LONG) round(p1[X]); - LONG const y1 = (LONG) round(rc.bottom-p1[Y]); - - LineTo( hdc, x1, y1 ); - } - else if (Geom::CubicBezier const *cubic = dynamic_cast(&*cit)) - { - std::vector points = cubic->points(); - //Geom::Point p0 = points[0]; - Geom::Point p1 = points[1]; - Geom::Point p2 = points[2]; - Geom::Point p3 = points[3]; - - //p0[X] = (p0[X] * IN_PER_PX * dwDPI); - p1[X] = (p1[X] * IN_PER_PX * dwDPI); - p2[X] = (p2[X] * IN_PER_PX * dwDPI); - p3[X] = (p3[X] * IN_PER_PX * dwDPI); - //p0[Y] = (p0[Y] * IN_PER_PX * dwDPI); - p1[Y] = (p1[Y] * IN_PER_PX * dwDPI); - p2[Y] = (p2[Y] * IN_PER_PX * dwDPI); - p3[Y] = (p3[Y] * IN_PER_PX * dwDPI); - - //LONG const x0 = (LONG) round(p0[X]); - //LONG const y0 = (LONG) round(rc.bottom-p0[Y]); - LONG const x1 = (LONG) round(p1[X]); - LONG const y1 = (LONG) round(rc.bottom-p1[Y]); - LONG const x2 = (LONG) round(p2[X]); - LONG const y2 = (LONG) round(rc.bottom-p2[Y]); - LONG const x3 = (LONG) round(p3[X]); - LONG const y3 = (LONG) round(rc.bottom-p3[Y]); - - POINT pt[3]; - pt[0].x = x1; - pt[0].y = y1; - pt[1].x = x2; - pt[1].y = y2; - pt[2].x = x3; - pt[2].y = y3; - - PolyBezierTo( hdc, pt, 3 ); - } - else - { - g_warning("logical error, because pathv_to_linear_and_cubic_beziers was used"); - } - } - - if (pit->end_default() == pit->end_closed()) { - CloseFigure( hdc ); - } - } - - EndPath( hdc ); - - return TRUE; -} - - -bool PrintEmfWin32::textToPath(Inkscape::Extension::Print * ext) -{ - return ext->get_param_bool("textToPath"); -} - -unsigned int PrintEmfWin32::text(Inkscape::Extension::Print * /*mod*/, char const *text, Geom::Point const &p, - SPStyle const *const style) -{ - if (!hdc) return 0; - - HFONT hfont = NULL; - Geom::Affine tf = m_tr_stack.top(); - double rot = 1800.0*std::atan2(tf[1], tf[0])/M_PI; // 0.1 degree rotation - -#ifdef USE_PANGO_WIN32 -/* - font_instance *tf = (font_factory::Default())->Face(style->text->font_family.value, font_style_to_pos(*style)); - if (tf) { - LOGFONT *lf = pango_win32_font_logfont(tf->pFont); - tf->Unref(); - hfont = CreateFontIndirect(lf); - g_free(lf); - } -*/ -#endif - - if (!hfont) { - LOGFONTW *lf = (LOGFONTW*)g_malloc(sizeof(LOGFONTW)); - g_assert(lf != NULL); - - lf->lfHeight = -style->font_size.computed * IN_PER_PX * dwDPI; - lf->lfWidth = 0; - lf->lfEscapement = rot; - lf->lfOrientation = rot; - lf->lfWeight = - style->font_weight.computed == SP_CSS_FONT_WEIGHT_100 ? FW_THIN : - style->font_weight.computed == SP_CSS_FONT_WEIGHT_200 ? FW_EXTRALIGHT : - style->font_weight.computed == SP_CSS_FONT_WEIGHT_300 ? FW_LIGHT : - style->font_weight.computed == SP_CSS_FONT_WEIGHT_400 ? FW_NORMAL : - style->font_weight.computed == SP_CSS_FONT_WEIGHT_500 ? FW_MEDIUM : - style->font_weight.computed == SP_CSS_FONT_WEIGHT_600 ? FW_SEMIBOLD : - style->font_weight.computed == SP_CSS_FONT_WEIGHT_700 ? FW_BOLD : - style->font_weight.computed == SP_CSS_FONT_WEIGHT_800 ? FW_EXTRABOLD : - style->font_weight.computed == SP_CSS_FONT_WEIGHT_900 ? FW_HEAVY : - FW_NORMAL; - lf->lfItalic = (style->font_style.computed == SP_CSS_FONT_STYLE_ITALIC); - lf->lfUnderline = style->text_decoration.underline; - lf->lfStrikeOut = style->text_decoration.line_through; - lf->lfCharSet = DEFAULT_CHARSET; - lf->lfOutPrecision = OUT_DEFAULT_PRECIS; - lf->lfClipPrecision = CLIP_DEFAULT_PRECIS; - lf->lfQuality = DEFAULT_QUALITY; - lf->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; - - gunichar2 *unicode_name = g_utf8_to_utf16( style->text->font_family.value, -1, NULL, NULL, NULL ); - wcsncpy(lf->lfFaceName, (wchar_t*) unicode_name, LF_FACESIZE-1); - g_free(unicode_name); - - hfont = CreateFontIndirectW(lf); - - g_free(lf); - } - - HFONT hfontOld = (HFONT) SelectObject(hdc, hfont); - - float rgb[3]; - sp_color_get_rgb_floatv( &style->fill.value.color, rgb ); - SetTextColor(hdc, RGB(255*rgb[0], 255*rgb[1], 255*rgb[2])); - - // Text alignment: - // - (x,y) coordinates received by this filter are those of the point where the text - // actually starts, and already takes into account the text object's alignment; - // - for this reason, the EMF text alignment must always be TA_BASELINE|TA_LEFT. - SetTextAlign(hdc, TA_BASELINE | TA_LEFT); - - // Transparent text background - SetBkMode(hdc, TRANSPARENT); - - Geom::Point p2 = p * tf; - p2[Geom::X] = (p2[Geom::X] * IN_PER_PX * dwDPI); - p2[Geom::Y] = (p2[Geom::Y] * IN_PER_PX * dwDPI); - - LONG const xpos = (LONG) round(p2[Geom::X]); - LONG const ypos = (LONG) round(rc.bottom - p2[Geom::Y]); - - { - gunichar2 *unicode_text = g_utf8_to_utf16( text, -1, NULL, NULL, NULL ); - TextOutW(hdc, xpos, ypos, (WCHAR*)unicode_text, wcslen((wchar_t*)unicode_text)); - } - - SelectObject(hdc, hfontOld); - DeleteObject(hfont); - - return 0; -} - -void PrintEmfWin32::init (void) -{ - /* EMF print */ - Inkscape::Extension::build_from_mem( - "\n" - "Enhanced Metafile Print\n" - "org.inkscape.print.emf.win32\n" - "\n" - "true\n" - "true\n" - "\n" - "", new PrintEmfWin32()); - - return; -} - -unsigned int PrintEmfWin32::image(Inkscape::Extension::Print * /* module */, /** not used */ - unsigned char *px, /** array of pixel values, Gdk::Pixbuf bitmap format */ - unsigned int /*w*/, /** width of bitmap */ - unsigned int /*h*/, /** height of bitmap */ - unsigned int /*rs*/, /** row stride (normally w*4) */ - Geom::Affine const & /*tf_ignore*/, /** WRONG affine transform, use the one from m_tr_stack */ - SPStyle const * /*style*/) /** provides indirect link to image object */ -{ - free(px); - return 0; -} - -} /* namespace Internal */ -} /* namespace Extension */ -} /* namespace Inkscape */ - -#endif /* WIN32 */ - -/* - 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 : diff -rNu3 src/extension/internal/emf-win32-print.h src/extension/internal/emf-win32-print.h --- src/extension/internal/emf-win32-print.h 2012-09-13 10:24:25.000000000 -0700 +++ src/extension/internal/emf-win32-print.h 1969-12-31 16:00:00.000000000 -0800 @@ -1,120 +0,0 @@ -/** @file - * @brief Enhanced Metafile printing - implementation - */ -/* Author: - * Ulf Erikson - * - * Copyright (C) 2006-2008 Authors - * - * Released under GNU GPL, read the file 'COPYING' for more information - */ -#ifndef __INKSCAPE_EXTENSION_INTERNAL_PRINT_EMF_WIN32_H__ -#define __INKSCAPE_EXTENSION_INTERNAL_PRINT_EMF_WIN32_H__ - -#ifdef WIN32 - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#define WIN32_LEAN_AND_MEAN -#include - -#include "extension/implementation/implementation.h" -//#include "extension/extension.h" - -#include <2geom/pathvector.h> - -#include - -namespace Inkscape { -namespace Extension { -namespace Internal { - -class PrintEmfWin32 : public Inkscape::Extension::Implementation::Implementation -{ - double _width; - double _height; - HDC hdc; - RECT rc; - - HBRUSH hbrush, hbrushOld; - HPEN hpen, hpenOld; - - std::stack m_tr_stack; - Geom::PathVector fill_pathv; - Geom::Affine fill_transform; - bool stroke_and_fill; - bool fill_only; - bool simple_shape; - - unsigned int print_pathv (Geom::PathVector const &pathv, const Geom::Affine &transform); - bool print_simple_shape (Geom::PathVector const &pathv, const Geom::Affine &transform); - unsigned int image(Inkscape::Extension::Print * /* module */, /** not used */ - unsigned char *px, /** array of pixel values, Gdk::Pixbuf bitmap format */ - unsigned int w, /** width of bitmap */ - unsigned int h, /** height of bitmap */ - unsigned int rs, /** row stride (normally w*4) */ - Geom::Affine const &tf_ignore, /** WRONG affine transform, use the one from m_tr_stack */ - SPStyle const *style); /** provides indirect link to image object */ - -public: - PrintEmfWin32 (void); - virtual ~PrintEmfWin32 (void); - - /* Print functions */ - virtual unsigned int setup (Inkscape::Extension::Print * module); - - virtual unsigned int begin (Inkscape::Extension::Print * module, SPDocument *doc); - virtual unsigned int finish (Inkscape::Extension::Print * module); - - /* Rendering methods */ - virtual unsigned int bind(Inkscape::Extension::Print *module, Geom::Affine const &transform, float opacity); - virtual unsigned int release(Inkscape::Extension::Print *module); - virtual unsigned int fill (Inkscape::Extension::Print *module, - Geom::PathVector const &pathv, - Geom::Affine const &ctm, SPStyle const *style, - Geom::OptRect const &pbox, Geom::OptRect const &dbox, - Geom::OptRect const &bbox); - virtual unsigned int stroke (Inkscape::Extension::Print * module, - Geom::PathVector const &pathv, - Geom::Affine const &ctm, SPStyle const *style, - Geom::OptRect const &pbox, Geom::OptRect const &dbox, - Geom::OptRect const &bbox); - virtual unsigned int comment(Inkscape::Extension::Print *module, const char * comment); - virtual unsigned int text(Inkscape::Extension::Print *module, char const *text, - Geom::Point const &p, SPStyle const *style); - bool textToPath (Inkscape::Extension::Print * ext); - - static void init (void); -protected: - int create_brush(SPStyle const *style); - - void destroy_brush(); - - void create_pen(SPStyle const *style, const Geom::Affine &transform); - - void destroy_pen(); - - void flush_fill(); - -}; - -} /* namespace Internal */ -} /* namespace Extension */ -} /* namespace Inkscape */ - -#endif /* WIN32 */ - -#endif /* __INKSCAPE_EXTENSION_INTERNAL_PRINT_EMF_WIN32_H__ */ - -/* - 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 : diff -rNu3 src/extension/internal/Makefile_insert src/extension/internal/Makefile_insert --- src/extension/internal/Makefile_insert 2012-09-13 10:24:25.000000000 -0700 +++ src/extension/internal/Makefile_insert 2012-09-13 13:15:09.000000000 -0700 @@ -133,10 +133,14 @@ extension/internal/filter/filter.h \ extension/internal/filter/drop-shadow.h \ extension/internal/filter/snow.h \ - extension/internal/emf-win32-print.h \ - extension/internal/emf-win32-print.cpp \ - extension/internal/emf-win32-inout.h \ - extension/internal/emf-win32-inout.cpp \ + extension/internal/uemf.c \ + extension/internal/uemf.h \ + extension/internal/uemf_endian.c \ + extension/internal/uemf_endian.h \ + extension/internal/emf-print.h \ + extension/internal/emf-print.cpp \ + extension/internal/emf-inout.h \ + extension/internal/emf-inout.cpp \ extension/internal/image-resolution.h \ extension/internal/image-resolution.cpp diff -rNu3 src/extension/internal/uemf.c src/extension/internal/uemf.c --- src/extension/internal/uemf.c 1969-12-31 16:00:00.000000000 -0800 +++ src/extension/internal/uemf.c 2012-09-14 14:31:37.000000000 -0700 @@ -0,0 +1,5612 @@ +/** + @file uemf.c Functions for manipulating EMF files and structures. + + [U_EMR*]_set all take data and return a pointer to memory holding the constructed record. + The size of that record is also returned in recsize. + It is also in the second int32 in the record, but may have been byte swapped and so not usable. + If something goes wrong a NULL pointer is returned and recsize is set to 0. + + Compile with "U_VALGRIND" defined defined to enable code which lets valgrind check each record for + uninitialized data. + + Compile with "SOL8" defined for Solaris 8 or 9 (Sparc). +*/ + +/* +File: uemf.c +Version: 0.0.8 +Date: 14-SEP-2012 +Author: David Mathog, Biology Division, Caltech +email: mathog@caltech.edu +Copyright: 2012 David Mathog and California Institute of Technology (Caltech) +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include +#include +#include // for INT_MAX, INT_MIN +#include // for U_ROUND() +#if 0 +#include //Not actually used, looking for collisions +#include //Not actually used, looking for collisions +#include //Not actually used, looking for collisions +#endif +#include "uemf.h" +/* one prototype from uemf_endian. Put it here because end user should never need to see it, sno +not in uemf.h or uemf_endian.h */ +void U_swap2(void *ul, unsigned int count); + +/* ********************************************************************************************** +These definitions are for code pieces that are used many times in the following implementation. These +definitions are not needed in end user code, so they are here rather than in uemf.h. +*********************************************************************************************** */ + +//! @cond + +// this one may also be used A=Msk,B=MskBmi and F=cbMsk +#define SET_CB_FROM_PXBMI(A,B,C,D,E,F) /* A=Px, B=Bmi, C=cbImage, D=cbImage4, E=cbBmi, F=cbPx */ \ + if(A){\ + if(!B)return(NULL); /* size is derived from U_BIMAPINFO, but NOT from its size field, go figure*/ \ + C = F;\ + D = UP4(C); /* pixel array might not be a multiples of 4 bytes*/ \ + E = sizeof(U_BITMAPINFOHEADER) + 4 * B->bmiHeader.biClrUsed; /* bmiheader + colortable*/ \ + }\ + else { D = 0; E=0; } + +// variable "off" must be declared in the function + +#define APPEND_PXBMISRC(A,B,C,D,E,F,G) /* A=record, B=U_EMR,C=cbBmi, D=Bmi, E=Px, F=cbImage, G=cbImage4 */ \ + if(C){\ + memcpy(A + off, D, C);\ + ((B *) A)->offBmiSrc = off;\ + ((B *) A)->cbBmiSrc = C;\ + off += C;\ + memcpy(A + off, E, F);\ + ((B *) A)->offBitsSrc = off;\ + ((B *) A)->cbBitsSrc = F;\ + if(G - F){ memset(A + off, 0, G - F); }\ + }\ + else {\ + ((B *) A)->offBmiSrc = 0;\ + ((B *) A)->cbBmiSrc = 0;\ + ((B *) A)->offBitsSrc = 0;\ + ((B *) A)->cbBitsSrc = 0;\ + } + +// variable "off" must be declared in the function + +#define APPEND_MSKBMISRC(A,B,C,D,E,F,G) /* A=record, B=U_EMR*,C=cbMskBmi, D=MskBmi, E=Msk, F=cbMskImage, G=cbMskImage4 */ \ + if(C){\ + memcpy(A + off, D, C);\ + ((B *) A)->offBmiMask = off;\ + ((B *) A)->cbBmiMask = C;\ + off += C;\ + memcpy(A + off, Msk, F);\ + ((B *) A)->offBitsMask = off;\ + ((B *) A)->cbBitsMask = F;\ + if(G - F){ memset(A + off, 0, G - F); }\ + }\ + else {\ + ((B *) A)->offBmiMask = 0;\ + ((B *) A)->cbBmiMask = 0;\ + ((B *) A)->offBitsMask = 0;\ + ((B *) A)->cbBitsMask = 0;\ + } + +/* iconv() has a funny cast on some older systems, on most recent ones + it is just char **. This tries to work around the issue. If you build this + on another funky system this code may need to be modified, or define ICONV_CAST + on the compile line(but it may be tricky). +*/ +#ifdef SOL8 +#define ICONV_CAST (const char **) +#endif //SOL8 +#if !defined(ICONV_CAST) +#define ICONV_CAST (char **) +#endif //ICONV_CAST +//! @endcond + +/* ********************************************************************************************** +These functions are used for development and debugging and should be be includied in production code. +*********************************************************************************************** */ + +/** + \brief Debugging utility, used with valgrind to find uninitialized values. Not for use in production code. + \param buf memory area to examine ! + \param size length in bytes of buf! +*/ +int memprobe( + void *buf, + size_t size + ){ + int sum=0; + char *ptr=(char *)buf; + for(;size;size--,ptr++){ sum += *ptr; } // read all bytes, trigger valgrind warning if any uninitialized + return(sum); +} + +/** + \brief Dump a UTF8 string. Not for use in production code. + \param src string to examine +*/ +void wchar8show( + const char *src + ){ + printf("char show\n"); + size_t srclen = 0; + while(*src){ printf("%d %d %x\n",(int) srclen,*src,*src); srclen++; src++; } +} + +/** + \brief Dump a UTF16 string. Not for use in production code. + \param src string to examine +*/ +void wchar16show( + const uint16_t *src + ){ + printf("uint16_t show\n"); + size_t srclen = 0; + while(*src){ printf("%d %d %x\n",(int) srclen,*src,*src); srclen++; src++; } +} + +/** + \brief Dump a UTF32 string. Not for use in production code. +*/ +void wchar32show( + const uint32_t *src + ){ + printf("uint32_t show\n"); + size_t srclen = 0; + while(*src){ printf("%d %d %x\n",(int) srclen,*src,*src); srclen++; src++; } +} + +/** + \brief Dump a wchar_t string. Not for use in production code. + \param src string to examine +*/ +void wchartshow( + const wchar_t *src + ){ + uint32_t val; + printf("wchar_t show\n"); + size_t srclen = 0; + while(*src){ + val = *src; // because *src is wchar_t is not strictly an integer type, can cause warnings on next line + printf("%d %d %x\n",(int) srclen,val,val); + srclen++; + src++; + } +} + +/** + \brief Dump the eht structure. Not for use in production code. + \param string Text to output before dumping eht structure + \param handle Handle + \param eht eht structure to dump +*/ +void dumpeht( + char *string, + unsigned int *handle, + EMFHANDLES *eht + ){ + uint32_t i; + printf("%s\n",string); + printf("sptr: %d peak: %d top: %d\n",eht->sptr,eht->peak,eht->top); + if(handle){ + printf("handle: %d \n",*handle); + } + for(i=0;i<=5;i++){ + printf("table[%d]: %d\n",i,eht->table[i]); + } + for(i=1;i<=5;i++){ + printf("stack[%d]: %d\n",i,eht->stack[i]); + } +} + +/* ********************************************************************************************** +These functions are used for character type conversions, Image conversions, and other +utility operations +*********************************************************************************************** */ + +/** + \brief Find the number of (storage) characters in a 16 bit character string, not including terminator. + \param src string to examine +*/ +size_t wchar16len( + const uint16_t *src + ){ + size_t srclen = 0; + while(*src){ srclen++; src++; } + return(srclen); +} + +/** + \brief Find the number of (storage) characters in a 32 bit character string, not including terminator. + \param src string to examine +*/ +size_t wchar32len( + const uint32_t *src + ){ + size_t srclen = 0; + while(*src){ srclen++; src++; } + return(srclen); +} + +/** + \brief Strncpy for wchar16 (UTF16). + \param dst destination (already allocated) + \param src source + \param nchars number of characters to copy +*/ +void wchar16strncpy( + uint16_t *dst, + const uint16_t *src, + size_t nchars + ){ + for(;nchars;nchars--,dst++,src++){ + *dst = *src; + if(!*src)break; + } +} + +/** + \brief Fill the output string with N characters, if the input string is shorter than N, pad with nulls. + \param dst destination (already allocated) + \param src source + \param nchars number of characters to copy + +*/ +void wchar16strncpypad( + uint16_t *dst, + const uint16_t *src, + size_t nchars + ){ + for(;*src && nchars;nchars--,dst++,src++){ *dst = *src; } + for(;nchars;nchars--,dst++){ *dst = 0; } // Pad the remainder +} + +/* For the following converstion functions, remember that iconv() modifies ALL of its parameters, + so save a pointer to the destination buffer!!!! + It isn't clear that terminators are being + copied properly, so be sure allocated space is a bit larger and cleared. +*/ + +/** + \brief Convert a UTF32LE string to a UTF16LE string. + \returns pointer to new string or NULL if it fails + \param src wchar_t string to convert + \param max number of characters to convert, if 0, until terminator + \param len number of characters in new string, NOT including terminator +*/ +uint16_t *U_Utf32leToUtf16le( + const uint32_t *src, + size_t max, + size_t *len + ){ + char *dst,*dst2; + size_t srclen,dstlen,status; + + if(max){ srclen = 4*max; } + else { srclen = 4 + 4*wchar32len(src); } //include terminator, length in BYTES + + dstlen = 2 + srclen; // this will always work, but may waste space + dst2 = dst = calloc(dstlen,1); // so there will be at least one terminator + if(!dst)return(NULL); + iconv_t conv = iconv_open("UTF-16LE", "UTF-32LE"); + status = iconv(conv, ICONV_CAST &src, &srclen, &dst, &dstlen); + iconv_close(conv); + if(status == (size_t) -1)return(NULL); + if(len)*len=wchar16len((uint16_t *)dst2); + return((uint16_t *)dst2); +} + +/** + \brief Convert a UTF16LE string to a UTF32LE string. + \return pointer to new string or NULL if it fails + \param src UTF16LE string to convert + \param max number of characters to convert, if 0, until terminator + \param len number of characters in new string, NOT including terminator +*/ +uint32_t *U_Utf16leToUtf32le( + const uint16_t *src, + size_t max, + size_t *len + ){ + char *dst,*dst2; + char *src2 = (char *) src; + size_t srclen,dstlen,status; + if(max){ srclen = 2*max; } + else { srclen = 2*wchar16len(src)+2; } // include terminator, length in BYTES + dstlen = 2*(2 + srclen); // This should always work + dst2 = dst = calloc(dstlen,1); + if(!dst)return(NULL); + iconv_t conv = iconv_open("UTF-32LE", "UTF-16LE"); + if ( conv == (iconv_t)-1)return(NULL); + status = iconv(conv, ICONV_CAST &src2, &srclen, &dst, &dstlen); + iconv_close(conv); + if(status == (size_t) -1)return(NULL); + if(len)*len=wchar32len((uint32_t *)dst2); + return((uint32_t *) dst2); +} + +/** + \brief Convert a Latin1 string to a UTF32LE string. + \return pointer to new string or NULL if it fails + \param src Latin1 string to convert + \param max number of characters to convert, if 0, until terminator + \param len number of characters in new string, NOT including terminator + + + U_EMR_EXTTEXTOUTA records are "8 bit ASCII". In theory that is ASCII in an 8 + bit character, but numerous applications store Latin1 in them, and some + _may_ store UTF-8 in them. Since very vew Latin1 strings are valid UTF-8 strings, + call U_Utf8ToUtf32le first, and if it fails, then call this function. +*/ +uint32_t *U_Latin1ToUtf32le( + const char *src, + size_t max, + size_t *len + ){ + char *dst,*dst2; + char *src2 = (char *) src; + size_t srclen,dstlen,status; + if(max){ srclen = max; } + else { srclen = strlen(src)+1; } // include terminator, length in BYTES + dstlen = sizeof(uint32_t)*(1 + srclen); // This should always work but might waste some space + dst2 = dst = calloc(dstlen,1); + if(!dst)return(NULL); + iconv_t conv = iconv_open("UTF-32LE", "LATIN1"); + if ( conv == (iconv_t) -1)return(NULL); + status = iconv(conv, ICONV_CAST &src2, &srclen, &dst, &dstlen); + iconv_close(conv); + if(status == (size_t) -1)return(NULL); + if(len)*len=wchar32len((uint32_t *)dst2); + return((uint32_t *) dst2); +} + +/** + \brief Convert a UTF8 string to a UTF32LE string. + \return pointer to new string or NULL if it fails + \param src UTF8 string to convert + \param max number of characters to convert, if 0, until terminator + \param len number of characters in new string, NOT including terminator +*/ +uint32_t *U_Utf8ToUtf32le( + const char *src, + size_t max, + size_t *len + ){ + char *dst,*dst2; + char *src2 = (char *) src; + size_t srclen,dstlen,status; + if(max){ srclen = max; } + else { srclen = strlen(src)+1; } // include terminator, length in BYTES + dstlen = sizeof(uint32_t)*(1 + srclen); // This should always work but might waste some space + dst2 = dst = calloc(dstlen,1); + if(!dst)return(NULL); + iconv_t conv = iconv_open("UTF-32LE", "UTF-8"); + if ( conv == (iconv_t) -1)return(NULL); + status = iconv(conv, ICONV_CAST &src2, &srclen, &dst, &dstlen); + iconv_close(conv); + if(status == (size_t) -1)return(NULL); + if(len)*len=wchar32len((uint32_t *)dst2); + return((uint32_t *) dst2); +} + +/** + \brief Convert a UTF32LE string to a UTF8 string. + \return pointer to new string or NULL if it fails + \param src wchar_t string to convert + \param max number of characters to convert, if 0, until terminator + \param len number of characters in new string, NOT including terminator +*/ +char *U_Utf32leToUtf8( + const uint32_t *src, + size_t max, + size_t *len + ){ + char *dst,*dst2; + char *src2 = (char *) src; + size_t srclen,dstlen,status; + if(max){ srclen = 4*max; } + else { srclen = 4*(1 + wchar32len(src)); } //include terminator, length in BYTES + dstlen = 1 + srclen; // This should always work but might waste some space + dst2 = dst = calloc(dstlen,1); + if(!dst)return(NULL); + iconv_t conv = iconv_open("UTF-8", "UTF-32LE"); + if ( conv == (iconv_t)-1)return(NULL); + status = iconv(conv, ICONV_CAST &src2, &srclen, &dst, &dstlen); + iconv_close(conv); + if(status == (size_t) -1)return(NULL); + if(len)*len=strlen(dst2); + return(dst2); +} + +/** + \brief Convert a UTF-8 string to a UTF16-LE string. + \return pointer to new string or NULL if it fails + \param src UTF8 string to convert + \param max number of characters to convert, if 0, until terminator + \param len number of characters in new string, NOT including terminator +*/ +uint16_t *U_Utf8ToUtf16le( + const char *src, + size_t max, + size_t *len + ){ + char *dst,*dst2; + size_t srclen,dstlen,status; + iconv_t conv; + + if(max){ srclen = max; } + else { srclen = strlen(src)+1; } // include terminator, length in BYTES + dstlen = 2 * (1 + srclen); // this will always work, but may waste space + dst2 = dst =calloc(dstlen,1); // so there will always be a terminator + if(!dst)return(NULL); + conv = iconv_open("UTF-16LE", "UTF-8"); + if (conv == (iconv_t) -1)return(NULL); + status = iconv(conv, ICONV_CAST &src, &srclen, &dst, &dstlen); + iconv_close(conv); + if(status == (size_t) -1)return(NULL); + if(len)*len=wchar16len((uint16_t *)dst2); + return((uint16_t *)dst2); +} + +/** + \brief Convert a UTF16LE string to a UTF8 string. + \return pointer to new UTF8 string or NULL if it fails + \param src UTF16LE string to convert + \param max number of characters to convert, if 0, until terminator + \param len number of characters in new string, NOT including terminator +*/ +char *U_Utf16leToUtf8( + const uint16_t *src, + size_t max, + size_t *len + ){ + char *dst, *dst2, *ret; + size_t srclen,dstlen,status; + if(max){ srclen = 2*max; } + else { srclen = 2*(1 +wchar16len(src)); } //include terminator, length in BYTES + dstlen = 1 + srclen; // this will always work, but may waste space + dst2 = dst = (char *) calloc(dstlen,1); + if(!dst)return(NULL); + iconv_t conv = iconv_open("UTF-8", "UTF-16LE"); + status = iconv(conv, ICONV_CAST &src, &srclen, &dst, &dstlen); + iconv_close(conv); + if(status == (size_t) -1)return(NULL); + if(len)*len=strlen(dst2); + ret=U_strdup(dst2); // make a string of exactly the right size + free(dst2); // free the one which was probably too big + return(ret); +} + +/** + \brief Put a single 16 bit character into UTF-16LE form. + + Used in conjunction with U_Utf16leEdit(), because the character + representation would otherwise be dependent on machine Endianness. + + \return UTF16LE representation of the character. + \param src 16 bit character + +*/ +uint16_t U_Utf16le(const uint16_t src){ + uint16_t dst=src; +#if U_BYTE_SWAP + U_swap2(&dst,1); +#endif + return(dst); +} + +/** + \brief Single character replacement in a UTF-16LE string. + + Used solely for the Description field which contains + embedded nulls, which makes it difficult to manipulate. Use some other character and then swap it. + + \return number of substitutions, or -1 if src is not defined + \param src UTF16LE string to edit + \param find character to replace + \param replace replacestitute character + +*/ +int U_Utf16leEdit( + uint16_t *src, + uint16_t find, + uint16_t replace + ){ + int count=0; + if(!src)return(-1); + while(*src){ + if(*src == find){ *src = replace; count++; } + src++; + } + return(count); +} + +/** + \brief strdup for when strict C99 compliance is enforced + \returns duplicate string or NULL on error + \param s string to duplicate +*/ +char *U_strdup(const char *s){ + char *news=NULL; + size_t slen; + if(s){ + slen = strlen(s) + 1; //include the terminator! + news = malloc(slen); + if(news){ + memcpy(news,s,slen); + } + } + return(news); + +} + +/** + \brief Make up an approximate dx array to pass to emrtext_set(), based on character height and weight. + + Take abs. value of character height, get width by multiplying by 0.6, and correct weight + approximately, with formula (measured on screen for one text line of Arial). + Caller is responsible for free() on the returned pointer. + + \return pointer to dx array + \param height character height (absolute value will be used) + \param weight LF_Weight Enumeration (character weight) + \param members Number of entries to put into dx + +*/ +uint32_t *dx_set( + int32_t height, + uint32_t weight, + uint32_t members + ){ + uint32_t i, width, *dx; + dx = (uint32_t *) malloc(members * sizeof(uint32_t)); + if(dx){ + if(U_FW_DONTCARE == weight)weight=U_FW_NORMAL; + width = (uint32_t) U_ROUND(((float) (height > 0 ? height : -height)) * 0.6 * (0.00024*(float) weight + 0.904)); + for ( i = 0; i < members; i++ ){ dx[i] = width; } + } + return(dx); +} + +/** + \brief Look up the properties (a bit map) of a type of EMR record. + + \return bitmap of EMR record properties, or 0xFFFFFFFF on error + \param type EMR record type + +*/ +uint32_t emr_properties(uint32_t type){ + static uint32_t *table=NULL; + if(!table){ + table = (uint32_t *) malloc(sizeof(uint32_t)*(1 + U_EMR_MAX)); + if(!table)return(0xFFFFFFFF); +// 0x40 0x20 0x10 0x08 0x04 0x02 0x01 +// Path properties (U_DRAW_*) ALTERS ONLYTO VISIBLE +// PATH FORCE CLOSED NOTEMPTY + table[ 0] = 0x00; //!< Does not map to any EMR record + table[ 1] = 0x00; //!< U_EMRHEADER 0 0 0 0 0 0 0 + table[ 2] = 0x03; //!< U_EMRPOLYBEZIER 0 0 0 0 0 1 1 + table[ 3] = 0x07; //!< U_EMRPOLYGON 0 0 0 0 1 1 1 + table[ 4] = 0x03; //!< U_EMRPOLYLINE 0 0 0 0 0 1 1 + table[ 5] = 0x0B; //!< U_EMRPOLYBEZIERTO 0 0 0 1 0 1 1 + table[ 6] = 0x0B; //!< U_EMRPOLYLINETO 0 0 0 1 0 1 1 + table[ 7] = 0x03; //!< U_EMRPOLYPOLYLINE 0 0 0 0 0 1 1 + table[ 8] = 0x07; //!< U_EMRPOLYPOLYGON 0 0 0 0 1 1 1 + table[ 9] = 0x20; //!< U_EMRSETWINDOWEXTEX 0 1 0 0 0 0 0 + table[ 10] = 0x20; //!< U_EMRSETWINDOWORGEX 0 1 0 0 0 0 0 + table[ 11] = 0x20; //!< U_EMRSETVIEWPORTEXTEX 0 1 0 0 0 0 0 + table[ 12] = 0x20; //!< U_EMRSETVIEWPORTORGEX 0 1 0 0 0 0 0 + table[ 13] = 0x20; //!< U_EMRSETBRUSHORGEX 0 1 0 0 0 0 0 + table[ 14] = 0x02; //!< U_EMREOF 0 1 0 0 0 0 0 Force out any pending draw + table[ 15] = 0x02; //!< U_EMRSETPIXELV 0 0 0 0 0 1 0 + table[ 16] = 0x20; //!< U_EMRSETMAPPERFLAGS 0 1 0 0 0 0 0 + table[ 17] = 0x20; //!< U_EMRSETMAPMODE 0 1 0 0 0 0 0 + table[ 18] = 0x20; //!< U_EMRSETBKMODE 0 1 0 0 0 0 0 + table[ 19] = 0x20; //!< U_EMRSETPOLYFILLMODE 0 1 0 0 0 0 0 + table[ 20] = 0x20; //!< U_EMRSETROP2 0 1 0 0 0 0 0 + table[ 21] = 0x20; //!< U_EMRSETSTRETCHBLTMODE 0 1 0 0 0 0 0 + table[ 22] = 0x20; //!< U_EMRSETTEXTALIGN 0 1 0 0 0 0 0 + table[ 23] = 0x20; //!< U_EMRSETCOLORADJUSTMENT 0 1 0 0 0 0 0 + table[ 24] = 0x20; //!< U_EMRSETTEXTCOLOR 0 1 0 0 0 0 0 + table[ 25] = 0x20; //!< U_EMRSETBKCOLOR 0 1 0 0 0 0 0 + table[ 26] = 0x20; //!< U_EMROFFSETCLIPRGN 0 1 0 0 0 0 0 + table[ 27] = 0x09; //!< U_EMRMOVETOEX 0 0 0 1 0 0 1 + table[ 28] = 0x20; //!< U_EMRSETMETARGN 0 1 0 0 0 0 0 + table[ 29] = 0x20; //!< U_EMREXCLUDECLIPRECT 0 1 0 0 0 0 0 + table[ 30] = 0x20; //!< U_EMRINTERSECTCLIPRECT 0 1 0 0 0 0 0 + table[ 31] = 0x20; //!< U_EMRSCALEVIEWPORTEXTEX 0 1 0 0 0 0 0 + table[ 32] = 0x20; //!< U_EMRSCALEWINDOWEXTEX 0 1 0 0 0 0 0 + table[ 33] = 0x20; //!< U_EMRSAVEDC 0 1 0 0 0 0 0 + table[ 34] = 0x20; //!< U_EMRRESTOREDC 0 1 0 0 0 0 0 + table[ 35] = 0x20; //!< U_EMRSETWORLDTRANSFORM 0 1 0 0 0 0 0 + table[ 36] = 0x20; //!< U_EMRMODIFYWORLDTRANSFORM 0 1 0 0 0 0 0 + table[ 37] = 0x20; //!< U_EMRSELECTOBJECT 0 1 0 0 0 0 0 + table[ 38] = 0x20; //!< U_EMRCREATEPEN 0 1 0 0 0 0 0 + table[ 39] = 0x20; //!< U_EMRCREATEBRUSHINDIRECT 0 1 0 0 0 0 0 + table[ 40] = 0x20; //!< U_EMRDELETEOBJECT 0 1 0 0 0 0 0 + table[ 41] = 0x03; //!< U_EMRANGLEARC 0 0 0 0 0 1 1 + table[ 42] = 0x07; //!< U_EMRELLIPSE 0 0 0 0 1 1 1 + table[ 43] = 0x07; //!< U_EMRRECTANGLE 0 0 0 0 1 1 1 + table[ 44] = 0x07; //!< U_EMRROUNDRECT 0 0 0 0 1 1 1 + table[ 45] = 0x03; //!< U_EMRARC 0 0 0 0 0 1 1 + table[ 46] = 0x07; //!< U_EMRCHORD 0 0 0 0 1 1 1 + table[ 47] = 0x07; //!< U_EMRPIE 0 0 0 0 1 1 1 + table[ 48] = 0x20; //!< U_EMRSELECTPALETTE 0 1 0 0 0 0 0 + table[ 49] = 0x20; //!< U_EMRCREATEPALETTE 0 1 0 0 0 0 0 + table[ 50] = 0x20; //!< U_EMRSETPALETTEENTRIES 0 1 0 0 0 0 0 + table[ 51] = 0x20; //!< U_EMRRESIZEPALETTE 0 1 0 0 0 0 0 + table[ 52] = 0x20; //!< U_EMRREALIZEPALETTE 0 1 0 0 0 0 0 + table[ 53] = 0x02; //!< U_EMREXTFLOODFILL 0 0 0 0 0 1 0 + table[ 54] = 0x0B; //!< U_EMRLINETO 0 0 0 1 0 1 1 + table[ 55] = 0x0B; //!< U_EMRARCTO 0 0 0 1 0 1 1 + table[ 56] = 0x03; //!< U_EMRPOLYDRAW 0 0 0 0 0 1 1 + table[ 57] = 0x20; //!< U_EMRSETARCDIRECTION 0 1 0 0 0 0 0 + table[ 58] = 0x20; //!< U_EMRSETMITERLIMIT 0 1 0 0 0 0 0 + table[ 59] = 0x60; //!< U_EMRBEGINPATH 1 1 0 0 0 0 0 + table[ 60] = 0x00; //!< U_EMRENDPATH 0 0 0 0 0 0 0 + table[ 61] = 0x04; //!< U_EMRCLOSEFIGURE 0 0 0 0 1 0 0 + table[ 62] = 0x14; //!< U_EMRFILLPATH 0 0 1 0 1 0 0 + table[ 63] = 0x14; //!< U_EMRSTROKEANDFILLPATH 0 0 1 0 1 0 0 + table[ 64] = 0x10; //!< U_EMRSTROKEPATH 0 0 1 0 0 0 0 + table[ 65] = 0x20; //!< U_EMRFLATTENPATH 0 1 0 0 0 0 0 + table[ 66] = 0x20; //!< U_EMRWIDENPATH 0 1 0 0 0 0 0 + table[ 67] = 0x20; //!< U_EMRSELECTCLIPPATH 0 1 0 0 0 0 0 + table[ 68] = 0x20; //!< U_EMRABORTPATH 0 1 0 0 0 0 0 + table[ 69] = 0x20; //!< U_EMRUNDEF69 0 1 0 0 0 0 0 + table[ 70] = 0x00; //!< U_EMRCOMMENT 0 0 0 0 0 0 0 + table[ 71] = 0x02; //!< U_EMRFILLRGN 0 0 0 0 0 1 0 + table[ 72] = 0x02; //!< U_EMRFRAMERGN 0 0 0 0 0 1 0 + table[ 73] = 0x02; //!< U_EMRINVERTRGN 0 0 0 0 0 1 0 + table[ 74] = 0x02; //!< U_EMRPAINTRGN 0 0 0 0 0 1 0 + table[ 75] = 0x20; //!< U_EMREXTSELECTCLIPRGN 0 1 0 0 0 0 0 + table[ 76] = 0x02; //!< U_EMRBITBLT 0 0 0 0 0 1 0 + table[ 77] = 0x02; //!< U_EMRSTRETCHBLT 0 0 0 0 0 1 0 + table[ 78] = 0x02; //!< U_EMRMASKBLT 0 0 0 0 0 1 0 + table[ 79] = 0x02; //!< U_EMRPLGBLT 0 0 0 0 0 1 0 + table[ 80] = 0x20; //!< U_EMRSETDIBITSTODEVICE 0 1 0 0 0 0 0 + table[ 81] = 0x20; //!< U_EMRSTRETCHDIBITS 0 1 0 0 0 0 0 + table[ 82] = 0x20; //!< U_EMREXTCREATEFONTINDIRECTW 0 1 0 0 0 0 0 + table[ 83] = 0x02; //!< U_EMREXTTEXTOUTA 0 0 0 0 0 1 0 + table[ 84] = 0x02; //!< U_EMREXTTEXTOUTW 0 0 0 0 0 1 0 + table[ 85] = 0x03; //!< U_EMRPOLYBEZIER16 0 0 0 0 0 1 1 + table[ 86] = 0x03; //!< U_EMRPOLYGON16 0 0 0 0 0 1 1 + table[ 87] = 0x03; //!< U_EMRPOLYLINE16 0 0 0 0 0 1 1 + table[ 88] = 0x0B; //!< U_EMRPOLYBEZIERTO16 0 0 0 1 0 1 1 + table[ 89] = 0x0B; //!< U_EMRPOLYLINETO16 0 0 0 1 0 1 1 + table[ 90] = 0x03; //!< U_EMRPOLYPOLYLINE16 0 0 0 0 0 1 1 + table[ 91] = 0x07; //!< U_EMRPOLYPOLYGON16 0 0 0 0 1 1 1 + table[ 92] = 0x03; //!< U_EMRPOLYDRAW16 0 0 0 0 0 1 1 + table[ 93] = 0x00; //!< U_EMRCREATEMONOBRUSH 0 0 0 0 0 0 0 Not selected yet, so no change in drawing conditions + table[ 94] = 0x00; //!< U_EMRCREATEDIBPATTERNBRUSHPT 0 0 0 0 0 0 0 " + table[ 95] = 0x00; //!< U_EMREXTCREATEPEN 0 0 0 0 0 0 0 " + table[ 96] = 0x02; //!< U_EMRPOLYTEXTOUTA 0 0 0 0 0 1 0 + table[ 97] = 0x02; //!< U_EMRPOLYTEXTOUTW 0 0 0 0 0 1 0 + table[ 98] = 0x20; //!< U_EMRSETICMMODE 0 1 0 0 0 0 0 + table[ 99] = 0x20; //!< U_EMRCREATECOLORSPACE 0 1 0 0 0 0 0 + table[100] = 0x20; //!< U_EMRSETCOLORSPACE 0 1 0 0 0 0 0 + table[101] = 0x20; //!< U_EMRDELETECOLORSPACE 0 1 0 0 0 0 0 + table[102] = 0x20; //!< U_EMRGLSRECORD 0 1 0 0 0 0 0 + table[103] = 0x20; //!< U_EMRGLSBOUNDEDRECORD 0 1 0 0 0 0 0 + table[104] = 0x20; //!< U_EMRPIXELFORMAT 0 1 0 0 0 0 0 + table[105] = 0x20; //!< U_EMRDRAWESCAPE 0 1 0 0 0 0 0 + table[106] = 0x20; //!< U_EMREXTESCAPE 0 1 0 0 0 0 0 + table[107] = 0x20; //!< U_EMRUNDEF107 0 1 0 0 0 0 0 + table[108] = 0x02; //!< U_EMRSMALLTEXTOUT 0 0 0 0 0 1 0 + table[109] = 0x20; //!< U_EMRFORCEUFIMAPPING 0 1 0 0 0 0 0 + table[110] = 0x20; //!< U_EMRNAMEDESCAPE 0 1 0 0 0 0 0 + table[111] = 0x20; //!< U_EMRCOLORCORRECTPALETTE 0 1 0 0 0 0 0 + table[112] = 0x20; //!< U_EMRSETICMPROFILEA 0 1 0 0 0 0 0 + table[113] = 0x20; //!< U_EMRSETICMPROFILEW 0 1 0 0 0 0 0 + table[114] = 0x02; //!< U_EMRALPHABLEND 0 0 0 0 0 1 0 + table[115] = 0x20; //!< U_EMRSETLAYOUT 0 1 0 0 0 0 0 + table[116] = 0x02; //!< U_EMRTRANSPARENTBLT 0 0 0 0 0 1 0 + table[117] = 0x20; //!< U_EMRUNDEF117 0 1 0 0 0 0 0 + table[118] = 0x02; //!< U_EMRGRADIENTFILL 0 0 0 0 0 1 0 + table[119] = 0x20; //!< U_EMRSETLINKEDUFIS 0 1 0 0 0 0 0 + table[120] = 0x20; //!< U_EMRSETTEXTJUSTIFICATION 0 1 0 0 0 0 0 + table[121] = 0x20; //!< U_EMRCOLORMATCHTOTARGETW 0 1 0 0 0 0 0 + table[122] = 0x20; //!< U_EMRCREATECOLORSPACEW 0 1 0 0 0 0 0 + } + if(type<1 || type >U_EMR_MAX)return(0xFFFFFFFF); + return(table[type]); +} + +/** + \brief Derive from an EMF arc, chord, or pie the center, start, and end points, and the bounding rectangle. + + \return 0 on success, other values on errors. + \param record U_EMRPIE, U_EMRCHORD, or _EMRARC record + \param f1 1 if rotation angle >= 180, else 0 + \param f2 Rotation direction, 1 if counter clockwise, else 0 + \param center Center coordinates + \param start Start coordinates (point on the ellipse defined by rect) + \param end End coordinates (point on the ellipse defined by rect) + \param size W,H of the x,y axes of the bounding rectangle. +*/ +int emr_arc_points( + PU_ENHMETARECORD record, + int *f1, + int f2, + PU_PAIRF center, + PU_PAIRF start, + PU_PAIRF end, + PU_PAIRF size + ){ + U_PAIRF estart; // EMF start position, defines a radial + U_PAIRF eend; // EMF end position, defines a radial + U_PAIRF vec_estart; // define a unit vector from the center to estart + U_PAIRF vec_eend; // define a unit vector from the center to eend + U_PAIRF radii; // x,y radii of ellipse + U_PAIRF ratio; // intermediate value + float scale, cross; + PU_EMRARC pEmr = (PU_EMRARC) (record); + center->x = ((float)(pEmr->rclBox.left + pEmr->rclBox.right ))/2.0; + center->y = ((float)(pEmr->rclBox.top + pEmr->rclBox.bottom))/2.0; + size->x = (float)(pEmr->rclBox.right - pEmr->rclBox.left ); + size->y = (float)(pEmr->rclBox.bottom - pEmr->rclBox.top ); + estart.x = (float)(pEmr->ptlStart.x); + estart.y = (float)(pEmr->ptlStart.y); + eend.x = (float)(pEmr->ptlEnd.x); + eend.y = (float)(pEmr->ptlEnd.y); + radii.x = size->x/2.0; + radii.y = size->y/2.0; + + vec_estart.x = (estart.x - center->x); // initial vector, not unit length + vec_estart.y = (estart.y - center->y); + scale = sqrt(vec_estart.x*vec_estart.x + vec_estart.y*vec_estart.y); + if(!scale)return(1); // bogus record, has start at center + vec_estart.x /= scale; // now a unit vector + vec_estart.y /= scale; + + vec_eend.x = (eend.x - center->x); // initial vector, not unit length + vec_eend.y = (eend.y - center->y); + scale = sqrt(vec_eend.x*vec_eend.x + vec_eend.y*vec_eend.y); + if(!scale)return(2); // bogus record, has end at center + vec_eend.x /= scale; // now a unit vector + vec_eend.y /= scale; + + + // Find the intersection of the vectors with the ellipse. With no loss of generality + // we can translate the ellipse to the origin, then we just need to find tu (t a factor, u the unit vector) + // that also satisfies (x/Rx)^2 + (y/Ry)^2 = 1. x is t*(ux), y is t*(uy), where ux,uy are the x,y components + // of the unit vector. Substituting gives: + // (t*(ux)/Rx)^2 + (t*(uy)/Ry)^2 = 1 + // t^2 = 1/( (ux/Rx)^2 + (uy/Ry)^2 ) + // t = sqrt(1/( (ux/Rx)^2 + (uy/Ry)^2 )) + + ratio.x = vec_estart.x/radii.x; + ratio.y = vec_estart.y/radii.y; + ratio.x *= ratio.x; // we only use the square + ratio.y *= ratio.y; + scale = 1.0/sqrt(ratio.x + ratio.y); + start->x = center->x + scale * vec_estart.x; + start->y = center->y + scale * vec_estart.y; + + ratio.x = vec_eend.x/radii.x; + ratio.y = vec_eend.y/radii.y; + ratio.x *= ratio.x; // we only use the square + ratio.y *= ratio.y; + scale = 1.0/sqrt(ratio.x + ratio.y); + end->x = center->x + scale * vec_eend.x; + end->y = center->y + scale * vec_eend.y; + + //lastly figure out if the swept angle is >180 degrees or not, based on the direction of rotation + //and the two unit vectors. + + cross = start->x * end->y - start->y * end->x; + if(!f2){ // counter clockwise rotation + if(cross >=0){ *f1 = 0; } + else { *f1 = 1; } + } + else { + if(cross >=0){ *f1 = 1; } + else { *f1 = 0; } + } + + + return(0); +} + +/** + \brief Convert a U_RGBA 32 bit pixmap to one of many different types of DIB pixmaps. + + Conversions to formats using color tables assume that the color table can hold every color + in the input image. If that assumption is false then the conversion will fail. Conversion + from 8 bit color to N bit colors (N<8) do so by shifting the appropriate number of bits. + + \return 0 on success, other values on errors. + \param px DIB pixel array + \param cbPx DIB pixel array size in bytes + \param ct DIB color table + \param numCt DIB color table number of entries + \param rgba_px U_RGBA pixel array (32 bits) + \param w Width of pixel array + \param h Height of pixel array + \param stride Row stride of input pixel array in bytes + \param colortype DIB BitCount Enumeration + \param use_ct If true use color table (only for 1-16 bit DIBs) + \param invert If DIB rows are in opposite order from RGBA rows +*/ +int RGBA_to_DIB( + char **px, + uint32_t *cbPx, + PU_RGBQUAD *ct, + int *numCt, + char *rgba_px, + int w, + int h, + int stride, + uint32_t colortype, + int use_ct, + int invert + ){ + int bs; + int pad; + int i,j,k; + int istart, iend, iinc; + uint8_t r,g,b,a,tmp8; + char *pxptr; + char *rptr; + int found; + int usedbytes; + U_RGBQUAD color; + PU_RGBQUAD lct; + int32_t index; + + *px=NULL; + *ct=NULL; + *numCt=0; + *cbPx=0; + // sanity checking + if(!w || !h || !stride || !colortype || !rgba_px)return(1); + if(use_ct && colortype >= U_BCBM_COLOR16)return(2); //color tables not used above 16 bit pixels + if(!use_ct && colortype < U_BCBM_COLOR16)return(3); //color tables mandatory for < 16 bit + + bs = colortype/8; + if(bs<1){ + bs=1; + usedbytes = (w*colortype + 7)/8; // width of line in fully and partially occupied bytes + } + else { + usedbytes = w*bs; + } + pad = UP4(usedbytes) - usedbytes; // DIB rows must be aligned on 4 byte boundaries, they are padded at the end to accomplish this.; + *cbPx = h * (usedbytes + pad); // Rows must start on a 4 byte boundary! + *px = (char *) malloc(*cbPx); + if(!px)return(4); + if(use_ct){ + *numCt = 1<< colortype; + if(*numCt >w*h)*numCt=w*h; + lct = (PU_RGBQUAD) malloc(*numCt * sizeof(U_RGBQUAD)); + if(!lct)return(5); + *ct = lct; + } + + if(invert){ + istart = h-1; + iend = -1; + iinc = -1; + } + else { + istart = 0; + iend = h; + iinc = 1; + } + + found = 0; + tmp8 = 0; + pxptr = *px; + for(i=istart; i!=iend; i+=iinc){ + rptr= rgba_px + i*stride; + for(j=0; j *numCt){ // More colors found than are supported by the color table + free(*ct); + free(*px); + *numCt=0; + *cbPx=0; + return(6); + } + index = found - 1; + *lct = color; + } + switch(colortype){ + case U_BCBM_MONOCHROME: // 2 colors. bmiColors array has two entries + tmp8 = tmp8 >> 1; // This seems wrong, as it fills from the top of each byte. But it works. + tmp8 |= index << 7; + if(!((j+1) % 8)){ + *pxptr++ = tmp8; + tmp8 = 0; + } + break; + case U_BCBM_COLOR4: // 2^4 colors. bmiColors array has 16 entries + tmp8 = tmp8 << 4; + tmp8 |= index; + if(!((j+1) % 2)){ + *pxptr++ = tmp8; + tmp8 = 0; + } + break; + case U_BCBM_COLOR8: // 2^8 colors. bmiColors array has 256 entries + tmp8 = index; + *pxptr++ = tmp8; + break; + case U_BCBM_COLOR16: // 2^16 colors. (Several different color methods)) + case U_BCBM_COLOR24: // 2^24 colors. bmiColors is not used. Pixels are U_RGBTRIPLE. + case U_BCBM_COLOR32: // 2^32 colors. bmiColors is not used. Pixels are U_RGBQUAD. + case U_BCBM_EXPLICIT: // Derinved from JPG or PNG compressed image or ? + default: + return(7); // This should not be possible, but might happen with memory corruption + } + } + else { + switch(colortype){ + case U_BCBM_COLOR16: // 2^16 colors. (Several different color methods)) + b /= 8; g /= 8; r /= 8; + // Do it in this way so that the bytes are always stored Little Endian + tmp8 = b; + tmp8 |= g<<5; // least significant 3 bits of green + *pxptr++ = tmp8; + tmp8 = g>>3; // most significant 2 bits of green (there are only 5 bits of data) + tmp8 |= r<<2; + *pxptr++ = tmp8; + break; + case U_BCBM_COLOR24: // 2^24 colors. bmiColors is not used. Pixels are U_RGBTRIPLE. + *pxptr++ = b; + *pxptr++ = g; + *pxptr++ = r; + break; + case U_BCBM_COLOR32: // 2^32 colors. bmiColors is not used. Pixels are U_RGBQUAD. + *pxptr++ = b; + *pxptr++ = g; + *pxptr++ = r; + *pxptr++ = a; + break; + case U_BCBM_MONOCHROME: // 2 colors. bmiColors array has two entries + case U_BCBM_COLOR4: // 2^4 colors. bmiColors array has 16 entries + case U_BCBM_COLOR8: // 2^8 colors. bmiColors array has 256 entries + case U_BCBM_EXPLICIT: // Derinved from JPG or PNG compressed image or ? + default: + return(7); // This should not be possible, but might happen with memory corruption + } + } + } + if( use_ct && colortype == U_BCBM_MONOCHROME && (j % 8) ){ + *pxptr++ = tmp8; // Write last few indices + tmp8 = 0; + } + if( use_ct && colortype == U_BCBM_COLOR4 && (j % 2) ){ + *pxptr++ = tmp8; // Write last few indices + tmp8 = 0; + } + if(pad){ + memset(pxptr,0,pad); // not strictly necessary, but set all bytes so that we can find important unset ones with valgrind + pxptr += pad; + } + } + return(0); +} + +/** + \brief Get the DIB parameters from the BMI of the record for use by DBI_to_RGBA() + + \return 0 on success, other values on errors. + \param pEmr pointer to EMR record that has a U_BITMAPINFO and bitmap + \param offBitsSrc Offset to the bitmap + \param offBmiSrc Offset to the U_BITMAPINFO + \param px pointer to DIB pixel array in pEmr + \param ct pointer to DIB color table in pEmr + \param numCt DIB color table number of entries + \param width Width of pixel array + \param height Height of pixel array (always returned as a positive number) + \param colortype DIB BitCount Enumeration + \param invert If DIB rows are in opposite order from RGBA rows +*/ +int get_DIB_params( + void *pEmr, + uint32_t offBitsSrc, + uint32_t offBmiSrc, + char **px, + PU_RGBQUAD *ct, + uint32_t *numCt, + uint32_t *width, + uint32_t *height, + uint32_t *colortype, + uint32_t *invert + ){ + PU_BITMAPINFO Bmi = (PU_BITMAPINFO)((char *)pEmr + offBmiSrc); + if(Bmi->bmiHeader.biCompression != U_BI_RGB)return(1); + *width = Bmi->bmiHeader.biWidth; + *colortype = Bmi->bmiHeader.biBitCount; + *numCt = Bmi->bmiHeader.biClrUsed; + if(Bmi->bmiHeader.biHeight < 0){ + *height = -Bmi->bmiHeader.biHeight; + *invert = 1; + } + else { + *height = Bmi->bmiHeader.biHeight; + *invert = 0; + } + if(numCt){ + *ct = (PU_RGBQUAD) ((char *)Bmi + sizeof(U_BITMAPINFOHEADER)); + } + *px = (char *)((char *)pEmr + offBitsSrc); + return(0); +} + +/** + \brief Convert one of many different types of DIB pixmaps to an RGBA 32 bit pixmap. + + \return 0 on success, other values on errors. + \param px DIB pixel array + \param ct DIB color table + \param numCt DIB color table number of entries + \param rgba_px U_RGBA pixel array (32 bits), created by this routine, caller must free. + \param w Width of pixel array + \param h Height of pixel array + \param colortype DIB BitCount Enumeration + \param use_ct Kept for symmetry with RGBA_to_DIB, should be set to numCt + \param invert If DIB rows are in opposite order from RGBA rows +*/ +int DIB_to_RGBA( + char *px, + PU_RGBQUAD ct, + int numCt, + char **rgba_px, + int w, + int h, + uint32_t colortype, + int use_ct, + int invert + ){ + uint32_t cbRgba_px; + int stride; + int bs; + int pad; + int i,j; + int istart, iend, iinc; + uint8_t r,g,b,a,tmp8; + char *pxptr; + char *rptr; + int usedbytes; + U_RGBQUAD color; + int32_t index; + + // sanity checking + if(!w || !h || !colortype || !px)return(1); + if(use_ct && colortype >= U_BCBM_COLOR16)return(2); //color tables not used above 16 bit pixels + if(!use_ct && colortype < U_BCBM_COLOR16)return(3); //color tables mandatory for < 16 bit + if(use_ct && !numCt)return(4); //color table not adequately described + + stride = w * 4; + cbRgba_px = stride * h; + bs = colortype/8; + if(bs<1){ + bs=1; + usedbytes = (w*colortype + 7)/8; // width of line in fully and partially occupied bytes + } + else { + usedbytes = w*bs; + } + pad = UP4(usedbytes) - usedbytes; // DIB rows must be aligned on 4 byte boundaries, they are padded at the end to accomplish this.; + *rgba_px = (char *) malloc(cbRgba_px); + if(!rgba_px)return(4); + + if(invert){ + istart = h-1; + iend = -1; + iinc = -1; + } + else { + istart = 0; + iend = h; + iinc = 1; + } + + pxptr = px; + tmp8 = 0; // silences a compiler warning, tmp8 always sets when j=0, so never used uninitialized + for(i=istart; i!=iend; i+=iinc){ + rptr= *rgba_px + i*stride; + for(j=0; j> 7; + tmp8 = tmp8 << 1; + break; + case U_BCBM_COLOR4: // 2^4 colors. bmiColors array has 16 entries + if(!(j % 2)){ tmp8 = *pxptr++; } + index = 0xF0 & tmp8; + index = index >> 4; + tmp8 = tmp8 << 4; + break; + case U_BCBM_COLOR8: // 2^8 colors. bmiColors array has 256 entries + index = (uint8_t) *pxptr++;; + break; + case U_BCBM_COLOR16: // 2^16 colors. (Several different color methods)) + case U_BCBM_COLOR24: // 2^24 colors. bmiColors is not used. Pixels are U_RGBTRIPLE. + case U_BCBM_COLOR32: // 2^32 colors. bmiColors is not used. Pixels are U_RGBQUAD. + case U_BCBM_EXPLICIT: // Derinved from JPG or PNG compressed image or ? + default: + return(7); // This should not be possible, but might happen with memory corruption + } + color = ct[index]; + b = U_BGRAGetB(color); + g = U_BGRAGetG(color); + r = U_BGRAGetR(color); + a = U_BGRAGetA(color); + } + else { + switch(colortype){ + case U_BCBM_COLOR16: // 2^16 colors. (Several different color methods)) + // Do it in this way because the bytes are always stored Little Endian + tmp8 = *pxptr++; + b = (0x1F & tmp8) <<3; // 5 bits of b into the top 5 of 8 + g = tmp8 >> 5; // least significant 3 bits of green + tmp8 = *pxptr++; + r = (0x7C & tmp8) << 1; // 5 bits of r into the top 5 of 8 + g |= (0x3 & tmp8) << 3; // most significant 2 bits of green (there are only 5 bits of data) + g = g << 3; // restore intensity (have lost 3 bits of accuracy) + a = 0; + break; + case U_BCBM_COLOR24: // 2^24 colors. bmiColors is not used. Pixels are U_RGBTRIPLE. + b = *pxptr++; + g = *pxptr++; + r = *pxptr++; + a = 0; + break; + case U_BCBM_COLOR32: // 2^32 colors. bmiColors is not used. Pixels are U_RGBQUAD. + b = *pxptr++; + g = *pxptr++; + r = *pxptr++; + a = *pxptr++; + break; + case U_BCBM_MONOCHROME: // 2 colors. bmiColors array has two entries + case U_BCBM_COLOR4: // 2^4 colors. bmiColors array has 16 entries + case U_BCBM_COLOR8: // 2^8 colors. bmiColors array has 256 entries + case U_BCBM_EXPLICIT: // Derinved from JPG or PNG compressed image or ? + default: + return(7); // This should not be possible, but might happen with memory corruption + } + } + *rptr++ = r; + *rptr++ = g; + *rptr++ = b; + *rptr++ = a; + } + for(j=0; jnSize; + dup=malloc(irecsize); + if(dup){ memcpy(dup,emr,irecsize); } + return(dup); +} + + +/** + \brief Start constructing an emf in memory. Supply the file name and initial size. + \return 0 for success, >=0 for failure. + \param name EMF filename (will be opened) + \param initsize Initialize EMF in memory to hold this many bytes + \param chunksize When needed increase EMF in memory by this number of bytes + \param et EMF in memory + + +*/ +int emf_start( + const char *name, + const uint32_t initsize, + const uint32_t chunksize, + EMFTRACK **et + ){ + FILE *fp; + EMFTRACK *etl=NULL; + + if(initsize < 1)return(1); + if(chunksize < 1)return(2); + if(!name)return(3); + etl = (EMFTRACK *) malloc(sizeof(EMFTRACK)); + if(!etl)return(4); + etl->buf = malloc(initsize); // no need to zero the memory + if(!etl->buf){ + free(etl); + return(5); + } + fp=emf_fopen(name,U_WRITE); + if(!fp){ + free(etl->buf); + free(etl); + return(6); + } + etl->fp = fp; + etl->allocated = initsize; + etl->used = 0; + etl->records = 0; + etl->PalEntries = 0; + etl->chunk = chunksize; + *et=etl; + return(0); +} + +/** + \brief Finalize the emf in memory and write it to the file. + \return 0 on success, >=1 on failure + \param et EMF in memory + \param eht EMF handle table (peak handle number needed) +*/ +int emf_finish( + EMFTRACK *et, + EMFHANDLES *eht + ){ + U_EMRHEADER *record; + + if(!et->fp)return(1); // This could happen if something stomps on memory, otherwise should be caught in emf_start + + // Set the header fields which were unknown up until this point + + record = (U_EMRHEADER *)et->buf; + record->nBytes = et->used; + record->nRecords = et->records; + record->nHandles = eht->peak + 1; + record->nPalEntries = et->PalEntries; + +#if U_BYTE_SWAP + //This is a Big Endian machine, EMF data must be Little Endian + U_emf_endian(et->buf,et->used,1); +#endif + + if(1 != fwrite(et->buf,et->used,1,et->fp))return(2); + (void) fclose(et->fp); + et->fp=NULL; + return(0); +} + +/** + \brief Release memory for an emf structure in memory. Call this after emf_finish(). + \return 0 on success, >=1 on failure + \param et EMF in memory +*/ +int emf_free( + EMFTRACK **et + ){ + EMFTRACK *etl; + if(!et)return(1); + etl=*et; + if(!etl)return(2); + free(etl->buf); + free(etl); + *et=NULL; + return(0); +} + +/** + \brief wrapper for fopen, works on any platform + \return 0 on success, >=1 on failure + \param filename file to open (either ASCII or UTF-8) + \param mode U_READ or U_WRITE (these map to "rb" and "wb") +*/ +FILE *emf_fopen( + const char *filename, + const int mode + ){ + FILE *fp = NULL; +#ifdef WIN32 + uint16_t *fn16; + uint16_t *md16; + if(mode == U_READ){ md16 = U_Utf8ToUtf16le("rb", 0, NULL); } + else { md16 = U_Utf8ToUtf16le("wb", 0, NULL); } + fn16 = U_Utf8ToUtf16le(filename, 0, NULL); + fp = _wfopen(fn16,md16); + free(fn16); + free(md16); +#else + if(mode == U_READ){ fp = fopen(filename,"rb"); } + else { fp = fopen(filename,"wb"); } +#endif + return(fp); +} +/** + \brief Retrieve contents of an EMF file by name. + \return 0 on success, >=1 on failure + \param filename Name of file to open, including the path + \param contents Contents of the file. Buffer must be free()'d by caller. + \param Number of bytes in Contents +*/ +int emf_readdata( + const char *filename, + char **contents, + size_t *length + ){ + FILE *fp; + int status=0; + + *contents=NULL; + fp=emf_fopen(filename,U_READ); + if(!fp){ status = 1; } + else { + // read the entire file into memory + fseek(fp, 0, SEEK_END); // move to end + *length = ftell(fp); + rewind(fp); + *contents = (char *) malloc(*length); + if(!*contents){ + status = 2; + } + else { + size_t inbytes = fread(*contents,*length,1,fp); + if(inbytes != 1){ + free(*contents); + status = 3; + } + else { +#if U_BYTE_SWAP + //This is a Big Endian machine, EMF data is Little Endian + U_emf_endian(*contents,*length,0); // LE to BE +#endif + } + } + fclose(fp); + } + return(status); +} + + +/** + \brief Append an EMF record to an emf in memory. This may reallocate buf memory. + \return 0 for success, >=1 for failure. + \param rec Record to append to EMF in memory + \param et EMF in memory + \param freerec If true, free rec after append +*/ +int emf_append( + U_ENHMETARECORD *rec, + EMFTRACK *et, + int freerec + ){ + size_t deficit; + +#ifdef U_VALGRIND + printf("\nbefore \n"); + printf(" probe %d\n",memprobe(rec, U_EMRSIZE(rec))); + printf("after \n"); +#endif + if(!rec)return(1); + if(!et)return(2); + if(rec->nSize + et->used > et->allocated){ + deficit = rec->nSize + et->used - et->allocated; + if(deficit < et->chunk)deficit = et->chunk; + et->allocated += deficit; + et->buf = realloc(et->buf,et->allocated); + if(!et->buf)return(3); + } + memcpy(et->buf + et->used, rec, rec->nSize); + et->used += rec->nSize; + et->records++; + if(rec->iType == U_EMR_EOF){ et->PalEntries = ((U_EMREOF *)rec)->cbPalEntries; } + if(freerec){ free(rec); } + return(0); +} + +/** + \brief Create a handle table. Entries filled with 0 are empty, entries >0 hold a handle. + \return 0 for success, >=1 for failure. + \param initsize Initialize with space for this number of handles + \param chunksize When needed increase space by this number of handles + \param eht EMF handle table +*/ +int htable_create( + uint32_t initsize, + uint32_t chunksize, + EMFHANDLES **eht + ){ + EMFHANDLES *ehtl; + unsigned int i; + + if(initsize<1)return(1); + if(chunksize<1)return(2); + ehtl = (EMFHANDLES *) malloc(sizeof(EMFHANDLES)); + if(!ehtl)return(3); + ehtl->table = malloc(initsize * sizeof(uint32_t)); + if(!ehtl->table){ + free(ehtl); + return(4); + } + ehtl->stack = malloc(initsize * sizeof(uint32_t)); + if(!ehtl->stack){ + free(ehtl); + free(ehtl->table); + return(5); + } + memset(ehtl->table , 0, initsize * sizeof(uint32_t)); // zero all slots in the table + for(i=1; istack[i]=i;} // preset the stack + ehtl->allocated = initsize; + ehtl->chunk = chunksize; + ehtl->table[0] = 0; // This slot isn't actually ever used + ehtl->stack[0] = 0; // This stack position isn't actually ever used + ehtl->peak = 1; + ehtl->sptr = 1; + ehtl->top = 0; + *eht = ehtl; + return(0); +} + + +/** + \brief Delete an entry from the handle table. Move it back onto the stack. The specified slot is filled with a 0. + \return 0 for success, >=1 for failure. + \param ih handle + \param eht EMF handle table + +*/ +int htable_delete( + uint32_t *ih, + EMFHANDLES *eht + ){ + if(!eht)return(1); + if(!eht->table)return(2); + if(!eht->stack)return(3); + if(*ih < 1)return(4); // invalid handle + if(!eht->table[*ih])return(5); // requested table position was not in use + eht->table[*ih]=0; // remove handle from table + while(eht->top>0 && !eht->table[eht->top]){ // adjust top + eht->top--; + } + eht->sptr--; // adjust stack + eht->stack[eht->sptr]=*ih; // place handle on stack + *ih=0; // invalidate handle variable, so a second delete will of it is not possible + return(0); +} + +/** + \brief Returns the index of the first free slot. + Call realloc() if needed. The slot is set to handle (indicates occupied) and the peak value is adjusted. + \return 0 for success, >=1 for failure. + \param ih handle + \param eht EMF handle table +*/ +int htable_insert( + uint32_t *ih, + EMFHANDLES *eht + ){ + unsigned int i; + size_t newsize; + + if(!eht)return(1); + if(!eht->table)return(2); + if(!eht->stack)return(3); + if(!ih)return(4); + if(eht->sptr >= eht->allocated - 1){ // need to reallocate + newsize=eht->allocated + eht->chunk; + eht->table = realloc(eht->table,newsize * sizeof(uint32_t)); + if(!eht->table)return(5); + memset(&eht->table[eht->allocated] , 0, eht->chunk * sizeof(uint32_t)); // zero all NEW slots in the table + + eht->stack = realloc(eht->stack,newsize * sizeof(uint32_t)); + if(!eht->stack)return(6); + for(i=eht->allocated; istack[i] = i; } // init all NEW slots in the stack + eht->allocated = newsize; + } + *ih = eht->stack[eht->sptr]; // handle that is inserted + if(eht->table[*ih])return(7); + eht->table[*ih] = *ih; // handle goes into preexisting (but zero) slot in table + eht->stack[eht->sptr] = 0; + if(*ih > eht->top){ eht->top = *ih; } + if(eht->sptr > eht->peak){ eht->peak = eht->sptr; } + eht->sptr++; // next available handle + + return(0); +} + +/** + \brief Free all memory in an htable. Sets the pointer to NULL. + \return 0 for success, >=1 for failure. + \param eht EMF handle table +*/ +int htable_free( + EMFHANDLES **eht + ){ + EMFHANDLES *ehtl; + if(!eht)return(1); + ehtl = *eht; + if(!ehtl)return(2); + if(!ehtl->table)return(3); + if(!ehtl->stack)return(4); + free(ehtl->table); + free(ehtl->stack); + free(ehtl); + *eht=NULL; + return(0); +} + +/* ********************************************************************************************** +These functions create standard structures used in the EMR records. +*********************************************************************************************** */ + + +/** + \brief Set up fields for an EMR_HEADER from the physical device's width and height in mm and dots per millimeter. + Typically this is something like 216,279,47.244 (Letter paper, 1200 DPI = 47.244 DPmm) + \return 0 for success, >=1 for failure. + \param xmm Device width in millimeters + \param ymm Device height in millimeters + \param dpmm Dots per millimeter + \param szlDev Device size structure in pixels + \param szlMm Device size structure in mm +*/ +int device_size( + const int xmm, + const int ymm, + const float dpmm, + U_SIZEL *szlDev, + U_SIZEL *szlMm + ){ + if(xmm < 0 || ymm < 0 || dpmm < 0)return(1); + szlDev->cx = U_ROUND((float) xmm * dpmm); + szlDev->cy = U_ROUND((float) ymm * dpmm);; + szlMm->cx = xmm; + szlMm->cy = ymm; + return(0); +} + +/** + \brief Set up fields for an EMR_HEADER for drawing by physical size in mm and dots per millimeter. + Technically rclBounds is supposed to be the extent of the drawing within the EMF, but libUEMF has no way + of knowing this since it never actually draws anything. Instead this is set to the full drawing size. + \return 0 for success, >=1 for failure. + \param xmm Drawing width in millimeters + \param ymm Drawing height in millimeters + \param dpmm Dots per millimeter + \param rclBounds Drawing size structure in pixels + \param rclFrame Drawing size structure in mm +*/ +int drawing_size( + const int xmm, + const int ymm, + const float dpmm, + U_RECTL *rclBounds, + U_RECTL *rclFrame + ){ + if(xmm < 0 || ymm < 0 || dpmm < 0)return(1); + rclBounds->left = 0; + rclBounds->top = 0; + rclBounds->right = U_ROUND((float) xmm * dpmm); // because coordinate system is 0,0 in upper left, N,M in lower right + rclBounds->bottom = U_ROUND((float) ymm * dpmm); + rclFrame->left = 0; + rclFrame->top = 0; + rclFrame->right = U_ROUND((float) xmm * 100.); + rclFrame->bottom = U_ROUND((float) ymm * 100.); + return(0); +} + +/** + \brief Set a U_COLORREF value from separeate R,G,B values. + Or use macro directly: cr = U_RGB(r,g,b). + \param red Red component + \param green Green component + \param blue Blue component + +*/ +U_COLORREF colorref_set( + uint8_t red, + uint8_t green, + uint8_t blue + ){ + U_COLORREF cr = (U_COLORREF){red , green, blue, 0}; + return(cr); +} + +/** + \brief Set rect and rectl objects from Upper Left and Lower Right corner points. + \param ul upper left corner of rectangle + \param lr lower right corner of rectangle +*/ +U_RECTL rectl_set( + U_POINTL ul, + U_POINTL lr + ){ + U_RECTL rct; + rct.left = ul.x; + rct.top = ul.y; + rct.right = lr.x; + rct.bottom = lr.y; + return(rct); +} + +/** + \brief Set sizel objects with X,Y values. + \param x X coordinate + \param y Y coordinate +*/ +U_SIZEL sizel_set( + int32_t x, + int32_t y + ){ + U_SIZEL sz; + sz.cx = x; + sz.cy = y; + return(sz); +} + +/** + \brief Set pointl objects with X,Y values. + \param x X coordinate + \param y Y coordinate +*/ +U_POINTL point32_set( + int32_t x, + int32_t y + ){ + U_POINTL pt; + pt.x = x; + pt.y = y; + return(pt); +} + +/** + \brief Set point16 objects with 16 bit X,Y values. + \param x X coordinate + \param y Y coordinate +*/ +U_POINT16 point16_set( + int16_t x, + int16_t y + ){ + U_POINT16 pt; + pt.x = x; + pt.y = y; + return(pt); +} + +/** + \brief Find the bounding rectangle from a polyline of a given width. + \param count number of points in the polyline + \param pts the polyline + \param width width of drawn line + +*/ +U_RECT findbounds( + uint32_t count, + PU_POINT pts, + uint32_t width + ){ + U_RECT rect={INT32_MAX, INT32_MAX, INT32_MIN, INT32_MIN }; + unsigned int i; + + for(i=0; ix < rect.left ) rect.left = pts->x; + if ( pts->x > rect.right ) rect.right = pts->x; + if ( pts->y < rect.top ) rect.top = pts->y; + if ( pts->y > rect.bottom ) rect.bottom = pts->y; + } + if(width > 0){ + rect.left -= width; + rect.right += width; + rect.top += width; + rect.bottom -= width; + } + return(rect); +} + +/** + \brief Find the bounding rectangle from a polyline of a given width. + \param count number of points in the polyline + \param pts the polyline + \param width width of drawn line + +*/ +U_RECT findbounds16( + uint32_t count, + PU_POINT16 pts, + uint32_t width + ){ + U_RECT rect={INT16_MAX, INT16_MAX, INT16_MIN, INT16_MIN }; + unsigned int i; + + for(i=0; ix < rect.left ) rect.left = pts->x; + if ( pts->x > rect.right ) rect.right = pts->x; + if ( pts->y < rect.top ) rect.top = pts->y; + if ( pts->y > rect.bottom ) rect.bottom = pts->y; + } + if(width > 0){ + rect.left -= width; + rect.right += width; + rect.top += width; + rect.bottom -= width; + } + return(rect); +} +/** + \brief Construct a U_LOGBRUSH structure. + \return U_LOGBRUSH structure + \param lbStyle LB_Style Enumeration + \param lbColor Brush color + \param lbHatch HatchStyle Enumertaion +*/ +U_LOGBRUSH logbrush_set( + uint32_t lbStyle, + U_COLORREF lbColor, + int32_t lbHatch + ){ + U_LOGBRUSH lb; + lb.lbStyle = lbStyle; + lb.lbColor = lbColor; + lb.lbHatch = lbHatch; + return(lb); +} + +/** + \brief Construct a U_XFORM structure. + \return U_XFORM structure + \param eM11 Rotation Matrix element + \param eM12 Rotation Matrix element + \param eM21 Rotation Matrix element + \param eM22 Rotation Matrix element + \param eDx Translation element + \param eDy Translation element +*/ +U_XFORM xform_set( + U_FLOAT eM11, + U_FLOAT eM12, + U_FLOAT eM21, + U_FLOAT eM22, + U_FLOAT eDx, + U_FLOAT eDy + ){ + U_XFORM xform; + xform.eM11 = eM11; + xform.eM12 = eM12; + xform.eM21 = eM21; + xform.eM22 = eM22; + xform.eDx = eDx; + xform.eDy = eDy; + return(xform); +} + +/** + \brief Construct a U_XFORM structure. + \return U_XFORM structure + \param scale Scale factor + \param axesRatio Ratio of minor axis/major axis + \param rot Rotation angle in degrees, positive is counter clockwise from the x axis. + \param axisRot Angle in degrees defining the major axis before rotation, positive is counter clockwise from the x axis. + \param eDx Translation element + \param eDy Translation element + + Operation is: + 1 Conformal map of points based on scale, axis rotation, and axis ratio, + 2. Apply rotation + 3. Apply offset +*/ +U_XFORM xform_alt_set( + U_FLOAT scale, + U_FLOAT ratio, + U_FLOAT rot, + U_FLOAT axisrot, + U_FLOAT eDx, + U_FLOAT eDy + ){ + U_XFORM xform; + U_MAT2X2 mat1, mat2; + // angles are in degrees, must be in radians + rot *= (2.0 * U_PI)/360.0; + axisrot *= -(2.0 * U_PI)/360.0; + mat1.M11 = cos(rot); // set up the rotation matrix + mat1.M12 = -sin(rot); + mat1.M21 = sin(rot); + mat1.M22 = cos(rot); + if(ratio!=1.0){ // set scale/ellipticity matrix + mat2.M11 = scale*( cos(axisrot)*cos(axisrot) + ratio*sin(axisrot)*sin(axisrot) ); + mat2.M12 = mat2.M21 = scale*( sin(axisrot)*cos(axisrot) * (1.0 - ratio) ); + mat2.M22 = scale*( sin(axisrot)*sin(axisrot) + ratio*cos(axisrot)*cos(axisrot) ); + } + else { // when the ratio is 1.0 then the major axis angle is ignored and only scale matters + mat2.M11 = scale; + mat2.M12 = 0.0; + mat2.M21 = 0.0; + mat2.M22 = scale; + } + xform.eM11 = mat2.M11 * mat1.M11 + mat2.M12 * mat1.M21; + xform.eM12 = mat2.M11 * mat1.M12 + mat2.M12 * mat1.M22;; + xform.eM21 = mat2.M21 * mat1.M11 + mat2.M22 * mat1.M21; + xform.eM22 = mat2.M21 * mat1.M12 + mat2.M22 * mat1.M22; + xform.eDx = eDx; + xform.eDy = eDy; + return(xform); +} + + +/** + \brief Construct a U_LOGCOLORSPACEA structure. + \return U_LOGCOLORSPACEA structure + \param lcsCSType LCS_CSType Enumeration + \param lcsIntent LCS_Intent Enumeration + \param lcsEndpoints CIE XYZ color space endpoints + \param lcsGammaRGB Gamma For RGB + \param lcsFilename Could name an external color profile file, otherwise empty string +*/ +U_LOGCOLORSPACEA logcolorspacea_set( + int32_t lcsCSType, + int32_t lcsIntent, + U_CIEXYZTRIPLE lcsEndpoints, + U_LCS_GAMMARGB lcsGammaRGB, + char *lcsFilename + ){ + U_LOGCOLORSPACEA lcsa; + lcsa.lcsSignature = U_LCS_SIGNATURE; + lcsa.lcsVersion = U_LCS_SIGNATURE; + lcsa.lcsSize = sizeof(U_LOGCOLORSPACEA); + lcsa.lcsCSType = lcsCSType; + lcsa.lcsIntent = lcsIntent; + lcsa.lcsEndpoints = lcsEndpoints; + lcsa.lcsGammaRGB = lcsGammaRGB; + memset(lcsa.lcsFilename,0,U_MAX_PATH); // zero out the Filename field + strncpy(lcsa.lcsFilename,lcsFilename,U_MAX_PATH); + return(lcsa); +} + +/** + + \brief Construct a U_LOGCOLORSPACEW structure. + \return U_LOGCOLORSPACEW structure + \param lcsCSType LCS_CSType Enumeration + \param lcsIntent LCS_Intent Enumeration + \param lcsEndpoints CIE XYZ color space endpoints + \param lcsGammaRGB Gamma For RGB + \param lcsFilename Could name an external color profile file, otherwise empty string +*/ +U_LOGCOLORSPACEW logcolorspacew_set( + int32_t lcsCSType, + int32_t lcsIntent, + U_CIEXYZTRIPLE lcsEndpoints, + U_LCS_GAMMARGB lcsGammaRGB, + uint16_t *lcsFilename + ){ + U_LOGCOLORSPACEW lcsa; + lcsa.lcsSignature = U_LCS_SIGNATURE; + lcsa.lcsVersion = U_LCS_SIGNATURE; + lcsa.lcsSize = sizeof(U_LOGCOLORSPACEW); + lcsa.lcsCSType = lcsCSType; + lcsa.lcsIntent = lcsIntent; + lcsa.lcsEndpoints = lcsEndpoints; + lcsa.lcsGammaRGB = lcsGammaRGB; + wchar16strncpypad(lcsa.lcsFilename,lcsFilename,U_MAX_PATH); + return(lcsa); +} + +/** + + \brief Construct a U_PANOSE structure. + \return U_PANOSE structure + \param bFamilyType FamilyType Enumeration + \param bSerifStyle SerifType Enumeration + \param bWeight Weight Enumeration + \param bProportion Proportion Enumeration + \param bContrast Contrast Enumeration + \param bStrokeVariation StrokeVariation Enumeration + \param bArmStyle ArmStyle Enumeration + \param bLetterform Letterform Enumeration + \param bMidline Midline Enumeration + \param bXHeight XHeight Enumeration +*/ +U_PANOSE panose_set( + uint8_t bFamilyType, + uint8_t bSerifStyle, + uint8_t bWeight, + uint8_t bProportion, + uint8_t bContrast, + uint8_t bStrokeVariation, + uint8_t bArmStyle, + uint8_t bLetterform, + uint8_t bMidline, + uint8_t bXHeight + ){ + U_PANOSE panose; + panose.bFamilyType = bFamilyType; + panose.bSerifStyle = bSerifStyle; + panose.bWeight = bWeight; + panose.bProportion = bProportion; + panose.bContrast = bContrast; + panose.bStrokeVariation = bStrokeVariation; + panose.bArmStyle = bArmStyle; + panose.bLetterform = bLetterform; + panose.bMidline = bMidline; + panose.bXHeight = bXHeight; + return(panose); +} + +/** + \brief Construct a U_LOGFONT structure. + \return U_LOGFONT structure + \param lfHeight Height in Logical units + \param lfWidth Average Width in Logical units + \param lfEscapement Angle in 0.1 degrees betweem escapement vector and X axis + \param lfOrientation Angle in 0.1 degrees between baseline and X axis + \param lfWeight LF_Weight Enumeration + \param lfItalic Italics: 0 or 1 + \param lfUnderline Underline: 0 or 1 + \param lfStrikeOut Strikeout: 0 or 1 + \param lfCharSet LF_CharSet Enumeration + \param lfOutPrecision LF_OutPrecision Enumeration + \param lfClipPrecision LF_ClipPrecision Enumeration + \param lfQuality LF_Quality Enumeration + \param lfPitchAndFamily LF_PitchAndFamily Enumeration + \param lfFaceName Name of font. truncates at U_LF_FACESIZE, smaller must be null terminated + +*/ +U_LOGFONT logfont_set( + int32_t lfHeight, + int32_t lfWidth, + int32_t lfEscapement, + int32_t lfOrientation, + int32_t lfWeight, + uint8_t lfItalic, + uint8_t lfUnderline, + uint8_t lfStrikeOut, + uint8_t lfCharSet, + uint8_t lfOutPrecision, + uint8_t lfClipPrecision, + uint8_t lfQuality, + uint8_t lfPitchAndFamily, + uint16_t *lfFaceName + ){ + U_LOGFONT lf; + lf.lfHeight = lfHeight; + lf.lfWidth = lfWidth; + lf.lfEscapement = lfEscapement; + lf.lfOrientation = lfOrientation; + lf.lfWeight = lfWeight; + lf.lfItalic = lfItalic; + lf.lfUnderline = lfUnderline; + lf.lfStrikeOut = lfStrikeOut; + lf.lfCharSet = lfCharSet; + lf.lfOutPrecision = lfOutPrecision; + lf.lfClipPrecision = lfClipPrecision; + lf.lfQuality = lfQuality; + lf.lfPitchAndFamily = lfPitchAndFamily; + wchar16strncpypad(lf.lfFaceName, lfFaceName, U_LF_FACESIZE); // pad this one as the intial structure was not set to zero + return(lf); +} + + +/** + \brief Construct a U_LOGFONT_PANOSE structure. + \return U_LOGFONT_PANOSE structure + \param elfLogFont Basic font attributes + \param elfFullName Font full name, truncates at U_LF_FULLFACESIZE, smaller must be null terminated + \param elfStyle Font style, truncates at U_LF_FULLFACESIZE, smaller must be null terminated + \param elfStyleSize Font hinting starting at this point size, if 0, starts at Height + \param elfPanose Panose Object. If all zero, it is ignored. +*/ +U_LOGFONT_PANOSE logfont_panose_set( + U_LOGFONT elfLogFont, + uint16_t *elfFullName, + uint16_t *elfStyle, + uint32_t elfStyleSize, + U_PANOSE elfPanose + ){ + U_LOGFONT_PANOSE lfp; + memset(&lfp,0,sizeof(U_LOGFONT_PANOSE)); // all fields zero unless needed. Many should be ignored or must be 0. + wchar16strncpy(lfp.elfFullName, elfFullName, U_LF_FULLFACESIZE); + wchar16strncpy(lfp.elfStyle, elfStyle, U_LF_FACESIZE); + lfp.elfLogFont = elfLogFont; + lfp.elfStyleSize = elfStyleSize; + lfp.elfPanose = elfPanose; + return(lfp); +} + +/** + \brief Construct a U_BITMAPINFOHEADER structure. + \return U_BITMAPINFOHEADER structure + \param biWidth Bitmap width in pixels + \param biHeight Bitmap height in pixels + \param biPlanes Planes (must be 1) + \param biBitCount BitCount Enumeration + \param biCompression BI_Compression Enumeration + \param biSizeImage Size in bytes of image + \param biXPelsPerMeter X Resolution in pixels/meter + \param biYPelsPerMeter Y Resolution in pixels/meter + \param biClrUsed Number of bmciColors in U_BITMAPCOREINFO + \param biClrImportant Number of bmciColors needed (0 means all). +*/ +U_BITMAPINFOHEADER bitmapinfoheader_set( + int32_t biWidth, + int32_t biHeight, + uint16_t biPlanes, + uint16_t biBitCount, + uint32_t biCompression, + uint32_t biSizeImage, + int32_t biXPelsPerMeter, + int32_t biYPelsPerMeter, + U_NUM_RGBQUAD biClrUsed, + uint32_t biClrImportant + ){ + U_BITMAPINFOHEADER Bmi; + Bmi.biSize = sizeof(U_BITMAPINFOHEADER); + Bmi.biWidth = biWidth; + Bmi.biHeight = biHeight; + Bmi.biPlanes = biPlanes; + Bmi.biBitCount = biBitCount; + Bmi.biCompression = biCompression; + Bmi.biSizeImage = biSizeImage; + Bmi.biXPelsPerMeter = biXPelsPerMeter; + Bmi.biYPelsPerMeter = biYPelsPerMeter; + Bmi.biClrUsed = biClrUsed; + Bmi.biClrImportant = biClrImportant; + return(Bmi); +} + + +/** + \brief Allocate and construct a U_BITMAPINFO structure. + \return Pointer to a U_BITMAPINFO structure + \param BmiHeader Geometry and pixel properties + \param BmiColors Color table (must be NULL for some values of BmiHeader->biBitCount) +*/ +PU_BITMAPINFO bitmapinfo_set( + U_BITMAPINFOHEADER BmiHeader, + PU_RGBQUAD BmiColors + ){ + char *record; + int irecsize; + int cbColors, cbColors4,off; + + cbColors = 4*BmiHeader.biClrUsed; + cbColors4 = UP4(cbColors); + irecsize = sizeof(U_BITMAPINFOHEADER) + cbColors4; + record = malloc(irecsize); + if(record){ + memcpy(record, &BmiHeader, sizeof(U_BITMAPINFOHEADER)); + if(cbColors){ + off = sizeof(U_BITMAPINFOHEADER); + memcpy(record + off, BmiColors, cbColors); + off += cbColors; + if(cbColors4 - cbColors){ memset(record + off, 0, cbColors4 - cbColors); } + } + } + return((PU_BITMAPINFO) record); +} + +/** + \brief Allocate and construct a U_EXTLOGPEN structure. + \return pointer to U_EXTLOGPEN structure, or NULL on error + \param elpPenStyle PenStyle Enumeration + \param elpWidth Width in logical units (elpPenStyle & U_PS_GEOMETRIC) or 1 (pixel) + \param elpBrushStyle LB_Style Enumeration + \param elpColor Pen color + \param elpHatch HatchStyle Enumeration + \param elpNumEntries Count of StyleEntry array + \param elpStyleEntry Array of StyleEntry (For user specified dot/dash patterns) +*/ +PU_EXTLOGPEN extlogpen_set( + uint32_t elpPenStyle, + uint32_t elpWidth, + uint32_t elpBrushStyle, + U_COLORREF elpColor, + int32_t elpHatch, + U_NUM_STYLEENTRY elpNumEntries, + U_STYLEENTRY *elpStyleEntry + ){ + int irecsize,szSyleArray; + char *record; + + if(elpNumEntries){ + if(!elpStyleEntry)return(NULL); + szSyleArray = elpNumEntries * sizeof(U_STYLEENTRY); + irecsize = sizeof(U_EXTLOGPEN) + szSyleArray - sizeof(U_STYLEENTRY); // first one is in the record + } + else { + szSyleArray = 0; + irecsize = sizeof(U_EXTLOGPEN); + } + record = malloc(irecsize); + if(record){ + ((PU_EXTLOGPEN) record)->elpPenStyle = elpPenStyle; + ((PU_EXTLOGPEN) record)->elpWidth = elpWidth; + ((PU_EXTLOGPEN) record)->elpBrushStyle = elpBrushStyle; + ((PU_EXTLOGPEN) record)->elpColor = elpColor; + ((PU_EXTLOGPEN) record)->elpHatch = elpHatch; + ((PU_EXTLOGPEN) record)->elpNumEntries = elpNumEntries; + if(elpNumEntries){ memcpy(((PU_EXTLOGPEN) record)->elpStyleEntry,elpStyleEntry,szSyleArray); } + else { memset(((PU_EXTLOGPEN) record)->elpStyleEntry,0,sizeof(U_STYLEENTRY)); } // not used, but this stops valgrind warnings + } + return((PU_EXTLOGPEN) record); +} + +/** + \brief Construct a U_LOGPEN structure. + \return U_LOGPEN structure + \param lopnStyle PenStyle Enumeration + \param lopnWidth Width of pen set by X, Y is ignored + \param lopnColor Pen color value + +*/ +U_LOGPEN logpen_set( + uint32_t lopnStyle, + U_POINT lopnWidth, + U_COLORREF lopnColor + ){ + U_LOGPEN lp; + lp.lopnStyle = lopnStyle; + lp.lopnWidth = lopnWidth; + lp.lopnColor = lopnColor; + return(lp); +} + +/** + \brief Construct a U_LOGPLTNTRY structure. + \return U_LOGPLTNTRY structure + \param peReserved Ignore + \param peRed Palette entry Red Intensity + \param peGreen Palette entry Green Intensity + \param peBlue Palette entry Blue Intensity +*/ +U_LOGPLTNTRY logpltntry_set( + uint8_t peReserved, + uint8_t peRed, + uint8_t peGreen, + uint8_t peBlue + ){ + U_LOGPLTNTRY lpny; + lpny.peReserved = peReserved; + lpny.peRed = peRed; + lpny.peGreen = peGreen; + lpny.peBlue = peBlue; + return(lpny); +} + +/** + \brief Allocate and construct a U_LOGPALETTE structure. + \return pointer to U_LOGPALETTE structure, or NULL on error. + \param palNumEntries Number of U_LOGPLTNTRY objects + \param palPalEntry array, PC_Entry Enumeration +*/ +PU_LOGPALETTE logpalette_set( + U_NUM_LOGPLTNTRY palNumEntries, + PU_LOGPLTNTRY *palPalEntry + ){ + PU_LOGPALETTE record; + int cbPalArray,irecsize; + + if(palNumEntries == 0 || !palPalEntry)return(NULL); + cbPalArray = palNumEntries * sizeof(U_LOGPLTNTRY); + irecsize = sizeof(U_LOGPALETTE) + cbPalArray - sizeof(U_LOGPLTNTRY); + record = (PU_LOGPALETTE) malloc(irecsize); + if(irecsize){ + record->palVersion = U_LP_VERSION; + record->palNumEntries = palNumEntries; + memcpy(record->palPalEntry,palPalEntry,cbPalArray); + } + return(record); +} + +/** + \brief Construct a U_RGNDATAHEADER structure. + \return U_RGNDATAHEADER structure + \param nCount Number of rectangles in region + \param rclBounds Region bounds +*/ +U_RGNDATAHEADER rgndataheader_set( + U_NUM_RECTL nCount, + U_RECTL rclBounds + ){ + U_RGNDATAHEADER rdh; + rdh.dwSize = U_RDH_OBJSIZE; + rdh.iType = U_RDH_RECTANGLES; + rdh.nCount = nCount; + rdh.nRgnSize = nCount * sizeof(U_RECTL); // Size in bytes of retangle array + rdh.rclBounds = rclBounds; + return(rdh); +} + +/** + \brief Allocate and construct a U_RGNDATA structure. + \return pointer to U_RGNDATA structure, or NULL on error. + \param rdh Data description + \param Buffer Array of U_RECTL elements +*/ +PU_RGNDATA rgndata_set( + U_RGNDATAHEADER rdh, + PU_RECTL Buffer + ){ + char *record; + int irecsize; + int szRgnArray,off; + + if(!Buffer || !rdh.nCount || !rdh.nRgnSize)return(NULL); + szRgnArray = rdh.nRgnSize; // size of the U_RECTL array + irecsize = sizeof(U_RGNDATA) + szRgnArray - sizeof(U_RECTL); // core + array - overlap + record = malloc(irecsize); + if(record){ + memcpy(record, &rdh, sizeof(U_RGNDATAHEADER)); + off = sizeof(U_RGNDATAHEADER); + memcpy(record + off, Buffer, szRgnArray); + } + return((PU_RGNDATA) record); +} + +/** + \brief Construct a U_COLORADJUSTMENT structure. + \return U_COLORADJUSTMENT structure + \param Size Size of this structure in bytes + \param Flags ColorAdjustment Enumeration + \param IlluminantIndex Illuminant Enumeration + \param RedGamma Red Gamma correction (range:2500:65000, 10000 is no correction) + \param GreenGamma Green Gamma correction (range:2500:65000, 10000 is no correction) + \param BlueGamma Blue Gamma correction (range:2500:65000, 10000 is no correction) + \param ReferenceBlack Values less than this are black (range:0:4000) + \param ReferenceWhite Values more than this are white (range:6000:10000) + \param Contrast Contrast adjustment (range:-100:100, 0 is no correction) + \param Brightness Brightness adjustment (range:-100:100, 0 is no correction) + \param Colorfulness Colorfulness adjustment (range:-100:100, 0 is no correction) + \param RedGreenTint Tine adjustment (range:-100:100, 0 is no correction) +*/ +U_COLORADJUSTMENT coloradjustment_set( + uint16_t Size, + uint16_t Flags, + uint16_t IlluminantIndex, + uint16_t RedGamma, + uint16_t GreenGamma, + uint16_t BlueGamma, + uint16_t ReferenceBlack, + uint16_t ReferenceWhite, + int16_t Contrast, + int16_t Brightness, + int16_t Colorfulness, + int16_t RedGreenTint + ){ + U_COLORADJUSTMENT ca; + ca.caSize = Size; + ca.caFlags = Flags; + ca.caIlluminantIndex = IlluminantIndex; + ca.caRedGamma = U_MNMX(RedGamma, U_RGB_GAMMA_MIN, U_RGB_GAMMA_MAX); + ca.caGreenGamma = U_MNMX(GreenGamma, U_RGB_GAMMA_MIN, U_RGB_GAMMA_MAX); + ca.caBlueGamma = U_MNMX(BlueGamma, U_RGB_GAMMA_MIN, U_RGB_GAMMA_MAX); + // Next one is different to eliminate compiler warning - U_R_B_MIN is 0 and unsigned + ca.caReferenceBlack = U_MAX( ReferenceBlack, U_REFERENCE_BLACK_MAX); + ca.caReferenceWhite = U_MNMX(ReferenceWhite, U_REFERENCE_WHITE_MIN, U_REFERENCE_WHITE_MAX); + ca.caContrast = U_MNMX(Contrast, U_COLOR_ADJ_MIN, U_COLOR_ADJ_MAX); + ca.caBrightness = U_MNMX(Brightness, U_COLOR_ADJ_MIN, U_COLOR_ADJ_MAX); + ca.caColorfulness = U_MNMX(Colorfulness, U_COLOR_ADJ_MIN, U_COLOR_ADJ_MAX); + ca.caRedGreenTint = U_MNMX(RedGreenTint, U_COLOR_ADJ_MIN, U_COLOR_ADJ_MAX); + return(ca); +} + +/** + \brief Construct a U_PIXELFORMATDESCRIPTOR structure. + \return U_PIXELFORMATDESCRIPTOR structure + \param dwFlags PFD_dwFlags Enumeration + \param iPixelType PFD_iPixelType Enumeration + \param cColorBits RGBA: total bits per pixel + \param cRedBits Red bits per pixel + \param cRedShift Red shift to data bits + \param cGreenBits Green bits per pixel + \param cGreenShift Green shift to data bits + \param cBlueBits Blue bits per pixel + \param cBlueShift Blue shift to data bits + \param cAlphaBits Alpha bits per pixel + \param cAlphaShift Alpha shift to data bits + \param cAccumBits Accumulator buffer, total bitplanes + \param cAccumRedBits Red accumulator buffer bitplanes + \param cAccumGreenBits Green accumulator buffer bitplanes + \param cAccumBlueBits Blue accumulator buffer bitplanes + \param cAccumAlphaBits Alpha accumulator buffer bitplanes + \param cDepthBits Depth of Z-buffer + \param cStencilBits Depth of stencil buffer + \param cAuxBuffers Depth of auxilliary buffers (not supported) + \param iLayerType PFD_iLayerType Enumeration, may be ignored + \param bReserved Bits 0:3/4:7 are number of Overlay/Underlay planes + \param dwLayerMask may be ignored + \param dwVisibleMask color or index of underlay plane + \param dwDamageMask may be ignored +*/ +U_PIXELFORMATDESCRIPTOR pixelformatdescriptor_set( + uint32_t dwFlags, + uint8_t iPixelType, + uint8_t cColorBits, + uint8_t cRedBits, + uint8_t cRedShift, + uint8_t cGreenBits, + uint8_t cGreenShift, + uint8_t cBlueBits, + uint8_t cBlueShift, + uint8_t cAlphaBits, + uint8_t cAlphaShift, + uint8_t cAccumBits, + uint8_t cAccumRedBits, + uint8_t cAccumGreenBits, + uint8_t cAccumBlueBits, + uint8_t cAccumAlphaBits, + uint8_t cDepthBits, + uint8_t cStencilBits, + uint8_t cAuxBuffers, + uint8_t iLayerType, + uint8_t bReserved, + uint32_t dwLayerMask, + uint32_t dwVisibleMask, + uint32_t dwDamageMask + ){ + U_PIXELFORMATDESCRIPTOR pfd; + pfd.nSize = sizeof(U_PIXELFORMATDESCRIPTOR); + pfd.nVersion = 1; + pfd.dwFlags = dwFlags; + pfd.iPixelType = iPixelType; + pfd.cColorBits = cColorBits; + pfd.cRedBits = cRedBits; + pfd.cRedShift = cRedShift; + pfd.cGreenBits = cGreenBits; + pfd.cGreenShift = cGreenShift; + pfd.cBlueBits = cBlueBits; + pfd.cBlueShift = cBlueShift; + pfd.cAlphaBits = cAlphaBits; + pfd.cAlphaShift = cAlphaShift; + pfd.cAccumBits = cAccumBits; + pfd.cAccumRedBits = cAccumRedBits; + pfd.cAccumGreenBits = cAccumGreenBits; + pfd.cAccumBlueBits = cAccumBlueBits; + pfd.cAccumAlphaBits = cAccumAlphaBits; + pfd.cDepthBits = cDepthBits; + pfd.cStencilBits = cStencilBits; + pfd.cAuxBuffers = cAuxBuffers; + pfd.iLayerType = iLayerType; + pfd.bReserved = bReserved; + pfd.dwLayerMask = dwLayerMask; + pfd.dwVisibleMask = dwVisibleMask; + pfd.dwDamageMask = dwDamageMask; + return(pfd); +} + +/** + \brief Allocate and create a U_EMRTEXT structure followed by its variable pieces via a char* pointer. + Dx cannot be NULL, if the calling program has no appropriate values call dx_set() first. + \return char* pointer to U_EMRTEXT structure followed by its variable pieces, or NULL on error + \param ptlReference String start coordinates + \param NumString Number of characters in string, does NOT include a terminator + \param cbChar Number of bytes per character + \param String String to write + \param fOptions ExtTextOutOptions Enumeration + \param rcl (Optional, when fOptions & 7) grayed/clipping/opaque rectangle + \param Dx Character spacing array from the start of the RECORD +*/ +char *emrtext_set( + U_POINTL ptlReference, + U_NUM_STR NumString, + uint32_t cbChar, + void *String, + uint32_t fOptions, + U_RECTL rcl, + uint32_t *Dx + ){ + int irecsize,cbDxArray,cbString4,cbString,off; + char *record; + uint32_t *loffDx; + + if(!String)return(NULL); + if(!Dx)return(NULL); + cbString = cbChar * NumString; // size of the string in bytes + cbString4 = UP4(cbString); // size of the string buffer + cbDxArray = sizeof(uint32_t)*NumString; // size of Dx array storage + if(fOptions & U_ETO_PDY)cbDxArray += cbDxArray; // of the Dx buffer, here do both X and Y coordinates + irecsize = sizeof(U_EMRTEXT) + sizeof(uint32_t) + cbString4 + cbDxArray; // core structure + offDx + string buf + dx buf + if(!(fOptions & U_ETO_NO_RECT)){ irecsize += sizeof(U_RECTL); } // plus variable U_RECTL, when it is present + record = malloc(irecsize); + if(record){ + ((PU_EMRTEXT)record)->ptlReference = ptlReference; + ((PU_EMRTEXT)record)->nChars = NumString; + // pick up ((PU_EMRTEXT)record)->offString later + ((PU_EMRTEXT)record)->fOptions = fOptions; + off = sizeof(U_EMRTEXT); // location where variable pieces will start to be written + if(!(fOptions & U_ETO_NO_RECT)){ // variable field, may or may not be present + memcpy(record + off,&rcl, sizeof(U_RECTL)); + off += sizeof(U_RECTL); + } + loffDx = (uint32_t *)(record + off); // offDx will go here, but we do not know with what value yet + off += sizeof(uint32_t); + memcpy(record + off,String,cbString); // copy the string data to its buffer + ((PU_EMRTEXT)record)->offString = off; // now save offset in the structure + off += cbString; + if(cbString < cbString4){ + memset(record+off,0,cbString4-cbString); // keeps valgrind happy (initialize padding after string) + off += cbString4-cbString; + } + memcpy(record + off, Dx, cbDxArray); // copy the Dx data to its buffer + *loffDx = off; // now save offDx to the structure + } + return(record); +} + + + +/* ********************************************************************************************** +These functions are simpler or more convenient ways to generate the specified types of EMR records. +Each should be called in preference to the underlying "base" EMR function. +*********************************************************************************************** */ + + +/** + \brief Allocate and construct a U_EMRCOMMENT structure with a UTF8 string. + A U_EMRCOMMENT contains application specific data, and that may include contain null characters. This function may be used when the + comment only incluces UT8 text. + \return pointer to U_EMRCOMMENT structure, or NULL on error. + \param string UTF8 string to store in the comment + + +*/ +char *textcomment_set( + char *string + ){ + if(!string)return(NULL); + return(U_EMRCOMMENT_set(1 + strlen(string),string)); +} + +/** + \brief Allocate and construct a U_EMRDELETEOBJECT structure and also delete the requested object from the table. + Use this function instead of calling U_EMRDELETEOBJECT_set() directly. + \return pointer to U_EMRDELETEOBJECT structure, or NULL on error. + \param ihObject Pointer to handle to delete. This value is set to 0 if the function succeeds. + \param eht EMF handle table + + Note that calling this function should always be conditional on the specifed object being defined. It is easy to + write a program where deleteobject_set() is called in a sequence where, at the time, we know that ihObject is defined. + Then a later modification, possibly quite far away in the code, causes it to be undefined. That distant change will + result in a failure when this function reutrns. That problem cannot be handled here because the only values which + may be returned are a valid U_EMRDELETEOBJECT record or a NULL, and other errors could result in the NULL. + So the object must be checked before the call. +*/ +char *deleteobject_set( + uint32_t *ihObject, + EMFHANDLES *eht + ){ + uint32_t saveObject=*ihObject; + if(htable_delete(ihObject,eht))return(NULL); // invalid handle or other problem, cannot be deleted + return(U_EMRDELETEOBJECT_set(saveObject)); +} + +/** + \brief Allocate and construct a U_EMRSELECTOBJECT structure, checks that the handle specified is one that can actually be selected. + Use this function instead of calling U_EMRSELECTOBJECT_set() directly. + \return pointer to U_EMRSELECTOBJECT structure, or NULL on error. + \param ihObject handle to select + \param eht EMF handle table +*/ +char *selectobject_set( + uint32_t ihObject, + EMFHANDLES *eht + ){ + if(!(U_STOCK_OBJECT & ihObject)){ // not a stock object, those go straight through + if(eht->top < ihObject)return(NULL); // handle this high is not in the table + if(!eht->table[ihObject])return(NULL); // handle is not in the table, so not active, so cannot be selected + } + return(U_EMRSELECTOBJECT_set(ihObject)); +} + +/** + \brief Allocate and construct a U_EMREXTCREATEPEN structure, create a handle and return it. + Use this function instead of calling U_EMREXTCREATEPEN_set() directly. + \return pointer to U_EMREXTCREATEPEN structure, or NULL on error. + \param ihPen handle to be used by new object + \param eht EMF handle table + \param Bmi bitmapbuffer + \param cbPx Size in bytes of pixel array (row stride * height, there may be some padding at the end of each row) + \param Px pixel array (NULL if cbPx == 0) + \param elp Pen parameters (Size is Variable!!!!) +*/ +char *extcreatepen_set( + uint32_t *ihPen, + EMFHANDLES *eht, + PU_BITMAPINFO Bmi, + const uint32_t cbPx, + char *Px, + PU_EXTLOGPEN elp + ){ + if(htable_insert(ihPen, eht))return(NULL); + return(U_EMREXTCREATEPEN_set(*ihPen, Bmi, cbPx, Px, elp )); +} + +/** + \brief Allocate and construct a U_EMRCREATEPEN structure, create a handle and returns it + Use this function instead of calling U_EMRCREATEPEN_set() directly. + \return pointer to U_EMRCREATEPEN structure, or NULL on error. + \param ihPen handle to be used by new object + \param eht EMF handle table + \param lopn Pen parameters +*/ +char *createpen_set( + uint32_t *ihPen, + EMFHANDLES *eht, + U_LOGPEN lopn + ){ + if(htable_insert(ihPen, eht))return(NULL); + return(U_EMRCREATEPEN_set(*ihPen, lopn)); +} + +/** + \brief Allocate and construct a U_EMRCREATEBRUSHINDIRECT structure, create a handle and returns it + Use this function instead of calling U_EMRCREATEBRUSHINDIRECT_set() directly. + \return pointer to U_EMRCREATEBRUSHINDIRECT structure, or NULL on error. + \param ihBrush handle to be used by new object + \param eht EMF handle table + \param lb Brush parameters +*/ +char *createbrushindirect_set( + uint32_t *ihBrush, + EMFHANDLES *eht, + U_LOGBRUSH lb + ){ + if(htable_insert(ihBrush, eht))return(NULL); + return(U_EMRCREATEBRUSHINDIRECT_set(*ihBrush, lb)); +} + +/** + \brief Allocate and construct a U_EMRCREATEDIBPATTERNBRUSHPT_set structure, create a handle and returns it + Use this function instead of calling U_EMRCREATEDIBPATTERNBRUSHPT_set() directly. + \return pointer to U_EMRCREATEDIBPATTERNBRUSHPT_set structure, or NULL on error. + \param ihBrush handle to be used by new object + \param eht EMF handle table + \param iUsage DIBColors enumeration + \param Bmi Bitmap info + \param cbPx Size in bytes of pixel array (row stride * height, there may be some padding at the end of each row) + \param Px (Optional) bitmapbuffer (pixel array section ) +*/ +char *createdibpatternbrushpt_set( + uint32_t *ihBrush, + EMFHANDLES *eht, + const uint32_t iUsage, + PU_BITMAPINFO Bmi, + const uint32_t cbPx, + const char *Px + + ){ + if(htable_insert(ihBrush, eht))return(NULL); + return(U_EMRCREATEDIBPATTERNBRUSHPT_set(*ihBrush, iUsage, Bmi, cbPx, Px)); +} + +/** + \brief Allocate and construct a U_EMRCREATEMONOBRUSH_set structure, create a handle and returns it + Use this function instead of calling U_EMRCREATEMONOBRUSH_set() directly. + \return pointer to U_EMRCREATEMONOBRUSH_set structure, or NULL on error. + \param ihBrush handle to be used by new object + \param eht EMF handle table + \param iUsage DIBColors enumeration + \param Bmi Bitmap info + \param cbPx Size in bytes of pixel array (row stride * height, there may be some padding at the end of each row) + \param Px (Optional) bitmapbuffer (pixel array section ) +*/ +char *createmonobrush_set( + uint32_t *ihBrush, + EMFHANDLES *eht, + const uint32_t iUsage, + PU_BITMAPINFO Bmi, + const uint32_t cbPx, + const char *Px + + ){ + if(htable_insert(ihBrush, eht))return(NULL); + return(U_EMRCREATEMONOBRUSH_set(*ihBrush, iUsage, Bmi, cbPx, Px)); +} + + +/** + \brief Allocate and construct a U_EMRCREATECOLORSPACE structure, create a handle and returns it + Use this function instead of calling U_EMRCREATECOLORSPACE_set() directly. + \return pointer to U_EMRCREATECOLORSPACE structure, or NULL on error. + \param ihCS ColorSpace handle, will be created and returned + \param eht Pointer to structure holding all EMF handles + \param lcs ColorSpace parameters +*/ +char *createcolorspace_set( + uint32_t *ihCS, + EMFHANDLES *eht, + U_LOGCOLORSPACEA lcs + ){ + if(htable_insert(ihCS, eht))return(NULL); + return(U_EMRCREATECOLORSPACE_set(*ihCS,lcs)); +} + +/** + \brief Allocate and construct a U_EMRCREATECOLORSPACEW structure, create a handle and returns it + Use this function instead of calling U_EMRCREATECOLORSPACEW_set() directly. + \return pointer to U_EMRCREATECOLORSPACEW structure, or NULL on error. + \param ihCS ColorSpace handle, will be created and returned + \param eht Pointer to structure holding all EMF handles + \param lcs ColorSpace parameters + \param dwFlags If low bit set Data is present + \param cbData Number of bytes of theData field. + \param Data (Optional, dwFlags & 1) color profile data +*/ +char *createcolorspacew_set( + uint32_t *ihCS, + EMFHANDLES *eht, + U_LOGCOLORSPACEW lcs, + uint32_t dwFlags, + U_CBDATA cbData, + uint8_t *Data + ){ + if(htable_insert(ihCS, eht))return(NULL); + return(U_EMRCREATECOLORSPACEW_set(*ihCS, lcs, dwFlags, cbData, Data)); +} + +/** + \brief Allocate and construct a U_EMREXTCREATEFONTINDIRECTW structure, create a handle and returns it + Use this function instead of calling U_EMREXTCREATEFONTINDIRECTW_set() directly. + \return pointer to U_EMREXTCREATEFONTINDIRECTW structure, or NULL on error. + \param ihFont Font handle, will be created and returned + \param eht Pointer to structure holding all EMF handles + \param elf Pointer to Font parameters asPU_LOGFONT + \param elfw Pointer to Font parameters as U_LOGFONT_PANOSE +*/ +char *extcreatefontindirectw_set( + uint32_t *ihFont, + EMFHANDLES *eht, + const char *elf, + const char *elfw + ){ + if(htable_insert(ihFont, eht))return(NULL); + return(U_EMREXTCREATEFONTINDIRECTW_set(*ihFont, elf, elfw)); +} + +/** + \brief Allocate and construct a U_EMRCREATEPALETTE structure, create a handle and returns it + Use this function instead of calling U_EMRCREATEPALETTE_set() directly. + \return pointer to U_EMRCREATEPALETTE structure, or NULL on error. + \param ihPal Palette handle, will be created and returned + \param eht Pointer to structure holding all EMF handles + \param lgpl PaletteFont parameters +*/ +char *createpalette_set( + uint32_t *ihPal, + EMFHANDLES *eht, + U_LOGPALETTE lgpl + ){ + if(htable_insert(ihPal, eht))return(NULL); + return(U_EMRCREATEPALETTE_set(*ihPal, lgpl)); +} + +/** + \brief Allocate and construct a U_EMRSETPALETTEENTRIES structure, create a handle and returns it + Use this function instead of calling U_EMRSETPALETTEENTRIES_set() directly. + \return pointer to U_EMRSETPALETTEENTRIES structure, or NULL on error. + \param ihPal Palette handle, will be created and returned + \param eht Pointer to structure holding all EMF handles + \param iStart First Palette entry in selected object to set + \param cEntries Number of Palette entries in selected object to set + \param aPalEntries Values to set with +*/ +char *setpaletteentries_set( + uint32_t *ihPal, + EMFHANDLES *eht, + const uint32_t iStart, + const U_NUM_LOGPLTNTRY cEntries, + const PU_LOGPLTNTRY aPalEntries + ){ + if(htable_insert(ihPal, eht))return(NULL); + return(U_EMRSETPALETTEENTRIES_set(*ihPal, iStart, cEntries, aPalEntries)); +} + +/** + \brief Allocate and construct a U_EMRFILLRGN structure, create a handle and returns it + Use this function instead of calling U_EMRFILLRGN_set() directly. + \return pointer to U_EMRFILLRGN structure, or NULL on error. + \param ihBrush Brush handle, will be created and returned + \param eht Pointer to structure holding all EMF handles + \param rclBounds Bounding rectangle in device units + \param RgnData Pointer to a U_RGNDATA structure +*/ +char *fillrgn_set( + uint32_t *ihBrush, + EMFHANDLES *eht, + const U_RECTL rclBounds, + const PU_RGNDATA RgnData + ){ + if(htable_insert(ihBrush, eht))return(NULL); + return(U_EMRFILLRGN_set(rclBounds, *ihBrush, RgnData)); +} + +/** + \brief Allocate and construct a U_EMRFRAMERGN structure, create a handle and returns it + Use this function instead of calling U_EMRFRAMERGN_set() directly. + \return pointer to U_EMRFRAMERGN structure, or NULL on error. + \param ihBrush Brush handle, will be created and returned + \param eht Pointer to structure holding all EMF handles + \param rclBounds Bounding rectangle in device units + \param szlStroke W & H of Brush stroke + \param RgnData Pointer to a U_RGNDATA structure +*/ +char *framergn_set( + uint32_t *ihBrush, + EMFHANDLES *eht, + const U_RECTL rclBounds, + const U_SIZEL szlStroke, + const PU_RGNDATA RgnData + ){ + if(htable_insert(ihBrush, eht))return(NULL); + return(U_EMRFRAMERGN_set(rclBounds, *ihBrush, szlStroke, RgnData)); +} + +/** + \brief Allocate and construct an array of U_POINT objects which has been subjected to a U_XFORM + \returns pointer to an array of U_POINT structures. + \param points pointer to the source U_POINT structures + \param count number of members in points + \param xform U_XFORM to apply + + May also be used to modify U_RECT by doubling the count and casting the pointer. +*/ +PU_POINT points_transform(PU_POINT points, int count, U_XFORM xform){ + PU_POINT newpts; + int i; + float x,y; + newpts = (PU_POINT) malloc(count * sizeof(U_POINT)); + for(i=0; i