summaryrefslogtreecommitdiffstats
path: root/src/extension/internal
diff options
context:
space:
mode:
authorMartin Owens <doctormo@gmail.com>2014-03-27 01:33:44 +0000
committerMartin Owens <doctormo@gmail.com>2014-03-27 01:33:44 +0000
commit5a4fb2325f60d292b47330f540b26a3279341c90 (patch)
treed2aa7967be25450b83e625025366c618101ae49f /src/extension/internal
parentThe Polar Arrange Tab of the Arrange Dialog now hides the parametric (diff)
parentRemove Snap menu item and improve grid menu item text (diff)
downloadinkscape-5a4fb2325f60d292b47330f540b26a3279341c90.tar.gz
inkscape-5a4fb2325f60d292b47330f540b26a3279341c90.zip
Commit a merge to trunk, with probabal errors
(bzr r11073.1.36)
Diffstat (limited to 'src/extension/internal')
-rw-r--r--src/extension/internal/Makefile_insert48
-rw-r--r--src/extension/internal/bitmap/despeckle.cpp2
-rw-r--r--src/extension/internal/bitmap/enhance.cpp2
-rw-r--r--src/extension/internal/bitmap/equalize.cpp2
-rw-r--r--src/extension/internal/bitmap/imagemagick.cpp8
-rw-r--r--src/extension/internal/bitmap/negate.cpp2
-rw-r--r--src/extension/internal/bitmap/normalize.cpp2
-rw-r--r--src/extension/internal/bluredge.cpp7
-rw-r--r--src/extension/internal/cairo-png-out.cpp2
-rw-r--r--src/extension/internal/cairo-ps-out.cpp148
-rw-r--r--src/extension/internal/cairo-render-context.cpp165
-rw-r--r--src/extension/internal/cairo-render-context.h27
-rw-r--r--src/extension/internal/cairo-renderer-pdf-out.cpp83
-rw-r--r--src/extension/internal/cairo-renderer.cpp73
-rw-r--r--src/extension/internal/cairo-renderer.h6
-rw-r--r--src/extension/internal/cdr-input.cpp339
-rw-r--r--src/extension/internal/cdr-input.h58
-rw-r--r--src/extension/internal/emf-inout.cpp3547
-rw-r--r--src/extension/internal/emf-inout.h183
-rw-r--r--src/extension/internal/emf-print.cpp2061
-rw-r--r--src/extension/internal/emf-print.h99
-rw-r--r--src/extension/internal/emf-win32-inout.cpp2524
-rw-r--r--src/extension/internal/emf-win32-inout.h57
-rw-r--r--src/extension/internal/emf-win32-print.cpp948
-rw-r--r--src/extension/internal/filter/bevels.h22
-rw-r--r--src/extension/internal/filter/blurs.h26
-rw-r--r--src/extension/internal/filter/bumps.h71
-rw-r--r--src/extension/internal/filter/color.h322
-rw-r--r--src/extension/internal/filter/distort.h28
-rw-r--r--[-rwxr-xr-x]src/extension/internal/filter/filter-all.cpp3
-rw-r--r--src/extension/internal/filter/filter-file.cpp3
-rw-r--r--src/extension/internal/filter/filter.cpp4
-rw-r--r--src/extension/internal/filter/filter.h2
-rw-r--r--src/extension/internal/filter/image.h2
-rw-r--r--src/extension/internal/filter/morphology.h24
-rw-r--r--src/extension/internal/filter/paint.h143
-rw-r--r--src/extension/internal/filter/protrusions.h2
-rw-r--r--src/extension/internal/filter/shadows.h19
-rw-r--r--src/extension/internal/filter/transparency.h22
-rw-r--r--src/extension/internal/gdkpixbuf-input.cpp163
-rw-r--r--src/extension/internal/grid.cpp51
-rw-r--r--src/extension/internal/image-resolution.cpp380
-rw-r--r--src/extension/internal/image-resolution.h41
-rw-r--r--src/extension/internal/javafx-out.cpp24
-rw-r--r--src/extension/internal/latex-pstricks.cpp19
-rw-r--r--src/extension/internal/latex-text-renderer.cpp55
-rw-r--r--src/extension/internal/latex-text-renderer.h17
-rw-r--r--src/extension/internal/metafile-inout.cpp211
-rw-r--r--src/extension/internal/metafile-inout.h93
-rw-r--r--src/extension/internal/metafile-print.cpp493
-rw-r--r--src/extension/internal/metafile-print.h134
-rw-r--r--src/extension/internal/odf.cpp1315
-rw-r--r--src/extension/internal/odf.h27
-rw-r--r--src/extension/internal/pdf-input-cairo.cpp66
-rw-r--r--src/extension/internal/pdf-input-cairo.h19
-rw-r--r--src/extension/internal/pdfinput/pdf-input.cpp63
-rw-r--r--src/extension/internal/pdfinput/pdf-input.h19
-rw-r--r--src/extension/internal/pdfinput/pdf-parser.cpp824
-rw-r--r--src/extension/internal/pdfinput/svg-builder.cpp112
-rw-r--r--src/extension/internal/pdfinput/svg-builder.h10
-rw-r--r--src/extension/internal/svg.cpp2
-rw-r--r--src/extension/internal/text_reassemble.c2926
-rw-r--r--src/extension/internal/text_reassemble.h387
-rw-r--r--src/extension/internal/vsd-input.cpp339
-rw-r--r--src/extension/internal/vsd-input.h58
-rw-r--r--src/extension/internal/wmf-inout.cpp3135
-rw-r--r--src/extension/internal/wmf-inout.h180
-rw-r--r--src/extension/internal/wmf-print.cpp1578
-rw-r--r--src/extension/internal/wmf-print.h (renamed from src/extension/internal/emf-win32-print.h)72
-rw-r--r--src/extension/internal/wpg-input.cpp8
70 files changed, 18444 insertions, 5463 deletions
diff --git a/src/extension/internal/Makefile_insert b/src/extension/internal/Makefile_insert
index 06fa275cb..a31843114 100644
--- a/src/extension/internal/Makefile_insert
+++ b/src/extension/internal/Makefile_insert
@@ -4,7 +4,18 @@ if WITH_LIBWPG
ink_common_sources += \
extension/internal/wpg-input.cpp \
extension/internal/wpg-input.h
+endif
+
+if WITH_LIBVISIO
+ink_common_sources += \
+ extension/internal/vsd-input.cpp \
+ extension/internal/vsd-input.h
+endif
+if WITH_LIBCDR
+ink_common_sources += \
+ extension/internal/cdr-input.cpp \
+ extension/internal/cdr-input.h
endif
if USE_IMAGE_MAGICK
@@ -127,13 +138,38 @@ ink_common_sources += \
extension/internal/latex-pstricks.h \
extension/internal/latex-pstricks-out.cpp \
extension/internal/latex-pstricks-out.h \
+ extension/internal/filter/bevels.h \
+ extension/internal/filter/blurs.h \
+ extension/internal/filter/bumps.h \
+ extension/internal/filter/color.h \
+ extension/internal/filter/distort.h \
+ extension/internal/filter/filter.h \
+ extension/internal/filter/image.h \
+ extension/internal/filter/morphology.h \
+ extension/internal/filter/overlays.h \
+ extension/internal/filter/paint.h \
+ extension/internal/filter/protrusions.h \
+ extension/internal/filter/shadows.h \
+ extension/internal/filter/textures.h \
+ extension/internal/filter/transparency.h \
extension/internal/filter/filter-all.cpp \
extension/internal/filter/filter-file.cpp \
extension/internal/filter/filter.cpp \
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/text_reassemble.c \
+ extension/internal/text_reassemble.h \
+ extension/internal/emf-print.h \
+ extension/internal/emf-print.cpp \
+ extension/internal/emf-inout.h \
+ extension/internal/emf-inout.cpp \
+ extension/internal/metafile-inout.h \
+ extension/internal/metafile-inout.cpp \
+ extension/internal/metafile-print.h \
+ extension/internal/metafile-print.cpp \
+ extension/internal/wmf-print.h \
+ extension/internal/wmf-print.cpp \
+ extension/internal/wmf-inout.h \
+ extension/internal/wmf-inout.cpp \
+ extension/internal/image-resolution.h \
+ extension/internal/image-resolution.cpp
+
diff --git a/src/extension/internal/bitmap/despeckle.cpp b/src/extension/internal/bitmap/despeckle.cpp
index f818ef81a..c40e6e227 100644
--- a/src/extension/internal/bitmap/despeckle.cpp
+++ b/src/extension/internal/bitmap/despeckle.cpp
@@ -23,7 +23,7 @@ Despeckle::applyEffect(Magick::Image *image) {
}
void
-Despeckle::refreshParameters(Inkscape::Extension::Effect *module) {
+Despeckle::refreshParameters(Inkscape::Extension::Effect */*module*/) {
}
#include "../clear-n_.h"
diff --git a/src/extension/internal/bitmap/enhance.cpp b/src/extension/internal/bitmap/enhance.cpp
index 0bd362e8c..5b06a2d27 100644
--- a/src/extension/internal/bitmap/enhance.cpp
+++ b/src/extension/internal/bitmap/enhance.cpp
@@ -23,7 +23,7 @@ Enhance::applyEffect(Magick::Image *image) {
}
void
-Enhance::refreshParameters(Inkscape::Extension::Effect *module) { }
+Enhance::refreshParameters(Inkscape::Extension::Effect */*module*/) { }
#include "../clear-n_.h"
diff --git a/src/extension/internal/bitmap/equalize.cpp b/src/extension/internal/bitmap/equalize.cpp
index b0726b61a..6098f74b1 100644
--- a/src/extension/internal/bitmap/equalize.cpp
+++ b/src/extension/internal/bitmap/equalize.cpp
@@ -23,7 +23,7 @@ Equalize::applyEffect(Magick::Image *image) {
}
void
-Equalize::refreshParameters(Inkscape::Extension::Effect *module) { }
+Equalize::refreshParameters(Inkscape::Extension::Effect */*module*/) { }
#include "../clear-n_.h"
diff --git a/src/extension/internal/bitmap/imagemagick.cpp b/src/extension/internal/bitmap/imagemagick.cpp
index 728c38dea..0d47240d4 100644
--- a/src/extension/internal/bitmap/imagemagick.cpp
+++ b/src/extension/internal/bitmap/imagemagick.cpp
@@ -8,6 +8,14 @@
* Released under GNU GPL, read the file 'COPYING' for more information
*/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H
+#include <glibmm/threads.h>
+#endif
+
#include <libintl.h>
#include <gtkmm/box.h>
diff --git a/src/extension/internal/bitmap/negate.cpp b/src/extension/internal/bitmap/negate.cpp
index 2e564821e..4737e6443 100644
--- a/src/extension/internal/bitmap/negate.cpp
+++ b/src/extension/internal/bitmap/negate.cpp
@@ -23,7 +23,7 @@ Negate::applyEffect(Magick::Image* image) {
}
void
-Negate::refreshParameters(Inkscape::Extension::Effect* module) {
+Negate::refreshParameters(Inkscape::Extension::Effect* /*module*/) {
}
#include "../clear-n_.h"
diff --git a/src/extension/internal/bitmap/normalize.cpp b/src/extension/internal/bitmap/normalize.cpp
index 591fe1fdd..1444aa654 100644
--- a/src/extension/internal/bitmap/normalize.cpp
+++ b/src/extension/internal/bitmap/normalize.cpp
@@ -23,7 +23,7 @@ Normalize::applyEffect(Magick::Image* image) {
}
void
-Normalize::refreshParameters(Inkscape::Extension::Effect* module) {
+Normalize::refreshParameters(Inkscape::Extension::Effect* /*module*/) {
}
#include "../clear-n_.h"
diff --git a/src/extension/internal/bluredge.cpp b/src/extension/internal/bluredge.cpp
index 8f1e07211..3ce537d9f 100644
--- a/src/extension/internal/bluredge.cpp
+++ b/src/extension/internal/bluredge.cpp
@@ -17,6 +17,7 @@
#include "document.h"
#include "selection.h"
#include "helper/action.h"
+#include "helper/action-context.h"
#include "preferences.h"
#include "path-chemistry.h"
#include "sp-item.h"
@@ -93,16 +94,16 @@ BlurEdge::effect (Inkscape::Extension::Effect *module, Inkscape::UI::View::View
new_group->appendChild(new_items[i]);
selection->add(new_items[i]);
- sp_selected_path_to_curves(static_cast<SPDesktop *>(desktop));
+ sp_selected_path_to_curves(selection, static_cast<SPDesktop *>(desktop));
if (offset < 0.0) {
/* Doing an inset here folks */
offset *= -1.0;
prefs->setDoubleUnit("/options/defaultoffsetwidth/value", offset, "px");
- sp_action_perform(Inkscape::Verb::get(SP_VERB_SELECTION_INSET)->get_action(desktop), NULL);
+ sp_action_perform(Inkscape::Verb::get(SP_VERB_SELECTION_INSET)->get_action(Inkscape::ActionContext(desktop)), NULL);
} else if (offset > 0.0) {
prefs->setDoubleUnit("/options/defaultoffsetwidth/value", offset, "px");
- sp_action_perform(Inkscape::Verb::get(SP_VERB_SELECTION_OFFSET)->get_action(desktop), NULL);
+ sp_action_perform(Inkscape::Verb::get(SP_VERB_SELECTION_OFFSET)->get_action(Inkscape::ActionContext(desktop)), NULL);
}
selection->clear();
diff --git a/src/extension/internal/cairo-png-out.cpp b/src/extension/internal/cairo-png-out.cpp
index 4751a229a..956fcce9a 100644
--- a/src/extension/internal/cairo-png-out.cpp
+++ b/src/extension/internal/cairo-png-out.cpp
@@ -68,7 +68,7 @@ png_render_document_to_file(SPDocument *doc, gchar const *filename)
ctx = renderer->createContext();
/* Render document */
- bool ret = renderer->setupDocument(ctx, doc, TRUE, NULL);
+ bool ret = renderer->setupDocument(ctx, doc, TRUE, 0., NULL);
if (ret) {
renderer->renderItem(ctx, base);
ctx->saveAsPng(filename);
diff --git a/src/extension/internal/cairo-ps-out.cpp b/src/extension/internal/cairo-ps-out.cpp
index 8b1b3fbfd..055a30add 100644
--- a/src/extension/internal/cairo-ps-out.cpp
+++ b/src/extension/internal/cairo-ps-out.cpp
@@ -66,7 +66,7 @@ bool CairoEpsOutput::check (Inkscape::Extension::Extension * /*module*/)
static bool
ps_print_document_to_file(SPDocument *doc, gchar const *filename, unsigned int level, bool texttopath, bool omittext,
- bool filtertobitmap, int resolution, const gchar * const exportId, bool exportDrawing, bool exportCanvas, bool eps = false)
+ bool filtertobitmap, int resolution, const gchar * const exportId, bool exportDrawing, bool exportCanvas, float bleedmargin_px, bool eps = false)
{
doc->ensureUpToDate();
@@ -97,14 +97,14 @@ ps_print_document_to_file(SPDocument *doc, gchar const *filename, unsigned int l
ctx->setPSLevel(level);
ctx->setEPS(eps);
ctx->setTextToPath(texttopath);
- renderer->_omitText = omittext;
+ ctx->setOmitText(omittext);
ctx->setFilterToBitmap(filtertobitmap);
ctx->setBitmapResolution(resolution);
bool ret = ctx->setPsTarget(filename);
if(ret) {
/* Render document */
- ret = renderer->setupDocument(ctx, doc, pageBoundingBox, base);
+ ret = renderer->setupDocument(ctx, doc, pageBoundingBox, bleedmargin_px, base);
if (ret) {
renderer->renderItem(ctx, base);
ret = ctx->finish();
@@ -136,12 +136,12 @@ CairoPsOutput::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar con
if (ext == NULL)
return;
- const gchar *new_level = NULL;
int level = CAIRO_PS_LEVEL_2;
try {
- new_level = mod->get_param_enum("PSlevel");
- if((new_level != NULL) && (g_ascii_strcasecmp("PS3", new_level) == 0))
+ const gchar *new_level = mod->get_param_enum("PSlevel");
+ if((new_level != NULL) && (g_ascii_strcasecmp("PS3", new_level) == 0)) {
level = CAIRO_PS_LEVEL_3;
+ }
} catch(...) {}
bool new_textToPath = FALSE;
@@ -169,12 +169,14 @@ CairoPsOutput::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar con
bool new_areaPage = true;
try {
- new_areaPage = mod->get_param_bool("areaPage");
+ new_areaPage = (strcmp(ext->get_param_optiongroup("area"), "page") == 0);
} catch(...) {}
- bool new_areaDrawing = true;
+ bool new_areaDrawing = !new_areaPage;
+
+ float bleedmargin_px = 0.;
try {
- new_areaDrawing = mod->get_param_bool("areaDrawing");
+ bleedmargin_px = ext->get_param_float("bleed");
} catch(...) {}
const gchar *new_exportId = NULL;
@@ -186,7 +188,11 @@ CairoPsOutput::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar con
{
gchar * final_name;
final_name = g_strdup_printf("> %s", filename);
- ret = ps_print_document_to_file(doc, final_name, level, new_textToPath, new_textToLaTeX, new_blurToBitmap, new_bitmapResolution, new_exportId, new_areaDrawing, new_areaPage);
+ ret = ps_print_document_to_file(doc, final_name, level, new_textToPath,
+ new_textToLaTeX, new_blurToBitmap,
+ new_bitmapResolution, new_exportId,
+ new_areaDrawing, new_areaPage,
+ bleedmargin_px);
g_free(final_name);
if (!ret)
@@ -195,7 +201,7 @@ CairoPsOutput::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar con
// Create LaTeX file (if requested)
if (new_textToLaTeX) {
- ret = latex_render_document_text_to_file(doc, filename, new_exportId, new_areaDrawing, new_areaPage, false);
+ ret = latex_render_document_text_to_file(doc, filename, new_exportId, new_areaDrawing, new_areaPage, 0., false);
if (!ret)
throw Inkscape::Extension::Output::save_failed();
@@ -219,12 +225,12 @@ CairoEpsOutput::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar co
if (ext == NULL)
return;
- const gchar *new_level = NULL;
int level = CAIRO_PS_LEVEL_2;
try {
- new_level = mod->get_param_enum("PSlevel");
- if((new_level != NULL) && (g_ascii_strcasecmp("PS3", new_level) == 0))
+ const gchar *new_level = mod->get_param_enum("PSlevel");
+ if((new_level != NULL) && (g_ascii_strcasecmp("PS3", new_level) == 0)) {
level = CAIRO_PS_LEVEL_3;
+ }
} catch(...) {}
bool new_textToPath = FALSE;
@@ -252,12 +258,14 @@ CairoEpsOutput::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar co
bool new_areaPage = true;
try {
- new_areaPage = mod->get_param_bool("areaPage");
+ new_areaPage = (strcmp(ext->get_param_optiongroup("area"), "page") == 0);
} catch(...) {}
- bool new_areaDrawing = true;
+ bool new_areaDrawing = !new_areaPage;
+
+ float bleedmargin_px = 0.;
try {
- new_areaDrawing = mod->get_param_bool("areaDrawing");
+ bleedmargin_px = ext->get_param_float("bleed");
} catch(...) {}
const gchar *new_exportId = NULL;
@@ -269,7 +277,11 @@ CairoEpsOutput::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar co
{
gchar * final_name;
final_name = g_strdup_printf("> %s", filename);
- ret = ps_print_document_to_file(doc, final_name, level, new_textToPath, new_textToLaTeX, new_blurToBitmap, new_bitmapResolution, new_exportId, new_areaDrawing, new_areaPage, true);
+ ret = ps_print_document_to_file(doc, final_name, level, new_textToPath,
+ new_textToLaTeX, new_blurToBitmap,
+ new_bitmapResolution, new_exportId,
+ new_areaDrawing, new_areaPage,
+ bleedmargin_px, true);
g_free(final_name);
if (!ret)
@@ -278,7 +290,7 @@ CairoEpsOutput::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar co
// Create LaTeX file (if requested)
if (new_textToLaTeX) {
- ret = latex_render_document_text_to_file(doc, filename, new_exportId, new_areaDrawing, new_areaPage, false);
+ ret = latex_render_document_text_to_file(doc, filename, new_exportId, new_areaDrawing, new_areaPage, 0., false);
if (!ret)
throw Inkscape::Extension::Output::save_failed();
@@ -310,32 +322,33 @@ CairoEpsOutput::textToPath(Inkscape::Extension::Print * ext)
void
CairoPsOutput::init (void)
{
- Inkscape::Extension::build_from_mem(
- "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
- "<name>" N_("PostScript") "</name>\n"
- "<id>" SP_MODULE_KEY_PRINT_CAIRO_PS "</id>\n"
- "<param name=\"PSlevel\" gui-text=\"" N_("Restrict to PS level:") "\" type=\"enum\" >\n"
- "<_item value='PS3'>" N_("PostScript level 3") "</_item>\n"
-#if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2))
+ Inkscape::Extension::build_from_mem(
+ "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
+ "<name>" N_("PostScript") "</name>\n"
+ "<id>" SP_MODULE_KEY_PRINT_CAIRO_PS "</id>\n"
+ "<param name=\"PSlevel\" gui-text=\"" N_("Restrict to PS level:") "\" type=\"enum\" >\n"
+ "<_item value='PS3'>" N_("PostScript level 3") "</_item>\n"
"<_item value='PS2'>" N_("PostScript level 2") "</_item>\n"
-#endif
"</param>\n"
- "<param name=\"textToPath\" gui-text=\"" N_("Convert texts to paths") "\" type=\"boolean\">false</param>\n"
- "<param name=\"textToLaTeX\" gui-text=\"" N_("PS+LaTeX: Omit text in PS, and create LaTeX file") "\" type=\"boolean\">false</param>\n"
- "<param name=\"blurToBitmap\" gui-text=\"" N_("Rasterize filter effects") "\" type=\"boolean\">true</param>\n"
- "<param name=\"resolution\" gui-text=\"" N_("Resolution for rasterization (dpi):") "\" type=\"int\" min=\"1\" max=\"10000\">90</param>\n"
- "<param name=\"areaDrawing\" gui-text=\"" N_("Export area is drawing") "\" type=\"boolean\">true</param>\n"
- "<param name=\"areaPage\" gui-text=\"" N_("Export area is page") "\" type=\"boolean\">true</param>\n"
- "<param name=\"exportId\" gui-text=\"" N_("Limit export to the object with ID:") "\" type=\"string\"></param>\n"
- "<output>\n"
- "<extension>.ps</extension>\n"
- "<mimetype>image/x-postscript</mimetype>\n"
- "<filetypename>" N_("PostScript (*.ps)") "</filetypename>\n"
- "<filetypetooltip>" N_("PostScript File") "</filetypetooltip>\n"
- "</output>\n"
- "</inkscape-extension>", new CairoPsOutput());
-
- return;
+ "<param name=\"textToPath\" gui-text=\"" N_("Convert texts to paths") "\" type=\"boolean\">false</param>\n"
+ "<param name=\"textToLaTeX\" gui-text=\"" N_("PS+LaTeX: Omit text in PS, and create LaTeX file") "\" type=\"boolean\">false</param>\n"
+ "<param name=\"blurToBitmap\" gui-text=\"" N_("Rasterize filter effects") "\" type=\"boolean\">true</param>\n"
+ "<param name=\"resolution\" gui-text=\"" N_("Resolution for rasterization (dpi):") "\" type=\"int\" min=\"1\" max=\"10000\">90</param>\n"
+ "<param name=\"area\" gui-text=\"" N_("Output page size") "\" type=\"optiongroup\" >\n"
+ "<_option value=\"page\">" N_("Use document's page size") "</_option>"
+ "<_option value=\"drawing\">" N_("Use exported object's size") "</_option>"
+ "</param>"
+ "<param name=\"bleed\" gui-text=\"" N_("Bleed/margin (mm):") "\" type=\"float\" min=\"-10000\" max=\"10000\">0</param>\n"
+ "<param name=\"exportId\" gui-text=\"" N_("Limit export to the object with ID:") "\" type=\"string\"></param>\n"
+ "<output>\n"
+ "<extension>.ps</extension>\n"
+ "<mimetype>image/x-postscript</mimetype>\n"
+ "<filetypename>" N_("PostScript (*.ps)") "</filetypename>\n"
+ "<filetypetooltip>" N_("PostScript File") "</filetypetooltip>\n"
+ "</output>\n"
+ "</inkscape-extension>", new CairoPsOutput());
+
+ return;
}
/**
@@ -348,32 +361,33 @@ CairoPsOutput::init (void)
void
CairoEpsOutput::init (void)
{
- Inkscape::Extension::build_from_mem(
- "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
- "<name>" N_("Encapsulated PostScript") "</name>\n"
- "<id>" SP_MODULE_KEY_PRINT_CAIRO_EPS "</id>\n"
- "<param name=\"PSlevel\" gui-text=\"" N_("Restrict to PS level:") "\" type=\"enum\" >\n"
- "<_item value='PS3'>" N_("PostScript level 3") "</_item>\n"
-#if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2))
+ Inkscape::Extension::build_from_mem(
+ "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
+ "<name>" N_("Encapsulated PostScript") "</name>\n"
+ "<id>" SP_MODULE_KEY_PRINT_CAIRO_EPS "</id>\n"
+ "<param name=\"PSlevel\" gui-text=\"" N_("Restrict to PS level:") "\" type=\"enum\" >\n"
+ "<_item value='PS3'>" N_("PostScript level 3") "</_item>\n"
"<_item value='PS2'>" N_("PostScript level 2") "</_item>\n"
-#endif
"</param>\n"
- "<param name=\"textToPath\" gui-text=\"" N_("Convert texts to paths") "\" type=\"boolean\">false</param>\n"
- "<param name=\"textToLaTeX\" gui-text=\"" N_("EPS+LaTeX: Omit text in EPS, and create LaTeX file") "\" type=\"boolean\">false</param>\n"
- "<param name=\"blurToBitmap\" gui-text=\"" N_("Rasterize filter effects") "\" type=\"boolean\">true</param>\n"
- "<param name=\"resolution\" gui-text=\"" N_("Resolution for rasterization (dpi):") "\" type=\"int\" min=\"1\" max=\"10000\">90</param>\n"
- "<param name=\"areaDrawing\" gui-text=\"" N_("Export area is drawing") "\" type=\"boolean\">true</param>\n"
- "<param name=\"areaPage\" gui-text=\"" N_("Export area is page") "\" type=\"boolean\">true</param>\n"
- "<param name=\"exportId\" gui-text=\"" N_("Limit export to the object with ID:") "\" type=\"string\"></param>\n"
- "<output>\n"
- "<extension>.eps</extension>\n"
- "<mimetype>image/x-e-postscript</mimetype>\n"
- "<filetypename>" N_("Encapsulated PostScript (*.eps)") "</filetypename>\n"
- "<filetypetooltip>" N_("Encapsulated PostScript File") "</filetypetooltip>\n"
- "</output>\n"
- "</inkscape-extension>", new CairoEpsOutput());
-
- return;
+ "<param name=\"textToPath\" gui-text=\"" N_("Convert texts to paths") "\" type=\"boolean\">false</param>\n"
+ "<param name=\"textToLaTeX\" gui-text=\"" N_("EPS+LaTeX: Omit text in EPS, and create LaTeX file") "\" type=\"boolean\">false</param>\n"
+ "<param name=\"blurToBitmap\" gui-text=\"" N_("Rasterize filter effects") "\" type=\"boolean\">true</param>\n"
+ "<param name=\"resolution\" gui-text=\"" N_("Resolution for rasterization (dpi):") "\" type=\"int\" min=\"1\" max=\"10000\">90</param>\n"
+ "<param name=\"area\" gui-text=\"" N_("Output page size") "\" type=\"optiongroup\" >\n"
+ "<_option value=\"page\">" N_("Use document's page size") "</_option>"
+ "<_option value=\"drawing\">" N_("Use exported object's size") "</_option>"
+ "</param>"
+ "<param name=\"bleed\" gui-text=\"" N_("Bleed/margin (mm)") "\" type=\"float\" min=\"-10000\" max=\"10000\">0</param>\n"
+ "<param name=\"exportId\" gui-text=\"" N_("Limit export to the object with ID:") "\" type=\"string\"></param>\n"
+ "<output>\n"
+ "<extension>.eps</extension>\n"
+ "<mimetype>image/x-e-postscript</mimetype>\n"
+ "<filetypename>" N_("Encapsulated PostScript (*.eps)") "</filetypename>\n"
+ "<filetypetooltip>" N_("Encapsulated PostScript File") "</filetypetooltip>\n"
+ "</output>\n"
+ "</inkscape-extension>", new CairoEpsOutput());
+
+ return;
}
} } } /* namespace Inkscape, Extension, Implementation */
diff --git a/src/extension/internal/cairo-render-context.cpp b/src/extension/internal/cairo-render-context.cpp
index b6a58c526..c09b8e9c8 100644
--- a/src/extension/internal/cairo-render-context.cpp
+++ b/src/extension/internal/cairo-render-context.cpp
@@ -44,12 +44,11 @@
#include "sp-pattern.h"
#include "sp-mask.h"
#include "sp-clippath.h"
+#include "util/units.h"
#ifdef WIN32
#include "libnrtype/FontFactory.h" // USE_PANGO_WIN32
#endif
-#include <unit-constants.h>
-
#include "cairo-render-context.h"
#include "cairo-renderer.h"
#include "extension/system.h"
@@ -73,8 +72,8 @@
#include <cairo-ft.h>
#endif
#ifdef CAIRO_HAS_WIN32_FONT
-#include <cairo-win32.h>
#include <pango/pangowin32.h>
+#include <cairo-win32.h>
#endif
#include <pango/pangofc-fontmap.h>
@@ -112,6 +111,7 @@ CairoRenderContext::CairoRenderContext(CairoRenderer *parent) :
_ps_level(1),
_eps(false),
_is_texttopath(FALSE),
+ _is_omittext(FALSE),
_is_filtertobitmap(FALSE),
_bitmapresolution(72),
_stream(NULL),
@@ -125,7 +125,8 @@ CairoRenderContext::CairoRenderContext(CairoRenderer *parent) :
_state(NULL),
_renderer(parent),
_render_mode(RENDER_MODE_NORMAL),
- _clip_mode(CLIP_MODE_MASK)
+ _clip_mode(CLIP_MODE_MASK),
+ _omittext_state(EMPTY)
{
font_table = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, font_data_free);
}
@@ -427,6 +428,16 @@ void CairoRenderContext::setTextToPath(bool texttopath)
_is_texttopath = texttopath;
}
+void CairoRenderContext::setOmitText(bool omittext)
+{
+ _is_omittext = omittext;
+}
+
+bool CairoRenderContext::getOmitText(void)
+{
+ return _is_omittext;
+}
+
void CairoRenderContext::setFilterToBitmap(bool filtertobitmap)
{
_is_filtertobitmap = filtertobitmap;
@@ -708,7 +719,7 @@ CairoRenderContext::popLayer(void)
for (int row = 0 ; row < height; row++) {
unsigned char *row_data = pixels + (row * stride);
for (int i = 0 ; i < width; i++) {
- guint32 *pixel = (guint32 *)row_data + i;
+ guint32 *pixel = reinterpret_cast<guint32 *>(row_data) + i;
float lum_alpha = (((*pixel & 0x00ff0000) >> 16) * coeff_r +
((*pixel & 0x0000ff00) >> 8) * coeff_g +
((*pixel & 0x000000ff) ) * coeff_b );
@@ -792,9 +803,7 @@ CairoRenderContext::setupSurface(double width, double height)
#ifdef CAIRO_HAS_PDF_SURFACE
case CAIRO_SURFACE_TYPE_PDF:
surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
-#if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 10, 0))
cairo_pdf_surface_restrict_to_version(surface, (cairo_pdf_version_t)_pdf_level);
-#endif
break;
#endif
#ifdef CAIRO_HAS_PS_SURFACE
@@ -803,10 +812,8 @@ CairoRenderContext::setupSurface(double width, double height)
if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
return FALSE;
}
-#if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2))
cairo_ps_surface_restrict_to_level(surface, (cairo_ps_level_t)_ps_level);
cairo_ps_surface_set_eps(surface, (cairo_bool_t) _eps);
-#endif
// Cairo calculates the bounding box itself, however we want to override this. See Launchpad bug #380501
#if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 11, 2))
// cairo_ps_dsc_comment(surface, os_bbox.str().c_str());
@@ -855,7 +862,7 @@ CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface, cairo_matrix_t
_surface = surface;
if (_vector_based_target) {
- cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
+ cairo_scale(_cr, Inkscape::Util::Quantity::convert(1, "px", "pt"), Inkscape::Util::Quantity::convert(1, "px", "pt"));
} else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
// set background color on non-alpha surfaces
// TODO: bgcolor should be derived from SPDocument
@@ -1080,11 +1087,11 @@ CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver
// show items and render them
for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
- if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
+ if (pat_i && SP_IS_OBJECT(pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
for ( SPObject *child = pat_i->firstChild() ; child; child = child->getNext() ) {
- if (SP_IS_ITEM (child)) {
- SP_ITEM (child)->invoke_show (drawing, dkey, SP_ITEM_REFERENCE_FLAGS);
- _renderer->renderItem(pattern_ctx, SP_ITEM (child));
+ if (SP_IS_ITEM(child)) {
+ SP_ITEM(child)->invoke_show(drawing, dkey, SP_ITEM_REFERENCE_FLAGS);
+ _renderer->renderItem(pattern_ctx, SP_ITEM(child));
}
}
break; // do not go further up the chain if children are found
@@ -1109,10 +1116,10 @@ CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver
// hide all items
for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
- if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
+ if (pat_i && SP_IS_OBJECT(pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
for ( SPObject *child = pat_i->firstChild() ; child; child = child->getNext() ) {
- if (SP_IS_ITEM (child)) {
- SP_ITEM (child)->invoke_hide (dkey);
+ if (SP_IS_ITEM(child)) {
+ SP_ITEM(child)->invoke_hide(dkey);
}
}
break; // do not go further up the chain if children are found
@@ -1181,7 +1188,7 @@ CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const pain
return NULL;
}
- if (pattern && SP_IS_GRADIENT (paintserver)) {
+ if (pattern && SP_IS_GRADIENT(paintserver)) {
SPGradient *g = SP_GRADIENT(paintserver);
// set extend type
@@ -1290,12 +1297,16 @@ CairoRenderContext::_setStrokeStyle(SPStyle const *style, Geom::OptRect const &p
}
}
- if (style->stroke_dash.n_dash &&
- style->stroke_dash.dash )
+ if (!style->stroke_dasharray.values.empty())
{
- cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
+ size_t ndashes = style->stroke_dasharray.values.size();
+ double* dashes =(double*)malloc(ndashes*sizeof(double));
+ for( unsigned i = 0; i < ndashes; ++i ) {
+ dashes[i] = style->stroke_dasharray.values[i];
+ }
+ cairo_set_dash(_cr, dashes, ndashes, style->stroke_dashoffset.value);
} else {
- cairo_set_dash(_cr, NULL, 0, 0.0); // disable dashing
+ cairo_set_dash(_cr, NULL, 0, 0.0); // disable dashing
}
cairo_set_line_width(_cr, style->stroke_width.computed);
@@ -1303,40 +1314,65 @@ CairoRenderContext::_setStrokeStyle(SPStyle const *style, Geom::OptRect const &p
// set line join type
cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
switch (style->stroke_linejoin.computed) {
- case SP_STROKE_LINEJOIN_MITER:
- join = CAIRO_LINE_JOIN_MITER;
- break;
- case SP_STROKE_LINEJOIN_ROUND:
- join = CAIRO_LINE_JOIN_ROUND;
- break;
- case SP_STROKE_LINEJOIN_BEVEL:
- join = CAIRO_LINE_JOIN_BEVEL;
- break;
+ case SP_STROKE_LINEJOIN_MITER:
+ join = CAIRO_LINE_JOIN_MITER;
+ break;
+ case SP_STROKE_LINEJOIN_ROUND:
+ join = CAIRO_LINE_JOIN_ROUND;
+ break;
+ case SP_STROKE_LINEJOIN_BEVEL:
+ join = CAIRO_LINE_JOIN_BEVEL;
+ break;
}
cairo_set_line_join(_cr, join);
// set line cap type
cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
switch (style->stroke_linecap.computed) {
- case SP_STROKE_LINECAP_BUTT:
- cap = CAIRO_LINE_CAP_BUTT;
- break;
- case SP_STROKE_LINECAP_ROUND:
- cap = CAIRO_LINE_CAP_ROUND;
- break;
- case SP_STROKE_LINECAP_SQUARE:
- cap = CAIRO_LINE_CAP_SQUARE;
- break;
+ case SP_STROKE_LINECAP_BUTT:
+ cap = CAIRO_LINE_CAP_BUTT;
+ break;
+ case SP_STROKE_LINECAP_ROUND:
+ cap = CAIRO_LINE_CAP_ROUND;
+ break;
+ case SP_STROKE_LINECAP_SQUARE:
+ cap = CAIRO_LINE_CAP_SQUARE;
+ break;
}
cairo_set_line_cap(_cr, cap);
cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
}
+void
+CairoRenderContext::_prepareRenderGraphic()
+{
+ // Only PDFLaTeX supports importing a single page of a graphics file,
+ // so only PDF backend gets interleaved text/graphics
+ if (_is_omittext && _target == CAIRO_SURFACE_TYPE_PDF) {
+ if (_omittext_state == NEW_PAGE_ON_GRAPHIC)
+ cairo_show_page(_cr);
+ _omittext_state = GRAPHIC_ON_TOP;
+ }
+}
+
+void
+CairoRenderContext::_prepareRenderText()
+{
+ // Only PDFLaTeX supports importing a single page of a graphics file,
+ // so only PDF backend gets interleaved text/graphics
+ if (_is_omittext && _target == CAIRO_SURFACE_TYPE_PDF) {
+ if (_omittext_state == GRAPHIC_ON_TOP)
+ _omittext_state = NEW_PAGE_ON_GRAPHIC;
+ }
+}
+
bool
CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, Geom::OptRect const &pbox)
{
g_assert( _is_valid );
+ _prepareRenderGraphic();
+
if (_render_mode == RENDER_MODE_CLIP) {
if (_clip_mode == CLIP_MODE_PATH) {
addClipPath(pathv, &style->fill_rule);
@@ -1400,8 +1436,8 @@ CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle con
return true;
}
-bool CairoRenderContext::renderImage(GdkPixbuf *pb,
- Geom::Affine const &image_transform, SPStyle const * /*style*/)
+bool CairoRenderContext::renderImage(Inkscape::Pixbuf *pb,
+ Geom::Affine const &image_transform, SPStyle const *style)
{
g_assert( _is_valid );
@@ -1409,16 +1445,18 @@ bool CairoRenderContext::renderImage(GdkPixbuf *pb,
return true;
}
- int w = gdk_pixbuf_get_width (pb);
- int h = gdk_pixbuf_get_height (pb);
+ _prepareRenderGraphic();
+
+ int w = pb->width();
+ int h = pb->height();
// TODO: reenable merge_opacity if useful
float opacity = _state->opacity;
- cairo_surface_t *image_surface = ink_cairo_surface_create_for_argb32_pixbuf(pb);
+ cairo_surface_t *image_surface = pb->getSurfaceRaw();
if (cairo_surface_status(image_surface)) {
TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
- return false;
+ return false;
}
cairo_save(_cr);
@@ -1434,10 +1472,30 @@ bool CairoRenderContext::renderImage(GdkPixbuf *pb,
cairo_rectangle(_cr, 0, 0, w, h);
cairo_clip(_cr);
}
+
+ // Cairo filter method will be mapped to PS/PDF 'interpolate' true/false).
+ // See cairo-pdf-surface.c
+ if (style) {
+ // See: http://www.w3.org/TR/SVG/painting.html#ImageRenderingProperty
+ // http://www.w3.org/TR/css4-images/#the-image-rendering
+ // style.h/style.cpp
+ switch (style->image_rendering.computed) {
+ case SP_CSS_COLOR_RENDERING_AUTO:
+ // Do nothing
+ break;
+ case SP_CSS_COLOR_RENDERING_OPTIMIZEQUALITY:
+ cairo_pattern_set_filter(cairo_get_source(_cr), CAIRO_FILTER_BEST );
+ break;
+ case SP_CSS_COLOR_RENDERING_OPTIMIZESPEED:
+ default:
+ cairo_pattern_set_filter(cairo_get_source(_cr), CAIRO_FILTER_NEAREST );
+ break;
+ }
+ }
+
cairo_paint_with_alpha(_cr, opacity);
cairo_restore(_cr);
- cairo_surface_destroy(image_surface);
return true;
}
@@ -1490,9 +1548,14 @@ unsigned int CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont * /*font*/,
bool
CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Affine const &font_matrix,
std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
-{
+{
+
+ _prepareRenderText();
+ if (_is_omittext)
+ return true;
+
// create a cairo_font_face from PangoFont
- double size = style->font_size.computed;
+ double size = style->font_size.computed; /// \fixme why is this variable never used?
gpointer fonthash = (gpointer)font;
cairo_font_face_t *font_face = (cairo_font_face_t *)g_hash_table_lookup(font_table, fonthash);
@@ -1633,9 +1696,9 @@ _write_callback(void *closure, const unsigned char *data, unsigned int length)
written = fwrite (data, 1, length, file);
if (written == length)
- return CAIRO_STATUS_SUCCESS;
+ return CAIRO_STATUS_SUCCESS;
else
- return CAIRO_STATUS_WRITE_ERROR;
+ return CAIRO_STATUS_WRITE_ERROR;
}
#include "clear-n_.h"
diff --git a/src/extension/internal/cairo-render-context.h b/src/extension/internal/cairo-render-context.h
index 94c7bb294..8d3e63775 100644
--- a/src/extension/internal/cairo-render-context.h
+++ b/src/extension/internal/cairo-render-context.h
@@ -6,7 +6,7 @@
*/
/*
* Authors:
- * Miklos Erdelyi <erdelyim@gmail.com>
+ * Miklos Erdelyi <erdelyim@gmail.com>
*
* Copyright (C) 2006 Miklos Erdelyi
*
@@ -32,13 +32,15 @@ class SPClipPath;
class SPMask;
namespace Inkscape {
+class Pixbuf;
+
namespace Extension {
namespace Internal {
class CairoRenderer;
class CairoRenderContext;
-class CairoRenderState;
-class CairoGlyphInfo;
+struct CairoRenderState;
+struct CairoGlyphInfo;
// Holds info for rendering a glyph
struct CairoGlyphInfo {
@@ -94,6 +96,8 @@ public:
void setPDFLevel(unsigned int level);
void setTextToPath(bool texttopath);
bool getTextToPath(void);
+ void setOmitText(bool omittext);
+ bool getOmitText(void);
void setFilterToBitmap(bool filtertobitmap);
bool getFilterToBitmap(void);
void setBitmapResolution(int resolution);
@@ -109,6 +113,9 @@ public:
/** Saves the contents of the context to a PNG file. */
bool saveAsPng(const char *file_name);
+ /** On targets supporting multiple pages, sends subsequent rendering to a new page*/
+ void newPage(void);
+
/* Render/clip mode setting/query */
void setRenderMode(CairoRenderMode mode);
CairoRenderMode getRenderMode(void) const;
@@ -139,7 +146,7 @@ public:
/* Rendering methods */
bool renderPathVector(Geom::PathVector const &pathv, SPStyle const *style, Geom::OptRect const &pbox);
- bool renderImage(GdkPixbuf *pb,
+ bool renderImage(Inkscape::Pixbuf *pb,
Geom::Affine const &image_transform, SPStyle const *style);
bool renderGlyphtext(PangoFont *font, Geom::Affine const &font_matrix,
std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style);
@@ -150,6 +157,12 @@ protected:
CairoRenderContext(CairoRenderer *renderer);
virtual ~CairoRenderContext(void);
+ enum CairoOmitTextPageState {
+ EMPTY,
+ GRAPHIC_ON_TOP,
+ NEW_PAGE_ON_GRAPHIC
+ };
+
float _width;
float _height;
unsigned short _dpi;
@@ -157,6 +170,7 @@ protected:
unsigned int _ps_level;
bool _eps;
bool _is_texttopath;
+ bool _is_omittext;
bool _is_filtertobitmap;
int _bitmapresolution;
@@ -182,6 +196,8 @@ protected:
CairoRenderMode _render_mode;
CairoClipMode _clip_mode;
+ CairoOmitTextPageState _omittext_state;
+
cairo_pattern_t *_createPatternForPaintServer(SPPaintServer const *const paintserver,
Geom::OptRect const &pbox, float alpha);
cairo_pattern_t *_createPatternPainter(SPPaintServer const *const paintserver, Geom::OptRect const &pbox);
@@ -196,6 +212,9 @@ protected:
void _concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0);
void _concatTransform(cairo_t *cr, Geom::Affine const &transform);
+ void _prepareRenderGraphic(void);
+ void _prepareRenderText(void);
+
GHashTable *font_table;
static void font_data_free(gpointer data);
diff --git a/src/extension/internal/cairo-renderer-pdf-out.cpp b/src/extension/internal/cairo-renderer-pdf-out.cpp
index 5d45fcf87..0c314a576 100644
--- a/src/extension/internal/cairo-renderer-pdf-out.cpp
+++ b/src/extension/internal/cairo-renderer-pdf-out.cpp
@@ -39,6 +39,8 @@
#include <2geom/affine.h>
#include "document.h"
+#include "util/units.h"
+
namespace Inkscape {
namespace Extension {
namespace Internal {
@@ -57,7 +59,7 @@ bool CairoRendererPdfOutput::check(Inkscape::Extension::Extension * /*module*/)
static bool
pdf_render_document_to_file(SPDocument *doc, gchar const *filename, unsigned int level,
bool texttopath, bool omittext, bool filtertobitmap, int resolution,
- const gchar * const exportId, bool exportDrawing, bool exportCanvas)
+ const gchar * const exportId, bool exportDrawing, bool exportCanvas, float bleedmargin_px)
{
doc->ensureUpToDate();
@@ -92,14 +94,14 @@ pdf_render_document_to_file(SPDocument *doc, gchar const *filename, unsigned int
CairoRenderContext *ctx = renderer->createContext();
ctx->setPDFLevel(level);
ctx->setTextToPath(texttopath);
- renderer->_omitText = omittext;
+ ctx->setOmitText(omittext);
ctx->setFilterToBitmap(filtertobitmap);
ctx->setBitmapResolution(resolution);
bool ret = ctx->setPdfTarget (filename);
if(ret) {
/* Render document */
- ret = renderer->setupDocument(ctx, doc, pageBoundingBox, base);
+ ret = renderer->setupDocument(ctx, doc, pageBoundingBox, bleedmargin_px, base);
if (ret) {
renderer->renderItem(ctx, base);
ret = ctx->finish();
@@ -134,12 +136,12 @@ CairoRendererPdfOutput::save(Inkscape::Extension::Output *mod, SPDocument *doc,
if (ext == NULL)
return;
- const gchar *new_level = NULL;
int level = 0;
try {
- new_level = mod->get_param_enum("PDFversion");
- if((new_level != NULL) && (g_ascii_strcasecmp("PDF-1.5", new_level) == 0))
+ const gchar *new_level = mod->get_param_enum("PDFversion");
+ if((new_level != NULL) && (g_ascii_strcasecmp("PDF-1.5", new_level) == 0)) {
level = 1;
+ }
}
catch(...) {
g_warning("Parameter <PDFversion> might not exist");
@@ -185,20 +187,20 @@ CairoRendererPdfOutput::save(Inkscape::Extension::Output *mod, SPDocument *doc,
g_warning("Parameter <exportId> might not exist");
}
- bool new_exportDrawing = FALSE;
+ bool new_exportCanvas = true;
try {
- new_exportDrawing = mod->get_param_bool("areaDrawing");
- }
- catch(...) {
- g_warning("Parameter <areaDrawing> might not exist");
+ new_exportCanvas = (strcmp(ext->get_param_optiongroup("area"), "page") == 0);
+ } catch(...) {
+ g_warning("Parameter <area> might not exist");
}
+ bool new_exportDrawing = !new_exportCanvas;
- bool new_exportCanvas = FALSE;
+ float new_bleedmargin_px = 0.;
try {
- new_exportCanvas = mod->get_param_bool("areaPage");
+ new_bleedmargin_px = Inkscape::Util::Quantity::convert(mod->get_param_float("bleed"), "mm", "px");
}
catch(...) {
- g_warning("Parameter <exportCanvas> might not exist");
+ g_warning("Parameter <bleed> might not exist");
}
// Create PDF file
@@ -207,7 +209,7 @@ CairoRendererPdfOutput::save(Inkscape::Extension::Output *mod, SPDocument *doc,
final_name = g_strdup_printf("> %s", filename);
ret = pdf_render_document_to_file(doc, final_name, level,
new_textToPath, new_textToLaTeX, new_blurToBitmap, new_bitmapResolution,
- new_exportId, new_exportDrawing, new_exportCanvas);
+ new_exportId, new_exportDrawing, new_exportCanvas, new_bleedmargin_px);
g_free(final_name);
if (!ret)
@@ -216,7 +218,7 @@ CairoRendererPdfOutput::save(Inkscape::Extension::Output *mod, SPDocument *doc,
// Create LaTeX file (if requested)
if (new_textToLaTeX) {
- ret = latex_render_document_text_to_file(doc, filename, new_exportId, new_exportDrawing, new_exportCanvas, true);
+ ret = latex_render_document_text_to_file(doc, filename, new_exportId, new_exportDrawing, new_exportCanvas, new_bleedmargin_px, true);
if (!ret)
throw Inkscape::Extension::Output::save_failed();
@@ -235,32 +237,35 @@ CairoRendererPdfOutput::save(Inkscape::Extension::Output *mod, SPDocument *doc,
void
CairoRendererPdfOutput::init (void)
{
- Inkscape::Extension::build_from_mem(
- "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
- "<name>Portable Document Format</name>\n"
- "<id>org.inkscape.output.pdf.cairorenderer</id>\n"
- "<param name=\"PDFversion\" gui-text=\"" N_("Restrict to PDF version:") "\" type=\"enum\" >\n"
+ Inkscape::Extension::build_from_mem(
+ "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
+ "<name>Portable Document Format</name>\n"
+ "<id>org.inkscape.output.pdf.cairorenderer</id>\n"
+ "<param name=\"PDFversion\" gui-text=\"" N_("Restrict to PDF version:") "\" type=\"enum\" >\n"
#if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 10, 0))
- "<_item value='PDF-1.5'>" N_("PDF 1.5") "</_item>\n"
+ "<_item value='PDF-1.5'>" N_("PDF 1.5") "</_item>\n"
#endif
"<_item value='PDF-1.4'>" N_("PDF 1.4") "</_item>\n"
- "</param>\n"
- "<param name=\"textToPath\" gui-text=\"" N_("Convert texts to paths") "\" type=\"boolean\">false</param>\n"
- "<param name=\"textToLaTeX\" gui-text=\"" N_("PDF+LaTeX: Omit text in PDF, and create LaTeX file") "\" type=\"boolean\">false</param>\n"
- "<param name=\"blurToBitmap\" gui-text=\"" N_("Rasterize filter effects") "\" type=\"boolean\">true</param>\n"
- "<param name=\"resolution\" gui-text=\"" N_("Resolution for rasterization (dpi):") "\" type=\"int\" min=\"1\" max=\"10000\">90</param>\n"
- "<param name=\"areaDrawing\" gui-text=\"" N_("Export area is drawing") "\" type=\"boolean\">false</param>\n"
- "<param name=\"areaPage\" gui-text=\"" N_("Export area is page") "\" type=\"boolean\">false</param>\n"
- "<param name=\"exportId\" gui-text=\"" N_("Limit export to the object with ID:") "\" type=\"string\"></param>\n"
- "<output>\n"
- "<extension>.pdf</extension>\n"
- "<mimetype>application/pdf</mimetype>\n"
- "<filetypename>Portable Document Format (*.pdf)</filetypename>\n"
- "<filetypetooltip>PDF File</filetypetooltip>\n"
- "</output>\n"
- "</inkscape-extension>", new CairoRendererPdfOutput());
-
- return;
+ "</param>\n"
+ "<param name=\"textToPath\" gui-text=\"" N_("Convert texts to paths") "\" type=\"boolean\">false</param>\n"
+ "<param name=\"textToLaTeX\" gui-text=\"" N_("PDF+LaTeX: Omit text in PDF, and create LaTeX file") "\" type=\"boolean\">false</param>\n"
+ "<param name=\"blurToBitmap\" gui-text=\"" N_("Rasterize filter effects") "\" type=\"boolean\">true</param>\n"
+ "<param name=\"resolution\" gui-text=\"" N_("Resolution for rasterization (dpi):") "\" type=\"int\" min=\"1\" max=\"10000\">90</param>\n"
+ "<param name=\"area\" gui-text=\"" N_("Output page size:") "\" type=\"optiongroup\" >\n"
+ "<_option value=\"page\">" N_("Use document's page size") "</_option>"
+ "<_option value=\"drawing\">" N_("Use exported object's size") "</_option>"
+ "</param>"
+ "<param name=\"bleed\" gui-text=\"" N_("Bleed/margin (mm):") "\" type=\"float\" min=\"-10000\" max=\"10000\">0</param>\n"
+ "<param name=\"exportId\" gui-text=\"" N_("Limit export to the object with ID:") "\" type=\"string\"></param>\n"
+ "<output>\n"
+ "<extension>.pdf</extension>\n"
+ "<mimetype>application/pdf</mimetype>\n"
+ "<filetypename>Portable Document Format (*.pdf)</filetypename>\n"
+ "<filetypetooltip>PDF File</filetypetooltip>\n"
+ "</output>\n"
+ "</inkscape-extension>", new CairoRendererPdfOutput());
+
+ return;
}
} } } /* namespace Inkscape, Extension, Internal */
diff --git a/src/extension/internal/cairo-renderer.cpp b/src/extension/internal/cairo-renderer.cpp
index 09d69becb..6fbc85c05 100644
--- a/src/extension/internal/cairo-renderer.cpp
+++ b/src/extension/internal/cairo-renderer.cpp
@@ -27,6 +27,7 @@
#include <signal.h>
#include <errno.h>
+#include <boost/scoped_ptr.hpp>
#include "libnrtype/Layout-TNG.h"
#include <2geom/transforms.h>
@@ -55,7 +56,7 @@
#include "sp-mask.h"
#include "sp-clippath.h"
-#include <unit-constants.h>
+#include "util/units.h"
#include "helper/png-write.h"
#include "helper/pixbuf-ops.h"
@@ -101,7 +102,6 @@ namespace Extension {
namespace Internal {
CairoRenderer::CairoRenderer(void)
- : _omitText(false)
{}
CairoRenderer::~CairoRenderer(void)
@@ -348,8 +348,8 @@ static void sp_image_render(SPItem *item, CairoRenderContext *ctx)
if (!image->pixbuf) return;
if ((image->width.computed <= 0.0) || (image->height.computed <= 0.0)) return;
- w = gdk_pixbuf_get_width (image->pixbuf);
- h = gdk_pixbuf_get_height (image->pixbuf);
+ w = image->pixbuf->width();
+ h = image->pixbuf->height();
double x = image->x.computed;
double y = image->y.computed;
@@ -442,7 +442,7 @@ static void sp_asbitmap_render(SPItem *item, CairoRenderContext *ctx)
*/
res = ctx->getBitmapResolution();
if(res == 0) {
- res = PX_PER_IN;
+ res = Inkscape::Util::Quantity::convert(1, "in", "px");
}
TRACE(("sp_asbitmap_render: resolution: %f\n", res ));
@@ -450,7 +450,7 @@ static void sp_asbitmap_render(SPItem *item, CairoRenderContext *ctx)
Geom::OptRect bbox = item->desktopVisualBounds();
// no bbox, e.g. empty group
- if (!bbox) {
+ if (!bbox) {
return;
}
@@ -463,8 +463,8 @@ static void sp_asbitmap_render(SPItem *item, CairoRenderContext *ctx)
}
// The width and height of the bitmap in pixels
- unsigned width = ceil(bbox->width() * (res / PX_PER_IN));
- unsigned height = ceil(bbox->height() * (res / PX_PER_IN));
+ unsigned width = ceil(bbox->width() * Inkscape::Util::Quantity::convert(res, "px", "in"));
+ unsigned height = ceil(bbox->height() * Inkscape::Util::Quantity::convert(res, "px", "in"));
if (width == 0 || height == 0) return;
@@ -477,7 +477,7 @@ static void sp_asbitmap_render(SPItem *item, CairoRenderContext *ctx)
double shift_y = bbox->max()[Geom::Y];
// For default 90 dpi, snap bitmap to pixel grid
- if (res == PX_PER_IN) {
+ if (res == Inkscape::Util::Quantity::convert(1, "in", "px")) {
shift_x = round (shift_x);
shift_y = -round (-shift_y); // Correct rounding despite coordinate inversion.
// Remove the negations when the inversion is gone.
@@ -498,17 +498,15 @@ static void sp_asbitmap_render(SPItem *item, CairoRenderContext *ctx)
GSList *items = NULL;
items = g_slist_append(items, item);
- GdkPixbuf *pb = sp_generate_internal_bitmap(document, NULL,
- bbox->min()[Geom::X], bbox->min()[Geom::Y], bbox->max()[Geom::X], bbox->max()[Geom::Y],
- width, height, res, res, (guint32) 0xffffff00, items );
+ boost::scoped_ptr<Inkscape::Pixbuf> pb(
+ sp_generate_internal_bitmap(document, NULL,
+ bbox->min()[Geom::X], bbox->min()[Geom::Y], bbox->max()[Geom::X], bbox->max()[Geom::Y],
+ width, height, res, res, (guint32) 0xffffff00, items ));
if (pb) {
- TEST(gdk_pixbuf_save( pb, "bitmap.png", "png", NULL, NULL ));
- // TODO this is stupid - we just converted to pixbuf format when generating the bitmap!
- convert_pixbuf_normal_to_argb32(pb);
- ctx->renderImage(pb, t, item->style);
- g_object_unref(pb);
- pb = 0;
+ //TEST(gdk_pixbuf_save( pb, "bitmap.png", "png", NULL, NULL ));
+
+ ctx->renderImage(pb.get(), t, item->style);
}
g_slist_free (items);
}
@@ -578,18 +576,13 @@ CairoRenderer::setStateForItem(CairoRenderContext *ctx, SPItem const *item)
// TODO change this to accept a const SPItem:
void CairoRenderer::renderItem(CairoRenderContext *ctx, SPItem *item)
{
- if ( _omitText && (SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item)) ) {
- // skip text if _omitText is true
- return;
- }
-
ctx->pushState();
setStateForItem(ctx, item);
CairoRenderState *state = ctx->getCurrentState();
state->need_layer = ( state->mask || state->clip_path || state->opacity != 1.0 );
- // Draw item on a temporary surface so a mask, clip path, or opacity can be applied to it.
+ // Draw item on a temporary surface so a mask, clip-path, or opacity can be applied to it.
if (state->need_layer) {
state->merge_opacity = FALSE;
ctx->pushLayer();
@@ -598,13 +591,13 @@ void CairoRenderer::renderItem(CairoRenderContext *ctx, SPItem *item)
sp_item_invoke_render(item, ctx);
if (state->need_layer)
- ctx->popLayer();
+ ctx->popLayer(); // This applies clipping/masking
ctx->popState();
}
bool
-CairoRenderer::setupDocument(CairoRenderContext *ctx, SPDocument *doc, bool pageBoundingBox, SPItem *base)
+CairoRenderer::setupDocument(CairoRenderContext *ctx, SPDocument *doc, bool pageBoundingBox, float bleedmargin_px, SPItem *base)
{
// PLEASE note when making changes to the boundingbox and transform calculation, corresponding changes should be made to PDFLaTeXRenderer::setupDocument !!!
@@ -625,10 +618,11 @@ CairoRenderer::setupDocument(CairoRenderContext *ctx, SPDocument *doc, bool page
}
d = *bbox;
}
+ d.expandBy(bleedmargin_px);
if (ctx->_vector_based_target) {
// convert from px to pt
- d *= Geom::Scale(PT_PER_PX);
+ d *= Geom::Scale(Inkscape::Util::Quantity::convert(1, "px", "pt"));
}
ctx->_width = d.width();
@@ -638,16 +632,21 @@ CairoRenderer::setupDocument(CairoRenderContext *ctx, SPDocument *doc, bool page
bool ret = ctx->setupSurface(ctx->_width, ctx->_height);
- if (ret && !pageBoundingBox)
- {
- double high = doc->getHeight();
- if (ctx->_vector_based_target)
- high *= PT_PER_PX;
-
- /// @fixme hardcoded dt2doc transform?
- Geom::Affine tp(Geom::Translate(-d.left() * (ctx->_vector_based_target ? PX_PER_PT : 1.0),
- (d.bottom() - high) * (ctx->_vector_based_target ? PX_PER_PT : 1.0)));
- ctx->transform(tp);
+ if (ret) {
+ if (pageBoundingBox) {
+ // translate to set bleed/margin
+ Geom::Affine tp( Geom::Translate( bleedmargin_px, bleedmargin_px ) );
+ ctx->transform(tp);
+ } else {
+ double high = doc->getHeight().value("px");
+ if (ctx->_vector_based_target)
+ high = Inkscape::Util::Quantity::convert(high, "px", "pt");
+
+ // this transform translates the export drawing to a virtual page (0,0)-(width,height)
+ Geom::Affine tp(Geom::Translate(-d.left() * (ctx->_vector_based_target ? Inkscape::Util::Quantity::convert(1, "pt", "px") : 1.0),
+ (d.bottom() - high) * (ctx->_vector_based_target ? Inkscape::Util::Quantity::convert(1, "pt", "px") : 1.0)));
+ ctx->transform(tp);
+ }
}
return ret;
diff --git a/src/extension/internal/cairo-renderer.h b/src/extension/internal/cairo-renderer.h
index 7fa7c7ff5..cfef6bdea 100644
--- a/src/extension/internal/cairo-renderer.h
+++ b/src/extension/internal/cairo-renderer.h
@@ -53,14 +53,10 @@ public:
/** Initializes the CairoRenderContext according to the specified
SPDocument. A set*Target function can only be called on the context
before setupDocument. */
- bool setupDocument(CairoRenderContext *ctx, SPDocument *doc, bool pageBoundingBox, SPItem *base);
+ bool setupDocument(CairoRenderContext *ctx, SPDocument *doc, bool pageBoundingBox, float bleedmargin_px, SPItem *base);
/** Traverses the object tree and invokes the render methods. */
void renderItem(CairoRenderContext *ctx, SPItem *item);
-
- /** If _omitText is true, no text will be output to the PDF document.
- The PDF will be exactly the same as if the text was written to it and then erased. */
- bool _omitText;
};
// FIXME: this should be a static method of CairoRenderer
diff --git a/src/extension/internal/cdr-input.cpp b/src/extension/internal/cdr-input.cpp
new file mode 100644
index 000000000..0111ed626
--- /dev/null
+++ b/src/extension/internal/cdr-input.cpp
@@ -0,0 +1,339 @@
+/*
+ * This file came from libwpg as a source, their utility wpg2svg
+ * specifically. It has been modified to work as an Inkscape extension.
+ * The Inkscape extension code is covered by this copyright, but the
+ * rest is covered by the one bellow.
+ *
+ * Authors:
+ * Fridrich Strba (fridrich.strba@bluewin.ch)
+ *
+ * Copyright (C) 2012 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ *
+ */
+
+#include <stdio.h>
+#include "config.h"
+
+#include "cdr-input.h"
+
+#ifdef WITH_LIBCDR
+
+#include <string>
+#include <cstring>
+
+#include <libcdr/libcdr.h>
+#include <libwpd-stream/libwpd-stream.h>
+
+#include <gtkmm/alignment.h>
+#include <gtkmm/comboboxtext.h>
+#include <gtkmm/drawingarea.h>
+#include <gtkmm/frame.h>
+#include <gtkmm/scale.h>
+
+#include "extension/system.h"
+#include "extension/input.h"
+#include "document.h"
+
+#include "document-private.h"
+#include "document-undo.h"
+#include "inkscape.h"
+
+#include "dialogs/dialog-events.h"
+#include <gtk/gtk.h>
+#include "ui/widget/spinbutton.h"
+#include "ui/widget/frame.h"
+#include <glibmm/i18n.h>
+
+#include <gdkmm/general.h>
+
+#include "svg-view.h"
+#include "svg-view-widget.h"
+
+#include "util/units.h"
+
+namespace Inkscape {
+namespace Extension {
+namespace Internal {
+
+
+class CdrImportDialog : public Gtk::Dialog {
+public:
+ CdrImportDialog(const std::vector<WPXString> &vec);
+ virtual ~CdrImportDialog();
+
+ bool showDialog();
+ unsigned getSelectedPage();
+ void getImportSettings(Inkscape::XML::Node *prefs);
+
+private:
+ void _setPreviewPage(unsigned page);
+
+ // Signal handlers
+#if !WITH_GTKMM_3_0
+ bool _onExposePreview(GdkEventExpose *event);
+#endif
+
+ void _onPageNumberChanged();
+
+ class Gtk::Button * cancelbutton;
+ class Gtk::Button * okbutton;
+ class Gtk::Label * _labelSelect;
+ class Inkscape::UI::Widget::SpinButton * _pageNumberSpin;
+ class Gtk::Label * _labelTotalPages;
+ class Gtk::VBox * vbox1;
+ class Gtk::VBox * vbox2;
+ class Gtk::Widget * _previewArea;
+
+ const std::vector<WPXString> &_vec; // Document to be imported
+ unsigned _current_page; // Current selected page
+ int _preview_width, _preview_height; // Size of the preview area
+};
+
+CdrImportDialog::CdrImportDialog(const std::vector<WPXString> &vec)
+ : _vec(vec), _current_page(1)
+{
+ int num_pages = _vec.size();
+ if ( num_pages <= 1 )
+ return;
+ cancelbutton = Gtk::manage(new class Gtk::Button(Gtk::StockID("gtk-cancel")));
+ okbutton = Gtk::manage(new class Gtk::Button(Gtk::StockID("gtk-ok")));
+ _labelSelect = Gtk::manage(new class Gtk::Label(_("Select page:")));
+
+ // Page number
+#if WITH_GTKMM_3_0
+ Glib::RefPtr<Gtk::Adjustment> _pageNumberSpin_adj = Gtk::Adjustment::create(1, 1, _vec.size(), 1, 10, 0);
+ _pageNumberSpin = Gtk::manage(new Inkscape::UI::Widget::SpinButton(_pageNumberSpin_adj, 1, 1));
+#else
+ Gtk::Adjustment *_pageNumberSpin_adj = Gtk::manage(
+ new class Gtk::Adjustment(1, 1, _vec.size(), 1, 10, 0));
+ _pageNumberSpin = Gtk::manage(new class Inkscape::UI::Widget::SpinButton(*_pageNumberSpin_adj, 1, 1));
+#endif
+ _labelTotalPages = Gtk::manage(new class Gtk::Label());
+ gchar *label_text = g_strdup_printf(_("out of %i"), num_pages);
+ _labelTotalPages->set_label(label_text);
+ g_free(label_text);
+
+ vbox1 = Gtk::manage(new class Gtk::VBox(false, 4));
+ SPDocument *doc = SPDocument::createNewDocFromMem(_vec[0].cstr(), strlen(_vec[0].cstr()), 0);
+ _previewArea = Glib::wrap(sp_svg_view_widget_new(doc));
+
+ vbox2 = Gtk::manage(new class Gtk::VBox(false, 4));
+ cancelbutton->set_can_focus();
+ cancelbutton->set_can_default();
+ cancelbutton->set_relief(Gtk::RELIEF_NORMAL);
+ okbutton->set_can_focus();
+ okbutton->set_can_default();
+ okbutton->set_relief(Gtk::RELIEF_NORMAL);
+ this->get_action_area()->property_layout_style().set_value(Gtk::BUTTONBOX_END);
+ _labelSelect->set_line_wrap(false);
+ _labelSelect->set_use_markup(false);
+ _labelSelect->set_selectable(false);
+ _pageNumberSpin->set_can_focus();
+ _pageNumberSpin->set_update_policy(Gtk::UPDATE_ALWAYS);
+ _pageNumberSpin->set_numeric(true);
+ _pageNumberSpin->set_digits(0);
+ _pageNumberSpin->set_wrap(false);
+ _labelTotalPages->set_line_wrap(false);
+ _labelTotalPages->set_use_markup(false);
+ _labelTotalPages->set_selectable(false);
+ vbox2->pack_start(*_previewArea, Gtk::PACK_SHRINK, 0);
+ this->get_vbox()->set_homogeneous(false);
+ this->get_vbox()->set_spacing(0);
+ this->get_vbox()->pack_start(*vbox2);
+ this->set_title(_("Page Selector"));
+ this->set_modal(true);
+ sp_transientize(GTK_WIDGET(this->gobj())); //Make transient
+ this->property_window_position().set_value(Gtk::WIN_POS_NONE);
+ this->set_resizable(true);
+ this->property_destroy_with_parent().set_value(false);
+ this->get_action_area()->add(*_labelSelect);
+ this->add_action_widget(*_pageNumberSpin, -7);
+ this->get_action_area()->add(*_labelTotalPages);
+ this->add_action_widget(*cancelbutton, -6);
+ this->add_action_widget(*okbutton, -5);
+ cancelbutton->show();
+ okbutton->show();
+ _labelSelect->show();
+ _pageNumberSpin->show();
+ _labelTotalPages->show();
+ vbox1->show();
+ _previewArea->show();
+ vbox2->show();
+
+ // Connect signals
+ _pageNumberSpin_adj->signal_value_changed().connect(sigc::mem_fun(*this, &CdrImportDialog::_onPageNumberChanged));
+}
+
+CdrImportDialog::~CdrImportDialog() {}
+
+bool CdrImportDialog::showDialog()
+{
+ show();
+ gint b = run();
+ hide();
+ if ( b == Gtk::RESPONSE_OK ) {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+unsigned CdrImportDialog::getSelectedPage()
+{
+ return _current_page;
+}
+
+void CdrImportDialog::_onPageNumberChanged()
+{
+ unsigned page = static_cast<unsigned>(_pageNumberSpin->get_value_as_int());
+ _current_page = CLAMP(page, 1U, _vec.size());
+ _setPreviewPage(_current_page);
+}
+
+/**
+ * \brief Renders the given page's thumbnail
+ */
+void CdrImportDialog::_setPreviewPage(unsigned page)
+{
+ SPDocument *doc = SPDocument::createNewDocFromMem(_vec[page-1].cstr(), strlen(_vec[page-1].cstr()), 0);
+ Gtk::Widget * tmpPreviewArea = Glib::wrap(sp_svg_view_widget_new(doc));
+ std::swap(_previewArea, tmpPreviewArea);
+ if (tmpPreviewArea) {
+ _previewArea->set_size_request( tmpPreviewArea->get_width(), tmpPreviewArea->get_height() );
+ delete tmpPreviewArea;
+ }
+ vbox2->pack_start(*_previewArea, Gtk::PACK_SHRINK, 0);
+ _previewArea->show_now();
+}
+
+SPDocument *CdrInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * uri)
+{
+ WPXFileStream input(uri);
+
+ if (!libcdr::CDRDocument::isSupported(&input)) {
+ return NULL;
+ }
+
+ libcdr::CDRStringVector output;
+ if (!libcdr::CDRDocument::generateSVG(&input, output)) {
+ return NULL;
+ }
+
+ if (output.empty()) {
+ return NULL;
+ }
+
+ std::vector<WPXString> tmpSVGOutput;
+ for (unsigned i=0; i<output.size(); ++i) {
+ WPXString tmpString("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
+ tmpString.append(output[i]);
+ tmpSVGOutput.push_back(tmpString);
+ }
+
+ unsigned page_num = 1;
+
+ // If only one page is present, import that one without bothering user
+ if (tmpSVGOutput.size() > 1) {
+ CdrImportDialog *dlg = 0;
+ if (inkscape_use_gui()) {
+ dlg = new CdrImportDialog(tmpSVGOutput);
+ if (!dlg->showDialog()) {
+ delete dlg;
+ return NULL;
+ }
+ }
+
+ // Get needed page
+ if (dlg) {
+ page_num = dlg->getSelectedPage();
+ if (page_num < 1)
+ page_num = 1;
+ if (page_num > tmpSVGOutput.size())
+ page_num = tmpSVGOutput.size();
+ }
+ }
+
+ SPDocument * doc = SPDocument::createNewDocFromMem(tmpSVGOutput[page_num-1].cstr(), strlen(tmpSVGOutput[page_num-1].cstr()), TRUE);
+
+ // Set viewBox if it doesn't exist
+ if (doc && !doc->getRoot()->viewBox_set) {
+ doc->setViewBox(Geom::Rect::from_xywh(0, 0, doc->getWidth().value(doc->getDefaultUnit()), doc->getHeight().value(doc->getDefaultUnit())));
+ }
+ return doc;
+}
+
+#include "clear-n_.h"
+
+void CdrInput::init(void)
+{
+ /* CDR */
+ Inkscape::Extension::build_from_mem(
+ "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
+ "<name>" N_("Corel DRAW Input") "</name>\n"
+ "<id>org.inkscape.input.cdr</id>\n"
+ "<input>\n"
+ "<extension>.cdr</extension>\n"
+ "<mimetype>image/x-xcdr</mimetype>\n"
+ "<filetypename>" N_("Corel DRAW 7-X4 files (*.cdr)") "</filetypename>\n"
+ "<filetypetooltip>" N_("Open files saved in Corel DRAW 7-X4") "</filetypetooltip>\n"
+ "</input>\n"
+ "</inkscape-extension>", new CdrInput());
+
+ /* CDT */
+ Inkscape::Extension::build_from_mem(
+ "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
+ "<name>" N_("Corel DRAW templates input") "</name>\n"
+ "<id>org.inkscape.input.cdt</id>\n"
+ "<input>\n"
+ "<extension>.cdt</extension>\n"
+ "<mimetype>application/x-xcdt</mimetype>\n"
+ "<filetypename>" N_("Corel DRAW 7-13 template files (*.cdt)") "</filetypename>\n"
+ "<filetypetooltip>" N_("Open files saved in Corel DRAW 7-13") "</filetypetooltip>\n"
+ "</input>\n"
+ "</inkscape-extension>", new CdrInput());
+
+ /* CCX */
+ Inkscape::Extension::build_from_mem(
+ "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
+ "<name>" N_("Corel DRAW Compressed Exchange files input") "</name>\n"
+ "<id>org.inkscape.input.ccx</id>\n"
+ "<input>\n"
+ "<extension>.ccx</extension>\n"
+ "<mimetype>application/x-xccx</mimetype>\n"
+ "<filetypename>" N_("Corel DRAW Compressed Exchange files (*.ccx)") "</filetypename>\n"
+ "<filetypetooltip>" N_("Open compressed exchange files saved in Corel DRAW") "</filetypetooltip>\n"
+ "</input>\n"
+ "</inkscape-extension>", new CdrInput());
+
+ /* CMX */
+ Inkscape::Extension::build_from_mem(
+ "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
+ "<name>" N_("Corel DRAW Presentation Exchange files input") "</name>\n"
+ "<id>org.inkscape.input.cmx</id>\n"
+ "<input>\n"
+ "<extension>.cmx</extension>\n"
+ "<mimetype>application/x-xcmx</mimetype>\n"
+ "<filetypename>" N_("Corel DRAW Presentation Exchange files (*.cmx)") "</filetypename>\n"
+ "<filetypetooltip>" N_("Open presentation exchange files saved in Corel DRAW") "</filetypetooltip>\n"
+ "</input>\n"
+ "</inkscape-extension>", new CdrInput());
+
+ return;
+
+} // init
+
+} } } /* namespace Inkscape, Extension, Implementation */
+#endif /* WITH_LIBCDR */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/extension/internal/cdr-input.h b/src/extension/internal/cdr-input.h
new file mode 100644
index 000000000..3de6c1ed0
--- /dev/null
+++ b/src/extension/internal/cdr-input.h
@@ -0,0 +1,58 @@
+/*
+ * This code abstracts the libwpg interfaces into the Inkscape
+ * input extension interface.
+ *
+ * Authors:
+ * Fridrich Strba (fridrich.strba@bluewin.ch)
+ *
+ * Copyright (C) 2012 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef __EXTENSION_INTERNAL_CDROUTPUT_H__
+#define __EXTENSION_INTERNAL_CDROUTPUT_H__
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef WITH_LIBCDR
+
+#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H
+#include <glibmm/threads.h>
+#endif
+
+#include <gtkmm/dialog.h>
+
+#include "../implementation/implementation.h"
+
+namespace Inkscape {
+namespace Extension {
+namespace Internal {
+
+class CdrInput : public Inkscape::Extension::Implementation::Implementation {
+ CdrInput () { };
+public:
+ SPDocument *open( Inkscape::Extension::Input *mod,
+ const gchar *uri );
+ static void init( void );
+
+};
+
+} } } /* namespace Inkscape, Extension, Implementation */
+
+#endif /* WITH_LIBCDR */
+#endif /* __EXTENSION_INTERNAL_CDROUTPUT_H__ */
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp
new file mode 100644
index 000000000..eae3bfb5a
--- /dev/null
+++ b/src/extension/internal/emf-inout.cpp
@@ -0,0 +1,3547 @@
+/** @file
+ * @brief Windows-only Enhanced Metafile input and output.
+ */
+/* Authors:
+ * Ulf Erikson <ulferikson@users.sf.net>
+ * Jon A. Cruz <jon@joncruz.org>
+ * David Mathog
+ * 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
+
+//#include <png.h> //This must precede text_reassemble.h or it blows up in pngconf.h when compiling
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <libuemf/symbol_convert.h>
+
+#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 "clear-n_.h"
+#include "document.h"
+#include "util/units.h"
+#include "shape-editor.h"
+#include "sp-namedview.h"
+#include "document-undo.h"
+#include "inkscape.h"
+
+#include "emf-print.h"
+#include "emf-inout.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 U_RECTL rc_old = rectl_set(pointl_set(-1,-1),pointl_set(-1,-1));
+static bool clipset = false;
+static uint32_t ICMmode = 0; // not used yet, but code to read it from EMF implemented
+static uint32_t BLTmode = 0;
+
+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;
+}
+
+
+void
+Emf::print_document_to_file(SPDocument *doc, const gchar *filename)
+{
+ Inkscape::Extension::Print *mod;
+ SPPrintContext context;
+ const gchar *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);
+ (void) 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_FixPPTLinGrad = mod->get_param_bool("FixPPTLinGrad"); // allow native rectangular linear gradient
+ bool new_FixPPTPatternAsHatch = mod->get_param_bool("FixPPTPatternAsHatch"); // force all patterns as standard EMF hatch
+ bool new_FixImageRot = mod->get_param_bool("FixImageRot"); // remove rotations on images
+
+ TableGen( //possibly regenerate the unicode-convert tables
+ mod->get_param_bool("TnrToSymbol"),
+ 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("FixPPTLinGrad",new_FixPPTLinGrad);
+ ext->set_param_bool("FixPPTPatternAsHatch",new_FixPPTPatternAsHatch);
+ ext->set_param_bool("FixImageRot",new_FixImageRot);
+ ext->set_param_bool("textToPath", new_val);
+
+ print_document_to_file(doc, filename);
+
+ return;
+}
+
+
+/* given the transformation matrix from worldTransform return the scale in the matrix part. Assumes that the
+ matrix is not used to skew, invert, or make another distorting transformation. */
+double Emf::current_scale(PEMF_CALLBACK_DATA d){
+ double scale =
+ d->dc[d->level].worldTransform.eM11 * d->dc[d->level].worldTransform.eM22 -
+ d->dc[d->level].worldTransform.eM12 * d->dc[d->level].worldTransform.eM21;
+ if(scale <= 0.0)scale=1.0; /* something is dreadfully wrong with the matrix, but do not crash over it */
+ scale=sqrt(scale);
+ return(scale);
+}
+
+/* given the transformation matrix from worldTransform and the current x,y position in inkscape coordinates,
+ generate an SVG transform that gives the same amount of rotation, no scaling, and maps x,y back onto x,y. This is used for
+ rotating objects when the location of at least one point in that object is known. Returns:
+ "matrix(a,b,c,d,e,f)" (WITH the double quotes)
+*/
+std::string Emf::current_matrix(PEMF_CALLBACK_DATA d, double x, double y, int useoffset){
+ std::stringstream cxform;
+ double scale = current_scale(d);
+ cxform << "\"matrix(";
+ cxform << d->dc[d->level].worldTransform.eM11/scale; cxform << ",";
+ cxform << d->dc[d->level].worldTransform.eM12/scale; cxform << ",";
+ cxform << d->dc[d->level].worldTransform.eM21/scale; cxform << ",";
+ cxform << d->dc[d->level].worldTransform.eM22/scale; cxform << ",";
+ if(useoffset){
+ /* for the "new" coordinates drop the worldtransform translations, not used here */
+ double newx = x * d->dc[d->level].worldTransform.eM11/scale + y * d->dc[d->level].worldTransform.eM21/scale;
+ double newy = x * d->dc[d->level].worldTransform.eM12/scale + y * d->dc[d->level].worldTransform.eM22/scale;
+ cxform << x - newx; cxform << ",";
+ cxform << y - newy;
+ }
+ else {
+ cxform << "0,0";
+ }
+ cxform << ")\"";
+ return(cxform.str());
+}
+
+/* given the transformation matrix from worldTransform return the rotation angle in radians.
+ counter clocwise from the x axis. */
+double Emf::current_rotation(PEMF_CALLBACK_DATA d){
+ return -std::atan2(d->dc[d->level].worldTransform.eM12, d->dc[d->level].worldTransform.eM11);
+}
+
+/* Add another 100 blank slots to the hatches array.
+*/
+void Emf::enlarge_hatches(PEMF_CALLBACK_DATA d){
+ d->hatches.size += 100;
+ d->hatches.strings = (char **) realloc(d->hatches.strings,d->hatches.size * sizeof(char *));
+}
+
+/* See if the pattern name is already in the list. If it is return its position (1->n, not 1-n-1)
+*/
+int Emf::in_hatches(PEMF_CALLBACK_DATA d, char *test){
+ int i;
+ for(i=0; i<d->hatches.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 <defs>.
+ This is also used to add the path part of the hatches, which they reference with a xlink:href
+*/
+uint32_t Emf::add_hatch(PEMF_CALLBACK_DATA d, uint32_t hatchType, U_COLORREF hatchColor){
+ char hatchname[64]; // big enough
+ char hpathname[64]; // big enough
+ char hbkname[64]; // big enough
+ char tmpcolor[8];
+ char bkcolor[8];
+ uint32_t idx;
+
+ switch(hatchType){
+ case U_HS_SOLIDTEXTCLR:
+ case U_HS_DITHEREDTEXTCLR:
+ sprintf(tmpcolor,"%6.6X",sethexcolor(d->dc[d->level].textColor));
+ break;
+ case U_HS_SOLIDBKCLR:
+ case U_HS_DITHEREDBKCLR:
+ sprintf(tmpcolor,"%6.6X",sethexcolor(d->dc[d->level].bkColor));
+ break;
+ default:
+ sprintf(tmpcolor,"%6.6X",sethexcolor(hatchColor));
+ break;
+ }
+
+ /* For both bkMode types set the PATH + FOREGROUND COLOR for the indicated standard hatch.
+ This will be used late to compose, or recompose the transparent or opaque final hatch.*/
+
+ std::string refpath; // used to reference later the path pieces which are about to be created
+ sprintf(hpathname,"EMFhpath%d_%s",hatchType,tmpcolor);
+ idx = in_hatches(d,hpathname);
+ if(!idx){ // add path/color if not already present
+ if(d->hatches.count == d->hatches.size){ enlarge_hatches(d); }
+ d->hatches.strings[d->hatches.count++]=strdup(hpathname);
+
+ *(d->defs) += "\n";
+ switch(hatchType){
+ case U_HS_HORIZONTAL:
+ *(d->defs) += " <path id=\"";
+ *(d->defs) += hpathname;
+ *(d->defs) += "\" d=\"M 0 0 6 0\" style=\"fill:none;stroke:#";
+ *(d->defs) += tmpcolor;
+ *(d->defs) += "\" />\n";
+ break;
+ case U_HS_VERTICAL:
+ *(d->defs) += " <path id=\"";
+ *(d->defs) += hpathname;
+ *(d->defs) += "\" d=\"M 0 0 0 6\" style=\"fill:none;stroke:#";
+ *(d->defs) += tmpcolor;
+ *(d->defs) += "\" />\n";
+ break;
+ case U_HS_FDIAGONAL:
+ *(d->defs) += " <line id=\"sub";
+ *(d->defs) += hpathname;
+ *(d->defs) += "\" x1=\"-1\" y1=\"-1\" x2=\"7\" y2=\"7\" stroke=\"#";
+ *(d->defs) += tmpcolor;
+ *(d->defs) += "\"/>\n";
+ break;
+ case U_HS_BDIAGONAL:
+ *(d->defs) += " <line id=\"sub";
+ *(d->defs) += hpathname;
+ *(d->defs) += "\" x1=\"-1\" y1=\"7\" x2=\"7\" y2=\"-1\" stroke=\"#";
+ *(d->defs) += tmpcolor;
+ *(d->defs) += "\"/>\n";
+ break;
+ case U_HS_CROSS:
+ *(d->defs) += " <path id=\"";
+ *(d->defs) += hpathname;
+ *(d->defs) += "\" d=\"M 0 0 6 0 M 0 0 0 6\" style=\"fill:none;stroke:#";
+ *(d->defs) += tmpcolor;
+ *(d->defs) += "\" />\n";
+ break;
+ case U_HS_DIAGCROSS:
+ *(d->defs) += " <line id=\"subfd";
+ *(d->defs) += hpathname;
+ *(d->defs) += "\" x1=\"-1\" y1=\"-1\" x2=\"7\" y2=\"7\" stroke=\"#";
+ *(d->defs) += tmpcolor;
+ *(d->defs) += "\"/>\n";
+ *(d->defs) += " <line id=\"subbd";
+ *(d->defs) += hpathname;
+ *(d->defs) += "\" x1=\"-1\" y1=\"7\" x2=\"7\" y2=\"-1\" stroke=\"#";
+ *(d->defs) += tmpcolor;
+ *(d->defs) += "\"/>\n";
+ break;
+ case U_HS_SOLIDCLR:
+ case U_HS_DITHEREDCLR:
+ case U_HS_SOLIDTEXTCLR:
+ case U_HS_DITHEREDTEXTCLR:
+ case U_HS_SOLIDBKCLR:
+ case U_HS_DITHEREDBKCLR:
+ default:
+ *(d->defs) += " <path id=\"";
+ *(d->defs) += hpathname;
+ *(d->defs) += "\" d=\"M 0 0 6 0 6 6 0 6 z\" style=\"fill:#";
+ *(d->defs) += tmpcolor;
+ *(d->defs) += ";stroke:none";
+ *(d->defs) += "\" />\n";
+ break;
+ }
+ }
+
+ // References to paths possibly just created above. These will be used in the actual patterns.
+ switch(hatchType){
+ case U_HS_HORIZONTAL:
+ case U_HS_VERTICAL:
+ case U_HS_CROSS:
+ case U_HS_SOLIDCLR:
+ case U_HS_DITHEREDCLR:
+ case U_HS_SOLIDTEXTCLR:
+ case U_HS_DITHEREDTEXTCLR:
+ case U_HS_SOLIDBKCLR:
+ case U_HS_DITHEREDBKCLR:
+ default:
+ refpath += " <use xlink:href=\"#";
+ refpath += hpathname;
+ refpath += "\" />\n";
+ break;
+ case U_HS_FDIAGONAL:
+ case U_HS_BDIAGONAL:
+ refpath += " <use xlink:href=\"#sub";
+ refpath += hpathname;
+ refpath += "\" />\n";
+ refpath += " <use xlink:href=\"#sub";
+ refpath += hpathname;
+ refpath += "\" transform=\"translate(6,0)\" />\n";
+ refpath += " <use xlink:href=\"#sub";
+ refpath += hpathname;
+ refpath += "\" transform=\"translate(-6,0)\" />\n";
+ break;
+ case U_HS_DIAGCROSS:
+ refpath += " <use xlink:href=\"#subfd";
+ refpath += hpathname;
+ refpath += "\" />\n";
+ refpath += " <use xlink:href=\"#subfd";
+ refpath += hpathname;
+ refpath += "\" transform=\"translate(6,0)\"/>\n";
+ refpath += " <use xlink:href=\"#subfd";
+ refpath += hpathname;
+ refpath += "\" transform=\"translate(-6,0)\"/>\n";
+ refpath += " <use xlink:href=\"#subbd";
+ refpath += hpathname;
+ refpath += "\" />\n";
+ refpath += " <use xlink:href=\"#subbd";
+ refpath += hpathname;
+ refpath += "\" transform=\"translate(6,0)\"/>\n";
+ refpath += " <use xlink:href=\"#subbd";
+ refpath += hpathname;
+ refpath += "\" transform=\"translate(-6,0)\"/>\n";
+ break;
+ }
+
+ if(d->dc[d->level].bkMode == U_TRANSPARENT || hatchType >= U_HS_SOLIDCLR){
+ sprintf(hatchname,"EMFhatch%d_%s",hatchType,tmpcolor);
+ sprintf(hpathname,"EMFhpath%d_%s",hatchType,tmpcolor);
+ idx = in_hatches(d,hatchname);
+ if(!idx){ // add it if not already present
+ if(d->hatches.count == d->hatches.size){ enlarge_hatches(d); }
+ d->hatches.strings[d->hatches.count++]=strdup(hatchname);
+ *(d->defs) += "\n";
+ *(d->defs) += " <pattern id=\"";
+ *(d->defs) += hatchname;
+ *(d->defs) += "\" xlink:href=\"#EMFhbasepattern\">\n";
+ *(d->defs) += refpath;
+ *(d->defs) += " </pattern>\n";
+ idx = d->hatches.count;
+ }
+ }
+ else { // bkMode==U_OPAQUE
+ /* Set up an object in the defs for this background, if there is not one already there */
+ sprintf(bkcolor,"%6.6X",sethexcolor(d->dc[d->level].bkColor));
+ sprintf(hbkname,"EMFhbkclr_%s",bkcolor);
+ idx = in_hatches(d,hbkname);
+ if(!idx){ // add path/color if not already present. Hatchtype is not needed in the name.
+ if(d->hatches.count == d->hatches.size){ enlarge_hatches(d); }
+ d->hatches.strings[d->hatches.count++]=strdup(hbkname);
+
+ *(d->defs) += "\n";
+ *(d->defs) += " <rect id=\"";
+ *(d->defs) += hbkname;
+ *(d->defs) += "\" x=\"0\" y=\"0\" width=\"6\" height=\"6\" fill=\"#";
+ *(d->defs) += bkcolor;
+ *(d->defs) += "\" />\n";
+ }
+
+ // this is the pattern, its name will show up in Inkscape's pattern selector
+ sprintf(hatchname,"EMFhatch%d_%s_%s",hatchType,tmpcolor,bkcolor);
+ idx = in_hatches(d,hatchname);
+ if(!idx){ // add it if not already present
+ if(d->hatches.count == d->hatches.size){ enlarge_hatches(d); }
+ d->hatches.strings[d->hatches.count++]=strdup(hatchname);
+ *(d->defs) += "\n";
+ *(d->defs) += " <pattern id=\"";
+ *(d->defs) += hatchname;
+ *(d->defs) += "\" xlink:href=\"#EMFhbasepattern\">\n";
+ *(d->defs) += " <use xlink:href=\"#";
+ *(d->defs) += hbkname;
+ *(d->defs) += "\" />\n";
+ *(d->defs) += refpath;
+ *(d->defs) += " </pattern>\n";
+ idx = d->hatches.count;
+ }
+ }
+ return(idx-1);
+}
+
+/* Add another 100 blank slots to the images array.
+*/
+void Emf::enlarge_images(PEMF_CALLBACK_DATA d){
+ d->images.size += 100;
+ d->images.strings = (char **) realloc(d->images.strings,d->images.size * sizeof(char *));
+}
+
+/* See if the image string is already in the list. If it is return its position (1->n, not 1-n-1)
+*/
+int Emf::in_images(PEMF_CALLBACK_DATA d, char *test){
+ int i;
+ for(i=0; i<d->images.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 <defs>.
+
+ U_EMRCREATEMONOBRUSH records only work when the bitmap is monochrome. If we hit one that isn't
+ set idx to 2^32-1 and let the caller handle it.
+*/
+uint32_t Emf::add_image(PEMF_CALLBACK_DATA d, void *pEmr, uint32_t cbBits, uint32_t cbBmi,
+ uint32_t iUsage, uint32_t offBits, uint32_t offBmi){
+
+ uint32_t idx;
+ char imagename[64]; // big enough
+ char imrotname[64]; // big enough
+ char xywh[64]; // big enough
+ int dibparams = U_BI_UNKNOWN; // type of image not yet determined
+
+ MEMPNG mempng; // PNG in memory comes back in this
+ mempng.buffer = NULL;
+
+ char *rgba_px = NULL; // RGBA pixels
+ const char *px = NULL; // DIB pixels
+ const U_RGBQUAD *ct = NULL; // DIB color table
+ U_RGBQUAD ct2[2];
+ uint32_t width, height, colortype, numCt, invert; // if needed these values will be set in get_DIB_params
+ if(cbBits && cbBmi && (iUsage == U_DIB_RGB_COLORS)){
+ // next call returns pointers and values, but allocates no memory
+ dibparams = get_DIB_params(pEmr, offBits, offBmi, &px, (const U_RGBQUAD **) &ct,
+ &numCt, &width, &height, &colortype, &invert);
+ if(dibparams ==U_BI_RGB){
+ // U_EMRCREATEMONOBRUSH uses text/bk colors instead of what is in the color map.
+ if(((PU_EMR)pEmr)->iType == U_EMR_CREATEMONOBRUSH){
+ if(numCt==2){
+ ct2[0] = U_RGB2BGR(d->dc[d->level].textColor);
+ ct2[1] = U_RGB2BGR(d->dc[d->level].bkColor);
+ ct = &ct2[0];
+ }
+ else { // This record is invalid, nothing more to do here, let caller handle it
+ return(U_EMR_INVALID);
+ }
+ }
+
+ if(!DIB_to_RGBA(
+ px, // DIB pixel array
+ ct, // DIB color table
+ numCt, // DIB color table number of entries
+ &rgba_px, // U_RGBA pixel array (32 bits), created by this routine, caller must free.
+ width, // Width of pixel array in record
+ height, // Height of pixel array in record
+ colortype, // DIB BitCount Enumeration
+ numCt, // Color table used if not 0
+ invert // If DIB rows are in opposite order from RGBA rows
+ )){
+ toPNG( // Get the image from the RGBA px into mempng
+ &mempng,
+ width, height, // of the SRC bitmap
+ rgba_px
+ );
+ free(rgba_px);
+ }
+ }
+ }
+
+ gchar *base64String=NULL;
+ if(dibparams == U_BI_JPEG || dibparams==U_BI_PNG){ // image was binary png or jpg in source file
+ base64String = g_base64_encode((guchar*) px, numCt );
+ }
+ else if(mempng.buffer){ // image was DIB in source file, converted to png in this routine
+ base64String = g_base64_encode((guchar*) mempng.buffer, mempng.size );
+ free(mempng.buffer);
+ }
+ else { // unknown or unsupported image type or failed conversion, insert the common bad image picture
+ width = 3;
+ height = 4;
+ base64String = bad_image_png();
+ }
+
+ idx = in_images(d, (char *) base64String);
+ if(!idx){ // add it if not already present - we looked at the actual data for comparison
+ if(d->images.count == d->images.size){ enlarge_images(d); }
+ idx = d->images.count;
+ d->images.strings[d->images.count++]=strdup(base64String);
+
+ sprintf(imagename,"EMFimage%d",idx++);
+ sprintf(xywh," x=\"0\" y=\"0\" width=\"%d\" height=\"%d\" ",width,height); // reuse this buffer
+
+ *(d->defs) += "\n";
+ *(d->defs) += " <image id=\"";
+ *(d->defs) += imagename;
+ *(d->defs) += "\"\n ";
+ *(d->defs) += xywh;
+ *(d->defs) += "\n";
+ if(dibparams == U_BI_JPEG){ *(d->defs) += " xlink:href=\"data:image/jpeg;base64,"; }
+ else { *(d->defs) += " xlink:href=\"data:image/png;base64,"; }
+ *(d->defs) += base64String;
+ *(d->defs) += "\"\n";
+ *(d->defs) += " preserveAspectRatio=\"none\"\n";
+ *(d->defs) += " />\n";
+
+
+ *(d->defs) += "\n";
+ *(d->defs) += " <pattern id=\"";
+ *(d->defs) += imagename;
+ *(d->defs) += "_ref\"\n ";
+ *(d->defs) += xywh;
+ *(d->defs) += "\n patternUnits=\"userSpaceOnUse\"";
+ *(d->defs) += " >\n";
+ *(d->defs) += " <use id=\"";
+ *(d->defs) += imagename;
+ *(d->defs) += "_ign\" ";
+ *(d->defs) += " xlink:href=\"#";
+ *(d->defs) += imagename;
+ *(d->defs) += "\" />\n";
+ *(d->defs) += " ";
+ *(d->defs) += " </pattern>\n";
+ }
+ g_free(base64String);//wait until this point to free because it might be a duplicate image
+
+ /* image allows the inner image to be rotated nicely, load this one second only if needed
+ imagename retained from above
+ Here comes a dreadful hack. How do we determine if this rotation of the base image has already
+ been loaded? The image names contain no identifying information, they are just numbered sequentially.
+ So the rotated name is EMFrotimage###_XXXXXX, where ### is the number of the referred to image, and
+ XXXX is the rotation in radians x 1000000 and truncated. That is then stored in BASE64 as the "image".
+ The corresponding SVG generated though is not for an image, but a reference to an image.
+ The name of the pattern MUST stil be EMFimage###_ref or output_style() will not be able to use it.
+ */
+ if(current_rotation(d) >= 0.00001 || current_rotation(d) <= -0.00001){ /* some rotation, allow a little rounding error around 0 degrees */
+ int tangle = round(current_rotation(d)*1000000.0);
+ sprintf(imrotname,"EMFrotimage%d_%d",idx-1,tangle);
+ base64String = g_base64_encode((guchar*) imrotname, strlen(imrotname) );
+ idx = in_images(d, (char *) base64String); // scan for this "image"
+ if(!idx){
+ if(d->images.count == d->images.size){ enlarge_images(d); }
+ idx = d->images.count;
+ d->images.strings[d->images.count++]=strdup(base64String);
+ sprintf(imrotname,"EMFimage%d",idx++);
+
+ *(d->defs) += "\n";
+ *(d->defs) += " <pattern\n";
+ *(d->defs) += " id=\"";
+ *(d->defs) += imrotname;
+ *(d->defs) += "_ref\"\n";
+ *(d->defs) += " xlink:href=\"#";
+ *(d->defs) += imagename;
+ *(d->defs) += "_ref\"\n";
+ *(d->defs) += " patternTransform=";
+ *(d->defs) += current_matrix(d, 0.0, 0.0, 0); //j use offset 0,0
+ *(d->defs) += " />\n";
+ }
+ g_free(base64String);
+ }
+
+ return(idx-1);
+}
+
+/* Add another 100 blank slots to the gradients array.
+*/
+void Emf::enlarge_gradients(PEMF_CALLBACK_DATA d){
+ d->gradients.size += 100;
+ d->gradients.strings = (char **) realloc(d->gradients.strings,d->gradients.size * sizeof(char *));
+}
+
+/* See if the gradient name is already in the list. If it is return its position (1->n, not 1-n-1)
+*/
+int Emf::in_gradients(PEMF_CALLBACK_DATA d, char *test){
+ int i;
+ for(i=0; i<d->gradients.count; i++){
+ if(strcmp(test,d->gradients.strings[i])==0)return(i+1);
+ }
+ return(0);
+}
+
+U_COLORREF trivertex_to_colorref(U_TRIVERTEX tv){
+ U_COLORREF uc;
+ uc.Red = tv.Red >> 8;
+ uc.Green = tv.Green >> 8;
+ uc.Blue = tv.Blue >> 8;
+ uc.Reserved = tv.Alpha >> 8; // Not used
+ return(uc);
+}
+
+/* (Conditionally) add a gradient. If a matching gradient already exists nothing happens. If one
+ does not exist it is added to the gradients list and also entered into <defs>.
+ Only call this with H or V gradient, not a triangle.
+*/
+uint32_t Emf::add_gradient(PEMF_CALLBACK_DATA d, uint32_t gradientType, U_TRIVERTEX tv1, U_TRIVERTEX tv2){
+ char hgradname[64]; // big enough
+ char tmpcolor1[8];
+ char tmpcolor2[8];
+ char gradc;
+ uint32_t idx;
+ std::string x2,y2;
+
+ U_COLORREF gradientColor1 = trivertex_to_colorref(tv1);
+ U_COLORREF gradientColor2 = trivertex_to_colorref(tv2);
+
+
+ sprintf(tmpcolor1,"%6.6X",sethexcolor(gradientColor1));
+ sprintf(tmpcolor2,"%6.6X",sethexcolor(gradientColor2));
+ switch(gradientType){
+ case U_GRADIENT_FILL_RECT_H:
+ gradc='H';
+ x2="100";
+ y2="0";
+ break;
+ case U_GRADIENT_FILL_RECT_V:
+ gradc='V';
+ x2="0";
+ y2="100";
+ break;
+ default: // this should never happen, but fill these in to avoid compiler warnings
+ gradc='!';
+ x2="0";
+ y2="0";
+ break;
+ }
+
+ /* Even though the gradient was defined as Horizontal or Vertical if the rectangle is rotated it needs to
+ be at some other alignment, and that needs gradienttransform. Set the name using the same sort of hack
+ as for add_image.
+ */
+ int tangle = round(current_rotation(d)*1000000.0);
+ sprintf(hgradname,"LinGrd%c_%s_%s_%d",gradc,tmpcolor1,tmpcolor2,tangle);
+
+ idx = in_gradients(d,hgradname);
+ if(!idx){ // gradient does not yet exist
+ if(d->gradients.count == d->gradients.size){ enlarge_gradients(d); }
+ d->gradients.strings[d->gradients.count++]=strdup(hgradname);
+ idx = d->gradients.count;
+ SVGOStringStream stmp;
+ stmp << " <linearGradient id=\"";
+ stmp << hgradname;
+ stmp << "\" x1=\"";
+ stmp << pix_to_x_point(d, tv1.x , tv1.y);
+ stmp << "\" y1=\"";
+ stmp << pix_to_y_point(d, tv1.x , tv1.y);
+ stmp << "\" x2=\"";
+ if(gradc=='H'){ // UR corner
+ stmp << pix_to_x_point(d, tv2.x , tv1.y);
+ stmp << "\" y2=\"";
+ stmp << pix_to_y_point(d, tv2.x , tv1.y);
+ }
+ else { // LL corner
+ stmp << pix_to_x_point(d, tv1.x , tv2.y);
+ stmp << "\" y2=\"";
+ stmp << pix_to_y_point(d, tv1.x , tv2.y);
+ }
+ stmp << "\" gradientTransform=\"(1,0,0,1,0,0)\"";
+ stmp << " gradientUnits=\"userSpaceOnUse\"\n";
+ stmp << ">\n";
+ stmp << " <stop offset=\"0\" style=\"stop-color:#";
+ stmp << tmpcolor1;
+ stmp << ";stop-opacity:1\" />\n";
+ stmp << " <stop offset=\"1\" style=\"stop-color:#";
+ stmp << tmpcolor2;
+ stmp << ";stop-opacity:1\" />\n";
+ stmp << " </linearGradient>\n";
+ *(d->defs) += stmp.str().c_str();
+ }
+
+ return(idx-1);
+}
+
+
+
+void
+Emf::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: // invert pattern
+ fill_rgb[0] = 1.0 - fill_rgb[0];
+ fill_rgb[1] = 1.0 - fill_rgb[1];
+ fill_rgb[2] = 1.0 - fill_rgb[2];
+ break;
+ case U_SRCINVERT: // treat all of these as black
+ 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_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:
+ case U_PATCOPY:
+ 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_LINEAR_GRADIENT:
+ 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 the stroke is the same as the fill, and the right size not to change the end size of the object, do not do it separately
+ if(
+ (d->dc[d->level].fill_set ) &&
+ (d->dc[d->level].stroke_set ) &&
+ (d->dc[d->level].style.stroke_width.value == 1 ) &&
+ (d->dc[d->level].fill_mode == d->dc[d->level].stroke_mode) &&
+ (
+ (d->dc[d->level].fill_mode != DRAW_PAINT) ||
+ (
+ (fill_rgb[0]==stroke_rgb[0]) &&
+ (fill_rgb[1]==stroke_rgb[1]) &&
+ (fill_rgb[2]==stroke_rgb[2])
+ )
+ )
+ ){
+ d->dc[d->level].stroke_set = false;
+ }
+ }
+
+ 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_LINEAR_GRADIENT:
+ 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_dasharray.values.empty() )
+ {
+ tmp_style << "stroke-dasharray:";
+ for (unsigned i=0; i<d->dc[d->level].style.stroke_dasharray.values.size(); i++) {
+ if (i)
+ tmp_style << ",";
+ tmp_style << d->dc[d->level].style.stroke_dasharray.values[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();
+}
+
+
+double
+Emf::_pix_x_to_point(PEMF_CALLBACK_DATA d, double px)
+{
+ double scale = (d->dc[d->level].ScaleInX ? d->dc[d->level].ScaleInX : 1.0);
+ double tmp;
+ tmp = ((((double) (px - d->dc[d->level].winorg.x))*scale) + d->dc[d->level].vieworg.x) * d->D2PscaleX;
+ tmp -= d->ulCornerOutX; //The EMF boundary rectangle can be anywhere, place its upper left corner in the Inkscape upper left corner
+ return(tmp);
+}
+
+double
+Emf::_pix_y_to_point(PEMF_CALLBACK_DATA d, double py)
+{
+ double scale = (d->dc[d->level].ScaleInY ? d->dc[d->level].ScaleInY : 1.0);
+ double tmp;
+ tmp = ((((double) (py - d->dc[d->level].winorg.y))*scale) * d->E2IdirY + d->dc[d->level].vieworg.y) * d->D2PscaleY;
+ tmp -= d->ulCornerOutY; //The EMF boundary rectangle can be anywhere, place its upper left corner in the Inkscape upper left corner
+ return(tmp);
+}
+
+
+double
+Emf::pix_to_x_point(PEMF_CALLBACK_DATA d, double px, double py)
+{
+ double wpx = px * d->dc[d->level].worldTransform.eM11 + py * d->dc[d->level].worldTransform.eM21 + d->dc[d->level].worldTransform.eDx;
+ double x = _pix_x_to_point(d, wpx);
+
+ return x;
+}
+
+double
+Emf::pix_to_y_point(PEMF_CALLBACK_DATA d, double px, double py)
+{
+
+ double wpy = px * d->dc[d->level].worldTransform.eM12 + py * d->dc[d->level].worldTransform.eM22 + d->dc[d->level].worldTransform.eDy;
+ double y = _pix_y_to_point(d, wpy);
+
+ return y;
+
+}
+
+double
+Emf::pix_to_abs_size(PEMF_CALLBACK_DATA d, double px)
+{
+ double ppx = fabs(px * (d->dc[d->level].ScaleInX ? d->dc[d->level].ScaleInX : 1.0) * d->D2PscaleX * current_scale(d));
+ return ppx;
+}
+
+/* returns "x,y" (without the quotes) in inkscape coordinates for a pair of EMF x,y coordinates
+*/
+std::string Emf::pix_to_xy(PEMF_CALLBACK_DATA d, double x, double y){
+ std::stringstream cxform;
+ cxform << pix_to_x_point(d,x,y);
+ cxform << ",";
+ cxform << pix_to_y_point(d,x,y);
+ return(cxform.str());
+}
+
+
+void
+Emf::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 penstyle = (pEmr->lopn.lopnStyle & U_PS_STYLE_MASK);
+ if (!d->dc[d->level].style.stroke_dasharray.values.empty() && (d->level==0 || (d->level>0 && d->dc[d->level].style.stroke_dasharray.values!=d->dc[d->level-1].style.stroke_dasharray.values)))
+ d->dc[d->level].style.stroke_dasharray.values.clear();
+ if (penstyle==U_PS_DASH || penstyle==U_PS_DASHDOT || penstyle==U_PS_DASHDOTDOT) {
+ d->dc[d->level].style.stroke_dasharray.values.push_back( 3 );
+ d->dc[d->level].style.stroke_dasharray.values.push_back( 1 );
+ }
+ if (penstyle==U_PS_DOT || penstyle==U_PS_DASHDOT || penstyle==U_PS_DASHDOTDOT) {
+ d->dc[d->level].style.stroke_dasharray.values.push_back( 1 );
+ d->dc[d->level].style.stroke_dasharray.values.push_back( 1 );
+ }
+ if (penstyle==U_PS_DASHDOTDOT) {
+ d->dc[d->level].style.stroke_dasharray.values.push_back( 1 );
+ d->dc[d->level].style.stroke_dasharray.values.push_back( 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_abs_size( d, pEmr->lopn.lopnWidth.x );
+ d->level = cur_level;
+ d->dc[d->level].style.stroke_width.value = pen_width;
+ } else { // this stroke should always be rendered as 1 pixel wide, independent of zoom level (can that be done in SVG?)
+ //d->dc[d->level].style.stroke_width.value = 1.0;
+ int cur_level = d->level;
+ d->level = d->emf_obj[index].level;
+ double pen_width = pix_to_abs_size( 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 );
+}
+
+
+void
+Emf::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) {
+ if (!d->dc[d->level].style.stroke_dasharray.values.empty() && (d->level==0 || (d->level>0 && d->dc[d->level].style.stroke_dasharray.values!=d->dc[d->level-1].style.stroke_dasharray.values)))
+ d->dc[d->level].style.stroke_dasharray.values.clear();
+ for (unsigned int i=0; i<pEmr->elp.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_abs_size( d, pEmr->elp.elpStyleEntry[i] );
+ double dash_length = pEmr->elp.elpStyleEntry[i];
+ d->level = cur_level;
+ d->dc[d->level].style.stroke_dasharray.values[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 penstyle = (pEmr->elp.elpPenStyle & U_PS_STYLE_MASK);
+ if (!d->dc[d->level].style.stroke_dasharray.values.empty() && (d->level==0 || (d->level>0 && d->dc[d->level].style.stroke_dasharray.values!=d->dc[d->level-1].style.stroke_dasharray.values)))
+ d->dc[d->level].style.stroke_dasharray.values.clear();
+ if (penstyle==U_PS_DASH || penstyle==U_PS_DASHDOT || penstyle==U_PS_DASHDOTDOT) {
+ d->dc[d->level].style.stroke_dasharray.values.push_back( 3 );
+ d->dc[d->level].style.stroke_dasharray.values.push_back( 2 );
+ }
+ if (penstyle==U_PS_DOT || penstyle==U_PS_DASHDOT || penstyle==U_PS_DASHDOTDOT) {
+ d->dc[d->level].style.stroke_dasharray.values.push_back( 1 );
+ d->dc[d->level].style.stroke_dasharray.values.push_back( 2 );
+ }
+ if (penstyle==U_PS_DASHDOTDOT) {
+ d->dc[d->level].style.stroke_dasharray.values.push_back( 1 );
+ d->dc[d->level].style.stroke_dasharray.values.push_back( 2 );
+ }
+
+ d->dc[d->level].style.stroke_dasharray.set = 1;
+ break;
+ }
+ case U_PS_SOLID:
+/* includes these for now, some should maybe not be in here
+ case U_PS_NULL:
+ case U_PS_INSIDEFRAME:
+ case U_PS_ALTERNATE:
+ case U_PS_STYLE_MASK:
+*/
+ default:
+ {
+ d->dc[d->level].style.stroke_dasharray.set = 0;
+ 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) { // draw nothing, but fill out all the values with something
+ double r, g, b;
+ r = SP_COLOR_U_TO_F( U_RGBAGetR(d->dc[d->level].textColor));
+ g = SP_COLOR_U_TO_F( U_RGBAGetG(d->dc[d->level].textColor));
+ b = SP_COLOR_U_TO_F( U_RGBAGetB(d->dc[d->level].textColor));
+ d->dc[d->level].style.stroke.value.color.set( r, g, b );
+ d->dc[d->level].style.stroke_width.value = 0;
+ d->dc[d->level].stroke_set = false;
+ d->dc[d->level].stroke_mode = DRAW_PAINT;
+ }
+ else {
+ if (pEmr->elp.elpWidth) {
+ int cur_level = d->level;
+ d->level = d->emf_obj[index].level;
+ double pen_width = pix_to_abs_size( d, pEmr->elp.elpWidth );
+ d->level = cur_level;
+ d->dc[d->level].style.stroke_width.value = pen_width;
+ } else { // this stroke should always be rendered as 1 pixel wide, independent of zoom level (can that be done in SVG?)
+ //d->dc[d->level].style.stroke_width.value = 1.0;
+ int cur_level = d->level;
+ d->level = d->emf_obj[index].level;
+ double pen_width = pix_to_abs_size( d, 1 );
+ d->level = cur_level;
+ d->dc[d->level].style.stroke_width.value = pen_width;
+ }
+
+ if( pEmr->elp.elpBrushStyle == U_BS_SOLID){
+ double r, g, b;
+ r = SP_COLOR_U_TO_F( U_RGBAGetR(pEmr->elp.elpColor) );
+ g = SP_COLOR_U_TO_F( U_RGBAGetG(pEmr->elp.elpColor) );
+ b = SP_COLOR_U_TO_F( U_RGBAGetB(pEmr->elp.elpColor) );
+ d->dc[d->level].style.stroke.value.color.set( r, g, b );
+ d->dc[d->level].stroke_mode = DRAW_PAINT;
+ d->dc[d->level].stroke_set = true;
+ }
+ else if(pEmr->elp.elpBrushStyle == U_BS_HATCHED){
+ d->dc[d->level].stroke_idx = add_hatch(d, pEmr->elp.elpHatch, pEmr->elp.elpColor);
+ d->dc[d->level].stroke_recidx = index; // used if the hatch needs to be redone due to bkMode, textmode, etc. changes
+ d->dc[d->level].stroke_mode = DRAW_PATTERN;
+ d->dc[d->level].stroke_set = true;
+ }
+ else if(pEmr->elp.elpBrushStyle == U_BS_DIBPATTERN || pEmr->elp.elpBrushStyle == U_BS_DIBPATTERNPT){
+ d->dc[d->level].stroke_idx = add_image(d, pEmr, pEmr->cbBits, pEmr->cbBmi, *(uint32_t *) &(pEmr->elp.elpColor), pEmr->offBits, pEmr->offBmi);
+ d->dc[d->level].stroke_mode = DRAW_IMAGE;
+ d->dc[d->level].stroke_set = true;
+ }
+ else { // U_BS_PATTERN and anything strange that falls in, stroke is solid textColor
+ double r, g, b;
+ r = SP_COLOR_U_TO_F( U_RGBAGetR(d->dc[d->level].textColor));
+ g = SP_COLOR_U_TO_F( U_RGBAGetG(d->dc[d->level].textColor));
+ b = SP_COLOR_U_TO_F( U_RGBAGetB(d->dc[d->level].textColor));
+ d->dc[d->level].style.stroke.value.color.set( r, g, b );
+ d->dc[d->level].stroke_mode = DRAW_PAINT;
+ d->dc[d->level].stroke_set = true;
+ }
+ }
+}
+
+
+void
+Emf::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_recidx = index; // used if the hatch needs to be redone due to bkMode, textmode, etc. changes
+ d->dc[d->level].fill_mode = DRAW_PATTERN;
+ d->dc[d->level].fill_set = true;
+ }
+ }
+ else if(iType == U_EMR_CREATEDIBPATTERNBRUSHPT || iType == U_EMR_CREATEMONOBRUSH){
+ PU_EMRCREATEDIBPATTERNBRUSHPT pEmr = (PU_EMRCREATEDIBPATTERNBRUSHPT) d->emf_obj[index].lpEMFR;
+ tidx = add_image(d, (void *) pEmr, pEmr->cbBits, pEmr->cbBmi, pEmr->iUsage, pEmr->offBits, pEmr->offBmi);
+ if(tidx == U_EMR_INVALID){ // 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;
+ }
+ }
+}
+
+
+void
+Emf::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_abs_size( d, pEmr->elfw.elfLogFont.lfHeight );
+ /* snap the font_size to the nearest 1/32nd of a point.
+ (The size is converted from Pixels to points, snapped, and converted back.)
+ See the notes where d->D2Pscale[XY] are set for the reason why.
+ Typically this will set the font to the desired exact size. If some peculiar size
+ was intended this will, at worst, make it .03125 off, which is unlikely to be a problem. */
+ font_size = round(20.0 * 0.8 * font_size)/(20.0 * 0.8);
+ d->level = cur_level;
+ d->dc[d->level].style.font_size.computed = font_size;
+ d->dc[d->level].style.font_weight.value =
+ 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_line.underline = pEmr->elfw.elfLogFont.lfUnderline;
+ d->dc[d->level].style.text_decoration_line.line_through = pEmr->elfw.elfLogFont.lfStrikeOut;
+ d->dc[d->level].style.text_decoration_line.set = true;
+ d->dc[d->level].style.text_decoration_line.inherit = false;
+ // malformed EMF with empty filename may exist, ignore font change if encountered
+ char *ctmp = U_Utf16leToUtf8((uint16_t *) (pEmr->elfw.elfLogFont.lfFaceName), U_LF_FACESIZE, NULL);
+ if(ctmp){
+ if (d->dc[d->level].font_name){ free(d->dc[d->level].font_name); }
+ if(*ctmp){
+ d->dc[d->level].font_name = ctmp;
+ }
+ else { // Malformed EMF might specify an empty font name
+ free(ctmp);
+ d->dc[d->level].font_name = strdup("Arial"); // Default font, EMF spec says device can pick whatever it wants
+ }
+ }
+ d->dc[d->level].style.baseline_shift.value = round((double)((pEmr->elfw.elfLogFont.lfEscapement + 3600) % 3600)) / 10.0; // use baseline_shift instead of text_transform to avoid overflow
+}
+
+void
+Emf::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;
+ }
+}
+
+
+void
+Emf::insert_object(PEMF_CALLBACK_DATA d, int index, int type, PU_ENHMETARECORD pObj)
+{
+ if (index >= 0 && index < d->n_obj) {
+ delete_object(d, index);
+ d->emf_obj[index].type = type;
+ d->emf_obj[index].level = d->level;
+ d->emf_obj[index].lpEMFR = emr_dup((char *) pObj);
+ }
+}
+
+/* Identify probable Adobe Illustrator produced EMF files, which do strange things with the scaling.
+ The few so far observed all had this format.
+*/
+int Emf::AI_hack(PU_EMRHEADER pEmr){
+ int ret=0;
+ char *ptr;
+ ptr = (char *)pEmr;
+ PU_EMRSETMAPMODE nEmr = (PU_EMRSETMAPMODE) (ptr + pEmr->emr.nSize);
+ char *string = NULL;
+ if(pEmr->nDescription)string = U_Utf16leToUtf8((uint16_t *)((char *) pEmr + pEmr->offDescription), pEmr->nDescription, NULL);
+ if(string){
+ if((pEmr->nDescription >= 13) &&
+ (0==strcmp("Adobe Systems",string)) &&
+ (nEmr->emr.iType == U_EMR_SETMAPMODE) &&
+ (nEmr->iMode == U_MM_ANISOTROPIC)){ ret=1; }
+ free(string);
+ }
+ return(ret);
+}
+
+/**
+ \fn create a UTF-32LE buffer and fill it with UNICODE unknown character
+ \param count number of copies of the Unicode unknown character to fill with
+*/
+uint32_t *Emf::unknown_chars(size_t count){
+ uint32_t *res = (uint32_t *) malloc(sizeof(uint32_t) * (count + 1));
+ if(!res)throw "Inkscape fatal memory allocation error - cannot continue";
+ for(uint32_t i=0; i<count; i++){ res[i] = 0xFFFD; }
+ res[count]=0;
+ return res;
+}
+
+/**
+ \fn store SVG for an image given the pixmap and various coordinate information
+ \param d
+ \param pEmr
+ \param dx (double) destination x in inkscape pixels
+ \param dy (double) destination y in inkscape pixels
+ \param dw (double) destination width in inkscape pixels
+ \param dh (double) destination height in inkscape pixels
+ \param sx (int) source x in src image pixels
+ \param sy (int) source y in src image pixels
+ \param iUsage
+ \param offBits
+ \param cbBits
+ \param offBmi
+ \param cbBmi
+*/
+void Emf::common_image_extraction(PEMF_CALLBACK_DATA d, void *pEmr,
+ double dx, double dy, double dw, double dh, int sx, int sy, int sw, int sh,
+ uint32_t iUsage, uint32_t offBits, uint32_t cbBits, uint32_t offBmi, uint32_t cbBmi){
+
+ SVGOStringStream tmp_image;
+ int dibparams = U_BI_UNKNOWN; // type of image not yet determined
+
+ tmp_image << "\n\t <image\n";
+ tmp_image << " y=\"" << dy << "\"\n x=\"" << dx <<"\"\n ";
+
+ MEMPNG mempng; // PNG in memory comes back in this
+ mempng.buffer = NULL;
+
+ char *rgba_px = NULL; // RGBA pixels
+ char *sub_px = NULL; // RGBA pixels, subarray
+ const char *px = NULL; // DIB pixels
+ const U_RGBQUAD *ct = NULL; // DIB color table
+ uint32_t width, height, colortype, numCt, invert; // if needed these values will be set in get_DIB_params
+ if(cbBits && cbBmi && (iUsage == U_DIB_RGB_COLORS)){
+ // next call returns pointers and values, but allocates no memory
+ dibparams = get_DIB_params(pEmr, offBits, offBmi, &px, (const U_RGBQUAD **) &ct,
+ &numCt, &width, &height, &colortype, &invert);
+ if(dibparams ==U_BI_RGB){
+ if(sw == 0 || sh == 0){
+ sw = width;
+ sh = height;
+ }
+
+ if(!DIB_to_RGBA(
+ px, // DIB pixel array
+ ct, // DIB color table
+ numCt, // DIB color table number of entries
+ &rgba_px, // U_RGBA pixel array (32 bits), created by this routine, caller must free.
+ width, // Width of pixel array
+ height, // Height of pixel array
+ colortype, // DIB BitCount Enumeration
+ numCt, // Color table used if not 0
+ invert // If DIB rows are in opposite order from RGBA rows
+ )){
+ sub_px = RGBA_to_RGBA( // returns either a subset (side effect: frees rgba_px) or NULL (for subset == entire image)
+ rgba_px, // full pixel array from DIB
+ width, // Width of pixel array
+ height, // Height of pixel array
+ sx,sy, // starting point in pixel array
+ &sw,&sh // columns/rows to extract from the pixel array (output array size)
+ );
+
+ if(!sub_px)sub_px=rgba_px;
+ toPNG( // Get the image from the RGBA px into mempng
+ &mempng,
+ sw, sh, // size of the extracted pixel array
+ sub_px
+ );
+ free(sub_px);
+ }
+ }
+ }
+
+ gchar *base64String=NULL;
+ if(dibparams == U_BI_JPEG){ // image was binary jpg in source file
+ tmp_image << " xlink:href=\"data:image/jpeg;base64,";
+ base64String = g_base64_encode((guchar*) px, numCt );
+ }
+ else if(dibparams==U_BI_PNG){ // image was binary png in source file
+ tmp_image << " xlink:href=\"data:image/png;base64,";
+ base64String = g_base64_encode((guchar*) px, numCt );
+ }
+ else if(mempng.buffer){ // image was DIB in source file, converted to png in this routine
+ tmp_image << " xlink:href=\"data:image/png;base64,";
+ base64String = g_base64_encode((guchar*) mempng.buffer, mempng.size );
+ free(mempng.buffer);
+ }
+ else { // unknown or unsupported image type or failed conversion, insert the common bad image picture
+ tmp_image << " xlink:href=\"data:image/png;base64,";
+ base64String = bad_image_png();
+ }
+ tmp_image << base64String;
+ g_free(base64String);
+
+ tmp_image << "\"\n height=\"" << dh << "\"\n width=\"" << dw << "\"\n";
+
+ tmp_image << " transform=" << current_matrix(d, dx, dy, 1); // calculate appropriate offset
+ tmp_image << " preserveAspectRatio=\"none\"\n";
+ tmp_image << "/> \n";
+
+ *(d->outsvg) += tmp_image.str().c_str();
+ *(d->path) = "";
+}
+
+/**
+ \fn myEnhMetaFileProc(char *contents, unsigned int length, PEMF_CALLBACK_DATA lpData)
+ \param contents binary contents of an EMF file
+ \param length length in bytes of contents
+ \param d Inkscape data structures returned by this call
+*/
+//THis was a callback, just build it into a normal function
+int Emf::myEnhMetaFileProc(char *contents, unsigned int length, PEMF_CALLBACK_DATA d)
+{
+ uint32_t off=0;
+ uint32_t emr_mask;
+ int OK =1;
+ PU_ENHMETARECORD lpEMFR;
+ TCHUNK_SPECS tsp;
+ uint32_t tbkMode = U_TRANSPARENT; // holds proposed change to bkMode, if text is involved saving these to the DC must wait until the text is written
+ U_COLORREF tbkColor = U_RGB(255, 255, 255); // holds proposed change to bkColor
+
+ /* initialize the tsp for text reassembly */
+ tsp.string = NULL;
+ tsp.ori = 0.0; /* degrees */
+ tsp.fs = 12.0; /* font size */
+ tsp.x = 0.0;
+ tsp.y = 0.0;
+ tsp.boff = 0.0; /* offset to baseline from LL corner of bounding rectangle, changes with fs and taln*/
+ tsp.vadvance = 0.0; /* meaningful only when a complex contains two or more lines */
+ tsp.taln = ALILEFT + ALIBASE;
+ tsp.ldir = LDIR_LR;
+ tsp.spaces = 0; // this field is only used for debugging
+ tsp.color.Red = 0; /* RGB Black */
+ tsp.color.Green = 0; /* RGB Black */
+ tsp.color.Blue = 0; /* RGB Black */
+ tsp.color.Reserved = 0; /* not used */
+ tsp.italics = 0;
+ tsp.weight = 80;
+ tsp.decoration = TXTDECOR_NONE;
+ tsp.condensed = 100;
+ tsp.co = 0;
+ tsp.fi_idx = -1; /* set to an invalid */
+
+ while(OK){
+ if(off>=length)return(0); //normally should exit from while after EMREOF sets OK to false.
+
+ lpEMFR = (PU_ENHMETARECORD)(contents + off);
+// Uncomment the following to track down toxic records
+//std::cout << "record type: " << lpEMFR->iType << " length: " << lpEMFR->nSize << " offset: " << off <<std::endl;
+ off += lpEMFR->nSize;
+
+ SVGOStringStream tmp_outsvg;
+ SVGOStringStream tmp_path;
+ SVGOStringStream tmp_str;
+ SVGOStringStream dbg_str;
+
+ emr_mask = emr_properties(lpEMFR->iType);
+ if(emr_mask == U_EMR_INVALID){ throw "Inkscape fatal memory allocation error - cannot continue"; }
+
+/* Uncomment the following to track down text problems */
+//std::cout << "tri->dirty:"<< d->tri->dirty << " emr_mask: " << std::hex << emr_mask << std::dec << std::endl;
+
+ // incompatible change to text drawing detected (color or background change) forces out existing text
+ // OR
+ // next record is valid type and forces pending text to be drawn immediately
+ if ((d->dc[d->level].dirty & DIRTY_TEXT) || ((emr_mask != U_EMR_INVALID) && (emr_mask & U_DRAW_TEXT) && d->tri->dirty)){
+ TR_layout_analyze(d->tri);
+ TR_layout_2_svg(d->tri);
+ SVGOStringStream ts;
+ ts << d->tri->out;
+ *(d->outsvg) += ts.str().c_str();
+ d->tri = trinfo_clear(d->tri);
+ }
+ if(d->dc[d->level].dirty){ //Apply the delayed background changes, clear the flag
+ d->dc[d->level].bkMode = tbkMode;
+ memcpy(&(d->dc[d->level].bkColor),&tbkColor, sizeof(U_COLORREF));
+
+ if(d->dc[d->level].dirty & DIRTY_TEXT){
+ // U_COLORREF and TRCOLORREF are exactly the same in memory, but the compiler needs some convincing...
+ if(tbkMode == U_TRANSPARENT){ (void) trinfo_load_bk(d->tri, BKCLR_NONE, *(TRCOLORREF *) &tbkColor); }
+ else { (void) trinfo_load_bk(d->tri, BKCLR_LINE, *(TRCOLORREF *) &tbkColor); } // Opaque
+ }
+
+ /* It is possible to have a series of EMF records that would result in
+ the following creating hash patterns which are never used. For instance, if
+ there were a series of records that changed the background color but did nothing
+ else.
+ */
+ if((d->dc[d->level].stroke_mode == DRAW_PATTERN) && (d->dc[d->level].dirty & DIRTY_STROKE)){
+ select_extpen(d, d->dc[d->level].stroke_recidx);
+ }
+
+ if((d->dc[d->level].fill_mode == DRAW_PATTERN) && (d->dc[d->level].dirty & DIRTY_FILL)){
+ select_brush(d, d->dc[d->level].fill_recidx);
+ }
+
+ d->dc[d->level].dirty = 0;
+ }
+
+//std::cout << "BEFORE DRAW logic d->mask: " << std::hex << d->mask << " emr_mask: " << emr_mask << std::dec << std::endl;
+/*
+std::cout << "BEFORE DRAW"
+ << " test0 " << ( d->mask & U_DRAW_VISIBLE)
+ << " test1 " << ( d->mask & U_DRAW_FORCE)
+ << " test2 " << (emr_mask & U_DRAW_ALTERS)
+ << " 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 != U_EMR_INVALID) && // next record is valid type
+ (d->mask & U_DRAW_VISIBLE) && // Current set of objects are drawable
+ (
+ (d->mask & U_DRAW_FORCE) || // This draw is forced by STROKE/FILL/STROKEANDFILL PATH
+ (emr_mask & U_DRAW_ALTERS) || // Next record would alter the drawing environment in some way
+ (
+ (emr_mask & U_DRAW_VISIBLE) && // Next record is visible...
+ (
+ ( !(d->mask & U_DRAW_ONLYTO) ) || // Non *TO records cannot be followed by any Visible
+ ((d->mask & U_DRAW_ONLYTO) && !(emr_mask & U_DRAW_ONLYTO) )// *TO records can only be followed by other *TO records
+ )
+ )
+ )
+ ){
+// std::cout << "PATH DRAW at TOP" << std::endl;
+ *(d->outsvg) += " <path "; // this is the ONLY place <path should be used!!! One exception, gradientfill.
+ if(d->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!!!! One exception, gradientfill.
+ *(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 << "<!-- U_EMR_HEADER -->\n";
+
+ *(d->outdef) += "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n";
+
+ if (d->pDesc) {
+ *(d->outdef) += "<!-- ";
+ *(d->outdef) += d->pDesc;
+ *(d->outdef) += " -->\n";
+ }
+
+ PU_EMRHEADER pEmr = (PU_EMRHEADER) lpEMFR;
+ SVGOStringStream tmp_outdef;
+ tmp_outdef << "<svg\n";
+ tmp_outdef << " xmlns:svg=\"http://www.w3.org/2000/svg\"\n";
+ tmp_outdef << " xmlns=\"http://www.w3.org/2000/svg\"\n";
+ tmp_outdef << " xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n";
+ tmp_outdef << " xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\"\n"; // needed for sodipodi:role
+ tmp_outdef << " version=\"1.0\"\n";
+
+
+ /* inclusive-inclusive, so the size is 1 more than the difference */
+ d->MM100InX = pEmr->rclFrame.right - pEmr->rclFrame.left + 1;
+ d->MM100InY = pEmr->rclFrame.bottom - pEmr->rclFrame.top + 1;
+ d->PixelsInX = pEmr->rclBounds.right - pEmr->rclBounds.left + 1;
+ d->PixelsInY = pEmr->rclBounds.bottom - pEmr->rclBounds.top + 1;
+
+ /*
+ calculate ratio of Inkscape dpi/EMF device dpi
+ This can cause problems later due to accuracy limits in the EMF. A high resolution
+ EMF might have a final D2Pscale[XY] of 0.074998, and adjusting the (integer) device size
+ by 1 will still not get it exactly to 0.075. Later when the font size is calculated it
+ can end up as 29.9992 or 22.4994 instead of the intended 30 or 22.5. This is handled by
+ snapping font sizes to the nearest .01. The best estimate is made by using both values.
+ */
+ if ((pEmr->szlMillimeters.cx + pEmr->szlMillimeters.cy) && ( pEmr->szlDevice.cx + pEmr->szlDevice.cy)){
+ d->E2IdirY = 1.0; // assume MM_TEXT, if not, this will be changed later
+ d->D2PscaleX = d->D2PscaleY = Inkscape::Util::Quantity::convert(1, "mm", "px") *
+ (double)(pEmr->szlMillimeters.cx + pEmr->szlMillimeters.cy)/
+ (double)( pEmr->szlDevice.cx + pEmr->szlDevice.cy);
+ }
+ trinfo_load_qe(d->tri, d->D2PscaleX); /* quantization error that will affect text positions */
+
+ /* Adobe Illustrator files set mapmode to MM_ANISOTROPIC and somehow or other this
+ converts the rclFrame values from MM_HIMETRIC to MM_HIENGLISH, with another factor of 3 thrown
+ in for good measure. Ours not to question why...
+ */
+ if(AI_hack(pEmr)){
+ d->MM100InX *= 25.4/(10.0*3.0);
+ d->MM100InY *= 25.4/(10.0*3.0);
+ d->D2PscaleX *= 25.4/(10.0*3.0);
+ d->D2PscaleY *= 25.4/(10.0*3.0);
+ }
+
+ d->MMX = d->MM100InX / 100.0;
+ d->MMY = d->MM100InY / 100.0;
+
+ d->PixelsOutX = Inkscape::Util::Quantity::convert(d->MMX, "mm", "px");
+ d->PixelsOutY = Inkscape::Util::Quantity::convert(d->MMY, "mm", "px");
+
+ // Upper left corner, from header rclBounds, in device units, usually both 0, but not always
+ d->ulCornerInX = pEmr->rclBounds.left;
+ d->ulCornerInY = pEmr->rclBounds.top;
+ d->ulCornerOutX = d->ulCornerInX * d->D2PscaleX;
+ d->ulCornerOutY = d->ulCornerInY * d->E2IdirY * d->D2PscaleY;
+
+ tmp_outdef <<
+ " width=\"" << d->MMX << "mm\"\n" <<
+ " height=\"" << d->MMY << "mm\">\n";
+ *(d->outdef) += tmp_outdef.str().c_str();
+ *(d->outdef) += "<defs>"; // temporary end of header
+
+ // d->defs holds any defines which are read in.
+
+ tmp_outsvg << "\n</defs>\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 << "<!-- U_EMR_POLYBEZIER -->\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_xy( d, pEmr->aptl[0].x, pEmr->aptl[0].y) << " ";
+
+ for (i=1; i<pEmr->cptl; ) {
+ tmp_str << "\n\tC ";
+ for (j=0; j<3 && i<pEmr->cptl; j++,i++) {
+ tmp_str << pix_to_xy( d, pEmr->aptl[i].x, pEmr->aptl[i].y) << " ";
+ }
+ }
+
+ tmp_path << tmp_str.str().c_str();
+
+ break;
+ }
+ case U_EMR_POLYGON:
+ {
+ dbg_str << "<!-- U_EMR_POLYGON -->\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_xy( d, pEmr->aptl[0].x, pEmr->aptl[0].y ) << " ";
+
+ for (i=1; i<pEmr->cptl; i++) {
+ tmp_str <<
+ "\n\tL " <<
+ pix_to_xy( 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 << "<!-- U_EMR_POLYLINE -->\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_xy( d, pEmr->aptl[0].x, pEmr->aptl[0].y ) << " ";
+
+ for (i=1; i<pEmr->cptl; i++) {
+ tmp_str <<
+ "\n\tL " <<
+ pix_to_xy( d, pEmr->aptl[i].x, pEmr->aptl[i].y ) << " ";
+ }
+
+ tmp_path << tmp_str.str().c_str();
+
+ break;
+ }
+ case U_EMR_POLYBEZIERTO:
+ {
+ dbg_str << "<!-- U_EMR_POLYBEZIERTO -->\n";
+
+ PU_EMRPOLYBEZIERTO pEmr = (PU_EMRPOLYBEZIERTO) lpEMFR;
+ uint32_t i,j;
+
+ d->mask |= emr_mask;
+
+ for (i=0; i<pEmr->cptl;) {
+ tmp_path << "\n\tC ";
+ for (j=0; j<3 && i<pEmr->cptl; j++,i++) {
+ tmp_path <<
+ pix_to_xy( d, pEmr->aptl[i].x, pEmr->aptl[i].y ) << " ";
+ }
+ }
+
+ break;
+ }
+ case U_EMR_POLYLINETO:
+ {
+ dbg_str << "<!-- U_EMR_POLYLINETO -->\n";
+
+ PU_EMRPOLYLINETO pEmr = (PU_EMRPOLYLINETO) lpEMFR;
+ uint32_t i;
+
+ d->mask |= emr_mask;
+
+ for (i=0; i<pEmr->cptl;i++) {
+ tmp_path <<
+ "\n\tL " <<
+ pix_to_xy( 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 << "<!-- U_EMR_POLYPOLYLINE -->\n";
+ if (lpEMFR->iType == U_EMR_POLYPOLYGON)
+ dbg_str << "<!-- U_EMR_POLYPOLYGON -->\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; n<pEmr->nPolys && i<pEmr->cptl; n++) {
+ SVGOStringStream poly_path;
+
+ poly_path << "\n\tM " << pix_to_xy( d, aptl[i].x, aptl[i].y) << " ";
+ i++;
+
+ for (j=1; j<pEmr->aPolyCounts[n] && i<pEmr->cptl; j++) {
+ poly_path << "\n\tL " << pix_to_xy( 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 << "<!-- U_EMR_SETWINDOWEXTEX -->\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->PixelsOutX;
+ d->dc[d->level].sizeWnd.cy = d->PixelsOutY;
+ }
+ }
+
+ if (!d->dc[d->level].sizeView.cx || !d->dc[d->level].sizeView.cy) {
+ d->dc[d->level].sizeView = d->dc[d->level].sizeWnd;
+ }
+
+ /* scales logical to EMF pixels, transfer a negative sign on Y, if any */
+ if (d->dc[d->level].sizeWnd.cx && d->dc[d->level].sizeWnd.cy) {
+ d->dc[d->level].ScaleInX = (double) d->dc[d->level].sizeView.cx / (double) d->dc[d->level].sizeWnd.cx;
+ d->dc[d->level].ScaleInY = (double) d->dc[d->level].sizeView.cy / (double) d->dc[d->level].sizeWnd.cy;
+ if(d->dc[d->level].ScaleInY < 0){
+ d->dc[d->level].ScaleInY *= -1.0;
+ d->E2IdirY = -1.0;
+ }
+ }
+ else {
+ d->dc[d->level].ScaleInX = 1;
+ d->dc[d->level].ScaleInY = 1;
+ }
+ break;
+ }
+ case U_EMR_SETWINDOWORGEX:
+ {
+ dbg_str << "<!-- U_EMR_SETWINDOWORGEX -->\n";
+
+ PU_EMRSETWINDOWORGEX pEmr = (PU_EMRSETWINDOWORGEX) lpEMFR;
+ d->dc[d->level].winorg = pEmr->ptlOrigin;
+ break;
+ }
+ case U_EMR_SETVIEWPORTEXTEX:
+ {
+ dbg_str << "<!-- U_EMR_SETVIEWPORTEXTEX -->\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->PixelsOutX;
+ d->dc[d->level].sizeView.cy = d->PixelsOutY;
+ }
+ }
+
+ if (!d->dc[d->level].sizeWnd.cx || !d->dc[d->level].sizeWnd.cy) {
+ d->dc[d->level].sizeWnd = d->dc[d->level].sizeView;
+ }
+
+ /* scales logical to EMF pixels, transfer a negative sign on Y, if any */
+ if (d->dc[d->level].sizeWnd.cx && d->dc[d->level].sizeWnd.cy) {
+ d->dc[d->level].ScaleInX = (double) d->dc[d->level].sizeView.cx / (double) d->dc[d->level].sizeWnd.cx;
+ d->dc[d->level].ScaleInY = (double) d->dc[d->level].sizeView.cy / (double) d->dc[d->level].sizeWnd.cy;
+ if( d->dc[d->level].ScaleInY < 0){
+ d->dc[d->level].ScaleInY *= -1.0;
+ d->E2IdirY = -1.0;
+ }
+ }
+ else {
+ d->dc[d->level].ScaleInX = 1;
+ d->dc[d->level].ScaleInY = 1;
+ }
+ break;
+ }
+ case U_EMR_SETVIEWPORTORGEX:
+ {
+ dbg_str << "<!-- U_EMR_SETVIEWPORTORGEX -->\n";
+
+ PU_EMRSETVIEWPORTORGEX pEmr = (PU_EMRSETVIEWPORTORGEX) lpEMFR;
+ d->dc[d->level].vieworg = pEmr->ptlOrigin;
+ break;
+ }
+ case U_EMR_SETBRUSHORGEX: dbg_str << "<!-- U_EMR_SETBRUSHORGEX -->\n"; break;
+ case U_EMR_EOF:
+ {
+ dbg_str << "<!-- U_EMR_EOF -->\n";
+
+ tmp_outsvg << "</svg>\n";
+ *(d->outsvg) = *(d->outdef) + *(d->defs) + *(d->outsvg);
+ OK=0;
+ break;
+ }
+ case U_EMR_SETPIXELV: dbg_str << "<!-- U_EMR_SETPIXELV -->\n"; break;
+ case U_EMR_SETMAPPERFLAGS: dbg_str << "<!-- U_EMR_SETMAPPERFLAGS -->\n"; break;
+ case U_EMR_SETMAPMODE:
+ {
+ dbg_str << "<!-- U_EMR_SETMAPMODE -->\n";
+ PU_EMRSETMAPMODE pEmr = (PU_EMRSETMAPMODE) lpEMFR;
+ switch (pEmr->iMode){
+ case U_MM_TEXT:
+ default:
+ // Use all values from the header.
+ break;
+ /* For all of the following the indicated scale this will be encoded in WindowExtEx/ViewportExtex
+ and show up in ScaleIn[XY]
+ */
+ case U_MM_LOMETRIC: // 1 LU = 0.1 mm,
+ case U_MM_HIMETRIC: // 1 LU = 0.01 mm
+ case U_MM_LOENGLISH: // 1 LU = 0.1 in
+ case U_MM_HIENGLISH: // 1 LU = 0.01 in
+ case U_MM_TWIPS: // 1 LU = 1/1440 in
+ d->E2IdirY = -1.0;
+ // Use d->D2Pscale[XY] values from the header.
+ break;
+ case U_MM_ISOTROPIC: // ScaleIn[XY] should be set elsewhere by SETVIEWPORTEXTEX and SETWINDOWEXTEX
+ case U_MM_ANISOTROPIC:
+ break;
+ }
+ break;
+ }
+ case U_EMR_SETBKMODE:
+ {
+ dbg_str << "<!-- U_EMR_SETBKMODE -->\n";
+ PU_EMRSETBKMODE pEmr = (PU_EMRSETBKMODE) lpEMFR;
+ tbkMode = pEmr->iMode;
+ if(tbkMode != d->dc[d->level].bkMode){
+ d->dc[d->level].dirty |= DIRTY_TEXT;
+ if(tbkMode != d->dc[d->level].bkMode){
+ if(d->dc[d->level].fill_mode == DRAW_PATTERN){ d->dc[d->level].dirty |= DIRTY_FILL; }
+ if(d->dc[d->level].stroke_mode == DRAW_PATTERN){ d->dc[d->level].dirty |= DIRTY_STROKE; }
+ }
+ memcpy(&tbkColor,&(d->dc[d->level].bkColor),sizeof(U_COLORREF));
+ }
+ break;
+ }
+ case U_EMR_SETPOLYFILLMODE:
+ {
+ dbg_str << "<!-- U_EMR_SETPOLYFILLMODE -->\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 << "<!-- U_EMR_SETROP2 -->\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 << "<!-- U_EMR_SETSTRETCHBLTMODE -->\n";
+ break;
+ }
+ case U_EMR_SETTEXTALIGN:
+ {
+ dbg_str << "<!-- U_EMR_SETTEXTALIGN -->\n";
+
+ PU_EMRSETTEXTALIGN pEmr = (PU_EMRSETTEXTALIGN) lpEMFR;
+ d->dc[d->level].textAlign = pEmr->iMode;
+ break;
+ }
+ case U_EMR_SETCOLORADJUSTMENT:
+ dbg_str << "<!-- U_EMR_SETCOLORADJUSTMENT -->\n";
+ break;
+ case U_EMR_SETTEXTCOLOR:
+ {
+ dbg_str << "<!-- U_EMR_SETTEXTCOLOR -->\n";
+
+ PU_EMRSETTEXTCOLOR pEmr = (PU_EMRSETTEXTCOLOR) lpEMFR;
+ d->dc[d->level].textColor = pEmr->crColor;
+ if(tbkMode != d->dc[d->level].bkMode){
+ if(d->dc[d->level].fill_mode == DRAW_PATTERN){ d->dc[d->level].dirty |= DIRTY_FILL; }
+ if(d->dc[d->level].stroke_mode == DRAW_PATTERN){ d->dc[d->level].dirty |= DIRTY_STROKE; }
+ }
+ // not text_dirty, because multicolored complex text is supported in libTERE
+ break;
+ }
+ case U_EMR_SETBKCOLOR:
+ {
+ dbg_str << "<!-- U_EMR_SETBKCOLOR -->\n";
+
+ PU_EMRSETBKCOLOR pEmr = (PU_EMRSETBKCOLOR) lpEMFR;
+ tbkColor = pEmr->crColor;
+ if(memcmp(&tbkColor, &(d->dc[d->level].bkColor), sizeof(U_COLORREF))){
+ d->dc[d->level].dirty |= DIRTY_TEXT;
+ if(d->dc[d->level].fill_mode == DRAW_PATTERN){ d->dc[d->level].dirty |= DIRTY_FILL; }
+ if(d->dc[d->level].stroke_mode == DRAW_PATTERN){ d->dc[d->level].dirty |= DIRTY_STROKE; }
+ tbkMode = d->dc[d->level].bkMode;
+ }
+ break;
+ }
+ case U_EMR_OFFSETCLIPRGN: dbg_str << "<!-- U_EMR_OFFSETCLIPRGN -->\n"; break;
+ case U_EMR_MOVETOEX:
+ {
+ dbg_str << "<!-- U_EMR_MOVETOEX -->\n";
+
+ PU_EMRMOVETOEX pEmr = (PU_EMRMOVETOEX) lpEMFR;
+
+ d->mask |= emr_mask;
+
+ d->dc[d->level].cur = pEmr->ptl;
+
+ tmp_path <<
+ "\n\tM " << pix_to_xy( d, pEmr->ptl.x, pEmr->ptl.y ) << " ";
+ break;
+ }
+ case U_EMR_SETMETARGN: dbg_str << "<!-- U_EMR_SETMETARGN -->\n"; break;
+ case U_EMR_EXCLUDECLIPRECT: dbg_str << "<!-- U_EMR_EXCLUDECLIPRECT -->\n"; break;
+ case U_EMR_INTERSECTCLIPRECT:
+ {
+ dbg_str << "<!-- U_EMR_INTERSECTCLIPRECT -->\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 dx = pix_to_x_point( d, rc.left, rc.top );
+ double dy = pix_to_y_point( d, rc.left, rc.top );
+ double dw = pix_to_abs_size( d, rc.right - rc.left + 1);
+ double dh = pix_to_abs_size( d, rc.bottom - rc.top + 1);
+
+ SVGOStringStream tmp_rectangle;
+ tmp_rectangle << "\n<clipPath\n\tclipPathUnits=\"userSpaceOnUse\" ";
+ tmp_rectangle << "\nid=\"clipEmfPath" << ++(d->id) << "\" >";
+ tmp_rectangle << "\n<rect ";
+ tmp_rectangle << "\n x=\"" << dx << "\" ";
+ tmp_rectangle << "\n y=\"" << dy << "\" ";
+ tmp_rectangle << "\n width=\"" << dw << "\" ";
+ tmp_rectangle << "\n height=\"" << dh << "\" ";
+ tmp_rectangle << "\n transform=" << current_matrix(d, dx, dy, 1); // calculate appropriate offset
+ tmp_rectangle << "/>\n</clipPath>";
+
+ *(d->outdef) += tmp_rectangle.str().c_str();
+ *(d->path) = "";
+ break;
+ }
+ case U_EMR_SCALEVIEWPORTEXTEX: dbg_str << "<!-- U_EMR_SCALEVIEWPORTEXTEX -->\n"; break;
+ case U_EMR_SCALEWINDOWEXTEX: dbg_str << "<!-- U_EMR_SCALEWINDOWEXTEX -->\n"; break;
+ case U_EMR_SAVEDC:
+ dbg_str << "<!-- U_EMR_SAVEDC -->\n";
+
+ if (d->level < EMF_MAX_DC) {
+ d->dc[d->level + 1] = d->dc[d->level];
+ if(d->dc[d->level].font_name){
+ d->dc[d->level + 1].font_name = strdup(d->dc[d->level].font_name); // or memory access problems because font name pointer duplicated
+ }
+ d->level = d->level + 1;
+ }
+ break;
+ case U_EMR_RESTOREDC:
+ {
+ dbg_str << "<!-- U_EMR_RESTOREDC -->\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_dasharray.values.empty() && (old_level==0 || (old_level>0 && d->dc[old_level].style.stroke_dasharray.values!=d->dc[old_level-1].style.stroke_dasharray.values))){
+ d->dc[old_level].style.stroke_dasharray.values.clear();
+ }
+ if(d->dc[old_level].font_name){
+ free(d->dc[old_level].font_name); // else memory leak
+ d->dc[old_level].font_name = NULL;
+ }
+ old_level--;
+ }
+ break;
+ }
+ case U_EMR_SETWORLDTRANSFORM:
+ {
+ dbg_str << "<!-- U_EMR_SETWORLDTRANSFORM -->\n";
+
+ PU_EMRSETWORLDTRANSFORM pEmr = (PU_EMRSETWORLDTRANSFORM) lpEMFR;
+ d->dc[d->level].worldTransform = pEmr->xform;
+ break;
+ }
+ case U_EMR_MODIFYWORLDTRANSFORM:
+ {
+ dbg_str << "<!-- U_EMR_MODIFYWORLDTRANSFORM -->\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 << "<!-- U_EMR_SELECTOBJECT -->\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 << "<!-- U_EMR_CREATEPEN -->\n";
+
+ PU_EMRCREATEPEN pEmr = (PU_EMRCREATEPEN) lpEMFR;
+ insert_object(d, pEmr->ihPen, U_EMR_CREATEPEN, lpEMFR);
+ break;
+ }
+ case U_EMR_CREATEBRUSHINDIRECT:
+ {
+ dbg_str << "<!-- U_EMR_CREATEBRUSHINDIRECT -->\n";
+
+ PU_EMRCREATEBRUSHINDIRECT pEmr = (PU_EMRCREATEBRUSHINDIRECT) lpEMFR;
+ insert_object(d, pEmr->ihBrush, U_EMR_CREATEBRUSHINDIRECT, lpEMFR);
+ break;
+ }
+ case U_EMR_DELETEOBJECT:
+ dbg_str << "<!-- U_EMR_DELETEOBJECT -->\n";
+ // Objects here are not deleted until the draw completes, new ones may write over an existing one.
+ break;
+ case U_EMR_ANGLEARC:
+ dbg_str << "<!-- U_EMR_ANGLEARC -->\n";
+ break;
+ case U_EMR_ELLIPSE:
+ {
+ dbg_str << "<!-- U_EMR_ELLIPSE -->\n";
+
+ PU_EMRELLIPSE pEmr = (PU_EMRELLIPSE) lpEMFR;
+ U_RECTL rclBox = pEmr->rclBox;
+
+ double cx = pix_to_x_point( d, (rclBox.left + rclBox.right)/2.0, (rclBox.bottom + rclBox.top)/2.0 );
+ double cy = pix_to_y_point( d, (rclBox.left + rclBox.right)/2.0, (rclBox.bottom + rclBox.top)/2.0 );
+ double rx = pix_to_abs_size( d, fabs(rclBox.right - rclBox.left )/2.0 );
+ double ry = pix_to_abs_size( d, fabs(rclBox.top - rclBox.bottom)/2.0 );
+
+ SVGOStringStream tmp_ellipse;
+ tmp_ellipse << "cx=\"" << cx << "\" ";
+ tmp_ellipse << "cy=\"" << cy << "\" ";
+ tmp_ellipse << "rx=\"" << rx << "\" ";
+ tmp_ellipse << "ry=\"" << ry << "\" ";
+
+ d->mask |= emr_mask;
+
+ *(d->outsvg) += " <ellipse ";
+ output_style(d, lpEMFR->iType); //
+ *(d->outsvg) += "\n\t";
+ *(d->outsvg) += tmp_ellipse.str().c_str();
+ *(d->outsvg) += "/> \n";
+ *(d->path) = "";
+ break;
+ }
+ case U_EMR_RECTANGLE:
+ {
+ dbg_str << "<!-- U_EMR_RECTANGLE -->\n";
+
+ PU_EMRRECTANGLE pEmr = (PU_EMRRECTANGLE) lpEMFR;
+ U_RECTL rc = pEmr->rclBox;
+
+ SVGOStringStream tmp_rectangle;
+ tmp_rectangle << "\n\tM " << pix_to_xy( d, rc.left , rc.top ) << " ";
+ tmp_rectangle << "\n\tL " << pix_to_xy( d, rc.right, rc.top ) << " ";
+ tmp_rectangle << "\n\tL " << pix_to_xy( d, rc.right, rc.bottom ) << " ";
+ tmp_rectangle << "\n\tL " << pix_to_xy( d, rc.left, rc.bottom ) << " ";
+ tmp_rectangle << "\n\tz";
+
+ d->mask |= emr_mask;
+
+ tmp_path << tmp_rectangle.str().c_str();
+ break;
+ }
+ case U_EMR_ROUNDRECT:
+ {
+ dbg_str << "<!-- U_EMR_ROUNDRECT -->\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 f1 = 1.0 - f;
+ double cnx = corner.cx/2;
+ double cny = corner.cy/2;
+
+ SVGOStringStream tmp_rectangle;
+ tmp_rectangle << "\n"
+ << " M "
+ << pix_to_xy(d, rc.left , rc.top + cny )
+ << "\n";
+ tmp_rectangle << " C "
+ << pix_to_xy(d, rc.left , rc.top + cny*f1 )
+ << " "
+ << pix_to_xy(d, rc.left + cnx*f1 , rc.top )
+ << " "
+ << pix_to_xy(d, rc.left + cnx , rc.top )
+ << "\n";
+ tmp_rectangle << " L "
+ << pix_to_xy(d, rc.right - cnx , rc.top )
+ << "\n";
+ tmp_rectangle << " C "
+ << pix_to_xy(d, rc.right - cnx*f1 , rc.top )
+ << " "
+ << pix_to_xy(d, rc.right , rc.top + cny*f1 )
+ << " "
+ << pix_to_xy(d, rc.right , rc.top + cny )
+ << "\n";
+ tmp_rectangle << " L "
+ << pix_to_xy(d, rc.right , rc.bottom - cny )
+ << "\n";
+ tmp_rectangle << " C "
+ << pix_to_xy(d, rc.right , rc.bottom - cny*f1 )
+ << " "
+ << pix_to_xy(d, rc.right - cnx*f1 , rc.bottom )
+ << " "
+ << pix_to_xy(d, rc.right - cnx , rc.bottom )
+ << "\n";
+ tmp_rectangle << " L "
+ << pix_to_xy(d, rc.left + cnx , rc.bottom )
+ << "\n";
+ tmp_rectangle << " C "
+ << pix_to_xy(d, rc.left + cnx*f1 , rc.bottom )
+ << " "
+ << pix_to_xy(d, rc.left , rc.bottom - cny*f1 )
+ << " "
+ << pix_to_xy(d, rc.left , rc.bottom - cny )
+ << "\n";
+ tmp_rectangle << " z\n";
+
+
+ d->mask |= emr_mask;
+
+ tmp_path << tmp_rectangle.str().c_str();
+ break;
+ }
+ case U_EMR_ARC:
+ {
+ dbg_str << "<!-- U_EMR_ARC -->\n";
+ U_PAIRF center,start,end,size;
+ int f1;
+ int f2 = (d->arcdir == U_AD_COUNTERCLOCKWISE ? 0 : 1);
+ int stat = emr_arc_points( lpEMFR, &f1, f2, &center, &start, &end, &size);
+ if(!stat){
+ tmp_path << "\n\tM " << pix_to_xy(d, start.x, start.y);
+ tmp_path << " A " << pix_to_abs_size(d, size.x)/2.0 << "," << pix_to_abs_size(d, size.y)/2.0;
+ tmp_path << " ";
+ tmp_path << 180.0 * current_rotation(d)/M_PI;
+ tmp_path << " ";
+ tmp_path << " " << f1 << "," << f2 << " ";
+ tmp_path << pix_to_xy(d, end.x, end.y) << " \n";
+ d->mask |= emr_mask;
+ }
+ else {
+ dbg_str << "<!-- ARC record is invalid -->\n";
+ }
+ break;
+ }
+ case U_EMR_CHORD:
+ {
+ dbg_str << "<!-- U_EMR_CHORD -->\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, &center, &start, &end, &size)){
+ tmp_path << "\n\tM " << pix_to_xy(d, start.x, start.y);
+ tmp_path << " A " << pix_to_abs_size(d, size.x)/2.0 << "," << pix_to_abs_size(d, size.y)/2.0;
+ tmp_path << " ";
+ tmp_path << 180.0 * current_rotation(d)/M_PI;
+ tmp_path << " ";
+ tmp_path << " " << f1 << "," << f2 << " ";
+ tmp_path << pix_to_xy(d, end.x, end.y) << " \n";
+ tmp_path << " z ";
+ d->mask |= emr_mask;
+ }
+ else {
+ dbg_str << "<!-- CHORD record is invalid -->\n";
+ }
+ break;
+ }
+ case U_EMR_PIE:
+ {
+ dbg_str << "<!-- U_EMR_PIE -->\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, &center, &start, &end, &size)){
+ tmp_path << "\n\tM " << pix_to_xy(d, center.x, center.y);
+ tmp_path << "\n\tL " << pix_to_xy(d, start.x, start.y);
+ tmp_path << " A " << pix_to_abs_size(d, size.x)/2.0 << "," << pix_to_abs_size(d, size.y)/2.0;
+ tmp_path << " ";
+ tmp_path << 180.0 * current_rotation(d)/M_PI;
+ tmp_path << " ";
+ tmp_path << " " << f1 << "," << f2 << " ";
+ tmp_path << pix_to_xy(d, end.x, end.y) << " \n";
+ tmp_path << " z ";
+ d->mask |= emr_mask;
+ }
+ else {
+ dbg_str << "<!-- PIE record is invalid -->\n";
+ }
+ break;
+ }
+ case U_EMR_SELECTPALETTE: dbg_str << "<!-- U_EMR_SELECTPALETTE -->\n"; break;
+ case U_EMR_CREATEPALETTE: dbg_str << "<!-- U_EMR_CREATEPALETTE -->\n"; break;
+ case U_EMR_SETPALETTEENTRIES: dbg_str << "<!-- U_EMR_SETPALETTEENTRIES -->\n"; break;
+ case U_EMR_RESIZEPALETTE: dbg_str << "<!-- U_EMR_RESIZEPALETTE -->\n"; break;
+ case U_EMR_REALIZEPALETTE: dbg_str << "<!-- U_EMR_REALIZEPALETTE -->\n"; break;
+ case U_EMR_EXTFLOODFILL: dbg_str << "<!-- U_EMR_EXTFLOODFILL -->\n"; break;
+ case U_EMR_LINETO:
+ {
+ dbg_str << "<!-- U_EMR_LINETO -->\n";
+
+ PU_EMRLINETO pEmr = (PU_EMRLINETO) lpEMFR;
+
+ d->mask |= emr_mask;
+
+ tmp_path <<
+ "\n\tL " << pix_to_xy( d, pEmr->ptl.x, pEmr->ptl.y) << " ";
+ break;
+ }
+ case U_EMR_ARCTO:
+ {
+ dbg_str << "<!-- U_EMR_ARCTO -->\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, &center, &start, &end, &size)){
+ // draw a line from current position to start, arc from there
+ tmp_path << "\n\tL " << pix_to_xy(d, start.x, start.y);
+ tmp_path << " A " << pix_to_abs_size(d, size.x)/2.0 << "," << pix_to_abs_size(d, size.y)/2.0;
+ tmp_path << " ";
+ tmp_path << 180.0 * current_rotation(d)/M_PI;
+ tmp_path << " ";
+ tmp_path << " " << f1 << "," << f2 << " ";
+ tmp_path << pix_to_xy(d, end.x, end.y)<< " ";
+
+ d->mask |= emr_mask;
+ }
+ else {
+ dbg_str << "<!-- ARCTO record is invalid -->\n";
+ }
+ break;
+ }
+ case U_EMR_POLYDRAW: dbg_str << "<!-- U_EMR_POLYDRAW -->\n"; break;
+ case U_EMR_SETARCDIRECTION:
+ {
+ dbg_str << "<!-- U_EMR_SETARCDIRECTION -->\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 << "<!-- U_EMR_SETMITERLIMIT -->\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 << "<!-- U_EMR_BEGINPATH -->\n";
+ // The next line should never be needed, should have been handled before main switch
+ // qualifier added because EMF's encountered where moveto preceded beginpath followed by lineto
+ if(d->mask & U_DRAW_VISIBLE){
+ *(d->path) = "";
+ }
+ d->mask |= emr_mask;
+ break;
+ }
+ case U_EMR_ENDPATH:
+ {
+ dbg_str << "<!-- U_EMR_ENDPATH -->\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 << "<!-- U_EMR_CLOSEFIGURE -->\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 << "<!-- U_EMR_FILLPATH -->\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 << "<!-- U_EMR_STROKEANDFILLPATH -->\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 << "<!-- U_EMR_STROKEPATH -->\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 << "<!-- U_EMR_FLATTENPATH -->\n"; break;
+ case U_EMR_WIDENPATH: dbg_str << "<!-- U_EMR_WIDENPATH -->\n"; break;
+ case U_EMR_SELECTCLIPPATH: dbg_str << "<!-- U_EMR_SELECTCLIPPATH -->\n"; break;
+ case U_EMR_ABORTPATH:
+ {
+ dbg_str << "<!-- U_EMR_ABORTPATH -->\n";
+ *(d->path) = "";
+ d->drawtype = 0;
+ break;
+ }
+ case U_EMR_UNDEF69: dbg_str << "<!-- U_EMR_UNDEF69 -->\n"; break;
+ case U_EMR_COMMENT:
+ {
+ dbg_str << "<!-- U_EMR_COMMENT -->\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 << " <!-- \"";
+ tmp_outsvg << tmp_str.str().c_str();
+ tmp_outsvg << "\" -->\n";
+ }
+
+ break;
+ }
+ case U_EMR_FILLRGN: dbg_str << "<!-- U_EMR_FILLRGN -->\n"; break;
+ case U_EMR_FRAMERGN: dbg_str << "<!-- U_EMR_FRAMERGN -->\n"; break;
+ case U_EMR_INVERTRGN: dbg_str << "<!-- U_EMR_INVERTRGN -->\n"; break;
+ case U_EMR_PAINTRGN: dbg_str << "<!-- U_EMR_PAINTRGN -->\n"; break;
+ case U_EMR_EXTSELECTCLIPRGN:
+ {
+ dbg_str << "<!-- U_EMR_EXTSELECTCLIPRGN -->\n";
+
+ PU_EMREXTSELECTCLIPRGN pEmr = (PU_EMREXTSELECTCLIPRGN) lpEMFR;
+ if (pEmr->iMode == U_RGN_COPY)
+ clipset = false;
+ break;
+ }
+ case U_EMR_BITBLT:
+ {
+ dbg_str << "<!-- U_EMR_BITBLT -->\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
+
+ if(pEmr->dwRop == U_NOOP)break; /* GDI applications apparently often end with this as a sort of flush(), nothing should be drawn */
+ int32_t dx = pEmr->Dest.x;
+ int32_t dy = pEmr->Dest.y;
+ int32_t dw = pEmr->cDest.x;
+ int32_t dh = pEmr->cDest.y;
+ SVGOStringStream tmp_rectangle;
+ tmp_rectangle << "\n\tM " << pix_to_xy( d, dx, dy ) << " ";
+ tmp_rectangle << "\n\tL " << pix_to_xy( d, dx + dw, dy ) << " ";
+ tmp_rectangle << "\n\tL " << pix_to_xy( d, dx + dw, dy + dh ) << " ";
+ tmp_rectangle << "\n\tL " << pix_to_xy( d, dx, dy + dh ) << " ";
+ tmp_rectangle << "\n\tz";
+
+ d->mask |= emr_mask;
+ 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();
+ }
+ else {
+ double dx = pix_to_x_point( d, pEmr->Dest.x, pEmr->Dest.y);
+ double dy = pix_to_y_point( d, pEmr->Dest.x, pEmr->Dest.y);
+ double dw = pix_to_abs_size( d, pEmr->cDest.x);
+ double dh = pix_to_abs_size( d, pEmr->cDest.y);
+ //source position within the bitmap, in pixels
+ int sx = pEmr->Src.x + pEmr->xformSrc.eDx;
+ int sy = pEmr->Src.y + pEmr->xformSrc.eDy;
+ int sw = 0; // extract all of the image
+ int sh = 0;
+ if(sx<0)sx=0;
+ if(sy<0)sy=0;
+ common_image_extraction(d,pEmr,dx,dy,dw,dh,sx,sy,sw,sh,
+ pEmr->iUsageSrc, pEmr->offBitsSrc, pEmr->cbBitsSrc, pEmr->offBmiSrc, pEmr->cbBmiSrc);
+ }
+ break;
+ }
+ case U_EMR_STRETCHBLT:
+ {
+ dbg_str << "<!-- U_EMR_STRETCHBLT -->\n";
+ PU_EMRSTRETCHBLT pEmr = (PU_EMRSTRETCHBLT) lpEMFR;
+ // Always grab image, ignore modes.
+ if (pEmr->cbBmiSrc) {
+ double dx = pix_to_x_point( d, pEmr->Dest.x, pEmr->Dest.y);
+ double dy = pix_to_y_point( d, pEmr->Dest.x, pEmr->Dest.y);
+ double dw = pix_to_abs_size( d, pEmr->cDest.x);
+ double dh = pix_to_abs_size( d, pEmr->cDest.y);
+ //source position within the bitmap, in pixels
+ int sx = pEmr->Src.x + pEmr->xformSrc.eDx;
+ int sy = pEmr->Src.y + pEmr->xformSrc.eDy;
+ int sw = pEmr->cSrc.x; // extract the specified amount of the image
+ int sh = pEmr->cSrc.y;
+ common_image_extraction(d,pEmr,dx,dy,dw,dh,sx,sy,sw,sh,
+ pEmr->iUsageSrc, pEmr->offBitsSrc, pEmr->cbBitsSrc, pEmr->offBmiSrc, pEmr->cbBmiSrc);
+ }
+ break;
+ }
+ case U_EMR_MASKBLT:
+ {
+ dbg_str << "<!-- U_EMR_MASKBLT -->\n";
+ PU_EMRMASKBLT pEmr = (PU_EMRMASKBLT) lpEMFR;
+ // Always grab image, ignore masks and modes.
+ if (pEmr->cbBmiSrc) {
+ double dx = pix_to_x_point( d, pEmr->Dest.x, pEmr->Dest.y);
+ double dy = pix_to_y_point( d, pEmr->Dest.x, pEmr->Dest.y);
+ double dw = pix_to_abs_size( d, pEmr->cDest.x);
+ double dh = pix_to_abs_size( d, pEmr->cDest.y);
+ int sx = pEmr->Src.x + pEmr->xformSrc.eDx; //source position within the bitmap, in pixels
+ int sy = pEmr->Src.y + pEmr->xformSrc.eDy;
+ int sw = 0; // extract all of the image
+ int sh = 0;
+ common_image_extraction(d,pEmr,dx,dy,dw,dh,sx,sy,sw,sh,
+ pEmr->iUsageSrc, pEmr->offBitsSrc, pEmr->cbBitsSrc, pEmr->offBmiSrc, pEmr->cbBmiSrc);
+ }
+ break;
+ }
+ case U_EMR_PLGBLT: dbg_str << "<!-- U_EMR_PLGBLT -->\n"; break;
+ case U_EMR_SETDIBITSTODEVICE: dbg_str << "<!-- U_EMR_SETDIBITSTODEVICE -->\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 dx = pix_to_x_point( d, pEmr->Dest.x, pEmr->Dest.y );
+ double dy = pix_to_y_point( d, pEmr->Dest.x, pEmr->Dest.y );
+ double dw = pix_to_abs_size( d, pEmr->cDest.x);
+ double dh = pix_to_abs_size( d, pEmr->cDest.y);
+ int sx = pEmr->Src.x; //source position within the bitmap, in pixels
+ int sy = pEmr->Src.y;
+ int sw = pEmr->cSrc.x; // extract the specified amount of the image
+ int sh = pEmr->cSrc.y;
+ common_image_extraction(d,pEmr,dx,dy,dw,dh,sx,sy,sw,sh,
+ pEmr->iUsageSrc, pEmr->offBitsSrc, pEmr->cbBitsSrc, pEmr->offBmiSrc, pEmr->cbBmiSrc);
+
+ dbg_str << "<!-- U_EMR_STRETCHDIBITS -->\n";
+ break;
+ }
+ case U_EMR_EXTCREATEFONTINDIRECTW:
+ {
+ dbg_str << "<!-- U_EMR_EXTCREATEFONTINDIRECTW -->\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 << "<!-- U_EMR_EXTTEXTOUTA/W -->\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;
+ }
+ uint32_t fOptions = pEmr->emrtext.fOptions;
+
+ 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);
+
+ /* Rotation issues are handled entirely in libTERE now */
+
+ 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].font_name)){
+ free(d->dc[d->level].font_name);
+ d->dc[d->level].font_name = strdup("Times New Roman");
+ }
+
+ char *ansi_text;
+ ansi_text = (char *) U_Utf32leToUtf8((uint32_t *)dup_wt, 0, NULL);
+ free(dup_wt);
+ // Empty string or starts with an invalid escape/control sequence, which is bogus text. Throw it out before g_markup_escape_text can make things worse
+ if(*((uint8_t *)ansi_text) <= 0x1F){
+ free(ansi_text);
+ ansi_text=NULL;
+ }
+
+ if (ansi_text) {
+
+ SVGOStringStream ts;
+
+ gchar *escaped_text = g_markup_escape_text(ansi_text, -1);
+
+ tsp.x = x*0.8; // TERE expects sizes in points.
+ tsp.y = y*0.8;
+ tsp.color.Red = d->dc[d->level].textColor.Red;
+ tsp.color.Green = d->dc[d->level].textColor.Green;
+ tsp.color.Blue = d->dc[d->level].textColor.Blue;
+ tsp.color.Reserved = 0;
+ switch(d->dc[d->level].style.font_style.value){
+ case SP_CSS_FONT_STYLE_OBLIQUE:
+ tsp.italics = FC_SLANT_OBLIQUE; break;
+ case SP_CSS_FONT_STYLE_ITALIC:
+ tsp.italics = FC_SLANT_ITALIC; break;
+ default:
+ case SP_CSS_FONT_STYLE_NORMAL:
+ tsp.italics = FC_SLANT_ROMAN; break;
+ }
+ switch(d->dc[d->level].style.font_weight.value){
+ case SP_CSS_FONT_WEIGHT_100: tsp.weight = FC_WEIGHT_THIN ; break;
+ case SP_CSS_FONT_WEIGHT_200: tsp.weight = FC_WEIGHT_EXTRALIGHT ; break;
+ case SP_CSS_FONT_WEIGHT_300: tsp.weight = FC_WEIGHT_LIGHT ; break;
+ case SP_CSS_FONT_WEIGHT_400: tsp.weight = FC_WEIGHT_NORMAL ; break;
+ case SP_CSS_FONT_WEIGHT_500: tsp.weight = FC_WEIGHT_MEDIUM ; break;
+ case SP_CSS_FONT_WEIGHT_600: tsp.weight = FC_WEIGHT_SEMIBOLD ; break;
+ case SP_CSS_FONT_WEIGHT_700: tsp.weight = FC_WEIGHT_BOLD ; break;
+ case SP_CSS_FONT_WEIGHT_800: tsp.weight = FC_WEIGHT_EXTRABOLD ; break;
+ case SP_CSS_FONT_WEIGHT_900: tsp.weight = FC_WEIGHT_HEAVY ; break;
+ case SP_CSS_FONT_WEIGHT_NORMAL: tsp.weight = FC_WEIGHT_NORMAL ; break;
+ case SP_CSS_FONT_WEIGHT_BOLD: tsp.weight = FC_WEIGHT_BOLD ; break;
+ case SP_CSS_FONT_WEIGHT_LIGHTER: tsp.weight = FC_WEIGHT_EXTRALIGHT ; break;
+ case SP_CSS_FONT_WEIGHT_BOLDER: tsp.weight = FC_WEIGHT_EXTRABOLD ; break;
+ default: tsp.weight = FC_WEIGHT_NORMAL ; break;
+ }
+ // EMF only supports two types of text decoration
+ tsp.decoration = TXTDECOR_NONE;
+ if(d->dc[d->level].style.text_decoration_line.underline){ tsp.decoration |= TXTDECOR_UNDER; }
+ if(d->dc[d->level].style.text_decoration_line.line_through){ tsp.decoration |= TXTDECOR_STRIKE;}
+
+ // EMF textalignment is a bit strange: 0x6 is center, 0x2 is right, 0x0 is left, the value 0x4 is also drawn left
+ tsp.taln = ((d->dc[d->level].textAlign & U_TA_CENTER) == U_TA_CENTER) ? ALICENTER :
+ (((d->dc[d->level].textAlign & U_TA_CENTER) == U_TA_LEFT) ? ALILEFT :
+ ALIRIGHT);
+ tsp.taln |= ((d->dc[d->level].textAlign & U_TA_BASEBIT) ? ALIBASE :
+ ((d->dc[d->level].textAlign & U_TA_BOTTOM) ? ALIBOT :
+ ALITOP));
+
+ // language direction can be encoded two ways, U_TA_RTLREADING is preferred
+ if( (fOptions & U_ETO_RTLREADING) || (d->dc[d->level].textAlign & U_TA_RTLREADING) ){ tsp.ldir = LDIR_RL; }
+ else{ tsp.ldir = LDIR_LR; }
+
+ tsp.condensed = FC_WIDTH_NORMAL; // Not implemented well in libTERE (yet)
+ tsp.ori = d->dc[d->level].style.baseline_shift.value; // For now orientation is always the same as escapement
+ tsp.ori += 180.0 * current_rotation(d)/ M_PI; // radians to degrees
+ tsp.string = (uint8_t *) U_strdup(escaped_text); // this will be free'd much later at a trinfo_clear().
+ tsp.fs = d->dc[d->level].style.font_size.computed * 0.8; // Font size in points
+ char *fontspec = TR_construct_fontspec(&tsp, d->dc[d->level].font_name);
+ tsp.fi_idx = ftinfo_load_fontname(d->tri->fti,fontspec);
+ free(fontspec);
+ // when font name includes narrow it may not be set to "condensed". Narrow fonts do not work well anyway though
+ // as the metrics from fontconfig may not match, or the font may not be present.
+ if(0<= TR_findcasesub(d->dc[d->level].font_name, (char *) "Narrow")){ tsp.co=1; }
+ else { tsp.co=0; }
+
+ int status = trinfo_load_textrec(d->tri, &tsp, tsp.ori,TR_EMFBOT); // ori is actually escapement
+ if(status==-1){ // change of escapement, emit what we have and reset
+ TR_layout_analyze(d->tri);
+ TR_layout_2_svg(d->tri);
+ ts << d->tri->out;
+ *(d->outsvg) += ts.str().c_str();
+ d->tri = trinfo_clear(d->tri);
+ (void) trinfo_load_textrec(d->tri, &tsp, tsp.ori,TR_EMFBOT); // ignore return status, it must work
+ }
+
+ g_free(escaped_text);
+ free(ansi_text);
+ }
+
+ break;
+ }
+ case U_EMR_POLYBEZIER16:
+ {
+ dbg_str << "<!-- U_EMR_POLYBEZIER16 -->\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_xy( d, apts[0].x, apts[0].y ) << " ";
+
+ for (i=1; i<pEmr->cpts; ) {
+ tmp_str << "\n\tC ";
+ for (j=0; j<3 && i<pEmr->cpts; j++,i++) {
+ tmp_str << pix_to_xy( d, apts[i].x, apts[i].y ) << " ";
+ }
+ }
+
+ tmp_path << tmp_str.str().c_str();
+
+ break;
+ }
+ case U_EMR_POLYGON16:
+ {
+ dbg_str << "<!-- U_EMR_POLYGON16 -->\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_xy( d, apts[first].x, apts[first].y ) << " ";
+
+ for (i=first+1; i<pEmr->cpts; i++) {
+ tmp_poly << "\n\tL " << pix_to_xy( 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 << "<!-- U_EMR_POLYLINE16 -->\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_xy( d, apts[0].x, apts[0].y ) << " ";
+
+ for (i=1; i<pEmr->cpts; i++) {
+ tmp_str << "\n\tL " << pix_to_xy( d, apts[i].x, apts[i].y ) << " ";
+ }
+
+ tmp_path << tmp_str.str().c_str();
+
+ break;
+ }
+ case U_EMR_POLYBEZIERTO16:
+ {
+ dbg_str << "<!-- U_EMR_POLYBEZIERTO16 -->\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; i<pEmr->cpts;) {
+ tmp_path << "\n\tC ";
+ for (j=0; j<3 && i<pEmr->cpts; j++,i++) {
+ tmp_path << pix_to_xy( d, apts[i].x, apts[i].y) << " ";
+ }
+ }
+
+ break;
+ }
+ case U_EMR_POLYLINETO16:
+ {
+ dbg_str << "<!-- U_EMR_POLYLINETO16 -->\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; i<pEmr->cpts;i++) {
+ tmp_path << "\n\tL " << pix_to_xy( d, apts[i].x, apts[i].y) << " ";
+ }
+
+ break;
+ }
+ case U_EMR_POLYPOLYLINE16:
+ case U_EMR_POLYPOLYGON16:
+ {
+ if (lpEMFR->iType == U_EMR_POLYPOLYLINE16)
+ dbg_str << "<!-- U_EMR_POLYPOLYLINE16 -->\n";
+ if (lpEMFR->iType == U_EMR_POLYPOLYGON16)
+ dbg_str << "<!-- U_EMR_POLYPOLYGON16 -->\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; n<pEmr->nPolys && i<pEmr->cpts; n++) {
+ SVGOStringStream poly_path;
+
+ poly_path << "\n\tM " << pix_to_xy( d, apts[i].x, apts[i].y) << " ";
+ i++;
+
+ for (j=1; j<pEmr->aPolyCounts[n] && i<pEmr->cpts; j++) {
+ poly_path << "\n\tL " << pix_to_xy( 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 << "<!-- U_EMR_POLYDRAW16 -->\n"; break;
+ case U_EMR_CREATEMONOBRUSH:
+ {
+ dbg_str << "<!-- U_EMR_CREATEDIBPATTERNBRUSHPT -->\n";
+
+ PU_EMRCREATEMONOBRUSH pEmr = (PU_EMRCREATEMONOBRUSH) lpEMFR;
+ insert_object(d, pEmr->ihBrush, U_EMR_CREATEMONOBRUSH, lpEMFR);
+ break;
+ }
+ case U_EMR_CREATEDIBPATTERNBRUSHPT:
+ {
+ dbg_str << "<!-- U_EMR_CREATEDIBPATTERNBRUSHPT -->\n";
+
+ PU_EMRCREATEDIBPATTERNBRUSHPT pEmr = (PU_EMRCREATEDIBPATTERNBRUSHPT) lpEMFR;
+ insert_object(d, pEmr->ihBrush, U_EMR_CREATEDIBPATTERNBRUSHPT, lpEMFR);
+ break;
+ }
+ case U_EMR_EXTCREATEPEN:
+ {
+ dbg_str << "<!-- U_EMR_EXTCREATEPEN -->\n";
+
+ PU_EMREXTCREATEPEN pEmr = (PU_EMREXTCREATEPEN) lpEMFR;
+ insert_object(d, pEmr->ihPen, U_EMR_EXTCREATEPEN, lpEMFR);
+ break;
+ }
+ case U_EMR_POLYTEXTOUTA: dbg_str << "<!-- U_EMR_POLYTEXTOUTA -->\n"; break;
+ case U_EMR_POLYTEXTOUTW: dbg_str << "<!-- U_EMR_POLYTEXTOUTW -->\n"; break;
+ case U_EMR_SETICMMODE:
+ {
+ dbg_str << "<!-- U_EMR_SETICMMODE -->\n";
+ PU_EMRSETICMMODE pEmr = (PU_EMRSETICMMODE) lpEMFR;
+ ICMmode= pEmr->iMode;
+ break;
+ }
+ case U_EMR_CREATECOLORSPACE: dbg_str << "<!-- U_EMR_CREATECOLORSPACE -->\n"; break;
+ case U_EMR_SETCOLORSPACE: dbg_str << "<!-- U_EMR_SETCOLORSPACE -->\n"; break;
+ case U_EMR_DELETECOLORSPACE: dbg_str << "<!-- U_EMR_DELETECOLORSPACE -->\n"; break;
+ case U_EMR_GLSRECORD: dbg_str << "<!-- U_EMR_GLSRECORD -->\n"; break;
+ case U_EMR_GLSBOUNDEDRECORD: dbg_str << "<!-- U_EMR_GLSBOUNDEDRECORD -->\n"; break;
+ case U_EMR_PIXELFORMAT: dbg_str << "<!-- U_EMR_PIXELFORMAT -->\n"; break;
+ case U_EMR_DRAWESCAPE: dbg_str << "<!-- U_EMR_DRAWESCAPE -->\n"; break;
+ case U_EMR_EXTESCAPE: dbg_str << "<!-- U_EMR_EXTESCAPE -->\n"; break;
+ case U_EMR_UNDEF107: dbg_str << "<!-- U_EMR_UNDEF107 -->\n"; break;
+ // U_EMR_SMALLTEXTOUT is handled with U_EMR_EXTTEXTOUTA/W above
+ case U_EMR_FORCEUFIMAPPING: dbg_str << "<!-- U_EMR_FORCEUFIMAPPING -->\n"; break;
+ case U_EMR_NAMEDESCAPE: dbg_str << "<!-- U_EMR_NAMEDESCAPE -->\n"; break;
+ case U_EMR_COLORCORRECTPALETTE: dbg_str << "<!-- U_EMR_COLORCORRECTPALETTE -->\n"; break;
+ case U_EMR_SETICMPROFILEA: dbg_str << "<!-- U_EMR_SETICMPROFILEA -->\n"; break;
+ case U_EMR_SETICMPROFILEW: dbg_str << "<!-- U_EMR_SETICMPROFILEW -->\n"; break;
+ case U_EMR_ALPHABLEND: dbg_str << "<!-- U_EMR_ALPHABLEND -->\n"; break;
+ case U_EMR_SETLAYOUT: dbg_str << "<!-- U_EMR_SETLAYOUT -->\n"; break;
+ case U_EMR_TRANSPARENTBLT: dbg_str << "<!-- U_EMR_TRANSPARENTBLT -->\n"; break;
+ case U_EMR_UNDEF117: dbg_str << "<!-- U_EMR_UNDEF117 -->\n"; break;
+ case U_EMR_GRADIENTFILL:
+ {
+ /* 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. So the linear gradient
+ is implemented, and the triangle fill just paints with the color of the first corner.
+
+ This record can hold a series of gradients so we are forced to add path elements directly here,
+ it cannot wait for the top of the main loop. Any existing path is erased.
+
+ */
+ dbg_str << "<!-- U_EMR_GRADIENTFILL -->\n";
+ PU_EMRGRADIENTFILL pEmr = (PU_EMRGRADIENTFILL) lpEMFR;
+ int nV = pEmr->nTriVert; // Number of TriVertex objects
+ int nG = pEmr->nGradObj; // Number of gradient triangle/rectangle objects
+ U_TRIVERTEX *tv = (U_TRIVERTEX *)(((char *)lpEMFR) + sizeof(U_EMRGRADIENTFILL));
+ if( pEmr->ulMode == U_GRADIENT_FILL_RECT_H ||
+ pEmr->ulMode == U_GRADIENT_FILL_RECT_V
+ ){
+ SVGOStringStream tmp_rectangle;
+ int i,fill_idx;
+ U_GRADIENT4 *rcs = (U_GRADIENT4 *)(((char *)lpEMFR) + sizeof(U_EMRGRADIENTFILL) + sizeof(U_TRIVERTEX)*nV);
+ for(i=0;i<nG;i++){
+ tmp_rectangle << "\n<path d=\"";
+ fill_idx = add_gradient(d, pEmr->ulMode, tv[rcs[i].UpperLeft], tv[rcs[i].LowerRight]);
+ tmp_rectangle << "\n\tM " << pix_to_xy( d, tv[rcs[i].UpperLeft ].x , tv[rcs[i].UpperLeft ].y ) << " ";
+ tmp_rectangle << "\n\tL " << pix_to_xy( d, tv[rcs[i].LowerRight].x , tv[rcs[i].UpperLeft ].y ) << " ";
+ tmp_rectangle << "\n\tL " << pix_to_xy( d, tv[rcs[i].LowerRight].x , tv[rcs[i].LowerRight].y ) << " ";
+ tmp_rectangle << "\n\tL " << pix_to_xy( d, tv[rcs[i].UpperLeft ].x , tv[rcs[i].LowerRight].y ) << " ";
+ tmp_rectangle << "\n\tz\"";
+ tmp_rectangle << "\n\tstyle=\"stroke:none;fill:url(#";
+ tmp_rectangle << d->gradients.strings[fill_idx];
+ tmp_rectangle << ");\"\n/>\n";
+ }
+ *(d->outsvg) += tmp_rectangle.str().c_str();
+ }
+ else if(pEmr->ulMode == U_GRADIENT_FILL_TRIANGLE){
+ SVGOStringStream tmp_triangle;
+ char tmpcolor[8];
+ int i;
+ U_GRADIENT3 *tris = (U_GRADIENT3 *)(((char *)lpEMFR) + sizeof(U_EMRGRADIENTFILL) + sizeof(U_TRIVERTEX)*nV);
+ for(i=0;i<nG;i++){
+ tmp_triangle << "\n<path d=\"";
+ sprintf(tmpcolor,"%6.6X",sethexcolor(trivertex_to_colorref(tv[tris[i].Vertex1])));
+ tmp_triangle << "\n\tM " << pix_to_xy( d, tv[tris[i].Vertex1].x , tv[tris[i].Vertex1].y ) << " ";
+ tmp_triangle << "\n\tL " << pix_to_xy( d, tv[tris[i].Vertex2].x , tv[tris[i].Vertex2].y ) << " ";
+ tmp_triangle << "\n\tL " << pix_to_xy( d, tv[tris[i].Vertex3].x , tv[tris[i].Vertex3].y ) << " ";
+ tmp_triangle << "\n\tz\"";
+ tmp_triangle << "\n\tstyle=\"stroke:none;fill:#";
+ tmp_triangle << tmpcolor;
+ tmp_triangle << ";\"\n/>\n";
+ }
+ *(d->outsvg) += tmp_triangle.str().c_str();
+ }
+ *(d->path) = "";
+ // if it is anything else the record is bogus, so ignore it
+ break;
+ }
+ case U_EMR_SETLINKEDUFIS: dbg_str << "<!-- U_EMR_SETLINKEDUFIS -->\n"; break;
+ case U_EMR_SETTEXTJUSTIFICATION: dbg_str << "<!-- U_EMR_SETTEXTJUSTIFICATION -->\n"; break;
+ case U_EMR_COLORMATCHTOTARGETW: dbg_str << "<!-- U_EMR_COLORMATCHTOTARGETW -->\n"; break;
+ case U_EMR_CREATECOLORSPACEW: dbg_str << "<!-- U_EMR_CREATECOLORSPACEW -->\n"; break;
+ default:
+ dbg_str << "<!-- U_EMR_??? -->\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;
+ (void) emr_properties(U_EMR_INVALID); // force the release of the lookup table memory, returned value is irrelevant
+
+ return 1;
+}
+
+void Emf::free_emf_strings(EMF_STRINGS name){
+ if(name.count){
+ for(int i=0; i< name.count; i++){ free(name.strings[i]); }
+ free(name.strings);
+ }
+}
+
+SPDocument *
+Emf::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri )
+{
+ EMF_CALLBACK_DATA d;
+
+// memset(&d, 0, sizeof(d));
+ memset(&d, 0, sizeof(EMF_CALLBACK_DATA));
+
+ for(int i = 0; i < EMF_MAX_DC+1; i++){ // be sure all values and pointers are empty to start with
+ memset(&(d.dc[i]),0,sizeof(EMF_DEVICE_CONTEXT));
+ }
+
+ 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;
+ d.dc[0].font_name = strdup("Arial"); // Default font, EMF spec says device can pick whatever it wants
+ d.dc[0].textColor = U_RGB(0, 0, 0); // default foreground color (black)
+ d.dc[0].bkColor = U_RGB(255, 255, 255); // default background color (white)
+ d.dc[0].bkMode = U_TRANSPARENT;
+ d.dc[0].dirty = 0;
+
+ if (uri == NULL) {
+ return NULL;
+ }
+
+ 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.E2IdirY = 1.0;
+ d.D2PscaleX = 1.0;
+ d.D2PscaleY = 1.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;
+ d.gradients.size = 0;
+ d.gradients.count = 0;
+ d.gradients.strings = NULL;
+
+ // set up the size default for patterns in defs. This might not be referenced if there are no patterns defined in the drawing.
+
+ *(d.defs) += "\n";
+ *(d.defs) += " <pattern id=\"EMFhbasepattern\" \n";
+ *(d.defs) += " patternUnits=\"userSpaceOnUse\"\n";
+ *(d.defs) += " width=\"6\" \n";
+ *(d.defs) += " height=\"6\" \n";
+ *(d.defs) += " x=\"0\" \n";
+ *(d.defs) += " y=\"0\"> \n";
+ *(d.defs) += " </pattern> \n";
+
+
+ size_t length;
+ char *contents;
+ if(emf_readdata(uri, &contents, &length))return(NULL);
+
+ d.pDesc = NULL;
+
+ // set up the text reassembly system
+ if(!(d.tri = trinfo_init(NULL)))return(NULL);
+ (void) trinfo_load_ft_opts(d.tri, 1,
+ FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP,
+ FT_KERNING_UNSCALED);
+
+ (void) myEnhMetaFileProc(contents,length, &d);
+ free(contents);
+
+ 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;
+ free_emf_strings(d.hatches);
+ free_emf_strings(d.images);
+ free_emf_strings(d.gradients);
+
+ if (d.emf_obj) {
+ int i;
+ for (i=0; i<d.n_obj; i++)
+ delete_object(&d, i);
+ delete[] d.emf_obj;
+ }
+
+ d.dc[0].style.stroke_dasharray.values.clear();
+
+ for(int i=0; i<=d.level;i++){
+ if(d.dc[i].font_name)free(d.dc[i].font_name);
+ }
+
+ d.tri = trinfo_release_except_FC(d.tri);
+
+ // Set viewBox if it doesn't exist
+ if (doc && !doc->getRoot()->viewBox_set) {
+ bool saved = Inkscape::DocumentUndo::getUndoSensitive(doc);
+ Inkscape::DocumentUndo::setUndoSensitive(doc, false);
+
+ doc->ensureUpToDate();
+
+ // Set document unit
+ Inkscape::XML::Node *repr = sp_document_namedview(doc, 0)->getRepr();
+ Inkscape::SVGOStringStream os;
+ Inkscape::Util::Unit const* doc_unit = doc->getWidth().unit;
+ os << doc_unit->abbr;
+ repr->setAttribute("inkscape:document-units", os.str().c_str());
+
+ // Set viewBox
+ doc->setViewBox(Geom::Rect::from_xywh(0, 0, doc->getWidth().value(doc_unit), doc->getHeight().value(doc_unit)));
+ doc->ensureUpToDate();
+
+ // Scale and translate objects
+ double scale = Inkscape::Util::Quantity::convert(1, "px", doc_unit);
+ ShapeEditor::blockSetItem(true);
+ double dh;
+ if(SP_ACTIVE_DOCUMENT){ // for file menu open or import, or paste from clipboard
+ dh = SP_ACTIVE_DOCUMENT->getHeight().value("px");
+ }
+ else { // for open via --file on command line
+ dh = doc->getHeight().value("px");
+ }
+ doc->getRoot()->scaleChildItemsRec(Geom::Scale(scale), Geom::Point(0, dh));
+ ShapeEditor::blockSetItem(false);
+
+ Inkscape::DocumentUndo::setUndoSensitive(doc, saved);
+ }
+
+ return doc;
+}
+
+
+void
+Emf::init (void)
+{
+ /* EMF in */
+ Inkscape::Extension::build_from_mem(
+ "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
+ "<name>" N_("EMF Input") "</name>\n"
+ "<id>org.inkscape.input.emf</id>\n"
+ "<input>\n"
+ "<extension>.emf</extension>\n"
+ "<mimetype>image/x-emf</mimetype>\n"
+ "<filetypename>" N_("Enhanced Metafiles (*.emf)") "</filetypename>\n"
+ "<filetypetooltip>" N_("Enhanced Metafiles") "</filetypetooltip>\n"
+ "<output_extension>org.inkscape.output.emf</output_extension>\n"
+ "</input>\n"
+ "</inkscape-extension>", new Emf());
+
+ /* EMF out */
+ Inkscape::Extension::build_from_mem(
+ "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
+ "<name>" N_("EMF Output") "</name>\n"
+ "<id>org.inkscape.output.emf</id>\n"
+ "<param name=\"textToPath\" gui-text=\"" N_("Convert texts to paths") "\" type=\"boolean\">true</param>\n"
+ "<param name=\"TnrToSymbol\" gui-text=\"" N_("Map Unicode to Symbol font") "\" type=\"boolean\">true</param>\n"
+ "<param name=\"TnrToWingdings\" gui-text=\"" N_("Map Unicode to Wingdings") "\" type=\"boolean\">true</param>\n"
+ "<param name=\"TnrToZapfDingbats\" gui-text=\"" N_("Map Unicode to Zapf Dingbats") "\" type=\"boolean\">true</param>\n"
+ "<param name=\"UsePUA\" gui-text=\"" N_("Use MS Unicode PUA (0xF020-0xF0FF) for converted characters") "\" type=\"boolean\">false</param>\n"
+ "<param name=\"FixPPTCharPos\" gui-text=\"" N_("Compensate for PPT font bug") "\" type=\"boolean\">false</param>\n"
+ "<param name=\"FixPPTDashLine\" gui-text=\"" N_("Convert dashed/dotted lines to single lines") "\" type=\"boolean\">false</param>\n"
+ "<param name=\"FixPPTGrad2Polys\" gui-text=\"" N_("Convert gradients to colored polygon series") "\" type=\"boolean\">false</param>\n"
+ "<param name=\"FixPPTLinGrad\" gui-text=\"" N_("Use native rectangular linear gradients") "\" type=\"boolean\">false</param>\n"
+ "<param name=\"FixPPTPatternAsHatch\" gui-text=\"" N_("Map all fill patterns to standard EMF hatches") "\" type=\"boolean\">false</param>\n"
+ "<param name=\"FixImageRot\" gui-text=\"" N_("Ignore image rotations") "\" type=\"boolean\">false</param>\n"
+ "<output>\n"
+ "<extension>.emf</extension>\n"
+ "<mimetype>image/x-emf</mimetype>\n"
+ "<filetypename>" N_("Enhanced Metafile (*.emf)") "</filetypename>\n"
+ "<filetypetooltip>" N_("Enhanced Metafile") "</filetypetooltip>\n"
+ "</output>\n"
+ "</inkscape-extension>", 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 --git a/src/extension/internal/emf-inout.h b/src/extension/internal/emf-inout.h
new file mode 100644
index 000000000..a97cb0a54
--- /dev/null
+++ b/src/extension/internal/emf-inout.h
@@ -0,0 +1,183 @@
+/** @file
+ * @brief Enhanced Metafile Input/Output
+ */
+/* Authors:
+ * Ulf Erikson <ulferikson@users.sf.net>
+ * David Mathog
+ *
+ * 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 <libuemf/uemf.h>
+#include "extension/internal/metafile-inout.h" // picks up PNG
+#include "extension/implementation/implementation.h"
+#include "style.h"
+#include "text_reassemble.h"
+
+namespace Inkscape {
+namespace Extension {
+namespace Internal {
+
+#define DIRTY_NONE 0x00
+#define DIRTY_TEXT 0x01
+#define DIRTY_FILL 0x02
+#define DIRTY_STROKE 0x04
+
+typedef struct {
+ int type;
+ int level;
+ char *lpEMFR;
+} EMF_OBJECT, *PEMF_OBJECT;
+
+typedef struct {
+ int size; // number of slots allocated in strings
+ int count; // number of slots used in strings
+ char **strings; // place to store strings
+} EMF_STRINGS, *PEMF_STRINGS;
+
+typedef struct emf_device_context {
+ struct SPStyle style;
+ char *font_name;
+ bool stroke_set;
+ int stroke_mode; // enumeration from drawmode, not used if fill_set is not True
+ int stroke_idx; // used with DRAW_PATTERN and DRAW_IMAGE to return the appropriate fill
+ int stroke_recidx;// record used to regenerate hatch when it needs to be redone due to bkmode, textmode, etc. change
+ 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
+ int fill_recidx; // record used to regenerate hatch when it needs to be redone due to bkmode, textmode, etc. change
+ int dirty; // holds the dirty bits for text, stroke, fill
+ U_SIZEL sizeWnd;
+ U_SIZEL sizeView;
+ U_POINTL winorg;
+ U_POINTL vieworg;
+ double ScaleInX, ScaleInY;
+ double ScaleOutX, ScaleOutY;
+ uint16_t bkMode;
+ U_COLORREF bkColor;
+ U_COLORREF textColor;
+ uint32_t textAlign;
+ U_XFORM worldTransform;
+ U_POINTL cur;
+} EMF_DEVICE_CONTEXT, *PEMF_DEVICE_CONTEXT;
+
+#define EMF_MAX_DC 128
+
+typedef struct {
+ Glib::ustring *outsvg;
+ Glib::ustring *path;
+ Glib::ustring *outdef;
+ Glib::ustring *defs;
+
+ EMF_DEVICE_CONTEXT dc[EMF_MAX_DC+1]; // FIXME: This should be dynamic..
+ int level;
+
+ double E2IdirY; // EMF Y direction relative to Inkscape Y direction. Will be negative for MM_LOMETRIC etc.
+ double D2PscaleX,D2PscaleY; // EMF device to Inkscape Page scale.
+ float MM100InX, MM100InY; // size of the drawing in hundredths of a millimeter
+ float PixelsInX, PixelsInY; // size of the drawing, in EMF device pixels
+ float PixelsOutX, PixelsOutY; // size of the drawing, in Inkscape pixels
+ double ulCornerInX,ulCornerInY; // Upper left corner, from header rclBounds, in logical units
+ double ulCornerOutX,ulCornerOutY; // Upper left corner, in Inkscape pixels
+ uint32_t mask; // Draw properties
+ int arcdir; //U_AD_COUNTERCLOCKWISE 1 or U_AD_CLOCKWISE 2
+
+ uint32_t dwRop2; // Binary raster operation, 0 if none (use brush/pen unmolested)
+ uint32_t dwRop3; // Ternary raster operation, 0 if none (use brush/pen unmolested)
+
+ float MMX;
+ float MMY;
+
+ unsigned int id;
+ unsigned int drawtype; // one of 0 or U_EMR_FILLPATH, U_EMR_STROKEPATH, U_EMR_STROKEANDFILLPATH
+ char *pDesc;
+ // both of these end up in <defs> under the names shown here. These structures allow duplicates to be avoided.
+ EMF_STRINGS hatches; // hold pattern names, all like EMFhatch#_$$$$$$ where # is the EMF hatch code and $$$$$$ is the color
+ EMF_STRINGS images; // hold images, all like Image#, where # is the slot the image lives.
+ EMF_STRINGS gradients; // hold gradient names, all like EMF[HV]_$$$$$$_$$$$$$ where $$$$$$ are the colors
+ TR_INFO *tri; // Text Reassembly data structure
+
+
+ int n_obj;
+ PEMF_OBJECT emf_obj;
+} EMF_CALLBACK_DATA, *PEMF_CALLBACK_DATA;
+
+class Emf : public Metafile
+{
+
+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:
+
+protected:
+ static void print_document_to_file(SPDocument *doc, const gchar *filename);
+ static double current_scale(PEMF_CALLBACK_DATA d);
+ static std::string current_matrix(PEMF_CALLBACK_DATA d, double x, double y, int useoffset);
+ static double current_rotation(PEMF_CALLBACK_DATA d);
+ static void enlarge_hatches(PEMF_CALLBACK_DATA d);
+ static int in_hatches(PEMF_CALLBACK_DATA d, char *test);
+ static uint32_t add_hatch(PEMF_CALLBACK_DATA d, uint32_t hatchType, U_COLORREF hatchColor);
+ static void enlarge_images(PEMF_CALLBACK_DATA d);
+ static int in_images(PEMF_CALLBACK_DATA d, char *test);
+ static 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);
+ static void enlarge_gradients(PEMF_CALLBACK_DATA d);
+ static int in_gradients(PEMF_CALLBACK_DATA d, char *test);
+ static uint32_t add_gradient(PEMF_CALLBACK_DATA d, uint32_t gradientType, U_TRIVERTEX tv1, U_TRIVERTEX tv2);
+ static void output_style(PEMF_CALLBACK_DATA d, int iType);
+ static double _pix_x_to_point(PEMF_CALLBACK_DATA d, double px);
+ static double _pix_y_to_point(PEMF_CALLBACK_DATA d, double py);
+ static double pix_to_x_point(PEMF_CALLBACK_DATA d, double px, double py);
+ static double pix_to_y_point(PEMF_CALLBACK_DATA d, double px, double py);
+ static double pix_to_abs_size(PEMF_CALLBACK_DATA d, double px);
+ static std::string pix_to_xy(PEMF_CALLBACK_DATA d, double x, double y);
+ static void select_pen(PEMF_CALLBACK_DATA d, int index);
+ static void select_extpen(PEMF_CALLBACK_DATA d, int index);
+ static void select_brush(PEMF_CALLBACK_DATA d, int index);
+ static void select_font(PEMF_CALLBACK_DATA d, int index);
+ static void delete_object(PEMF_CALLBACK_DATA d, int index);
+ static void insert_object(PEMF_CALLBACK_DATA d, int index, int type, PU_ENHMETARECORD pObj);
+ static int AI_hack(PU_EMRHEADER pEmr);
+ static uint32_t *unknown_chars(size_t count);
+ static void common_image_extraction(PEMF_CALLBACK_DATA d, void *pEmr,
+ double dx, double dy, double dw, double dh, int sx, int sy, int sw, int sh,
+ uint32_t iUsage, uint32_t offBits, uint32_t cbBits, uint32_t offBmi, uint32_t cbBmi);
+ static int myEnhMetaFileProc(char *contents, unsigned int length, PEMF_CALLBACK_DATA d);
+ static void free_emf_strings(EMF_STRINGS name);
+
+};
+
+} } } /* 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 --git a/src/extension/internal/emf-print.cpp b/src/extension/internal/emf-print.cpp
new file mode 100644
index 000000000..f4f7f08cb
--- /dev/null
+++ b/src/extension/internal/emf-print.cpp
@@ -0,0 +1,2061 @@
+/** @file
+ * @brief Enhanced Metafile printing
+ *//*
+ * Authors:
+ * Ulf Erikson <ulferikson@users.sf.net>
+ * Jon A. Cruz <jon@joncruz.org>
+ * 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 <string.h>
+#include <glibmm/miscutils.h>
+#include <libuemf/symbol_convert.h>
+#include <2geom/sbasis-to-bezier.h>
+#include <2geom/path.h>
+#include <2geom/pathvector.h>
+#include <2geom/rect.h>
+#include <2geom/curves.h>
+
+#include "helper/geom.h"
+#include "helper/geom-curves.h"
+#include "sp-item.h"
+#include "util/units.h"
+
+#include "style.h"
+#include "inkscape-version.h"
+#include "sp-root.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 "display/cairo-utils.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 "emf-print.h"
+
+
+namespace Inkscape {
+namespace Extension {
+namespace Internal {
+
+#define PXPERMETER 2835
+
+
+/* globals */
+static double PX2WORLD;
+static bool FixPPTCharPos, FixPPTDashLine, FixPPTGrad2Polys, FixPPTLinGrad, FixPPTPatternAsHatch, FixImageRot;
+static EMFTRACK *et = NULL;
+static EMFHANDLES *eht = NULL;
+
+void PrintEmf::smuggle_adxkyrtl_out(const char *string, uint32_t **adx, double *ky, int *rtl, int *ndx, float scale)
+{
+ float fdx;
+ int i;
+ uint32_t *ladx;
+ const char *cptr = &string[strlen(string) + 1]; // this works because of the first fake terminator
+
+ *adx = NULL;
+ *ky = 0.0; // set a default value
+ sscanf(cptr, "%7d", ndx);
+ if (!*ndx) {
+ return; // this could happen with an empty string
+ }
+ cptr += 7;
+ ladx = (uint32_t *) malloc(*ndx * sizeof(uint32_t));
+ if (!ladx) {
+ g_message("Out of memory");
+ }
+ *adx = ladx;
+ for (i = 0; i < *ndx; i++, cptr += 7, ladx++) {
+ sscanf(cptr, "%7f", &fdx);
+ *ladx = (uint32_t) round(fdx * scale);
+ }
+ cptr++; // skip 2nd fake terminator
+ sscanf(cptr, "%7f", &fdx);
+ *ky = fdx;
+ cptr += 7; // advance over ky and its space
+ sscanf(cptr, "%07d", rtl);
+}
+
+PrintEmf::PrintEmf()
+{
+ // all of the class variables are initialized elsewhere, many in PrintEmf::Begin,
+}
+
+
+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");
+
+ // Typically PX2WORLD is 1200/90, using inkscape's default dpi
+ PX2WORLD = 1200.0 / Inkscape::Util::Quantity::convert(1.0, "in", "px");
+ FixPPTCharPos = mod->get_param_bool("FixPPTCharPos");
+ FixPPTDashLine = mod->get_param_bool("FixPPTDashLine");
+ FixPPTGrad2Polys = mod->get_param_bool("FixPPTGrad2Polys");
+ FixPPTLinGrad = mod->get_param_bool("FixPPTLinGrad");
+ FixPPTPatternAsHatch = mod->get_param_bool("FixPPTPatternAsHatch");
+ FixImageRot = mod->get_param_bool("FixImageRot");
+
+ (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().value("px");
+ _height = doc->getHeight().value("px");
+
+ // initialize a few global variables
+ hbrush = hbrushOld = hpen = 0;
+ htextalignment = U_TA_BASELINE | U_TA_LEFT;
+ use_stroke = use_fill = simple_shape = usebk = false;
+
+ 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(Inkscape::Util::Quantity::convert(1, "px", "in"));
+
+ float dwInchesX = d.width();
+ float dwInchesY = d.height();
+
+ // dwInchesX x dwInchesY in micrometer units, 1200 dpi/25.4 -> dpmm
+ (void) drawing_size((int) ceil(dwInchesX * 25.4), (int) ceil(dwInchesY * 25.4),1200.0/25.4, &rclBounds, &rclFrame);
+
+ // set up the reference device as 100 X A4 horizontal, (1200 dpi/25.4 -> dpmm). Extra digits maintain dpi better in EMF
+ int MMX = 216;
+ int MMY = 279;
+ (void) device_size(MMX, MMY, 1200.0 / 25.4, &szlDev, &szlMm);
+ 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)) {
+ g_error("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)) {
+ g_error("Fatal programming error in PrintEmf::begin at EMRSETMAPMODE");
+ }
+
+
+ // In earlier versions this was used to scale from inkscape's dpi of 90 to
+ // the files 1200 dpi, taking into account PX2WORLD which was 20. Now PX2WORLD
+ // is set so that this matrix is unitary. The usual value of PX2WORLD is 1200/90,
+ // but might be different if the internal dpi is changed.
+
+ U_XFORM worldTransform;
+ worldTransform.eM11 = 1.0;
+ worldTransform.eM12 = 0.0;
+ worldTransform.eM21 = 0.0;
+ worldTransform.eM22 = 1.0;
+ 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)) {
+ g_error("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)) {
+ g_error("Fatal programming error in PrintEmf::begin at textcomment_set 1");
+ }
+
+ snprintf(buff, sizeof(buff) - 1, "Drawing=%.1lfx%.1lfpx, %.1lfx%.1lfmm", _width, _height, Inkscape::Util::Quantity::convert(dwInchesX, "in", "mm"), Inkscape::Util::Quantity::convert(dwInchesY, "in", "mm"));
+ rec = textcomment_set(buff);
+ if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintEmf::begin at textcomment_set 1");
+ }
+ }
+
+ /* set some parameters, else the program that reads the EMF may default to other values */
+
+ rec = U_EMRSETBKMODE_set(U_TRANSPARENT);
+ if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintEmf::begin at U_EMRSETBKMODE_set");
+ }
+
+ hpolyfillmode = U_WINDING;
+ rec = U_EMRSETPOLYFILLMODE_set(U_WINDING);
+ if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintEmf::begin at U_EMRSETPOLYFILLMODE_set");
+ }
+
+ // Text alignment: (only changed if RTL text is encountered )
+ // - (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.
+ htextalignment = U_TA_BASELINE | U_TA_LEFT;
+ rec = U_EMRSETTEXTALIGN_set(U_TA_BASELINE | U_TA_LEFT);
+ if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintEmf::begin at U_EMRSETTEXTALIGN_set");
+ }
+
+ htextcolor_rgb[0] = htextcolor_rgb[1] = htextcolor_rgb[2] = 0.0;
+ rec = U_EMRSETTEXTCOLOR_set(U_RGB(0, 0, 0));
+ if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintEmf::begin at U_EMRSETTEXTCOLOR_set");
+ }
+
+ rec = U_EMRSETROP2_set(U_R2_COPYPEN);
+ if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintEmf::begin at U_EMRSETROP2_set");
+ }
+
+ /* miterlimit is set with eah pen, so no need to check for it changes as in WMF */
+
+ return 0;
+}
+
+
+unsigned int PrintEmf::finish(Inkscape::Extension::Print * /*mod*/)
+{
+ 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)) {
+ g_error("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
+
+ return 0;
+}
+
+
+unsigned int PrintEmf::comment(
+ Inkscape::Extension::Print * /*module*/,
+ const char * /*comment*/)
+{
+ if (!et) {
+ return 0;
+ }
+
+ // earlier versions had flush of fill here, but it never executed and was removed
+
+ return 0;
+}
+
+
+// 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)
+{
+ float rgb[3];
+ char *rec;
+ U_LOGBRUSH lb;
+ uint32_t brush, fmode;
+ MFDrawMode fill_mode;
+ Inkscape::Pixbuf *pixbuf;
+ uint32_t brushStyle;
+ int hatchType;
+ U_COLORREF hatchColor;
+ U_COLORREF bkColor;
+ 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;
+ bkColor = U_RGB(0, 0, 0);
+ if (fcolor) {
+ hatchColor = *fcolor;
+ } else {
+ hatchColor = U_RGB(0, 0, 0);
+ }
+
+ if (!fcolor && style) {
+ if (style->fill.isColor()) {
+ fill_mode = DRAW_PAINT;
+#if 0
+// opacity not supported by EMF
+ float opacity = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
+ if (opacity <= 0.0) {
+ opacity = 0.0; // basically the same as no fill
+ }
+#endif
+ sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
+ hatchColor = U_RGB(255 * rgb[0], 255 * rgb[1], 255 * rgb[2]);
+
+ 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, &bkColor);
+ 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 || FixPPTLinGrad) {
+ 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:
+ // SVG text has no background attribute, so OPAQUE mode ALWAYS cancels after the next draw, otherwise it would mess up future text output.
+ if (usebk) {
+ rec = U_EMRSETBKCOLOR_set(bkColor);
+ if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintEmf::create_brush at U_EMRSETBKCOLOR_set");
+ }
+ rec = U_EMRSETBKMODE_set(U_OPAQUE);
+ if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintEmf::create_brush at U_EMRSETBKMODE_set");
+ }
+ }
+ rec = createbrushindirect_set(&brush, eht, lb);
+ if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintEmf::create_brush at createbrushindirect_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 *) pixbuf->pixels(); // 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)) {
+ g_error("Fatal programming error in PrintEmf::create_brush at createdibpatternbrushpt_set");
+ }
+ free(px);
+ free(Bmi); // ct will be NULL because of colortype
+ break;
+ }
+
+ hbrush = brush; // need this later for destroy_brush
+ rec = selectobject_set(brush, eht);
+ if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintEmf::create_brush at selectobject_set");
+ }
+
+ if (fmode != hpolyfillmode) {
+ hpolyfillmode = fmode;
+ rec = U_EMRSETPOLYFILLMODE_set(fmode);
+ if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintEmf::create_brush at U_EMRSETPOLYdrawmode_set");
+ }
+ }
+
+ return 0;
+}
+
+
+void PrintEmf::destroy_brush()
+{
+ 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)) {
+ g_error("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)) {
+ g_error("Fatal programming error in PrintEmf::destroy_brush");
+ }
+ hbrush = 0;
+ }
+}
+
+
+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 brushStyle;
+ Inkscape::Pixbuf *pixbuf;
+ int hatchType;
+ U_COLORREF hatchColor;
+ U_COLORREF bkColor;
+ 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;
+
+ if (!et) {
+ return 0;
+ }
+
+ // set a default stroke in case we can't figure out a better way to do it
+ brushStyle = U_BS_SOLID;
+ hatchColor = U_RGB(0, 0, 0);
+ hatchType = U_HS_HORIZONTAL;
+ bkColor = U_RGB(0, 0, 0);
+
+ 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, &bkColor);
+ if (pixbuf) {
+ brushStyle = U_BS_DIBPATTERN;
+ rgba_px = (char *) pixbuf->pixels(); // 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
+ brushStyle = U_BS_HATCHED;
+ if (usebk) { // OPAQUE mode ALWAYS cancels after the next draw, otherwise it would mess up future text output.
+ rec = U_EMRSETBKCOLOR_set(bkColor);
+ if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintEmf::create_pen at U_EMRSETBKCOLOR_set");
+ }
+ rec = U_EMRSETBKMODE_set(U_OPAQUE);
+ if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintEmf::create_pen at U_EMRSETBKMODE_set");
+ }
+ }
+ 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
+ brushStyle = 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);
+ brushStyle = 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) round(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_dasharray.values.empty()) {
+ if (FixPPTDashLine) { // will break up line into many smaller lines. Override gradient if that was set, cannot do both.
+ brushStyle = U_BS_SOLID;
+ hatchType = U_HS_HORIZONTAL;
+ } else {
+ unsigned i = 0;
+ while ((linestyle != U_PS_USERSTYLE) && (i < style->stroke_dasharray.values.size())) {
+ if (style->stroke_dasharray.values[i] > 0.00000001) {
+ linestyle = U_PS_USERSTYLE;
+ }
+ i++;
+ }
+
+ if (linestyle == U_PS_USERSTYLE) {
+ n_dash = style->stroke_dasharray.values.size();
+ dash = new uint32_t[n_dash];
+ for (i = 0; i < n_dash; i++) {
+ dash[i] = (uint32_t)(Inkscape::Util::Quantity::convert(1, "mm", "px") * style->stroke_dasharray.values[i]);
+ }
+ }
+ }
+ }
+
+ elp = extlogpen_set(
+ U_PS_GEOMETRIC | linestyle | linecap | linejoin,
+ linewidth,
+ brushStyle,
+ 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)) {
+ g_error("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)) {
+ g_error("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)) {
+ g_error("Fatal programming error in PrintEmf::create_pen at U_EMRSETMITERLIMIT_set");
+ }
+ }
+
+ if (n_dash) {
+ delete[] dash;
+ }
+ return 0;
+}
+
+// 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()
+{
+ 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)) {
+ g_error("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)) {
+ g_error("Fatal programming error in PrintEmf::destroy_pen");
+ }
+ hpen = 0;
+ }
+}
+
+/* Return a Path consisting of just the corner points of the single path in a a PathVector. If the
+PathVector has more than one path, or that one path is open, or any of its segments are curved, then the
+returned PathVector is . If the input path is already just straight lines and vertices the output will be the
+same as the sole path in the input. */
+
+Geom::Path PrintEmf::pathv_to_simple_polygon(Geom::PathVector const &pathv, int *vertices)
+{
+ Geom::Point P1_trail;
+ Geom::Point P1;
+ Geom::Point P1_lead;
+ Geom::Point v1,v2;
+ Geom::Path output;
+ Geom::Path bad;
+ Geom::PathVector pv = pathv_to_linear_and_cubic_beziers(pathv);
+ Geom::PathVector::const_iterator pit = pv.begin();
+ Geom::PathVector::const_iterator pit2 = pv.begin();
+ ++pit2;
+ *vertices = 0;
+ if(pit->end_closed() != pit->end_default())return(bad); // path must be closed
+ if(pit2 != pv.end())return(bad); // there may only be one path
+ P1_trail = pit->finalPoint();
+ Geom::Path::const_iterator cit = pit->begin();
+ P1 = cit->initialPoint();
+ for(;cit != pit->end_closed();++cit) {
+ if (!is_straight_curve(*cit)) {
+ *vertices = 0;
+ return(bad);
+ }
+ P1_lead = cit->finalPoint();
+ if(Geom::are_near(P1_lead, P1, 1e-5))continue; // duplicate points at the same coordinate
+ v1 = unit_vector(P1 - P1_trail);
+ v2 = unit_vector(P1_lead - P1 );
+ if(Geom::are_near(dot(v1,v2), 1.0, 1e-5)){ // P1 is within a straight line
+ P1 = P1_lead;
+ continue;
+ }
+ // P1 is the center point of a turn of some angle
+ if(!*vertices){
+ output.start( P1 );
+ output.close( pit->closed() );
+ }
+ *vertices += 1;
+ Geom::LineSegment ls(P1_trail, P1);
+ output.append(ls);
+ P1_trail = P1;
+ P1 = P1_lead;
+ }
+ return(output);
+}
+
+/* Returns the simplified PathVector (no matter what).
+ Sets is_rect if it is a rectangle.
+ Sets angle that will rotate side closest to horizontal onto horizontal.
+*/
+Geom::Path PrintEmf::pathv_to_rect(Geom::PathVector const &pathv, bool *is_rect, double *angle)
+{
+ Geom::Point P1_trail;
+ Geom::Point P1;
+ Geom::Point P1_lead;
+ Geom::Point v1,v2;
+ int vertices;
+ Geom::Path pR = pathv_to_simple_polygon(pathv, &vertices);
+ *is_rect = false;
+ if(vertices==4){ // or else it cannot be a rectangle
+ int vertex_count=0;
+ /* Get the ends of the LAST line segment.
+ Find minimum rotation to align rectangle with X,Y axes. (Very degenerate if it is rotated 45 degrees.) */
+ *angle = 10.0; /* must be > than the actual angle in radians. */
+ for(Geom::Path::const_iterator cit = pR.begin(); cit != pR.end_open(); ++cit){
+ P1_trail = cit->initialPoint();
+ P1 = cit->finalPoint();
+ v1 = unit_vector(P1 - P1_trail);
+ if(v1[Geom::X] > 0){ // only check the 1 or 2 points on vectors aimed the same direction as unit X
+ double ang = asin(v1[Geom::Y]); // because component is rotation by ang of {1,0| vector
+ if(fabs(ang) < fabs(*angle))*angle = -ang; // y increases down, flips sign on angle
+ }
+ }
+
+ /* For increased numerical stability, snap the angle to the nearest 1/100th of a degree. */
+ double convert = 36000.0/ (2.0 * M_PI);
+ *angle = round(*angle * convert)/convert;
+
+ for(Geom::Path::const_iterator cit = pR.begin(); cit != pR.end_open();++cit) {
+ P1_lead = cit->finalPoint();
+ v1 = unit_vector(P1 - P1_trail);
+ v2 = unit_vector(P1_lead - P1 );
+ // P1 is center of a turn that is not 90 degrees. Limit comes from cos(89.9) = .001745
+ if(!Geom::are_near(dot(v1,v2), 0.0, 2e-3))break;
+ P1_trail = P1;
+ P1 = P1_lead;
+ vertex_count++;
+ }
+ if(vertex_count == 4){
+ *is_rect=true;
+ }
+ }
+ return(pR);
+}
+
+/* Compare a vector with a rectangle's orientation (angle needed to rotate side(s)
+ closest to horizontal to exactly horizontal) and return:
+ 0 none of the following
+ 1 parallel to horizontal
+ 2 parallel to vertical
+ 3 antiparallel to horizontal
+ 4 antiparallel to vertical
+*/
+int PrintEmf::vector_rect_alignment(double angle, Geom::Point vtest){
+ int stat = 0;
+ Geom::Point v1 = Geom::unit_vector(vtest); // unit vector to test alignment
+ Geom::Point v2 = Geom::Point(1,0) * Geom::Rotate(-angle); // unit horizontal side (sign change because Y increases DOWN)
+ if( Geom::are_near(dot(v1,v2), 1.0, 1e-5)){ stat = 1; }
+ else if(Geom::are_near(dot(v1,v2),-1.0, 1e-5)){ stat = 2; }
+ if(!stat){
+ v2 = Geom::Point(0,1) * Geom::Rotate(-angle); // unit vertical side
+ if( Geom::are_near(dot(v1,v2), 1.0, 1e-5)){ stat = 3; }
+ else if(Geom::are_near(dot(v1,v2),-1.0, 1e-5)){ stat = 4; }
+ }
+ return(stat);
+}
+
+/* retrieve the point at the indicated corner:
+ 0 UL (and default)
+ 1 UR
+ 2 LR
+ 3 LL
+ Needed because the start can be any point, and the direction could run either
+ clockwise or counterclockwise. This should work even if the corners of the rectangle
+ are slightly displaced.
+*/
+Geom::Point PrintEmf::get_pathrect_corner(Geom::Path pathRect, double angle, int corner){
+ Geom::Point center(0,0);
+ for(Geom::Path::const_iterator cit = pathRect.begin(); cit != pathRect.end_open(); ++cit) {
+ center += cit->initialPoint()/4.0;
+ }
+
+ int LR; // 1 if Left, 0 if Right
+ int UL; // 1 if Lower, 0 if Upper (as viewed on screen, y coordinates increase downwards)
+ switch(corner){
+ case 1: //UR
+ LR = 0;
+ UL = 0;
+ break;
+ case 2: //LR
+ LR = 0;
+ UL = 1;
+ break;
+ case 3: //LL
+ LR = 1;
+ UL = 1;
+ break;
+ default: //UL
+ LR = 1;
+ UL = 0;
+ break;
+ }
+
+ Geom::Point v1 = Geom::Point(1,0) * Geom::Rotate(-angle); // unit horizontal side (sign change because Y increases DOWN)
+ Geom::Point v2 = Geom::Point(0,1) * Geom::Rotate(-angle); // unit vertical side (sign change because Y increases DOWN)
+ Geom::Point P1;
+ for(Geom::Path::const_iterator cit = pathRect.begin(); cit != pathRect.end_open(); ++cit) {
+ P1 = cit->initialPoint();
+
+ if ( ( LR == (dot(P1 - center,v1) > 0 ? 0 : 1) )
+ && ( UL == (dot(P1 - center,v2) > 0 ? 1 : 0) ) ) break;
+ }
+ return(P1);
+}
+
+U_TRIVERTEX PrintEmf::make_trivertex(Geom::Point Pt, U_COLORREF uc){
+ U_TRIVERTEX tv;
+ using Geom::X;
+ using Geom::Y;
+ tv.x = (int32_t) round(Pt[X]);
+ tv.y = (int32_t) round(Pt[Y]);
+ tv.Red = uc.Red << 8;
+ tv.Green = uc.Green << 8;
+ tv.Blue = uc.Blue << 8;
+ tv.Alpha = uc.Reserved << 8; // EMF will ignore this
+ return(tv);
+}
+
+unsigned int PrintEmf::fill(
+ Inkscape::Extension::Print * /*mod*/,
+ Geom::PathVector const &pathv, Geom::Affine const & /*transform*/, SPStyle const *style,
+ Geom::OptRect const &/*pbox*/, Geom::OptRect const &/*dbox*/, Geom::OptRect const &/*bbox*/)
+{
+ using Geom::X;
+ using Geom::Y;
+
+ Geom::Affine tf = m_tr_stack.top();
+
+ use_fill = true;
+ use_stroke = false;
+
+ fill_transform = tf;
+
+ int brush_stat = create_brush(style, NULL);
+
+ /* native linear gradients are only used if the object is a rectangle AND the gradient is parallel to the sides of the object */
+ bool is_Rect = false;
+ double angle;
+ int rectDir=0;
+ Geom::Path pathRect;
+ if(FixPPTLinGrad && brush_stat && gv.mode == DRAW_LINEAR_GRADIENT){
+ Geom::PathVector pvr = pathv * fill_transform;
+ pathRect = pathv_to_rect(pvr, &is_Rect, &angle);
+ if(is_Rect){
+ /* Gradientfill records can only be used if the gradient is parallel to the sides of the rectangle.
+ That must be checked here so that we can fall back to another form of gradient fill if it is not
+ the case. */
+ rectDir = vector_rect_alignment(angle, (gv.p2 - gv.p1) * fill_transform);
+ if(!rectDir)is_Rect = false;
+ }
+ if(!is_Rect && !FixPPTGrad2Polys)brush_stat=0; // fall all the way back to a solid fill
+ }
+
+ if (brush_stat) { // only happens if the style is a gradient
+ /*
+ Handle gradients. Uses modified livarot as 2geom boolops is currently broken.
+ Can handle gradients with multiple stops.
+
+ 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; // first stop
+ c1 = U_RGBA(255 * rgb[0], 255 * rgb[1], 255 * rgb[2], 255 * opa);
+ sp_color_get_rgb_floatv(&tg->vector.stops[nstops - 1].color, rgb);
+ opa = tg->vector.stops[nstops - 1].opacity; // last stop
+ 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) {
+ istop++;
+ if (istop >= nstops) {
+ istop = nstops - 1;
+ continue; // could happen on a rounding error
+ }
+ doff_base = doff_range;
+ doff_range = tg->vector.stops[istop].offset; // next or last stop
+ c1 = c2;
+ sp_color_get_rgb_floatv(&tg->vector.stops[istop].color, rgb);
+ opa = tg->vector.stops[istop].opacity;
+ c2 = U_RGBA(255 * rgb[0], 255 * rgb[1], 255 * rgb[2], 255 * opa);
+ }
+ }
+ } else if (gv.mode == DRAW_LINEAR_GRADIENT) {
+ if(is_Rect){
+ char *rec;
+ int gMode;
+ Geom::Point ul, ur, lr;
+ Geom::Point outUL, outLR; // UL,LR corners of a stop rectangle, in OUTPUT coordinates
+ U_TRIVERTEX ut[2];
+ U_GRADIENT4 ug4;
+ U_RECTL rcb;
+ U_XFORM tmpTransform;
+ double wRect, hRect;
+
+ /* coordinates: upper left, upper right, and lower right corners of the rectangle.
+ inkscape transform already applied, but needs to be scaled to EMF coordinates. */
+ ul = get_pathrect_corner(pathRect, angle, 0) * PX2WORLD;
+ ur = get_pathrect_corner(pathRect, angle, 1) * PX2WORLD;
+ lr = get_pathrect_corner(pathRect, angle, 2) * PX2WORLD;
+ wRect = Geom::distance(ul,ur);
+ hRect = Geom::distance(ur,lr);
+
+ /* The basic rectangle for all of these is placed with its UL corner at 0,0 with a size wRect,hRect.
+ Apply a world transform to place/scale it into the appropriate position on the drawing.
+ Actual gradientfill records are either this entire rectangle or slices of it as defined by the stops.
+ This rectangle has already been transformed by tf (whatever rotation/scale) Inkscape had applied to it.
+ */
+
+ Geom::Affine tf2 = Geom::Rotate(-angle); // the rectangle may be drawn skewed to the coordinate system
+ tmpTransform.eM11 = tf2[0];
+ tmpTransform.eM12 = tf2[1];
+ tmpTransform.eM21 = tf2[2];
+ tmpTransform.eM22 = tf2[3];
+ tmpTransform.eDx = round((ul)[Geom::X]); // use explicit round for better stability
+ tmpTransform.eDy = round((ul)[Geom::Y]);
+
+ rec = U_EMRSAVEDC_set();
+ if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintEmf::image at U_EMRSAVEDC_set");
+ }
+
+ rec = U_EMRMODIFYWORLDTRANSFORM_set(tmpTransform, U_MWT_LEFTMULTIPLY);
+ if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintEmf::image at EMRMODIFYWORLDTRANSFORM");
+ }
+
+ for(;istop<nstops;istop++){
+ doff_range = tg->vector.stops[istop].offset; // next or last stop
+ if(rectDir == 1 || rectDir == 2){
+ outUL = Geom::Point(doff_base *wRect, 0 );
+ outLR = Geom::Point(doff_range*wRect, hRect);
+ gMode = U_GRADIENT_FILL_RECT_H;
+ }
+ else {
+ outUL = Geom::Point(0, doff_base *hRect);
+ outLR = Geom::Point(wRect,doff_range*hRect);
+ gMode = U_GRADIENT_FILL_RECT_V;
+ }
+ doff_base = doff_range;
+ rcb.left = round(outUL[X]); // use explicit round for better stability
+ rcb.top = round(outUL[Y]);
+ rcb.right = round(outLR[X]);
+ rcb.bottom = round(outLR[Y]);
+ sp_color_get_rgb_floatv(&tg->vector.stops[istop].color, rgb);
+ opa = tg->vector.stops[istop].opacity;
+ c2 = U_RGBA(255 * rgb[0], 255 * rgb[1], 255 * rgb[2], 255 * opa);
+
+ if(rectDir == 2 || rectDir == 4){ // gradient is reversed, so swap colors
+ ut[0] = make_trivertex(outUL, c2);
+ ut[1] = make_trivertex(outLR, c1);
+ }
+ else {
+ ut[0] = make_trivertex(outUL, c1);
+ ut[1] = make_trivertex(outLR, c2);
+ }
+ c1 = c2; // for next stop
+ ug4.UpperLeft = 0;
+ ug4.LowerRight= 1;
+ rec = U_EMRGRADIENTFILL_set(rcb, 2, 1, gMode, ut, (uint32_t *) &ug4 );
+ if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintEmf::fill at U_EMRGRADIENTFILL_set");
+ }
+ }
+
+ rec = U_EMRRESTOREDC_set(-1);
+ if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintEmf::fill at U_EMRRESTOREDC_set");
+ }
+ }
+ else {
+ Geom::Point uv = Geom::unit_vector(gv.p2 - gv.p1); // unit vector
+ Geom::Point puv = uv.cw(); // perp. to unit vector
+ double range = Geom::distance(gv.p1, gv.p2); // length along the gradient
+ double step = range / divisions; // adequate approximation for gradient
+ double overlap = step / 4.0; // overlap slices slightly
+ double start;
+ double stop;
+ Geom::PathVector pathvc, pathvr;
+
+ /* before lower end of gradient, overlap first slice position */
+ wc = weight_opacity(c1);
+ (void) create_brush(style, &wc);
+ pathvc = rect_cutter(gv.p1, uv * (overlap), uv * (-50000.0), puv * 50000.0);
+ pathvr = sp_pathvector_boolop(pathvc, pathv, bool_op_inters, (FillRule) fill_nonZero, frb);
+ print_pathv(pathvr, fill_transform);
+
+ /* after high end of gradient, overlap last slice position */
+ 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) {
+ istop++;
+ if (istop >= nstops) {
+ istop = nstops - 1;
+ continue; // could happen on a rounding error
+ }
+ doff_base = doff_range;
+ doff_range = tg->vector.stops[istop].offset; // next or last stop
+ c1 = c2;
+ sp_color_get_rgb_floatv(&tg->vector.stops[istop].color, rgb);
+ opa = tg->vector.stops[istop].opacity;
+ c2 = U_RGBA(255 * rgb[0], 255 * rgb[1], 255 * rgb[2], 255 * opa);
+ }
+ }
+ }
+ } else {
+ g_error("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.isNone() || style->stroke.noneSet || style->stroke_width.computed == 0.0) ||
+ (!style->stroke_dasharray.values.empty() && FixPPTDashLine) ||
+ !all_closed
+ ) {
+ print_pathv(pathv, fill_transform); // do any fills. side effect: clears fill_pathv
+ use_fill = false;
+ }
+ }
+
+ 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*/)
+{
+
+ char *rec = NULL;
+ 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_dasharray.values.empty() && FixPPTDashLine) {
+ // convert the path, gets its complete length, and then make a new path with parameter length instead of t
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > tmp_pathpw; // pathv-> sbasis
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > tmp_pathpw2; // sbasis using arc length parameter
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > tmp_pathpw3; // new (discontinuous) path, composed of dots/dashes
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > first_frag; // first fragment, will be appended at end
+ int n_dash = style->stroke_dasharray.values.size();
+ 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_dasharray.values[i++];
+ if (elength > tlength) {
+ elength = tlength;
+ }
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > fragment(portion(tmp_pathpw2, slength, elength));
+ if (slength) {
+ tmp_pathpw3.concat(fragment);
+ } else {
+ first_frag = fragment;
+ }
+ slength = elength;
+ slength += style->stroke_dasharray.values[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;
+
+ if (usebk) { // OPAQUE was set, revert to TRANSPARENT
+ usebk = false;
+ rec = U_EMRSETBKMODE_set(U_TRANSPARENT);
+ if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintEmf::stroke at U_EMRSETBKMODE_set");
+ }
+ }
+
+ 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)
+{
+
+ Geom::PathVector pv = pathv_to_linear_and_cubic_beziers(pathv * transform);
+
+ int nodes = 0;
+ int moves = 0;
+ int lines = 0;
+ int curves = 0;
+ char *rec = NULL;
+
+ 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 (&*cit) {
+ curves++;
+ }
+ }
+ }
+
+ if (!nodes) {
+ return false;
+ }
+
+ U_POINT *lpPoints = new U_POINT[moves + lines + curves * 3];
+ int i = 0;
+
+ /**
+ * For all Subpaths in the <path>
+ */
+ 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<Geom::CubicBezier const *>(&*cit)) {
+ std::vector<Geom::Point> 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)) {
+ g_error("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)) {
+ g_error("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)) {
+ g_error("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)) {
+ g_error("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)) {
+ g_error("Fatal programming error in PrintEmf::print_simple_shape at selectobject_set brush");
+ }
+ }
+ }
+
+ delete[] lpPoints;
+
+ return done;
+}
+
+/** Some parts based on win32.cpp by Lauris Kaplinski <lauris@kaplinski.com>. 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. Transparency is lost on export. (Apparently a limitation of the EMF format.)
+ 2. Probably messes up if row stride != w*4
+ 3. 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_rect, /** affine transform only used for defining location and size of rect, for all other tranforms, use the one from m_tr_stack */
+ SPStyle const * /*style*/) /** provides indirect link to image object */
+{
+ double x1, y1, dw, dh;
+ char *rec = NULL;
+ Geom::Affine tf = m_tr_stack.top();
+
+ rec = U_EMRSETSTRETCHBLTMODE_set(U_COLORONCOLOR);
+ if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintEmf::image at EMRHEADER");
+ }
+
+ x1 = tf_rect[4];
+ y1 = tf_rect[5];
+ dw = ((double) w) * tf_rect[0];
+ dh = ((double) h) * tf_rect[3];
+ Geom::Point pLL(x1, y1);
+ Geom::Point pLL2 = pLL * tf; //location of LL corner in Inkscape coordinates
+
+ 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(pLL2[Geom::X] * PX2WORLD), round(pLL2[Geom::Y] * PX2WORLD));
+ U_POINTL cDest = pointl_set(round(dw * PX2WORLD), round(dh * PX2WORLD));
+ U_POINTL Src = pointl_set(0, 0);
+ U_POINTL cSrc = pointl_set(w, h);
+ /* map the integer Dest coordinates back into pLL2, so that the rounded part does not destabilize the transform offset below */
+ pLL2[Geom::X] = Dest.x;
+ pLL2[Geom::Y] = Dest.y;
+ pLL2 /= PX2WORLD;
+ if (!FixImageRot) { /* Rotate images - some programs cannot read them in correctly if they are rotated */
+ tf[4] = tf[5] = 0.0; // get rid of the offset in the transform
+ Geom::Point pLL2prime = pLL2 * tf;
+ U_XFORM tmpTransform;
+ tmpTransform.eM11 = tf[0];
+ tmpTransform.eM12 = tf[1];
+ tmpTransform.eM21 = tf[2];
+ tmpTransform.eM22 = tf[3];
+ tmpTransform.eDx = (pLL2[Geom::X] - pLL2prime[Geom::X]) * PX2WORLD;
+ tmpTransform.eDy = (pLL2[Geom::Y] - pLL2prime[Geom::Y]) * PX2WORLD;
+
+ rec = U_EMRSAVEDC_set();
+ if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintEmf::image at U_EMRSAVEDC_set");
+ }
+
+ rec = U_EMRMODIFYWORLDTRANSFORM_set(tmpTransform, U_MWT_LEFTMULTIPLY);
+ if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintEmf::image at EMRMODIFYWORLDTRANSFORM");
+ }
+ }
+ 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)) {
+ g_error("Fatal programming error in PrintEmf::image at U_EMRSTRETCHDIBITS_set");
+ }
+ free(px);
+ free(Bmi);
+ if (numCt) {
+ free(ct);
+ }
+
+ if (!FixImageRot) {
+ rec = U_EMRRESTOREDC_set(-1);
+ if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintEmf::image at U_EMRRESTOREDC_set");
+ }
+ }
+
+ return 0;
+}
+
+// may also be called with a simple_shape or an empty path, whereupon it just returns without doing anything
+unsigned int PrintEmf::print_pathv(Geom::PathVector const &pathv, const Geom::Affine &transform)
+{
+ Geom::Affine tf = transform;
+ char *rec = NULL;
+
+ simple_shape = print_simple_shape(pathv, tf);
+ if (simple_shape || pathv.empty()) {
+ if (use_fill) {
+ destroy_brush(); // these must be cleared even if nothing is drawn or hbrush,hpen fill up
+ }
+ if (use_stroke) {
+ destroy_pen();
+ }
+ return TRUE;
+ }
+
+ /* inkscape to EMF scaling is done below, but NOT the rotation/translation transform,
+ that is handled by the EMF MODIFYWORLDTRANSFORM record
+ */
+
+ Geom::PathVector pv = pathv_to_linear_and_cubic_beziers(pathv * tf);
+
+ rec = U_EMRBEGINPATH_set();
+ if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintEmf::print_pathv at U_EMRBEGINPATH_set");
+ }
+
+ /**
+ * For all Subpaths in the <path>
+ */
+ 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)) {
+ g_error("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)) {
+ g_error("Fatal programming error in PrintEmf::print_pathv at U_EMRLINETO_set");
+ }
+ } else if (Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const *>(&*cit)) {
+ std::vector<Geom::Point> 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)) {
+ g_error("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)) {
+ g_error("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)) {
+ g_error("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)) {
+ g_error("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)) {
+ g_error("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)) {
+ g_error("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();
+ }
+
+ return TRUE;
+}
+
+
+unsigned int PrintEmf::text(Inkscape::Extension::Print * /*mod*/, char const *text, Geom::Point const &p,
+ SPStyle const *const style)
+{
+ 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 ky;
+
+ // the dx array is smuggled in like: text<nul>w1 w2 w3 ...wn<nul><nul>, where the widths are floats 7 characters wide, including the space
+ int ndx, rtl;
+ uint32_t *adx;
+ smuggle_adxkyrtl_out(text, &adx, &ky, &rtl, &ndx, PX2WORLD * std::min(tf.expansionX(), tf.expansionY())); // side effect: free() adx
+
+ uint32_t textalignment;
+ if (rtl > 0) {
+ textalignment = U_TA_BASELINE | U_TA_LEFT;
+ } else {
+ textalignment = U_TA_BASELINE | U_TA_RIGHT | U_TA_RTLREADING;
+ }
+ if (textalignment != htextalignment) {
+ htextalignment = textalignment;
+ rec = U_EMRSETTEXTALIGN_set(textalignment);
+ if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintEmf::text at U_EMRSETTEXTALIGN_set");
+ }
+ }
+
+ 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.
+ FontfixParams params;
+
+ if (FixPPTCharPos) {
+ switch (newfont) {
+ case CVTSYM:
+ _lookup_ppt_fontfix("Convert To Symbol", params);
+ break;
+ case CVTZDG:
+ _lookup_ppt_fontfix("Convert To Zapf Dingbats", params);
+ break;
+ case CVTWDG:
+ _lookup_ppt_fontfix("Convert To Wingdings", params);
+ break;
+ default: //also CVTNON
+ _lookup_ppt_fontfix(style->text->font_family.value, params);
+ break;
+ }
+ if (params.f2 != 0 || params.f3 != 0) {
+ 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,
+ round(rot),
+ round(rot),
+ _translate_weight(style->font_weight.computed),
+ (style->font_style.computed == SP_CSS_FONT_STYLE_ITALIC),
+ style->text_decoration_line.underline,
+ style->text_decoration_line.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)) {
+ g_error("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)) {
+ g_error("Fatal programming error in PrintEmf::text at selectobject_set");
+ }
+
+ float rgb[3];
+ sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
+ // only change the text color when it needs to be changed
+ if (memcmp(htextcolor_rgb, rgb, 3 * sizeof(float))) {
+ memcpy(htextcolor_rgb, rgb, 3 * sizeof(float));
+ 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)) {
+ g_error("Fatal programming error in PrintEmf::text at U_EMRSETTEXTCOLOR_set");
+ }
+ }
+
+ Geom::Point p2 = p * tf;
+
+ //Handle super/subscripts and vertical kerning
+ /* Previously used this, but vertical kerning was not supported
+ p2[Geom::X] -= style->baseline_shift.computed * std::sin( rotb );
+ p2[Geom::Y] -= style->baseline_shift.computed * std::cos( rotb );
+ */
+ p2[Geom::X] += ky * std::sin(rotb);
+ p2[Geom::Y] += ky * 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 = params.f3 * style->font_size.computed * std::cos(rotb);
+ } else if (fix90n == 2) { //horizontal
+ dx = params.f2 * style->font_size.computed * std::sin(rotb);
+ dy = 0.0;
+ } else {
+ dx = params.f1 * style->font_size.computed * std::sin(rotb);
+ dy = params.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;
+ if (rtl > 0) {
+ rec2 = emrtext_set((U_POINTL) {
+ xpos, ypos
+ }, ndx, 2, unicode_text, U_ETO_NONE, U_RCL_DEF, adx);
+ } else { // RTL text, U_TA_RTLREADING should be enough, but set this one too just in case
+ rec2 = emrtext_set((U_POINTL) {
+ xpos, ypos
+ }, ndx, 2, unicode_text, U_ETO_RTLREADING, 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)) {
+ g_error("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)) {
+ g_error("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)) {
+ g_error("Fatal programming error in PrintEmf::text at deleteobject_set");
+ }
+ }
+
+ return 0;
+}
+
+void PrintEmf::init(void)
+{
+ _load_ppt_fontfix_data();
+
+ /* EMF print */
+ Inkscape::Extension::build_from_mem(
+ "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
+ "<name>Enhanced Metafile Print</name>\n"
+ "<id>org.inkscape.print.emf</id>\n"
+ "<param name=\"destination\" type=\"string\"></param>\n"
+ "<param name=\"textToPath\" type=\"boolean\">true</param>\n"
+ "<param name=\"pageBoundingBox\" type=\"boolean\">true</param>\n"
+ "<param name=\"FixPPTCharPos\" type=\"boolean\">false</param>\n"
+ "<param name=\"FixPPTDashLine\" type=\"boolean\">false</param>\n"
+ "<param name=\"FixPPTGrad2Polys\" type=\"boolean\">false</param>\n"
+ "<param name=\"FixPPTLinGrad\" type=\"boolean\">false</param>\n"
+ "<param name=\"FixPPTPatternAsHatch\" type=\"boolean\">false</param>\n"
+ "<param name=\"FixImageRot\" type=\"boolean\">false</param>\n"
+ "<print/>\n"
+ "</inkscape-extension>", 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 --git a/src/extension/internal/emf-print.h b/src/extension/internal/emf-print.h
new file mode 100644
index 000000000..1e4970a46
--- /dev/null
+++ b/src/extension/internal/emf-print.h
@@ -0,0 +1,99 @@
+/** @file
+ * @brief Enhanced Metafile printing - implementation
+ */
+/* Authors:
+ * Ulf Erikson <ulferikson@users.sf.net>
+ * David Mathog
+ *
+ * Copyright (C) 2006-2008 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+#ifndef SEEN_INKSCAPE_EXTENSION_INTERNAL_EMF_PRINT_H
+#define SEEN_INKSCAPE_EXTENSION_INTERNAL_EMF_PRINT_H
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <libuemf/uemf.h>
+#include "extension/internal/metafile-print.h"
+
+#include "sp-gradient.h"
+#include "splivarot.h" // pieces for union on shapes
+#include "display/canvas-bpath.h" // for SPWindRule
+
+namespace Inkscape {
+namespace Extension {
+namespace Internal {
+
+class PrintEmf : public PrintMetafile
+{
+ uint32_t hbrush, hbrushOld, hpen, hpenOld;
+
+ 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();
+
+ /* 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 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);
+
+ static void init (void);
+protected:
+ static void smuggle_adxkyrtl_out(const char *string, uint32_t **adx, double *ky, int *rtl, int *ndx, float scale);
+
+ Geom::Path pathv_to_simple_polygon(Geom::PathVector const &pathv, int *vertices);
+ Geom::Path pathv_to_rect(Geom::PathVector const &pathv, bool *is_rect, double *angle);
+ Geom::Point get_pathrect_corner(Geom::Path pathRect, double angle, int corner);
+ U_TRIVERTEX make_trivertex(Geom::Point Pt, U_COLORREF uc);
+ int vector_rect_alignment(double angle, Geom::Point vtest);
+ 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 --git a/src/extension/internal/emf-win32-inout.cpp b/src/extension/internal/emf-win32-inout.cpp
deleted file mode 100644
index f54bfc6ea..000000000
--- a/src/extension/internal/emf-win32-inout.cpp
+++ /dev/null
@@ -1,2524 +0,0 @@
-/** @file
- * @brief Windows-only Enhanced Metafile input and output.
- */
-/* Authors:
- * Ulf Erikson <ulferikson@users.sf.net>
- * Jon A. Cruz <jon@joncruz.org>
- * 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 <windows.h>
-
-#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; i<d->dc[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; i<pEmr->elp.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->outsvg) += " <path \t";
- output_style(d, EMR_STROKEPATH);
- if (strstr(d->path->c_str(), "d=\"") == NULL) {
- *(d->outsvg) += "d=\"";
- *(d->outsvg) += "\n\t";
- }
- *(d->outsvg) += *(d->path);
- *(d->outsvg) += " \" /> \n";
- *(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_BEGINPATH)
- {
- *(d->outsvg) += " <path ";
- output_style(d, EMR_STROKEPATH);
- *(d->outsvg) += "\n\t";
- *(d->outsvg) += *(d->path);
- *(d->outsvg) += " \" /> \n";
- *(d->path) = "";
- d->pathless_stroke = false;
- }
- }
-
- switch (lpEMFR->iType)
- {
- case EMR_HEADER:
- {
- dbg_str << "<!-- EMR_HEADER -->\n";
-
- *(d->outdef) += "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n";
-
- if (d->pDesc) {
- *(d->outdef) += "<!-- ";
- *(d->outdef) += d->pDesc;
- *(d->outdef) += " -->\n";
- }
-
- ENHMETAHEADER *pEmr = (ENHMETAHEADER *) lpEMFR;
- SVGOStringStream tmp_outdef;
- tmp_outdef << "<svg\n";
- tmp_outdef << " xmlns:svg=\"http://www.w3.org/2000/svg\"\n";
- tmp_outdef << " xmlns=\"http://www.w3.org/2000/svg\"\n";
- tmp_outdef << " version=\"1.0\"\n";
-
- d->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) += "<defs>"; // temporary end of header
-
- tmp_outsvg << "\n</defs>\n<g>\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 << "<!-- EMR_POLYBEZIER -->\n";
-
- PEMRPOLYBEZIER pEmr = (PEMRPOLYBEZIER) lpEMFR;
- DWORD i,j;
-
- if (pEmr->cptl<4)
- break;
-
- if (!d->inpath) {
- assert_empty_path(d, "EMR_POLYBEZIER");
-
- *(d->outsvg) += " <path ";
- output_style(d, EMR_STROKEPATH);
- *(d->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; i<pEmr->cptl; ) {
- tmp_str << "\n\tC ";
- for (j=0; j<3 && i<pEmr->cptl; 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 << "<!-- EMR_POLYGON -->\n";
-
- EMRPOLYGON *pEmr = (EMRPOLYGON *) lpEMFR;
- DWORD i;
-
- if (pEmr->cptl < 2)
- break;
-
- assert_empty_path(d, "EMR_POLYGON");
-
- *(d->outsvg) += " <path ";
- output_style(d, EMR_STROKEANDFILLPATH);
- *(d->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; i<pEmr->cptl; 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 << "<!-- EMR_POLYLINE -->\n";
-
- EMRPOLYLINE *pEmr = (EMRPOLYLINE *) lpEMFR;
- DWORD i;
-
- if (pEmr->cptl<2)
- break;
-
- if (!d->inpath) {
- assert_empty_path(d, "EMR_POLYLINE");
-
- *(d->outsvg) += " <path ";
- output_style(d, EMR_STROKEPATH);
- *(d->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; i<pEmr->cptl; 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 << "<!-- EMR_POLYBEZIERTO -->\n";
-
- PEMRPOLYBEZIERTO pEmr = (PEMRPOLYBEZIERTO) lpEMFR;
- DWORD i,j;
-
- if (d->path->empty()) {
- d->pathless_stroke = true;
- *(d->path) = "d=\"";
- }
-
- for (i=0; i<pEmr->cptl;) {
- tmp_path << "\n\tC ";
- for (j=0; j<3 && i<pEmr->cptl; 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 << "<!-- EMR_POLYLINETO -->\n";
-
- PEMRPOLYLINETO pEmr = (PEMRPOLYLINETO) lpEMFR;
- DWORD i;
-
- if (d->path->empty()) {
- d->pathless_stroke = true;
- *(d->path) = "d=\"";
- }
-
- for (i=0; i<pEmr->cptl;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 << "<!-- EMR_POLYPOLYLINE -->\n";
- if (lpEMFR->iType == EMR_POLYPOLYGON)
- dbg_str << "<!-- EMR_POLYPOLYGON -->\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) += " <path ";
- output_style(d, lpEMFR->iType==EMR_POLYPOLYGON ? EMR_STROKEANDFILLPATH : EMR_STROKEPATH);
- *(d->outsvg) += "\n\td=\"";
- }
-
- POINTL *aptl = (POINTL *) &pEmr->aPolyCounts[pEmr->nPolys];
-
- i = 0;
- for (n=0; n<pEmr->nPolys && i<pEmr->cptl; 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; j<pEmr->aPolyCounts[n] && i<pEmr->cptl; 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 << "<!-- EMR_SETWINDOWEXTEX -->\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 << "<!-- EMR_SETWINDOWORGEX -->\n";
-
- PEMRSETWINDOWORGEX pEmr = (PEMRSETWINDOWORGEX) lpEMFR;
- d->dc[d->level].winorg = pEmr->ptlOrigin;
- break;
- }
- case EMR_SETVIEWPORTEXTEX:
- {
- dbg_str << "<!-- EMR_SETVIEWPORTEXTEX -->\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 << "<!-- EMR_SETVIEWPORTORGEX -->\n";
-
- PEMRSETVIEWPORTORGEX pEmr = (PEMRSETVIEWPORTORGEX) lpEMFR;
- d->dc[d->level].vieworg = pEmr->ptlOrigin;
- break;
- }
- case EMR_SETBRUSHORGEX:
- dbg_str << "<!-- EMR_SETBRUSHORGEX -->\n";
- break;
- case EMR_EOF:
- {
- dbg_str << "<!-- EMR_EOF -->\n";
-
- assert_empty_path(d, "EMR_EOF");
- tmp_outsvg << "</g>\n";
- tmp_outsvg << "</svg>\n";
- *(d->outsvg) = *(d->outdef) + *(d->outsvg);
- break;
- }
- case EMR_SETPIXELV:
- dbg_str << "<!-- EMR_SETPIXELV -->\n";
- break;
- case EMR_SETMAPPERFLAGS:
- dbg_str << "<!-- EMR_SETMAPPERFLAGS -->\n";
- break;
- case EMR_SETMAPMODE:
- dbg_str << "<!-- EMR_SETMAPMODE -->\n";
- break;
- case EMR_SETBKMODE:
- dbg_str << "<!-- EMR_SETBKMODE -->\n";
- break;
- case EMR_SETPOLYFILLMODE:
- {
- dbg_str << "<!-- EMR_SETPOLYFILLMODE -->\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 << "<!-- EMR_SETROP2 -->\n";
- break;
- case EMR_SETSTRETCHBLTMODE:
- dbg_str << "<!-- EMR_SETSTRETCHBLTMODE -->\n";
- break;
- case EMR_SETTEXTALIGN:
- {
- dbg_str << "<!-- EMR_SETTEXTALIGN -->\n";
-
- PEMRSETTEXTALIGN pEmr = (PEMRSETTEXTALIGN) lpEMFR;
- d->dc[d->level].textAlign = pEmr->iMode;
- break;
- }
- case EMR_SETCOLORADJUSTMENT:
- dbg_str << "<!-- EMR_SETCOLORADJUSTMENT -->\n";
- break;
- case EMR_SETTEXTCOLOR:
- {
- dbg_str << "<!-- EMR_SETTEXTCOLOR -->\n";
-
- PEMRSETTEXTCOLOR pEmr = (PEMRSETTEXTCOLOR) lpEMFR;
- d->dc[d->level].textColor = pEmr->crColor;
- d->dc[d->level].textColorSet = true;
- break;
- }
- case EMR_SETBKCOLOR:
- dbg_str << "<!-- EMR_SETBKCOLOR -->\n";
- break;
- case EMR_OFFSETCLIPRGN:
- dbg_str << "<!-- EMR_OFFSETCLIPRGN -->\n";
- break;
- case EMR_MOVETOEX:
- {
- dbg_str << "<!-- EMR_MOVETOEX -->\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 << "<!-- EMR_SETMETARGN -->\n";
- break;
- case EMR_EXCLUDECLIPRECT:
- dbg_str << "<!-- EMR_EXCLUDECLIPRECT -->\n";
- break;
- case EMR_INTERSECTCLIPRECT:
- {
- dbg_str << "<!-- EMR_INTERSECTCLIPRECT -->\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 << "\n<clipPath\n\tclipPathUnits=\"userSpaceOnUse\" ";
- tmp_rectangle << "\n\tid=\"clipEmfPath" << ++(d->id) << "\" >";
- tmp_rectangle << "\n<rect ";
- tmp_rectangle << "\n\tx=\"" << l << "\" ";
- tmp_rectangle << "\n\ty=\"" << t << "\" ";
- tmp_rectangle << "\n\twidth=\"" << r-l << "\" ";
- tmp_rectangle << "\n\theight=\"" << b-t << "\" />";
- tmp_rectangle << "\n</clipPath>";
-
- assert_empty_path(d, "EMR_RECTANGLE");
-
- *(d->outdef) += tmp_rectangle.str().c_str();
- *(d->path) = "";
- break;
- }
- case EMR_SCALEVIEWPORTEXTEX:
- dbg_str << "<!-- EMR_SCALEVIEWPORTEXTEX -->\n";
- break;
- case EMR_SCALEWINDOWEXTEX:
- dbg_str << "<!-- EMR_SCALEWINDOWEXTEX -->\n";
- break;
- case EMR_SAVEDC:
- dbg_str << "<!-- EMR_SAVEDC -->\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 << "<!-- EMR_RESTOREDC -->\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 << "<!-- EMR_SETWORLDTRANSFORM -->\n";
-
- PEMRSETWORLDTRANSFORM pEmr = (PEMRSETWORLDTRANSFORM) lpEMFR;
- d->dc[d->level].worldTransform = pEmr->xform;
- break;
- }
- case EMR_MODIFYWORLDTRANSFORM:
- {
- dbg_str << "<!-- EMR_MODIFYWORLDTRANSFORM -->\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 << "<!-- EMR_SELECTOBJECT -->\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 << "<!-- EMR_CREATEPEN -->\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 << "<!-- EMR_CREATEBRUSHINDIRECT -->\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 << "<!-- EMR_DELETEOBJECT -->\n";
- break;
- case EMR_ANGLEARC:
- dbg_str << "<!-- EMR_ANGLEARC -->\n";
- break;
- case EMR_ELLIPSE:
- {
- dbg_str << "<!-- EMR_ELLIPSE -->\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) += " <ellipse ";
- output_style(d, lpEMFR->iType);
- *(d->outsvg) += "\n\t";
- *(d->outsvg) += tmp_ellipse.str().c_str();
- *(d->outsvg) += "/> \n";
- *(d->path) = "";
- break;
- }
- case EMR_RECTANGLE:
- {
- dbg_str << "<!-- EMR_RECTANGLE -->\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) += " <path ";
- output_style(d, lpEMFR->iType);
- *(d->outsvg) += "\n\t";
- *(d->outsvg) += tmp_rectangle.str().c_str();
- *(d->outsvg) += " \" /> \n";
- *(d->path) = "";
- break;
- }
- case EMR_ROUNDRECT:
- {
- dbg_str << "<!-- EMR_ROUNDRECT -->\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) += " <path ";
- output_style(d, lpEMFR->iType);
- *(d->outsvg) += "\n\t";
- *(d->outsvg) += tmp_rectangle.str().c_str();
- *(d->outsvg) += " \" /> \n";
- *(d->path) = "";
- break;
- }
- case EMR_ARC:
- dbg_str << "<!-- EMR_ARC -->\n";
- break;
- case EMR_CHORD:
- dbg_str << "<!-- EMR_CHORD -->\n";
- break;
- case EMR_PIE:
- dbg_str << "<!-- EMR_PIE -->\n";
- break;
- case EMR_SELECTPALETTE:
- dbg_str << "<!-- EMR_SELECTPALETTE -->\n";
- break;
- case EMR_CREATEPALETTE:
- dbg_str << "<!-- EMR_CREATEPALETTE -->\n";
- break;
- case EMR_SETPALETTEENTRIES:
- dbg_str << "<!-- EMR_SETPALETTEENTRIES -->\n";
- break;
- case EMR_RESIZEPALETTE:
- dbg_str << "<!-- EMR_RESIZEPALETTE -->\n";
- break;
- case EMR_REALIZEPALETTE:
- dbg_str << "<!-- EMR_REALIZEPALETTE -->\n";
- break;
- case EMR_EXTFLOODFILL:
- dbg_str << "<!-- EMR_EXTFLOODFILL -->\n";
- break;
- case EMR_LINETO:
- {
- dbg_str << "<!-- EMR_LINETO -->\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 << "<!-- EMR_ARCTO -->\n";
- break;
- case EMR_POLYDRAW:
- dbg_str << "<!-- EMR_POLYDRAW -->\n";
- break;
- case EMR_SETARCDIRECTION:
- dbg_str << "<!-- EMR_SETARCDIRECTION -->\n";
- break;
- case EMR_SETMITERLIMIT:
- {
- dbg_str << "<!-- EMR_SETMITERLIMIT -->\n";
-
- PEMRSETMITERLIMIT pEmr = (PEMRSETMITERLIMIT) lpEMFR;
-
- float miterlimit = pEmr->eMiterLimit;
- miterlimit = miterlimit * 4.0 / 10.0;
- d->dc[d->level].style.stroke_miterlimit.value = pix_to_size_point( d, miterlimit );
- if (d->dc[d->level].style.stroke_miterlimit.value < 1)
- d->dc[d->level].style.stroke_miterlimit.value = 4.0;
- break;
- }
- case EMR_BEGINPATH:
- {
- dbg_str << "<!-- EMR_BEGINPATH -->\n";
-
- if (!d->pathless_stroke) {
- tmp_path << "d=\"";
- *(d->path) = "";
- }
- d->pathless_stroke = false;
- d->inpath = true;
- break;
- }
- case EMR_ENDPATH:
- {
- dbg_str << "<!-- EMR_ENDPATH -->\n";
-
- tmp_path << "\"";
- d->inpath = false;
- break;
- }
- case EMR_CLOSEFIGURE:
- {
- dbg_str << "<!-- EMR_CLOSEFIGURE -->\n";
-
- tmp_path << "\n\tz";
- break;
- }
- case EMR_FILLPATH:
- case EMR_STROKEANDFILLPATH:
- case EMR_STROKEPATH:
- {
- if (lpEMFR->iType == EMR_FILLPATH)
- dbg_str << "<!-- EMR_FILLPATH -->\n";
- if (lpEMFR->iType == EMR_STROKEANDFILLPATH)
- dbg_str << "<!-- EMR_STROKEANDFILLPATH -->\n";
- if (lpEMFR->iType == EMR_STROKEPATH)
- dbg_str << "<!-- EMR_STROKEPATH -->\n";
-
- *(d->outsvg) += " <path ";
- output_style(d, lpEMFR->iType);
- *(d->outsvg) += "\n\t";
- *(d->outsvg) += *(d->path);
- *(d->outsvg) += " /> \n";
- *(d->path) = "";
- break;
- }
- case EMR_FLATTENPATH:
- dbg_str << "<!-- EMR_FLATTENPATH -->\n";
- break;
- case EMR_WIDENPATH:
- dbg_str << "<!-- EMR_WIDENPATH -->\n";
- break;
- case EMR_SELECTCLIPPATH:
- dbg_str << "<!-- EMR_SELECTCLIPPATH -->\n";
- break;
- case EMR_ABORTPATH:
- dbg_str << "<!-- EMR_ABORTPATH -->\n";
- break;
- case EMR_GDICOMMENT:
- {
- dbg_str << "<!-- EMR_GDICOMMENT -->\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 << " <!-- \"";
- tmp_outsvg << tmp_str.str().c_str();
- tmp_outsvg << "\" -->\n";
- }
-
- break;
- }
- case EMR_FILLRGN:
- dbg_str << "<!-- EMR_FILLRGN -->\n";
- break;
- case EMR_FRAMERGN:
- dbg_str << "<!-- EMR_FRAMERGN -->\n";
- break;
- case EMR_INVERTRGN:
- dbg_str << "<!-- EMR_INVERTRGN -->\n";
- break;
- case EMR_PAINTRGN:
- dbg_str << "<!-- EMR_PAINTRGN -->\n";
- break;
- case EMR_EXTSELECTCLIPRGN:
- {
- dbg_str << "<!-- EMR_EXTSELECTCLIPRGN -->\n";
-
- PEMREXTSELECTCLIPRGN pEmr = (PEMREXTSELECTCLIPRGN) lpEMFR;
- if (pEmr->iMode == RGN_COPY)
- clipset = false;
- break;
- }
- case EMR_BITBLT:
- {
- dbg_str << "<!-- EMR_BITBLT -->\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) += " <path ";
- output_style(d, lpEMFR->iType);
- *(d->outsvg) += "\n\t";
- *(d->outsvg) += tmp_rectangle.str().c_str();
- *(d->outsvg) += " \" /> \n";
- *(d->path) = "";
- }
- break;
- }
- case EMR_STRETCHBLT:
- dbg_str << "<!-- EMR_STRETCHBLT -->\n";
- break;
- case EMR_MASKBLT:
- dbg_str << "<!-- EMR_MASKBLT -->\n";
- break;
- case EMR_PLGBLT:
- dbg_str << "<!-- EMR_PLGBLT -->\n";
- break;
- case EMR_SETDIBITSTODEVICE:
- dbg_str << "<!-- EMR_SETDIBITSTODEVICE -->\n";
- break;
- case EMR_STRETCHDIBITS:
- dbg_str << "<!-- EMR_STRETCHDIBITS -->\n";
- break;
- case EMR_EXTCREATEFONTINDIRECTW:
- {
- dbg_str << "<!-- EMR_EXTCREATEFONTINDIRECTW -->\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 << "<!-- EMR_EXTTEXTOUTA -->\n";
- break;
- }
- case EMR_EXTTEXTOUTW:
- {
- dbg_str << "<!-- EMR_EXTTEXTOUTW -->\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 << " <text\n";
-// ts << " id=\"" << (d->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 << "</text>\n";
-
- *(d->outsvg) += ts.str().c_str();
-
- g_free(escaped_text);
- g_free(ansi_text);
- }
-
- break;
- }
- case EMR_POLYBEZIER16:
- {
- dbg_str << "<!-- EMR_POLYBEZIER16 -->\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) += " <path ";
- output_style(d, EMR_STROKEPATH);
- *(d->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; i<pEmr->cpts; ) {
- tmp_str << "\n\tC ";
- for (j=0; j<3 && i<pEmr->cpts; 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 << "<!-- EMR_POLYGON16 -->\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) += " <path ";
- output_style(d, EMR_STROKEANDFILLPATH);
- *(d->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; i<pEmr->cpts; 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 << "<!-- EMR_POLYLINE16 -->\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) += " <path ";
- output_style(d, EMR_STROKEPATH);
- *(d->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; i<pEmr->cpts; 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 << "<!-- EMR_POLYBEZIERTO16 -->\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; i<pEmr->cpts;) {
- tmp_path << "\n\tC ";
- for (j=0; j<3 && i<pEmr->cpts; 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 << "<!-- EMR_POLYLINETO16 -->\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; i<pEmr->cpts;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 << "<!-- EMR_POLYPOLYLINE16 -->\n";
- if (lpEMFR->iType == EMR_POLYPOLYGON16)
- dbg_str << "<!-- EMR_POLYPOLYGON16 -->\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) += " <path ";
- output_style(d, lpEMFR->iType==EMR_POLYPOLYGON16 ? EMR_STROKEANDFILLPATH : EMR_STROKEPATH);
- *(d->outsvg) += "\n\td=\"";
- }
-
- POINTS *apts = (POINTS *) &pEmr->aPolyCounts[pEmr->nPolys];
-
- i = 0;
- for (n=0; n<pEmr->nPolys && i<pEmr->cpts; 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; j<pEmr->aPolyCounts[n] && i<pEmr->cpts; 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 << "<!-- EMR_POLYDRAW16 -->\n";
- break;
- case EMR_CREATEMONOBRUSH:
- dbg_str << "<!-- EMR_CREATEMONOBRUSH -->\n";
- break;
- case EMR_CREATEDIBPATTERNBRUSHPT:
- {
- dbg_str << "<!-- EMR_CREATEDIBPATTERNBRUSHPT -->\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 << "<!-- EMR_EXTCREATEPEN -->\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; i<pEmr->elp.elpNumEntries; i++) {
- pPen->elp.elpStyleEntry[i] = pEmr->elp.elpStyleEntry[i];
- }
- insert_object(d, index, EMR_EXTCREATEPEN, (ENHMETARECORD *) pPen);
-
- break;
- }
- case EMR_POLYTEXTOUTA:
- dbg_str << "<!-- EMR_POLYTEXTOUTA -->\n";
- break;
- case EMR_POLYTEXTOUTW:
- dbg_str << "<!-- EMR_POLYTEXTOUTW -->\n";
- break;
- case EMR_SETICMMODE:
- dbg_str << "<!-- EMR_SETICMMODE -->\n";
- break;
- case EMR_CREATECOLORSPACE:
- dbg_str << "<!-- EMR_CREATECOLORSPACE -->\n";
- break;
- case EMR_SETCOLORSPACE:
- dbg_str << "<!-- EMR_SETCOLORSPACE -->\n";
- break;
- case EMR_DELETECOLORSPACE:
- dbg_str << "<!-- EMR_DELETECOLORSPACE -->\n";
- break;
- case EMR_GLSRECORD:
- dbg_str << "<!-- EMR_GLSRECORD -->\n";
- break;
- case EMR_GLSBOUNDEDRECORD:
- dbg_str << "<!-- EMR_GLSBOUNDEDRECORD -->\n";
- break;
- case EMR_PIXELFORMAT:
- dbg_str << "<!-- EMR_PIXELFORMAT -->\n";
- break;
- default:
- dbg_str << "<!-- EMR_??? -->\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<ENHMFENUMPROC>(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<d.n_obj; i++)
- delete_object(&d, i);
- delete[] d.emf_obj;
- }
-
- if (d.dc[0].style.stroke_dash.dash)
- delete[] d.dc[0].style.stroke_dash.dash;
-
- if (local_fn)
- g_free(local_fn);
- if (unicode_fn)
- g_free(unicode_fn);
-
- return doc;
-}
-
-
-void
-EmfWin32::init (void)
-{
- /* EMF in */
- Inkscape::Extension::build_from_mem(
- "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
- "<name>" N_("EMF Input") "</name>\n"
- "<id>org.inkscape.input.emf.win32</id>\n"
- "<input>\n"
- "<extension>.emf</extension>\n"
- "<mimetype>image/x-emf</mimetype>\n"
- "<filetypename>" N_("Enhanced Metafiles (*.emf)") "</filetypename>\n"
- "<filetypetooltip>" N_("Enhanced Metafiles") "</filetypetooltip>\n"
- "<output_extension>org.inkscape.output.emf.win32</output_extension>\n"
- "</input>\n"
- "</inkscape-extension>", new EmfWin32());
-
- /* WMF in */
- Inkscape::Extension::build_from_mem(
- "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
- "<name>" N_("WMF Input") "</name>\n"
- "<id>org.inkscape.input.wmf.win32</id>\n"
- "<input>\n"
- "<extension>.wmf</extension>\n"
- "<mimetype>image/x-wmf</mimetype>\n"
- "<filetypename>" N_("Windows Metafiles (*.wmf)") "</filetypename>\n"
- "<filetypetooltip>" N_("Windows Metafiles") "</filetypetooltip>\n"
- "<output_extension>org.inkscape.output.emf.win32</output_extension>\n"
- "</input>\n"
- "</inkscape-extension>", new EmfWin32());
-
- /* EMF out */
- Inkscape::Extension::build_from_mem(
- "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
- "<name>" N_("EMF Output") "</name>\n"
- "<id>org.inkscape.output.emf.win32</id>\n"
- "<param name=\"textToPath\" gui-text=\"" N_("Convert texts to paths") "\" type=\"boolean\">true</param>\n"
- "<output>\n"
- "<extension>.emf</extension>\n"
- "<mimetype>image/x-emf</mimetype>\n"
- "<filetypename>" N_("Enhanced Metafile (*.emf)") "</filetypename>\n"
- "<filetypetooltip>" N_("Enhanced Metafile") "</filetypetooltip>\n"
- "</output>\n"
- "</inkscape-extension>", 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 --git a/src/extension/internal/emf-win32-inout.h b/src/extension/internal/emf-win32-inout.h
deleted file mode 100644
index 4b975c8de..000000000
--- a/src/extension/internal/emf-win32-inout.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/** @file
- * @brief Enhanced Metafile Input/Output
- */
-/* Authors:
- * Ulf Erikson <ulferikson@users.sf.net>
- *
- * 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 --git a/src/extension/internal/emf-win32-print.cpp b/src/extension/internal/emf-win32-print.cpp
deleted file mode 100644
index ef76c2c14..000000000
--- a/src/extension/internal/emf-win32-print.cpp
+++ /dev/null
@@ -1,948 +0,0 @@
-/** @file
- * @brief Enhanced Metafile printing
- */
-/* Authors:
- * Ulf Erikson <ulferikson@users.sf.net>
- * Jon A. Cruz <jon@joncruz.org>
- * 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;
-
- miterlimit = miterlimit * 10.0 / 4.0;
- if (miterlimit < 1)
- miterlimit = 10.0;
-
- miterlimit = miterlimit * IN_PER_PX * dwDPI;
-
- 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<Geom::CubicBezier const*>(&*cit)) {
- cubic = cubic;
- curves++;
- }
- }
- }
-
- if (!nodes)
- return false;
-
- POINT *lpPoints = new POINT[moves + lines + curves*3];
- int i = 0;
-
- /**
- * For all Subpaths in the <path>
- */
- 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<Geom::CubicBezier const*>(&*cit))
- {
- std::vector<Geom::Point> 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 <path>
- */
- 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<Geom::CubicBezier const*>(&*cit))
- {
- std::vector<Geom::Point> 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(
- "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
- "<name>Enhanced Metafile Print</name>\n"
- "<id>org.inkscape.print.emf.win32</id>\n"
- "<param name=\"destination\" type=\"string\"></param>\n"
- "<param name=\"textToPath\" type=\"boolean\">true</param>\n"
- "<param name=\"pageBoundingBox\" type=\"boolean\">true</param>\n"
- "<print/>\n"
- "</inkscape-extension>", 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 --git a/src/extension/internal/filter/bevels.h b/src/extension/internal/filter/bevels.h
index 6fc73e58a..ea9d537ad 100644
--- a/src/extension/internal/filter/bevels.h
+++ b/src/extension/internal/filter/bevels.h
@@ -52,9 +52,9 @@ public:
"<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
"<name>" N_("Diffuse Light") "</name>\n"
"<id>org.inkscape.effect.filter.DiffuseLight</id>\n"
- "<param name=\"smooth\" gui-text=\"" N_("Smoothness:") "\" type=\"float\" appearance=\"full\" min=\"0.0\" max=\"10\">6</param>\n"
- "<param name=\"elevation\" gui-text=\"" N_("Elevation (°):") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"360\">25</param>\n"
- "<param name=\"azimuth\" gui-text=\"" N_("Azimuth (°):") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"360\">235</param>\n"
+ "<param name=\"smooth\" gui-text=\"" N_("Smoothness") "\" type=\"float\" appearance=\"full\" min=\"0.0\" max=\"10\">6</param>\n"
+ "<param name=\"elevation\" gui-text=\"" N_("Elevation (°)") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"360\">25</param>\n"
+ "<param name=\"azimuth\" gui-text=\"" N_("Azimuth (°)") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"360\">235</param>\n"
"<param name=\"color\" gui-text=\"" N_("Lighting color") "\" type=\"color\">-1</param>\n"
"<effect>\n"
"<object-type>all</object-type>\n"
@@ -132,10 +132,10 @@ public:
"<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
"<name>" N_("Matte Jelly") "</name>\n"
"<id>org.inkscape.effect.filter.MatteJelly</id>\n"
- "<param name=\"smooth\" gui-text=\"" N_("Smoothness:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.00\" max=\"10.00\">7</param>\n"
- "<param name=\"bright\" gui-text=\"" N_("Brightness:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.00\" max=\"5.00\">0.9</param>\n"
- "<param name=\"elevation\" gui-text=\"" N_("Elevation (°):") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"360\">60</param>\n"
- "<param name=\"azimuth\" gui-text=\"" N_("Azimuth (°):") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"360\">225</param>\n"
+ "<param name=\"smooth\" gui-text=\"" N_("Smoothness") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.00\" max=\"10.00\">7</param>\n"
+ "<param name=\"bright\" gui-text=\"" N_("Brightness") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.00\" max=\"5.00\">0.9</param>\n"
+ "<param name=\"elevation\" gui-text=\"" N_("Elevation (°)") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"360\">60</param>\n"
+ "<param name=\"azimuth\" gui-text=\"" N_("Azimuth (°)") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"360\">225</param>\n"
"<param name=\"color\" gui-text=\"" N_("Lighting color") "\" type=\"color\">-1</param>\n"
"<effect>\n"
"<object-type>all</object-type>\n"
@@ -216,10 +216,10 @@ public:
"<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
"<name>" N_("Specular Light") "</name>\n"
"<id>org.inkscape.effect.filter.SpecularLight</id>\n"
- "<param name=\"smooth\" gui-text=\"" N_("Smoothness:") "\" type=\"float\" appearance=\"full\" min=\"0.0\" max=\"10\">6</param>\n"
- "<param name=\"bright\" gui-text=\"" N_("Brightness:") "\" type=\"float\" appearance=\"full\" min=\"0.0\" max=\"5\">1</param>\n"
- "<param name=\"elevation\" gui-text=\"" N_("Elevation (°):") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"360\">45</param>\n"
- "<param name=\"azimuth\" gui-text=\"" N_("Azimuth (°):") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"360\">235</param>\n"
+ "<param name=\"smooth\" gui-text=\"" N_("Smoothness") "\" type=\"float\" appearance=\"full\" min=\"0.0\" max=\"10\">6</param>\n"
+ "<param name=\"bright\" gui-text=\"" N_("Brightness") "\" type=\"float\" appearance=\"full\" min=\"0.0\" max=\"5\">1</param>\n"
+ "<param name=\"elevation\" gui-text=\"" N_("Elevation (°)") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"360\">45</param>\n"
+ "<param name=\"azimuth\" gui-text=\"" N_("Azimuth (°)") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"360\">235</param>\n"
"<param name=\"color\" gui-text=\"" N_("Lighting color") "\" type=\"color\">-1</param>\n"
"<effect>\n"
"<object-type>all</object-type>\n"
diff --git a/src/extension/internal/filter/blurs.h b/src/extension/internal/filter/blurs.h
index 59790b1be..fe1b9c930 100644
--- a/src/extension/internal/filter/blurs.h
+++ b/src/extension/internal/filter/blurs.h
@@ -53,8 +53,8 @@ public:
"<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
"<name>" N_("Blur") "</name>\n"
"<id>org.inkscape.effect.filter.Blur</id>\n"
- "<param name=\"hblur\" gui-text=\"" N_("Horizontal blur:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"100.00\">2</param>\n"
- "<param name=\"vblur\" gui-text=\"" N_("Vertical blur:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"100.00\">2</param>\n"
+ "<param name=\"hblur\" gui-text=\"" N_("Horizontal blur") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"100.00\">2</param>\n"
+ "<param name=\"vblur\" gui-text=\"" N_("Vertical blur") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"100.00\">2</param>\n"
"<param name=\"content\" gui-text=\"" N_("Blur content only") "\" type=\"boolean\">False</param>\n"
"<effect>\n"
"<object-type>all</object-type>\n"
@@ -124,7 +124,7 @@ public:
"<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
"<name>" N_("Clean Edges") "</name>\n"
"<id>org.inkscape.effect.filter.CleanEdges</id>\n"
- "<param name=\"blur\" gui-text=\"" N_("Strength:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"2.00\">0.4</param>\n"
+ "<param name=\"blur\" gui-text=\"" N_("Strength") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"2.00\">0.4</param>\n"
"<effect>\n"
"<object-type>all</object-type>\n"
"<effects-menu>\n"
@@ -184,10 +184,10 @@ public:
"<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
"<name>" N_("Cross Blur") "</name>\n"
"<id>org.inkscape.effect.filter.CrossBlur</id>\n"
- "<param name=\"bright\" gui-text=\"" N_("Brightness:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.\" max=\"10.00\">0</param>\n"
- "<param name=\"fade\" gui-text=\"" N_("Fading:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.\" max=\"1.00\">0</param>\n"
- "<param name=\"hblur\" gui-text=\"" N_("Horizontal blur:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"20.00\">5</param>\n"
- "<param name=\"vblur\" gui-text=\"" N_("Vertical blur:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"20.00\">5</param>\n"
+ "<param name=\"bright\" gui-text=\"" N_("Brightness") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.\" max=\"10.00\">0</param>\n"
+ "<param name=\"fade\" gui-text=\"" N_("Fading") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.\" max=\"1.00\">0</param>\n"
+ "<param name=\"hblur\" gui-text=\"" N_("Horizontal blur") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"20.00\">5</param>\n"
+ "<param name=\"vblur\" gui-text=\"" N_("Vertical blur") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"20.00\">5</param>\n"
"<param name=\"blend\" gui-text=\"" N_("Blend:") "\" type=\"enum\">\n"
"<_item value=\"darken\">" N_("Darken") "</_item>\n"
"<_item value=\"screen\">" N_("Screen") "</_item>\n"
@@ -259,7 +259,7 @@ public:
"<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
"<name>" N_("Feather") "</name>\n"
"<id>org.inkscape.effect.filter.Feather</id>\n"
- "<param name=\"blur\" gui-text=\"" N_("Strength:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"100.00\">5</param>\n"
+ "<param name=\"blur\" gui-text=\"" N_("Strength") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"100.00\">5</param>\n"
"<effect>\n"
"<object-type>all</object-type>\n"
"<effects-menu>\n"
@@ -326,11 +326,11 @@ public:
"<id>org.inkscape.effect.filter.ImageBlur</id>\n"
"<param name=\"tab\" type=\"notebook\">\n"
"<page name=\"optionstab\" _gui-text=\"Options\">\n"
- "<param name=\"hblur\" gui-text=\"" N_("Horizontal blur:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"50.00\">3</param>\n"
- "<param name=\"vblur\" gui-text=\"" N_("Vertical blur:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"50.00\">3</param>\n"
- "<param name=\"dilat\" gui-text=\"" N_("Dilatation:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"100\">6</param>\n"
- "<param name=\"erosion\" gui-text=\"" N_("Erosion:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"100\">2</param>\n"
- "<param name=\"opacity\" gui-text=\"" N_("Opacity:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"1\">1</param>\n"
+ "<param name=\"hblur\" gui-text=\"" N_("Horizontal blur") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"50.00\">3</param>\n"
+ "<param name=\"vblur\" gui-text=\"" N_("Vertical blur") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"50.00\">3</param>\n"
+ "<param name=\"dilat\" gui-text=\"" N_("Dilatation") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"100\">6</param>\n"
+ "<param name=\"erosion\" gui-text=\"" N_("Erosion") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"100\">2</param>\n"
+ "<param name=\"opacity\" gui-text=\"" N_("Opacity") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"1\">1</param>\n"
"</page>\n"
"<page name=\"backgroundtab\" _gui-text=\"Background\">\n"
"<param name=\"color\" gui-text=\"" N_("Background color") "\" type=\"color\">-1</param>\n"
diff --git a/src/extension/internal/filter/bumps.h b/src/extension/internal/filter/bumps.h
index 8db6c67d2..b59b1ef50 100644
--- a/src/extension/internal/filter/bumps.h
+++ b/src/extension/internal/filter/bumps.h
@@ -81,13 +81,13 @@ public:
"<id>org.inkscape.effect.filter.Bump</id>\n"
"<param name=\"tab\" type=\"notebook\">\n"
"<page name=\"optionstab\" _gui-text=\"Options\">\n"
- "<param name=\"simplifyImage\" gui-text=\"" N_("Image simplification:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"10.00\">0.01</param>\n"
- "<param name=\"simplifyBump\" gui-text=\"" N_("Bump simplification:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"10.00\">0.01</param>\n"
- "<param name=\"crop\" gui-text=\"" N_("Crop:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-50.\" max=\"50.\">0</param>\n"
+ "<param name=\"simplifyImage\" gui-text=\"" N_("Image simplification") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"10.00\">0.01</param>\n"
+ "<param name=\"simplifyBump\" gui-text=\"" N_("Bump simplification") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"10.00\">0.01</param>\n"
+ "<param name=\"crop\" gui-text=\"" N_("Crop") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-50.\" max=\"50.\">0</param>\n"
"<_param name=\"sourceHeader\" type=\"description\" appearance=\"header\">" N_("Bump source") "</_param>\n"
- "<param name=\"red\" gui-text=\"" N_("Red:") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"-50.\" max=\"50.\">0</param>\n"
- "<param name=\"green\" gui-text=\"" N_("Green:") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"-50.\" max=\"50.\">0</param>\n"
- "<param name=\"blue\" gui-text=\"" N_("Blue:") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"-50.\" max=\"50.\">0</param>\n"
+ "<param name=\"red\" gui-text=\"" N_("Red") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"-50.\" max=\"50.\">0</param>\n"
+ "<param name=\"green\" gui-text=\"" N_("Green") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"-50.\" max=\"50.\">0</param>\n"
+ "<param name=\"blue\" gui-text=\"" N_("Blue") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"-50.\" max=\"50.\">0</param>\n"
"<param name=\"background\" gui-text=\"" N_("Bump from background") "\" indent=\"1\" type=\"boolean\">false</param>\n"
"</page>\n"
"<page name=\"lightingtab\" _gui-text=\"Lighting\">\n"
@@ -95,9 +95,9 @@ public:
"<_item value=\"specular\">" N_("Specular") "</_item>\n"
"<_item value=\"diffuse\">" N_("Diffuse") "</_item>\n"
"</param>\n"
- "<param name=\"height\" gui-text=\"" N_("Height:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"50.\">5</param>\n"
- "<param name=\"lightness\" gui-text=\"" N_("Lightness:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"5.\">1</param>\n"
- "<param name=\"precision\" gui-text=\"" N_("Precision:") "\" type=\"int\" appearance=\"full\" min=\"1\" max=\"128\">15</param>\n"
+ "<param name=\"height\" gui-text=\"" N_("Height") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"50.\">5</param>\n"
+ "<param name=\"lightness\" gui-text=\"" N_("Lightness") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"5.\">1</param>\n"
+ "<param name=\"precision\" gui-text=\"" N_("Precision") "\" type=\"int\" appearance=\"full\" min=\"1\" max=\"128\">15</param>\n"
"<param name=\"lightingColor\" gui-text=\"" N_("Color") "\" type=\"color\">-1</param>\n"
"</page>\n"
"<page name=\"lightsourcetab\" _gui-text=\"" N_("Light source") "\">\n"
@@ -107,21 +107,21 @@ public:
"<_item value=\"spot\">" N_("Spot") "</_item>\n"
"</param>\n"
"<_param name=\"distantHeader\" type=\"description\" appearance=\"header\">" N_("Distant light options") "</_param>\n"
- "<param name=\"distantAzimuth\" gui-text=\"" N_("Azimuth:") "\" type=\"int\" indent=\"1\" appearance=\"full\" min=\"0\" max=\"360\">225</param>\n"
- "<param name=\"distantElevation\" gui-text=\"" N_("Elevation:") "\" type=\"int\" indent=\"1\" appearance=\"full\" min=\"0\" max=\"180\">45</param>\n"
+ "<param name=\"distantAzimuth\" gui-text=\"" N_("Azimuth") "\" type=\"int\" indent=\"1\" appearance=\"full\" min=\"0\" max=\"360\">225</param>\n"
+ "<param name=\"distantElevation\" gui-text=\"" N_("Elevation") "\" type=\"int\" indent=\"1\" appearance=\"full\" min=\"0\" max=\"180\">45</param>\n"
"<_param name=\"pointHeader\" type=\"description\" appearance=\"header\">" N_("Point light options") "</_param>\n"
- "<param name=\"pointX\" gui-text=\"" N_("X location:") "\" type=\"int\" indent=\"1\" appearance=\"full\" min=\"-5000\" max=\"5000\">526</param>\n"
- "<param name=\"pointY\" gui-text=\"" N_("Y location:") "\" type=\"int\" indent=\"1\" appearance=\"full\" min=\"-5000\" max=\"5000\">372</param>\n"
- "<param name=\"pointZ\" gui-text=\"" N_("Z location:") "\" type=\"int\" indent=\"1\" appearance=\"full\" min=\"0\" max=\"5000\">150</param>\n"
+ "<param name=\"pointX\" gui-text=\"" N_("X location") "\" type=\"int\" indent=\"1\" appearance=\"full\" min=\"-5000\" max=\"5000\">526</param>\n"
+ "<param name=\"pointY\" gui-text=\"" N_("Y location") "\" type=\"int\" indent=\"1\" appearance=\"full\" min=\"-5000\" max=\"5000\">372</param>\n"
+ "<param name=\"pointZ\" gui-text=\"" N_("Z location") "\" type=\"int\" indent=\"1\" appearance=\"full\" min=\"0\" max=\"5000\">150</param>\n"
"<_param name=\"spotHeader\" type=\"description\" appearance=\"header\">" N_("Spot light options") "</_param>\n"
- "<param name=\"spotX\" gui-text=\"" N_("X location:") "\" type=\"int\" indent=\"1\" appearance=\"full\" min=\"-5000\" max=\"5000\">526</param>\n"
- "<param name=\"spotY\" gui-text=\"" N_("Y location:") "\" type=\"int\" indent=\"1\" appearance=\"full\" min=\"-5000\" max=\"5000\">372</param>\n"
- "<param name=\"spotZ\" gui-text=\"" N_("Z location:") "\" type=\"int\" indent=\"1\" appearance=\"full\" min=\"-5000\" max=\"5000\">150</param>\n"
- "<param name=\"spotAtX\" gui-text=\"" N_("X target:") "\" type=\"int\" indent=\"1\" appearance=\"full\" min=\"-5000\" max=\"5000\">0</param>\n"
- "<param name=\"spotAtY\" gui-text=\"" N_("Y target:") "\" type=\"int\" indent=\"1\" appearance=\"full\" min=\"-5000\" max=\"5000\">0</param>\n"
- "<param name=\"spotAtZ\" gui-text=\"" N_("Z target:") "\" type=\"int\" indent=\"1\" appearance=\"full\" min=\"-5000\" max=\"0\">-1000</param>\n"
- "<param name=\"spotExponent\" gui-text=\"" N_("Specular exponent:") "\" type=\"int\" indent=\"1\" appearance=\"full\" min=\"1\" max=\"100\">1</param>\n"
- "<param name=\"spotConeAngle\" gui-text=\"" N_("Cone angle:") "\" type=\"int\" indent=\"1\" appearance=\"full\" min=\"0\" max=\"100\">50</param>\n"
+ "<param name=\"spotX\" gui-text=\"" N_("X location") "\" type=\"int\" indent=\"1\" appearance=\"full\" min=\"-5000\" max=\"5000\">526</param>\n"
+ "<param name=\"spotY\" gui-text=\"" N_("Y location") "\" type=\"int\" indent=\"1\" appearance=\"full\" min=\"-5000\" max=\"5000\">372</param>\n"
+ "<param name=\"spotZ\" gui-text=\"" N_("Z location") "\" type=\"int\" indent=\"1\" appearance=\"full\" min=\"-5000\" max=\"5000\">150</param>\n"
+ "<param name=\"spotAtX\" gui-text=\"" N_("X target") "\" type=\"int\" indent=\"1\" appearance=\"full\" min=\"-5000\" max=\"5000\">0</param>\n"
+ "<param name=\"spotAtY\" gui-text=\"" N_("Y target") "\" type=\"int\" indent=\"1\" appearance=\"full\" min=\"-5000\" max=\"5000\">0</param>\n"
+ "<param name=\"spotAtZ\" gui-text=\"" N_("Z target") "\" type=\"int\" indent=\"1\" appearance=\"full\" min=\"-5000\" max=\"0\">-1000</param>\n"
+ "<param name=\"spotExponent\" gui-text=\"" N_("Specular exponent") "\" type=\"int\" indent=\"1\" appearance=\"full\" min=\"1\" max=\"100\">1</param>\n"
+ "<param name=\"spotConeAngle\" gui-text=\"" N_("Cone angle") "\" type=\"int\" indent=\"1\" appearance=\"full\" min=\"0\" max=\"100\">50</param>\n"
"</page>\n"
"<page name=\"colortab\" _gui-text=\"Color bump\">\n"
"<param name=\"imageColor\" gui-text=\"" N_("Image color") "\" type=\"color\">-987158017</param>\n"
@@ -167,9 +167,6 @@ Bump::get_filter_text (Inkscape::Extension::Extension * ext)
std::ostringstream lightOptions;
std::ostringstream lightEnd;
- std::ostringstream lightRed;
- std::ostringstream lightGreen;
- std::ostringstream lightBlue;
std::ostringstream floodRed;
std::ostringstream floodGreen;
std::ostringstream floodBlue;
@@ -313,27 +310,27 @@ public:
"<id>org.inkscape.effect.filter.WaxBump</id>\n"
"<param name=\"tab\" type=\"notebook\">\n"
"<page name=\"optionstab\" _gui-text=\"Options\">\n"
- "<param name=\"simplifyImage\" gui-text=\"" N_("Image simplification:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"10.00\">1.5</param>\n"
- "<param name=\"simplifyBump\" gui-text=\"" N_("Bump simplification:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"10.00\">1</param>\n"
- "<param name=\"crop\" gui-text=\"" N_("Crop:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-10.\" max=\"10.\">1</param>\n"
+ "<param name=\"simplifyImage\" gui-text=\"" N_("Image simplification") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"10.00\">1.5</param>\n"
+ "<param name=\"simplifyBump\" gui-text=\"" N_("Bump simplification") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"10.00\">1</param>\n"
+ "<param name=\"crop\" gui-text=\"" N_("Crop") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-10.\" max=\"10.\">1</param>\n"
"<_param name=\"sourceHeader\" type=\"description\" appearance=\"header\">" N_("Bump source") "</_param>\n"
- "<param name=\"red\" gui-text=\"" N_("Red:") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"-10.\" max=\"10.\">0</param>\n"
- "<param name=\"green\" gui-text=\"" N_("Green:") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"-10.\" max=\"10.\">0</param>\n"
- "<param name=\"blue\" gui-text=\"" N_("Blue:") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"-10.\" max=\"10.\">0</param>\n"
+ "<param name=\"red\" gui-text=\"" N_("Red") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"-10.\" max=\"10.\">0</param>\n"
+ "<param name=\"green\" gui-text=\"" N_("Green") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"-10.\" max=\"10.\">0</param>\n"
+ "<param name=\"blue\" gui-text=\"" N_("Blue") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"-10.\" max=\"10.\">0</param>\n"
"<param name=\"background\" gui-text=\"" N_("Background:") "\" type=\"enum\">\n"
"<_item value=\"flood1\">" N_("Color") "</_item>\n"
"<_item value=\"SourceGraphic\">" N_("Image") "</_item>\n"
"<_item value=\"blur1\">" N_("Blurred image") "</_item>\n"
"</param>\n"
- "<param name=\"bgopacity\" gui-text=\"" N_("Background opacity:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.\" max=\"1.\">0</param>\n"
+ "<param name=\"bgopacity\" gui-text=\"" N_("Background opacity") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.\" max=\"1.\">0</param>\n"
"</page>\n"
"<page name=\"lightingtab\" _gui-text=\"" N_("Lighting") "\">\n"
"<param name=\"lightingColor\" gui-text=\"" N_("Color") "\" type=\"color\">-1</param>\n"
- "<param name=\"height\" gui-text=\"" N_("Height:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-50.\" max=\"50.\">5</param>\n"
- "<param name=\"lightness\" gui-text=\"" N_("Lightness:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"10.\">1.4</param>\n"
- "<param name=\"precision\" gui-text=\"" N_("Precision:") "\" type=\"int\" appearance=\"full\" min=\"1\" max=\"50\">35</param>\n"
- "<param name=\"distantAzimuth\" gui-text=\"" N_("Azimuth:") "\" type=\"int\" indent=\"1\" appearance=\"full\" min=\"0\" max=\"360\">225</param>\n"
- "<param name=\"distantElevation\" gui-text=\"" N_("Elevation:") "\" type=\"int\" indent=\"1\" appearance=\"full\" min=\"0\" max=\"180\">60</param>\n"
+ "<param name=\"height\" gui-text=\"" N_("Height") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-50.\" max=\"50.\">5</param>\n"
+ "<param name=\"lightness\" gui-text=\"" N_("Lightness") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"10.\">1.4</param>\n"
+ "<param name=\"precision\" gui-text=\"" N_("Precision") "\" type=\"int\" appearance=\"full\" min=\"1\" max=\"50\">35</param>\n"
+ "<param name=\"distantAzimuth\" gui-text=\"" N_("Azimuth") "\" type=\"int\" indent=\"1\" appearance=\"full\" min=\"0\" max=\"360\">225</param>\n"
+ "<param name=\"distantElevation\" gui-text=\"" N_("Elevation") "\" type=\"int\" indent=\"1\" appearance=\"full\" min=\"0\" max=\"180\">60</param>\n"
"<param name=\"lightingblend\" gui-text=\"" N_("Lighting blend:") "\" type=\"enum\">\n"
"<_item value=\"screen\">" N_("Screen") "</_item>\n"
"<_item value=\"normal\">" N_("Normal") "</_item>\n"
diff --git a/src/extension/internal/filter/color.h b/src/extension/internal/filter/color.h
index b6b194c8b..7059ad29b 100644
--- a/src/extension/internal/filter/color.h
+++ b/src/extension/internal/filter/color.h
@@ -3,7 +3,7 @@
/* Change the 'COLOR' above to be your file name */
/*
- * Copyright (C) 2011 Authors:
+ * Copyright (C) 2013 Authors:
* Ivan Louette (filters)
* Nicolas Dufour (UI) <nicoduf@yahoo.fr>
*
@@ -20,7 +20,8 @@
* Invert
* Lighting
* Lightness-contrast
- * Nudge
+ * Nudge RGB
+ * Nudge CMY
* Quadritone
* Solarize
* Tritone
@@ -70,9 +71,9 @@ public:
"<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
"<name>" N_("Brilliance") "</name>\n"
"<id>org.inkscape.effect.filter.Brilliance</id>\n"
- "<param name=\"brightness\" gui-text=\"" N_("Brightness:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"1\" max=\"10.00\">2</param>\n"
- "<param name=\"sat\" gui-text=\"" N_("Over-saturation:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.0\" max=\"10.00\">0.5</param>\n"
- "<param name=\"lightness\" gui-text=\"" N_("Lightness:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-10.00\" max=\"10.00\">0</param>\n"
+ "<param name=\"brightness\" gui-text=\"" N_("Brightness") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"1\" max=\"10.00\">2</param>\n"
+ "<param name=\"sat\" gui-text=\"" N_("Over-saturation") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.0\" max=\"10.00\">0.5</param>\n"
+ "<param name=\"lightness\" gui-text=\"" N_("Lightness") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-10.00\" max=\"10.00\">0</param>\n"
"<param name=\"invert\" gui-text=\"" N_("Inverted") "\" type=\"boolean\">false</param>\n"
"<effect>\n"
"<object-type>all</object-type>\n"
@@ -152,11 +153,11 @@ public:
"<id>org.inkscape.effect.filter.ChannelPaint</id>\n"
"<param name=\"tab\" type=\"notebook\">\n"
"<page name=\"optionstab\" _gui-text=\"Options\">\n"
- "<param name=\"saturation\" gui-text=\"" N_("Saturation:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.\" max=\"1.\">1</param>\n"
- "<param name=\"red\" gui-text=\"" N_("Red:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-10.\" max=\"10.\">-1</param>\n"
- "<param name=\"green\" gui-text=\"" N_("Green:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-10.\" max=\"10.\">0.5</param>\n"
- "<param name=\"blue\" gui-text=\"" N_("Blue:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-10.\" max=\"10.\">0.5</param>\n"
- "<param name=\"alpha\" gui-text=\"" N_("Alpha:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-10.\" max=\"10.\">1</param>\n"
+ "<param name=\"saturation\" gui-text=\"" N_("Saturation") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.\" max=\"1.\">1</param>\n"
+ "<param name=\"red\" gui-text=\"" N_("Red") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-10.\" max=\"10.\">-1</param>\n"
+ "<param name=\"green\" gui-text=\"" N_("Green") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-10.\" max=\"10.\">0.5</param>\n"
+ "<param name=\"blue\" gui-text=\"" N_("Blue") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-10.\" max=\"10.\">0.5</param>\n"
+ "<param name=\"alpha\" gui-text=\"" N_("Alpha") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-10.\" max=\"10.\">1</param>\n"
"<param name=\"invert\" gui-text=\"" N_("Inverted") "\" type=\"boolean\">false</param>\n"
"</page>\n"
"<page name=\"colortab\" _gui-text=\"Color\">\n"
@@ -211,7 +212,7 @@ ChannelPaint::get_filter_text (Inkscape::Extension::Extension * ext)
}
_filter = g_strdup_printf(
- "<filter inkscape:label=\"Channel Painting\" style=\"color-interpolation-filters:sRGB;\" >\n"
+ "<filter xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\" style=\"color-interpolation-filters:sRGB;\" inkscape:label=\"Channel Painting\">\n"
"<feColorMatrix values=\"%s\" type=\"saturate\" result=\"colormatrix1\" />\n"
"<feColorMatrix values=\"1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 %s %s %s %s 0 \" in=\"SourceGraphic\" result=\"colormatrix2\" />\n"
"<feFlood flood-color=\"rgb(%s,%s,%s)\" flood-opacity=\"%s\" result=\"flood\" />\n"
@@ -252,8 +253,8 @@ public:
"<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
"<name>" N_("Color Shift") "</name>\n"
"<id>org.inkscape.effect.filter.ColorShift</id>\n"
- "<param name=\"shift\" gui-text=\"" N_("Shift (°):") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"360\">330</param>\n"
- "<param name=\"sat\" gui-text=\"" N_("Saturation:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.\" max=\"1\">0.6</param>\n"
+ "<param name=\"shift\" gui-text=\"" N_("Shift (°)") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"360\">330</param>\n"
+ "<param name=\"sat\" gui-text=\"" N_("Saturation") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.\" max=\"1\">0.6</param>\n"
"<effect>\n"
"<object-type>all</object-type>\n"
"<effects-menu>\n"
@@ -317,8 +318,8 @@ public:
"<id>org.inkscape.effect.filter.Colorize</id>\n"
"<param name=\"tab\" type=\"notebook\">\n"
"<page name=\"optionstab\" _gui-text=\"Options\">\n"
- "<param name=\"hlight\" gui-text=\"" N_("Harsh light:") "\" type=\"float\" appearance=\"full\" min=\"0\" max=\"10\">0</param>\n"
- "<param name=\"nlight\" gui-text=\"" N_("Normal light:") "\" type=\"float\" appearance=\"full\" min=\"0\" max=\"10\">1</param>\n"
+ "<param name=\"hlight\" gui-text=\"" N_("Harsh light") "\" type=\"float\" appearance=\"full\" min=\"0\" max=\"10\">0</param>\n"
+ "<param name=\"nlight\" gui-text=\"" N_("Normal light") "\" type=\"float\" appearance=\"full\" min=\"0\" max=\"10\">1</param>\n"
"<param name=\"duotone\" gui-text=\"" N_("Duotone") "\" type=\"boolean\" >false</param>\n"
"<param name=\"blend1\" gui-text=\"" N_("Blend 1:") "\" type=\"enum\">\n"
"<_item value=\"multiply\">" N_("Multiply") "</_item>\n"
@@ -509,7 +510,7 @@ public:
"<id>org.inkscape.effect.filter.Duochrome</id>\n"
"<param name=\"tab\" type=\"notebook\">\n"
"<page name=\"optionstab\" _gui-text=\"Options\">\n"
- "<param name=\"fluo\" gui-text=\"" N_("Fluorescence level:") "\" type=\"float\" appearance=\"full\" min=\"0\" max=\"2\">0</param>\n"
+ "<param name=\"fluo\" gui-text=\"" N_("Fluorescence level") "\" type=\"float\" appearance=\"full\" min=\"0\" max=\"2\">0</param>\n"
"<param name=\"swap\" gui-text=\"" N_("Swap:") "\" type=\"enum\">\n"
"<_item value=\"none\">" N_("No swap") "</_item>\n"
"<_item value=\"full\">" N_("Color and alpha") "</_item>\n"
@@ -615,9 +616,8 @@ Duochrome::get_filter_text (Inkscape::Extension::Extension * ext)
Filter's parameters:
* Channel (enum, all colors, default Red) -> colormatrix (values)
- * Background blend (enum, all blend modes, default Multiply) -> blend (mode)
+ * Background blend (enum, Normal, Multiply, Screen, default Normal) -> blend (mode)
* Channel to alpha (boolean, default false) -> colormatrix (values)
- * Invert (boolean, default false) -> colormatrix (values)
*/
class ExtractChannel : public Inkscape::Extension::Internal::Filter::Filter {
@@ -637,16 +637,16 @@ public:
"<_item value=\"r\">" N_("Red") "</_item>\n"
"<_item value=\"g\">" N_("Green") "</_item>\n"
"<_item value=\"b\">" N_("Blue") "</_item>\n"
+ "<_item value=\"c\">" N_("Cyan") "</_item>\n"
+ "<_item value=\"m\">" N_("Magenta") "</_item>\n"
+ "<_item value=\"y\">" N_("Yellow") "</_item>\n"
"</param>\n"
"<param name=\"blend\" gui-text=\"" N_("Background blend mode:") "\" type=\"enum\">\n"
"<_item value=\"multiply\">" N_("Multiply") "</_item>\n"
"<_item value=\"normal\">" N_("Normal") "</_item>\n"
"<_item value=\"screen\">" N_("Screen") "</_item>\n"
- "<_item value=\"darken\">" N_("Darken") "</_item>\n"
- "<_item value=\"lighten\">" N_("Lighten") "</_item>\n"
"</param>\n"
"<param name=\"alpha\" gui-text=\"" N_("Channel to alpha") "\" type=\"boolean\">false</param>\n"
- "<param name=\"invert\" gui-text=\"" N_("Inverted") "\" type=\"boolean\">false</param>\n"
"<effect>\n"
"<object-type>all</object-type>\n"
"<effects-menu>\n"
@@ -667,45 +667,45 @@ ExtractChannel::get_filter_text (Inkscape::Extension::Extension * ext)
std::ostringstream blend;
std::ostringstream colors;
- std::ostringstream alpha;
- std::ostringstream invert;
blend << ext->get_param_enum("blend");
const gchar *channel = ext->get_param_enum("source");
if (ext->get_param_bool("alpha")) {
- colors << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0";
- } else if ((g_ascii_strcasecmp("r", channel) == 0)) {
- colors << "0 0 0 0 1 0 0 0 0 0 0 0 0 0 0";
- } else if ((g_ascii_strcasecmp("g", channel) == 0)) {
- colors << "0 0 0 0 0 0 0 0 0 1 0 0 0 0 0";
- } else {
- colors << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 1";
- }
-
- if (ext->get_param_bool("invert")) {
if ((g_ascii_strcasecmp("r", channel) == 0)) {
- alpha << "-1 0 0 1";
+ colors << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0";
} else if ((g_ascii_strcasecmp("g", channel) == 0)) {
- alpha << "0 -1 0 1";
+ colors << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0";
+ } else if ((g_ascii_strcasecmp("b", channel) == 0)) {
+ colors << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0";
+ } else if ((g_ascii_strcasecmp("c", channel) == 0)) {
+ colors << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 0 0 1 0";
+ } else if ((g_ascii_strcasecmp("m", channel) == 0)) {
+ colors << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 0 1 0";
} else {
- alpha << "0 0 -1 1";
+ colors << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 1 0";
}
} else {
if ((g_ascii_strcasecmp("r", channel) == 0)) {
- alpha << "1 0 0 0";
+ colors << "0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0";
} else if ((g_ascii_strcasecmp("g", channel) == 0)) {
- alpha << "0 1 0 0";
+ colors << "0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0";
+ } else if ((g_ascii_strcasecmp("b", channel) == 0)) {
+ colors << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0";
+ } else if ((g_ascii_strcasecmp("c", channel) == 0)) {
+ colors << "0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 -1 0 0 1 0";
+ } else if ((g_ascii_strcasecmp("m", channel) == 0)) {
+ colors << "0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 -1 0 1 0";
} else {
- alpha << "0 0 1 0";
+ colors << "0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 -1 1 0";
}
}
_filter = g_strdup_printf(
"<filter xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\" style=\"color-interpolation-filters:sRGB;\" inkscape:label=\"Extract Channel\">\n"
- "<feColorMatrix in=\"SourceGraphic\" values=\"%s %s %s 0 \" result=\"colormatrix\" />\n"
+ "<feColorMatrix in=\"SourceGraphic\" values=\"%s 0 \" result=\"colormatrix\" />\n"
"<feBlend in2=\"BackgroundImage\" mode=\"%s\" result=\"blend\" />\n"
- "</filter>\n", colors.str().c_str(), alpha.str().c_str(), invert.str().c_str(), blend.str().c_str() );
+ "</filter>\n", colors.str().c_str(), blend.str().c_str() );
return _filter;
}; /* ExtractChannel filter */
@@ -739,7 +739,7 @@ public:
"<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
"<name>" N_("Fade to Black or White") "</name>\n"
"<id>org.inkscape.effect.filter.FadeToBW</id>\n"
- "<param name=\"level\" gui-text=\"" N_("Level:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"1\">1</param>\n"
+ "<param name=\"level\" gui-text=\"" N_("Level") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"1\">1</param>\n"
"<param name=\"fadeto\" gui-text=\"" N_("Fade to:") "\" type=\"enum\">\n"
"<_item value=\"black\">" N_("Black") "</_item>\n"
"<_item value=\"white\">" N_("White") "</_item>\n"
@@ -818,10 +818,10 @@ public:
"<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
"<name>" N_("Greyscale") "</name>\n"
"<id>org.inkscape.effect.filter.Greyscale</id>\n"
- "<param name=\"red\" gui-text=\"" N_("Red:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-10.00\" max=\"10.00\">0.21</param>\n"
- "<param name=\"green\" gui-text=\"" N_("Green:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-10.00\" max=\"10.00\">0.72</param>\n"
- "<param name=\"blue\" gui-text=\"" N_("Blue:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-10.00\" max=\"10.00\">0.072</param>\n"
- "<param name=\"strength\" gui-text=\"" N_("Lightness:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-10.00\" max=\"10.00\">0</param>\n"
+ "<param name=\"red\" gui-text=\"" N_("Red") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-10.00\" max=\"10.00\">0.21</param>\n"
+ "<param name=\"green\" gui-text=\"" N_("Green") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-10.00\" max=\"10.00\">0.72</param>\n"
+ "<param name=\"blue\" gui-text=\"" N_("Blue") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-10.00\" max=\"10.00\">0.072</param>\n"
+ "<param name=\"strength\" gui-text=\"" N_("Lightness") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-10.00\" max=\"10.00\">0</param>\n"
"<param name=\"transparent\" gui-text=\"" N_("Transparent") "\" type=\"boolean\" >false</param>\n"
"<effect>\n"
"<object-type>all</object-type>\n"
@@ -910,7 +910,7 @@ public:
"<_item value=\"2\">" N_("Red and green") "</_item>\n"
"<_item value=\"3\">" N_("Green and blue") "</_item>\n"
"</param>\n"
- "<param name=\"opacify\" gui-text=\"" N_("Light transparency:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.\" max=\"1\">0</param>\n"
+ "<param name=\"opacify\" gui-text=\"" N_("Light transparency") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.\" max=\"1\">0</param>\n"
"<param name=\"hue\" gui-text=\"" N_("Invert hue") "\" type=\"boolean\" >false</param>\n"
"<param name=\"lightness\" gui-text=\"" N_("Invert lightness") "\" type=\"boolean\" >false</param>\n"
"<param name=\"transparency\" gui-text=\"" N_("Invert transparency") "\" type=\"boolean\" >false</param>\n"
@@ -1039,9 +1039,9 @@ public:
"<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
"<name>" N_("Lighting") "</name>\n"
"<id>org.inkscape.effect.filter.Lighting</id>\n"
- "<param name=\"amplitude\" gui-text=\"" N_("Lights:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.00\" max=\"20.00\">1</param>\n"
- "<param name=\"exponent\" gui-text=\"" N_("Shadows:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.00\" max=\"20.00\">1</param>\n"
- "<param name=\"offset\" gui-text=\"" N_("Offset:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-1.00\" max=\"1.00\">0</param>\n"
+ "<param name=\"amplitude\" gui-text=\"" N_("Lights") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.00\" max=\"20.00\">1</param>\n"
+ "<param name=\"exponent\" gui-text=\"" N_("Shadows") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.00\" max=\"20.00\">1</param>\n"
+ "<param name=\"offset\" gui-text=\"" N_("Offset") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-1.00\" max=\"1.00\">0</param>\n"
"<effect>\n"
"<object-type>all</object-type>\n"
"<effects-menu>\n"
@@ -1110,8 +1110,8 @@ public:
"<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
"<name>" N_("Lightness-Contrast") "</name>\n"
"<id>org.inkscape.effect.filter.LightnessContrast</id>\n"
- "<param name=\"lightness\" gui-text=\"" N_("Lightness:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-100\" max=\"100\">0</param>\n"
- "<param name=\"contrast\" gui-text=\"" N_("Contrast:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-100\" max=\"100\">0</param>\n"
+ "<param name=\"lightness\" gui-text=\"" N_("Lightness") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-100\" max=\"100\">0</param>\n"
+ "<param name=\"contrast\" gui-text=\"" N_("Contrast") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-100\" max=\"100\">0</param>\n"
"<effect>\n"
"<object-type>all</object-type>\n"
"<effects-menu>\n"
@@ -1157,72 +1157,52 @@ LightnessContrast::get_filter_text (Inkscape::Extension::Extension * ext)
}; /* Lightness-Contrast filter */
/**
- \brief Custom predefined Nudge filter.
+ \brief Custom predefined Nudge RGB filter.
- Nudge separately RGB channels and blend them to different types of backgrounds
+ Nudge RGB channels separately and blend them to different types of backgrounds
Filter's parameters:
Offsets
* Red
- * x (-100.->100., default -7) -> offset1 (dx)
- * y (-100.->100., default 5) -> offset1 (dy)
+ * x (-100.->100., default -6) -> offset1 (dx)
+ * y (-100.->100., default -6) -> offset1 (dy)
* Green
- * x (-100.->100., default 0) -> offset2 (dx)
- * y (-100.->100., default 10) -> offset2 (dy)
+ * x (-100.->100., default 6) -> offset2 (dx)
+ * y (-100.->100., default 7) -> offset2 (dy)
* Blue
- * x (-100.->100., default 3) -> offset3 (dx)
- * y (-100.->100., default -9) -> offset3 (dy)
+ * x (-100.->100., default 1) -> offset3 (dx)
+ * y (-100.->100., default -16) -> offset3 (dy)
Color
- * Background color (guint, default -1)-> flood (flood-color, flood-opacity)
- * Blend type (enum [normal,multiply and screen], default screen) -> blend1,2,3 (mode)
- * Blend source (enum, default color) ->
- * color: blend1 (in="flood")
- * image: blend1 (in="SourceGraphic")
- * background: blend1 (in="BackgroundImage")
- * Composite (enum [in,over], default over) -> composite (operator)
+ * Background color (guint, default 255)-> flood (flood-color, flood-opacity)
*/
-class Nudge : public Inkscape::Extension::Internal::Filter::Filter {
+class NudgeRGB : public Inkscape::Extension::Internal::Filter::Filter {
protected:
virtual gchar const * get_filter_text (Inkscape::Extension::Extension * ext);
public:
- Nudge ( ) : Filter() { };
- virtual ~Nudge ( ) { if (_filter != NULL) g_free((void *)_filter); return; }
+ NudgeRGB ( ) : Filter() { };
+ virtual ~NudgeRGB ( ) { if (_filter != NULL) g_free((void *)_filter); return; }
static void init (void) {
Inkscape::Extension::build_from_mem(
"<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
- "<name>" N_("Nudge") "</name>\n"
- "<id>org.inkscape.effect.filter.Nudge</id>\n"
+ "<name>" N_("Nudge RGB") "</name>\n"
+ "<id>org.inkscape.effect.filter.NudgeRGB</id>\n"
"<param name=\"tab\" type=\"notebook\">\n"
"<page name=\"offsettab\" _gui-text=\"Offset\">\n"
"<_param name=\"redOffset\" type=\"description\" appearance=\"header\">" N_("Red offset") "</_param>\n"
- "<param name=\"rx\" gui-text=\"" N_("X:") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"-100.\" max=\"100.\">-7</param>\n"
- "<param name=\"ry\" gui-text=\"" N_("Y:") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"-100.\" max=\"100.\">5</param>\n"
+ "<param name=\"rx\" gui-text=\"" N_("X") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"-100.\" max=\"100.\">-6</param>\n"
+ "<param name=\"ry\" gui-text=\"" N_("Y") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"-100.\" max=\"100.\">-6</param>\n"
"<_param name=\"greenOffset\" type=\"description\" appearance=\"header\">" N_("Green offset") "</_param>\n"
- "<param name=\"gx\" gui-text=\"" N_("X:") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"-100.\" max=\"100.\">0</param>\n"
- "<param name=\"gy\" gui-text=\"" N_("Y:") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"-100.\" max=\"100.\">10</param>\n"
+ "<param name=\"gx\" gui-text=\"" N_("X") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"-100.\" max=\"100.\">6</param>\n"
+ "<param name=\"gy\" gui-text=\"" N_("Y") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"-100.\" max=\"100.\">7</param>\n"
"<_param name=\"redOffset\" type=\"description\" appearance=\"header\">" N_("Blue offset") "</_param>\n"
- "<param name=\"bx\" gui-text=\"" N_("X:") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"-100.\" max=\"100.\">3</param>\n"
- "<param name=\"by\" gui-text=\"" N_("Y:") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"-100.\" max=\"100.\">-9</param>\n"
+ "<param name=\"bx\" gui-text=\"" N_("X") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"-100.\" max=\"100.\">1</param>\n"
+ "<param name=\"by\" gui-text=\"" N_("Y") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"-100.\" max=\"100.\">-16</param>\n"
"</page>\n"
"<page name=\"coltab\" _gui-text=\"Color\">\n"
"<param name=\"color\" gui-text=\"" N_("Background color") "\" type=\"color\">255</param>\n"
- "<param name=\"blend\" gui-text=\"" N_("Blend type:") "\" type=\"enum\">\n"
- "<_item value=\"screen\">" N_("Screen") "</_item>\n"
- "<_item value=\"multiply\">" N_("Multiply") "</_item>\n"
- "<_item value=\"normal\">" N_("Normal") "</_item>\n"
- "</param>\n"
- "<param name=\"source\" gui-text=\"" N_("Blend source:") "\" type=\"enum\">\n"
- "<_item value=\"flood\">" N_("Color") "</_item>\n"
- "<_item value=\"SourceGraphic\">" N_("Image") "</_item>\n"
- "<_item value=\"BackgroundImage\">" N_("Background") "</_item>\n"
- "</param>\n"
- "<param name=\"composite\" gui-text=\"" N_("Composite:") "\" type=\"enum\">\n"
- "<_item value=\"over\">" N_("Over") "</_item>\n"
- "<_item value=\"in\">" N_("In") "</_item>\n"
- "</param>\n"
"</page>\n"
"</param>\n"
"<effect>\n"
@@ -1232,14 +1212,14 @@ public:
"<submenu name=\"" N_("Color") "\"/>\n"
"</submenu>\n"
"</effects-menu>\n"
- "<menu-tip>" N_("Nudge separately RGB channels and blend them to different types of backgrounds") "</menu-tip>\n"
+ "<menu-tip>" N_("Nudge RGB channels separately and blend them to different types of backgrounds") "</menu-tip>\n"
"</effect>\n"
- "</inkscape-extension>\n", new Nudge());
+ "</inkscape-extension>\n", new NudgeRGB());
};
};
gchar const *
-Nudge::get_filter_text (Inkscape::Extension::Extension * ext)
+NudgeRGB::get_filter_text (Inkscape::Extension::Extension * ext)
{
if (_filter != NULL) g_free((void *)_filter);
@@ -1250,10 +1230,6 @@ Nudge::get_filter_text (Inkscape::Extension::Extension * ext)
std::ostringstream bx;
std::ostringstream by;
- std::ostringstream blend;
- std::ostringstream source;
- std::ostringstream composite;
-
std::ostringstream a;
std::ostringstream r;
std::ostringstream g;
@@ -1266,10 +1242,6 @@ Nudge::get_filter_text (Inkscape::Extension::Extension * ext)
bx << ext->get_param_float("bx");
by << ext->get_param_float("by");
- blend << ext->get_param_enum("blend");
- source << ext->get_param_enum("source");
- composite << ext->get_param_enum("composite");
-
guint32 color = ext->get_param_color("color");
r << ((color >> 24) & 0xff);
g << ((color >> 16) & 0xff);
@@ -1277,27 +1249,137 @@ Nudge::get_filter_text (Inkscape::Extension::Extension * ext)
a << (color & 0xff) / 255.0F;
_filter = g_strdup_printf(
- "<filter xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\" style=\"color-interpolation-filters:sRGB;\" inkscape:label=\"Nudge\">\n"
+ "<filter xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\" style=\"color-interpolation-filters:sRGB;\" inkscape:label=\"Nudge RGB\">\n"
"<feFlood flood-opacity=\"%s\" flood-color=\"rgb(%s,%s,%s)\" result=\"flood\" />\n"
"<feColorMatrix in=\"SourceGraphic\" values=\"0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 \" result=\"colormatrix1\" />\n"
"<feOffset dy=\"%s\" dx=\"%s\" result=\"offset1\" />\n"
- "<feBlend in2=\"%s\" mode=\"%s\" result=\"blend1\" />\n"
+ "<feBlend in2=\"flood\" mode=\"screen\" result=\"blend1\" />\n"
"<feColorMatrix in=\"SourceGraphic\" values=\"0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 \" result=\"colormatrix2\" />\n"
"<feOffset dy=\"%s\" dx=\"%s\" result=\"offset2\" />\n"
- "<feBlend in2=\"blend1\" mode=\"%s\" result=\"blend2\" />\n"
+ "<feBlend in2=\"blend1\" mode=\"screen\" result=\"blend2\" />\n"
"<feOffset dy=\"%s\" dx=\"%s\" result=\"offset3\" />\n"
"<feColorMatrix in=\"SourceGraphic\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 \" result=\"colormatrix3\" />\n"
- "<feBlend in2=\"offset3\" mode=\"%s\" result=\"blend3\" />\n"
- "<feComposite in2=\"SourceGraphic\" operator=\"%s\" />\n"
+ "<feBlend in2=\"offset3\" mode=\"screen\" result=\"blend3\" />\n"
+ "</filter>\n", a.str().c_str(), r.str().c_str(), g.str().c_str(), b.str().c_str(),
+ rx.str().c_str(), ry.str().c_str(),
+ gx.str().c_str(), gy.str().c_str(),
+ bx.str().c_str(), by.str().c_str() );
+
+ return _filter;
+
+}; /* Nudge RGB filter */
+
+/**
+ \brief Custom predefined Nudge CMY filter.
+
+ Nudge CMY channels separately and blend them to different types of backgrounds
+
+ Filter's parameters:
+ Offsets
+ * Cyan
+ * x (-100.->100., default -6) -> offset1 (dx)
+ * y (-100.->100., default -6) -> offset1 (dy)
+ * Magenta
+ * x (-100.->100., default 6) -> offset2 (dx)
+ * y (-100.->100., default 7) -> offset2 (dy)
+ * Yellow
+ * x (-100.->100., default 1) -> offset3 (dx)
+ * y (-100.->100., default -16) -> offset3 (dy)
+ Color
+ * Background color (guint, default -1)-> flood (flood-color, flood-opacity)
+*/
+class NudgeCMY : public Inkscape::Extension::Internal::Filter::Filter {
+protected:
+ virtual gchar const * get_filter_text (Inkscape::Extension::Extension * ext);
+
+public:
+ NudgeCMY ( ) : Filter() { };
+ virtual ~NudgeCMY ( ) { if (_filter != NULL) g_free((void *)_filter); return; }
+
+ static void init (void) {
+ Inkscape::Extension::build_from_mem(
+ "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
+ "<name>" N_("Nudge CMY") "</name>\n"
+ "<id>org.inkscape.effect.filter.NudgeCMY</id>\n"
+ "<param name=\"tab\" type=\"notebook\">\n"
+ "<page name=\"offsettab\" _gui-text=\"Offset\">\n"
+ "<_param name=\"cyanOffset\" type=\"description\" appearance=\"header\">" N_("Cyan offset") "</_param>\n"
+ "<param name=\"cx\" gui-text=\"" N_("X") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"-100.\" max=\"100.\">-6</param>\n"
+ "<param name=\"cy\" gui-text=\"" N_("Y") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"-100.\" max=\"100.\">-6</param>\n"
+ "<_param name=\"magentaOffset\" type=\"description\" appearance=\"header\">" N_("Magenta offset") "</_param>\n"
+ "<param name=\"mx\" gui-text=\"" N_("X") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"-100.\" max=\"100.\">6</param>\n"
+ "<param name=\"my\" gui-text=\"" N_("Y") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"-100.\" max=\"100.\">7</param>\n"
+ "<_param name=\"yellowOffset\" type=\"description\" appearance=\"header\">" N_("Yellow offset") "</_param>\n"
+ "<param name=\"yx\" gui-text=\"" N_("X") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"-100.\" max=\"100.\">1</param>\n"
+ "<param name=\"yy\" gui-text=\"" N_("Y") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"-100.\" max=\"100.\">-16</param>\n"
+ "</page>\n"
+ "<page name=\"coltab\" _gui-text=\"Color\">\n"
+ "<param name=\"color\" gui-text=\"" N_("Background color") "\" type=\"color\">-1</param>\n"
+ "</page>\n"
+ "</param>\n"
+ "<effect>\n"
+ "<object-type>all</object-type>\n"
+ "<effects-menu>\n"
+ "<submenu name=\"" N_("Filters") "\">\n"
+ "<submenu name=\"" N_("Color") "\"/>\n"
+ "</submenu>\n"
+ "</effects-menu>\n"
+ "<menu-tip>" N_("Nudge CMY channels separately and blend them to different types of backgrounds") "</menu-tip>\n"
+ "</effect>\n"
+ "</inkscape-extension>\n", new NudgeCMY());
+ };
+};
+
+gchar const *
+NudgeCMY::get_filter_text (Inkscape::Extension::Extension * ext)
+{
+ if (_filter != NULL) g_free((void *)_filter);
+
+ std::ostringstream cx;
+ std::ostringstream cy;
+ std::ostringstream mx;
+ std::ostringstream my;
+ std::ostringstream yx;
+ std::ostringstream yy;
+
+ std::ostringstream a;
+ std::ostringstream r;
+ std::ostringstream g;
+ std::ostringstream b;
+
+ cx << ext->get_param_float("cx");
+ cy << ext->get_param_float("cy");
+ mx << ext->get_param_float("mx");
+ my << ext->get_param_float("my");
+ yx << ext->get_param_float("yx");
+ yy << ext->get_param_float("yy");
+
+ guint32 color = ext->get_param_color("color");
+ r << ((color >> 24) & 0xff);
+ g << ((color >> 16) & 0xff);
+ b << ((color >> 8) & 0xff);
+ a << (color & 0xff) / 255.0F;
+
+ _filter = g_strdup_printf(
+ "<filter xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\" style=\"color-interpolation-filters:sRGB;\" inkscape:label=\"Nudge CMY\">\n"
+ "<feFlood flood-opacity=\"%s\" flood-color=\"rgb(%s,%s,%s)\" result=\"flood\" />\n"
+ "<feColorMatrix in=\"SourceGraphic\" values=\"0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 -1 0 0 1 0 \" result=\"colormatrix1\" />\n"
+ "<feOffset dy=\"%s\" dx=\"%s\" result=\"offset1\" />\n"
+ "<feBlend in2=\"flood\" mode=\"multiply\" result=\"blend1\" />\n"
+ "<feColorMatrix in=\"SourceGraphic\" values=\"0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 -1 0 1 0 \" result=\"colormatrix2\" />\n"
+ "<feOffset dy=\"%s\" dx=\"%s\" result=\"offset2\" />\n"
+ "<feBlend in2=\"blend1\" mode=\"multiply\" result=\"blend2\" />\n"
+ "<feOffset dy=\"%s\" dx=\"%s\" result=\"offset3\" />\n"
+ "<feColorMatrix in=\"SourceGraphic\" values=\"0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 -1 1 0 \" result=\"colormatrix3\" />\n"
+ "<feBlend in2=\"offset3\" mode=\"multiply\" result=\"blend3\" />\n"
"</filter>\n", a.str().c_str(), r.str().c_str(), g.str().c_str(), b.str().c_str(),
- rx.str().c_str(), ry.str().c_str(), source.str().c_str(), blend.str().c_str(),
- gx.str().c_str(), gy.str().c_str(), blend.str().c_str(),
- bx.str().c_str(), by.str().c_str(), blend.str().c_str(),
- composite.str().c_str() );
+ cx.str().c_str(), cy.str().c_str(),
+ mx.str().c_str(), my.str().c_str(),
+ yx.str().c_str(), yy.str().c_str() );
return _filter;
-}; /* Nudge filter */
+}; /* Nudge CMY filter */
/**
\brief Custom predefined Quadritone filter.
@@ -1325,14 +1407,14 @@ public:
"<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
"<name>" N_("Quadritone fantasy") "</name>\n"
"<id>org.inkscape.effect.filter.Quadritone</id>\n"
- "<param name=\"dist\" gui-text=\"" N_("Hue distribution (°):") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"360\">280</param>\n"
- "<param name=\"colors\" gui-text=\"" N_("Colors:") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"360\">100</param>\n"
+ "<param name=\"dist\" gui-text=\"" N_("Hue distribution (°)") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"360\">280</param>\n"
+ "<param name=\"colors\" gui-text=\"" N_("Colors") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"360\">100</param>\n"
"<param name=\"blend1\" gui-text=\"" N_("Blend 1:") "\" type=\"enum\">\n"
"<_item value=\"normal\">" N_("Normal") "</_item>\n"
"<_item value=\"multiply\">" N_("Multiply") "</_item>\n"
"<_item value=\"screen\">" N_("Screen") "</_item>\n"
"</param>\n"
- "<param name=\"sat\" gui-text=\"" N_("Over-saturation:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.00\" max=\"1.00\">0</param>\n"
+ "<param name=\"sat\" gui-text=\"" N_("Over-saturation") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.00\" max=\"1.00\">0</param>\n"
"<param name=\"blend2\" gui-text=\"" N_("Blend 2:") "\" type=\"enum\">\n"
"<_item value=\"normal\">" N_("Normal") "</_item>\n"
"<_item value=\"screen\">" N_("Screen") "</_item>\n"
@@ -1411,7 +1493,7 @@ public:
"<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
"<name>" N_("Solarize") "</name>\n"
"<id>org.inkscape.effect.filter.Solarize</id>\n"
- "<param name=\"rotate\" gui-text=\"" N_("Hue rotation (°):") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"360\">0</param>\n"
+ "<param name=\"rotate\" gui-text=\"" N_("Hue rotation (°)") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"360\">0</param>\n"
"<param name=\"type\" gui-text=\"" N_("Type:") "\" type=\"enum\">\n"
"<_item value=\"solarize\">" N_("Solarize") "</_item>\n"
"<_item value=\"moonarize\">" N_("Moonarize") "</_item>\n"
@@ -1513,14 +1595,14 @@ public:
"<_item value=\"multiply\">" N_("Multiply") "</_item>\n"
"<_item value=\"darken\">" N_("Darken") "</_item>\n"
"</param>\n"
- "<param name=\"glow\" gui-text=\"" N_("Glow:") "\" type=\"float\" appearance=\"full\" min=\"0.01\" max=\"10\">0.01</param>\n"
+ "<param name=\"glow\" gui-text=\"" N_("Glow") "\" type=\"float\" appearance=\"full\" min=\"0.01\" max=\"10\">0.01</param>\n"
"<param name=\"glowblend\" gui-text=\"" N_("Glow blend:") "\" type=\"enum\">\n"
"<_item value=\"normal\">" N_("Normal") "</_item>\n"
"<_item value=\"multiply\">" N_("Multiply") "</_item>\n"
"<_item value=\"darken\">" N_("Darken") "</_item>\n"
"</param>\n"
- "<param name=\"llight\" gui-text=\"" N_("Local light:") "\" type=\"float\" appearance=\"full\" min=\"0\" max=\"10\">0</param>\n"
- "<param name=\"glight\" gui-text=\"" N_("Global light:") "\" type=\"float\" appearance=\"full\" min=\"0\" max=\"10\">1</param>\n"
+ "<param name=\"llight\" gui-text=\"" N_("Local light") "\" type=\"float\" appearance=\"full\" min=\"0\" max=\"10\">0</param>\n"
+ "<param name=\"glight\" gui-text=\"" N_("Global light") "\" type=\"float\" appearance=\"full\" min=\"0\" max=\"10\">1</param>\n"
"</page>\n"
"<page name=\"co1tab\" _gui-text=\"Color\">\n"
"<param name=\"dist\" gui-text=\"" N_("Hue distribution (°):") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"360\">0</param>\n"
diff --git a/src/extension/internal/filter/distort.h b/src/extension/internal/filter/distort.h
index 415762466..586d34226 100644
--- a/src/extension/internal/filter/distort.h
+++ b/src/extension/internal/filter/distort.h
@@ -70,10 +70,10 @@ public:
"<_item value=\"in\">" N_("In") "</_item>\n"
"<_item value=\"out\">" N_("Out") "</_item>\n"
"</param>\n"
- "<param name=\"hblur\" gui-text=\"" N_("Horizontal blur:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"30.00\">15</param>\n"
- "<param name=\"vblur\" gui-text=\"" N_("Vertical blur:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"30.00\">15</param>\n"
- "<param name=\"dilat\" gui-text=\"" N_("Dilatation:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"100\">1</param>\n"
- "<param name=\"erosion\" gui-text=\"" N_("Erosion:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"100\">0</param>\n"
+ "<param name=\"hblur\" gui-text=\"" N_("Horizontal blur") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"30.00\">15</param>\n"
+ "<param name=\"vblur\" gui-text=\"" N_("Vertical blur") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"30.00\">15</param>\n"
+ "<param name=\"dilat\" gui-text=\"" N_("Dilatation") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"100\">1</param>\n"
+ "<param name=\"erosion\" gui-text=\"" N_("Erosion") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"100\">0</param>\n"
"<param name=\"stroke\" gui-text=\"" N_("Stroke:") "\" type=\"enum\">\n"
"<_item value=\"atop\">" N_("Normal") "</_item>\n"
"<_item value=\"over\">" N_("Wide") "</_item>\n"
@@ -84,11 +84,11 @@ public:
"<_item value=\"fractalNoise\">" N_("Fractal noise") "</_item>\n"
"<_item value=\"turbulence\">" N_("Turbulence") "</_item>\n"
"</param>\n"
- "<param name=\"hfreq\" gui-text=\"" N_("Horizontal frequency:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"100.\">5</param>\n"
- "<param name=\"vfreq\" gui-text=\"" N_("Vertical frequency:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"100.\">5</param>\n"
- "<param name=\"complexity\" gui-text=\"" N_("Complexity:") "\" type=\"int\" appearance=\"full\" min=\"1\" max=\"5\">3</param>\n"
- "<param name=\"variation\" gui-text=\"" N_("Variation:") "\" type=\"int\" appearance=\"full\" min=\"1\" max=\"100\">0</param>\n"
- "<param name=\"intensity\" gui-text=\"" N_("Intensity:") "\" type=\"float\" appearance=\"full\" min=\"0.0\" max=\"100\">30</param>\n"
+ "<param name=\"hfreq\" gui-text=\"" N_("Horizontal frequency") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"100.\">5</param>\n"
+ "<param name=\"vfreq\" gui-text=\"" N_("Vertical frequency") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"100.\">5</param>\n"
+ "<param name=\"complexity\" gui-text=\"" N_("Complexity") "\" type=\"int\" appearance=\"full\" min=\"1\" max=\"5\">3</param>\n"
+ "<param name=\"variation\" gui-text=\"" N_("Variation") "\" type=\"int\" appearance=\"full\" min=\"1\" max=\"100\">0</param>\n"
+ "<param name=\"intensity\" gui-text=\"" N_("Intensity") "\" type=\"float\" appearance=\"full\" min=\"0.0\" max=\"100\">30</param>\n"
"<effect>\n"
"<object-type>all</object-type>\n"
"<effects-menu>\n"
@@ -193,11 +193,11 @@ public:
"<_item value=\"fractalNoise\">" N_("Fractal noise") "</_item>\n"
"<_item value=\"turbulence\">" N_("Turbulence") "</_item>\n"
"</param>\n"
- "<param name=\"hfreq\" gui-text=\"" N_("Horizontal frequency:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.1\" max=\"1000.00\">1.3</param>\n"
- "<param name=\"vfreq\" gui-text=\"" N_("Vertical frequency:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.1\" max=\"1000.00\">1.3</param>\n"
- "<param name=\"complexity\" gui-text=\"" N_("Complexity:") "\" type=\"int\" appearance=\"full\" min=\"1\" max=\"5\">5</param>\n"
- "<param name=\"variation\" gui-text=\"" N_("Variation:") "\" type=\"int\" appearance=\"full\" min=\"1\" max=\"360\">0</param>\n"
- "<param name=\"intensity\" gui-text=\"" N_("Intensity:") "\" type=\"float\" appearance=\"full\" min=\"0.0\" max=\"50\">6.6</param>\n"
+ "<param name=\"hfreq\" gui-text=\"" N_("Horizontal frequency") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.1\" max=\"1000.00\">1.3</param>\n"
+ "<param name=\"vfreq\" gui-text=\"" N_("Vertical frequency") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.1\" max=\"1000.00\">1.3</param>\n"
+ "<param name=\"complexity\" gui-text=\"" N_("Complexity") "\" type=\"int\" appearance=\"full\" min=\"1\" max=\"5\">5</param>\n"
+ "<param name=\"variation\" gui-text=\"" N_("Variation") "\" type=\"int\" appearance=\"full\" min=\"1\" max=\"360\">0</param>\n"
+ "<param name=\"intensity\" gui-text=\"" N_("Intensity") "\" type=\"float\" appearance=\"full\" min=\"0.0\" max=\"50\">6.6</param>\n"
"<effect>\n"
"<object-type>all</object-type>\n"
"<effects-menu>\n"
diff --git a/src/extension/internal/filter/filter-all.cpp b/src/extension/internal/filter/filter-all.cpp
index b5d47ae45..0273d1669 100755..100644
--- a/src/extension/internal/filter/filter-all.cpp
+++ b/src/extension/internal/filter/filter-all.cpp
@@ -64,7 +64,8 @@ Filter::filters_all (void )
Invert::init();
Lighting::init();
LightnessContrast::init();
- Nudge::init();
+ NudgeRGB::init();
+ NudgeCMY::init();
Quadritone::init();
Solarize::init();
Tritone::init();
diff --git a/src/extension/internal/filter/filter-file.cpp b/src/extension/internal/filter/filter-file.cpp
index d129f590c..48e64f089 100644
--- a/src/extension/internal/filter/filter-file.cpp
+++ b/src/extension/internal/filter/filter-file.cpp
@@ -20,6 +20,7 @@
/* System includes */
#include <glibmm/i18n.h>
+#include <glibmm/fileutils.h>
namespace Inkscape {
namespace Extension {
@@ -43,7 +44,7 @@ void
Filter::filters_load_dir (gchar const * dirname, gchar * menuname)
{
if (!dirname) {
- g_warning(_("Null external module directory name. Filters will not be loaded."));
+ g_warning("%s", _("Null external module directory name. Filters will not be loaded."));
return;
}
diff --git a/src/extension/internal/filter/filter.cpp b/src/extension/internal/filter/filter.cpp
index af597685b..a2c565699 100644
--- a/src/extension/internal/filter/filter.cpp
+++ b/src/extension/internal/filter/filter.cpp
@@ -144,11 +144,11 @@ void Filter::effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::Vie
if (filter == NULL) {
Inkscape::XML::Node * newfilterroot = xmldoc->createElement("svg:filter");
+ merge_filters(newfilterroot, filterdoc->root(), xmldoc);
defsrepr->appendChild(newfilterroot);
Glib::ustring url = "url(#"; url += newfilterroot->attribute("id"); url += ")";
- merge_filters(newfilterroot, filterdoc->root(), xmldoc);
Inkscape::GC::release(newfilterroot);
@@ -172,7 +172,7 @@ void Filter::effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::Vie
// no filter
if (filternode == NULL) {
- g_warning("no assoziating filter found!");
+ g_warning("no assigned filter found!");
continue;
}
diff --git a/src/extension/internal/filter/filter.h b/src/extension/internal/filter/filter.h
index 1e8a3be0f..9aaa84aec 100644
--- a/src/extension/internal/filter/filter.h
+++ b/src/extension/internal/filter/filter.h
@@ -19,7 +19,7 @@
namespace Inkscape {
namespace XML {
- class Document;
+ struct Document;
}
namespace Extension {
diff --git a/src/extension/internal/filter/image.h b/src/extension/internal/filter/image.h
index 60e1a1665..00cd2cf21 100644
--- a/src/extension/internal/filter/image.h
+++ b/src/extension/internal/filter/image.h
@@ -53,7 +53,7 @@ public:
"<_item value=\"vertical\">" N_("Vertical lines") "</_item>\n"
"<_item value=\"horizontal\">" N_("Horizontal lines") "</_item>\n"
"</param>\n"
- "<param name=\"level\" gui-text=\"" N_("Level:") "\" type=\"float\" appearance=\"full\" min=\"0.1\" max=\"100.0\">1.0</param>\n"
+ "<param name=\"level\" gui-text=\"" N_("Level") "\" type=\"float\" appearance=\"full\" min=\"0.1\" max=\"100.0\">1.0</param>\n"
"<param name=\"inverted\" gui-text=\"" N_("Invert colors") "\" type=\"boolean\" >false</param>\n"
"<effect>\n"
"<object-type>all</object-type>\n"
diff --git a/src/extension/internal/filter/morphology.h b/src/extension/internal/filter/morphology.h
index 123d912b0..7e10884a1 100644
--- a/src/extension/internal/filter/morphology.h
+++ b/src/extension/internal/filter/morphology.h
@@ -62,11 +62,11 @@ public:
"<_item value=\"over\">" N_("Outer") "</_item>\n"
"<_item value=\"xor\">" N_("Open") "</_item>\n"
"</param>\n"
- "<param name=\"width\" gui-text=\"" N_("Width:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"30.\">10</param>\n"
- "<param name=\"level\" gui-text=\"" N_("Level:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.2\" max=\"2\">1</param>\n"
- "<param name=\"dilat\" gui-text=\"" N_("Dilatation:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"1\" max=\"100\">10</param>\n"
- "<param name=\"erosion\" gui-text=\"" N_("Erosion:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"1\" max=\"100\">1</param>\n"
- "<param name=\"antialias\" gui-text=\"" N_("Antialiasing:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"1\">1</param>\n"
+ "<param name=\"width\" gui-text=\"" N_("Width") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"30.\">10</param>\n"
+ "<param name=\"level\" gui-text=\"" N_("Level") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.2\" max=\"2\">1</param>\n"
+ "<param name=\"dilat\" gui-text=\"" N_("Dilatation") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"1\" max=\"100\">10</param>\n"
+ "<param name=\"erosion\" gui-text=\"" N_("Erosion") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"1\" max=\"100\">1</param>\n"
+ "<param name=\"antialias\" gui-text=\"" N_("Antialiasing") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"1\">1</param>\n"
"<param name=\"content\" gui-text=\"" N_("Blur content") "\" type=\"boolean\" >false</param>\n"
"<effect>\n"
@@ -181,13 +181,13 @@ public:
"<_item value=\"outside\">" N_("Outside") "</_item>\n"
"<_item value=\"overlayed\">" N_("Overlayed") "</_item>\n"
"</param>\n"
- "<param name=\"width1\" gui-text=\"" N_("Width 1:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"20.00\">4</param>\n"
- "<param name=\"dilat1\" gui-text=\"" N_("Dilatation 1:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"1\" max=\"100\">100</param>\n"
- "<param name=\"erosion1\" gui-text=\"" N_("Erosion 1:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"100\">1</param>\n"
- "<param name=\"width2\" gui-text=\"" N_("Width 2:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"20.00\">0.5</param>\n"
- "<param name=\"dilat2\" gui-text=\"" N_("Dilatation 2:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"1\" max=\"100\">50</param>\n"
- "<param name=\"erosion2\" gui-text=\"" N_("Erosion 2:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"100\">5</param>\n"
- "<param name=\"antialias\" gui-text=\"" N_("Antialiasing:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"1\">1</param>\n"
+ "<param name=\"width1\" gui-text=\"" N_("Width 1") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"20.00\">4</param>\n"
+ "<param name=\"dilat1\" gui-text=\"" N_("Dilatation 1") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"1\" max=\"100\">100</param>\n"
+ "<param name=\"erosion1\" gui-text=\"" N_("Erosion 1") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"100\">1</param>\n"
+ "<param name=\"width2\" gui-text=\"" N_("Width 2") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"20.00\">0.5</param>\n"
+ "<param name=\"dilat2\" gui-text=\"" N_("Dilatation 2") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"1\" max=\"100\">50</param>\n"
+ "<param name=\"erosion2\" gui-text=\"" N_("Erosion 2") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"100\">5</param>\n"
+ "<param name=\"antialias\" gui-text=\"" N_("Antialiasing") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"1\">1</param>\n"
"<param name=\"smooth\" gui-text=\"" N_("Smooth") "\" type=\"boolean\" >false</param>\n"
"</page>\n"
"<page name=\"co11tab\" _gui-text=\"Color\">\n"
diff --git a/src/extension/internal/filter/paint.h b/src/extension/internal/filter/paint.h
index debdd7d79..941177f39 100644
--- a/src/extension/internal/filter/paint.h
+++ b/src/extension/internal/filter/paint.h
@@ -3,7 +3,7 @@
/* Change the 'PAINT' above to be your file name */
/*
- * Copyright (C) 2011 Authors:
+ * Copyright (C) 2012 Authors:
* Ivan Louette (filters)
* Nicolas Dufour (UI) <nicoduf@yahoo.fr>
*
@@ -83,19 +83,19 @@ public:
"<param name=\"transparent\" gui-text=\"" N_("Transparent") "\" type=\"boolean\" >false</param>\n"
"<param name=\"dented\" gui-text=\"" N_("Dented") "\" type=\"boolean\" >false</param>\n"
"<param name=\"inverted\" gui-text=\"" N_("Inverted") "\" type=\"boolean\" >false</param>\n"
- "<param name=\"light\" gui-text=\"" N_("Lightness:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"10\">0</param>\n"
- "<param name=\"saturation\" gui-text=\"" N_("Saturation:") "\" type=\"float\" precision=\"2\" appearance=\"full\" min=\"0\" max=\"1\">1</param>\n"
- "<param name=\"noise\" gui-text=\"" N_("Noise reduction:") "\" type=\"int\" appearance=\"full\" min=\"1\" max=\"1000\">10</param>\n"
- "<param name=\"smooth\" gui-text=\"" N_("Smoothness:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"10.00\">1</param>\n"
+ "<param name=\"light\" gui-text=\"" N_("Lightness") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"10\">0</param>\n"
+ "<param name=\"saturation\" gui-text=\"" N_("Saturation") "\" type=\"float\" precision=\"2\" appearance=\"full\" min=\"0\" max=\"1\">1</param>\n"
+ "<param name=\"noise\" gui-text=\"" N_("Noise reduction") "\" type=\"int\" appearance=\"full\" min=\"1\" max=\"1000\">10</param>\n"
+ "<param name=\"smooth\" gui-text=\"" N_("Smoothness") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"10.00\">1</param>\n"
"</page>\n"
- "<page name=\"graintab\" _gui-text=\""N_("Grain") "\">\n"
+ "<page name=\"graintab\" _gui-text=\"" N_("Grain") "\">\n"
"<param name=\"grain\" gui-text=\"" N_("Grain mode") "\" type=\"boolean\" >true</param>\n"
- "<param name=\"grainxf\" gui-text=\"" N_("Horizontal frequency:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"1000\">1000</param>\n"
- "<param name=\"grainyf\" gui-text=\"" N_("Vertical frequency:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"1000\">1000</param>\n"
- "<param name=\"grainc\" gui-text=\"" N_("Complexity:") "\" type=\"int\" appearance=\"full\" min=\"1\" max=\"5\">1</param>\n"
- "<param name=\"grainv\" gui-text=\"" N_("Variation:") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"1000\">0</param>\n"
- "<param name=\"grainexp\" gui-text=\"" N_("Expansion:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"1\" max=\"50\">1</param>\n"
- "<param name=\"grainero\" gui-text=\"" N_("Erosion:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"40\">0</param>\n"
+ "<param name=\"grainxf\" gui-text=\"" N_("Horizontal frequency") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"1000\">1000</param>\n"
+ "<param name=\"grainyf\" gui-text=\"" N_("Vertical frequency") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"1000\">1000</param>\n"
+ "<param name=\"grainc\" gui-text=\"" N_("Complexity") "\" type=\"int\" appearance=\"full\" min=\"1\" max=\"5\">1</param>\n"
+ "<param name=\"grainv\" gui-text=\"" N_("Variation") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"1000\">0</param>\n"
+ "<param name=\"grainexp\" gui-text=\"" N_("Expansion") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"1\" max=\"50\">1</param>\n"
+ "<param name=\"grainero\" gui-text=\"" N_("Erosion") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"40\">0</param>\n"
"<param name=\"graincol\" gui-text=\"" N_("Color") "\" type=\"boolean\" >true</param>\n"
"<param name=\"gblend\" gui-text=\"" N_("Grain blend:") "\" type=\"enum\">\n"
"<_item value=\"normal\">Normal</_item>\n"
@@ -133,7 +133,6 @@ Chromolitho::get_filter_text (Inkscape::Extension::Extension * ext)
std::ostringstream noise;
std::ostringstream dblend;
std::ostringstream smooth;
- std::ostringstream grain;
std::ostringstream grainxf;
std::ostringstream grainyf;
std::ostringstream grainc;
@@ -232,11 +231,11 @@ public:
"<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
"<name>" N_("Cross Engraving") "</name>\n"
"<id>org.inkscape.effect.filter.CrossEngraving</id>\n"
- "<param name=\"clean\" gui-text=\"" N_("Clean-up:") "\" type=\"int\" appearance=\"full\" min=\"1\" max=\"500\">30</param>\n"
- "<param name=\"dilat\" gui-text=\"" N_("Dilatation:") "\" type=\"float\" appearance=\"full\" min=\"1\" max=\"50\">1</param>\n"
- "<param name=\"erosion\" gui-text=\"" N_("Erosion:") "\" type=\"float\" appearance=\"full\" min=\"0\" max=\"50\">0</param>\n"
- "<param name=\"strength\" gui-text=\"" N_("Strength:") "\" type=\"float\" appearance=\"full\" min=\"0.1\" max=\"10\">0.5</param>\n"
- "<param name=\"length\" gui-text=\"" N_("Length:") "\" type=\"float\" appearance=\"full\" min=\"0.5\" max=\"20\">4</param>\n"
+ "<param name=\"clean\" gui-text=\"" N_("Clean-up") "\" type=\"int\" appearance=\"full\" min=\"1\" max=\"500\">30</param>\n"
+ "<param name=\"dilat\" gui-text=\"" N_("Dilatation") "\" type=\"float\" appearance=\"full\" min=\"1\" max=\"50\">1</param>\n"
+ "<param name=\"erosion\" gui-text=\"" N_("Erosion") "\" type=\"float\" appearance=\"full\" min=\"0\" max=\"50\">0</param>\n"
+ "<param name=\"strength\" gui-text=\"" N_("Strength") "\" type=\"float\" appearance=\"full\" min=\"0.1\" max=\"10\">0.5</param>\n"
+ "<param name=\"length\" gui-text=\"" N_("Length") "\" type=\"float\" appearance=\"full\" min=\"0.5\" max=\"20\">4</param>\n"
"<param name=\"trans\" gui-text=\"" N_("Transparent") "\" type=\"boolean\" >false</param>\n"
"<effect>\n"
"<object-type>all</object-type>\n"
@@ -334,18 +333,18 @@ public:
"<param name=\"tab\" type=\"notebook\">\n"
"<page name=\"optionstab\" _gui-text=\"Options\">\n"
"<_param name=\"simplifyheader\" type=\"description\" appearance=\"header\">" N_("Simplify") "</_param>\n"
- "<param name=\"simply\" gui-text=\"" N_("Strength:") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"20.00\">0.6</param>\n"
- "<param name=\"clean\" gui-text=\"" N_("Clean-up:") "\" type=\"int\" indent=\"1\" appearance=\"full\" min=\"1\" max=\"500\">10</param>\n"
- "<param name=\"erase\" gui-text=\"" N_("Erase:") "\" type=\"float\" indent=\"1\" appearance=\"full\" min=\"0\" max=\"60\">0</param>\n"
+ "<param name=\"simply\" gui-text=\"" N_("Strength") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"20.00\">0.6</param>\n"
+ "<param name=\"clean\" gui-text=\"" N_("Clean-up") "\" type=\"int\" indent=\"1\" appearance=\"full\" min=\"1\" max=\"500\">10</param>\n"
+ "<param name=\"erase\" gui-text=\"" N_("Erase") "\" type=\"float\" indent=\"1\" appearance=\"full\" min=\"0\" max=\"60\">0</param>\n"
"<param name=\"translucent\" gui-text=\"" N_("Translucent") "\" indent=\"1\" type=\"boolean\" >false</param>\n"
"<_param name=\"smoothheader\" type=\"description\" appearance=\"header\">" N_("Smoothness") "</_param>\n"
- "<param name=\"smooth\" gui-text=\"" N_("Strength:") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"20.00\">0.6</param>\n"
- "<param name=\"dilat\" gui-text=\"" N_("Dilatation:") "\" type=\"float\" indent=\"1\" appearance=\"full\" min=\"1\" max=\"50\">6</param>\n"
- "<param name=\"erosion\" gui-text=\"" N_("Erosion:") "\" type=\"float\" indent=\"1\" appearance=\"full\" min=\"0\" max=\"50\">2</param>\n"
+ "<param name=\"smooth\" gui-text=\"" N_("Strength") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"20.00\">0.6</param>\n"
+ "<param name=\"dilat\" gui-text=\"" N_("Dilatation") "\" type=\"float\" indent=\"1\" appearance=\"full\" min=\"1\" max=\"50\">6</param>\n"
+ "<param name=\"erosion\" gui-text=\"" N_("Erosion") "\" type=\"float\" indent=\"1\" appearance=\"full\" min=\"0\" max=\"50\">2</param>\n"
"<_param name=\"meltheader\" type=\"description\" appearance=\"header\">" N_("Melt") "</_param>\n"
- "<param name=\"blur\" gui-text=\"" N_("Level:") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"20.00\">1</param>\n"
- "<param name=\"bdilat\" gui-text=\"" N_("Dilatation:") "\" type=\"float\" indent=\"1\" appearance=\"full\" min=\"1\" max=\"50\">6</param>\n"
- "<param name=\"berosion\" gui-text=\"" N_("Erosion:") "\" type=\"float\" indent=\"1\" appearance=\"full\" min=\"0\" max=\"50\">2</param>\n"
+ "<param name=\"blur\" gui-text=\"" N_("Level") "\" type=\"float\" indent=\"1\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"20.00\">1</param>\n"
+ "<param name=\"bdilat\" gui-text=\"" N_("Dilatation") "\" type=\"float\" indent=\"1\" appearance=\"full\" min=\"1\" max=\"50\">6</param>\n"
+ "<param name=\"berosion\" gui-text=\"" N_("Erosion") "\" type=\"float\" indent=\"1\" appearance=\"full\" min=\"0\" max=\"50\">2</param>\n"
"</page>\n"
"<page name=\"co11tab\" _gui-text=\"Fill color\">\n"
"<param name=\"fcolor\" gui-text=\"" N_("Fill color") "\" type=\"color\">-1515870721</param>\n"
@@ -354,7 +353,7 @@ public:
"<page name=\"co12tab\" _gui-text=\"Stroke color\">\n"
"<param name=\"scolor\" gui-text=\"" N_("Stroke color") "\" type=\"color\">589505535</param>\n"
"<param name=\"ios\" gui-text=\"" N_("Image on stroke") "\" type=\"boolean\" >false</param>\n"
- "<param name=\"offset\" gui-text=\"" N_("Offset:") "\" type=\"int\" appearance=\"full\" min=\"-100\" max=\"100\">0</param>\n"
+ "<param name=\"offset\" gui-text=\"" N_("Offset") "\" type=\"int\" appearance=\"full\" min=\"-100\" max=\"100\">0</param>\n"
"</page>\n"
"</param>\n"
"<effect>\n"
@@ -494,12 +493,12 @@ public:
"<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
"<name>" N_("Electrize") "</name>\n"
"<id>org.inkscape.effect.filter.Electrize</id>\n"
- "<param name=\"blur\" gui-text=\"" N_("Simplify:") "\" type=\"float\" appearance=\"full\" min=\"0.01\" max=\"10.0\">2.0</param>\n"
+ "<param name=\"blur\" gui-text=\"" N_("Simplify") "\" type=\"float\" appearance=\"full\" min=\"0.01\" max=\"10.0\">2.0</param>\n"
"<param name=\"type\" gui-text=\"" N_("Effect type:") "\" type=\"enum\">\n"
"<_item value=\"table\">" N_("Table") "</_item>\n"
"<_item value=\"discrete\">" N_("Discrete") "</_item>\n"
"</param>\n"
- "<param name=\"levels\" gui-text=\"" N_("Levels:") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"10\">3</param>\n"
+ "<param name=\"levels\" gui-text=\"" N_("Levels") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"10\">3</param>\n"
"<param name=\"invert\" gui-text=\"" N_("Inverted") "\" type=\"boolean\">false</param>\n"
"<effect>\n"
"<object-type>all</object-type>\n"
@@ -563,13 +562,13 @@ Electrize::get_filter_text (Inkscape::Extension::Extension * ext)
Filter's parameters:
* Lines type (enum, default smooth) ->
- smooth = component1 (type="table"), component2 (type="table"), composite1 (in2="blur2")
- hard = component1 (type="discrete"), component2 (type="discrete"), composite1 (in2="component1")
- * Simplify (0.01->20., default 1.5) -> blur1 (stdDeviation)
- * Line width (0.01->20., default 1.5) -> blur2 (stdDeviation)
- * Lightness (0.->10., default 0.5) -> composite1 (k3)
+ smooth = component2 (type="table"), composite1 (in2="blur2")
+ hard = component2 (type="discrete"), composite1 (in2="component1")
+ * Simplify (0.01->20., default 3) -> blur1 (stdDeviation)
+ * Line width (0.01->20., default 3) -> blur2 (stdDeviation)
+ * Lightness (0.->10., default 1) -> composite1 (k2)
* Blend (enum [normal, multiply, screen], default normal) -> blend (mode)
- * Dark mode (boolean, default false) -> composite1 (true: in2="component2")
+ * Dark mode (boolean, default false) -> composite2 (true: in2="component2")
*/
class NeonDraw : public Inkscape::Extension::Internal::Filter::Filter {
protected:
@@ -588,15 +587,14 @@ public:
"<_item value=\"table\">" N_("Smoothed") "</_item>\n"
"<_item value=\"discrete\">" N_("Contrasted") "</_item>\n"
"</param>\n"
- "<param name=\"simply\" gui-text=\"" N_("Simplify:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"20.00\">1.5</param>\n"
- "<param name=\"width\" gui-text=\"" N_("Line width:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"20.00\">1.5</param>\n"
- "<param name=\"lightness\" gui-text=\"" N_("Lightness:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.00\" max=\"10.00\">0.5</param>\n"
+ "<param name=\"simply\" gui-text=\"" N_("Simplify") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"20.00\">3</param>\n"
+ "<param name=\"width\" gui-text=\"" N_("Line width") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"20.00\">3</param>\n"
+ "<param name=\"lightness\" gui-text=\"" N_("Lightness") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.00\" max=\"10.00\">1</param>\n"
"<param name=\"blend\" gui-text=\"" N_("Blend mode:") "\" type=\"enum\">\n"
"<_item value=\"normal\">Normal</_item>\n"
"<_item value=\"multiply\">Multiply</_item>\n"
"<_item value=\"screen\">Screen</_item>\n"
"</param>\n"
- "<param name=\"dark\" gui-text=\"" N_("Dark mode") "\" type=\"boolean\" >false</param>\n"
"<effect>\n"
"<object-type>all</object-type>\n"
"<effects-menu>\n"
@@ -620,7 +618,6 @@ NeonDraw::get_filter_text (Inkscape::Extension::Extension * ext)
std::ostringstream width;
std::ostringstream lightness;
std::ostringstream type;
- std::ostringstream dark;
type << ext->get_param_enum("type");
blend << ext->get_param_enum("blend");
@@ -628,33 +625,26 @@ NeonDraw::get_filter_text (Inkscape::Extension::Extension * ext)
width << ext->get_param_float("width");
lightness << ext->get_param_float("lightness");
- const gchar *typestr = ext->get_param_enum("type");
- if (ext->get_param_bool("dark")) {
- dark << "component2";
- } else if ((g_ascii_strcasecmp("table", typestr) == 0)) {
- dark << "blur2";
- } else {
- dark << "component1";
- }
-
_filter = g_strdup_printf(
"<filter xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\" style=\"color-interpolation-filters:sRGB;\" inkscape:label=\"Neon Draw\">\n"
"<feBlend mode=\"%s\" result=\"blend\" />\n"
"<feGaussianBlur in=\"blend\" stdDeviation=\"%s\" result=\"blur1\" />\n"
+ "<feColorMatrix values=\"1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 50 0\" result=\"color1\" />\n"
"<feComponentTransfer result=\"component1\">\n"
- "<feFuncR type=\"discrete\" tableValues=\"0 0.3 0.6 1 1\" />\n"
- "<feFuncG type=\"discrete\" tableValues=\"0 0.3 0.6 1 1\" />\n"
- "<feFuncB type=\"discrete\" tableValues=\"0 0.3 0.6 1 1\" />\n"
+ "<feFuncR type=\"discrete\" tableValues=\"0 0.3 0.3 0.3 0.3 0.6 0.6 0.6 0.6 1 1\" />\n"
+ "<feFuncG type=\"discrete\" tableValues=\"0 0.3 0.3 0.3 0.3 0.6 0.6 0.6 0.6 1 1\" />\n"
+ "<feFuncB type=\"discrete\" tableValues=\"0 0.3 0.3 0.3 0.3 0.6 0.6 0.6 0.6 1 1\" />\n"
"</feComponentTransfer>\n"
"<feGaussianBlur in=\"component1\" stdDeviation=\"%s\" result=\"blur2\" />\n"
- "<feComponentTransfer in=\"blur2\" result=\"component2\">\n"
- "<feFuncR type=\"%s\" tableValues=\"0 1 0 1 0 1 0 1\" />\n"
- "<feFuncG type=\"%s\" tableValues=\"0 1 0 1 0 1 0 1\" />\n"
- "<feFuncB type=\"%s\" tableValues=\"0 1 0 1 0 1 0 1\" />\n"
+ "<feColorMatrix values=\"1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 50 0\" result=\"color2\" />\n"
+ "<feComponentTransfer in=\"color2\" result=\"component2\">\n"
+ "<feFuncR type=\"%s\" tableValues=\"0 1 1 1 0 0 0 1 1 1 0 0 0 1 1 1 0 0 0 1\" />\n"
+ "<feFuncG type=\"%s\" tableValues=\"0 1 1 1 0 0 0 1 1 1 0 0 0 1 1 1 0 0 0 1\" />\n"
+ "<feFuncB type=\"%s\" tableValues=\"0 1 1 1 0 0 0 1 1 1 0 0 0 1 1 1 0 0 0 1\" />\n"
"</feComponentTransfer>\n"
- "<feComposite in=\"component2\" in2=\"%s\" k3=\"%s\" operator=\"arithmetic\" k2=\"1\" result=\"composite1\" />\n"
+ "<feComposite in=\"component2\" in2=\"blur2\" k3=\"%s\" operator=\"arithmetic\" k2=\"1\" result=\"composite1\" />\n"
"<feComposite in=\"composite1\" in2=\"SourceGraphic\" operator=\"in\" result=\"composite2\" />\n"
- "</filter>\n", blend.str().c_str(), simply.str().c_str(), width.str().c_str(), type.str().c_str(), type.str().c_str(), type.str().c_str(), dark.str().c_str(), lightness.str().c_str());
+ "</filter>\n", blend.str().c_str(), simply.str().c_str(), width.str().c_str(), type.str().c_str(), type.str().c_str(), type.str().c_str(), lightness.str().c_str());
return _filter;
}; /* NeonDraw filter */
@@ -702,11 +692,11 @@ public:
"<_item value=\"fractalNoise\">" N_("Fractal noise") "</_item>\n"
"<_item value=\"turbulence\">" N_("Turbulence") "</_item>\n"
"</param>\n"
- "<param name=\"hfreq\" gui-text=\"" N_("Horizontal frequency:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.1\" max=\"100.00\">100</param>\n"
- "<param name=\"vfreq\" gui-text=\"" N_("Vertical frequency:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.1\" max=\"100.00\">100</param>\n"
- "<param name=\"complexity\" gui-text=\"" N_("Complexity:") "\" type=\"int\" appearance=\"full\" min=\"1\" max=\"5\">1</param>\n"
- "<param name=\"variation\" gui-text=\"" N_("Variation:") "\" type=\"int\" appearance=\"full\" min=\"1\" max=\"100\">0</param>\n"
- "<param name=\"reduction\" gui-text=\"" N_("Noise reduction:") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"500\">45</param>\n"
+ "<param name=\"hfreq\" gui-text=\"" N_("Horizontal frequency") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.1\" max=\"100.00\">100</param>\n"
+ "<param name=\"vfreq\" gui-text=\"" N_("Vertical frequency") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.1\" max=\"100.00\">100</param>\n"
+ "<param name=\"complexity\" gui-text=\"" N_("Complexity") "\" type=\"int\" appearance=\"full\" min=\"1\" max=\"5\">1</param>\n"
+ "<param name=\"variation\" gui-text=\"" N_("Variation") "\" type=\"int\" appearance=\"full\" min=\"1\" max=\"100\">0</param>\n"
+ "<param name=\"reduction\" gui-text=\"" N_("Noise reduction") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"500\">45</param>\n"
"<param name=\"blend\" gui-text=\"" N_("Noise blend:") "\" type=\"enum\">\n"
"<_item value=\"multiply\">" N_("Multiply") "</_item>\n"
"<_item value=\"normal\">" N_("Normal") "</_item>\n"
@@ -714,10 +704,10 @@ public:
"<_item value=\"lighten\">" N_("Lighten") "</_item>\n"
"<_item value=\"darken\">" N_("Darken") "</_item>\n"
"</param>\n"
- "<param name=\"lightness\" gui-text=\"" N_("Lightness:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"10\">2.5</param>\n"
- "<param name=\"grain\" gui-text=\"" N_("Grain lightness:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"10\">1.3</param>\n"
- "<param name=\"erase\" gui-text=\"" N_("Erase:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"1\">0</param>\n"
- "<param name=\"blur\" gui-text=\"" N_("Blur:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"2\">0.5</param>\n"
+ "<param name=\"lightness\" gui-text=\"" N_("Lightness") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"10\">2.5</param>\n"
+ "<param name=\"grain\" gui-text=\"" N_("Grain lightness") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"10\">1.3</param>\n"
+ "<param name=\"erase\" gui-text=\"" N_("Erase") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0\" max=\"1\">0</param>\n"
+ "<param name=\"blur\" gui-text=\"" N_("Blur") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"2\">0.5</param>\n"
"</page>\n"
"<page name=\"fcolortab\" _gui-text=\"" N_("Fill color") "\">\n"
"<param name=\"fcolor\" gui-text=\"" N_("Color") "\" type=\"color\">-1</param>\n"
@@ -867,7 +857,7 @@ public:
"<_item value=\"discrete\">" N_("Poster") "</_item>\n"
"<_item value=\"table\">" N_("Painting") "</_item>\n"
"</param>\n"
- "<param name=\"levels\" gui-text=\"" N_("Levels:") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"15\">5</param>\n"
+ "<param name=\"levels\" gui-text=\"" N_("Levels") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"15\">5</param>\n"
"<param name=\"blend\" gui-text=\"" N_("Blend mode:") "\" type=\"enum\">\n"
"<_item value=\"lighten\">Lighten</_item>\n"
"<_item value=\"normal\">Normal</_item>\n"
@@ -875,10 +865,10 @@ public:
"<_item value=\"multiply\">Multiply</_item>\n"
"<_item value=\"screen\">Screen</_item>\n"
"</param>\n"
- "<param name=\"blur1\" gui-text=\"" N_("Simplify (primary):") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"100.00\">4.0</param>\n"
- "<param name=\"blur2\" gui-text=\"" N_("Simplify (secondary):") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"100.00\">0.5</param>\n"
- "<param name=\"presaturation\" gui-text=\"" N_("Pre-saturation:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.00\" max=\"1.00\">1.00</param>\n"
- "<param name=\"postsaturation\" gui-text=\"" N_("Post-saturation:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.00\" max=\"1.00\">1.00</param>\n"
+ "<param name=\"blur1\" gui-text=\"" N_("Simplify (primary)") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"100.00\">4.0</param>\n"
+ "<param name=\"blur2\" gui-text=\"" N_("Simplify (secondary)") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"100.00\">0.5</param>\n"
+ "<param name=\"presaturation\" gui-text=\"" N_("Pre-saturation") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.00\" max=\"1.00\">1.00</param>\n"
+ "<param name=\"postsaturation\" gui-text=\"" N_("Post-saturation") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.00\" max=\"1.00\">1.00</param>\n"
"<param name=\"antialiasing\" gui-text=\"" N_("Simulate antialiasing") "\" type=\"boolean\">false</param>\n"
"<effect>\n"
"<object-type>all</object-type>\n"
@@ -982,8 +972,8 @@ public:
"<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
"<name>" N_("Posterize Basic") "</name>\n"
"<id>org.inkscape.effect.filter.PosterizeBasic</id>\n"
- "<param name=\"levels\" gui-text=\"" N_("Levels:") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"20\">5</param>\n"
- "<param name=\"blur\" gui-text=\"" N_("Simplify:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"20.00\">4.0</param>\n"
+ "<param name=\"levels\" gui-text=\"" N_("Levels") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"20\">5</param>\n"
+ "<param name=\"blur\" gui-text=\"" N_("Simplify") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"20.00\">4.0</param>\n"
"<effect>\n"
"<object-type>all</object-type>\n"
"<effects-menu>\n"
@@ -1009,9 +999,8 @@ PosterizeBasic::get_filter_text (Inkscape::Extension::Extension * ext)
transf << "0";
int levels = ext->get_param_int("levels") + 1;
- float val = 0.0;
for ( int step = 1 ; step <= levels ; step++ ) {
- val = (float) step / levels;
+ const float val = (float) step / levels;
transf << " " << val;
}
transf << " 1";
diff --git a/src/extension/internal/filter/protrusions.h b/src/extension/internal/filter/protrusions.h
index 8ba35db62..965a1bdc5 100644
--- a/src/extension/internal/filter/protrusions.h
+++ b/src/extension/internal/filter/protrusions.h
@@ -47,7 +47,7 @@ public:
"<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
"<name>" N_("Snow crest") "</name>\n"
"<id>org.inkscape.effect.filter.snow</id>\n"
- "<param name=\"drift\" gui-text=\"" N_("Drift Size:") "\" type=\"float\" appearance=\"full\" min=\"0.0\" max=\"20.0\">3.5</param>\n"
+ "<param name=\"drift\" gui-text=\"" N_("Drift Size") "\" type=\"float\" appearance=\"full\" min=\"0.0\" max=\"20.0\">3.5</param>\n"
"<effect>\n"
"<object-type>all</object-type>\n"
"<effects-menu>\n"
diff --git a/src/extension/internal/filter/shadows.h b/src/extension/internal/filter/shadows.h
index d76358a96..0d54950bf 100644
--- a/src/extension/internal/filter/shadows.h
+++ b/src/extension/internal/filter/shadows.h
@@ -3,7 +3,7 @@
/* Change the 'SHADOWS' above to be your file name */
/*
- * Copyright (C) 2011 Authors:
+ * Copyright (C) 2013 Authors:
* Ivan Louette (filters)
* Nicolas Dufour (UI) <nicoduf@yahoo.fr>
*
@@ -39,6 +39,7 @@ namespace Filter {
inner = composite1 (operator="out"), composite2 (operator="atop", in1="offset", in2="SourceGraphic")
innercut = composite1 (operator="in"), composite2 (operator="out", in1="offset", in2="SourceGraphic")
outercut = composite1 (operator="out"), composite2 (operator="in", in1="SourceGraphic", in2="offset")
+ shadow = composite1 (operator="out"), composite2 (operator="atop", in1="offset", in2="offset")
* Color (guint, default 0,0,0,127) -> flood (flood-opacity, flood-color)
* Use object's color (boolean, default false) -> composite1 (in1, in2)
*/
@@ -57,14 +58,15 @@ public:
"<id>org.inkscape.effect.filter.ColorDropShadow</id>\n"
"<param name=\"tab\" type=\"notebook\">\n"
"<page name=\"optionstab\" _gui-text=\"" N_("Options") "\">\n"
- "<param name=\"blur\" gui-text=\"" N_("Blur radius (px):") "\" type=\"float\" appearance=\"full\" min=\"0.0\" max=\"200.0\">3.0</param>\n"
- "<param name=\"xoffset\" gui-text=\"" N_("Horizontal offset (px):") "\" type=\"float\" appearance=\"full\" min=\"-50.0\" max=\"50.0\">6.0</param>\n"
- "<param name=\"yoffset\" gui-text=\"" N_("Vertical offset (px):") "\" type=\"float\" appearance=\"full\" min=\"-50.0\" max=\"50.0\">6.0</param>\n"
- "<param name=\"type\" gui-text=\"" N_("Blur type:") "\" type=\"enum\" >\n"
+ "<param name=\"blur\" gui-text=\"" N_("Blur radius (px)") "\" type=\"float\" appearance=\"full\" min=\"0.0\" max=\"200.0\">3.0</param>\n"
+ "<param name=\"xoffset\" gui-text=\"" N_("Horizontal offset (px)") "\" type=\"float\" appearance=\"full\" min=\"-50.0\" max=\"50.0\">6.0</param>\n"
+ "<param name=\"yoffset\" gui-text=\"" N_("Vertical offset (px)") "\" type=\"float\" appearance=\"full\" min=\"-50.0\" max=\"50.0\">6.0</param>\n"
+ "<param name=\"type\" gui-text=\"" N_("Shadow type:") "\" type=\"enum\" >\n"
"<_item value=\"outer\">" N_("Outer") "</_item>\n"
"<_item value=\"inner\">" N_("Inner") "</_item>\n"
"<_item value=\"outercut\">" N_("Outer cutout") "</_item>\n"
"<_item value=\"innercut\">" N_("Inner cutout") "</_item>\n"
+ "<_item value=\"shadow\">" N_("Shadow only") "</_item>\n"
"</param>\n"
"</page>\n"
"<page name=\"coltab\" _gui-text=\"" N_("Blur color") "\">\n"
@@ -151,11 +153,16 @@ ColorizableDropShadow::get_filter_text (Inkscape::Extension::Extension * ext)
comp2op << "out";
comp2in1 << "offset";
comp2in2 << "SourceGraphic";
- } else { //innercut
+ } else if ((g_ascii_strcasecmp("innercut", type) == 0)){
comp1op << "out";
comp1in1 << "flood";
comp1in2 << "SourceGraphic";
comp2op << "in";
+ } else { //shadow
+ comp1op << "in";
+ comp2op << "atop";
+ comp2in1 << "offset";
+ comp2in2 << "offset";
}
_filter = g_strdup_printf(
diff --git a/src/extension/internal/filter/transparency.h b/src/extension/internal/filter/transparency.h
index b29a0a1b3..dcbae26b5 100644
--- a/src/extension/internal/filter/transparency.h
+++ b/src/extension/internal/filter/transparency.h
@@ -129,10 +129,10 @@ public:
"<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
"<name>" N_("Channel Transparency") "</name>\n"
"<id>org.inkscape.effect.filter.ChannelTransparency</id>\n"
- "<param name=\"red\" gui-text=\"" N_("Red:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-10.\" max=\"10.\">-1</param>\n"
- "<param name=\"green\" gui-text=\"" N_("Green:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-10.\" max=\"10.\">0.5</param>\n"
- "<param name=\"blue\" gui-text=\"" N_("Blue:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-10.\" max=\"10.\">0.5</param>\n"
- "<param name=\"alpha\" gui-text=\"" N_("Alpha:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-10.\" max=\"10.\">1</param>\n"
+ "<param name=\"red\" gui-text=\"" N_("Red") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-10.\" max=\"10.\">-1</param>\n"
+ "<param name=\"green\" gui-text=\"" N_("Green") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-10.\" max=\"10.\">0.5</param>\n"
+ "<param name=\"blue\" gui-text=\"" N_("Blue") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-10.\" max=\"10.\">0.5</param>\n"
+ "<param name=\"alpha\" gui-text=\"" N_("Alpha") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"-10.\" max=\"10.\">1</param>\n"
"<param name=\"invert\" gui-text=\"" N_("Inverted") "\" type=\"boolean\">false</param>\n"
"<effect>\n"
"<object-type>all</object-type>\n"
@@ -204,9 +204,9 @@ public:
"<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
"<name>" N_("Light Eraser") "</name>\n"
"<id>org.inkscape.effect.filter.LightEraser</id>\n"
- "<param name=\"expand\" gui-text=\"" N_("Expansion:") "\" type=\"float\" appearance=\"full\" min=\"0\" max=\"1000\">50</param>\n"
- "<param name=\"erode\" gui-text=\"" N_("Erosion:") "\" type=\"float\" appearance=\"full\" min=\"1\" max=\"1000\">100</param>\n"
- "<param name=\"opacity\" gui-text=\"" N_("Global opacity:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.\" max=\"1.\">1</param>\n"
+ "<param name=\"expand\" gui-text=\"" N_("Expansion") "\" type=\"float\" appearance=\"full\" min=\"0\" max=\"1000\">50</param>\n"
+ "<param name=\"erode\" gui-text=\"" N_("Erosion") "\" type=\"float\" appearance=\"full\" min=\"1\" max=\"1000\">100</param>\n"
+ "<param name=\"opacity\" gui-text=\"" N_("Global opacity") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.\" max=\"1.\">1</param>\n"
"<param name=\"invert\" gui-text=\"" N_("Inverted") "\" type=\"boolean\">false</param>\n"
"<effect>\n"
"<object-type>all</object-type>\n"
@@ -278,9 +278,9 @@ public:
"<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
"<name>" N_("Opacity") "</name>\n"
"<id>org.inkscape.effect.filter.Opacity</id>\n"
- "<param name=\"expand\" gui-text=\"" N_("Expansion:") "\" type=\"float\" appearance=\"full\" min=\"1\" max=\"1000\">5</param>\n"
- "<param name=\"erode\" gui-text=\"" N_("Erosion:") "\" type=\"float\" appearance=\"full\" min=\"0\" max=\"1000\">1</param>\n"
- "<param name=\"opacity\" gui-text=\"" N_("Global opacity:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.\" max=\"1.\">1</param>\n"
+ "<param name=\"expand\" gui-text=\"" N_("Expansion") "\" type=\"float\" appearance=\"full\" min=\"1\" max=\"1000\">5</param>\n"
+ "<param name=\"erode\" gui-text=\"" N_("Erosion") "\" type=\"float\" appearance=\"full\" min=\"0\" max=\"1000\">1</param>\n"
+ "<param name=\"opacity\" gui-text=\"" N_("Global opacity") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.\" max=\"1.\">1</param>\n"
"<effect>\n"
"<object-type>all</object-type>\n"
"<effects-menu>\n"
@@ -340,7 +340,7 @@ public:
"<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
"<name>" N_("Silhouette") "</name>\n"
"<id>org.inkscape.effect.filter.Silhouette</id>\n"
- "<param name=\"blur\" gui-text=\"" N_("Blur:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"50.00\">0.01</param>\n"
+ "<param name=\"blur\" gui-text=\"" N_("Blur") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.01\" max=\"50.00\">0.01</param>\n"
"<param name=\"cutout\" gui-text=\"" N_("Cutout") "\" type=\"boolean\">false</param>\n"
"<param name=\"color\" gui-text=\"" N_("Color") "\" type=\"color\">255</param>\n"
"<effect>\n"
diff --git a/src/extension/internal/gdkpixbuf-input.cpp b/src/extension/internal/gdkpixbuf-input.cpp
index 80be2ecc2..a384c7bde 100644
--- a/src/extension/internal/gdkpixbuf-input.cpp
+++ b/src/extension/internal/gdkpixbuf-input.cpp
@@ -1,6 +1,7 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
+#include <boost/scoped_ptr.hpp>
#include <glib/gprintf.h>
#include <glibmm/i18n.h>
#include "document-private.h"
@@ -12,96 +13,111 @@
#include "selection-chemistry.h"
#include "sp-image.h"
#include "document-undo.h"
+#include "util/units.h"
+#include "image-resolution.h"
+#include "display/cairo-utils.h"
#include <set>
namespace Inkscape {
-namespace IO {
-GdkPixbuf* pixbuf_new_from_file( char const *utf8name, GError **error );
-}
-
namespace Extension {
namespace Internal {
-static std::set<Glib::ustring> create_lossy_set()
-{
- std::set<Glib::ustring> lossy;
- lossy.insert(".jpg");
- lossy.insert(".jpeg");
- return lossy;
-}
-
SPDocument *
GdkpixbufInput::open(Inkscape::Extension::Input *mod, char const *uri)
{
- bool embed = false;
+ // Determine whether the image should be embedded
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
- Glib::ustring attr = prefs->getString("/dialogs/import/link");
- if (strcmp(attr.c_str(), "embed") == 0) {
- embed = true;
- } else if (strcmp(attr.c_str(), "link") == 0) {
- embed = false;
- } else {
- embed = (strcmp(mod->get_param_optiongroup("link"), "embed") == 0);
- if (mod->get_param_bool("ask")) {
- prefs->setString("/dialogs/import/link", mod->get_param_optiongroup("link"));
- mod->set_param_bool("ask", false);
+ bool ask = prefs->getBool("/dialogs/import/ask");
+ Glib::ustring link = prefs->getString("/dialogs/import/link");
+ bool forcexdpi = prefs->getBool("/dialogs/import/forcexdpi");
+ Glib::ustring scale = prefs->getString("/dialogs/import/scale");
+ // std::cout << "GkdpixbufInput::open: "
+ // << " ask: " << ask
+ // << ", link: " << link
+ // << ", forcexdpi: " << forcexdpi
+ // << ", scale: " << scale << std::endl;
+ // std::cout << " in preferences: "
+ // << " ask: " << !mod->get_param_bool("do_not_ask")
+ // << ", link: " << mod->get_param_optiongroup("link")
+ // << ", mod_dpi: " << mod->get_param_optiongroup("dpi")
+ // << ", scale: " << mod->get_param_optiongroup("scale") << std::endl;
+ if( ask ) {
+ Glib::ustring mod_link = mod->get_param_optiongroup("link");
+ Glib::ustring mod_dpi = mod->get_param_optiongroup("dpi");
+ bool mod_forcexdpi = ( mod_dpi.compare( "from_default" ) == 0 );
+ Glib::ustring mod_scale = mod->get_param_optiongroup("scale");
+ if( link.compare( mod_link ) != 0 ) {
+ link = mod_link;
+ }
+ prefs->setString("/dialogs/import/link", link );
+ if( forcexdpi != mod_forcexdpi ) {
+ forcexdpi = mod_forcexdpi;
+ }
+ prefs->setBool("/dialogs/import/forcexdpi", forcexdpi );
+ if( scale.compare( mod_scale ) != 0 ) {
+ scale = mod_scale;
}
+ prefs->setString("/dialogs/import/scale", scale );
+ prefs->setBool("/dialogs/import/ask", !mod->get_param_bool("do_not_ask") );
}
-
+ bool embed = ( link.compare( "embed" ) == 0 );
+
SPDocument *doc = NULL;
- GdkPixbuf *pb = Inkscape::IO::pixbuf_new_from_file( uri, NULL );
- static std::set<Glib::ustring> lossy = create_lossy_set();
-
- if (pb) { /* We are readable */
- // TODO revisit: bool is_lossy;
- Glib::ustring mime_type, ext;
- Glib::ustring u = uri;
- std::size_t dotpos = u.rfind('.');
- if (dotpos != Glib::ustring::npos) {
- ext = u.substr(dotpos, Glib::ustring::npos);
- }
+ boost::scoped_ptr<Inkscape::Pixbuf> pb(Inkscape::Pixbuf::create_from_file(uri));
- // HACK: replace with something better based on GIO
- if (!ext.empty() && lossy.find(ext) != lossy.end()) {
- // TODO revisit: is_lossy = true;
- mime_type = "image/jpeg";
- } else {
- // TODO revisit: is_lossy = false;
- mime_type = "image/png";
- }
+ // TODO: the pixbuf is created again from the base64-encoded attribute in SPImage.
+ // Find a way to create the pixbuf only once.
+ if (pb) {
doc = SPDocument::createNewDoc(NULL, TRUE, TRUE);
bool saved = DocumentUndo::getUndoSensitive(doc);
DocumentUndo::setUndoSensitive(doc, false); // no need to undo in this temporary document
- double width = gdk_pixbuf_get_width(pb);
- double height = gdk_pixbuf_get_height(pb);
- gchar const *str = gdk_pixbuf_get_option( pb, "Inkscape::DpiX" );
- if ( str ) {
- gint dpi = atoi(str);
- if ( dpi > 0 && dpi != 72 ) {
- double scale = 72.0 / (double)dpi;
- width *= scale;
- }
+ double width = pb->width();
+ double height = pb->height();
+ double defaultxdpi = prefs->getDouble("/dialogs/import/defaultxdpi/value", Inkscape::Util::Quantity::convert(1, "in", "px"));
+ //bool forcexdpi = prefs->getBool("/dialogs/import/forcexdpi");
+ ImageResolution *ir = 0;
+ double xscale = 1;
+ double yscale = 1;
+
+
+ if (!ir && !forcexdpi) {
+ ir = new ImageResolution(uri);
}
- str = gdk_pixbuf_get_option( pb, "Inkscape::DpiY" );
- if ( str ) {
- gint dpi = atoi(str);
- if ( dpi > 0 && dpi != 72 ) {
- double scale = 72.0 / (double)dpi;
- height *= scale;
- }
+ if (ir && ir->ok()) {
+ xscale = 900.0 / floor(10.*ir->x() + .5); // round-off to 0.1 dpi
+ yscale = 900.0 / floor(10.*ir->y() + .5);
+ } else {
+ xscale = 90.0 / defaultxdpi;
+ yscale = 90.0 / defaultxdpi;
}
+ width *= xscale;
+ height *= yscale;
+
+ delete ir; // deleting NULL is safe
+
// Create image node
Inkscape::XML::Document *xml_doc = doc->getReprDoc();
Inkscape::XML::Node *image_node = xml_doc->createElement("svg:image");
sp_repr_set_svg_double(image_node, "width", width);
sp_repr_set_svg_double(image_node, "height", height);
+ // Added 11 Feb 2014 as we now honor "preserveAspectRatio" and this is
+ // what Inkscaper's expect.
+ image_node->setAttribute("preserveAspectRatio", "none");
+
+ if( scale.compare( "auto" ) != 0 ) {
+ SPCSSAttr *css = sp_repr_css_attr_new();
+ sp_repr_css_set_property(css, "image-rendering", scale.c_str());
+ sp_repr_css_set(image_node, css, "style");
+ sp_repr_css_attr_unref( css );
+ }
+
if (embed) {
- sp_embed_image(image_node, pb, mime_type);
+ sp_embed_image(image_node, pb.get());
} else {
// convert filename to uri
gchar* _uri = g_filename_to_uri(uri, NULL, NULL);
@@ -113,12 +129,17 @@ GdkpixbufInput::open(Inkscape::Extension::Input *mod, char const *uri)
}
}
- g_object_unref(pb);
-
// Add it to the current layer
doc->getRoot()->appendChildRepr(image_node);
Inkscape::GC::release(image_node);
fit_canvas_to_drawing(doc);
+
+ // Set viewBox if it doesn't exist
+ if (!doc->getRoot()->viewBox_set) {
+ std::cout << "Viewbox not set, setting" << std::endl;
+ doc->setViewBox(Geom::Rect::from_xywh(0, 0, doc->getWidth().value(doc->getDefaultUnit()), doc->getHeight().value(doc->getDefaultUnit())));
+ }
+
// restore undo, as now this document may be shown to the user if a bitmap was opened
DocumentUndo::setUndoSensitive(doc, saved);
} else {
@@ -166,12 +187,24 @@ GdkpixbufInput::init(void)
"<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
"<name>%s</name>\n"
"<id>org.inkscape.input.gdkpixbuf.%s</id>\n"
- "<param name='link' type='optiongroup' appearance='full' _gui-text='" N_("Link or embed image:") "' >\n"
+
+ "<param name='link' type='optiongroup' appearance='full' _gui-text='" N_("Image Import Type:") "' _gui-description='" N_("Embed results in stand-alone, larger SVG files. Link references a file outside this SVG document and all files must be moved together.") "' >\n"
"<_option value='embed' >" N_("Embed") "</_option>\n"
"<_option value='link' >" N_("Link") "</_option>\n"
"</param>\n"
- "<_param name='help' type='description'>" N_("Embed results in stand-alone, larger SVG files. Link references a file outside this SVG document and all files must be moved together.") "</_param>\n"
- "<param name=\"ask\" _gui-description='" N_("Hide the dialog next time and always apply the same action.") "' gui-text=\"" N_("Don't ask again") "\" type=\"boolean\" >false</param>\n"
+
+ "<param name='dpi' type='optiongroup' appearance='full' _gui-text='" N_("Image DPI:") "' _gui-description='" N_("Take information from file or use default bitmap import resolution as defined in the preferences.") "' >\n"
+ "<_option value='from_file' >" N_("From file") "</_option>\n"
+ "<_option value='from_default' >" N_("Default import resolution") "</_option>\n"
+ "</param>\n"
+
+ "<param name='scale' type='optiongroup' appearance='full' _gui-text='" N_("Image Rendering Mode:") "' _gui-description='" N_("When an image is upscaled, apply smoothing or keep blocky (pixelated). (Will not work in all browsers.)") "' >\n"
+ "<_option value='auto' >" N_("None (auto)") "</_option>\n"
+ "<_option value='optimizeQuality' >" N_("Smooth (optimizeQuality)") "</_option>\n"
+ "<_option value='optimizeSpeed' >" N_("Blocky (optimizeSpeed)") "</_option>\n"
+ "</param>\n"
+
+ "<param name=\"do_not_ask\" _gui-description='" N_("Hide the dialog next time and always apply the same actions.") "' gui-text=\"" N_("Don't ask again") "\" type=\"boolean\" >false</param>\n"
"<input>\n"
"<extension>.%s</extension>\n"
"<mimetype>%s</mimetype>\n"
diff --git a/src/extension/internal/grid.cpp b/src/extension/internal/grid.cpp
index 56bce2fe8..0059bbec2 100644
--- a/src/extension/internal/grid.cpp
+++ b/src/extension/internal/grid.cpp
@@ -11,6 +11,14 @@
* Released under GNU GPL, read the file 'COPYING' for more information
*/
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H
+#include <glibmm/threads.h>
+#endif
+
#include <gtkmm/box.h>
#include <gtkmm/adjustment.h>
#include <gtkmm/spinbutton.h>
@@ -21,12 +29,14 @@
#include "selection.h"
#include "sp-object.h"
#include "util/glib-list-iterators.h"
+#include "2geom/geom.h"
#include "svg/path-string.h"
#include "extension/effect.h"
#include "extension/system.h"
+#include "util/units.h"
#include "grid.h"
@@ -49,31 +59,31 @@ Grid::load (Inkscape::Extension::Extension */*module*/)
namespace {
Glib::ustring build_lines(Geom::Rect bounding_area,
- float offset[], float spacing[])
+ Geom::Point const &offset, Geom::Point const &spacing)
{
Geom::Point point_offset(0.0, 0.0);
SVG::PathString path_data;
- for ( int axis = 0 ; axis < 2 ; ++axis ) {
+ for ( int axis = Geom::X ; axis <= Geom::Y ; ++axis ) {
point_offset[axis] = offset[axis];
for (Geom::Point start_point = bounding_area.min();
- start_point[axis] + offset[axis] <= (bounding_area.max())[axis];
- start_point[axis] += spacing[axis]) {
+ start_point[axis] + offset[axis] <= (bounding_area.max())[axis];
+ start_point[axis] += spacing[axis]) {
Geom::Point end_point = start_point;
end_point[1-axis] = (bounding_area.max())[1-axis];
path_data.moveTo(start_point + point_offset)
- .lineTo(end_point + point_offset);
+ .lineTo(end_point + point_offset);
}
}
- // std::cout << "Path data:" << path_data.c_str() << std::endl;
- return path_data;
- }
-
+ // std::cout << "Path data:" << path_data.c_str() << std::endl;
+ return path_data;
}
+} // namespace
+
/**
\brief This actually draws the grid.
\param module The effect that was called (unused)
@@ -89,30 +99,31 @@ Grid::effect (Inkscape::Extension::Effect *module, Inkscape::UI::View::View *doc
/* get page size */
SPDocument * doc = document->doc();
bounding_area = Geom::Rect( Geom::Point(0,0),
- Geom::Point(doc->getWidth(), doc->getHeight()) );
+ Geom::Point(doc->getWidth().value("px"), doc->getHeight().value("px")) );
} else {
Geom::OptRect bounds = selection->visualBounds();
if (bounds) {
bounding_area = *bounds;
}
- gdouble doc_height = (document->doc())->getHeight();
+ gdouble doc_height = (document->doc())->getHeight().value("px");
Geom::Rect temprec = Geom::Rect(Geom::Point(bounding_area.min()[Geom::X], doc_height - bounding_area.min()[Geom::Y]),
Geom::Point(bounding_area.max()[Geom::X], doc_height - bounding_area.max()[Geom::Y]));
bounding_area = temprec;
}
- float spacings[2] = { module->get_param_float("xspacing"),
- module->get_param_float("yspacing") };
- float line_width = module->get_param_float("lineWidth");
- float offsets[2] = { module->get_param_float("xoffset"),
- module->get_param_float("yoffset") };
+ gdouble scale = Inkscape::Util::Quantity::convert(1, "px", (document->doc())->getDefaultUnit());
+ bounding_area *= Geom::Scale(scale);
+ Geom::Point spacings( scale * module->get_param_float("xspacing"),
+ scale * module->get_param_float("yspacing") );
+ gdouble line_width = scale * module->get_param_float("lineWidth");
+ Geom::Point offsets( scale * module->get_param_float("xoffset"),
+ scale * module->get_param_float("yoffset") );
Glib::ustring path_data("");
- path_data = build_lines(bounding_area,
- offsets, spacings);
+ path_data = build_lines(bounding_area, offsets, spacings);
Inkscape::XML::Document * xml_doc = document->doc()->getReprDoc();
//XML Tree being used directly here while it shouldn't be.
@@ -132,9 +143,7 @@ Grid::effect (Inkscape::Extension::Effect *module, Inkscape::UI::View::View *doc
path->setAttribute("style", style.c_str());
current_layer->appendChild(path);
- Inkscape::GC::release(path);
-
- return;
+ Inkscape::GC::release(path);
}
/** \brief A class to make an adjustment that uses Extension params */
diff --git a/src/extension/internal/image-resolution.cpp b/src/extension/internal/image-resolution.cpp
new file mode 100644
index 000000000..18eb97f80
--- /dev/null
+++ b/src/extension/internal/image-resolution.cpp
@@ -0,0 +1,380 @@
+/*
+ * Authors:
+ * Daniel Wagenaar <daw@caltech.edu>
+ *
+ * Copyright (C) 2012 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "util/units.h"
+#include "image-resolution.h"
+
+#define IR_TRY_PNG 1
+#include <png.h>
+
+#ifdef HAVE_EXIF
+#include <math.h>
+#include <libexif/exif-data.h>
+#endif
+
+#define IR_TRY_EXIV 0
+
+#ifdef HAVE_JPEG
+#define IR_TRY_JFIF 1
+#include <jpeglib.h>
+#include <setjmp.h>
+#endif
+
+#ifdef WITH_IMAGE_MAGICK
+#include <Magick++.h>
+#endif
+
+namespace Inkscape {
+namespace Extension {
+namespace Internal {
+
+ImageResolution::ImageResolution(char const *fn) {
+ ok_ = false;
+
+ readpng(fn);
+ if (!ok_) {
+ readexiv(fn);
+ }
+ if (!ok_) {
+ readjfif(fn);
+ }
+ if (!ok_) {
+ readexif(fn);
+ }
+ if (!ok_) {
+ readmagick(fn);
+ }
+}
+
+bool ImageResolution::ok() const {
+ return ok_;
+}
+
+double ImageResolution::x() const {
+ return x_;
+}
+
+double ImageResolution::y() const {
+ return y_;
+}
+
+
+
+#if IR_TRY_PNG
+
+static bool haspngheader(FILE *fp) {
+ unsigned char header[8];
+ if ( fread(header, 1, 8, fp) != 8 ) {
+ return false;
+ }
+
+ fseek(fp, 0, SEEK_SET);
+
+ if (png_sig_cmp(header, 0, 8)) {
+ return false;
+ }
+
+ return true;
+}
+
+// Implementation using libpng
+void ImageResolution::readpng(char const *fn) {
+ FILE *fp = fopen(fn, "rb");
+ if (!fp)
+ return;
+
+ if (!haspngheader(fp)) {
+ fclose(fp);
+ return;
+ }
+
+ png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
+ if (!png_ptr)
+ return;
+
+ png_infop info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_read_struct(&png_ptr, 0, 0);
+ return;
+ }
+
+ if (setjmp(png_jmpbuf(png_ptr))) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, 0);
+ fclose(fp);
+ return;
+ }
+
+ png_init_io(png_ptr, fp);
+ png_read_info(png_ptr, info_ptr);
+
+ png_uint_32 res_x, res_y;
+ int unit_type;
+ png_get_pHYs(png_ptr, info_ptr, &res_x, &res_y, &unit_type);
+
+ png_destroy_read_struct(&png_ptr, &info_ptr, 0);
+ fclose(fp);
+
+ if (unit_type == PNG_RESOLUTION_METER) {
+ ok_ = true;
+ x_ = res_x * 2.54 / 100;
+ y_ = res_y * 2.54 / 100;
+ }
+}
+#else
+
+// Dummy implementation
+void ImageResolution::readpng(char const *) {
+}
+
+#endif
+
+#if IR_TRY_EXIF
+
+static double exifDouble(ExifEntry *entry, ExifByteOrder byte_order) {
+ switch (entry->format) {
+ case EXIF_FORMAT_BYTE: {
+ return double(entry->data[0]);
+ }
+ case EXIF_FORMAT_SHORT: {
+ return double(exif_get_short(entry->data, byte_order));
+ }
+ case EXIF_FORMAT_LONG: {
+ return double(exif_get_long(entry->data, byte_order));
+ }
+ case EXIF_FORMAT_RATIONAL: {
+ ExifRational r = exif_get_rational(entry->data, byte_order);
+ return double(r.numerator) / double(r.denominator);
+ }
+ case EXIF_FORMAT_SBYTE: {
+ return double(*(signed char *)entry->data);
+ }
+ case EXIF_FORMAT_SSHORT: {
+ return double(exif_get_sshort(entry->data, byte_order));
+ }
+ case EXIF_FORMAT_SLONG: {
+ return double(exif_get_slong(entry->data, byte_order));
+ }
+ case EXIF_FORMAT_SRATIONAL: {
+ ExifSRational r = exif_get_srational(entry->data, byte_order);
+ return double(r.numerator) / double(r.denominator);
+ }
+ case EXIF_FORMAT_FLOAT: {
+ return double((reinterpret_cast<float *>(entry->data))[0]);
+ }
+ case EXIF_FORMAT_DOUBLE: {
+ return (reinterpret_cast<double *>(entry->data))[0];
+ }
+ default: {
+ return nan(0);
+ }
+ }
+}
+
+// Implementation using libexif
+void ImageResolution::readexif(char const *fn) {
+ ExifData *ed;
+ ed = exif_data_new_from_file(fn);
+ if (!ed)
+ return;
+
+ ExifByteOrder byte_order = exif_data_get_byte_order(ed);
+
+ ExifEntry *xres = exif_content_get_entry(ed->ifd[EXIF_IFD_0], EXIF_TAG_X_RESOLUTION);
+ ExifEntry *yres = exif_content_get_entry(ed->ifd[EXIF_IFD_0], EXIF_TAG_Y_RESOLUTION);
+ ExifEntry *unit = exif_content_get_entry(ed->ifd[EXIF_IFD_0], EXIF_TAG_RESOLUTION_UNIT);
+
+ if ( xres && yres ) {
+ x_ = exifDouble(xres, byte_order);
+ y_ = exifDouble(yres, byte_order);
+ if (unit) {
+ double u = exifDouble(unit, byte_order);
+ if ( u == 3 ) {
+ x_ *= 2.54;
+ y_ *= 2.54;
+ }
+ }
+ ok_ = true;
+ }
+ exif_data_free(ed);
+}
+
+#else
+
+// Dummy implementation
+void ImageResolution::readexif(char const *) {
+}
+
+#endif
+
+#if IR_TRY_EXIV
+
+void ImageResolution::readexiv(char const *fn) {
+ Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(fn);
+ if (!image.get())
+ return;
+
+ image->readMetadata();
+ Exiv2::ExifData &exifData = image->exifData();
+ if (exifData.empty())
+ return;
+
+ Exiv2::ExifData::const_iterator end = exifData.end();
+ bool havex = false;
+ bool havey = false;
+ bool haveunit = false;
+ int unit;
+ for (Exiv2::ExifData::const_iterator i = exifData.begin(); i != end; ++i) {
+ if (ok_)
+ break;
+ if ( i->tag() == 0x011a ) {
+ // X Resolution
+ x_ = i->toFloat();
+ havex = true;
+ } else if ( i->tag() == 0x011b ) {
+ // Y Resolution
+ y_ = i->toFloat();
+ havey = true;
+ } else if ( i->tag() == 0x0128 ) {
+ unit = i->toLong();
+ }
+ ok_ = havex && havey && haveunit;
+ }
+ if (haveunit) {
+ if ( unit == 3 ) {
+ x_ *= 2.54;
+ y_ *= 2.54;
+ }
+ }
+ ok_ = havex && havey;
+}
+
+#else
+
+// Dummy implementation
+void ImageResolution::readexiv(char const *) {
+}
+
+#endif
+
+#if IR_TRY_JFIF
+
+static void irjfif_error_exit(j_common_ptr cinfo) {
+ longjmp(*(jmp_buf*)cinfo->client_data, 1);
+}
+
+static void irjfif_emit_message(j_common_ptr, int) {
+}
+
+static void irjfif_output_message(j_common_ptr) {
+}
+
+static void irjfif_format_message(j_common_ptr, char *) {
+}
+
+static void irjfif_reset(j_common_ptr) {
+}
+
+void ImageResolution::readjfif(char const *fn) {
+ FILE *ifd = fopen(fn, "rb");
+ if (!ifd) {
+ return;
+ }
+
+ struct jpeg_decompress_struct cinfo;
+ jmp_buf jbuf;
+ struct jpeg_error_mgr jerr;
+
+ if (setjmp(jbuf)) {
+ fclose(ifd);
+ jpeg_destroy_decompress(&cinfo);
+ return;
+ }
+
+ cinfo.err = jpeg_std_error(&jerr);
+ jpeg_create_decompress(&cinfo);
+ jerr.error_exit = &irjfif_error_exit;
+ jerr.emit_message = &irjfif_emit_message;
+ jerr.output_message = &irjfif_output_message;
+ jerr.format_message = &irjfif_format_message;
+ jerr.reset_error_mgr = &irjfif_reset;
+ cinfo.client_data = (void*)&jbuf;
+
+ jpeg_stdio_src(&cinfo, ifd);
+ jpeg_read_header(&cinfo, TRUE);
+
+ if (cinfo.saw_JFIF_marker) { // JFIF APP0 marker was seen
+ if ( cinfo.density_unit == 1 ) { // dots/inch
+ x_ = cinfo.X_density;
+ y_ = cinfo.Y_density;
+ ok_ = true;
+ } else if ( cinfo.density_unit == 2 ) { // dots/cm
+ x_ = cinfo.X_density * 2.54;
+ y_ = cinfo.Y_density * 2.54;
+ ok_ = true;
+ }
+ /* According to http://www.jpeg.org/public/jfif.pdf (page 7):
+ * "Xdensity and Ydensity should always be non-zero".
+ * but in some cases, they are (see LP bug #1275443) */
+ if (x_ == 0 or y_ == 0) {
+ ok_ = false;
+ }
+ }
+ jpeg_destroy_decompress(&cinfo);
+ fclose(ifd);
+}
+
+#else
+
+// Dummy implementation
+void ImageResolution::readjfif(char const *) {
+}
+
+#endif
+
+#ifdef WITH_IMAGE_MAGICK
+void ImageResolution::readmagick(char const *fn) {
+ Magick::Image image;
+ try {
+ image.read(fn);
+ } catch (Magick::Error & err) {
+ g_warning("ImageMagick error: %s", err.what());
+ return;
+ } catch (...) {
+ g_warning("ImageResolution::readmagick: Unknown error");
+ return;
+ }
+ Magick::Geometry geo = image.density();
+ std::string type = image.magick();
+
+ if (type == "PNG") { // PNG only supports pixelspercentimeter
+ x_ = Inkscape::Util::Quantity::convert((double)geo.width(), "in", "cm");
+ y_ = Inkscape::Util::Quantity::convert((double)geo.height(), "in", "cm");
+ } else {
+ x_ = (double)geo.width();
+ y_ = (double)geo.height();
+ }
+
+ ok_ = true;
+}
+
+#else
+
+// Dummy implementation
+void ImageResolution::readmagick(char const *) {
+}
+
+#endif /* WITH_IMAGE_MAGICK */
+
+}
+}
+}
diff --git a/src/extension/internal/image-resolution.h b/src/extension/internal/image-resolution.h
new file mode 100644
index 000000000..6c4928a35
--- /dev/null
+++ b/src/extension/internal/image-resolution.h
@@ -0,0 +1,41 @@
+/*
+ * Authors:
+ * Daniel Wagenaar <daw@caltech.edu>
+ *
+ * Copyright (C) 2012 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+
+#ifndef IMAGE_RESOLUTION_H
+
+#define IMAGE_RESOLUTION_H
+
+namespace Inkscape {
+namespace Extension {
+namespace Internal {
+
+class ImageResolution {
+public:
+ ImageResolution(char const *fn);
+ bool ok() const;
+ double x() const;
+ double y() const;
+private:
+ bool ok_;
+ double x_;
+ double y_;
+private:
+ void readpng(char const *fn);
+ void readexif(char const *fn);
+ void readexiv(char const *fn);
+ void readjfif(char const *fn);
+ void readmagick(char const *fn);
+};
+
+}
+}
+}
+
+#endif
diff --git a/src/extension/internal/javafx-out.cpp b/src/extension/internal/javafx-out.cpp
index 67c0fae66..19946022c 100644
--- a/src/extension/internal/javafx-out.cpp
+++ b/src/extension/internal/javafx-out.cpp
@@ -20,6 +20,8 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
+
+#include <glibmm/miscutils.h>
#include "javafx-out.h"
#include <inkscape.h>
#include <inkscape-version.h>
@@ -394,12 +396,14 @@ bool JavaFXOutput::doStyle(SPStyle *style)
}
else if (fill.isPaintserver()){
if (fill.value.href && fill.value.href->getURI() ){
- String uri = fill.value.href->getURI()->toString();
+ gchar *str = fill.value.href->getURI()->toString();
+ String uri = (str ? str : "");
/* trim the anchor '#' from the front */
if (uri.size() > 0 && uri[0]=='#') {
uri = uri.substr(1);
}
out(" fill: %s()\n", sanatize(uri).c_str());
+ g_free(str);
}
}
@@ -414,10 +418,8 @@ bool JavaFXOutput::doStyle(SPStyle *style)
* SPIEnum stroke_linecap;
* SPIEnum stroke_linejoin;
* SPIFloat stroke_miterlimit;
- * NRVpathDash stroke_dash;
- * unsigned stroke_dasharray_set : 1;
- * unsigned stroke_dasharray_inherit : 1;
- * unsigned stroke_dashoffset_set : 1;
+ * SPIDashArray stroke_dasharray;
+ * SPILength stroke_dashoffset;
* SPIScale24 stroke_opacity;
*/
if (style->stroke_opacity.value > 0)
@@ -432,16 +434,16 @@ bool JavaFXOutput::doStyle(SPStyle *style)
out(" strokeLineCap: %s\n", getStrokeLineCap(linecap).c_str());
out(" strokeLineJoin: %s\n", getStrokeLineJoin(linejoin).c_str());
out(" strokeMiterLimit: %s\n", DSTR(style->stroke_miterlimit.value));
- if (style->stroke_dasharray_set) {
- if (style->stroke_dashoffset_set) {
- out(" strokeDashOffset: %s\n", DSTR(style->stroke_dash.offset));
+ if (style->stroke_dasharray.set) {
+ if (style->stroke_dashoffset.set) {
+ out(" strokeDashOffset: %s\n", DSTR(style->stroke_dashoffset.value));
}
out(" strokeDashArray: [ ");
- for(int i = 0; i < style->stroke_dash.n_dash; i++ ) {
+ for(unsigned i = 0; i < style->stroke_dasharray.values.size(); i++ ) {
if (i > 0) {
- out(", %.2lf", style->stroke_dash.dash[i]);
+ out(", %.2lf", style->stroke_dasharray.values[i]);
}else {
- out(" %.2lf", style->stroke_dash.dash[i]);
+ out(" %.2lf", style->stroke_dasharray.values[i]);
}
}
out(" ]\n");
diff --git a/src/extension/internal/latex-pstricks.cpp b/src/extension/internal/latex-pstricks.cpp
index c1eddf539..6aaa1bca4 100644
--- a/src/extension/internal/latex-pstricks.cpp
+++ b/src/extension/internal/latex-pstricks.cpp
@@ -21,7 +21,7 @@
#include <2geom/hvlinesegment.h>
#include <errno.h>
#include <signal.h>
-#include <unit-constants.h>
+#include "util/units.h"
#include "helper/geom-curves.h"
#include "extension/print.h"
@@ -117,8 +117,8 @@ unsigned int PrintLatex::begin (Inkscape::Extension::Print *mod, SPDocument *doc
}
// width and height in pt
- _width = doc->getWidth() * PT_PER_PX;
- _height = doc->getHeight() * PT_PER_PX;
+ _width = doc->getWidth().value("pt");
+ _height = doc->getHeight().value("pt");
if (res >= 0) {
@@ -128,10 +128,10 @@ unsigned int PrintLatex::begin (Inkscape::Extension::Print *mod, SPDocument *doc
os << "\\psset{xunit=.5pt,yunit=.5pt,runit=.5pt}\n";
// from now on we can output px, but they will be treated as pt
- os << "\\begin{pspicture}(" << doc->getWidth() << "," << doc->getHeight() << ")\n";
+ os << "\\begin{pspicture}(" << doc->getWidth().value("px") << "," << doc->getHeight().value("px") << ")\n";
}
- m_tr_stack.push( Geom::Scale(1, -1) * Geom::Translate(0, doc->getHeight())); /// @fixme hardcoded doc2dt transform
+ m_tr_stack.push( Geom::Scale(1, -1) * Geom::Translate(0, doc->getHeight().value("px"))); /// @fixme hardcoded doc2dt transform
return fprintf(_stream, "%s", os.str().c_str());
}
@@ -239,16 +239,13 @@ unsigned int PrintLatex::stroke(Inkscape::Extension::Print * /*mod*/,
os<<",strokeopacity="<<stroke_opacity;
}
- if (style->stroke_dasharray_set &&
- style->stroke_dash.n_dash &&
- style->stroke_dash.dash) {
- int i;
+ if (style->stroke_dasharray.set && !style->stroke_dasharray.values.empty()) {
os << ",linestyle=dashed,dash=";
- for (i = 0; i < style->stroke_dash.n_dash; i++) {
+ for (unsigned i = 0; i < style->stroke_dasharray.values.size(); i++) {
if ((i)) {
os << " ";
}
- os << style->stroke_dash.dash[i];
+ os << style->stroke_dasharray.values[i];
}
}
diff --git a/src/extension/internal/latex-text-renderer.cpp b/src/extension/internal/latex-text-renderer.cpp
index ebd73a033..398c9f061 100644
--- a/src/extension/internal/latex-text-renderer.cpp
+++ b/src/extension/internal/latex-text-renderer.cpp
@@ -39,7 +39,7 @@
#include "sp-rect.h"
#include "text-editing.h"
-#include <unit-constants.h>
+#include "util/units.h"
#include "extension/system.h"
@@ -56,7 +56,7 @@ namespace Internal {
*/
bool
latex_render_document_text_to_file( SPDocument *doc, gchar const *filename,
- const gchar * const exportId, bool exportDrawing, bool exportCanvas,
+ const gchar * const exportId, bool exportDrawing, bool exportCanvas, float bleedmargin_px,
bool pdflatex)
{
doc->ensureUpToDate();
@@ -84,7 +84,7 @@ latex_render_document_text_to_file( SPDocument *doc, gchar const *filename,
bool ret = renderer->setTargetFile(filename);
if (ret) {
/* Render document */
- bool ret = renderer->setupDocument(doc, pageBoundingBox, base);
+ bool ret = renderer->setupDocument(doc, pageBoundingBox, bleedmargin_px, base);
if (ret) {
renderer->renderItem(base);
}
@@ -98,7 +98,9 @@ latex_render_document_text_to_file( SPDocument *doc, gchar const *filename,
LaTeXTextRenderer::LaTeXTextRenderer(bool pdflatex)
: _stream(NULL),
_filename(NULL),
- _pdflatex(pdflatex)
+ _pdflatex(pdflatex),
+ _omittext_state(EMPTY),
+ _omittext_page(1)
{
push_transform(Geom::identity());
}
@@ -262,6 +264,11 @@ LaTeXTextRenderer::sp_use_render(SPItem *item)
void
LaTeXTextRenderer::sp_text_render(SPItem *item)
{
+ // Only PDFLaTeX supports importing a single page of a graphics file,
+ // so only PDF backend gets interleaved text/graphics
+ if (_pdflatex && _omittext_state == GRAPHIC_ON_TOP)
+ _omittext_state = NEW_PAGE_ON_GRAPHIC;
+
SPText *textobj = SP_TEXT (item);
SPStyle *style = item->style;
@@ -395,6 +402,11 @@ Flowtext is possible by using a minipage! :)
Flowing in rectangle is possible, not in arb shape.
*/
+ // Only PDFLaTeX supports importing a single page of a graphics file,
+ // so only PDF backend gets interleaved text/graphics
+ if (_pdflatex && _omittext_state == GRAPHIC_ON_TOP)
+ _omittext_state = NEW_PAGE_ON_GRAPHIC;
+
SPFlowtext *flowtext = SP_FLOWTEXT(item);
SPStyle *style = item->style;
@@ -405,7 +417,7 @@ Flowing in rectangle is possible, not in arb shape.
}
SPRect *frame = SP_RECT(frame_item);
- Geom::Rect framebox = sp_rect_get_rect(frame) * transform();
+ Geom::Rect framebox = frame->getRect() * transform();
// get position and alignment
// Align on topleft corner.
@@ -556,8 +568,13 @@ LaTeXTextRenderer::sp_item_invoke_render(SPItem *item)
return sp_text_render(item);
} else if (SP_IS_FLOWTEXT(item)) {
return sp_flowtext_render(item);
+ } else {
+ // Only PDFLaTeX supports importing a single page of a graphics file,
+ // so only PDF backend gets interleaved text/graphics
+ if (_pdflatex && (_omittext_state == EMPTY || _omittext_state == NEW_PAGE_ON_GRAPHIC))
+ writeGraphicPage();
+ _omittext_state = GRAPHIC_ON_TOP;
}
- // We are not interested in writing the other SPItem types to LaTeX
}
void
@@ -568,8 +585,22 @@ LaTeXTextRenderer::renderItem(SPItem *item)
pop_transform();
}
+void
+LaTeXTextRenderer::writeGraphicPage(void) {
+ Inkscape::SVGOStringStream os;
+ os.setf(std::ios::fixed); // no scientific notation
+
+ // strip pathname, as it is probably desired. Having a specific path in the TeX file is not convenient.
+ if (_pdflatex)
+ os << " \\put(0,0){\\includegraphics[width=\\unitlength,page=" << _omittext_page++ << "]{" << _filename << "}}%\n";
+ else
+ os << " \\put(0,0){\\includegraphics[width=\\unitlength]{" << _filename << "}}%\n";
+
+ fprintf(_stream, "%s", os.str().c_str());
+}
+
bool
-LaTeXTextRenderer::setupDocument(SPDocument *doc, bool pageBoundingBox, SPItem *base)
+LaTeXTextRenderer::setupDocument(SPDocument *doc, bool pageBoundingBox, float bleedmargin_px, SPItem *base)
{
// The boundingbox calculation here should be exactly the same as the one by CairoRenderer::setupDocument !
@@ -588,6 +619,7 @@ LaTeXTextRenderer::setupDocument(SPDocument *doc, bool pageBoundingBox, SPItem *
}
d = *bbox;
}
+ d.expandBy(bleedmargin_px);
// scale all coordinates, such that the width of the image is 1, this is convenient for scaling the image in LaTeX
double scale = 1/(d.width());
@@ -601,7 +633,7 @@ LaTeXTextRenderer::setupDocument(SPDocument *doc, bool pageBoundingBox, SPItem *
}
// flip y-axis
- push_transform( Geom::Scale(1,-1) * Geom::Translate(0, doc->getHeight()) ); /// @fixme hardcoded desktop transform!
+ push_transform( Geom::Scale(1,-1) * Geom::Translate(0, doc->getHeight().value("px")) ); /// @fixme hardcoded desktop transform!
// write the info to LaTeX
Inkscape::SVGOStringStream os;
@@ -610,7 +642,7 @@ LaTeXTextRenderer::setupDocument(SPDocument *doc, bool pageBoundingBox, SPItem *
// scaling of the image when including it in LaTeX
os << " \\ifx\\svgwidth\\undefined%\n";
- os << " \\setlength{\\unitlength}{" << d.width() * PT_PER_PX << "bp}%\n"; // note: 'bp' is the Postscript pt unit in LaTeX, see LP bug #792384
+ os << " \\setlength{\\unitlength}{" << Inkscape::Util::Quantity::convert(d.width(), "px", "pt") << "bp}%\n"; // note: 'bp' is the Postscript pt unit in LaTeX, see LP bug #792384
os << " \\ifx\\svgscale\\undefined%\n";
os << " \\relax%\n";
os << " \\else%\n";
@@ -624,11 +656,12 @@ LaTeXTextRenderer::setupDocument(SPDocument *doc, bool pageBoundingBox, SPItem *
os << " \\makeatother%\n";
os << " \\begin{picture}(" << _width << "," << _height << ")%\n";
- // strip pathname, as it is probably desired. Having a specific path in the TeX file is not convenient.
- os << " \\put(0,0){\\includegraphics[width=\\unitlength]{" << _filename << "}}%\n";
fprintf(_stream, "%s", os.str().c_str());
+ if (!_pdflatex)
+ writeGraphicPage();
+
return true;
}
diff --git a/src/extension/internal/latex-text-renderer.h b/src/extension/internal/latex-text-renderer.h
index 66055a3bc..77d12bc61 100644
--- a/src/extension/internal/latex-text-renderer.h
+++ b/src/extension/internal/latex-text-renderer.h
@@ -22,14 +22,14 @@
#include <stack>
class SPItem;
-struct SPRoot;
+class SPRoot;
namespace Inkscape {
namespace Extension {
namespace Internal {
bool latex_render_document_text_to_file(SPDocument *doc, gchar const *filename,
- const gchar * const exportId, bool exportDrawing, bool exportCanvas,
+ const gchar * const exportId, bool exportDrawing, bool exportCanvas, float bleedmargin_px,
bool pdflatex);
class LaTeXTextRenderer {
@@ -41,17 +41,26 @@ public:
/** Initializes the LaTeXTextRenderer according to the specified
SPDocument. Important to set the boundingbox to the pdf boundingbox */
- bool setupDocument(SPDocument *doc, bool pageBoundingBox, SPItem *base);
+ bool setupDocument(SPDocument *doc, bool pageBoundingBox, float bleedmargin_px, SPItem *base);
/** Traverses the object tree and invokes the render methods. */
void renderItem(SPItem *item);
protected:
+ enum LaTeXOmitTextPageState {
+ EMPTY,
+ GRAPHIC_ON_TOP,
+ NEW_PAGE_ON_GRAPHIC
+ };
+
FILE * _stream;
gchar * _filename;
bool _pdflatex; /** true if ouputting for pdfLaTeX*/
+ LaTeXOmitTextPageState _omittext_state;
+ gulong _omittext_page;
+
void push_transform(Geom::Affine const &transform);
Geom::Affine const & transform();
void pop_transform();
@@ -60,6 +69,8 @@ protected:
void writePreamble();
void writePostamble();
+ void writeGraphicPage();
+
void sp_item_invoke_render(SPItem *item);
void sp_root_render(SPRoot *item);
void sp_group_render(SPItem *item);
diff --git a/src/extension/internal/metafile-inout.cpp b/src/extension/internal/metafile-inout.cpp
new file mode 100644
index 000000000..1d419a6a0
--- /dev/null
+++ b/src/extension/internal/metafile-inout.cpp
@@ -0,0 +1,211 @@
+/** @file
+ * @brief Metafile input - common routines
+ *//*
+ * Authors:
+ * David Mathog
+ *
+ * Copyright (C) 2013 Authors
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <cstring>
+#include <fstream>
+#include <glib.h>
+#include <glibmm/miscutils.h>
+
+#include "display/curve.h"
+#include "extension/internal/metafile-inout.h" // picks up PNG
+#include "extension/print.h"
+#include "path-prefix.h"
+#include "sp-gradient.h"
+#include "sp-image.h"
+#include "sp-linear-gradient.h"
+#include "sp-pattern.h"
+#include "sp-radial-gradient.h"
+#include "style.h"
+
+namespace Inkscape {
+namespace Extension {
+namespace Internal {
+
+Metafile::~Metafile()
+{
+ return;
+}
+
+/** 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
+
+Originally here, but moved up
+
+#include <png.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+*/
+
+
+/* Given "bitmap", this returns the pixel of bitmap at the point
+ ("x", "y"). */
+
+pixel_t * Metafile::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
+Metafile::my_png_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+ PMEMPNG p=(PMEMPNG)png_get_io_ptr(png_ptr);
+
+ size_t nsize = p->size + length;
+
+ /* allocate or grow buffer */
+ if(p->buffer){ p->buffer = (char *) realloc(p->buffer, nsize); }
+ else{ p->buffer = (char *) malloc(nsize); }
+
+ if(!p->buffer){ png_error(png_ptr, "Write Error"); }
+
+ /* copy new bytes to end of buffer */
+ memcpy(p->buffer + p->size, data, length);
+ p->size += length;
+}
+
+void Metafile::toPNG(PMEMPNG accum, int width, int height, const char *px){
+ bitmap_t bmStore;
+ bitmap_t *bitmap = &bmStore;
+ accum->buffer=NULL; // PNG constructed in memory will end up here, caller must free().
+ accum->size=0;
+ bitmap->pixels=(pixel_t *)px;
+ bitmap->width = width;
+ bitmap->height = height;
+
+ png_structp png_ptr = NULL;
+ png_infop info_ptr = NULL;
+ size_t x, y;
+ png_byte ** row_pointers = NULL;
+ /* The following number is set by trial and error only. I cannot
+ see where it it is documented in the libpng manual.
+ */
+ 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);
+
+}
+
+
+/* convert an EMF RGB(A) color to 0RGB
+inverse of gethexcolor() in emf-print.cpp
+*/
+uint32_t Metafile::sethexcolor(U_COLORREF color){
+
+ uint32_t out;
+ out = (U_RGBAGetR(color) << 16) +
+ (U_RGBAGetG(color) << 8 ) +
+ (U_RGBAGetB(color) );
+ return(out);
+}
+
+/* Return the base64 encoded png which is shown for all bad images.
+Currently a random 3x4 blotch.
+Caller must free.
+*/
+gchar *Metafile::bad_image_png(void){
+ gchar *gstring = g_strdup("iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAAA3NCSVQICAjb4U/gAAAALElEQVQImQXBQQ2AMAAAsUJQMSWI2H8qME1yMshojwrvGB8XcHKvR1XtOTc/8HENumHCsOMAAAAASUVORK5CYII=");
+ return(gstring);
+}
+
+
+
+} // 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 --git a/src/extension/internal/metafile-inout.h b/src/extension/internal/metafile-inout.h
new file mode 100644
index 000000000..968773a3a
--- /dev/null
+++ b/src/extension/internal/metafile-inout.h
@@ -0,0 +1,93 @@
+/** @file
+ * @brief Metafile input - common functions
+ *//*
+ * Authors:
+ * David Mathog
+ *
+ * Copyright (C) 2013 Authors
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef SEEN_INKSCAPE_EXTENSION_INTERNAL_METAFILE_INOUT_H
+#define SEEN_INKSCAPE_EXTENSION_INTERNAL_METAFILE_INOUT_H
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#define PNG_SKIP_SETJMP_CHECK // else any further png.h include blows up in the compiler
+#include <png.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <map>
+#include <stack>
+#include <glibmm/ustring.h>
+#include <libuemf/uemf.h>
+#include <2geom/affine.h>
+#include <2geom/pathvector.h>
+
+#include "extension/implementation/implementation.h"
+
+class SPObject;
+
+namespace Inkscape {
+class Pixbuf;
+
+namespace Extension {
+namespace Internal {
+
+/* 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;
+
+class Metafile
+ : public Inkscape::Extension::Implementation::Implementation
+{
+public:
+ Metafile() {}
+ ~Metafile();
+
+protected:
+ static uint32_t sethexcolor(U_COLORREF color);
+ static pixel_t *pixel_at (bitmap_t * bitmap, int x, int y);
+ static void my_png_write_data(png_structp png_ptr, png_bytep data, png_size_t length);
+ static void toPNG(PMEMPNG accum, int width, int height, const char *px);
+ static gchar *bad_image_png(void);
+
+
+private:
+};
+
+} // namespace Internal
+} // namespace Extension
+} // namespace Inkscape
+
+#endif // SEEN_INKSCAPE_EXTENSION_INTERNAL_METAFILE_INOUT_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:
+*/
diff --git a/src/extension/internal/metafile-print.cpp b/src/extension/internal/metafile-print.cpp
new file mode 100644
index 000000000..73d63f27d
--- /dev/null
+++ b/src/extension/internal/metafile-print.cpp
@@ -0,0 +1,493 @@
+/** @file
+ * @brief Metafile printing - common routines
+ *//*
+ * Authors:
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * Copyright (C) 2013 Authors
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <cstring>
+#include <fstream>
+#include <glib.h>
+#include <glibmm/miscutils.h>
+#include <2geom/rect.h>
+#include <2geom/curves.h>
+#include <2geom/svg-path-parser.h>
+
+#include "extension/internal/metafile-print.h"
+#include "extension/print.h"
+#include "path-prefix.h"
+#include "sp-gradient.h"
+#include "sp-image.h"
+#include "sp-linear-gradient.h"
+#include "sp-pattern.h"
+#include "sp-radial-gradient.h"
+#include "style.h"
+
+namespace Inkscape {
+namespace Extension {
+namespace Internal {
+
+bool PrintMetafile::_ppt_fontfix_read = false;
+PrintMetafile::FontfixMap PrintMetafile::_ppt_fixable_fonts;
+
+PrintMetafile::~PrintMetafile()
+{
+#ifndef G_OS_WIN32
+ // restore default signal handling for SIGPIPE
+ (void) signal(SIGPIPE, SIG_DFL);
+#endif
+ return;
+}
+
+bool PrintMetafile::textToPath(Inkscape::Extension::Print *ext)
+{
+ return ext->get_param_bool("textToPath");
+}
+
+unsigned int PrintMetafile::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 PrintMetafile::release(Inkscape::Extension::Print * /*mod*/)
+{
+ m_tr_stack.pop();
+ return 1;
+}
+
+bool PrintMetafile::_load_ppt_fontfix_data() //this is not called by any other source files
+{
+ static bool ppt_fontfix_available = false;
+
+ if (_ppt_fontfix_read) return ppt_fontfix_available;
+ _ppt_fontfix_read = true;
+
+ // add default entry
+ _ppt_fixable_fonts.insert(std::make_pair(Glib::ustring(""), FontfixParams()));
+
+ std::string fontfix_path = Glib::build_filename(INKSCAPE_EXTENSIONDIR, "fontfix.conf");
+ std::ifstream fontfix_file(fontfix_path.c_str(), std::ios::in);
+
+ if (!fontfix_file.is_open()) {
+ g_warning("Unable to open PowerPoint fontfix file: %s\n"
+ "PowerPoint ungrouping compensation in WMF/EMF export will not be available.",
+ fontfix_path.c_str());
+ return (ppt_fontfix_available = false);
+ }
+
+ char *oldlocale = g_strdup(setlocale(LC_NUMERIC, NULL));
+ setlocale(LC_NUMERIC, "C");
+
+ std::string instr;
+ while (std::getline(fontfix_file, instr)) {
+ if (instr[0] == '#') {
+ continue;
+ }
+ // not a comment, get the 4 values from the line
+ FontfixParams params;
+ char fontname[128];
+ int elements = sscanf(instr.c_str(), "%lf %lf %lf %127[^\n]",
+ &params.f1, &params.f2, &params.f3, &fontname[0]);
+ if (elements != 4) {
+ g_warning("Malformed line in %s: %s\n", fontfix_path.c_str(), instr.c_str());
+ continue;
+ }
+ _ppt_fixable_fonts.insert(std::make_pair(Glib::ustring(fontname), params));
+ }
+ fontfix_file.close(); // not strictly necessary
+
+ setlocale(LC_NUMERIC, oldlocale);
+ g_free(oldlocale);
+ return (ppt_fontfix_available = true);
+}
+
+// Finds font fix parameters for the given fontname.
+void PrintMetafile::_lookup_ppt_fontfix(Glib::ustring const &fontname, FontfixParams &params)
+{
+ if (!_ppt_fontfix_read) _load_ppt_fontfix_data();
+
+ FontfixMap::iterator f = _ppt_fixable_fonts.find(fontname);
+ if (f != _ppt_fixable_fonts.end()) {
+ params = f->second;
+ }
+}
+
+U_COLORREF PrintMetafile::_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 PrintMetafile::_translate_weight(unsigned inkweight)
+{
+ switch (inkweight) {
+ // 400 is tested first, as it is the most common case
+ case SP_CSS_FONT_WEIGHT_400: return U_FW_NORMAL;
+ case SP_CSS_FONT_WEIGHT_100: return U_FW_THIN;
+ case SP_CSS_FONT_WEIGHT_200: return U_FW_EXTRALIGHT;
+ case SP_CSS_FONT_WEIGHT_300: return U_FW_LIGHT;
+ case SP_CSS_FONT_WEIGHT_500: return U_FW_MEDIUM;
+ case SP_CSS_FONT_WEIGHT_600: return U_FW_SEMIBOLD;
+ case SP_CSS_FONT_WEIGHT_700: return U_FW_BOLD;
+ case SP_CSS_FONT_WEIGHT_800: return U_FW_EXTRABOLD;
+ case SP_CSS_FONT_WEIGHT_900: return U_FW_HEAVY;
+ default: return U_FW_NORMAL;
+ }
+}
+
+/* opacity weighting of two colors as float. v1 is the color, op is its opacity, v2 is the background color */
+inline float opweight(float v1, float v2, float op)
+{
+ return v1 * op + v2 * (1.0 - op);
+}
+
+U_COLORREF PrintMetafile::avg_stop_color(SPGradient *gr)
+{
+ U_COLORREF cr;
+ int last = gr->vector.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;
+}
+
+U_COLORREF PrintMetafile::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;
+}
+
+/* t between 0 and 1, values outside that range use the nearest limit */
+U_COLORREF PrintMetafile::weight_colors(U_COLORREF c1, U_COLORREF c2, double t)
+{
+#define clrweight(a,b,t) ((1-t)*((double) a) + (t)*((double) b))
+ U_COLORREF result;
+ t = ( t > 1.0 ? 1.0 : ( t < 0.0 ? 0.0 : t));
+ 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;
+}
+
+// Extract hatchType, hatchColor from a name like
+// EMFhatch<hatchType>_<hatchColor>
+// 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 PrintMetafile::hatch_classify(char *name, int *hatchType, U_COLORREF *hatchColor, U_COLORREF *bkColor)
+{
+ int val;
+ uint32_t hcolor = 0;
+ uint32_t bcolor = 0;
+
+ // name should be EMFhatch or WMFhatch but *MFhatch will be accepted
+ if (0 != strncmp(&name[1], "MFhatch", 7)) {
+ 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 (2 != sscanf(name, "%X_%X", &hcolor, &bcolor)) { // not a pattern with background
+ if (1 != sscanf(name, "%X", &hcolor)) {
+ *hatchType = -1; // not a pattern, cannot classify
+ }
+ *hatchColor = _gethexcolor(hcolor);
+ } else {
+ *hatchColor = _gethexcolor(hcolor);
+ *bkColor = _gethexcolor(bcolor);
+ usebk = true;
+ }
+ }
+ /* Everything > U_HS_SOLIDCLR is solid, just specify the color in the brush rather than messing around with background or textcolor */
+ 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 [EW]MFhatch3_3F7FFF return hatchType=3, hatchColor=3F7FFF (as a uint32_t),
+// otherwise hatchType is set to -1 and hatchColor is not defined.
+//
+
+void PrintMetafile::brush_classify(SPObject *parent, int depth, Inkscape::Pixbuf **epixbuf, int *hatchType, U_COLORREF *hatchColor, U_COLORREF *bkColor)
+{
+ if (depth == 0) {
+ *epixbuf = NULL;
+ *hatchType = -1;
+ *hatchColor = U_RGB(0, 0, 0);
+ *bkColor = U_RGB(255, 255, 255);
+ }
+ 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 [EW]MFhatch#_######
+ hatch_classify(temp, hatchType, hatchColor, bkColor);
+ 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, bkColor);
+ 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, bkColor);
+ child = child->getNext();
+ }
+ }
+}
+
+//swap R/B in 4 byte pixel
+void PrintMetafile::swapRBinRGBA(char *px, int pixels)
+{
+ char tmp;
+ for (int i = 0; i < pixels * 4; px += 4, i += 4) {
+ tmp = px[2];
+ px[2] = px[0];
+ px[0] = tmp;
+ }
+}
+
+int PrintMetafile::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 {
+ g_error("Fatal programming error, hold_gradient() in metafile-print.cpp called with invalid draw mode");
+ }
+ return 1;
+}
+
+/* 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 PrintMetafile::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<Geom::Path> outres = Geom::parse_svg_path(text);
+ return outres;
+}
+
+
+/* rx2,ry2 must be larger than rx1,ry1!
+ angle is in RADIANS
+*/
+Geom::PathVector PrintMetafile::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<Geom::Path> outres = Geom::parse_svg_path(text);
+
+ return outres;
+}
+
+/* Elliptical hole in a large square extending from -50k to +50k */
+Geom::PathVector PrintMetafile::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<Geom::Path> 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 PrintMetafile::rect_cutter(Geom::Point ctr, Geom::Point pos, Geom::Point neg, Geom::Point width)
+{
+ std::vector<Geom::Path> outres;
+ Geom::Path cutter;
+ cutter.start(ctr + pos - width);
+ cutter.appendNew<Geom::LineSegment>(ctr + pos + width);
+ cutter.appendNew<Geom::LineSegment>(ctr + neg + width);
+ cutter.appendNew<Geom::LineSegment>(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 PrintMetafile::SPWR_to_LVFR(SPWindRule wr)
+{
+ FillRule fr;
+ if (wr == SP_WIND_RULE_EVENODD) {
+ fr = fill_oddEven;
+ } else {
+ fr = fill_nonZero;
+ }
+ return fr;
+}
+
+} // 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 --git a/src/extension/internal/metafile-print.h b/src/extension/internal/metafile-print.h
new file mode 100644
index 000000000..a21f9de58
--- /dev/null
+++ b/src/extension/internal/metafile-print.h
@@ -0,0 +1,134 @@
+/** @file
+ * @brief Metafile printing - common functions
+ *//*
+ * Authors:
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * Copyright (C) 2013 Authors
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef SEEN_INKSCAPE_EXTENSION_INTERNAL_METAFILE_PRINT_H
+#define SEEN_INKSCAPE_EXTENSION_INTERNAL_METAFILE_PRINT_H
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <map>
+#include <stack>
+#include <glibmm/ustring.h>
+#include <libuemf/uemf.h>
+#include <2geom/affine.h>
+#include <2geom/pathvector.h>
+
+#include "extension/implementation/implementation.h"
+#include "splivarot.h"
+#include "display/canvas-bpath.h"
+
+class SPGradient;
+class SPObject;
+
+namespace Inkscape {
+class Pixbuf;
+
+namespace Extension {
+namespace Internal {
+
+enum MFDrawMode {DRAW_PAINT, DRAW_PATTERN, DRAW_IMAGE, DRAW_LINEAR_GRADIENT, DRAW_RADIAL_GRADIENT};
+
+struct FontfixParams {
+ double f1; //Vertical (rotating) offset factor (* font height)
+ double f2; //Vertical (nonrotating) offset factor (* font height)
+ double f3; //Horizontal (nonrotating) offset factor (* font height)
+ FontfixParams() : f1(0), f2(0), f3(0) {}
+};
+
+class PrintMetafile
+ : public Inkscape::Extension::Implementation::Implementation
+{
+public:
+ PrintMetafile() {}
+ ~PrintMetafile();
+
+ bool textToPath (Inkscape::Extension::Print * ext);
+ unsigned int bind(Inkscape::Extension::Print *module, Geom::Affine const &transform, float opacity);
+ unsigned int release(Inkscape::Extension::Print *module);
+
+protected:
+ 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.
+ };
+
+ double _width;
+ double _height;
+ U_RECTL rc;
+
+ uint32_t htextalignment;
+ uint32_t hpolyfillmode; // used to minimize redundant records that set this
+ float htextcolor_rgb[3]; // used to minimize redundant records that set this
+
+ std::stack<Geom::Affine> m_tr_stack;
+ Geom::PathVector fill_pathv;
+ Geom::Affine fill_transform;
+ bool use_stroke;
+ bool use_fill;
+ bool simple_shape;
+ bool usebk;
+
+ GRADVALUES gv;
+
+ static bool _load_ppt_fontfix_data();
+ static void _lookup_ppt_fontfix(Glib::ustring const &fontname, FontfixParams &);
+ static U_COLORREF _gethexcolor(uint32_t color);
+ static uint32_t _translate_weight(unsigned inkweight);
+
+ U_COLORREF avg_stop_color(SPGradient *gr);
+ U_COLORREF weight_opacity(U_COLORREF c1);
+ U_COLORREF weight_colors(U_COLORREF c1, U_COLORREF c2, double t);
+
+ void hatch_classify(char *name, int *hatchType, U_COLORREF *hatchColor, U_COLORREF *bkColor);
+ void brush_classify(SPObject *parent, int depth, Inkscape::Pixbuf **epixbuf, int *hatchType, U_COLORREF *hatchColor, U_COLORREF *bkColor);
+ static void swapRBinRGBA(char *px, int pixels);
+
+ int hold_gradient(void *gr, int mode);
+ static Geom::PathVector center_ellipse_as_SVG_PathV(Geom::Point ctr, double rx, double ry, double F);
+ static Geom::PathVector center_elliptical_ring_as_SVG_PathV(Geom::Point ctr, double rx1, double ry1, double rx2, double ry2, double F);
+ static Geom::PathVector center_elliptical_hole_as_SVG_PathV(Geom::Point ctr, double rx, double ry, double F);
+ static Geom::PathVector rect_cutter(Geom::Point ctr, Geom::Point pos, Geom::Point neg, Geom::Point width);
+ static FillRule SPWR_to_LVFR(SPWindRule wr);
+
+ virtual int create_brush(SPStyle const *style, PU_COLORREF fcolor) = 0;
+ virtual void destroy_brush() = 0;
+ virtual int create_pen(SPStyle const *style, const Geom::Affine &transform) = 0;
+ virtual void destroy_pen() = 0;
+
+private:
+ typedef std::map<Glib::ustring, FontfixParams> FontfixMap;
+ static FontfixMap _ppt_fixable_fonts;
+ static bool _ppt_fontfix_read;
+};
+
+} // namespace Internal
+} // namespace Extension
+} // namespace Inkscape
+
+#endif
+
+/*
+ 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 --git a/src/extension/internal/odf.cpp b/src/extension/internal/odf.cpp
index 8bb96920d..52fabcf3c 100644
--- a/src/extension/internal/odf.cpp
+++ b/src/extension/internal/odf.cpp
@@ -4,7 +4,7 @@
* This is an an entry in the extensions mechanism to begin to enable
* the inputting and outputting of OpenDocument Format (ODF) files from
* within Inkscape. Although the initial implementations will be very lossy
- * do to the differences in the models of SVG and ODF, they will hopefully
+ * due to the differences in the models of SVG and ODF, they will hopefully
* improve greatly with time. People should consider this to be a framework
* that can be continously upgraded for ever improving fidelity. Potential
* developers should especially look in preprocess() and writeTree() to see how
@@ -15,8 +15,10 @@
* Authors:
* Bob Jamison
* Abhishek Sharma
+ * Kris De Gussem
*
* Copyright (C) 2006, 2007 Bob Jamison
+ * Copyright (C) 2013 Kris De Gussem
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -45,7 +47,7 @@
#include <stdio.h>
#include <time.h>
#include <vector>
-
+#include <math.h>
//# Inkscape includes
#include "clear-n_.h"
@@ -73,37 +75,29 @@
#include "sp-flowtext.h"
#include "svg/svg.h"
#include "text-editing.h"
+#include "util/units.h"
+#include "uri.h"
-//# DOM-specific includes
-#include "dom/dom.h"
-#include "dom/util/ziptool.h"
-#include "dom/io/domstream.h"
-#include "dom/io/bufferstream.h"
-#include "dom/io/stringstream.h"
-
-
+#include "inkscape-version.h"
#include "document.h"
-
#include "extension/extension.h"
-
-
+#include "io/inkscapestream.h"
+#include "io/bufferstream.h"
+#include <util/ziptool.h>
+#include <iomanip>
namespace Inkscape
{
namespace Extension
{
namespace Internal
{
-
-
-
//# Shorthand notation
-typedef org::w3c::dom::DOMString DOMString;
-typedef org::w3c::dom::XMLCh XMLCh;
-typedef org::w3c::dom::io::OutputStreamWriter OutputStreamWriter;
-typedef org::w3c::dom::io::BufferOutputStream BufferOutputStream;
-typedef org::w3c::dom::io::StringOutputStream StringOutputStream;
+typedef Inkscape::IO::BufferOutputStream BufferOutputStream;
+typedef Inkscape::IO::OutputStreamWriter OutputStreamWriter;
+typedef Inkscape::IO::StringOutputStream StringOutputStream;
+
//########################################################################
//# C L A S S SingularValueDecomposition
@@ -211,9 +205,11 @@ public:
SVDMatrix transpose()
{
SVDMatrix result(cols, rows);
- for (unsigned int i=0 ; i<rows ; i++)
- for (unsigned int j=0 ; j<cols ; j++)
+ for (unsigned int i=0 ; i<rows ; i++){
+ for (unsigned int j=0 ; j<cols ; j++){
result(j, i) = d[i*cols + j];
+ }
+ }
return result;
}
@@ -239,9 +235,11 @@ private:
rows = other.rows;
cols = other.cols;
size = other.size;
+ badval = other.badval;
d = new double[size];
- for (unsigned int i=0 ; i<size ; i++)
+ for (unsigned int i=0 ; i<size ; i++){
d[i] = other.d[i];
+ }
}
double badval;
@@ -289,11 +287,13 @@ public:
@return Structure to access U, S and V.
*/
- SingularValueDecomposition (const SVDMatrix &mat)
+ SingularValueDecomposition (const SVDMatrix &mat) :
+ A (mat),
+ U (),
+ s (NULL),
+ s_size (0),
+ V ()
{
- A = mat;
- s = NULL;
- s_size = 0;
calculate();
}
@@ -801,7 +801,6 @@ void SingularValueDecomposition::calculate()
}
-
/**
* Return the left singular vectors
* @return U
@@ -899,7 +898,6 @@ static Glib::ustring getAttribute( Inkscape::XML::Node *node, char const *attrNa
}
-
/**
* Get the extension suffix from the end of a file name
*/
@@ -919,7 +917,6 @@ static Glib::ustring getExtension(const Glib::ustring &fname)
return ext;
}
-
static Glib::ustring formatTransform(Geom::Affine &tf)
{
Glib::ustring str;
@@ -935,9 +932,6 @@ static Glib::ustring formatTransform(Geom::Affine &tf)
}
-
-
-
/**
* Get the general transform from SVG pixels to
* ODF cm
@@ -947,7 +941,7 @@ static Geom::Affine getODFTransform(const SPItem *item)
//### Get SVG-to-ODF transform
Geom::Affine tf (item->i2dt_affine());
//Flip Y into document coordinates
- double doc_height = SP_ACTIVE_DOCUMENT->getHeight();
+ double doc_height = SP_ACTIVE_DOCUMENT->getHeight().value("px");
Geom::Affine doc2dt_tf = Geom::Affine(Geom::Scale(1.0, -1.0)); /// @fixme hardcoded desktop transform
doc2dt_tf = doc2dt_tf * Geom::Affine(Geom::Translate(0, doc_height));
tf = tf * doc2dt_tf;
@@ -956,8 +950,6 @@ static Geom::Affine getODFTransform(const SPItem *item)
}
-
-
/**
* Get the bounding box of an item, as mapped onto
* an ODF document, in cm.
@@ -965,7 +957,7 @@ static Geom::Affine getODFTransform(const SPItem *item)
static Geom::OptRect getODFBoundingBox(const SPItem *item)
{
// TODO: geometric or visual?
- Geom::OptRect bbox = ((SPItem *)item)->documentVisualBounds();
+ Geom::OptRect bbox = item->documentVisualBounds();
if (bbox) {
*bbox *= Geom::Affine(Geom::Scale(pxToCm));
}
@@ -973,7 +965,6 @@ static Geom::OptRect getODFBoundingBox(const SPItem *item)
}
-
/**
* Get the transform for an item, correcting for
* handedness reversal
@@ -987,7 +978,6 @@ static Geom::Affine getODFItemTransform(const SPItem *item)
}
-
/**
* Get some fun facts from the transform
*/
@@ -1011,16 +1001,9 @@ static void analyzeTransform(Geom::Affine &tf,
double s1 = svd.getS(1);
xscale = s0;
yscale = s1;
- //g_message("## s0:%.3f s1:%.3f", s0, s1);
- //g_message("## u:%.3f %.3f %.3f %.3f", U(0,0), U(0,1), U(1,0), U(1,1));
- //g_message("## v:%.3f %.3f %.3f %.3f", V(0,0), V(0,1), V(1,0), V(1,1));
- //g_message("## vt:%.3f %.3f %.3f %.3f", Vt(0,0), Vt(0,1), Vt(1,0), Vt(1,1));
- //g_message("## uvt:%.3f %.3f %.3f %.3f", UVt(0,0), UVt(0,1), UVt(1,0), UVt(1,1));
rotate = UVt(0,0);
}
-
-
static void gatherText(Inkscape::XML::Node *node, Glib::ustring &buf)
{
if (node->type() == Inkscape::XML::TEXT_NODE)
@@ -1038,15 +1021,14 @@ static void gatherText(Inkscape::XML::Node *node, Glib::ustring &buf)
}
+
/**
* FIRST PASS.
* Method descends into the repr tree, converting image, style, and gradient info
* into forms compatible in ODF.
*/
-void
-OdfOutput::preprocess(ZipFile &zf, Inkscape::XML::Node *node)
+void OdfOutput::preprocess(ZipFile &zf, Inkscape::XML::Node *node)
{
-
Glib::ustring nodeName = node->name();
Glib::ustring id = getAttribute(node, "id");
@@ -1074,18 +1056,16 @@ OdfOutput::preprocess(ZipFile &zf, Inkscape::XML::Node *node)
//Now consider items.
SPObject *reprobj = SP_ACTIVE_DOCUMENT->getObjectByRepr(node);
if (!reprobj)
+ {
return;
+ }
if (!SP_IS_ITEM(reprobj))
- {
+ {
return;
- }
- SPItem *item = SP_ITEM(reprobj);
- //### Get SVG-to-ODF transform
- Geom::Affine tf = getODFTransform(item);
+ }
if (nodeName == "image" || nodeName == "svg:image")
{
- //g_message("image");
Glib::ustring href = getAttribute(node, "xlink:href");
if (href.size() > 0)
{
@@ -1102,12 +1082,9 @@ OdfOutput::preprocess(ZipFile &zf, Inkscape::XML::Node *node)
imageTable[oldName] = newName;
Glib::ustring comment = "old name was: ";
comment.append(oldName);
- URI oldUri(oldName);
- //g_message("oldpath:%s", oldUri.getNativePath().c_str());
+ Inkscape::URI oldUri(oldName.c_str());
//# if relative to the documentURI, get proper path
- URI resUri = documentUri.resolve(oldUri);
- DOMString pathName = resUri.getNativePath();
- //g_message("native path:%s", pathName.c_str());
+ std::string pathName = documentUri.getFullPath(oldUri.getFullPath(""));
ZipEntry *ze = zf.addFile(pathName, comment);
if (ze)
{
@@ -1127,7 +1104,6 @@ OdfOutput::preprocess(ZipFile &zf, Inkscape::XML::Node *node)
}
-
/**
* Writes the manifest. Currently it only changes according to the
* file names of images packed into the zip file.
@@ -1140,25 +1116,25 @@ bool OdfOutput::writeManifest(ZipFile &zf)
time_t tim;
time(&tim);
- outs.printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
- outs.printf("<!DOCTYPE manifest:manifest PUBLIC \"-//OpenOffice.org//DTD Manifest 1.0//EN\" \"Manifest.dtd\">\n");
- outs.printf("\n");
- outs.printf("\n");
- outs.printf("<!--\n");
- outs.printf("*************************************************************************\n");
- outs.printf(" file: manifest.xml\n");
- outs.printf(" Generated by Inkscape: %s", ctime(&tim)); //ctime has its own <cr>
- outs.printf(" http://www.inkscape.org\n");
- outs.printf("*************************************************************************\n");
- outs.printf("-->\n");
- outs.printf("\n");
- outs.printf("\n");
- outs.printf("<manifest:manifest xmlns:manifest=\"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0\">\n");
- outs.printf(" <manifest:file-entry manifest:media-type=\"application/vnd.oasis.opendocument.graphics\" manifest:full-path=\"/\"/>\n");
- outs.printf(" <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"content.xml\"/>\n");
- outs.printf(" <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"styles.xml\"/>\n");
- outs.printf(" <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"meta.xml\"/>\n");
- outs.printf(" <!--List our images here-->\n");
+ outs.writeString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+ outs.writeString("<!DOCTYPE manifest:manifest PUBLIC \"-//OpenOffice.org//DTD Manifest 1.0//EN\" \"Manifest.dtd\">\n");
+ outs.writeString("\n");
+ outs.writeString("\n");
+ outs.writeString("<!--\n");
+ outs.writeString("*************************************************************************\n");
+ outs.writeString(" file: manifest.xml\n");
+ outs.printf (" Generated by Inkscape: %s", ctime(&tim)); //ctime has its own <cr>
+ outs.writeString(" http://www.inkscape.org\n");
+ outs.writeString("*************************************************************************\n");
+ outs.writeString("-->\n");
+ outs.writeString("\n");
+ outs.writeString("\n");
+ outs.writeString("<manifest:manifest xmlns:manifest=\"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0\">\n");
+ outs.writeString(" <manifest:file-entry manifest:media-type=\"application/vnd.oasis.opendocument.graphics\" manifest:full-path=\"/\"/>\n");
+ outs.writeString(" <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"content.xml\"/>\n");
+ outs.writeString(" <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"styles.xml\"/>\n");
+ outs.writeString(" <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"meta.xml\"/>\n");
+ outs.writeString(" <!--List our images here-->\n");
std::map<Glib::ustring, Glib::ustring>::iterator iter;
for (iter = imageTable.begin() ; iter!=imageTable.end() ; ++iter)
{
@@ -1176,7 +1152,7 @@ bool OdfOutput::writeManifest(ZipFile &zf)
else if (ext == ".jpg")
outs.printf("image/jpeg");
outs.printf("\" manifest:full-path=\"");
- outs.printf(newName.c_str());
+ outs.writeString(newName.c_str());
outs.printf("\"/>\n");
}
outs.printf("</manifest:manifest>\n");
@@ -1204,65 +1180,79 @@ bool OdfOutput::writeMeta(ZipFile &zf)
time(&tim);
std::map<Glib::ustring, Glib::ustring>::iterator iter;
- Glib::ustring creator = "unknown";
+ Glib::ustring InkscapeVersion = Glib::ustring("Inkscape.org - ") + Inkscape::version_string;
+ Glib::ustring creator = InkscapeVersion;
iter = metadata.find("dc:creator");
if (iter != metadata.end())
+ {
creator = iter->second;
- Glib::ustring date = "";
+ }
+
+ Glib::ustring date;
+ Glib::ustring moddate;
+ char buf [80];
+ time_t rawtime;
+ struct tm * timeinfo;
+ time (&rawtime);
+ timeinfo = localtime (&rawtime);
+ strftime (buf,80,"%Y-%m-%d %H:%M:%S",timeinfo);
+ moddate = Glib::ustring(buf);
+
iter = metadata.find("dc:date");
if (iter != metadata.end())
+ {
date = iter->second;
+ }
+ else
+ {
+ date = moddate;
+ }
- outs.printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
- outs.printf("\n");
- outs.printf("\n");
- outs.printf("<!--\n");
- outs.printf("*************************************************************************\n");
- outs.printf(" file: meta.xml\n");
- outs.printf(" Generated by Inkscape: %s", ctime(&tim)); //ctime has its own <cr>
- outs.printf(" http://www.inkscape.org\n");
- outs.printf("*************************************************************************\n");
- outs.printf("-->\n");
- outs.printf("\n");
- outs.printf("\n");
- outs.printf("<office:document-meta\n");
- outs.printf("xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"\n");
- outs.printf("xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n");
- outs.printf("xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n");
- outs.printf("xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"\n");
- outs.printf("xmlns:presentation=\"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0\"\n");
- outs.printf("xmlns:ooo=\"http://openoffice.org/2004/office\"\n");
- outs.printf("xmlns:smil=\"urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0\"\n");
- outs.printf("xmlns:anim=\"urn:oasis:names:tc:opendocument:xmlns:animation:1.0\"\n");
- outs.printf("office:version=\"1.0\">\n");
- outs.printf("<office:meta>\n");
- outs.printf(" <meta:generator>Inkscape.org - 0.45</meta:generator>\n");
- outs.printf(" <meta:initial-creator>%#s</meta:initial-creator>\n",
- creator.c_str());
- outs.printf(" <meta:creation-date>%#s</meta:creation-date>\n", date.c_str());
+ outs.writeString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+ outs.writeString("\n");
+ outs.writeString("<!--\n");
+ outs.writeString("*************************************************************************\n");
+ outs.writeString(" file: meta.xml\n");
+ outs.printf (" Generated by Inkscape: %s", ctime(&tim)); //ctime has its own <cr>
+ outs.writeString(" http://www.inkscape.org\n");
+ outs.writeString("*************************************************************************\n");
+ outs.writeString("-->\n");
+ outs.writeString("\n");
+ outs.writeString("<office:document-meta\n");
+ outs.writeString("xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"\n");
+ outs.writeString("xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n");
+ outs.writeString("xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n");
+ outs.writeString("xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"\n");
+ outs.writeString("xmlns:presentation=\"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0\"\n");
+ outs.writeString("xmlns:ooo=\"http://openoffice.org/2004/office\"\n");
+ outs.writeString("xmlns:smil=\"urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0\"\n");
+ outs.writeString("xmlns:anim=\"urn:oasis:names:tc:opendocument:xmlns:animation:1.0\"\n");
+ outs.writeString("office:version=\"1.0\">\n");
+ outs.writeString("<office:meta>\n");
+ Glib::ustring tmp = Glib::ustring::compose(" <meta:generator>%1</meta:generator>\n", InkscapeVersion);
+ tmp += Glib::ustring::compose(" <meta:initial-creator>%1</meta:initial-creator>\n", creator);
+ tmp += Glib::ustring::compose(" <meta:creation-date>%1</meta:creation-date>\n", date);
+ tmp += Glib::ustring::compose(" <dc:date>%1</dc:date>\n", moddate);
+ outs.writeUString(tmp);
for (iter = metadata.begin() ; iter != metadata.end() ; ++iter)
- {
+ {
Glib::ustring name = iter->first;
Glib::ustring value = iter->second;
- if (name.size() > 0 && value.size()>0)
- {
- outs.printf(" <%#s>%#s</%#s>\n",
- name.c_str(), value.c_str(), name.c_str());
- }
+ if (!name.empty() && !value.empty())
+ {
+ tmp = Glib::ustring::compose(" <%1>%2</%3>\n", name, value, name);
+ outs.writeUString(tmp);
}
- outs.printf(" <meta:editing-cycles>2</meta:editing-cycles>\n");
- outs.printf(" <meta:editing-duration>PT56S</meta:editing-duration>\n");
- outs.printf(" <meta:user-defined meta:name=\"Info 1\"/>\n");
- outs.printf(" <meta:user-defined meta:name=\"Info 2\"/>\n");
- outs.printf(" <meta:user-defined meta:name=\"Info 3\"/>\n");
- outs.printf(" <meta:user-defined meta:name=\"Info 4\"/>\n");
- outs.printf(" <meta:document-statistic meta:object-count=\"2\"/>\n");
- outs.printf("</office:meta>\n");
- outs.printf("</office:document-meta>\n");
- outs.printf("\n");
- outs.printf("\n");
-
-
+ }
+ // outs.writeString(" <meta:editing-cycles>2</meta:editing-cycles>\n");
+ // outs.writeString(" <meta:editing-duration>PT56S</meta:editing-duration>\n");
+ // outs.writeString(" <meta:user-defined meta:name=\"Info 1\"/>\n");
+ // outs.writeString(" <meta:user-defined meta:name=\"Info 2\"/>\n");
+ // outs.writeString(" <meta:user-defined meta:name=\"Info 3\"/>\n");
+ // outs.writeString(" <meta:user-defined meta:name=\"Info 4\"/>\n");
+ // outs.writeString(" <meta:document-statistic meta:object-count=\"2\"/>\n");
+ outs.writeString("</office:meta>\n");
+ outs.writeString("</office:document-meta>\n");
outs.close();
//Make our entry
@@ -1274,208 +1264,6 @@ bool OdfOutput::writeMeta(ZipFile &zf)
}
-
-
-/**
- * This is called just before writeTree(), since it will write style and
- * gradient information above the <draw> tag in the content.xml file
- */
-bool OdfOutput::writeStyle(ZipFile &zf)
-{
- BufferOutputStream bouts;
- OutputStreamWriter outs(bouts);
-
- /*
- ==========================================================
- Dump our style table. Styles should have a general layout
- something like the following. Look in:
- http://books.evc-cit.info/odbook/ch06.html#draw-style-file-section
- for style and gradient information.
- <style:style style:name="gr13"
- style:family="graphic" style:parent-style-name="standard">
- <style:graphic-properties draw:stroke="solid"
- svg:stroke-width="0.1cm"
- svg:stroke-color="#ff0000"
- draw:fill="solid" draw:fill-color="#e6e6ff"/>
- </style:style>
- ==========================================================
- */
- outs.printf("<!-- ####### Styles from Inkscape document ####### -->\n");
- std::vector<StyleInfo>::iterator iter;
- for (iter = styleTable.begin() ; iter != styleTable.end() ; ++iter)
- {
- outs.printf("<style:style style:name=\"%s\"", iter->name.c_str());
- StyleInfo s(*iter);
- outs.printf(" style:family=\"graphic\" style:parent-style-name=\"standard\">\n");
- outs.printf(" <style:graphic-properties");
- outs.printf(" draw:fill=\"%s\" ", s.fill.c_str());
- if (s.fill != "none")
- {
- outs.printf(" draw:fill-color=\"%s\" ", s.fillColor.c_str());
- outs.printf(" draw:fill-opacity=\"%s\" ", s.fillOpacity.c_str());
- }
- outs.printf(" draw:stroke=\"%s\" ", s.stroke.c_str());
- if (s.stroke != "none")
- {
- outs.printf(" svg:stroke-width=\"%s\" ", s.strokeWidth.c_str());
- outs.printf(" svg:stroke-color=\"%s\" ", s.strokeColor.c_str());
- outs.printf(" svg:stroke-opacity=\"%s\" ", s.strokeOpacity.c_str());
- }
- outs.printf("/>\n");
- outs.printf("</style:style>\n");
- }
-
- //## Dump our gradient table
- int gradientCount = 0;
- outs.printf("\n");
- outs.printf("<!-- ####### Gradients from Inkscape document ####### -->\n");
- std::vector<GradientInfo>::iterator giter;
- for (giter = gradientTable.begin() ; giter != gradientTable.end() ; ++giter)
- {
- GradientInfo gi(*giter);
- if (gi.style == "linear")
- {
- /*
- ===================================================================
- LINEAR gradient. We need something that looks like this:
- <draw:gradient draw:name="Gradient_20_7"
- draw:display-name="Gradient 7"
- draw:style="linear"
- draw:start-color="#008080" draw:end-color="#993366"
- draw:start-intensity="100%" draw:end-intensity="100%"
- draw:angle="150" draw:border="0%"/>
- ===================================================================
- */
- if (gi.stops.size() < 2)
- {
- g_warning("Need at least 2 tops for a linear gradient");
- continue;
- }
- outs.printf("<svg:linearGradient ");
- outs.printf("id=\"%#s_g\" ", gi.name.c_str());
- outs.printf("draw:name=\"%#s_g\"\n", gi.name.c_str());
- outs.printf(" draw:display-name=\"imported linear %u\"\n",
- gradientCount);
- outs.printf(" svg:x1=\"%05.3fcm\" svg:y1=\"%05.3fcm\"\n",
- gi.x1, gi.y1);
- outs.printf(" svg:x2=\"%05.3fcm\" svg:y2=\"%05.3fcm\"\n",
- gi.x2, gi.y2);
- outs.printf(" svg:gradientUnits=\"objectBoundingBox\">\n");
- outs.printf(" <svg:stop\n");
- outs.printf(" svg:stop-color=\"#%06lx\"\n",
- gi.stops[0].rgb);
- outs.printf(" svg:stop-opacity=\"%f%%\"\n",
- gi.stops[0].opacity * 100.0);
- outs.printf(" svg:offset=\"0\"/>\n");
- outs.printf(" <svg:stop\n");
- outs.printf(" svg:stop-color=\"#%06lx\"\n",
- gi.stops[1].rgb);
- outs.printf(" svg:stop-opacity=\"%f%%\"\n",
- gi.stops[1].opacity * 100.0);
- outs.printf(" svg:offset=\"1\"/>\n");
- outs.printf("</svg:linearGradient>\n");
- }
- else if (gi.style == "radial")
- {
- /*
- ===================================================================
- RADIAL gradient. We need something that looks like this:
- <!-- radial gradient, light gray to white, centered, 0% border -->
- <draw:gradient draw:name="radial_20_borderless"
- draw:display-name="radial borderless"
- draw:style="radial"
- draw:cx="50%" draw:cy="50%"
- draw:start-color="#999999" draw:end-color="#ffffff"
- draw:border="0%"/>
- ===================================================================
- */
- if (gi.stops.size() < 2)
- {
- g_warning("Need at least 2 tops for a radial gradient");
- continue;
- }
- outs.printf("<svg:radialGradient ");
- outs.printf("id=\"%#s_g\" ", gi.name.c_str());
- outs.printf("draw:name=\"%#s_g\"\n", gi.name.c_str());
- outs.printf(" draw:display-name=\"imported radial %d\"\n",
- gradientCount);
- outs.printf(" svg:cx=\"%05.3f\" svg:cy=\"%05.3f\"\n",
- gi.cx, gi.cy);
- outs.printf(" svg:fx=\"%05.3f\" svg:fy=\"%05.3f\"\n",
- gi.fx, gi.fy);
- outs.printf(" svg:r=\"%05.3f\"\n",
- gi.r);
- outs.printf(" svg:gradientUnits=\"objectBoundingBox\">\n");
- outs.printf(" <svg:stop\n");
- outs.printf(" svg:stop-color=\"#%06lx\"\n",
- gi.stops[0].rgb);
- outs.printf(" svg:stop-opacity=\"%f%%\"\n",
- gi.stops[0].opacity * 100.0);
- outs.printf(" svg:offset=\"0\"/>\n");
- outs.printf(" <svg:stop\n");
- outs.printf(" svg:stop-color=\"#%06lx\"\n",
- gi.stops[1].rgb);
- outs.printf(" svg:stop-opacity=\"%f%%\"\n",
- gi.stops[1].opacity * 100.0);
- outs.printf(" svg:offset=\"1\"/>\n");
- outs.printf("</svg:radialGradient>\n");
- }
- else
- {
- g_warning("unsupported gradient style '%s'", gi.style.c_str());
- }
- outs.printf("<style:style style:name=\"%#s\" style:family=\"graphic\" ",
- gi.name.c_str());
- outs.printf("style:parent-style-name=\"standard\">\n");
- outs.printf(" <style:graphic-properties draw:fill=\"gradient\" ");
- outs.printf("draw:fill-gradient-name=\"%#s_g\"\n",
- gi.name.c_str());
- outs.printf(" draw:textarea-horizontal-align=\"center\" ");
- outs.printf("draw:textarea-vertical-align=\"middle\"/>\n");
- outs.printf("</style:style>\n\n");
-
- gradientCount++;
- }
-
- outs.printf("\n");
- outs.printf("</office:automatic-styles>\n");
- outs.printf("\n");
- outs.printf("\n");
- outs.printf("<office:master-styles>\n");
- outs.printf("<draw:layer-set>\n");
- outs.printf(" <draw:layer draw:name=\"layout\"/>\n");
- outs.printf(" <draw:layer draw:name=\"background\"/>\n");
- outs.printf(" <draw:layer draw:name=\"backgroundobjects\"/>\n");
- outs.printf(" <draw:layer draw:name=\"controls\"/>\n");
- outs.printf(" <draw:layer draw:name=\"measurelines\"/>\n");
- outs.printf("</draw:layer-set>\n");
- outs.printf("\n");
- outs.printf("<style:master-page style:name=\"Default\"\n");
- outs.printf(" style:page-master-name=\"PM1\" draw:style-name=\"dp1\"/>\n");
- outs.printf("</office:master-styles>\n");
- outs.printf("\n");
- outs.printf("\n");
- outs.printf("\n");
- outs.printf("</office:document-styles>\n");
- outs.printf("\n");
- outs.printf("<!--\n");
- outs.printf("*************************************************************************\n");
- outs.printf(" E N D O F F I L E\n");
- outs.printf(" Have a nice day - ishmal\n");
- outs.printf("*************************************************************************\n");
- outs.printf("-->\n");
- outs.printf("\n");
-
- //Make our entry
- ZipEntry *ze = zf.newEntry("styles.xml", "ODF style file");
- ze->setUncompressedData(bouts.getBuffer());
- ze->finish();
-
- return true;
-}
-
-
-
/**
* Writes an SVG path as an ODF <draw:path> and returns the number of points written
*/
@@ -1533,24 +1321,30 @@ writePath(Writer &outs, Geom::PathVector const &pathv,
return nrPoints;
}
-
-
-bool OdfOutput::processStyle(Writer &outs, SPItem *item,
- const Glib::ustring &id)
+bool OdfOutput::processStyle(SPItem *item, const Glib::ustring &id, const Glib::ustring &gradientNameFill, const Glib::ustring &gradientNameStroke, Glib::ustring& output)
{
+ output.clear();
+ if (!item)
+ {
+ return false;
+ }
+
SPStyle *style = item->style;
-
+ if (!style)
+ {
+ return false;
+ }
+
StyleInfo si;
- //## FILL
+ // FILL
if (style->fill.isColor())
- {
+ {
guint32 fillCol = style->fill.value.color.toRGBA32( 0 );
char buf[16];
int r = (fillCol >> 24) & 0xff;
int g = (fillCol >> 16) & 0xff;
int b = (fillCol >> 8) & 0xff;
- //g_message("## %s %lx", id.c_str(), (unsigned int)fillCol);
snprintf(buf, 15, "#%02x%02x%02x", r, g, b);
si.fillColor = buf;
si.fill = "solid";
@@ -1558,11 +1352,19 @@ bool OdfOutput::processStyle(Writer &outs, SPItem *item,
(SP_SCALE24_TO_FLOAT(style->fill_opacity.value));
snprintf(buf, 15, "%.3f%%", opacityPercent);
si.fillOpacity = buf;
+ }
+ else if (style->fill.isPaintserver())
+ {
+ SPGradient *gradient = SP_GRADIENT(SP_STYLE_FILL_SERVER(style));
+ if (gradient)
+ {
+ si.fill = "gradient";
}
+ }
- //## STROKE
+ // STROKE
if (style->stroke.isColor())
- {
+ {
guint32 strokeCol = style->stroke.value.color.toRGBA32( 0 );
char buf[16];
int r = (strokeCol >> 24) & 0xff;
@@ -1577,157 +1379,170 @@ bool OdfOutput::processStyle(Writer &outs, SPItem *item,
(SP_SCALE24_TO_FLOAT(style->stroke_opacity.value));
snprintf(buf, 15, "%.3f%%", opacityPercent);
si.strokeOpacity = buf;
+ }
+ else if (style->stroke.isPaintserver())
+ {
+ SPGradient *gradient = SP_GRADIENT(SP_STYLE_STROKE_SERVER(style));
+ if (gradient)
+ {
+ si.stroke = "gradient";
}
+ }
//Look for existing identical style;
bool styleMatch = false;
std::vector<StyleInfo>::iterator iter;
for (iter=styleTable.begin() ; iter!=styleTable.end() ; ++iter)
- {
+ {
if (si.equals(*iter))
- {
+ {
//map to existing styleTable entry
Glib::ustring styleName = iter->name;
- //g_message("found duplicate style:%s", styleName.c_str());
styleLookupTable[id] = styleName;
styleMatch = true;
break;
- }
}
+ }
- //## Dont need a new style
+ // Dont need a new style
if (styleMatch)
+ {
return false;
+ }
- char buf[16];
- snprintf(buf, 15, "style%d", (int)styleTable.size());
- Glib::ustring styleName = buf;
+ Glib::ustring styleName = Glib::ustring::compose("style%1", styleTable.size());
si.name = styleName;
styleTable.push_back(si);
styleLookupTable[id] = styleName;
- outs.printf("<style:style style:name=\"%s\"", si.name.c_str());
- outs.printf(" style:family=\"graphic\" style:parent-style-name=\"standard\">\n");
- outs.printf(" <style:graphic-properties");
- outs.printf(" draw:fill=\"%s\" ", si.fill.c_str());
- if (si.fill != "none")
+ output = Glib::ustring::compose ("<style:style style:name=\"%1\" style:family=\"graphic\" style:parent-style-name=\"standard\">\n", si.name);
+ output += "<style:graphic-properties";
+ if (si.fill == "gradient")
+ {
+ output += Glib::ustring::compose (" draw:fill=\"gradient\" draw:fill-gradient-name=\"%1\"", gradientNameFill);
+ }
+ else
+ {
+ output += Glib::ustring(" draw:fill=\"") + si.fill + "\"";
+ if(si.fill != "none")
{
- outs.printf(" draw:fill-color=\"%s\" ", si.fillColor.c_str());
- outs.printf(" draw:fill-opacity=\"%s\" ", si.fillOpacity.c_str());
+ output += Glib::ustring::compose(" draw:fill-color=\"%1\"", si.fillColor);
}
- outs.printf(" draw:stroke=\"%s\" ", si.stroke.c_str());
- if (si.stroke != "none")
+ }
+ if (si.stroke == "gradient")
+ {
+ //does not seem to be supported by Open Office.org
+ output += Glib::ustring::compose (" draw:stroke=\"gradient\" draw:stroke-gradient-name=\"%1\"", gradientNameStroke);
+ }
+ else
+ {
+ output += Glib::ustring(" draw:stroke=\"") + si.stroke + "\"";
+ if (si.stroke != "none")
{
- outs.printf(" svg:stroke-width=\"%s\" ", si.strokeWidth.c_str());
- outs.printf(" svg:stroke-color=\"%s\" ", si.strokeColor.c_str());
- outs.printf(" svg:stroke-opacity=\"%s\" ", si.strokeOpacity.c_str());
+ output += Glib::ustring::compose (" svg:stroke-width=\"%1\" svg:stroke-color=\"%2\" ", si.strokeWidth, si.strokeColor);
}
- outs.printf("/>\n");
- outs.printf("</style:style>\n");
+ }
+ output += "/>\n</style:style>\n";
return true;
}
-
-
-
-bool OdfOutput::processGradient(Writer &outs, SPItem *item,
- const Glib::ustring &id, Geom::Affine &/*tf*/)
+bool OdfOutput::processGradient(SPItem *item,
+ const Glib::ustring &id, Geom::Affine &/*tf*/,
+ Glib::ustring& gradientName, Glib::ustring& output, bool checkFillGradient)
{
+ output.clear();
if (!item)
+ {
return false;
+ }
SPStyle *style = item->style;
-
if (!style)
+ {
return false;
+ }
- if (!style->fill.isPaintserver())
- return false;
-
- //## Gradient. Look in writeStyle() below to see what info
- // we need to read into GradientInfo.
- if (!SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)))
+ if ((checkFillGradient? (!style->fill.isPaintserver()) : (!style->stroke.isPaintserver())))
+ {
return false;
+ }
- SPGradient *gradient = SP_GRADIENT(SP_STYLE_FILL_SERVER(style));
+ //## Gradient
+ SPGradient *gradient = SP_GRADIENT((checkFillGradient?(SP_STYLE_FILL_SERVER(style)) :(SP_STYLE_STROKE_SERVER(style))));
+ if (gradient == NULL)
+ {
+ return false;
+ }
GradientInfo gi;
-
SPGradient *grvec = gradient->getVector(FALSE);
- for (SPStop *stop = grvec->getFirstStop() ;
+ for (SPStop *stop = grvec->getFirstStop();
stop ; stop = stop->getNextStop())
- {
- unsigned long rgba = sp_stop_get_rgba32(stop);
+ {
+ unsigned long rgba = stop->get_rgba32();
unsigned long rgb = (rgba >> 8) & 0xffffff;
- double opacity = ((double)(rgba & 0xff)) / 256.0;
+ double opacity = (static_cast<double>(rgba & 0xff)) / 256.0;
GradientStop gs(rgb, opacity);
gi.stops.push_back(gs);
- }
+ }
+ Glib::ustring gradientName2;
if (SP_IS_LINEARGRADIENT(gradient))
- {
+ {
gi.style = "linear";
SPLinearGradient *linGrad = SP_LINEARGRADIENT(gradient);
- /*
- Geom::Point p1(linGrad->x1.value, linGrad->y1.value);
- p1 = p1 * tf;
- gi.x1 = p1[Geom::X];
- gi.y1 = p1[Geom::Y];
- Geom::Point p2(linGrad->x2.value, linGrad->y2.value);
- p2 = p2 * tf;
- gi.x2 = p2[Geom::X];
- gi.y2 = p2[Geom::Y];
- */
gi.x1 = linGrad->x1.value;
gi.y1 = linGrad->y1.value;
gi.x2 = linGrad->x2.value;
gi.y2 = linGrad->y2.value;
- }
+ gradientName2 = Glib::ustring::compose("ImportedLinearGradient%1", gradientTable.size());
+ }
else if (SP_IS_RADIALGRADIENT(gradient))
- {
+ {
gi.style = "radial";
SPRadialGradient *radGrad = SP_RADIALGRADIENT(gradient);
- gi.cx = radGrad->cx.computed * 100.0;//ODG cx is percentages
- gi.cy = radGrad->cy.computed * 100.0;
- }
+ Geom::OptRect bbox = item->documentVisualBounds();
+ gi.cx = (radGrad->cx.value-bbox->left())/bbox->width();
+ gi.cy = (radGrad->cy.value-bbox->top())/bbox->height();
+ gradientName2 = Glib::ustring::compose("ImportedRadialGradient%1", gradientTable.size());
+ }
else
- {
+ {
g_warning("not a supported gradient type");
return false;
- }
+ }
//Look for existing identical style;
bool gradientMatch = false;
std::vector<GradientInfo>::iterator iter;
for (iter=gradientTable.begin() ; iter!=gradientTable.end() ; ++iter)
- {
+ {
if (gi.equals(*iter))
- {
+ {
//map to existing gradientTable entry
- Glib::ustring gradientName = iter->name;
- //g_message("found duplicate style:%s", gradientName.c_str());
+ gradientName = iter->name;
gradientLookupTable[id] = gradientName;
gradientMatch = true;
break;
- }
}
+ }
if (gradientMatch)
+ {
return true;
+ }
- //## No match, let us write a new entry
- char buf[16];
- snprintf(buf, 15, "gradient%d", (int)gradientTable.size());
- Glib::ustring gradientName = buf;
+ // No match, let us write a new entry
+ gradientName = gradientName2;
gi.name = gradientName;
gradientTable.push_back(gi);
gradientLookupTable[id] = gradientName;
- int gradientCount = gradientTable.size();
-
+ // int gradientCount = gradientTable.size();
+ char buf[128];
if (gi.style == "linear")
- {
+ {
/*
===================================================================
LINEAR gradient. We need something that looks like this:
@@ -1740,36 +1555,25 @@ bool OdfOutput::processGradient(Writer &outs, SPItem *item,
===================================================================
*/
if (gi.stops.size() < 2)
- {
+ {
g_warning("Need at least 2 stops for a linear gradient");
- return false;;
- }
- outs.printf("<svg:linearGradient ");
- outs.printf("id=\"%#s_g\" ", gi.name.c_str());
- outs.printf("draw:name=\"%#s_g\"\n", gi.name.c_str());
- outs.printf(" draw:display-name=\"imported linear %d\"\n",
- gradientCount);
- outs.printf(" svg:gradientUnits=\"objectBoundingBox\"\n");
- outs.printf(" svg:x1=\"%05.3fcm\" svg:y1=\"%05.3fcm\"\n",
- gi.x1 * pxToCm, gi.y1 * pxToCm);
- outs.printf(" svg:x2=\"%05.3fcm\" svg:y2=\"%05.3fcm\">\n",
- gi.x2 * pxToCm, gi.y2 * pxToCm);
- outs.printf(" <svg:stop\n");
- outs.printf(" svg:stop-color=\"#%06lx\"\n",
- gi.stops[0].rgb);
- outs.printf(" svg:stop-opacity=\"%f%%\"\n",
- gi.stops[0].opacity * 100.0);
- outs.printf(" svg:offset=\"0\"/>\n");
- outs.printf(" <svg:stop\n");
- outs.printf(" svg:stop-color=\"#%06lx\"\n",
- gi.stops[1].rgb);
- outs.printf(" svg:stop-opacity=\"%f%%\"\n",
- gi.stops[1].opacity * 100.0);
- outs.printf(" svg:offset=\"1\"/>\n");
- outs.printf("</svg:linearGradient>\n");
+ return false;
}
+ output += Glib::ustring::compose("<draw:gradient draw:name=\"%1\"", gi.name);
+ output += Glib::ustring::compose(" draw:display-name=\"%1\"", gi.name);
+ output += " draw:style=\"linear\"";
+ snprintf(buf, 127, " draw:start-color=\"#%06lx\" draw:end-color=\"#%06lx\"", gi.stops[0].rgb, gi.stops[1].rgb);
+ output += buf;
+ //TODO: apply maths, to define begin of gradient, taking gradient begin and end, as well as object boundary into account
+ double angle = (gi.y2-gi.y1);
+ angle = (angle != 0.) ? (atan((gi.x2-gi.x1)/(gi.y2-gi.y1))* 180. / pi) : 90;
+ angle = (angle < 0)?(180+angle):angle;
+ angle = angle * 10; //why do we need this: precision?????????????
+ output += Glib::ustring::compose(" draw:start-intensity=\"%1\" draw:end-intensity=\"%2\" draw:angle=\"%3\"/>\n",
+ gi.stops[0].opacity * 100.0, gi.stops[1].opacity * 100.0, angle);// draw:border=\"0%%\"
+ }
else if (gi.style == "radial")
- {
+ {
/*
===================================================================
RADIAL gradient. We need something that looks like this:
@@ -1783,62 +1587,31 @@ bool OdfOutput::processGradient(Writer &outs, SPItem *item,
===================================================================
*/
if (gi.stops.size() < 2)
- {
+ {
g_warning("Need at least 2 stops for a radial gradient");
return false;
- }
- outs.printf("<svg:radialGradient ");
- outs.printf("id=\"%#s_g\" ", gi.name.c_str());
- outs.printf("draw:name=\"%#s_g\"\n", gi.name.c_str());
- outs.printf(" draw:display-name=\"imported radial %d\"\n",
- gradientCount);
- outs.printf(" svg:gradientUnits=\"objectBoundingBox\"\n");
- outs.printf(" svg:cx=\"%05.3f\" svg:cy=\"%05.3f\"\n",
- gi.cx, gi.cy);
- outs.printf(" svg:fx=\"%05.3f\" svg:fy=\"%05.3f\"\n",
- gi.fx, gi.fy);
- outs.printf(" svg:r=\"%05.3f\">\n",
- gi.r);
- outs.printf(" <svg:stop\n");
- outs.printf(" svg:stop-color=\"#%06lx\"\n",
- gi.stops[0].rgb);
- outs.printf(" svg:stop-opacity=\"%f%%\"\n",
- gi.stops[0].opacity * 100.0);
- outs.printf(" svg:offset=\"0\"/>\n");
- outs.printf(" <svg:stop\n");
- outs.printf(" svg:stop-color=\"#%06lx\"\n",
- gi.stops[1].rgb);
- outs.printf(" svg:stop-opacity=\"%f%%\"\n",
- gi.stops[1].opacity * 100.0);
- outs.printf(" svg:offset=\"1\"/>\n");
- outs.printf("</svg:radialGradient>\n");
}
+ output += Glib::ustring::compose("<draw:gradient draw:name=\"%1\" draw:display-name=\"%1\" ", gi.name);
+ snprintf(buf, 127, "draw:cx=\"%05.3f\" draw:cy=\"%05.3f\" ", gi.cx*100, gi.cy*100);
+ output += Glib::ustring("draw:style=\"radial\" ") + buf;
+ snprintf(buf, 127, "draw:start-color=\"#%06lx\" draw:end-color=\"#%06lx\" ", gi.stops[0].rgb, gi.stops[1].rgb);
+ output += buf;
+ snprintf(buf, 127, "draw:start-intensity=\"%f%%\" draw:end-intensity=\"%f%%\" ", gi.stops[0].opacity*100.0, gi.stops[1].opacity*100.0);
+ output += buf;
+ output += "/>\n";//draw:border=\"0%\"
+ }
else
- {
+ {
g_warning("unsupported gradient style '%s'", gi.style.c_str());
return false;
- }
- outs.printf("<style:style style:name=\"%#s\" style:family=\"graphic\" ",
- gi.name.c_str());
- outs.printf("style:parent-style-name=\"standard\">\n");
- outs.printf(" <style:graphic-properties draw:fill=\"gradient\" ");
- outs.printf("draw:fill-gradient-name=\"%#s_g\"\n",
- gi.name.c_str());
- outs.printf(" draw:textarea-horizontal-align=\"center\" ");
- outs.printf("draw:textarea-vertical-align=\"middle\"/>\n");
- outs.printf("</style:style>\n\n");
-
+ }
return true;
}
-
-
/**
* SECOND PASS.
- * This is the main SPObject tree output to ODF. preprocess()
- * must be called prior to this, as elements will often reference
- * data parsed and tabled in preprocess().
+ * This is the main SPObject tree output to ODF.
*/
bool OdfOutput::writeTree(Writer &couts, Writer &souts,
Inkscape::XML::Node *node)
@@ -1846,22 +1619,19 @@ bool OdfOutput::writeTree(Writer &couts, Writer &souts,
//# Get the SPItem, if applicable
SPObject *reprobj = SP_ACTIVE_DOCUMENT->getObjectByRepr(node);
if (!reprobj)
+ {
return true;
+ }
if (!SP_IS_ITEM(reprobj))
- {
+ {
return true;
- }
+ }
SPItem *item = SP_ITEM(reprobj);
-
Glib::ustring nodeName = node->name();
Glib::ustring id = getAttribute(node, "id");
-
- //### Get SVG-to-ODF transform
- Geom::Affine tf = getODFTransform(item);
-
- //### Get ODF bounding box params for item
- Geom::OptRect bbox = getODFBoundingBox(item);
+ Geom::Affine tf = getODFTransform(item);//Get SVG-to-ODF transform
+ Geom::OptRect bbox = getODFBoundingBox(item);//Get ODF bounding box params for item
if (!bbox) {
return true;
}
@@ -1881,63 +1651,73 @@ bool OdfOutput::writeTree(Writer &couts, Writer &souts,
//# Do our stuff
SPCurve *curve = NULL;
-
-
if (nodeName == "svg" || nodeName == "svg:svg")
- {
+ {
//# Iterate through the children
for (Inkscape::XML::Node *child = node->firstChild() ;
child ; child = child->next())
- {
+ {
if (!writeTree(couts, souts, child))
+ {
return false;
}
- return true;
}
+ return true;
+ }
else if (nodeName == "g" || nodeName == "svg:g")
+ {
+ if (!id.empty())
{
- if (id.size() > 0)
couts.printf("<draw:g id=\"%s\">\n", id.c_str());
+ }
else
+ {
couts.printf("<draw:g>\n");
+ }
//# Iterate through the children
for (Inkscape::XML::Node *child = node->firstChild() ;
child ; child = child->next())
- {
+ {
if (!writeTree(couts, souts, child))
+ {
return false;
}
- if (id.size() > 0)
+ }
+ if (!id.empty())
+ {
couts.printf("</draw:g> <!-- id=\"%s\" -->\n", id.c_str());
+ }
else
+ {
couts.printf("</draw:g>\n");
- return true;
}
+ return true;
+ }
- //######################################
- //# S T Y L E
- //######################################
- processStyle(souts, item, id);
-
- //######################################
- //# G R A D I E N T
- //######################################
- processGradient(souts, item, id, tf);
-
-
-
-
- //######################################
- //# I T E M D A T A
- //######################################
- //g_message("##### %s #####", nodeName.c_str());
+ //# GRADIENT
+ Glib::ustring gradientNameFill;
+ Glib::ustring gradientNameStroke;
+ Glib::ustring outputFill;
+ Glib::ustring outputStroke;
+ Glib::ustring outputStyle;
+
+ processGradient(item, id, tf, gradientNameFill, outputFill, 1);
+ processGradient(item, id, tf, gradientNameStroke, outputStroke, 0);
+ souts.writeUString(outputFill);
+ souts.writeUString(outputStroke);
+
+ //# STYLE
+ processStyle(item, id, gradientNameFill, gradientNameStroke, outputStyle);
+ souts.writeUString(outputStyle);
+
+ //# ITEM DATA
if (nodeName == "image" || nodeName == "svg:image")
- {
+ {
if (!SP_IS_IMAGE(item))
- {
- g_warning("<image> is not an SPImage. Why? ;-)");
+ {
+ g_warning("<image> is not an SPImage.");
return false;
- }
+ }
SPImage *img = SP_IMAGE(item);
double ix = img->x.value;
@@ -1949,8 +1729,6 @@ bool OdfOutput::writeTree(Writer &couts, Writer &souts,
ibbox = ibbox * tf;
ix = ibbox.min()[Geom::X];
iy = ibbox.min()[Geom::Y];
- //iwidth = ibbox.max()[Geom::X] - ibbox.min()[Geom::X];
- //iheight = ibbox.max()[Geom::Y] - ibbox.min()[Geom::Y];
iwidth = xscale * iwidth;
iheight = yscale * iheight;
@@ -1961,99 +1739,89 @@ bool OdfOutput::writeTree(Writer &couts, Writer &souts,
Glib::ustring href = getAttribute(node, "xlink:href");
std::map<Glib::ustring, Glib::ustring>::iterator iter = imageTable.find(href);
if (iter == imageTable.end())
- {
+ {
g_warning("image '%s' not in table", href.c_str());
return false;
- }
+ }
Glib::ustring newName = iter->second;
couts.printf("<draw:frame ");
- if (id.size() > 0)
+ if (!id.empty())
+ {
couts.printf("id=\"%s\" ", id.c_str());
+ }
couts.printf("draw:style-name=\"gr1\" draw:text-style-name=\"P1\" draw:layer=\"layout\" ");
//no x or y. make them the translate transform, last one
couts.printf("svg:width=\"%.3fcm\" svg:height=\"%.3fcm\" ",
iwidth, iheight);
- if (itemTransformString.size() > 0)
- {
+ if (!itemTransformString.empty())
+ {
couts.printf("draw:transform=\"%s translate(%.3fcm, %.3fcm)\" ",
itemTransformString.c_str(), ix, iy);
- }
+ }
else
- {
- couts.printf("draw:transform=\"translate(%.3fcm, %.3fcm)\" ",
- ix, iy);
- }
+ {
+ couts.printf("draw:transform=\"translate(%.3fcm, %.3fcm)\" ", ix, iy);
+ }
- couts.printf(">\n");
+ couts.writeString(">\n");
couts.printf(" <draw:image xlink:href=\"%s\" xlink:type=\"simple\"\n",
newName.c_str());
- couts.printf(" xlink:show=\"embed\" xlink:actuate=\"onLoad\">\n");
- couts.printf(" <text:p/>\n");
- couts.printf(" </draw:image>\n");
- couts.printf("</draw:frame>\n");
+ couts.writeString(" xlink:show=\"embed\" xlink:actuate=\"onLoad\">\n");
+ couts.writeString(" <text:p/>\n");
+ couts.writeString(" </draw:image>\n");
+ couts.writeString("</draw:frame>\n");
return true;
- }
+ }
else if (SP_IS_SHAPE(item))
- {
- //g_message("### %s is a shape", nodeName.c_str());
+ {
curve = SP_SHAPE(item)->getCurve();
- }
+ }
else if (SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))
- {
+ {
curve = te_get_layout(item)->convertToCurves();
- }
+ }
if (curve)
- {
+ {
//### Default <path> output
-
- couts.printf("<draw:path ");
- if (id.size()>0)
+ couts.writeString("<draw:path ");
+ if (!id.empty())
+ {
couts.printf("id=\"%s\" ", id.c_str());
+ }
std::map<Glib::ustring, Glib::ustring>::iterator siter;
siter = styleLookupTable.find(id);
if (siter != styleLookupTable.end())
- {
+ {
Glib::ustring styleName = siter->second;
couts.printf("draw:style-name=\"%s\" ", styleName.c_str());
- }
-
- std::map<Glib::ustring, Glib::ustring>::iterator giter;
- giter = gradientLookupTable.find(id);
- if (giter != gradientLookupTable.end())
- {
- Glib::ustring gradientName = giter->second;
- couts.printf("draw:fill-gradient-name=\"%s\" ",
- gradientName.c_str());
- }
+ }
couts.printf("draw:layer=\"layout\" svg:x=\"%.3fcm\" svg:y=\"%.3fcm\" ",
bbox_x, bbox_y);
couts.printf("svg:width=\"%.3fcm\" svg:height=\"%.3fcm\" ",
bbox_width, bbox_height);
- couts.printf("svg:viewBox=\"0.0 0.0 %.3f %.3f\"\n",
+ couts.printf("svg:viewBox=\"0.0 0.0 %.3f %.3f\"",
bbox_width * 1000.0, bbox_height * 1000.0);
- couts.printf(" svg:d=\"");
+ couts.printf(" svg:d=\"");
int nrPoints = writePath(couts, curve->get_pathvector(),
tf, bbox_x, bbox_y);
- couts.printf("\"");
+ couts.writeString("\"");
- couts.printf(">\n");
+ couts.writeString(">\n");
couts.printf(" <!-- %d nodes -->\n", nrPoints);
- couts.printf("</draw:path>\n\n");
-
+ couts.writeString("</draw:path>\n\n");
curve->unref();
- }
+ }
return true;
}
-
/**
* Write the header for the content.xml file
*/
@@ -2062,59 +1830,56 @@ bool OdfOutput::writeStyleHeader(Writer &outs)
time_t tim;
time(&tim);
- outs.printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
- outs.printf("\n");
- outs.printf("\n");
- outs.printf("<!--\n");
- outs.printf("*************************************************************************\n");
- outs.printf(" file: styles.xml\n");
- outs.printf(" Generated by Inkscape: %s", ctime(&tim)); //ctime has its own <cr>
- outs.printf(" http://www.inkscape.org\n");
- outs.printf("*************************************************************************\n");
- outs.printf("-->\n");
- outs.printf("\n");
- outs.printf("\n");
- outs.printf("<office:document-styles\n");
- outs.printf(" xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"\n");
- outs.printf(" xmlns:style=\"urn:oasis:names:tc:opendocument:xmlns:style:1.0\"\n");
- outs.printf(" xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\"\n");
- outs.printf(" xmlns:table=\"urn:oasis:names:tc:opendocument:xmlns:table:1.0\"\n");
- outs.printf(" xmlns:draw=\"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0\"\n");
- outs.printf(" xmlns:fo=\"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0\"\n");
- outs.printf(" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n");
- outs.printf(" xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n");
- outs.printf(" xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"\n");
- outs.printf(" xmlns:number=\"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0\"\n");
- outs.printf(" xmlns:presentation=\"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0\"\n");
- outs.printf(" xmlns:svg=\"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0\"\n");
- outs.printf(" xmlns:chart=\"urn:oasis:names:tc:opendocument:xmlns:chart:1.0\"\n");
- outs.printf(" xmlns:dr3d=\"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0\"\n");
- outs.printf(" xmlns:math=\"http://www.w3.org/1998/Math/MathML\"\n");
- outs.printf(" xmlns:form=\"urn:oasis:names:tc:opendocument:xmlns:form:1.0\"\n");
- outs.printf(" xmlns:script=\"urn:oasis:names:tc:opendocument:xmlns:script:1.0\"\n");
- outs.printf(" xmlns:ooo=\"http://openoffice.org/2004/office\"\n");
- outs.printf(" xmlns:ooow=\"http://openoffice.org/2004/writer\"\n");
- outs.printf(" xmlns:oooc=\"http://openoffice.org/2004/calc\"\n");
- outs.printf(" xmlns:dom=\"http://www.w3.org/2001/xml-events\"\n");
- outs.printf(" xmlns:xforms=\"http://www.w3.org/2002/xforms\"\n");
- outs.printf(" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n");
- outs.printf(" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n");
- outs.printf(" xmlns:smil=\"urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0\"\n");
- outs.printf(" xmlns:anim=\"urn:oasis:names:tc:opendocument:xmlns:animation:1.0\"\n");
- outs.printf(" office:version=\"1.0\">\n");
- outs.printf("\n");
- outs.printf("\n");
- outs.printf("<!--\n");
- outs.printf("*************************************************************************\n");
- outs.printf(" S T Y L E S\n");
- outs.printf(" Style entries have been pulled from the svg style and\n");
- outs.printf(" representation attributes in the SVG tree. The tree elements\n");
- outs.printf(" then refer to them by name, in the ODF manner\n");
- outs.printf("*************************************************************************\n");
- outs.printf("-->\n");
- outs.printf("\n");
- outs.printf("<office:styles>\n");
- outs.printf("\n");
+ outs.writeString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+ outs.writeString("\n");
+ outs.writeString("<!--\n");
+ outs.writeString("*************************************************************************\n");
+ outs.writeString(" file: styles.xml\n");
+ outs.printf (" Generated by Inkscape: %s", ctime(&tim)); //ctime has its own <cr>
+ outs.writeString(" http://www.inkscape.org\n");
+ outs.writeString("*************************************************************************\n");
+ outs.writeString("-->\n");
+ outs.writeString("\n");
+ outs.writeString("<office:document-styles\n");
+ outs.writeString(" xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"\n");
+ outs.writeString(" xmlns:style=\"urn:oasis:names:tc:opendocument:xmlns:style:1.0\"\n");
+ outs.writeString(" xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\"\n");
+ outs.writeString(" xmlns:table=\"urn:oasis:names:tc:opendocument:xmlns:table:1.0\"\n");
+ outs.writeString(" xmlns:draw=\"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0\"\n");
+ outs.writeString(" xmlns:fo=\"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0\"\n");
+ outs.writeString(" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n");
+ outs.writeString(" xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n");
+ outs.writeString(" xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"\n");
+ outs.writeString(" xmlns:number=\"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0\"\n");
+ outs.writeString(" xmlns:presentation=\"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0\"\n");
+ outs.writeString(" xmlns:svg=\"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0\"\n");
+ outs.writeString(" xmlns:chart=\"urn:oasis:names:tc:opendocument:xmlns:chart:1.0\"\n");
+ outs.writeString(" xmlns:dr3d=\"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0\"\n");
+ outs.writeString(" xmlns:math=\"http://www.w3.org/1998/Math/MathML\"\n");
+ outs.writeString(" xmlns:form=\"urn:oasis:names:tc:opendocument:xmlns:form:1.0\"\n");
+ outs.writeString(" xmlns:script=\"urn:oasis:names:tc:opendocument:xmlns:script:1.0\"\n");
+ outs.writeString(" xmlns:ooo=\"http://openoffice.org/2004/office\"\n");
+ outs.writeString(" xmlns:ooow=\"http://openoffice.org/2004/writer\"\n");
+ outs.writeString(" xmlns:oooc=\"http://openoffice.org/2004/calc\"\n");
+ outs.writeString(" xmlns:dom=\"http://www.w3.org/2001/xml-events\"\n");
+ outs.writeString(" xmlns:xforms=\"http://www.w3.org/2002/xforms\"\n");
+ outs.writeString(" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n");
+ outs.writeString(" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n");
+ outs.writeString(" xmlns:smil=\"urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0\"\n");
+ outs.writeString(" xmlns:anim=\"urn:oasis:names:tc:opendocument:xmlns:animation:1.0\"\n");
+ outs.writeString(" office:version=\"1.0\">\n");
+ outs.writeString("\n");
+ outs.writeString("<!--\n");
+ outs.writeString("*************************************************************************\n");
+ outs.writeString(" S T Y L E S\n");
+ outs.writeString(" Style entries have been pulled from the svg style and\n");
+ outs.writeString(" representation attributes in the SVG tree. The tree elements\n");
+ outs.writeString(" then refer to them by name, in the ODF manner\n");
+ outs.writeString("*************************************************************************\n");
+ outs.writeString("-->\n");
+ outs.writeString("\n");
+ outs.writeString("<office:styles>\n");
+ outs.writeString("\n");
return true;
}
@@ -2125,58 +1890,49 @@ bool OdfOutput::writeStyleHeader(Writer &outs)
*/
bool OdfOutput::writeStyleFooter(Writer &outs)
{
- outs.printf("\n");
- outs.printf("</office:styles>\n");
- outs.printf("\n");
- outs.printf("\n");
- outs.printf("<office:automatic-styles>\n");
- outs.printf("<!-- ####### 'Standard' styles ####### -->\n");
- outs.printf("<style:style style:name=\"dp1\" style:family=\"drawing-page\"/>\n");
- outs.printf("<style:style style:name=\"gr1\" style:family=\"graphic\" style:parent-style-name=\"standard\">\n");
- outs.printf(" <style:graphic-properties draw:stroke=\"none\" draw:fill=\"none\"\n");
- outs.printf(" draw:textarea-horizontal-align=\"center\"\n");
- outs.printf(" draw:textarea-vertical-align=\"middle\" draw:color-mode=\"standard\"\n");
- outs.printf(" draw:luminance=\"0%%\" draw:contrast=\"0%%\" draw:gamma=\"100%%\" draw:red=\"0%%\"\n");
- outs.printf(" draw:green=\"0%%\" draw:blue=\"0%%\" fo:clip=\"rect(0cm 0cm 0cm 0cm)\"\n");
- outs.printf(" draw:image-opacity=\"100%%\" style:mirror=\"none\"/>\n");
- outs.printf("</style:style>\n");
- outs.printf("<style:style style:name=\"P1\" style:family=\"paragraph\">\n");
- outs.printf(" <style:paragraph-properties fo:text-align=\"center\"/>\n");
- outs.printf("</style:style>\n");
- outs.printf("</office:automatic-styles>\n");
- outs.printf("\n");
- outs.printf("\n");
- outs.printf("<office:master-styles>\n");
- outs.printf("<draw:layer-set>\n");
- outs.printf(" <draw:layer draw:name=\"layout\"/>\n");
- outs.printf(" <draw:layer draw:name=\"background\"/>\n");
- outs.printf(" <draw:layer draw:name=\"backgroundobjects\"/>\n");
- outs.printf(" <draw:layer draw:name=\"controls\"/>\n");
- outs.printf(" <draw:layer draw:name=\"measurelines\"/>\n");
- outs.printf("</draw:layer-set>\n");
- outs.printf("\n");
- outs.printf("<style:master-page style:name=\"Default\"\n");
- outs.printf(" style:page-master-name=\"PM1\" draw:style-name=\"dp1\"/>\n");
- outs.printf("</office:master-styles>\n");
- outs.printf("\n");
- outs.printf("\n");
- outs.printf("\n");
- outs.printf("</office:document-styles>\n");
- outs.printf("\n");
- outs.printf("<!--\n");
- outs.printf("*************************************************************************\n");
- outs.printf(" E N D O F F I L E\n");
- outs.printf(" Have a nice day - ishmal\n");
- outs.printf("*************************************************************************\n");
- outs.printf("-->\n");
- outs.printf("\n");
+ outs.writeString("\n");
+ outs.writeString("</office:styles>\n");
+ outs.writeString("\n");
+ outs.writeString("<office:automatic-styles>\n");
+ outs.writeString("<!-- ####### 'Standard' styles ####### -->\n");
+ outs.writeString("<style:style style:name=\"dp1\" style:family=\"drawing-page\"/>\n");
+ outs.writeString("<style:style style:name=\"standard\" style:family=\"graphic\">\n");
+
+///TODO: add default document style here
+
+ outs.writeString("</style:style>\n");
+ outs.writeString("<style:style style:name=\"gr1\" style:family=\"graphic\" style:parent-style-name=\"standard\">\n");
+ outs.writeString(" <style:graphic-properties draw:stroke=\"none\" draw:fill=\"none\"\n");
+ outs.writeString(" draw:textarea-horizontal-align=\"center\"\n");
+ outs.writeString(" draw:textarea-vertical-align=\"middle\" draw:color-mode=\"standard\"\n");
+ outs.writeString(" draw:luminance=\"0%\" draw:contrast=\"0%\" draw:gamma=\"100%\" draw:red=\"0%\"\n");
+ outs.writeString(" draw:green=\"0%\" draw:blue=\"0%\" fo:clip=\"rect(0cm 0cm 0cm 0cm)\"\n");
+ outs.writeString(" draw:image-opacity=\"100%\" style:mirror=\"none\"/>\n");
+ outs.writeString("</style:style>\n");
+ outs.writeString("<style:style style:name=\"P1\" style:family=\"paragraph\">\n");
+ outs.writeString(" <style:paragraph-properties fo:text-align=\"center\"/>\n");
+ outs.writeString("</style:style>\n");
+ outs.writeString("</office:automatic-styles>\n");
+ outs.writeString("\n");
+ outs.writeString("<office:master-styles>\n");
+ outs.writeString("<draw:layer-set>\n");
+ outs.writeString(" <draw:layer draw:name=\"layout\"/>\n");
+ outs.writeString(" <draw:layer draw:name=\"background\"/>\n");
+ outs.writeString(" <draw:layer draw:name=\"backgroundobjects\"/>\n");
+ outs.writeString(" <draw:layer draw:name=\"controls\"/>\n");
+ outs.writeString(" <draw:layer draw:name=\"measurelines\"/>\n");
+ outs.writeString("</draw:layer-set>\n");
+ outs.writeString("\n");
+ outs.writeString("<style:master-page style:name=\"Default\"\n");
+ outs.writeString(" style:page-master-name=\"PM1\" draw:style-name=\"dp1\"/>\n");
+ outs.writeString("</office:master-styles>\n");
+ outs.writeString("\n");
+ outs.writeString("</office:document-styles>\n");
return true;
}
-
-
/**
* Write the header for the content.xml file
*/
@@ -2185,69 +1941,61 @@ bool OdfOutput::writeContentHeader(Writer &outs)
time_t tim;
time(&tim);
- outs.printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
- outs.printf("\n");
- outs.printf("\n");
- outs.printf("<!--\n");
- outs.printf("*************************************************************************\n");
- outs.printf(" file: content.xml\n");
- outs.printf(" Generated by Inkscape: %s", ctime(&tim)); //ctime has its own <cr>
- outs.printf(" http://www.inkscape.org\n");
- outs.printf("*************************************************************************\n");
- outs.printf("-->\n");
- outs.printf("\n");
- outs.printf("\n");
- outs.printf("<office:document-content\n");
- outs.printf(" xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"\n");
- outs.printf(" xmlns:style=\"urn:oasis:names:tc:opendocument:xmlns:style:1.0\"\n");
- outs.printf(" xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\"\n");
- outs.printf(" xmlns:table=\"urn:oasis:names:tc:opendocument:xmlns:table:1.0\"\n");
- outs.printf(" xmlns:draw=\"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0\"\n");
- outs.printf(" xmlns:fo=\"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0\"\n");
- outs.printf(" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n");
- outs.printf(" xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n");
- outs.printf(" xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"\n");
- outs.printf(" xmlns:number=\"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0\"\n");
- outs.printf(" xmlns:presentation=\"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0\"\n");
- outs.printf(" xmlns:svg=\"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0\"\n");
- outs.printf(" xmlns:chart=\"urn:oasis:names:tc:opendocument:xmlns:chart:1.0\"\n");
- outs.printf(" xmlns:dr3d=\"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0\"\n");
- outs.printf(" xmlns:math=\"http://www.w3.org/1998/Math/MathML\"\n");
- outs.printf(" xmlns:form=\"urn:oasis:names:tc:opendocument:xmlns:form:1.0\"\n");
- outs.printf(" xmlns:script=\"urn:oasis:names:tc:opendocument:xmlns:script:1.0\"\n");
- outs.printf(" xmlns:ooo=\"http://openoffice.org/2004/office\"\n");
- outs.printf(" xmlns:ooow=\"http://openoffice.org/2004/writer\"\n");
- outs.printf(" xmlns:oooc=\"http://openoffice.org/2004/calc\"\n");
- outs.printf(" xmlns:dom=\"http://www.w3.org/2001/xml-events\"\n");
- outs.printf(" xmlns:xforms=\"http://www.w3.org/2002/xforms\"\n");
- outs.printf(" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n");
- outs.printf(" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n");
- outs.printf(" xmlns:smil=\"urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0\"\n");
- outs.printf(" xmlns:anim=\"urn:oasis:names:tc:opendocument:xmlns:animation:1.0\"\n");
- outs.printf(" office:version=\"1.0\">\n");
- outs.printf("\n");
- outs.printf("\n");
- outs.printf("<office:scripts/>\n");
- outs.printf("\n");
- outs.printf("\n");
- outs.printf("<!--\n");
- outs.printf("*************************************************************************\n");
- outs.printf(" D R A W I N G\n");
- outs.printf(" This section is the heart of SVG-ODF conversion. We are\n");
- outs.printf(" starting with simple conversions, and will slowly evolve\n");
- outs.printf(" into a 'smarter' translation as time progresses. Any help\n");
- outs.printf(" in improving .odg export is welcome.\n");
- outs.printf("*************************************************************************\n");
- outs.printf("-->\n");
- outs.printf("\n");
- outs.printf("\n");
- outs.printf("<office:body>\n");
- outs.printf("<office:drawing>\n");
- outs.printf("<draw:page draw:name=\"page1\" draw:style-name=\"dp1\"\n");
- outs.printf(" draw:master-page-name=\"Default\">\n");
- outs.printf("\n");
- outs.printf("\n");
-
+ outs.writeString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+ outs.writeString("\n");
+ outs.writeString("<!--\n");
+ outs.writeString("*************************************************************************\n");
+ outs.writeString(" file: content.xml\n");
+ outs.printf (" Generated by Inkscape: %s", ctime(&tim)); //ctime has its own <cr>
+ outs.writeString(" http://www.inkscape.org\n");
+ outs.writeString("*************************************************************************\n");
+ outs.writeString("-->\n");
+ outs.writeString("\n");
+ outs.writeString("<office:document-content\n");
+ outs.writeString(" xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"\n");
+ outs.writeString(" xmlns:style=\"urn:oasis:names:tc:opendocument:xmlns:style:1.0\"\n");
+ outs.writeString(" xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\"\n");
+ outs.writeString(" xmlns:table=\"urn:oasis:names:tc:opendocument:xmlns:table:1.0\"\n");
+ outs.writeString(" xmlns:draw=\"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0\"\n");
+ outs.writeString(" xmlns:fo=\"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0\"\n");
+ outs.writeString(" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n");
+ outs.writeString(" xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n");
+ outs.writeString(" xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"\n");
+ outs.writeString(" xmlns:number=\"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0\"\n");
+ outs.writeString(" xmlns:presentation=\"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0\"\n");
+ outs.writeString(" xmlns:svg=\"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0\"\n");
+ outs.writeString(" xmlns:chart=\"urn:oasis:names:tc:opendocument:xmlns:chart:1.0\"\n");
+ outs.writeString(" xmlns:dr3d=\"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0\"\n");
+ outs.writeString(" xmlns:math=\"http://www.w3.org/1998/Math/MathML\"\n");
+ outs.writeString(" xmlns:form=\"urn:oasis:names:tc:opendocument:xmlns:form:1.0\"\n");
+ outs.writeString(" xmlns:script=\"urn:oasis:names:tc:opendocument:xmlns:script:1.0\"\n");
+ outs.writeString(" xmlns:ooo=\"http://openoffice.org/2004/office\"\n");
+ outs.writeString(" xmlns:ooow=\"http://openoffice.org/2004/writer\"\n");
+ outs.writeString(" xmlns:oooc=\"http://openoffice.org/2004/calc\"\n");
+ outs.writeString(" xmlns:dom=\"http://www.w3.org/2001/xml-events\"\n");
+ outs.writeString(" xmlns:xforms=\"http://www.w3.org/2002/xforms\"\n");
+ outs.writeString(" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n");
+ outs.writeString(" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n");
+ outs.writeString(" xmlns:smil=\"urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0\"\n");
+ outs.writeString(" xmlns:anim=\"urn:oasis:names:tc:opendocument:xmlns:animation:1.0\"\n");
+ outs.writeString(" office:version=\"1.0\">\n");
+ outs.writeString("<office:scripts/>\n");
+ outs.writeString("\n");
+ outs.writeString("<!--\n");
+ outs.writeString("*************************************************************************\n");
+ outs.writeString(" D R A W I N G\n");
+ outs.writeString(" This section is the heart of SVG-ODF conversion. We are\n");
+ outs.writeString(" starting with simple conversions, and will slowly evolve\n");
+ outs.writeString(" into a 'smarter' translation as time progresses. Any help\n");
+ outs.writeString(" in improving .odg export is welcome.\n");
+ outs.writeString("*************************************************************************\n");
+ outs.writeString("-->\n");
+ outs.writeString("\n");
+ outs.writeString("<office:body>\n");
+ outs.writeString("<office:drawing>\n");
+ outs.writeString("<draw:page draw:name=\"page1\" draw:style-name=\"dp1\"\n");
+ outs.writeString(" draw:master-page-name=\"Default\">\n");
+ outs.writeString("\n");
return true;
}
@@ -2257,37 +2005,18 @@ bool OdfOutput::writeContentHeader(Writer &outs)
*/
bool OdfOutput::writeContentFooter(Writer &outs)
{
- outs.printf("\n");
- outs.printf("\n");
-
- outs.printf("</draw:page>\n");
- outs.printf("</office:drawing>\n");
-
- outs.printf("\n");
- outs.printf("\n");
- outs.printf("<!-- ######### CONVERSION FROM SVG ENDS ######## -->\n");
- outs.printf("\n");
- outs.printf("\n");
-
- outs.printf("</office:body>\n");
- outs.printf("</office:document-content>\n");
- outs.printf("\n");
- outs.printf("\n");
- outs.printf("\n");
- outs.printf("<!--\n");
- outs.printf("*************************************************************************\n");
- outs.printf(" E N D O F F I L E\n");
- outs.printf(" Have a nice day - ishmal\n");
- outs.printf("*************************************************************************\n");
- outs.printf("-->\n");
- outs.printf("\n");
- outs.printf("\n");
-
+ outs.writeString("\n");
+ outs.writeString("</draw:page>\n");
+ outs.writeString("</office:drawing>\n");
+ outs.writeString("\n");
+ outs.writeString("<!-- ######### CONVERSION FROM SVG ENDS ######## -->\n");
+ outs.writeString("\n");
+ outs.writeString("</office:body>\n");
+ outs.writeString("</office:document-content>\n");
return true;
}
-
/**
* Write the content.xml file. Writes the namesspace headers, then
* calls writeTree().
@@ -2299,39 +2028,48 @@ bool OdfOutput::writeContent(ZipFile &zf, Inkscape::XML::Node *node)
OutputStreamWriter couts(cbouts);
if (!writeContentHeader(couts))
+ {
return false;
+ }
//Style.xml stream
BufferOutputStream sbouts;
OutputStreamWriter souts(sbouts);
if (!writeStyleHeader(souts))
+ {
return false;
-
+ }
//# Descend into the tree, doing all of our conversions
- //# to both files as the same time
+ //# to both files at the same time
+ char *oldlocale = g_strdup (setlocale (LC_NUMERIC, NULL));
+ setlocale (LC_NUMERIC, "C");
if (!writeTree(couts, souts, node))
- {
+ {
g_warning("Failed to convert SVG tree");
+ setlocale (LC_NUMERIC, oldlocale);
+ g_free (oldlocale);
return false;
- }
-
-
+ }
+ setlocale (LC_NUMERIC, oldlocale);
+ g_free (oldlocale);
//# Finish content file
if (!writeContentFooter(couts))
+ {
return false;
+ }
ZipEntry *ze = zf.newEntry("content.xml", "ODF master content file");
ze->setUncompressedData(cbouts.getBuffer());
ze->finish();
-
-
//# Finish style file
if (!writeStyleFooter(souts))
+ {
return false;
+ }
ze = zf.newEntry("styles.xml", "ODF style file");
ze->setUncompressedData(sbouts.getBuffer());
@@ -2344,8 +2082,7 @@ bool OdfOutput::writeContent(ZipFile &zf, Inkscape::XML::Node *node)
/**
* Resets class to its pristine condition, ready to use again
*/
-void
-OdfOutput::reset()
+void OdfOutput::reset()
{
metadata.clear();
styleTable.clear();
@@ -2353,23 +2090,17 @@ OdfOutput::reset()
gradientTable.clear();
gradientLookupTable.clear();
imageTable.clear();
-
-
}
/**
* Descends into the SVG tree, mapping things to ODF when appropriate
*/
-void
-OdfOutput::save(Inkscape::Extension::Output */*mod*/, SPDocument *doc, gchar const *filename)
+void OdfOutput::save(Inkscape::Extension::Output */*mod*/, SPDocument *doc, gchar const *filename)
{
reset();
- //g_message("native file:%s\n", filename);
- documentUri = URI(filename);
- /* fixme: It looks like we really are using documentUri as a URI, so we ought to call
- * g_filename_to_uri for the URI constructor. */
+ documentUri = Inkscape::URI(filename);
ZipFile zf;
preprocess(zf, doc->rroot);
@@ -2404,8 +2135,7 @@ OdfOutput::save(Inkscape::Extension::Output */*mod*/, SPDocument *doc, gchar con
* calls the extension system with the memory allocated XML that
* describes the data.
*/
-void
-OdfOutput::init()
+void OdfOutput::init()
{
Inkscape::Extension::build_from_mem(
"<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
@@ -2424,8 +2154,7 @@ OdfOutput::init()
/**
* Make sure that we are in the database
*/
-bool
-OdfOutput::check (Inkscape::Extension::Extension */*module*/)
+bool OdfOutput::check (Inkscape::Extension::Extension */*module*/)
{
/* We don't need a Key
if (NULL == Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_POV))
@@ -2435,30 +2164,6 @@ OdfOutput::check (Inkscape::Extension::Extension */*module*/)
return TRUE;
}
-
-
-//########################################################################
-//# I N P U T
-//########################################################################
-
-
-
-//#######################
-//# L A T E R !!! :-)
-//#######################
-
-
-
-
-
-
-
-
-
-
-
-
-
} //namespace Internal
} //namespace Extension
} //namespace Inkscape
diff --git a/src/extension/internal/odf.h b/src/extension/internal/odf.h
index 6b8883134..f8712d5eb 100644
--- a/src/extension/internal/odf.h
+++ b/src/extension/internal/odf.h
@@ -23,23 +23,20 @@
#ifndef EXTENSION_INTERNAL_ODG_OUT_H
#define EXTENSION_INTERNAL_ODG_OUT_H
-#include <dom/dom.h>
-#include <dom/io/stringstream.h>
-#include <dom/uri.h>
+#include <io/stringstream.h>
+#include <util/ziptool.h>
-#include <glibmm.h>
#include "extension/implementation/implementation.h"
-
#include <xml/repr.h>
-
#include <string>
#include <map>
-#include <dom/util/ziptool.h>
-#include <dom/io/domstream.h>
+#include "uri.h"
#include "sp-item.h"
+#include <glibmm/ustring.h>
+
namespace Inkscape
{
namespace Extension
@@ -47,9 +44,7 @@ namespace Extension
namespace Internal
{
-typedef org::w3c::dom::URI URI;
-typedef org::w3c::dom::io::Writer Writer;
-
+typedef Inkscape::IO::Writer Writer;
class StyleInfo
{
@@ -281,7 +276,7 @@ public:
private:
- URI documentUri;
+ Inkscape::URI documentUri;
void reset();
@@ -315,10 +310,10 @@ private:
bool writeStyle(ZipFile &zf);
- bool processStyle(Writer &outs, SPItem *item, const Glib::ustring &id);
+ bool processStyle(SPItem *item, const Glib::ustring &id, const Glib::ustring &gradientNameFill, const Glib::ustring &gradientNameStroke, Glib::ustring& output);
- bool processGradient(Writer &outs, SPItem *item,
- const Glib::ustring &id, Geom::Affine &tf);
+ bool processGradient(SPItem *item,
+ const Glib::ustring &id, Geom::Affine &tf, Glib::ustring& gradientName, Glib::ustring& output, bool checkFillGradient = 1);
bool writeStyleHeader(Writer &outs);
@@ -335,8 +330,6 @@ private:
};
-
-
} //namespace Internal
} //namespace Extension
} //namespace Inkscape
diff --git a/src/extension/internal/pdf-input-cairo.cpp b/src/extension/internal/pdf-input-cairo.cpp
index 5cb4f96c3..c45367c08 100644
--- a/src/extension/internal/pdf-input-cairo.cpp
+++ b/src/extension/internal/pdf-input-cairo.cpp
@@ -23,6 +23,10 @@
#include "extension/input.h"
#include "dialogs/dialog-events.h"
#include "document.h"
+#include "sp-root.h"
+#include "util/units.h"
+
+#include <2geom/rect.h>
#include "inkscape.h"
@@ -95,11 +99,7 @@ PdfImportCairoDialog::PdfImportCairoDialog(PopplerDocument *doc)
_cropTypeCombo = Gtk::manage(new class Gtk::ComboBoxText());
int num_crop_choices = sizeof(crop_setting_choices) / sizeof(crop_setting_choices[0]);
for ( int i = 0 ; i < num_crop_choices ; i++ ) {
-#if WITH_GTKMM_2_24
_cropTypeCombo->append(_(crop_setting_choices[i]));
-#else
- _cropTypeCombo->append_text(_(crop_setting_choices[i]));
-#endif
}
_cropTypeCombo->set_active_text(_(crop_setting_choices[0]));
_cropTypeCombo->set_sensitive(false);
@@ -112,7 +112,7 @@ PdfImportCairoDialog::PdfImportCairoDialog(PopplerDocument *doc)
#if WITH_GTKMM_3_0
_fallbackPrecisionSlider_adj = Gtk::Adjustment::create(2, 1, 256, 1, 10, 10);
- _fallbackPrecisionSlider = Gtk::manage(new Gtk::HScale(_fallbackPrecisionSlider_adj));
+ _fallbackPrecisionSlider = Gtk::manage(new Gtk::Scale(_fallbackPrecisionSlider_adj));
#else
_fallbackPrecisionSlider_adj = Gtk::manage(new class Gtk::Adjustment(2, 1, 256, 1, 10, 10));
_fallbackPrecisionSlider = Gtk::manage(new class Gtk::HScale(*_fallbackPrecisionSlider_adj));
@@ -124,11 +124,7 @@ PdfImportCairoDialog::PdfImportCairoDialog(PopplerDocument *doc)
// Text options
_labelText = Gtk::manage(new class Gtk::Label(_("Text handling:")));
_textHandlingCombo = Gtk::manage(new class Gtk::ComboBoxText());
-#if WITH_GTKMM_2_24
_textHandlingCombo->append(_("Import text as text"));
-#else
- _textHandlingCombo->append_text(_("Import text as text"));
-#endif
_textHandlingCombo->set_active_text(_("Import text as text"));
_localFontsCheck = Gtk::manage(new class Gtk::CheckButton(_("Replace PDF fonts by closest-named installed fonts")));
@@ -232,12 +228,20 @@ PdfImportCairoDialog::PdfImportCairoDialog(PopplerDocument *doc)
vbox1->pack_start(*_importSettingsFrame, Gtk::PACK_EXPAND_PADDING, 0);
hbox1->pack_start(*vbox1);
hbox1->pack_start(*_previewArea, Gtk::PACK_EXPAND_WIDGET, 4);
+
+#if WITH_GTKMM_3_0
+ get_content_area()->set_homogeneous(false);
+ get_content_area()->set_spacing(0);
+ get_content_area()->pack_start(*hbox1);
+#else
this->get_vbox()->set_homogeneous(false);
this->get_vbox()->set_spacing(0);
this->get_vbox()->pack_start(*hbox1);
+#endif
+
this->set_title(_("PDF Import Settings"));
this->set_modal(true);
- sp_transientize((GtkWidget *)this->gobj()); //Make transient
+ sp_transientize(GTK_WIDGET(this->gobj())); //Make transient
this->property_window_position().set_value(Gtk::WIN_POS_NONE);
this->set_resizable(true);
this->property_destroy_with_parent().set_value(false);
@@ -271,7 +275,11 @@ PdfImportCairoDialog::PdfImportCairoDialog(PopplerDocument *doc)
hbox1->show();
// Connect signals
+#if WITH_GTKMM_3_0
+ _previewArea->signal_draw().connect(sigc::mem_fun(*this, &PdfImportCairoDialog::_onDraw));
+#else
_previewArea->signal_expose_event().connect(sigc::mem_fun(*this, &PdfImportCairoDialog::_onExposePreview));
+#endif
_pageNumberSpin_adj->signal_value_changed().connect(sigc::mem_fun(*this, &PdfImportCairoDialog::_onPageNumberChanged));
_cropCheck->signal_toggled().connect(sigc::mem_fun(*this, &PdfImportCairoDialog::_onToggleCropping));
_fallbackPrecisionSlider_adj->signal_value_changed().connect(sigc::mem_fun(*this, &PdfImportCairoDialog::_onPrecisionChanged));
@@ -420,7 +428,7 @@ static void copy_cairo_surface_to_pixbuf (cairo_surface_t *surface,
cairo_height = gdk_pixbuf_get_height (pixbuf);
for (y = 0; y < cairo_height; y++)
{
- src = (unsigned int *) (cairo_data + y * cairo_rowstride);
+ src = reinterpret_cast<unsigned int *>(cairo_data + y * cairo_rowstride);
dst = pixbuf_data + y * pixbuf_rowstride;
for (x = 0; x < cairo_width; x++)
{
@@ -438,8 +446,15 @@ static void copy_cairo_surface_to_pixbuf (cairo_surface_t *surface,
/**
* \brief Updates the preview area with the previously rendered thumbnail
*/
-bool PdfImportCairoDialog::_onExposePreview(GdkEventExpose */*event*/) {
+#if !WITH_GTKMM_3_0
+bool PdfImportCairoDialog::_onExposePreview(GdkEventExpose * /*event*/) {
+ Cairo::RefPtr<Cairo::Context> cr = _previewArea->get_window()->create_cairo_context();
+ return _onDraw(cr);
+}
+#endif
+
+bool PdfImportCairoDialog::_onDraw(const Cairo::RefPtr<Cairo::Context>& cr) {
// Check if we have a thumbnail at all
if (!_thumb_data) {
return true;
@@ -461,24 +476,14 @@ bool PdfImportCairoDialog::_onExposePreview(GdkEventExpose */*event*/) {
// Set background to white
if (_render_thumb) {
thumb->fill(0xffffffff);
- Glib::RefPtr<Gdk::Pixmap> back_pixmap = Gdk::Pixmap::create(
- _previewArea->get_window(), _thumb_width, _thumb_height, -1);
- if (!back_pixmap) {
- return true;
- }
-
- Cairo::RefPtr<Cairo::Context> cr = back_pixmap->create_cairo_context();
Gdk::Cairo::set_source_pixbuf(cr, thumb, 0, 0);
cr->paint();
- _previewArea->get_window()->set_back_pixmap(back_pixmap, false);
- _previewArea->get_window()->clear();
}
// Copy the thumbnail image from the Cairo surface
if (_render_thumb) {
copy_cairo_surface_to_pixbuf(_cairo_surface, _thumb_data, thumb->gobj());
}
- Cairo::RefPtr<Cairo::Context> cr = _previewArea->get_window()->create_cairo_context();
Gdk::Cairo::set_source_pixbuf(cr, thumb, 0, _render_thumb ? 0 : 20);
cr->paint();
@@ -490,7 +495,7 @@ bool PdfImportCairoDialog::_onExposePreview(GdkEventExpose */*event*/) {
*/
void PdfImportCairoDialog::_setPreviewPage(int page) {
- PopplerPage *_previewed_page = poppler_document_get_page(_poppler_doc, page);
+ PopplerPage *_previewed_page = poppler_document_get_page(_poppler_doc, page-1);
// Try to get a thumbnail from the PDF if possible
if (!_render_thumb) {
@@ -619,6 +624,11 @@ PdfInputCairo::open(Inkscape::Extension::Input * /*mod*/, const gchar * uri) {
SPDocument * doc = SPDocument::createNewDocFromMem(output->c_str(), output->length(), TRUE);
+ // Set viewBox if it doesn't exist
+ if (doc && !doc->getRoot()->viewBox_set) {
+ doc->setViewBox(Geom::Rect::from_xywh(0, 0, doc->getWidth().value(doc->getDefaultUnit()), doc->getHeight().value(doc->getDefaultUnit())));
+ }
+
delete output;
g_object_unref(page);
g_object_unref(document);
@@ -629,8 +639,8 @@ PdfInputCairo::open(Inkscape::Extension::Input * /*mod*/, const gchar * uri) {
static cairo_status_t
_write_ustring_cb(void *closure, const unsigned char *data, unsigned int length)
{
- Glib::ustring* stream = (Glib::ustring*)closure;
- stream->append((const char*)data, length);
+ Glib::ustring* stream = static_cast<Glib::ustring*>(closure);
+ stream->append(reinterpret_cast<const char*>(data), length);
return CAIRO_STATUS_SUCCESS;
}
@@ -642,13 +652,13 @@ void
PdfInputCairo::init(void) {
Inkscape::Extension::build_from_mem(
"<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
- "<name>PDF Input</name>\n"
+ "<name>" N_("PDF Input") "</name>\n"
"<id>org.inkscape.input.cairo-pdf</id>\n"
"<input>\n"
"<extension>.pdf</extension>\n"
"<mimetype>application/pdf</mimetype>\n"
- "<filetypename>Adobe PDF via poppler-cairo (*.pdf)</filetypename>\n"
- "<filetypetooltip>PDF Document</filetypetooltip>\n"
+ "<filetypename>" N_("Adobe PDF via poppler-cairo (*.pdf)") "</filetypename>\n"
+ "<filetypetooltip>" N_("PDF Document") "</filetypetooltip>\n"
"</input>\n"
"</inkscape-extension>", new PdfInputCairo());
} // init
diff --git a/src/extension/internal/pdf-input-cairo.h b/src/extension/internal/pdf-input-cairo.h
index 15235ee19..b65d22f48 100644
--- a/src/extension/internal/pdf-input-cairo.h
+++ b/src/extension/internal/pdf-input-cairo.h
@@ -16,6 +16,10 @@
# include <config.h>
#endif
+#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H
+#include <glibmm/threads.h>
+#endif
+
#include <gtkmm/dialog.h>
#include <gtkmm/button.h>
#include <gtkmm/buttonbox.h>
@@ -37,6 +41,14 @@
#include "../implementation/implementation.h"
+namespace Gtk {
+#if WITH_GTKMM_3_0
+ class Scale;
+#else
+ class HScale;
+#endif
+}
+
namespace Inkscape {
namespace UI {
@@ -63,7 +75,11 @@ private:
void _setPreviewPage(int page);
// Signal handlers
+#if !WITH_GTKMM_3_0
bool _onExposePreview(GdkEventExpose *event);
+#endif
+
+ bool _onDraw(const Cairo::RefPtr<Cairo::Context>& cr);
void _onPageNumberChanged();
void _onToggleCropping();
void _onPrecisionChanged();
@@ -81,10 +97,11 @@ private:
class Inkscape::UI::Widget::Frame * _pageSettingsFrame;
class Gtk::Label * _labelPrecision;
class Gtk::Label * _labelPrecisionWarning;
- class Gtk::HScale * _fallbackPrecisionSlider;
#if WITH_GTKMM_3_0
+ class Gtk::Scale * _fallbackPrecisionSlider;
Glib::RefPtr<Gtk::Adjustment> _fallbackPrecisionSlider_adj;
#else
+ class Gtk::HScale * _fallbackPrecisionSlider;
class Gtk::Adjustment *_fallbackPrecisionSlider_adj;
#endif
class Gtk::Label * _labelPrecisionComment;
diff --git a/src/extension/internal/pdfinput/pdf-input.cpp b/src/extension/internal/pdfinput/pdf-input.cpp
index d4fc6a8a2..3155ac098 100644
--- a/src/extension/internal/pdfinput/pdf-input.cpp
+++ b/src/extension/internal/pdfinput/pdf-input.cpp
@@ -32,6 +32,7 @@
#endif
#include <gtkmm/alignment.h>
+#include <gtkmm/checkbutton.h>
#include <gtkmm/comboboxtext.h>
#include <gtkmm/drawingarea.h>
#include <gtkmm/frame.h>
@@ -45,6 +46,7 @@
#include "document-private.h"
#include "document-undo.h"
#include "inkscape.h"
+#include "util/units.h"
#include "dialogs/dialog-events.h"
#include <gtk/gtk.h>
@@ -111,11 +113,7 @@ PdfImportDialog::PdfImportDialog(PDFDoc *doc, const gchar */*uri*/)
_cropTypeCombo = Gtk::manage(new class Gtk::ComboBoxText());
int num_crop_choices = sizeof(crop_setting_choices) / sizeof(crop_setting_choices[0]);
for ( int i = 0 ; i < num_crop_choices ; i++ ) {
-#if WITH_GTKMM_2_24
_cropTypeCombo->append(_(crop_setting_choices[i]));
-#else
- _cropTypeCombo->append_text(_(crop_setting_choices[i]));
-#endif
}
_cropTypeCombo->set_active_text(_(crop_setting_choices[0]));
_cropTypeCombo->set_sensitive(false);
@@ -128,7 +126,7 @@ PdfImportDialog::PdfImportDialog(PDFDoc *doc, const gchar */*uri*/)
#if WITH_GTKMM_3_0
_fallbackPrecisionSlider_adj = Gtk::Adjustment::create(2, 1, 256, 1, 10, 10);
- _fallbackPrecisionSlider = Gtk::manage(new class Gtk::HScale(_fallbackPrecisionSlider_adj));
+ _fallbackPrecisionSlider = Gtk::manage(new class Gtk::Scale(_fallbackPrecisionSlider_adj));
#else
_fallbackPrecisionSlider_adj = Gtk::manage(new class Gtk::Adjustment(2, 1, 256, 1, 10, 10));
_fallbackPrecisionSlider = Gtk::manage(new class Gtk::HScale(*_fallbackPrecisionSlider_adj));
@@ -140,11 +138,7 @@ PdfImportDialog::PdfImportDialog(PDFDoc *doc, const gchar */*uri*/)
// Text options
_labelText = Gtk::manage(new class Gtk::Label(_("Text handling:")));
_textHandlingCombo = Gtk::manage(new class Gtk::ComboBoxText());
-#if WITH_GTKMM_2_24
_textHandlingCombo->append(_("Import text as text"));
-#else
- _textHandlingCombo->append_text(_("Import text as text"));
-#endif
_textHandlingCombo->set_active_text(_("Import text as text"));
_localFontsCheck = Gtk::manage(new class Gtk::CheckButton(_("Replace PDF fonts by closest-named installed fonts")));
@@ -248,12 +242,20 @@ PdfImportDialog::PdfImportDialog(PDFDoc *doc, const gchar */*uri*/)
vbox1->pack_start(*_importSettingsFrame, Gtk::PACK_EXPAND_PADDING, 0);
hbox1->pack_start(*vbox1);
hbox1->pack_start(*_previewArea, Gtk::PACK_EXPAND_WIDGET, 4);
+
+#if WITH_GTKMM_3_0
+ get_content_area()->set_homogeneous(false);
+ get_content_area()->set_spacing(0);
+ get_content_area()->pack_start(*hbox1);
+#else
this->get_vbox()->set_homogeneous(false);
this->get_vbox()->set_spacing(0);
this->get_vbox()->pack_start(*hbox1);
+#endif
+
this->set_title(_("PDF Import Settings"));
this->set_modal(true);
- sp_transientize((GtkWidget *)this->gobj()); //Make transient
+ sp_transientize(GTK_WIDGET(this->gobj())); //Make transient
this->property_window_position().set_value(Gtk::WIN_POS_NONE);
this->set_resizable(true);
this->property_destroy_with_parent().set_value(false);
@@ -287,7 +289,12 @@ PdfImportDialog::PdfImportDialog(PDFDoc *doc, const gchar */*uri*/)
hbox1->show();
// Connect signals
+#if WITH_GTKMM_3_0
+ _previewArea->signal_draw().connect(sigc::mem_fun(*this, &PdfImportDialog::_onDraw));
+#else
_previewArea->signal_expose_event().connect(sigc::mem_fun(*this, &PdfImportDialog::_onExposePreview));
+#endif
+
_pageNumberSpin_adj->signal_value_changed().connect(sigc::mem_fun(*this, &PdfImportDialog::_onPageNumberChanged));
_cropCheck->signal_toggled().connect(sigc::mem_fun(*this, &PdfImportDialog::_onToggleCropping));
_fallbackPrecisionSlider_adj->signal_value_changed().connect(sigc::mem_fun(*this, &PdfImportDialog::_onPrecisionChanged));
@@ -449,7 +456,7 @@ static void copy_cairo_surface_to_pixbuf (cairo_surface_t *surface,
cairo_height = gdk_pixbuf_get_height (pixbuf);
for (y = 0; y < cairo_height; y++)
{
- src = (unsigned int *) (cairo_data + y * cairo_rowstride);
+ src = reinterpret_cast<unsigned int *>(cairo_data + y * cairo_rowstride);
dst = pixbuf_data + y * pixbuf_rowstride;
for (x = 0; x < cairo_width; x++)
{
@@ -469,8 +476,14 @@ static void copy_cairo_surface_to_pixbuf (cairo_surface_t *surface,
/**
* \brief Updates the preview area with the previously rendered thumbnail
*/
-bool PdfImportDialog::_onExposePreview(GdkEventExpose */*event*/) {
+#if !WITH_GTKMM_3_0
+bool PdfImportDialog::_onExposePreview(GdkEventExpose * /*event*/) {
+ Cairo::RefPtr<Cairo::Context> cr = _previewArea->get_window()->create_cairo_context();
+ return _onDraw(cr);
+}
+#endif
+bool PdfImportDialog::_onDraw(const Cairo::RefPtr<Cairo::Context>& cr) {
// Check if we have a thumbnail at all
if (!_thumb_data) {
return true;
@@ -478,6 +491,7 @@ bool PdfImportDialog::_onExposePreview(GdkEventExpose */*event*/) {
// Create the pixbuf for the thumbnail
Glib::RefPtr<Gdk::Pixbuf> thumb;
+
if (_render_thumb) {
thumb = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true,
8, _thumb_width, _thumb_height);
@@ -492,17 +506,8 @@ bool PdfImportDialog::_onExposePreview(GdkEventExpose */*event*/) {
// Set background to white
if (_render_thumb) {
thumb->fill(0xffffffff);
- Glib::RefPtr<Gdk::Pixmap> back_pixmap = Gdk::Pixmap::create(
- _previewArea->get_window(), _thumb_width, _thumb_height, -1);
- if (!back_pixmap) {
- return true;
- }
-
- Cairo::RefPtr<Cairo::Context> cr = back_pixmap->create_cairo_context();
Gdk::Cairo::set_source_pixbuf(cr, thumb, 0, 0);
cr->paint();
- _previewArea->get_window()->set_back_pixmap(back_pixmap, false);
- _previewArea->get_window()->clear();
}
#ifdef HAVE_POPPLER_CAIRO
// Copy the thumbnail image from the Cairo surface
@@ -511,7 +516,6 @@ bool PdfImportDialog::_onExposePreview(GdkEventExpose */*event*/) {
}
#endif
- Cairo::RefPtr<Cairo::Context> cr = _previewArea->get_window()->create_cairo_context();
Gdk::Cairo::set_source_pixbuf(cr, thumb, 0, _render_thumb ? 0 : 20);
cr->paint();
return true;
@@ -586,12 +590,19 @@ void PdfImportDialog::_setPreviewPage(int page) {
////////////////////////////////////////////////////////////////////////////////
+bool
+PdfInput::wasCancelled () {
+ return _cancelled;
+}
+
/**
* Parses the selected page of the given PDF document using PdfParser.
*/
SPDocument *
PdfInput::open(::Inkscape::Extension::Input * /*mod*/, const gchar * uri) {
+ _cancelled = false;
+
// Initialize the globalParams variable for poppler
if (!globalParams) {
globalParams = new GlobalParams();
@@ -602,7 +613,7 @@ PdfInput::open(::Inkscape::Extension::Input * /*mod*/, const gchar * uri) {
PDFDoc *pdf_doc = new PDFDoc(filename_goo, NULL, NULL, NULL); // TODO: Could ask for password
//delete filename_goo;
#else
- wchar_t *wfilename = (wchar_t*)g_utf8_to_utf16 (uri, -1, NULL, NULL, NULL);
+ wchar_t *wfilename = reinterpret_cast<wchar_t*>(g_utf8_to_utf16 (uri, -1, NULL, NULL, NULL));
if (wfilename == NULL) {
return NULL;
@@ -646,6 +657,7 @@ PdfInput::open(::Inkscape::Extension::Input * /*mod*/, const gchar * uri) {
if (inkscape_use_gui()) {
dlg = new PdfImportDialog(pdf_doc, uri);
if (!dlg->showDialog()) {
+ _cancelled = true;
delete dlg;
delete pdf_doc;
return NULL;
@@ -736,6 +748,11 @@ PdfInput::open(::Inkscape::Extension::Input * /*mod*/, const gchar * uri) {
delete pdf_doc;
delete dlg;
+ // Set viewBox if it doesn't exist
+ if (!doc->getRoot()->viewBox_set) {
+ doc->setViewBox(Geom::Rect::from_xywh(0, 0, doc->getWidth().value(doc->getDefaultUnit()), doc->getHeight().value(doc->getDefaultUnit())));
+ }
+
// Restore undo
DocumentUndo::setUndoSensitive(doc, saved);
diff --git a/src/extension/internal/pdfinput/pdf-input.h b/src/extension/internal/pdfinput/pdf-input.h
index 3b79e6f29..f22a783ff 100644
--- a/src/extension/internal/pdfinput/pdf-input.h
+++ b/src/extension/internal/pdfinput/pdf-input.h
@@ -16,6 +16,10 @@
#ifdef HAVE_POPPLER
+#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H
+#include <glibmm/threads.h>
+#endif
+
#include <gtkmm/dialog.h>
#include "../../implementation/implementation.h"
@@ -39,7 +43,11 @@ namespace Gtk {
class DrawingArea;
class Frame;
class HBox;
+#if WITH_GTKMM_3_0
+ class Scale;
+#else
class HScale;
+#endif
class VBox;
class Label;
}
@@ -73,7 +81,11 @@ private:
void _setPreviewPage(int page);
// Signal handlers
+#if !WITH_GTKMM_3_0
bool _onExposePreview(GdkEventExpose *event);
+#endif
+
+ bool _onDraw(const Cairo::RefPtr<Cairo::Context>& cr);
void _onPageNumberChanged();
void _onToggleCropping();
void _onPrecisionChanged();
@@ -91,10 +103,11 @@ private:
class Inkscape::UI::Widget::Frame * _pageSettingsFrame;
class Gtk::Label * _labelPrecision;
class Gtk::Label * _labelPrecisionWarning;
- class Gtk::HScale * _fallbackPrecisionSlider;
#if WITH_GTKMM_3_0
+ class Gtk::Scale * _fallbackPrecisionSlider;
Glib::RefPtr<Gtk::Adjustment> _fallbackPrecisionSlider_adj;
#else
+ class Gtk::HScale * _fallbackPrecisionSlider;
class Gtk::Adjustment *_fallbackPrecisionSlider_adj;
#endif
class Gtk::Label * _labelPrecisionComment;
@@ -131,7 +144,9 @@ public:
SPDocument *open( Inkscape::Extension::Input *mod,
const gchar *uri );
static void init( void );
-
+ virtual bool wasCancelled();
+private:
+ bool _cancelled;
};
} // namespace Implementation
diff --git a/src/extension/internal/pdfinput/pdf-parser.cpp b/src/extension/internal/pdfinput/pdf-parser.cpp
index 5e8b005e2..b398486e6 100644
--- a/src/extension/internal/pdfinput/pdf-parser.cpp
+++ b/src/extension/internal/pdfinput/pdf-parser.cpp
@@ -34,7 +34,7 @@ extern "C" {
#include "svg-builder.h"
#include "Gfx.h"
#include "pdf-parser.h"
-#include "unit-constants.h"
+#include "util/units.h"
#include "goo/gmem.h"
#include "goo/GooTimer.h"
@@ -76,10 +76,6 @@ extern "C" {
// Operator table
//------------------------------------------------------------------------
-#ifdef WIN32 // this works around a bug in the VC7 compiler
-# pragma optimize("",off)
-#endif
-
PdfOperator PdfParser::opTab[] = {
{"\"", 3, {tchkNum, tchkNum, tchkString},
&PdfParser::opMoveSetShowText},
@@ -249,10 +245,6 @@ PdfOperator PdfParser::opTab[] = {
&PdfParser::opCurveTo2}
};
-#ifdef WIN32 // this works around a bug in the VC7 compiler
-# pragma optimize("",on)
-#endif
-
#define numOps (sizeof(opTab) / sizeof(PdfOperator))
//------------------------------------------------------------------------
@@ -279,14 +271,14 @@ PdfParser::PdfParser(XRef *xrefA, Inkscape::Extension::Internal::SvgBuilder *bui
ignoreUndef = 0;
operatorHistory = NULL;
builder = builderA;
- builder->setDocumentSize(state->getPageWidth()*PX_PER_PT,
- state->getPageHeight()*PX_PER_PT);
+ builder->setDocumentSize(Inkscape::Util::Quantity::convert(state->getPageWidth(), "pt", "px"),
+ Inkscape::Util::Quantity::convert(state->getPageHeight(), "pt", "px"));
double *ctm = state->getCTM();
double scaledCTM[6];
for (int i = 0; i < 6; ++i) {
baseMatrix[i] = ctm[i];
- scaledCTM[i] = PX_PER_PT * ctm[i];
+ scaledCTM[i] = Inkscape::Util::Quantity::convert(ctm[i], "pt", "px");
}
saveState();
builder->setTransform((double*)&scaledCTM);
@@ -368,15 +360,23 @@ void PdfParser::parse(Object *obj, GBool topLevel) {
for (int i = 0; i < obj->arrayGetLength(); ++i) {
obj->arrayGet(i, &obj2);
if (!obj2.isStream()) {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errInternal, -1, "Weird page contents");
+#else
error(-1, const_cast<char*>("Weird page contents"));
+#endif
obj2.free();
return;
}
obj2.free();
}
} else if (!obj->isStream()) {
- error(-1, const_cast<char*>("Weird page contents"));
- return;
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errInternal, -1, "Weird page contents");
+#else
+ error(-1, const_cast<char*>("Weird page contents"));
+#endif
+ return;
}
parser = new Parser(xref, new Lexer(xref, obj), gFalse);
go(topLevel);
@@ -420,7 +420,11 @@ void PdfParser::go(GBool /*topLevel*/)
// too many arguments - something is wrong
} else {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "Too many args in content stream");
+#else
error(getPos(), const_cast<char*>("Too many args in content stream"));
+#endif
if (printCommands) {
printf("throwing away arg: ");
obj.print(stdout);
@@ -437,7 +441,11 @@ void PdfParser::go(GBool /*topLevel*/)
// args at end with no command
if (numArgs > 0) {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "Leftover args in content stream");
+#else
error(getPos(), const_cast<char*>("Leftover args in content stream"));
+#endif
if (printCommands) {
printf("%d leftovers:", numArgs);
for (int i = 0; i < numArgs; ++i) {
@@ -503,7 +511,11 @@ void PdfParser::execOp(Object *cmd, Object args[], int numArgs) {
name = cmd->getCmd();
if (!(op = findOp(name))) {
if (ignoreUndef == 0)
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "Unknown operator '{0:s}'", name);
+#else
error(getPos(), const_cast<char*>("Unknown operator '%s'"), name);
+#endif
return;
}
@@ -511,26 +523,42 @@ void PdfParser::execOp(Object *cmd, Object args[], int numArgs) {
argPtr = args;
if (op->numArgs >= 0) {
if (numArgs < op->numArgs) {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "Too few ({0:d}) args to '{1:d}' operator", numArgs, name);
+#else
error(getPos(), const_cast<char*>("Too few (%d) args to '%s' operator"), numArgs, name);
+#endif
return;
}
if (numArgs > op->numArgs) {
#if 0
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "Too many ({0:d}) args to '{1:s}' operator", numArgs, name);
+#else
error(getPos(), "Too many (%d) args to '%s' operator", numArgs, name);
#endif
+#endif
argPtr += numArgs - op->numArgs;
numArgs = op->numArgs;
}
} else {
if (numArgs > -op->numArgs) {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "Too many ({0:d}) args to '{1:s}' operator",
+#else
error(getPos(), const_cast<char*>("Too many (%d) args to '%s' operator"),
+#endif
numArgs, name);
return;
}
}
for (i = 0; i < numArgs; ++i) {
if (!checkArg(&argPtr[i], op->tchk[i])) {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "Arg #{0:d} to '{1:s}' operator is wrong type ({2:s})",
+#else
error(getPos(), const_cast<char*>("Arg #%d to '%s' operator is wrong type (%s)"),
+#endif
i, name, argPtr[i].getTypeName());
return;
}
@@ -543,14 +571,13 @@ void PdfParser::execOp(Object *cmd, Object args[], int numArgs) {
(this->*op->func)(argPtr, numArgs);
}
-PdfOperator *PdfParser::findOp(char *name) {
- int a, b, m, cmp;
-
- a = -1;
- b = numOps;
+PdfOperator* PdfParser::findOp(char *name) {
+ int a = -1;
+ int b = numOps;
+ int cmp = -1;
// invariant: opTab[a] < name < opTab[b]
while (b - a > 1) {
- m = (a + b) / 2;
+ const int m = (a + b) / 2;
cmp = strcmp(opTab[m].name, name);
if (cmp < 0)
a = m;
@@ -700,7 +727,11 @@ void PdfParser::opSetExtGState(Object args[], int /*numArgs*/)
return;
}
if (!obj1.isDict()) {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "ExtGState '{0:s}' is wrong type"), args[0].getName();
+#else
error(getPos(), const_cast<char*>("ExtGState '%s' is wrong type"), args[0].getName());
+#endif
obj1.free();
return;
}
@@ -716,7 +747,11 @@ void PdfParser::opSetExtGState(Object args[], int /*numArgs*/)
if (state->parseBlendMode(&obj2, &mode)) {
state->setBlendMode(mode);
} else {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "Invalid blend mode in ExtGState");
+#else
error(getPos(), const_cast<char*>("Invalid blend mode in ExtGState"));
+#endif
}
}
obj2.free();
@@ -778,7 +813,11 @@ void PdfParser::opSetExtGState(Object args[], int /*numArgs*/)
state->setTransfer(funcs);
}
} else if (!obj2.isNull()) {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "Invalid transfer function in ExtGState");
+#else
error(getPos(), const_cast<char*>("Invalid transfer function in ExtGState"));
+#endif
}
obj2.free();
@@ -798,8 +837,11 @@ void PdfParser::opSetExtGState(Object args[], int /*numArgs*/)
funcs[0] = Function::parse(&obj3);
if (funcs[0]->getInputSize() != 1 ||
funcs[0]->getOutputSize() != 1) {
- error(getPos(),
- const_cast<char*>("Invalid transfer function in soft mask in ExtGState"));
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "Invalid transfer function in soft mask in ExtGState");
+#else
+ error(getPos(), const_cast<char*>("Invalid transfer function in soft mask in ExtGState"));
+#endif
delete funcs[0];
funcs[0] = NULL;
}
@@ -824,7 +866,7 @@ void PdfParser::opSetExtGState(Object args[], int /*numArgs*/)
GBool isolated = gFalse;
GBool knockout = gFalse;
if (!obj4.dictLookup(const_cast<char*>("CS"), &obj5)->isNull()) {
-#ifdef POPPLER_NEW_COLOR_SPACE_API
+#if defined(POPPLER_NEW_COLOR_SPACE_API) || defined(POPPLER_NEW_ERRORAPI)
blendingColorSpace = GfxColorSpace::parse(&obj5, NULL);
#else
blendingColorSpace = GfxColorSpace::parse(&obj5);
@@ -855,15 +897,27 @@ void PdfParser::opSetExtGState(Object args[], int /*numArgs*/)
delete funcs[0];
}
} else {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "Invalid soft mask in ExtGState - missing group");
+#else
error(getPos(), const_cast<char*>("Invalid soft mask in ExtGState - missing group"));
+#endif
}
obj4.free();
} else {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "Invalid soft mask in ExtGState - missing group");
+#else
error(getPos(), const_cast<char*>("Invalid soft mask in ExtGState - missing group"));
+#endif
}
obj3.free();
} else if (!obj2.isNull()) {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "Invalid soft mask in ExtGState");
+#else
error(getPos(), const_cast<char*>("Invalid soft mask in ExtGState"));
+#endif
}
}
obj2.free();
@@ -891,7 +945,11 @@ void PdfParser::doSoftMask(Object *str, GBool alpha,
// check form type
dict->lookup(const_cast<char*>("FormType"), &obj1);
if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "Unknown form type");
+#else
error(getPos(), const_cast<char*>("Unknown form type"));
+#endif
}
obj1.free();
@@ -899,7 +957,11 @@ void PdfParser::doSoftMask(Object *str, GBool alpha,
dict->lookup(const_cast<char*>("BBox"), &obj1);
if (!obj1.isArray()) {
obj1.free();
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "Bad form bounding box");
+#else
error(getPos(), const_cast<char*>("Bad form bounding box"));
+#endif
return;
}
for (i = 0; i < 4; ++i) {
@@ -1038,7 +1100,7 @@ void PdfParser::opSetFillColorSpace(Object args[], int /*numArgs*/)
res->lookupColorSpace(args[0].getName(), &obj);
GfxColorSpace *colorSpace = 0;
-#ifdef POPPLER_NEW_COLOR_SPACE_API
+#if defined(POPPLER_NEW_COLOR_SPACE_API) || defined(POPPLER_NEW_ERRORAPI)
if (obj.isNull()) {
colorSpace = GfxColorSpace::parse(&args[0], NULL);
} else {
@@ -1059,7 +1121,11 @@ void PdfParser::opSetFillColorSpace(Object args[], int /*numArgs*/)
state->setFillColor(&color);
builder->updateStyle(state);
} else {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "Bad color space (fill)");
+#else
error(getPos(), const_cast<char*>("Bad color space (fill)"));
+#endif
}
}
@@ -1071,7 +1137,7 @@ void PdfParser::opSetStrokeColorSpace(Object args[], int /*numArgs*/)
state->setStrokePattern(NULL);
res->lookupColorSpace(args[0].getName(), &obj);
-#ifdef POPPLER_NEW_COLOR_SPACE_API
+#if defined(POPPLER_NEW_COLOR_SPACE_API) || defined(POPPLER_NEW_ERRORAPI)
if (obj.isNull()) {
colorSpace = GfxColorSpace::parse(&args[0], NULL);
} else {
@@ -1092,7 +1158,11 @@ void PdfParser::opSetStrokeColorSpace(Object args[], int /*numArgs*/)
state->setStrokeColor(&color);
builder->updateStyle(state);
} else {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "Bad color space (stroke)");
+#else
error(getPos(), const_cast<char*>("Bad color space (stroke)"));
+#endif
}
}
@@ -1101,7 +1171,11 @@ void PdfParser::opSetFillColor(Object args[], int numArgs) {
int i;
if (numArgs != state->getFillColorSpace()->getNComps()) {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "Incorrect number of arguments in 'sc' command");
+#else
error(getPos(), const_cast<char*>("Incorrect number of arguments in 'sc' command"));
+#endif
return;
}
state->setFillPattern(NULL);
@@ -1117,7 +1191,11 @@ void PdfParser::opSetStrokeColor(Object args[], int numArgs) {
int i;
if (numArgs != state->getStrokeColorSpace()->getNComps()) {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "Incorrect number of arguments in 'SC' command");
+#else
error(getPos(), const_cast<char*>("Incorrect number of arguments in 'SC' command"));
+#endif
return;
}
state->setStrokePattern(NULL);
@@ -1130,7 +1208,6 @@ void PdfParser::opSetStrokeColor(Object args[], int numArgs) {
void PdfParser::opSetFillColorN(Object args[], int numArgs) {
GfxColor color;
- GfxPattern *pattern;
int i;
if (state->getFillColorSpace()->getMode() == csPattern) {
@@ -1138,7 +1215,11 @@ void PdfParser::opSetFillColorN(Object args[], int numArgs) {
if (!((GfxPatternColorSpace *)state->getFillColorSpace())->getUnder() ||
numArgs - 1 != ((GfxPatternColorSpace *)state->getFillColorSpace())
->getUnder()->getNComps()) {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "Incorrect number of arguments in 'scn' command");
+#else
error(getPos(), const_cast<char*>("Incorrect number of arguments in 'scn' command"));
+#endif
return;
}
for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
@@ -1149,7 +1230,8 @@ void PdfParser::opSetFillColorN(Object args[], int numArgs) {
state->setFillColor(&color);
builder->updateStyle(state);
}
-#ifdef POPPLER_NEW_COLOR_SPACE_API
+ GfxPattern *pattern;
+#if defined(POPPLER_NEW_COLOR_SPACE_API) || defined(POPPLER_NEW_ERRORAPI)
if (args[numArgs-1].isName() &&
(pattern = res->lookupPattern(args[numArgs-1].getName(), NULL))) {
state->setFillPattern(pattern);
@@ -1165,7 +1247,11 @@ void PdfParser::opSetFillColorN(Object args[], int numArgs) {
} else {
if (numArgs != state->getFillColorSpace()->getNComps()) {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "Incorrect number of arguments in 'scn' command");
+#else
error(getPos(), const_cast<char*>("Incorrect number of arguments in 'scn' command"));
+#endif
return;
}
state->setFillPattern(NULL);
@@ -1181,7 +1267,6 @@ void PdfParser::opSetFillColorN(Object args[], int numArgs) {
void PdfParser::opSetStrokeColorN(Object args[], int numArgs) {
GfxColor color;
- GfxPattern *pattern;
int i;
if (state->getStrokeColorSpace()->getMode() == csPattern) {
@@ -1190,7 +1275,11 @@ void PdfParser::opSetStrokeColorN(Object args[], int numArgs) {
->getUnder() ||
numArgs - 1 != ((GfxPatternColorSpace *)state->getStrokeColorSpace())
->getUnder()->getNComps()) {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "Incorrect number of arguments in 'SCN' command");
+#else
error(getPos(), const_cast<char*>("Incorrect number of arguments in 'SCN' command"));
+#endif
return;
}
for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
@@ -1201,7 +1290,8 @@ void PdfParser::opSetStrokeColorN(Object args[], int numArgs) {
state->setStrokeColor(&color);
builder->updateStyle(state);
}
-#ifdef POPPLER_NEW_COLOR_SPACE_API
+ GfxPattern *pattern;
+#if defined(POPPLER_NEW_COLOR_SPACE_API) || defined(POPPLER_NEW_ERRORAPI)
if (args[numArgs-1].isName() &&
(pattern = res->lookupPattern(args[numArgs-1].getName(), NULL))) {
state->setStrokePattern(pattern);
@@ -1217,7 +1307,11 @@ void PdfParser::opSetStrokeColorN(Object args[], int numArgs) {
} else {
if (numArgs != state->getStrokeColorSpace()->getNComps()) {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "Incorrect number of arguments in 'SCN' command");
+#else
error(getPos(), const_cast<char*>("Incorrect number of arguments in 'SCN' command"));
+#endif
return;
}
state->setStrokePattern(NULL);
@@ -1245,7 +1339,11 @@ void PdfParser::opMoveTo(Object args[], int /*numArgs*/)
void PdfParser::opLineTo(Object args[], int /*numArgs*/)
{
if (!state->isCurPt()) {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "No current point in lineto");
+#else
error(getPos(), const_cast<char*>("No current point in lineto"));
+#endif
return;
}
state->lineTo(args[0].getNum(), args[1].getNum());
@@ -1255,7 +1353,11 @@ void PdfParser::opLineTo(Object args[], int /*numArgs*/)
void PdfParser::opCurveTo(Object args[], int /*numArgs*/)
{
if (!state->isCurPt()) {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "No current point in curveto");
+#else
error(getPos(), const_cast<char*>("No current point in curveto"));
+#endif
return;
}
double x1 = args[0].getNum();
@@ -1271,7 +1373,11 @@ void PdfParser::opCurveTo(Object args[], int /*numArgs*/)
void PdfParser::opCurveTo1(Object args[], int /*numArgs*/)
{
if (!state->isCurPt()) {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "No current point in curveto1");
+#else
error(getPos(), const_cast<char*>("No current point in curveto1"));
+#endif
return;
}
double x1 = state->getCurX();
@@ -1287,7 +1393,11 @@ void PdfParser::opCurveTo1(Object args[], int /*numArgs*/)
void PdfParser::opCurveTo2(Object args[], int /*numArgs*/)
{
if (!state->isCurPt()) {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "No current point in curveto2");
+#else
error(getPos(), const_cast<char*>("No current point in curveto2"));
+#endif
return;
}
double x1 = args[0].getNum();
@@ -1316,7 +1426,11 @@ void PdfParser::opRectangle(Object args[], int /*numArgs*/)
void PdfParser::opClosePath(Object /*args*/[], int /*numArgs*/)
{
if (!state->isCurPt()) {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "No current point in closepath");
+#else
error(getPos(), const_cast<char*>("No current point in closepath"));
+#endif
return;
}
state->closePath();
@@ -1482,7 +1596,11 @@ void PdfParser::doPatternFillFallback(GBool eoFill) {
doShadingPatternFillFallback(static_cast<GfxShadingPattern *>(pattern), gFalse, eoFill);
break;
default:
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errUnimplemented, getPos(), "Unimplemented pattern type (%d) in fill",
+#else
error(getPos(), const_cast<char*>("Unimplemented pattern type (%d) in fill"),
+#endif
pattern->getType());
break;
}
@@ -1501,7 +1619,11 @@ void PdfParser::doPatternStrokeFallback() {
doShadingPatternFillFallback(static_cast<GfxShadingPattern *>(pattern), gTrue, gFalse);
break;
default:
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errUnimplemented, getPos(), "Unimplemented pattern type ({0:d}) in stroke",
+#else
error(getPos(), const_cast<char*>("Unimplemented pattern type (%d) in stroke"),
+#endif
pattern->getType());
break;
}
@@ -1624,7 +1746,7 @@ void PdfParser::opShFill(Object args[], int /*numArgs*/)
double *matrix = NULL;
GBool savedState = gFalse;
-#ifdef POPPLER_NEW_COLOR_SPACE_API
+#if defined(POPPLER_NEW_COLOR_SPACE_API) || defined(POPPLER_NEW_ERRORAPI)
if (!(shading = res->lookupShading(args[0].getName(), NULL))) {
return;
}
@@ -2244,7 +2366,11 @@ void PdfParser::opTextNextLine(Object /*args*/[], int /*numArgs*/)
void PdfParser::opShowText(Object args[], int /*numArgs*/)
{
if (!state->getFont()) {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "No font in show");
+#else
error(getPos(), const_cast<char*>("No font in show"));
+#endif
return;
}
if (fontChanged) {
@@ -2261,7 +2387,11 @@ void PdfParser::opMoveShowText(Object args[], int /*numArgs*/)
double ty = 0;
if (!state->getFont()) {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "No font in move/show");
+#else
error(getPos(), const_cast<char*>("No font in move/show"));
+#endif
return;
}
if (fontChanged) {
@@ -2282,7 +2412,11 @@ void PdfParser::opMoveSetShowText(Object args[], int /*numArgs*/)
double ty = 0;
if (!state->getFont()) {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "No font in move/set/show");
+#else
error(getPos(), const_cast<char*>("No font in move/set/show"));
+#endif
return;
}
if (fontChanged) {
@@ -2306,7 +2440,11 @@ void PdfParser::opShowSpaceText(Object args[], int /*numArgs*/)
int wMode = 0;
if (!state->getFont()) {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "No font in show/space");
+#else
error(getPos(), const_cast<char*>("No font in show/space"));
+#endif
return;
}
if (fontChanged) {
@@ -2331,7 +2469,11 @@ void PdfParser::opShowSpaceText(Object args[], int /*numArgs*/)
} else if (obj.isString()) {
doShowText(obj.getString());
} else {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "Element of show/space array must be number or string");
+#else
error(getPos(), const_cast<char*>("Element of show/space array must be number or string"));
+#endif
}
obj.free();
}
@@ -2429,7 +2571,11 @@ void PdfParser::doShowText(GooString *s) {
if (charProc.isStream()) {
//parse(&charProc, gFalse); // TODO: parse into SVG font
} else {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "Missing or bad Type3 CharProc entry");
+#else
error(getPos(), const_cast<char*>("Missing or bad Type3 CharProc entry"));
+#endif
}
//out->endType3Char(state);
if (resDict) {
@@ -2506,7 +2652,11 @@ void PdfParser::opXObject(Object args[], int /*numArgs*/)
return;
}
if (!obj1.isStream()) {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "XObject '{0:s}' is wrong type", name);
+#else
error(getPos(), const_cast<char*>("XObject '%s' is wrong type"), name);
+#endif
obj1.free();
return;
}
@@ -2522,9 +2672,17 @@ void PdfParser::opXObject(Object args[], int /*numArgs*/)
/* out->psXObject(obj1.getStream(),
obj3.isStream() ? obj3.getStream() : (Stream *)NULL);*/
} else if (obj2.isName()) {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "Unknown XObject subtype '{0:s}'", obj2.getName());
+#else
error(getPos(), const_cast<char*>("Unknown XObject subtype '%s'"), obj2.getName());
+#endif
} else {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "XObject subtype is missing or wrong type");
+#else
error(getPos(), const_cast<char*>("XObject subtype is missing or wrong type"));
+#endif
}
obj2.free();
obj1.free();
@@ -2532,330 +2690,339 @@ void PdfParser::opXObject(Object args[], int /*numArgs*/)
void PdfParser::doImage(Object * /*ref*/, Stream *str, GBool inlineImg)
{
- Dict *dict, *maskDict;
- int width, height;
- int bits;
- StreamColorSpaceMode csMode;
- GBool mask;
- GBool invert;
- GfxColorSpace *colorSpace, *maskColorSpace;
- GfxImageColorMap *colorMap, *maskColorMap;
- Object maskObj, smaskObj;
- GBool haveColorKeyMask, haveExplicitMask, haveSoftMask;
- int maskColors[2*gfxColorMaxComps];
- int maskWidth, maskHeight;
- GBool maskInvert;
- Stream *maskStr;
- Object obj1, obj2;
- int i;
-
- // get info from the stream
- bits = 0;
- csMode = streamCSNone;
- str->getImageParams(&bits, &csMode);
-
- // get stream dict
- dict = str->getDict();
-
- // get size
- dict->lookup(const_cast<char*>("Width"), &obj1);
- if (obj1.isNull()) {
- obj1.free();
- dict->lookup(const_cast<char*>("W"), &obj1);
- }
- if (obj1.isInt())
- width = obj1.getInt();
- else if (obj1.isReal())
- width = (int)obj1.getReal();
- else
- goto err2;
- obj1.free();
- dict->lookup(const_cast<char*>("Height"), &obj1);
- if (obj1.isNull()) {
- obj1.free();
- dict->lookup(const_cast<char*>("H"), &obj1);
- }
- if (obj1.isInt())
- height = obj1.getInt();
- else if (obj1.isReal())
- height = (int)obj1.getReal();
- else
- goto err2;
- obj1.free();
-
- // image or mask?
- dict->lookup(const_cast<char*>("ImageMask"), &obj1);
- if (obj1.isNull()) {
- obj1.free();
- dict->lookup(const_cast<char*>("IM"), &obj1);
- }
- mask = gFalse;
- if (obj1.isBool())
- mask = obj1.getBool();
- else if (!obj1.isNull())
- goto err2;
- obj1.free();
-
- // bit depth
- if (bits == 0) {
- dict->lookup(const_cast<char*>("BitsPerComponent"), &obj1);
+ Dict *dict;
+ int width, height;
+ int bits;
+ StreamColorSpaceMode csMode;
+ GBool mask;
+ GBool invert;
+ Object maskObj, smaskObj;
+ GBool haveColorKeyMask, haveExplicitMask, haveSoftMask;
+ GBool maskInvert;
+ Object obj1, obj2;
+
+ // get info from the stream
+ bits = 0;
+ csMode = streamCSNone;
+ str->getImageParams(&bits, &csMode);
+
+ // get stream dict
+ dict = str->getDict();
+
+ // get size
+ dict->lookup(const_cast<char*>("Width"), &obj1);
if (obj1.isNull()) {
- obj1.free();
- dict->lookup(const_cast<char*>("BPC"), &obj1);
+ obj1.free();
+ dict->lookup(const_cast<char*>("W"), &obj1);
}
- if (obj1.isInt()) {
- bits = obj1.getInt();
- } else if (mask) {
- bits = 1;
- } else {
- goto err2;
+ if (obj1.isInt()){
+ width = obj1.getInt();
}
- obj1.free();
- }
-
- // display a mask
- if (mask) {
-
- // check for inverted mask
- if (bits != 1)
- goto err1;
- invert = gFalse;
- dict->lookup(const_cast<char*>("Decode"), &obj1);
- if (obj1.isNull()) {
- obj1.free();
- dict->lookup(const_cast<char*>("D"), &obj1);
+ else if (obj1.isReal()) {
+ width = (int)obj1.getReal();
}
- if (obj1.isArray()) {
- obj1.arrayGet(0, &obj2);
- if (obj2.isInt() && obj2.getInt() == 1)
- invert = gTrue;
- obj2.free();
- } else if (!obj1.isNull()) {
- goto err2;
+ else {
+ goto err2;
}
obj1.free();
-
- // draw it
- builder->addImageMask(state, str, width, height, invert);
-
- } else {
-
- // get color space and color map
- dict->lookup(const_cast<char*>("ColorSpace"), &obj1);
+ dict->lookup(const_cast<char*>("Height"), &obj1);
if (obj1.isNull()) {
- obj1.free();
- dict->lookup(const_cast<char*>("CS"), &obj1);
+ obj1.free();
+ dict->lookup(const_cast<char*>("H"), &obj1);
}
- if (obj1.isName()) {
- res->lookupColorSpace(obj1.getName(), &obj2);
- if (!obj2.isNull()) {
- obj1.free();
- obj1 = obj2;
- } else {
- obj2.free();
- }
+ if (obj1.isInt()) {
+ height = obj1.getInt();
}
- if (!obj1.isNull()) {
-#ifdef POPPLER_NEW_COLOR_SPACE_API
- colorSpace = GfxColorSpace::parse(&obj1, NULL);
-#else
- colorSpace = GfxColorSpace::parse(&obj1);
-#endif
- } else if (csMode == streamCSDeviceGray) {
- colorSpace = new GfxDeviceGrayColorSpace();
- } else if (csMode == streamCSDeviceRGB) {
- colorSpace = new GfxDeviceRGBColorSpace();
- } else if (csMode == streamCSDeviceCMYK) {
- colorSpace = new GfxDeviceCMYKColorSpace();
- } else {
- colorSpace = NULL;
+ else if (obj1.isReal()){
+ height = static_cast<int>(obj1.getReal());
}
- obj1.free();
- if (!colorSpace) {
- goto err1;
+ else {
+ goto err2;
}
- dict->lookup(const_cast<char*>("Decode"), &obj1);
+ obj1.free();
+
+ // image or mask?
+ dict->lookup(const_cast<char*>("ImageMask"), &obj1);
if (obj1.isNull()) {
- obj1.free();
- dict->lookup(const_cast<char*>("D"), &obj1);
+ obj1.free();
+ dict->lookup(const_cast<char*>("IM"), &obj1);
+ }
+ mask = gFalse;
+ if (obj1.isBool()) {
+ mask = obj1.getBool();
+ }
+ else if (!obj1.isNull()) {
+ goto err2;
}
- colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
obj1.free();
- if (!colorMap->isOk()) {
- delete colorMap;
- goto err1;
- }
-
- // get the mask
- haveColorKeyMask = haveExplicitMask = haveSoftMask = gFalse;
- maskStr = NULL; // make gcc happy
- maskWidth = maskHeight = 0; // make gcc happy
- maskInvert = gFalse; // make gcc happy
- maskColorMap = NULL; // make gcc happy
- dict->lookup(const_cast<char*>("Mask"), &maskObj);
- dict->lookup(const_cast<char*>("SMask"), &smaskObj);
- if (smaskObj.isStream()) {
- // soft mask
- if (inlineImg) {
- goto err1;
- }
- maskStr = smaskObj.getStream();
- maskDict = smaskObj.streamGetDict();
- maskDict->lookup(const_cast<char*>("Width"), &obj1);
+
+ // bit depth
+ if (bits == 0) {
+ dict->lookup(const_cast<char*>("BitsPerComponent"), &obj1);
if (obj1.isNull()) {
- obj1.free();
- maskDict->lookup(const_cast<char*>("W"), &obj1);
+ obj1.free();
+ dict->lookup(const_cast<char*>("BPC"), &obj1);
}
- if (!obj1.isInt()) {
- goto err2;
+ if (obj1.isInt()) {
+ bits = obj1.getInt();
+ } else if (mask) {
+ bits = 1;
+ } else {
+ goto err2;
}
- maskWidth = obj1.getInt();
obj1.free();
- maskDict->lookup(const_cast<char*>("Height"), &obj1);
- if (obj1.isNull()) {
- obj1.free();
- maskDict->lookup(const_cast<char*>("H"), &obj1);
- }
- if (!obj1.isInt()) {
- goto err2;
+ }
+
+ // display a mask
+ if (mask) {
+ // check for inverted mask
+ if (bits != 1) {
+ goto err1;
}
- maskHeight = obj1.getInt();
- obj1.free();
- maskDict->lookup(const_cast<char*>("BitsPerComponent"), &obj1);
+ invert = gFalse;
+ dict->lookup(const_cast<char*>("Decode"), &obj1);
if (obj1.isNull()) {
- obj1.free();
- maskDict->lookup(const_cast<char*>("BPC"), &obj1);
+ obj1.free();
+ dict->lookup(const_cast<char*>("D"), &obj1);
}
- if (!obj1.isInt()) {
- goto err2;
+ if (obj1.isArray()) {
+ obj1.arrayGet(0, &obj2);
+ if (obj2.isInt() && obj2.getInt() == 1) {
+ invert = gTrue;
+ }
+ obj2.free();
+ } else if (!obj1.isNull()) {
+ goto err2;
}
- int maskBits = obj1.getInt();
obj1.free();
- maskDict->lookup(const_cast<char*>("ColorSpace"), &obj1);
+
+ // draw it
+ builder->addImageMask(state, str, width, height, invert);
+
+ } else {
+ // get color space and color map
+ GfxColorSpace *colorSpace;
+ dict->lookup(const_cast<char*>("ColorSpace"), &obj1);
if (obj1.isNull()) {
- obj1.free();
- maskDict->lookup(const_cast<char*>("CS"), &obj1);
+ obj1.free();
+ dict->lookup(const_cast<char*>("CS"), &obj1);
}
if (obj1.isName()) {
- res->lookupColorSpace(obj1.getName(), &obj2);
- if (!obj2.isNull()) {
+ res->lookupColorSpace(obj1.getName(), &obj2);
+ if (!obj2.isNull()) {
obj1.free();
obj1 = obj2;
- } else {
+ } else {
obj2.free();
- }
+ }
}
-#ifdef POPPLER_NEW_COLOR_SPACE_API
- maskColorSpace = GfxColorSpace::parse(&obj1, NULL);
+ if (!obj1.isNull()) {
+#if defined(POPPLER_NEW_COLOR_SPACE_API) || defined(POPPLER_NEW_ERRORAPI)
+ colorSpace = GfxColorSpace::parse(&obj1, NULL);
#else
- maskColorSpace = GfxColorSpace::parse(&obj1);
+ colorSpace = GfxColorSpace::parse(&obj1);
#endif
+ } else if (csMode == streamCSDeviceGray) {
+ colorSpace = new GfxDeviceGrayColorSpace();
+ } else if (csMode == streamCSDeviceRGB) {
+ colorSpace = new GfxDeviceRGBColorSpace();
+ } else if (csMode == streamCSDeviceCMYK) {
+ colorSpace = new GfxDeviceCMYKColorSpace();
+ } else {
+ colorSpace = NULL;
+ }
obj1.free();
- if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) {
+ if (!colorSpace) {
goto err1;
}
- maskDict->lookup(const_cast<char*>("Decode"), &obj1);
+ dict->lookup(const_cast<char*>("Decode"), &obj1);
if (obj1.isNull()) {
- obj1.free();
- maskDict->lookup(const_cast<char*>("D"), &obj1);
+ obj1.free();
+ dict->lookup(const_cast<char*>("D"), &obj1);
}
- maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace);
+ GfxImageColorMap *colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
obj1.free();
- if (!maskColorMap->isOk()) {
- delete maskColorMap;
+ if (!colorMap->isOk()) {
+ delete colorMap;
goto err1;
}
- //~ handle the Matte entry
- haveSoftMask = gTrue;
- } else if (maskObj.isArray()) {
- // color key mask
- for (i = 0;
- i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps;
- ++i) {
- maskObj.arrayGet(i, &obj1);
- maskColors[i] = obj1.getInt();
- obj1.free();
- }
- haveColorKeyMask = gTrue;
- } else if (maskObj.isStream()) {
- // explicit mask
- if (inlineImg) {
- goto err1;
- }
- maskStr = maskObj.getStream();
- maskDict = maskObj.streamGetDict();
- maskDict->lookup(const_cast<char*>("Width"), &obj1);
- if (obj1.isNull()) {
- obj1.free();
- maskDict->lookup(const_cast<char*>("W"), &obj1);
- }
- if (!obj1.isInt()) {
- goto err2;
- }
- maskWidth = obj1.getInt();
- obj1.free();
- maskDict->lookup(const_cast<char*>("Height"), &obj1);
- if (obj1.isNull()) {
- obj1.free();
- maskDict->lookup(const_cast<char*>("H"), &obj1);
- }
- if (!obj1.isInt()) {
- goto err2;
- }
- maskHeight = obj1.getInt();
- obj1.free();
- maskDict->lookup(const_cast<char*>("ImageMask"), &obj1);
- if (obj1.isNull()) {
- obj1.free();
- maskDict->lookup(const_cast<char*>("IM"), &obj1);
- }
- if (!obj1.isBool() || !obj1.getBool()) {
- goto err2;
- }
- obj1.free();
- maskInvert = gFalse;
- maskDict->lookup(const_cast<char*>("Decode"), &obj1);
- if (obj1.isNull()) {
- obj1.free();
- maskDict->lookup(const_cast<char*>("D"), &obj1);
- }
- if (obj1.isArray()) {
- obj1.arrayGet(0, &obj2);
- if (obj2.isInt() && obj2.getInt() == 1) {
- maskInvert = gTrue;
- }
- obj2.free();
- } else if (!obj1.isNull()) {
- goto err2;
- }
- obj1.free();
- haveExplicitMask = gTrue;
- }
-
- // draw it
- if (haveSoftMask) {
- builder->addSoftMaskedImage(state, str, width, height, colorMap,
+
+ // get the mask
+ int maskColors[2*gfxColorMaxComps];
+ haveColorKeyMask = haveExplicitMask = haveSoftMask = gFalse;
+ Stream *maskStr = NULL;
+ int maskWidth = 0;
+ int maskHeight = 0;
+ maskInvert = gFalse;
+ GfxImageColorMap *maskColorMap = NULL;
+ dict->lookup(const_cast<char*>("Mask"), &maskObj);
+ dict->lookup(const_cast<char*>("SMask"), &smaskObj);
+ Dict* maskDict;
+ if (smaskObj.isStream()) {
+ // soft mask
+ if (inlineImg) {
+ goto err1;
+ }
+ maskStr = smaskObj.getStream();
+ maskDict = smaskObj.streamGetDict();
+ maskDict->lookup(const_cast<char*>("Width"), &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ maskDict->lookup(const_cast<char*>("W"), &obj1);
+ }
+ if (!obj1.isInt()) {
+ goto err2;
+ }
+ maskWidth = obj1.getInt();
+ obj1.free();
+ maskDict->lookup(const_cast<char*>("Height"), &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ maskDict->lookup(const_cast<char*>("H"), &obj1);
+ }
+ if (!obj1.isInt()) {
+ goto err2;
+ }
+ maskHeight = obj1.getInt();
+ obj1.free();
+ maskDict->lookup(const_cast<char*>("BitsPerComponent"), &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ maskDict->lookup(const_cast<char*>("BPC"), &obj1);
+ }
+ if (!obj1.isInt()) {
+ goto err2;
+ }
+ int maskBits = obj1.getInt();
+ obj1.free();
+ maskDict->lookup(const_cast<char*>("ColorSpace"), &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ maskDict->lookup(const_cast<char*>("CS"), &obj1);
+ }
+ if (obj1.isName()) {
+ res->lookupColorSpace(obj1.getName(), &obj2);
+ if (!obj2.isNull()) {
+ obj1.free();
+ obj1 = obj2;
+ } else {
+ obj2.free();
+ }
+ }
+#if defined(POPPLER_NEW_COLOR_SPACE_API) || defined(POPPLER_NEW_ERRORAPI)
+ GfxColorSpace *maskColorSpace = GfxColorSpace::parse(&obj1, NULL);
+#else
+ GfxColorSpace *maskColorSpace = GfxColorSpace::parse(&obj1);
+#endif
+ obj1.free();
+ if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) {
+ goto err1;
+ }
+ maskDict->lookup(const_cast<char*>("Decode"), &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ maskDict->lookup(const_cast<char*>("D"), &obj1);
+ }
+ maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace);
+ obj1.free();
+ if (!maskColorMap->isOk()) {
+ delete maskColorMap;
+ goto err1;
+ }
+ //~ handle the Matte entry
+ haveSoftMask = gTrue;
+ } else if (maskObj.isArray()) {
+ // color key mask
+ int i;
+ for (i = 0; i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps; ++i) {
+ maskObj.arrayGet(i, &obj1);
+ maskColors[i] = obj1.getInt();
+ obj1.free();
+ }
+ haveColorKeyMask = gTrue;
+ } else if (maskObj.isStream()) {
+ // explicit mask
+ if (inlineImg) {
+ goto err1;
+ }
+ maskStr = maskObj.getStream();
+ maskDict = maskObj.streamGetDict();
+ maskDict->lookup(const_cast<char*>("Width"), &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ maskDict->lookup(const_cast<char*>("W"), &obj1);
+ }
+ if (!obj1.isInt()) {
+ goto err2;
+ }
+ maskWidth = obj1.getInt();
+ obj1.free();
+ maskDict->lookup(const_cast<char*>("Height"), &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ maskDict->lookup(const_cast<char*>("H"), &obj1);
+ }
+ if (!obj1.isInt()) {
+ goto err2;
+ }
+ maskHeight = obj1.getInt();
+ obj1.free();
+ maskDict->lookup(const_cast<char*>("ImageMask"), &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ maskDict->lookup(const_cast<char*>("IM"), &obj1);
+ }
+ if (!obj1.isBool() || !obj1.getBool()) {
+ goto err2;
+ }
+ obj1.free();
+ maskInvert = gFalse;
+ maskDict->lookup(const_cast<char*>("Decode"), &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ maskDict->lookup(const_cast<char*>("D"), &obj1);
+ }
+ if (obj1.isArray()) {
+ obj1.arrayGet(0, &obj2);
+ if (obj2.isInt() && obj2.getInt() == 1) {
+ maskInvert = gTrue;
+ }
+ obj2.free();
+ } else if (!obj1.isNull()) {
+ goto err2;
+ }
+ obj1.free();
+ haveExplicitMask = gTrue;
+ }
+
+ // draw it
+ if (haveSoftMask) {
+ builder->addSoftMaskedImage(state, str, width, height, colorMap,
maskStr, maskWidth, maskHeight, maskColorMap);
- delete maskColorMap;
- } else if (haveExplicitMask) {
- builder->addMaskedImage(state, str, width, height, colorMap,
+ delete maskColorMap;
+ } else if (haveExplicitMask) {
+ builder->addMaskedImage(state, str, width, height, colorMap,
maskStr, maskWidth, maskHeight, maskInvert);
- } else {
- builder->addImage(state, str, width, height, colorMap,
- haveColorKeyMask ? maskColors : (int *)NULL);
+ } else {
+ builder->addImage(state, str, width, height, colorMap,
+ haveColorKeyMask ? maskColors : static_cast<int *>(NULL));
+ }
+ delete colorMap;
+
+ maskObj.free();
+ smaskObj.free();
}
- delete colorMap;
-
- maskObj.free();
- smaskObj.free();
- }
- return;
+ return;
err2:
- obj1.free();
+ obj1.free();
err1:
- error(getPos(), const_cast<char*>("Bad image parameters"));
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "Bad image parameters");
+#else
+ error(getPos(), const_cast<char*>("Bad image parameters"));
+#endif
}
void PdfParser::doForm(Object *str) {
@@ -2880,7 +3047,11 @@ void PdfParser::doForm(Object *str) {
// check form type
dict->lookup(const_cast<char*>("FormType"), &obj1);
if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "Unknown form type");
+#else
error(getPos(), const_cast<char*>("Unknown form type"));
+#endif
}
obj1.free();
@@ -2888,7 +3059,11 @@ void PdfParser::doForm(Object *str) {
dict->lookup(const_cast<char*>("BBox"), &bboxObj);
if (!bboxObj.isArray()) {
bboxObj.free();
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "Bad form bounding box");
+#else
error(getPos(), const_cast<char*>("Bad form bounding box"));
+#endif
return;
}
for (i = 0; i < 4; ++i) {
@@ -2924,7 +3099,7 @@ void PdfParser::doForm(Object *str) {
if (obj1.dictLookup(const_cast<char*>("S"), &obj2)->isName(const_cast<char*>("Transparency"))) {
transpGroup = gTrue;
if (!obj1.dictLookup(const_cast<char*>("CS"), &obj3)->isNull()) {
-#ifdef POPPLER_NEW_COLOR_SPACE_API
+#if defined(POPPLER_NEW_COLOR_SPACE_API) || defined(POPPLER_NEW_ERRORAPI)
blendingColorSpace = GfxColorSpace::parse(&obj3, NULL);
#else
blendingColorSpace = GfxColorSpace::parse(&obj3);
@@ -3085,7 +3260,11 @@ Stream *PdfParser::buildImageStream() {
parser->getObj(&obj);
while (!obj.isCmd(const_cast<char*>("ID")) && !obj.isEOF()) {
if (!obj.isName()) {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "Inline image dictionary key must be a name object");
+#else
error(getPos(), const_cast<char*>("Inline image dictionary key must be a name object"));
+#endif
obj.free();
} else {
key = copyString(obj.getName());
@@ -3100,7 +3279,11 @@ Stream *PdfParser::buildImageStream() {
parser->getObj(&obj);
}
if (obj.isEOF()) {
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errSyntaxError, getPos(), "End of file in inline image");
+#else
error(getPos(), const_cast<char*>("End of file in inline image"));
+#endif
obj.free();
dict.free();
return NULL;
@@ -3116,12 +3299,20 @@ Stream *PdfParser::buildImageStream() {
void PdfParser::opImageData(Object /*args*/[], int /*numArgs*/)
{
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errInternal, getPos(), "Internal: got 'ID' operator");
+#else
error(getPos(), const_cast<char*>("Internal: got 'ID' operator"));
+#endif
}
void PdfParser::opEndImage(Object /*args*/[], int /*numArgs*/)
{
+#ifdef POPPLER_NEW_ERRORAPI
+ error(errInternal, getPos(), "Internal: got 'EI' operator");
+#else
error(getPos(), const_cast<char*>("Internal: got 'EI' operator"));
+#endif
}
//------------------------------------------------------------------------
@@ -3198,9 +3389,22 @@ void PdfParser::opMarkPoint(Object args[], int numArgs) {
//------------------------------------------------------------------------
void PdfParser::saveState() {
+ bool is_radial = false;
+
+ GfxPattern *pattern = state->getFillPattern();
+ if (pattern != NULL)
+ if (pattern->getType() == 2 ) {
+ GfxShadingPattern *shading_pattern = static_cast<GfxShadingPattern *>(pattern);
+ GfxShading *shading = shading_pattern->getShading();
+ if (shading->getType() == 3)
+ is_radial = true;
+ }
+
builder->saveState();
-// state = state->save(); // nasty hack to prevent GfxRadialShading from getting corrupted during copy operation
- state->save(); // see LP Bug 919176 comment 8
+ if (is_radial)
+ state->save(); // nasty hack to prevent GfxRadialShading from getting corrupted during copy operation
+ else
+ state = state->save(); // see LP Bug 919176 comment 8
clipHistory = clipHistory->save();
}
diff --git a/src/extension/internal/pdfinput/svg-builder.cpp b/src/extension/internal/pdfinput/svg-builder.cpp
index 2f36e19a4..71e6dc6ae 100644
--- a/src/extension/internal/pdfinput/svg-builder.cpp
+++ b/src/extension/internal/pdfinput/svg-builder.cpp
@@ -33,7 +33,7 @@
#include "svg/css-ostringstream.h"
#include "svg/svg-color.h"
#include "color.h"
-#include "unit-constants.h"
+#include "util/units.h"
#include "io/stringstream.h"
#include "io/base64stream.h"
#include "display/nr-filter-utils.h"
@@ -56,15 +56,12 @@ namespace Internal {
#define TRACE(_args) IFTRACE(g_print _args)
-static double ttm[6] = {1, 0, 0, 1, 0, 0}; // temporary transform matrix
-static bool ttm_is_set = false; // flag to forbid setting ttm
-
/**
* \struct SvgTransparencyGroup
* \brief Holds information about a PDF transparency group
*/
struct SvgTransparencyGroup {
- double bbox[6];
+ double bbox[6]; // TODO should this be 4?
Inkscape::XML::Node *container;
bool isolated;
@@ -94,6 +91,9 @@ SvgBuilder::SvgBuilder(SPDocument *document, gchar *docname, XRef *xref)
_preferences = _xml_doc->createElement("svgbuilder:prefs");
_preferences->setAttribute("embedImages", "1");
_preferences->setAttribute("localFonts", "1");
+
+ _ttm[0] = 1; _ttm[1] = 0; _ttm[2] = 0; _ttm[3] = 1; _ttm[4] = 0; _ttm[5] = 0;
+ _ttm_is_set = false;
}
SvgBuilder::SvgBuilder(SvgBuilder *parent, Inkscape::XML::Node *root) {
@@ -216,9 +216,9 @@ Inkscape::XML::Node *SvgBuilder::pushGroup() {
}
}
if (_container->parent()->attribute("inkscape:groupmode") != NULL) {
- ttm[0] = ttm[3] = 1.0; // clear ttm if parent is a layer
- ttm[1] = ttm[2] = ttm[4] = ttm[5] = 0.0;
- ttm_is_set = false;
+ _ttm[0] = _ttm[3] = 1.0; // clear ttm if parent is a layer
+ _ttm[1] = _ttm[2] = _ttm[4] = _ttm[5] = 0.0;
+ _ttm_is_set = false;
}
return _container;
}
@@ -266,14 +266,12 @@ static void svgSetTransform(Inkscape::XML::Node *node, double c0, double c1,
* \brief Generates a SVG path string from poppler's data structure
*/
static gchar *svgInterpretPath(GfxPath *path) {
- GfxSubpath *subpath;
Inkscape::SVG::PathString pathString;
- int i, j;
- for ( i = 0 ; i < path->getNumSubpaths() ; ++i ) {
- subpath = path->getSubpath(i);
+ for (int i = 0 ; i < path->getNumSubpaths() ; ++i ) {
+ GfxSubpath *subpath = path->getSubpath(i);
if (subpath->getNumPoints() > 0) {
pathString.moveTo(subpath->getX(0), subpath->getY(0));
- j = 1;
+ int j = 1;
while (j < subpath->getNumPoints()) {
if (subpath->getCurve(j)) {
pathString.curveTo(subpath->getX(j), subpath->getY(j),
@@ -300,14 +298,6 @@ static gchar *svgInterpretPath(GfxPath *path) {
* Uses the given SPCSSAttr for storing the style properties
*/
void SvgBuilder::_setStrokeStyle(SPCSSAttr *css, GfxState *state) {
-
- // Check line width
- if ( state->getLineWidth() <= 0.0 ) {
- // Ignore stroke
- sp_repr_css_set_property(css, "stroke", "none");
- return;
- }
-
// Stroke color/pattern
if ( state->getStrokeColorSpace()->getMode() == csPattern ) {
gchar *urltext = _createPattern(state->getStrokePattern(), state, true);
@@ -328,7 +318,14 @@ void SvgBuilder::_setStrokeStyle(SPCSSAttr *css, GfxState *state) {
// Line width
Inkscape::CSSOStringStream os_width;
- os_width << state->getLineWidth();
+ double lw = state->getLineWidth();
+ if (lw > 0.0) {
+ os_width << lw;
+ } else {
+ // emit a stroke which is 1px in toplevel user units
+ double pxw = Inkscape::Util::Quantity::convert(1.0, "pt", "px");
+ os_width << 1.0 / state->transformWidth(pxw);
+ }
sp_repr_css_set_property(css, "stroke-width", os_width.str().c_str());
// Line cap
@@ -572,14 +569,14 @@ bool SvgBuilder::getTransform(double *transform) {
void SvgBuilder::setTransform(double c0, double c1, double c2, double c3,
double c4, double c5) {
// do not remember the group which is a layer
- if ((_container->attribute("inkscape:groupmode") == NULL) && !ttm_is_set) {
- ttm[0] = c0;
- ttm[1] = c1;
- ttm[2] = c2;
- ttm[3] = c3;
- ttm[4] = c4;
- ttm[5] = c5;
- ttm_is_set = true;
+ if ((_container->attribute("inkscape:groupmode") == NULL) && !_ttm_is_set) {
+ _ttm[0] = c0;
+ _ttm[1] = c1;
+ _ttm[2] = c2;
+ _ttm[3] = c3;
+ _ttm[4] = c4;
+ _ttm[5] = c5;
+ _ttm_is_set = true;
}
// Avoid transforming a group with an already set clip-path
@@ -629,21 +626,21 @@ gchar *SvgBuilder::_createPattern(GfxPattern *pattern, GfxState *state, bool is_
if ( pattern->getType() == 2 ) { // Shading pattern
GfxShadingPattern *shading_pattern = static_cast<GfxShadingPattern *>(pattern);
double *ptm;
- double ittm[6]; // invert ttm
double m[6] = {1, 0, 0, 1, 0, 0};
double det;
// construct a (pattern space) -> (current space) transform matrix
ptm = shading_pattern->getMatrix();
- det = ttm[0] * ttm[3] - ttm[1] * ttm[2];
+ det = _ttm[0] * _ttm[3] - _ttm[1] * _ttm[2];
if (det) {
- ittm[0] = ttm[3] / det;
- ittm[1] = -ttm[1] / det;
- ittm[2] = -ttm[2] / det;
- ittm[3] = ttm[0] / det;
- ittm[4] = (ttm[2] * ttm[5] - ttm[3] * ttm[4]) / det;
- ittm[5] = (ttm[1] * ttm[4] - ttm[0] * ttm[5]) / det;
+ double ittm[6]; // invert ttm
+ ittm[0] = _ttm[3] / det;
+ ittm[1] = -_ttm[1] / det;
+ ittm[2] = -_ttm[2] / det;
+ ittm[3] = _ttm[0] / det;
+ ittm[4] = (_ttm[2] * _ttm[5] - _ttm[3] * _ttm[4]) / det;
+ ittm[5] = (_ttm[1] * _ttm[4] - _ttm[0] * _ttm[5]) / det;
m[0] = ptm[0] * ittm[0] + ptm[1] * ittm[2];
m[1] = ptm[0] * ittm[1] + ptm[1] * ittm[3];
m[2] = ptm[2] * ittm[0] + ptm[3] * ittm[2];
@@ -676,7 +673,25 @@ gchar *SvgBuilder::_createTilingPattern(GfxTilingPattern *tiling_pattern,
Inkscape::XML::Node *pattern_node = _xml_doc->createElement("svg:pattern");
// Set pattern transform matrix
double *p2u = tiling_pattern->getMatrix();
- Geom::Affine pat_matrix(p2u[0], p2u[1], p2u[2], p2u[3], p2u[4], p2u[5]);
+ double m[6] = {1, 0, 0, 1, 0, 0};
+ double det;
+ det = _ttm[0] * _ttm[3] - _ttm[1] * _ttm[2]; // see LP Bug 1168908
+ if (det) {
+ double ittm[6]; // invert ttm
+ ittm[0] = _ttm[3] / det;
+ ittm[1] = -_ttm[1] / det;
+ ittm[2] = -_ttm[2] / det;
+ ittm[3] = _ttm[0] / det;
+ ittm[4] = (_ttm[2] * _ttm[5] - _ttm[3] * _ttm[4]) / det;
+ ittm[5] = (_ttm[1] * _ttm[4] - _ttm[0] * _ttm[5]) / det;
+ m[0] = p2u[0] * ittm[0] + p2u[1] * ittm[2];
+ m[1] = p2u[0] * ittm[1] + p2u[1] * ittm[3];
+ m[2] = p2u[2] * ittm[0] + p2u[3] * ittm[2];
+ m[3] = p2u[2] * ittm[1] + p2u[3] * ittm[3];
+ m[4] = p2u[4] * ittm[0] + p2u[5] * ittm[2] + ittm[4];
+ m[5] = p2u[4] * ittm[1] + p2u[5] * ittm[3] + ittm[5];
+ }
+ Geom::Affine pat_matrix(m[0], m[1], m[2], m[3], m[4], m[5]);
gchar *transform_text = sp_svg_transform_write(pat_matrix);
pattern_node->setAttribute("patternTransform", transform_text);
g_free(transform_text);
@@ -777,7 +792,7 @@ gchar *SvgBuilder::_createGradient(GfxShading *shading, double *matrix, bool for
Geom::Affine pat_matrix(matrix[0], matrix[1], matrix[2], matrix[3],
matrix[4], matrix[5]);
if ( !for_shading && _is_top_level ) {
- Geom::Affine flip(1.0, 0.0, 0.0, -1.0, 0.0, _height * PT_PER_PX);
+ Geom::Affine flip(1.0, 0.0, 0.0, -1.0, 0.0, Inkscape::Util::Quantity::convert(_height, "px", "pt"));
pat_matrix *= flip;
}
gchar *transform_text = sp_svg_transform_write(pat_matrix);
@@ -1004,9 +1019,7 @@ void SvgBuilder::updateFont(GfxState *state) {
_font_style = sp_repr_css_attr_new();
GfxFont *font = state->getFont();
// Store original name
- if (font->getOrigName()) {
- _font_specification = font->getOrigName()->getCString();
- } else if (font->getName()) {
+ if (font->getName()) {
_font_specification = font->getName()->getCString();
} else {
_font_specification = (char*) "Arial";
@@ -1210,7 +1223,7 @@ void SvgBuilder::updateTextMatrix(GfxState *state) {
*/
void SvgBuilder::_flushText() {
// Ignore empty strings
- if ( _glyphs.size() < 1 ) {
+ if ( _glyphs.empty()) {
_glyphs.clear();
return;
}
@@ -1302,7 +1315,8 @@ void SvgBuilder::_flushText() {
sp_repr_css_set_property(glyph.style, "-inkscape-font-specification", properFontSpec.c_str());
// Set style and unref SPCSSAttr if it won't be needed anymore
- sp_repr_css_change(tspan_node, glyph.style, "style");
+ // assume all <tspan> nodes in a <text> node share the same style
+ sp_repr_css_change(text_node, glyph.style, "style");
if ( glyph.style_changed && i != _glyphs.begin() ) { // Free previous style
sp_repr_css_attr_unref((*prev_iterator).style);
}
@@ -1642,6 +1656,10 @@ Inkscape::XML::Node *SvgBuilder::_createImage(Stream *str, int width, int height
Inkscape::XML::Node *image_node = _xml_doc->createElement("svg:image");
sp_repr_set_svg_double(image_node, "width", 1);
sp_repr_set_svg_double(image_node, "height", 1);
+
+ // PS/PDF images are placed via a transformation matrix, no preserveAspectRatio used
+ image_node->setAttribute("preserveAspectRatio", "none");
+
// Set transformation
svgSetTransform(image_node, 1.0, 0.0, 0.0, -1.0, 0.0, 1.0);
@@ -1818,7 +1836,9 @@ void SvgBuilder::pushTransparencyGroup(GfxState * /*state*/, double *bbox,
// Setup new transparency group
SvgTransparencyGroup *transpGroup = new SvgTransparencyGroup;
- memcpy(&transpGroup->bbox, bbox, sizeof(bbox));
+ for (size_t i = 0; i < 4; i++) {
+ transpGroup->bbox[i] = bbox[i];
+ }
transpGroup->isolated = isolated;
transpGroup->knockout = knockout;
transpGroup->for_softmask = for_softmask;
diff --git a/src/extension/internal/pdfinput/svg-builder.h b/src/extension/internal/pdfinput/svg-builder.h
index a550565d6..f1ce02cf0 100644
--- a/src/extension/internal/pdfinput/svg-builder.h
+++ b/src/extension/internal/pdfinput/svg-builder.h
@@ -19,7 +19,7 @@
class SPDocument;
namespace Inkscape {
namespace XML {
- class Document;
+ struct Document;
class Node;
}
}
@@ -31,10 +31,10 @@ namespace Inkscape {
#include "CharTypes.h"
class GooString;
class Function;
-struct GfxState;
-class GfxColor;
+class GfxState;
+struct GfxColor;
class GfxColorSpace;
-class GfxRGB;
+struct GfxRGB;
class GfxPath;
class GfxPattern;
class GfxTilingPattern;
@@ -223,6 +223,8 @@ private:
Inkscape::XML::Node *_preferences; // Preferences container node
double _width; // Document size in px
double _height; // Document size in px
+ double _ttm[6]; ///< temporary transform matrix
+ bool _ttm_is_set;
};
diff --git a/src/extension/internal/svg.cpp b/src/extension/internal/svg.cpp
index 9b1098afd..8b272af60 100644
--- a/src/extension/internal/svg.cpp
+++ b/src/extension/internal/svg.cpp
@@ -44,7 +44,7 @@ using Inkscape::XML::Node;
-void pruneExtendedAttributes( Inkscape::XML::Node *repr )
+static void pruneExtendedAttributes( Inkscape::XML::Node *repr )
{
if (repr) {
if ( repr->type() == Inkscape::XML::ELEMENT_NODE ) {
diff --git a/src/extension/internal/text_reassemble.c b/src/extension/internal/text_reassemble.c
new file mode 100644
index 000000000..c62c83120
--- /dev/null
+++ b/src/extension/internal/text_reassemble.c
@@ -0,0 +1,2926 @@
+/**
+ @file text_reassemble.c
+
+\verbatim
+Method:
+ 1. For all ordered text objects which are sequential and share the same esc.
+ 2. For the first only pull x,y,esc and save, these define origin and rotation.
+ 3. Save the text object.
+ 4. Phase I: For all saved text objects construct lines.
+ 5. Check for allowed overlaps on sequential saved text object bounding rectangles.
+ 6 If found merge second with first, check next one.
+ 7. If not found, start a new complex (line).
+ 8. Phase II; for all lines construct paragraphs.
+ 9. Check alignment and line spacing of preceding line with current line.
+ 10. if alignment is the same, and line spacing is compatible merge current line into
+ current paragraph. Reaverage line spacing over all lines in paragraph. Check next one.
+ 11. If alignment does not match start a new paragraph.
+ (Test program)
+ 12. Over all phase II paragraphs
+ 13. Over all phase I lines in each paragraph.
+ 14. Over all text objects in each line.
+ Emit SVG corresponding to this construct to a file dump.svg.
+ (Conversion to other file types would be modeled on this example.)
+ 15. Clean up.
+ (General program)
+ Like for the Test program, but final representation may not be SVG.
+ Text object and bounding rectangle memory would all be released. If another set of
+ text will be processed then hang onto both Freetype and Fontconfig structures. If no
+ other text will be processed here, then also release Freetype structures. If the caller uses
+ Fontconfig elsewhere then do not release it, otherwise, do so.
+
+NOTE ON COORDINATES: x is positive to the right, y is positive down. So (0,0) is the upper left corner, and the
+lower left corner of a rectangle has a LARGER Y coordinate than the upper left. Ie, LL=(10,10) UR=(30,5) is typical.
+\endverbatim
+*/
+
+/*
+
+Compilation of test program (with all debugging output, but not loop testing):
+On Windows use:
+
+ gcc -Wall -DWIN32 -DTEST -DDBG_TR_PARA -DDBG_TR_INPUT \
+ -I. -I/c/progs/devlibs32/include -I/c/progs/devlibs32/include/freetype2\
+ -o text_reassemble text_reassemble.c uemf_utf.c \
+ -lfreetype6 -lfontconfig-1 -liconv -lm -L/c/progs/devlibs32/bin
+
+On Linux use:
+
+ gcc -Wall -DTEST -DDBG_TR_PARA -DDBG_TR_INPUT -I. -I/usr/include/freetype2 -o text_reassemble text_reassemble.c uemf_utf.c -lfreetype -lfontconfig -lm
+
+Compilation of object file only (Windows):
+
+ gcc -Wall -DWIN32 -c \
+ -I. -I/c/progs/devlibs32/include -I/c/progs/devlibs32/include/freetype2\
+ text_reassemble.c
+
+Compilation of object file only (Linux):
+ gcc -Wall -c -I. -I/usr/include/freetype2 text_reassemble.c
+
+
+Optional compiler switches for development:
+ -DDBG_TR_PARA draw bounding rectangles for paragraphs in SVG output
+ -DDBG_TR_INPUT draw input text and their bounding rectangles in SVG output
+ -DTEST build the test program
+ -DDBG_LOOP force the test program to cycle 5 times. Useful for finding
+ memory leaks. Ouput file is overwritten each time.
+
+
+File: text_reassemble.c
+Version: 0.0.13
+Date: 24-MAR-2014
+Author: David Mathog, Biology Division, Caltech
+email: mathog@caltech.edu
+Copyright: 2014 David Mathog and California Institute of Technology (Caltech)
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "text_reassemble.h"
+#include <libuemf/uemf_utf.h> /* For a couple of text functions. Exact copy from libUEMF. */
+#include <float.h>
+
+/* Code generated by make_ucd_mn_table.c using:
+ cat mnlist.txt | ./make_ucd_mn_table >generated.c
+*/
+#include <stdint.h>
+int is_mn_unicode(int test){
+#define MN_TEST_LIMIT 921600
+#define N_SPAGES 225
+#define N_CPAGES 192
+#define N_CPDATA 344
+#define C_PER_S 16
+ uint8_t superpages[N_SPAGES]={
+ 0x01, 0x02, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x06,
+ 0x07, 0x08, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0B};
+
+ uint8_t cpages[N_CPAGES]={
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+ 0x0E, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x00, 0x00,
+ 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x19, 0x00, 0x00,
+ 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x1C, 0x1D, 0x1E, 0x1F, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x21, 0x00,
+ 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x24, 0x25, 0x00, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27,
+ 0x00, 0x28, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+ uint32_t cpage_data[N_CPDATA]={
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000FFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x000000F8, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFE0000, 0xBFFFFFFF, 0x000000B6, 0x00000000,
+ 0x07FF0000, 0x00000000, 0xFFFFF800, 0x00010000, 0x00000000, 0x00000000, 0x9FC00000, 0x00003D9F,
+ 0x00020000, 0xFFFF0000, 0x000007FF, 0x00000000, 0x00000000, 0x0001FFC0, 0x00000000, 0x000FF800,
+ 0xFBC00000, 0x00003EEF, 0x0E000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x7FFFFFF0,
+ 0x00000007, 0x14000000, 0x00FE21FE, 0x0000000C, 0x00000002, 0x10000000, 0x0000201E, 0x0000000C,
+ 0x00000006, 0x10000000, 0x00023986, 0x00230000, 0x00000006, 0x10000000, 0x000021BE, 0x0000000C,
+ 0x00000002, 0x90000000, 0x0040201E, 0x0000000C, 0x00000004, 0x00000000, 0x00002001, 0x00000000,
+ 0x00000000, 0xC0000000, 0x00603DC1, 0x0000000C, 0x00000000, 0x90000000, 0x00003040, 0x0000000C,
+ 0x00000000, 0x00000000, 0x0000201E, 0x0000000C, 0x00000000, 0x00000000, 0x005C0400, 0x00000000,
+ 0x00000000, 0x07F20000, 0x00007F80, 0x00000000, 0x00000000, 0x1BF20000, 0x00003F00, 0x00000000,
+ 0x03000000, 0x02A00000, 0x00000000, 0x7FFE0000, 0xFEFFE0DF, 0x1FFFFFFF, 0x00000040, 0x00000000,
+ 0x00000000, 0x66FDE000, 0xC3000000, 0x001E0001, 0x20002064, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0xE0000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x001C0000, 0x001C0000, 0x000C0000, 0x000C0000, 0x00000000, 0x3FB00000, 0x200FFE40, 0x00000000,
+ 0x00003800, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000200, 0x00000000, 0x00000000,
+ 0x00000000, 0x0E040187, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x01800000, 0x00000000, 0x7F400000, 0x9FF81FE5, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x0000000F, 0x17D00000, 0x00000004, 0x000FF800, 0x00000003, 0x00000B3C, 0x00000000, 0x0003A340,
+ 0x00000000, 0x00CFF000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFF70000, 0x001021FD,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xF000007F,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x1FFF0000, 0x0001FFE2,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00038000,
+ 0x00000000, 0x00000000, 0x00000000, 0x80000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF,
+ 0x00000000, 0x00003C00, 0x00000000, 0x00000000, 0x06000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x3FF08000, 0x80000000, 0x00000000, 0x00000000, 0x00030000,
+ 0x00000844, 0x00000060, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000010, 0x0003FFFF,
+ 0x00000000, 0x00003FC0, 0x0003FF80, 0x00000000, 0x00000007, 0x13C80000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00667E00, 0x00001008, 0x00000000, 0x00000000, 0xC19D0000, 0x00000002, 0x00403000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00002120,
+ 0x40000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x0000FFFF, 0x0000007F, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x20000000,
+ 0x0000F06E, 0x87000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000002, 0xFF000000, 0x0000007F, 0x00000000, 0x00000003, 0x06780000, 0x00000000, 0x00000000,
+ 0x00000007, 0x001FEF80, 0x00000000, 0x00000000, 0x00000003, 0x7FC00000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00BF2800, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00078000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0xF8000380, 0x00000FE7, 0x00003C00, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x0000001C, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000FFFF};
+
+ int result=0;
+
+ int spage_idx;
+ int cpage_row, cpage_column, cpage_idx;
+ int cd_row, cd_column, cd_idx, cd_bit;
+
+ if(test<MN_TEST_LIMIT){
+ spage_idx = test >> 12;
+ cpage_row = superpages[spage_idx];
+ cpage_column = (test>>8) & 15;
+ cpage_idx = C_PER_S*cpage_row + cpage_column;
+ cd_row = cpages[cpage_idx];
+ cd_column = test>>5 & 7;
+ cd_idx = 8*cd_row + cd_column;
+ cd_bit = test & 31;
+ result = cpage_data[cd_idx] & (1 << cd_bit);
+ }
+ return(result);
+}
+
+
+
+/**
+ \brief Find a (sub)string in a caseinvariant manner, used for locating "Narrow" in font name
+ \return Returns -1 if no match, else returns the position (numbered from 0) of the first character of the match.
+ \param string Text to search
+ \param sub Text to find
+*/
+int TR_findcasesub(const char *string, const char *sub){
+ int i,j;
+ int match=0;
+ for(i=0; string[i]; i++){
+ for(match=1,j=0; sub[j] && string[i+j]; j++){
+ if(toupper(sub[j]) != toupper(string[i+j])){
+ match=0;
+ break;
+ }
+ }
+ if(match && !sub[j])break; /* matched over the entire substring */
+ }
+ return((match ? i : -1));
+}
+
+/**
+ \brief Constrouct a fontspec from a TCHUNK_SPECS and a fontname
+ \return Returns NULL on error, new fontspec on success
+ \param tsp pointer to TCHUNK_SPECS to use for information
+ \param fontname Fontname to use in the new fontspec
+*/
+ /* construct a font name */
+char *TR_construct_fontspec(const TCHUNK_SPECS *tsp, const char *fontname){
+ int newlen = 128 + strlen(fontname); /* too big, but not by much */
+ char *newfs = NULL;
+ newfs = (char *) malloc(newlen);
+ sprintf(newfs,"%s:slant=%d:weight=%d:size=%lf:width=%d",fontname,tsp->italics,tsp->weight,tsp->fs,(tsp->co ? 75 : tsp->condensed));
+ return(newfs);
+}
+
+
+/**
+ \brief Reconstrouct a fontspec by substituting a font name into an existing spec
+ \return Returns NULL on error, new fontspec on success
+ \param fontspec Original fontspec, only the name will be changed
+ \param fontname Fontname to substitute into the new fontspec
+*/
+ /* construct a font name */
+char *TR_reconstruct_fontspec(const char *fontspec, const char *fontname){
+ int colon;
+ int newlen = strlen(fontspec) + strlen(fontname) + 1; /* too big, but not by much */
+ char *newfs = NULL;
+ newfs = (char *) malloc(newlen);
+ colon = strcspn(fontspec,":");
+ if(colon){ sprintf(newfs,"%s%s",fontname,&fontspec[colon]); }
+ return(newfs);
+}
+
+/**
+ \brief Find a font in the list that has a glyph for this character, change alternate to match
+ \return Returns 0 if no match or an error, else returns the glyph index in the new alternate font
+ \param fti pointer to the FT_INFO structure, may be modified if alternate font is added
+ \param efsp Pointer to a Pointer to the original FNT_SPECS struct. On return contains the FNT_SPECS corresponding to the glyph_index..
+ \param wc Current character (32 bit int)
+*/
+int TR_find_alternate_font(FT_INFO *fti, FNT_SPECS **efsp, uint32_t wc){
+ int glyph_index=0; /* this is the unknown character glyph */
+ uint32_t i;
+ FcCharSet *cs;
+ FcResult result = FcResultMatch;
+ FcPattern *pattern, *fpat;
+ char *filename;
+ char *fontname;
+ char *newfontspec;
+ int fi_idx;
+ FNT_SPECS *fsp,*fsp2;
+ if(!fti || !efsp || !*efsp)return(0);
+ fsp = *efsp;
+ for(i=0;i<fsp->used;i++){ /* first check in alts */
+ fsp2 = &fti->fonts[fsp->alts[i].fi_idx]; /* these are in order of descending previous usage */
+ glyph_index = FT_Get_Char_Index( fsp2->face, wc); /* we have the face, might as well check that directly */
+ if (glyph_index){ /* found a glyph for the character in this font */
+ (void) fsp_alts_weight(fsp, i);
+ *efsp = fsp2;
+ return(glyph_index);
+ }
+ }
+
+ /* it was not in alts, now go through fontset and see if it is in there */
+ for(i=1; i< (unsigned int) fsp->fontset->nfont;i++){ /* already know the primary does not have this character */
+ result = FcPatternGetCharSet(fsp->fontset->fonts[i], FC_CHARSET, 0, &cs);
+ if(result != FcResultMatch) return(0); /* some terrible problem, this should never happen */
+ if (FcCharSetHasChar(cs, wc)){ /* found a glyph for the character in this font */
+ glyph_index = i;
+
+ /* Do a lot of work to find the filename corresponding to the fontset entry.
+ None of these should ever fail, but if one does, return 0
+ */
+ if(
+ !(pattern = FcNameParse((const FcChar8 *)&(fsp->fontspec))) ||
+ !FcConfigSubstitute(NULL, pattern, FcMatchPattern)
+ )return(0);
+ FcDefaultSubstitute(pattern);
+ if(
+ !(fpat = FcFontRenderPrepare(NULL, pattern, fsp->fontset->fonts[i])) ||
+ (FcPatternGetString( fpat, FC_FILE, 0, (FcChar8 **)&filename) != FcResultMatch) ||
+ (FcPatternGetString( fsp->fontset->fonts[i], FC_FULLNAME, 0, (FcChar8 **)&fontname) != FcResultMatch)
+ )return(0);
+
+ /* find the font (added from an unrelated fontset, for instance) or insert it as new */
+ fi_idx = ftinfo_find_loaded_by_src(fti, (uint8_t *) filename);
+ if(fi_idx < 0){
+ newfontspec = TR_reconstruct_fontspec((char *) fsp->fontspec, fontname);
+ fi_idx = ftinfo_load_fontname(fti, newfontspec);
+ free(newfontspec);
+ if(fi_idx < 0)return(0); /* This could happen if we run out of memory*/
+ }
+
+ /* add the new font index to the alts list on the (current) fsp. */
+ (void) fsp_alts_insert(fsp, fi_idx);
+
+ /* release FC's own memory related to this call that does not need to be kept around so that face will work */
+ FcPatternDestroy(pattern);
+
+ *efsp = &(fti->fonts[fi_idx]);
+ return(glyph_index);
+ }
+ }
+
+ return(0);
+}
+
+/**
+ \brief Get the advance for the 32 bit character
+
+ \return Returns -1 on error, or advance in units of 1/64th of a Point.
+ \param fti pointer to the FT_INFO structure, may be modified if alternate font is required
+ \param fsp Pointer to FNT_SPECS struct.
+ \param wc Current character (32 bit int)
+ \param pc Previous character
+ \param load_flags Controls internal advance:
+ FT_LOAD_NO_SCALE, internal advance is in 1/64th of a point. (kerning values are still scaled)
+ FT_LOAD_TARGET_NORMAL internal advance is in 1/64th of a point. The scale
+ factor seems to be (Font Size in points)*(DPI)/(32.0 pnts)*(72 dpi).
+ \param kern_mode FT_KERNING_DEFAULT, FT_KERNING_UNFITTED, or FT_KERNING_UNSCALED. Set to match calling application.
+ \param ymin If the pointer is defined, the value is adjusted if ymin of wc character is less than the current value.
+ \param ymax If the pointer is defined, the value is adjusted if ymin of wc character is more than the current value.
+*/
+int TR_getadvance(FT_INFO *fti, FNT_SPECS *fsp, uint32_t wc, uint32_t pc, int load_flags, int kern_mode, int *ymin, int *ymax){
+ FT_Glyph glyph;
+ int glyph_index;
+ int advance=-1;
+ FT_BBox bbox;
+
+ if(is_mn_unicode(wc))return(0); /* no advance on Unicode Mn characters */
+
+ glyph_index = FT_Get_Char_Index( fsp->face, wc);
+ if(!glyph_index){ /* not in primary font, check alternates */
+ glyph_index = TR_find_alternate_font(fti, &fsp, wc);
+ }
+ if(glyph_index){
+ if (!FT_Load_Glyph( fsp->face, glyph_index, load_flags )){
+ if ( !FT_Get_Glyph( fsp->face->glyph, &glyph ) ) {
+ advance = fsp->face->glyph->advance.x;
+ FT_Glyph_Get_CBox( glyph, FT_GLYPH_BBOX_UNSCALED, &bbox );
+ if(ymin && (bbox.yMin < *ymin))*ymin=bbox.yMin;
+ if(ymax && (bbox.yMax > *ymax))*ymax=bbox.yMax;
+ if(pc)advance += TR_getkern2(fsp, wc, pc, kern_mode);
+ FT_Done_Glyph(glyph);
+ }
+ }
+ }
+ /* If there was no way to determine the width, this returns the error value */
+ return(advance);
+}
+
+/**
+ \brief Get the kerning for a pair of 32 bit characters
+ \return Returns 0 on error, or kerning value (which may be 0) for the pair in units of 1/64th of a point.
+ \param fsp Pointer to FNT_SPECS struct.
+ \param wc Current character (32 bit int)
+ \param pc Previous character
+ \param kern_mode FT_KERNING_DEFAULT, FT_KERNING_UNFITTED, or FT_KERNING_UNSCALED. Set to match calling application.
+*/
+int TR_getkern2(FNT_SPECS *fsp, uint32_t wc, uint32_t pc, int kern_mode){
+ int this_glyph_index;
+ int prev_glyph_index;
+ int kern=0;
+ FT_Vector akerning;
+
+ this_glyph_index = FT_Get_Char_Index( fsp->face, wc);
+ prev_glyph_index = FT_Get_Char_Index( fsp->face, pc);
+ if(!FT_Get_Kerning( fsp->face,
+ prev_glyph_index,
+ this_glyph_index,
+ kern_mode,
+ &akerning )){
+ kern = akerning.x; /* Is sign correct? */
+ }
+ return(kern);
+}
+
+/**
+ \brief Get the kerning for a pair of 32 bit characters, where one is the last character in the previous text block, and the other is the first in the current text block.
+ \return Returns 0 on error, or kerning value (which may be 0) for the pair in units of 1/64th of a point.
+ \param fsp Pointer to FNT_SPECS struct.
+ \param tsp current text object
+ \param ptsp previous text object
+ \param kern_mode FT_KERNING_DEFAULT, FT_KERNING_UNFITTED, or FT_KERNING_UNSCALED. Set to match calling application.
+*/
+int TR_kern_gap(FNT_SPECS *fsp, TCHUNK_SPECS *tsp, TCHUNK_SPECS *ptsp, int kern_mode){
+ int kern=0;
+ uint32_t *text32=NULL;
+ uint32_t *ptxt32=NULL;
+ size_t tlen,plen;
+ while(ptsp && tsp){
+ text32 = U_Utf8ToUtf32le((char *) tsp->string, 0, &tlen);
+ if(!text32){ // LATIN1 encoded >128 are generally not valid UTF, so the first will fail
+ text32 = U_Latin1ToUtf32le((char *) tsp->string,0, &tlen);
+ if(!text32)break;
+ }
+ ptxt32 = U_Utf8ToUtf32le((char *) ptsp->string,0,&plen);
+ if(!ptxt32){ // LATIN1 encoded >128 are generally not valid UTF, so the first will fail
+ ptxt32 = U_Latin1ToUtf32le((char *) ptsp->string,0, &plen);
+ if(!ptxt32)break;
+ }
+ kern = TR_getkern2(fsp, *text32, ptxt32[plen-1], kern_mode);
+ break;
+ }
+ if(text32)free(text32);
+ if(ptxt32)free(ptxt32);
+ return(kern);
+}
+
+
+
+
+/**
+ \brief Find baseline on Y axis of a complex.
+ If the complex is a TR_TEXT or TR_LINE find its baseline.
+ If the complex is TR_PARA_[UCLR]J find the baseline of the last line.
+ If there are multiple text elements in a TR_LINE, the baseline is that of the
+ element that uses the largest font. This will definitely give the wrong
+ result if that line starts with a super or subscript that is full font size, but
+ they are usually smaller.
+ \return Returns 0 if it cannot determine a baseline, else returns the baseline Y coordinate.
+ \param tri pointer to the TR_INFO structure holding all TR data
+ \param src index of the current complex
+ \param ymax If the pointer is defined, the value is adjusted if ymax of current complex is more than the current value.
+ \param ymin If the pointer is defined, the value is adjusted if ymin of current complex is less than the current value.
+*/
+double TR_baseline(TR_INFO *tri, int src, double *ymax, double *ymin){
+ double baseline=0;
+ volatile double tmp=0.0; /* This MUST be volatile */
+ double yheight;
+ int last;
+ int i;
+ int trec;
+ CX_INFO *cxi=tri->cxi;
+ BR_INFO *bri=tri->bri;
+ TP_INFO *tpi=tri->tpi;
+ FT_INFO *fti=tri->fti;
+ FNT_SPECS *fsp;
+ last = cxi->cx[src].kids.used - 1;
+ switch (cxi->cx[src].type){
+ case TR_TEXT:
+ trec = cxi->cx[src].kids.members[0]; /* for this complex type there is only ever one member */
+ baseline = bri->rects[trec].yll - tpi->chunks[trec].boff;
+ fsp = &(fti->fonts[tpi->chunks[trec].fi_idx]);
+ yheight = fsp->face->bbox.yMax - fsp->face->bbox.yMin;
+ if(ymax){
+ tmp = tpi->chunks[trec].fs * ((double)fsp->face->bbox.yMax/yheight);
+ if(*ymax <= tmp)*ymax = tmp;
+ }
+ else if(ymin){
+ tmp = tpi->chunks[trec].fs * ((double)-fsp->face->bbox.yMin/yheight); /* yMin in face is negative */
+ if(*ymin <= tmp)*ymin = tmp;
+ }
+ break;
+ case TR_LINE:
+ for(i=last;i>=0;i--){ /* here last is the count of text objects in the complex */
+ trec = cxi->cx[src].kids.members[i];
+ fsp = &(fti->fonts[tpi->chunks[trec].fi_idx]);
+ yheight = fsp->face->bbox.yMax - fsp->face->bbox.yMin;
+ if(ymax){
+ tmp = tpi->chunks[trec].fs * (((double)fsp->face->bbox.yMax)/yheight);
+ /* gcc 4.6.3 had a bizarre optimization error for -O2 and -O3 where *ymax <= tmp was
+ not true when *ymax == tmp, as verified by examining the binary representations.
+ This was apparently due to retained excess precision. Making tmp volatile
+ forces it to be stored into a 64 bit location, dropping the extra 12 bits from
+ the 80 bit register. */
+ if(*ymax <= tmp){
+ *ymax = tmp;
+ baseline = bri->rects[trec].yll - tpi->chunks[trec].boff;
+ }
+ }
+ else if(ymin){
+ tmp = tpi->chunks[trec].fs * (((double)-fsp->face->bbox.yMin)/yheight); /* yMin in face is negative */
+ if(*ymin <= tmp){
+ *ymin = tmp;
+ baseline = bri->rects[trec].yll - tpi->chunks[trec].boff;
+ }
+ }
+ }
+ break;
+ case TR_PARA_UJ:
+ case TR_PARA_LJ:
+ case TR_PARA_CJ:
+ case TR_PARA_RJ:
+ trec = cxi->cx[src].kids.members[last];
+ baseline = TR_baseline(tri, trec, ymax, ymin);
+ break;
+ }
+ return(baseline);
+}
+
+/**
+ \brief Check or set vertical advance on the growing complex relative to the current complex.
+ Vadvance is a multiplicative factor like 1.25.
+ The distance between successive baselines is vadvance * max(font_size), where the maximum
+ is over all text elements in src.
+ The growing complex is always the last one in the CX_INFO section of the TR_INFO structure.
+ If an existing vadvance does not match the one which would be required to fit the next complex
+ to add to the growing one, it terminates a growing complex. (Ie, starts a new paragraph.)
+ Find baseline on Y axis of a complex.
+ If the complex is a TR_TEXT or TR_LINE find its baseline.
+ If the complex is TR_PARA+* find the baseline of the last line.
+ If there are multiple text elements in a TR_LINE, the baseline is that of the
+ element that uses the largest font. This will definitely give the wrong
+ result if that line starts with a super or subscript that is full font size, but
+ they are usually smaller.
+ \return Returns 0 on success, !0 on failure.
+ \param tri pointer to the TR_INFO structure holding all TR data
+ \param src index of the current complex, to be added to the growing complex.
+ This lets the value of "src - lines" determine the weight to give to each new vadvance value
+ as it is merged into the running weighted average. This improves the accuracy of the vertical advance,
+ since there can be some noise introduced when lines have different maximum font sizes.
+ \param lines index of the first text block that was added to the growing complex.
+*/
+int TR_check_set_vadvance(TR_INFO *tri, int src, int lines){
+ int status = 0;
+ CX_INFO *cxi = tri->cxi;
+ TP_INFO *tpi = tri->tpi;
+ double ymax = DBL_MIN;
+ double ymin = DBL_MIN;
+ double prevbase;
+ double thisbase;
+ double weight;
+ int trec;
+ double newV;
+ int dst;
+
+ dst = cxi->used-1; /* complex being grown */
+
+ prevbase = TR_baseline(tri, dst, NULL, &ymin);
+ thisbase = TR_baseline(tri, src, &ymax, NULL);
+ newV = (thisbase - prevbase)/(ymax + ymin);
+ trec = cxi->cx[dst].kids.members[0]; /* complex whose first text record holds vadvance for this complex */
+ trec = cxi->cx[trec].kids.members[0]; /* text record that halds vadvance for this complex */
+ if(tpi->chunks[trec].vadvance){
+ /* already set on the first text (only place it is stored.)
+ See if the line to be added is compatible.
+ All text fields in a complex have the same advance, so just set/check the first one.
+ vadvance must be within 1% or do not add a new line */
+ if(fabs(1.0 - (tpi->chunks[trec].vadvance/newV) > 0.01)){
+ status = 1;
+ }
+ else { /* recalculate the weighted vadvance */
+ weight = (1.0 / (double) (src - lines));
+ tpi->chunks[trec].vadvance = tpi->chunks[trec].vadvance*(1.0-weight) + newV*weight;
+ }
+ }
+ else { /* only happens when src = lines + 1*/
+ tpi->chunks[trec].vadvance = newV;
+ }
+ return(status);
+}
+
+
+/**
+ \brief Initialize an FT_INFO structure. Sets up a freetype library to use in this context.
+ \returns a pointer to the FT_INFO structure created, or NULL on error.
+*/
+FT_INFO *ftinfo_init(void){
+ FT_INFO *fti = NULL;
+ if(FcInit()){
+ fti = (FT_INFO *)calloc(1,sizeof(FT_INFO));
+ if(fti){
+ if(!FT_Init_FreeType( &(fti->library))){
+ fti->space=0;
+ fti->used=0;
+
+ if(ftinfo_make_insertable(fti)){
+ FT_Done_FreeType(fti->library);
+ free(fti);
+ fti=NULL;
+ }
+ }
+ else {
+ free(fti);
+ fti=NULL;
+ }
+ }
+ if(!fti)FcFini();
+ }
+ return(fti);
+}
+
+/**
+ \brief Make an FT_INFO structure insertable. Adds storage as needed.
+ \param fti pointer to the FT_INFO structure
+ \returns 0 on success, !0 on error.
+*/
+int ftinfo_make_insertable(FT_INFO *fti){
+ int status=0;
+ if(!fti)return(2);
+ if(fti->used >= fti->space){
+ fti->space += ALLOCINFO_CHUNK;
+ if((fti->fonts = (FNT_SPECS *) realloc(fti->fonts, fti->space * sizeof(FNT_SPECS) ))){
+ memset(&fti->fonts[fti->used],0,(fti->space - fti->used)*sizeof(FNT_SPECS));
+ }
+ else {
+ status=1;
+ }
+ }
+ return(status);
+}
+
+
+/**
+ \brief Insert a copy of a FNT_SPECS structure into the FT_INFO structure.
+ \param fti pointer to the FT_INFO structure.
+ \param fsp pointer to the FNT_SPECS structure.
+ \returns 0 on success, !0 on error.
+*/
+int ftinfo_insert(FT_INFO *fti, FNT_SPECS *fsp){
+ int status=1;
+ if(!fti)return(2);
+ if(!fsp)return(3);
+ if(!(status = ftinfo_make_insertable(fti))){
+ memcpy(&(fti->fonts[fti->used]),fsp,sizeof(FNT_SPECS));
+ fti->used++;
+ }
+ return(status);
+}
+
+
+
+/**
+ \brief Release an FT_INFO structure. Release all associated memory.
+ Use like: fi_ptr = ftinfo_release(fi_ptr)
+ \param fti pointer to the FT_INFO structure.
+ \returns NULL.
+*/
+FT_INFO *ftinfo_release(FT_INFO *fti){
+ (void) ftinfo_clear(fti);
+ FcFini(); /* shut down FontConfig, release memory, patterns must have already been released or boom! */
+ return NULL;
+}
+
+
+/**
+ \brief Clear an FT_INFO structure. Release all Freetype memory but does not release Fontconfig.
+ This would be called in preference to ftinfo_release() if some other part of the program needed
+ to continue using Fontconfig.
+ Use like: fi_ptr = ftinfo_clear(fi_ptr)
+ \param fti pointer to the FT_INFO structure.
+ \returns NULL.
+*/
+FT_INFO *ftinfo_clear(FT_INFO *fti){
+ uint32_t i;
+ FNT_SPECS *fsp;
+ if(fti){
+ for(i=0;i<fti->used;i++){
+ fsp = &(fti->fonts[i]);
+ FT_Done_Face(fsp->face); /* release memory for face controlled by FreeType */
+ free(fsp->file); /* release memory holding copies of paths */
+ free(fsp->fontspec); /* release memory holding copies of font names */
+ FcPatternDestroy(fsp->fpat); /* release memory for FontConfig fpats */
+ FcFontSetDestroy(fsp->fontset);
+ if(fsp->alts){ free(fsp->alts); }
+ }
+ free(fti->fonts);
+ FT_Done_FreeType(fti->library); /* release all other FreeType memory */
+ free(fti);
+ }
+ return NULL;
+}
+
+
+/**
+ \brief Find the loaded font matching fontspec
+ \returns index of font on success, -1 if not found
+ \param tri pointer to the TR_INFO structure.
+ \param fontspec UTF-8 description of the font, as constructed in trinfo_load_fontname
+*/
+
+int ftinfo_find_loaded_by_spec(const FT_INFO *fti, const uint8_t *fontspec){
+ uint32_t i;
+ int status = -1;
+ /* If it is already loaded, do not load it again */
+ for(i=0;i<fti->used;i++){
+ if(0==strcmp((char *) fti->fonts[i].fontspec, (char *)fontspec)){
+ status=i;
+ break;
+ }
+ }
+ return(status);
+}
+
+/**
+ \brief Find the loaded font matching the source file
+ \returns index of font on success, -1 if not found
+ \param tri pointer to the TR_INFO structure.
+ \param filename UTF-8 file name for the font
+*/
+
+int ftinfo_find_loaded_by_src(const FT_INFO *fti, const uint8_t *filename){
+ uint32_t i;
+ int status = -1;
+ /* If it is already loaded, do not load it again */
+ for(i=0;i<fti->used;i++){
+ if(0==strcmp((char *) fti->fonts[i].file, (char *) filename)){
+ status=i;
+ break;
+ }
+ }
+ return(status);
+}
+
+
+/**
+ \brief Load a (new) font by name into a TR_INFO structure or find it if it is already loaded
+ \returns fi_idx of inserted (or found) font on success, <0 on error.
+ \param fti pointer to the FT_INFO structure.
+ \param fontname UTF-8 font name
+ \param fontspec UTF-8 font specification used for query string.
+*/
+
+int ftinfo_load_fontname(FT_INFO *fti, const char *fontspec){
+ FcPattern *pattern = NULL;
+ FcPattern *fpat = NULL;
+ FcFontSet *fontset = NULL;
+ FcResult result = FcResultMatch;
+ char *filename;
+ double fd;
+ FNT_SPECS *fsp;
+ int status;
+ int fi_idx;
+
+ if(!fti)return(-1);
+
+ /* If it is already loaded, do not load it again */
+ status = ftinfo_find_loaded_by_spec(fti, (uint8_t *) fontspec);
+ if(status >= 0){ return(status); }
+ status = 0; /* was -1, reset to 0 */
+
+ ftinfo_make_insertable(fti);
+ fi_idx = fti->used;
+
+ pattern = FcNameParse((const FcChar8 *)fontspec);
+ while(1) { /* this is NOT a loop, it uses breaks to avoid gotos and deep nesting */
+ if(!(pattern)){ status = -2; break; }
+ if(!FcConfigSubstitute(NULL, pattern, FcMatchPattern)){ status = -3; break; };
+ FcDefaultSubstitute(pattern);
+ /* get a fontset, trimmed to only those with new glyphs as needed, so that missing glyph's may be handled */
+ if(!(fontset = FcFontSort (NULL,pattern, FcTrue, NULL, &result)) || (result != FcResultMatch)){ status = -4; break; }
+ if(!(fpat = FcFontRenderPrepare(NULL, pattern, fontset->fonts[0]))){ status = -405; break; }
+ if(FcPatternGetString( fpat, FC_FILE, 0, (FcChar8 **)&filename) != FcResultMatch){ status = -5; break; }
+ if(FcPatternGetDouble( fpat, FC_SIZE, 0, &fd) != FcResultMatch){ status = -6; break; }
+
+ /* copy these into memory for external use */
+ fsp = &(fti->fonts[fti->used]);
+ fsp->fontset = fontset;
+ fsp->alts = NULL; /* Initially no links to alternate fonts */
+ fsp->space = 0;
+ fsp->file = (uint8_t *) U_strdup((char *) filename);
+ fsp->fontspec = (uint8_t *) U_strdup((char *) fontspec);
+ fsp->fpat = fpat;
+ fsp->fsize = fd;
+ break;
+ }
+ /* release FC's own memory related to this call that does not need to be kept around so that face will work */
+ if(pattern)FcPatternDestroy(pattern); /* done with this memory */
+ if(status<0){
+ if(fontset)FcFontSetDestroy(fontset);
+ if(fpat)FcPatternDestroy(fpat);
+ return(status);
+ }
+
+ /* get the current face */
+ if(FT_New_Face( fti->library, (const char *) fsp->file, 0, &(fsp->face) )){ return(-8); }
+
+ if(FT_Set_Char_Size(
+ fsp->face, /* handle to face object */
+ 0, /* char_width in 1/64th of points */
+ fd*64, /* char_height in 1/64th of points */
+ 72, /* horizontal device resolution, DPI */
+ 72) /* vebrical device resolution, DPI */
+ ){ return(-9); }
+
+ /* The space advance is needed in various places. Get it now, and get it in the font units,
+ so that it can be scaled later with the text size */
+ status = TR_getadvance(fti, fsp,' ',0,FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP, FT_KERNING_UNSCALED, NULL, NULL);
+ if(status < 0)return(-7);
+ fsp->spcadv = ((double) status)/(64.0);
+
+ fti->used++;
+
+/*
+ char *fs;
+ int fb;
+ if(FcPatternGetBool( fpat, FC_OUTLINE, 0, &fb)== FcResultMatch){ printf("outline: %d\n",fb);fflush(stdout); }
+ if(FcPatternGetBool( fpat, FC_SCALABLE, 0, &fb)== FcResultMatch){ printf("scalable: %d\n",fb);fflush(stdout); }
+ if(FcPatternGetDouble( fpat, FC_DPI, 0, &fd)== FcResultMatch){ printf("DPI: %lf\n",fd);fflush(stdout); }
+ if(FcPatternGetInteger( fpat, FC_FONTVERSION, 0, &fb)== FcResultMatch){ printf("fontversion: %d\n",fb);fflush(stdout); }
+ if(FcPatternGetString( fpat, FC_FULLNAME , 0, (FcChar8 **)&fs)== FcResultMatch){ printf("FULLNAME : %s\n",fs);fflush(stdout); }
+ if(FcPatternGetString( fpat, FC_FAMILY , 0, (FcChar8 **)&fs)== FcResultMatch){ printf("FAMILY : %s\n",fs);fflush(stdout); }
+ if(FcPatternGetString( fpat, FC_STYLE , 0, (FcChar8 **)&fs)== FcResultMatch){ printf("STYLE : %s\n",fs);fflush(stdout); }
+ if(FcPatternGetString( fpat, FC_FOUNDRY , 0, (FcChar8 **)&fs)== FcResultMatch){ printf("FOUNDRY : %s\n",fs);fflush(stdout); }
+ if(FcPatternGetString( fpat, FC_FAMILYLANG , 0, (FcChar8 **)&fs)== FcResultMatch){ printf("FAMILYLANG : %s\n",fs);fflush(stdout); }
+ if(FcPatternGetString( fpat, FC_STYLELANG , 0, (FcChar8 **)&fs)== FcResultMatch){ printf("STYLELANG : %s\n",fs);fflush(stdout); }
+ if(FcPatternGetString( fpat, FC_FULLNAMELANG, 0, (FcChar8 **)&fs)== FcResultMatch){ printf("FULLNAMELANG: %s\n",fs);fflush(stdout); }
+ if(FcPatternGetString( fpat, FC_CAPABILITY , 0, (FcChar8 **)&fs)== FcResultMatch){ printf("CAPABILITY : %s\n",fs);fflush(stdout); }
+ if(FcPatternGetString( fpat, FC_FONTFORMAT , 0, (FcChar8 **)&fs)== FcResultMatch){ printf("FONTFORMAT : %s\n",fs);fflush(stdout); }
+*/
+
+ return(fi_idx);
+}
+
+/**
+ \brief Dump the contents of the TR_INFO structure to stdout. For debugging purposes,not used in production code.
+ \param tri pointer to the TR_INFO structure.
+*/
+void ftinfo_dump(const FT_INFO *fti){
+ uint32_t i,j;
+ FNT_SPECS *fsp;
+ printf("fti space: %d\n",fti->space);
+ printf("fti used: %d\n",fti->used);
+ for(i=0; i< fti->used; i++){
+ fsp = &(fti->fonts[i]);
+ printf("fti font: %6d space: %6d used: %6d spcadv %8lf fsize %8lf \n",i,fsp->space,fsp->used,fsp->spcadv,fsp->fsize);
+ printf(" file: %s\n",fsp->file);
+ printf(" fspc: %s\n",fsp->fontspec);
+ for(j=0;j<fsp->used;j++){
+ printf(" alts: %6d fi_idx: %6d wgt: %6d\n",j,fsp->alts[j].fi_idx,fsp->alts[j].weight);
+ }
+ }
+
+}
+
+/**
+ \brief Make the FNT_SPECS alts structure insertable. Adds storage as needed.
+ \param fti pointer to the FT_INFO structure
+ \returns 0 on success, !0 on error.
+*/
+int fsp_alts_make_insertable(FNT_SPECS *fsp){
+ int status=0;
+ if(!fsp)return(2);
+ if(fsp->used >= fsp->space){
+ fsp->space += ALLOCINFO_CHUNK;
+ if((fsp->alts = (ALT_SPECS *) realloc(fsp->alts, fsp->space * sizeof(ALT_SPECS) ))){
+ memset(&fsp->alts[fsp->used],0,(fsp->space - fsp->used)*sizeof(ALT_SPECS));
+ }
+ else {
+ status=1;
+ }
+ }
+ return(status);
+}
+
+
+/**
+ \brief Insert a new ALT_SPECS into the FNT_SPECS alts list.
+ \param fsp pointer to the FNT_SPECS structure.
+ \param fi_idx font index to add to the alts list
+ \returns 0 on success, !0 on error.
+*/
+int fsp_alts_insert(FNT_SPECS *fsp, uint32_t fi_idx){
+ int status=1;
+ ALT_SPECS alt;
+ if(!fsp)return(3);
+ alt.fi_idx = fi_idx;
+ alt.weight = 1; /* new ones start with this weight, it can only go up */
+ if(!(status = fsp_alts_make_insertable(fsp))){
+ fsp->alts[fsp->used] = alt;
+ fsp->used++;
+ }
+ return(status);
+}
+
+/**
+ \brief Increment the weight of an alts entry by 1, readjust order if necessary
+ \param fsp pointer to the FNT_SPECS structure.
+ \param idx index of the alts entry to increment
+ \returns 0 on success, !0 on error.
+*/
+int fsp_alts_weight(FNT_SPECS *fsp, uint32_t a_idx){
+ uint32_t i;
+ ALT_SPECS alt;
+ if(!fsp)return(1);
+ if(!fsp->used)return(2);
+ if(a_idx >= fsp->used)return(3);
+ /* If a counter hits the limit divide all counts in half. */
+ if(fsp->alts[a_idx].weight == UINT32_MAX){
+ for(i=0; i<fsp->used; i++){ fsp->alts[i].weight /= 2; }
+ }
+ fsp->alts[a_idx].weight++;
+ for(i=a_idx; i>0; i--){
+ if(fsp->alts[i-1].weight >= fsp->alts[a_idx].weight)break;
+ alt = fsp->alts[i-1];
+ fsp->alts[i-1] = fsp->alts[a_idx];
+ fsp->alts[a_idx] = alt;
+ }
+ return(0);
+}
+
+
+
+/**
+ \brief Make a CHILD_SPECS structure insertable. Adds storage as needed.
+ \param csp pointer to the CHILD_SPECS structure
+ \returns 0 on success, !0 on error.
+*/
+int csp_make_insertable(CHILD_SPECS *csp){
+ int status=0;
+ if(!csp)return(2);
+ if(csp->used >= csp->space){
+ csp->space += ALLOCINFO_CHUNK;
+ if((csp->members = (int *) realloc(csp->members, csp->space * sizeof(int) ))){
+ memset(&csp->members[csp->used],0,(csp->space - csp->used)*sizeof(int));
+ }
+ else {
+ status=1;
+ }
+ }
+ return(status);
+}
+
+/**
+ \brief Add a member to a CHILD_SPECS structure. (Member is an index for either a text object or a complex.)
+ \param dst pointer to the CHILD_SPECS structure.
+ \param src index of the member.
+ \returns 0 on success, !0 on error.
+*/
+int csp_insert(CHILD_SPECS *dst, int src){
+ int status=1;
+ if(!dst)return(2);
+ if(!(status=csp_make_insertable(dst))){
+ dst->members[dst->used]=src;
+ dst->used++;
+ }
+ return(status);
+}
+
+/**
+ \brief Append all the members of one CHILD_SPECS structure to another CHILD_SPECS structure.
+ Member is an index for either a text object or a complex.
+ The donor is not modified.
+ \param dst pointer to the recipient CHILD_SPECS structure.
+ \param src pointer to the donor CHILD_SPECS structure.
+ \returns 0 on success, !0 on error.
+*/
+int csp_merge(CHILD_SPECS *dst, CHILD_SPECS *src){
+ uint32_t i;
+ int status=1;
+ if(!dst)return(2);
+ if(!src)return(3);
+ for(i=0;i<src->used;i++){
+ status = csp_insert(dst, src->members[i]);
+ if(status)break;
+ }
+ return(status);
+}
+
+/**
+ \brief Release a CHILD_SPECS structure. Release all associated memory.
+ \param csp pointer to the CHILD_SPECS structure.
+ \returns NULL.
+*/
+void csp_release(CHILD_SPECS *csp){
+ if(csp){
+ free(csp->members);
+ csp->space = 0;
+ csp->used = 0;
+ }
+}
+
+/**
+ \brief Clear a CHILD_SPECS structure, making all allocated slots usable. Does not release associated memory.
+ \param csp pointer to the CHILD_SPECS structure.
+ \returns NULL.
+*/
+void csp_clear(CHILD_SPECS *csp){
+ csp->used = 0;
+}
+
+
+/**
+ \brief Initialize an CX_INFO structure. Holds complexes (multiple text objects in known positions and order.)
+ \returns a pointer to the CX_INFO structure created, or NULL on error.
+*/
+CX_INFO *cxinfo_init(void){
+ CX_INFO *cxi = NULL;
+ cxi = (CX_INFO *)calloc(1,sizeof(CX_INFO));
+ if(cxi){
+ if(cxinfo_make_insertable(cxi)){
+ free(cxi);
+ cxi=NULL;
+ }
+ }
+ return(cxi);
+}
+
+/**
+ \brief Make a CX_INFO structure insertable. Adds storage as needed.
+ \returns 0 on success, !0 on error.
+ \param cxi pointer to the CX_INFO structure
+*/
+int cxinfo_make_insertable(CX_INFO *cxi){
+ int status=0;
+ if(cxi->used >= cxi->space){
+ cxi->space += ALLOCINFO_CHUNK;
+ if((cxi->cx = (CX_SPECS *) realloc(cxi->cx, cxi->space * sizeof(CX_SPECS) ))){
+ memset(&cxi->cx[cxi->used],0,(cxi->space - cxi->used)*sizeof(CX_SPECS));
+ }
+ else {
+ status=1;
+ }
+ }
+ return(status);
+}
+
+/**
+ \brief Insert a complex into the CX_INFO structure. (Insert may be either TR_TEXT or TR_LINE.)
+ \returns 0 on success, !0 on error.
+ \param cxi pointer to the CX_INFO structure (complexes).
+ \param src index of the complex to insert.
+ \param src_rt_tidx index of the bounding rectangle
+ \param type TR_TEXT (index is for tpi->chunks[]) or TR_LINE (index is for cxi->kids[])
+*/
+int cxinfo_insert(CX_INFO *cxi, int src, int src_rt_tidx, enum tr_classes type){
+ int status=1;
+ if(!cxi)return(2);
+ if(!(status=cxinfo_make_insertable(cxi))){
+ cxi->cx[cxi->used].rt_cidx = src_rt_tidx;
+ cxi->cx[cxi->used].type = type;
+ status = csp_insert(&(cxi->cx[cxi->used].kids), src);
+ cxi->used++;
+ }
+ return(status);
+}
+
+/**
+ \brief Append a complex to the CX_INFO structure and give it a type.
+ \param cxi pointer to the CX_INFO structure (complexes).
+ \param src index of the complex to append.
+ \param type TR_LINE (src is an index for tpi->chunks[]) or TR_PARA (src is an index for cxi->kids[]).
+ \returns 0 on success, !0 on error.
+*/
+int cxinfo_append(CX_INFO *cxi, int src, enum tr_classes type){
+ int status=1;
+ if(!cxi)return(2);
+ if(!(status=cxinfo_make_insertable(cxi))){
+ cxi->cx[cxi->used-1].type = type;
+ status = csp_insert(&(cxi->cx[cxi->used-1].kids), src);
+ }
+ return(status);
+}
+
+
+/**
+ \brief Merge a complex dst with N members (N>=1) by adding a second complex src, and change the type.
+ \param cxi pointer to the CX_INFO structure (complexes).
+ \param dst index of the complex to expand.
+ \param src index of the donor complex (which is not modified).
+ \param type TR_LINE (src is an index for tpi->chunks[]) or TR_PARA (src is an index for cxi->kids[]).
+ \returns 0 on success, !0 on error.
+*/
+int cxinfo_merge(CX_INFO *cxi, int dst, int src, enum tr_classes type){
+ int status =1;
+ if(!cxi)return(2);
+ if(!cxi->used)return(3);
+ if(dst < 0 || dst >= (int) cxi->used)return(4);
+ if(src < 0)return(5);
+ cxi->cx[dst].type = type;
+ status = csp_merge(&(cxi->cx[dst].kids), &(cxi->cx[src].kids));
+ return(status);
+}
+
+/**
+ \brief Trim the last complex from thelist of complexes.
+ \param cxi pointer to the CX_INFO structure (complexes).
+ \returns 0 on success, !0 on error.
+*/
+int cxinfo_trim(CX_INFO *cxi){
+ int status = 0;
+ int last ;
+ if(!cxi)return(1);
+ if(!cxi->used)return(2);
+ last = cxi->used - 1;
+ csp_clear(&(cxi->cx[last].kids));
+ cxi->used--;
+ return(status);
+}
+
+
+/**
+ \brief Dump the contents of the TR_INFO structure to stdout. For debugging purposes,not used in production code.
+ \param tri pointer to the TR_INFO structure.
+*/
+void cxinfo_dump(const TR_INFO *tri){
+ uint32_t i,j,k;
+ CX_INFO *cxi = tri->cxi;
+ BR_INFO *bri = tri->bri;
+ TP_INFO *tpi = tri->tpi;
+ BRECT_SPECS *bsp;
+ CX_SPECS *csp;
+ if(cxi){
+ printf("cxi space: %d\n",cxi->space);
+ printf("cxi used: %d\n",cxi->used);
+ printf("cxi phase1: %d\n",cxi->phase1);
+ printf("cxi lines: %d\n",cxi->lines);
+ printf("cxi paras: %d\n",cxi->paras);
+ printf("cxi xy: %lf , %lf\n",tri->x,tri->y);
+
+ for(i=0;i<cxi->used;i++){
+ csp = &(cxi->cx[i]);
+ bsp = &(bri->rects[csp->rt_cidx]);
+ printf("cxi cx[%d] type:%d rt_tidx:%d kids_used:%d kids_space:%d\n",i, csp->type, csp->rt_cidx, csp->kids.used, csp->kids.space);
+ printf("cxi cx[%d] br (LL,UR) (%lf,%lf),(%lf,%lf)\n",i,bsp->xll,bsp->yll,bsp->xur,bsp->yur);
+ for(j=0;j<csp->kids.used;j++){
+ k = csp->kids.members[j];
+ bsp = &(bri->rects[k]);
+ if(csp->type == TR_TEXT || csp->type == TR_LINE){
+ printf("cxi cx[%d] member:%3d tp_idx:%3d ldir:%d rt_tidx:%3d br (LL,UR) (%8.3lf,%8.3lf),(%8.3lf,%8.3lf) xy (%8.3lf,%8.3lf) kern (%8.3lf,%8.3lf) text:<%s> decor:%5.5x\n",
+ i, j, k, tpi->chunks[k].ldir, tpi->chunks[k].rt_tidx,
+ bsp->xll,bsp->yll,bsp->xur,bsp->yur,
+ tpi->chunks[k].x, tpi->chunks[k].y,
+ tpi->chunks[k].xkern, tpi->chunks[k].ykern,
+ tpi->chunks[k].string, tpi->chunks[k].decoration );
+ }
+ else { /* TR_PARA_* */
+ printf("cxi cx[%d] member:%d cx_idx:%d\n",i, j, k);
+ }
+ }
+ }
+ }
+ return;
+}
+
+/**
+ \brief Release a CX_INFO structure. Release all associated memory.
+ use like: cxi = cxiinfo_release(cxi);
+ \param cxi pointer to the CX_INFO structure.
+ \returns NULL.
+*/
+CX_INFO *cxinfo_release(CX_INFO *cxi){
+ uint32_t i;
+ if(cxi){
+ for(i=0;i<cxi->used;i++){ csp_release(&cxi->cx[i].kids); }
+ free(cxi->cx);
+ free(cxi); /* release the overall cxinfo structure */
+ }
+ return NULL;
+}
+
+
+/**
+ \brief Initialize an TP_INFO structure. Holds text objects from which complexes are built.
+ \returns a pointer to the TP_INFO structure created, or NULL on error.
+*/
+TP_INFO *tpinfo_init(void){
+ TP_INFO *tpi = NULL;
+ tpi = (TP_INFO *)calloc(1,sizeof(TP_INFO));
+ if(tpi){
+ if(tpinfo_make_insertable(tpi)){
+ free(tpi);
+ tpi=NULL;
+ }
+ }
+ return(tpi);
+}
+
+
+/**
+ \brief Make a TP_INFO structure insertable. Adds storage as needed.
+ \returns 0 on success, !0 on error.
+ \param tpi pointer to the TP_INFO structure
+*/
+int tpinfo_make_insertable(TP_INFO *tpi){
+ int status=0;
+ if(tpi->used >= tpi->space){
+ tpi->space += ALLOCINFO_CHUNK;
+ if((tpi->chunks = (TCHUNK_SPECS *) realloc(tpi->chunks, tpi->space * sizeof(TCHUNK_SPECS) ))){
+ memset(&tpi->chunks[tpi->used],0,(tpi->space - tpi->used)*sizeof(TCHUNK_SPECS));
+ }
+ else {
+ status=1;
+ }
+ }
+ return(status);
+}
+
+/**
+ \brief Insert a copy of a TCHUNK_SPECS structure into a TP_INFO structure. (Insert a text object.)
+ \returns 0 on success, !0 on error.
+ \param tpi pointer to the TP_INFO structure
+ \param tsp pointer to the TCHUNK_SPECS structure
+*/
+int tpinfo_insert(TP_INFO *tpi, const TCHUNK_SPECS *tsp){
+ int status=1;
+ TCHUNK_SPECS *ltsp;
+ if(!tpi)return(2);
+ if(!tsp)return(3);
+ if(!(status = tpinfo_make_insertable(tpi))){
+ ltsp = &(tpi->chunks[tpi->used]);
+ memcpy(ltsp,tsp,sizeof(TCHUNK_SPECS));
+ if(tsp->co)ltsp->condensed = 75; /* Narrow was set in the font name */
+ ltsp->xkern = ltsp->ykern = 0.0; /* kerning will be calculated from the derived layout */
+ tpi->used++;
+ }
+ return(status);
+}
+
+/**
+ \brief Release a TP_INFO structure. Release all associated memory.
+ use like: tpi = tpinfo_release(tpi);
+ \returns NULL.
+ \param tpi pointer to the TP_INFO structure.
+*/
+TP_INFO *tpinfo_release(TP_INFO *tpi){
+ uint32_t i;
+ if(tpi){
+ for(i=0;i<tpi->used;i++){
+ free(tpi->chunks[i].string); }
+ free(tpi->chunks); /* release the array */
+ free(tpi); /* release the overall tpinfo structure */
+ }
+ return NULL;
+}
+
+/**
+ \brief Initialize an BR_INFO structure. Holds bounding rectangles, for both text objects and complexes.
+ \returns a pointer to the BR_INFO structure created, or NULL on error.
+*/
+BR_INFO *brinfo_init(void){
+ BR_INFO *bri = NULL;
+ bri = (BR_INFO *)calloc(1,sizeof(BR_INFO));
+ if(bri){
+ if(brinfo_make_insertable(bri)){
+ free(bri);
+ bri=NULL;
+ }
+ }
+ return(bri);
+}
+
+/**
+ \brief Make a BR_INFO structure insertable. Adds storage as needed.
+ \returns 0 on success, !0 on error.
+ \param bri pointer to the BR_INFO structure
+*/
+int brinfo_make_insertable(BR_INFO *bri){
+ int status=0;
+ if(!bri)return(2);
+ if(bri->used >= bri->space){
+ bri->space += ALLOCINFO_CHUNK;
+ if(!(bri->rects = (BRECT_SPECS *) realloc(bri->rects, bri->space * sizeof(BRECT_SPECS) ))){ status = 1; }
+ }
+ return(status);
+}
+
+/**
+ \brief Insert a copy of a BRECT_SPEC structure into a BR_INFO structure. (Insert a bounding rectangle.)
+ \returns 0 on success, !0 on error.
+ \param bri pointer to the BR_INFO structure
+ \param element pointer to the BRECT_SPECS structure
+*/
+int brinfo_insert(BR_INFO *bri, const BRECT_SPECS *element){
+ int status=1;
+ if(!bri)return(2);
+ if(!(status=brinfo_make_insertable(bri))){
+ memcpy(&(bri->rects[bri->used]),element,sizeof(BRECT_SPECS));
+ bri->used++;
+ }
+ return(status);
+}
+
+/**
+ \brief Merge BRECT_SPEC element src into/with BRECT_SPEC element dst. src is unchanged. (Merge two bounding rectangles.)
+ \returns 0 on success, !0 on error.
+ \param bri pointer to the BR_INFO structure
+ \param dst index of the destination bounding rectangle.
+ \param src index of the source bounding rectangle.
+*/
+int brinfo_merge(BR_INFO *bri, int dst, int src){
+ if(!bri)return(1);
+ if(!bri->used)return(2);
+ if(dst<0 || dst >= (int) bri->used)return(3);
+ if(src<0 || src >= (int) bri->used)return(4);
+ bri->rects[dst].xll = TEREMIN(bri->rects[dst].xll, bri->rects[src].xll);
+ bri->rects[dst].yll = TEREMAX(bri->rects[dst].yll, bri->rects[src].yll); /* MAX because Y is positive DOWN */
+ bri->rects[dst].xur = TEREMAX(bri->rects[dst].xur, bri->rects[src].xur);
+ bri->rects[dst].yur = TEREMIN(bri->rects[dst].yur, bri->rects[src].yur); /* MIN because Y is positive DOWN */
+/*
+printf("bri_Merge into rect:%d (LL,UR) dst:(%lf,%lf),(%lf,%lf) src:(%lf,%lf),(%lf,%lf)\n",dst,
+(bri->rects[dst].xll),
+(bri->rects[dst].yll),
+(bri->rects[dst].xur),
+(bri->rects[dst].yur),
+(bri->rects[src].xll),
+(bri->rects[src].yll),
+(bri->rects[src].xur),
+(bri->rects[src].yur));
+*/
+ return(0);
+}
+
+/**
+ \brief Check for an allowable overlap of two bounding rectangles.
+ Allowable overlap is any area overlap of src and dst bounding rectangles, after
+ they have been expanded (padded) by allowed edge expansions. (For instance, if
+ missing spaces must be accounted for.)
+ The method works backwards: look for all reasons they might not overlap,
+ if none are found, then the rectangles do overlap.
+ An overlap here does not count just a line or a point - area must be involved.
+ \returns 0 on success (overlap detected), 1 on no overlap, anything else is an error.
+ \param bri pointer to the BR_INFO structure
+ \param dst index of the destination bounding rectangle.
+ \param src index of the source bounding rectangle.
+ \param rp_dst Pointer to edge padding values for dst.
+ \param rp_src Pointer to edge padding values for src.
+*/
+int brinfo_overlap(const BR_INFO *bri, int dst, int src, RT_PAD *rp_dst, RT_PAD *rp_src){
+ int status;
+ BRECT_SPECS *br_dst;
+ BRECT_SPECS *br_src;
+ if(!bri || !rp_dst || !rp_src)return(2);
+ if(!bri->used)return(3);
+ if(dst<0 || dst>= (int) bri->used)return(4);
+ if(src<0 || src>= (int) bri->used)return(5);
+ br_dst=&bri->rects[dst];
+ br_src=&bri->rects[src];
+ if( /* Test all conditions that exclude overlap, if any are true, then no overlap */
+ ((br_dst->xur + rp_dst->right) < (br_src->xll - rp_src->left) ) || /* dst fully to the left */
+ ((br_dst->xll - rp_dst->left) > (br_src->xur + rp_src->right) ) || /* dst fully to the right */
+ ((br_dst->yur - rp_dst->up) > (br_src->yll + rp_src->down) ) || /* dst fully below (Y is positive DOWN) */
+ ((br_dst->yll + rp_dst->down) < (br_src->yur - rp_src->up) ) /* dst fully above (Y is positive DOWN) */
+ ){
+ status = 1;
+ }
+ else {
+ /* overlap not excluded, so it must occur.
+ Only accept overlaps that are mostly at one end or the other, not mostly top or bottom.
+ If the following condition is true then there is no more than a tiny bit of horizontal overlap of src
+ within dist, which suggests that the two pieces of text may be considered part of one line.
+ (For a vertical alphabet the same method could be used for up/down.) */
+ if(
+ (br_src->xll >= br_dst->xur - rp_dst->right) || /* src overlaps just a little on the right (L->R language) */
+ (br_src->xur <= br_dst->xll + rp_dst->left) /* src overlaps just a little on the left (R->L language) */
+ ){
+ status = 0;
+ }
+ else { /* Too much overlap, reject the overlap */
+ status = 1;
+ }
+ }
+/*
+printf("Overlap status:%d\nOverlap trects (LL,UR) dst:(%lf,%lf),(%lf,%lf) src:(%lf,%lf),(%lf,%lf)\n",
+status,
+(br_dst->xll - rp_dst->left ),
+(br_dst->yll - rp_dst->down ),
+(br_dst->xur + rp_dst->right),
+(br_dst->yur + rp_dst->up ),
+(br_src->xll - rp_src->left ),
+(br_src->yll - rp_src->down ),
+(br_src->xur + rp_src->right),
+(br_src->yur + rp_src->up ));
+printf("Overlap brects (LL,UR) dst:(%lf,%lf),(%lf,%lf) src:(%lf,%lf),(%lf,%lf)\n",
+(br_dst->xll),
+(br_dst->yll),
+(br_dst->xur),
+(br_dst->yur),
+(br_src->xll),
+(br_src->yll),
+(br_src->xur),
+(br_src->yur));
+printf("Overlap rprect (LL,UR) dst:(%lf,%lf),(%lf,%lf) src:(%lf,%lf),(%lf,%lf)\n",
+(rp_dst->left),
+(rp_dst->down),
+(rp_dst->right),
+(rp_dst->up),
+(rp_src->left),
+(rp_src->down),
+(rp_src->right),
+(rp_src->up));
+*/
+ return(status);
+}
+
+/**
+ \brief Check for various sorts of invalid text elements upstream (language dir changes, draw order backwards from language direction)
+ \returns 0 on success (not upstream), 1 if upstream, anything else is an error.
+ \param bri pointer to the BR_INFO structure
+ \param dst index of the destination bounding rectangle.
+ \param src index of the source bounding rectangle.
+ \param ddir direction of dst
+ \param sdir direction of src
+*/
+
+int brinfo_upstream(BR_INFO *bri, int dst, int src, int ddir, int sdir){
+ int status=0;
+ BRECT_SPECS *br_dst;
+ BRECT_SPECS *br_src;
+ if(!bri)return(2);
+ if(!bri->used)return(3);
+ if(dst<0 || dst>= (int) bri->used)return(4);
+ if(src<0 || src>= (int) bri->used)return(5);
+ br_dst=&bri->rects[dst];
+ br_src=&bri->rects[src];
+ if( ddir == LDIR_RL && sdir == LDIR_LR){
+ if(br_dst->xur <= (br_src->xll + br_src->xur)/2.0){ status = 1; }
+ }
+ else if( ddir == LDIR_LR && sdir == LDIR_RL){
+ if((br_src->xll + br_src->xur)/2.0 <= br_dst->xll ){ status = 1; }
+ }
+ else if( ddir == LDIR_RL && sdir == LDIR_RL){
+ if(br_dst->xur <= (br_src->xll + br_src->xur)/2.0){ status = 1; }
+ }
+ else if( ddir == LDIR_LR && sdir == LDIR_LR){
+ if((br_src->xll + br_src->xur)/2.0 <= br_dst->xll ){ status = 1; }
+ }
+ return(status);
+}
+
+
+/**
+ \brief Try to deduce justification of a paragraph from the bounding rectangles for two successive lines.
+ \returns one of TR_PARA_ UJ (unknown justified), LJ, CJ, or RJ (left, center, or right justified).
+ \param bri pointer to the BR_INFO structure
+ \param dst index of the destination bounding rectangle.
+ \param src index of the source bounding rectangle.
+ \param slop allowed error in edge alignment.
+ \param type Preexisting justification for dst, if any. Justification of dst and src must match this or
+ TR_PARA_UJ is returned even if dst and src have some (other) alignment.
+*/
+enum tr_classes brinfo_pp_alignment(const BR_INFO *bri, int dst, int src, double slop, enum tr_classes type){
+ enum tr_classes newtype;
+ BRECT_SPECS *br_dst = & bri->rects[dst];
+ BRECT_SPECS *br_src = & bri->rects[src];
+ if((br_dst->yur >= br_src->yur) || (br_dst->yll >= br_src->yll)){ /* Y is positive DOWN */
+ /* lines in the wrong vertical order, no paragraph possible (Y is positive down) */
+ newtype = TR_PARA_UJ;
+ }
+ else if(fabs(br_dst->xll - br_src->xll) < slop){
+ /* LJ (might also be CJ but LJ takes precedence) */
+ newtype = TR_PARA_LJ;
+ }
+ else if(fabs(br_dst->xur - br_src->xur) < slop){
+ /* RJ */
+ newtype = TR_PARA_RJ;
+ }
+ else if(fabs( (br_dst->xur + br_dst->xll)/2.0 - (br_src->xur + br_src->xll)/2.0 ) < slop){
+ /* CJ */
+ newtype = TR_PARA_CJ;
+ }
+ else {
+ /* not aligned */
+ newtype = TR_PARA_UJ;
+ }
+ /* within a paragraph type can change from unknown to known, but not from one known type to another*/
+ if((type != TR_PARA_UJ) && (newtype != type)){
+ newtype = TR_PARA_UJ;
+ }
+/*
+printf("pp_align newtype:%d brects (LL,UR) dst:(%lf,%lf),(%lf,%lf) src:(%lf,%lf),(%lf,%lf)\n",
+newtype,
+(br_dst->xll),
+(br_dst->yll),
+(br_dst->xur),
+(br_dst->yur),
+(br_src->xll),
+(br_src->yll),
+(br_src->xur),
+(br_src->yur));
+*/
+ return(newtype);
+}
+
+/**
+ \brief Release a BR_INFO structure. Release all associated memory.
+ use like: bri = brinfo_release(bri);
+ \param bri pointer to the BR_INFO structure.
+ \returns NULL.
+*/
+BR_INFO *brinfo_release(BR_INFO *bri){
+ if(bri){
+ free(bri->rects);
+ free(bri); /* release the overall brinfo structure */
+ }
+ return NULL;
+}
+
+
+
+/**
+ \brief Initialize an TR_INFO structure. Holds all data for text reassembly.
+ \returns a pointer to the TR_INFO structure created, or NULL on error.
+*/
+TR_INFO *trinfo_init(TR_INFO *tri){
+ if(tri)return(tri); /* tri is already set, double initialization is not allowed */
+ if(!(tri = (TR_INFO *)calloc(1,sizeof(TR_INFO))) ||
+ !(tri->fti = ftinfo_init()) ||
+ !(tri->tpi = tpinfo_init()) ||
+ !(tri->bri = brinfo_init()) ||
+ !(tri->cxi = cxinfo_init())
+ ){ tri = trinfo_release(tri); }
+ tri->out = NULL; /* This will allocate as needed, it might not ever be needed. */
+ tri->qe = 0.0;
+ tri->esc = 0.0;
+ tri->x = DBL_MAX;
+ tri->y = DBL_MAX;
+ tri->dirty = 0;
+ tri->use_kern = 1;
+ tri->load_flags = FT_LOAD_NO_SCALE;
+ tri->kern_mode = FT_KERNING_UNSCALED;
+ tri->outspace = 0;
+ tri->outused = 0;
+ tri->usebk = BKCLR_NONE;
+ memset(&(tri->bkcolor),0,sizeof(TRCOLORREF));
+ return(tri);
+}
+
+/**
+ \brief Release a TR_INFO structure completely.
+ Release all associated memory, including FontConfig.
+ See also trinfo_clear() and trinfo_release_except_FC().
+ use like: tri = trinfo_release(tri);
+ \param tri pointer to the TR_INFO structure.
+ \returns NULL.
+*/
+TR_INFO *trinfo_release(TR_INFO *tri){
+ if(tri){
+ if(tri->bri)tri->bri=brinfo_release(tri->bri);
+ if(tri->tpi)tri->tpi=tpinfo_release(tri->tpi);
+ if(tri->fti)tri->fti=ftinfo_release(tri->fti);
+ if(tri->cxi)tri->cxi=cxinfo_release(tri->cxi);
+ if(tri->out){ free(tri->out); tri->out=NULL; };
+ free(tri);
+ }
+ return(NULL);
+}
+
+/**
+ \brief Release a TR_INFO structure mostly.
+ Release all associated memory EXCEPT Fontconfig.
+ Fontconfig may still be needed elsewhere in a program and there is no way to figure that out here.
+ See also trinfo_clear() and trinfo_release().
+ use like: tri = trinfo_release_except_FC(tri);
+ \param tri pointer to the TR_INFO structure.
+ \returns NULL.
+*/
+TR_INFO *trinfo_release_except_FC(TR_INFO *tri){
+ if(tri){
+ if(tri->bri)tri->bri=brinfo_release(tri->bri);
+ if(tri->tpi)tri->tpi=tpinfo_release(tri->tpi);
+ if(tri->fti)tri->fti=ftinfo_clear(tri->fti);
+ if(tri->cxi)tri->cxi=cxinfo_release(tri->cxi);
+ if(tri->out){ free(tri->out); tri->out=NULL; };
+ free(tri);
+ }
+ return(NULL);
+}
+
+/**
+ \brief Clear a TR_INFO structure.
+ Releases text and rectangle information, but retains font information, both
+ Freetype information and Fontconfig information.
+ See also trinfo_release() and trinfo_release_except_FC().
+ Use like: tri = trinfo_clear(tri);
+ \param tri pointer to the TR_INFO structure.
+ \returns NULL.
+*/
+TR_INFO *trinfo_clear(TR_INFO *tri){
+ if(tri){
+
+ if(tri->bri)tri->bri=brinfo_release(tri->bri);
+ if(tri->tpi)tri->tpi=tpinfo_release(tri->tpi);
+ if(tri->cxi)tri->cxi=cxinfo_release(tri->cxi);
+ if(tri->out){
+ free(tri->out);
+ tri->out = NULL;
+ tri->outused = 0;
+ tri->outspace = 0;
+ };
+ /* Do NOT modify: qe, use_kern, usebk, load_flags, kern_mode, or bkcolor. Set the rest back to their defaults */
+ tri->esc = 0.0;
+ tri->x = DBL_MAX;
+ tri->y = DBL_MAX;
+ tri->dirty = 0;
+ if(!(tri->tpi = tpinfo_init()) || /* re-init the pieces just released */
+ !(tri->bri = brinfo_init()) ||
+ !(tri->cxi = cxinfo_init())
+ ){
+ tri = trinfo_release(tri); /* something horrible happened, clean out tri and return NULL */
+ }
+ }
+ return(tri);
+}
+
+
+/**
+ \brief Set the quantization error value for a TR_INFO structure.
+ If coordinates have passed through an integer form limits
+ in accuracy may have been imposed. For instance, if the X coordinate of a point in such a file
+ is 1000, and the conversion factor from those coordinates to points is .04, then eq is .04. This
+ just says that single coordinates are only good to within .04, and two coordinates may differ by as much
+ as .08, just due to quantization error. So if some calculation shows a difference of
+ .02 it may be interpreted as this sort of error and set to 0.0.
+ \returns 0 on success, !0 on error.
+ \param tri pointer to TR_INFO structure
+ \param qe quantization error.
+*/
+int trinfo_load_qe(TR_INFO *tri, double qe){
+ if(!tri)return(1);
+ if(qe<0.0)return(2);
+ tri->qe=qe;
+ return(0);
+}
+
+/**
+ \brief Set the background color and whether or not to use it.
+ When background color is turned on each line of text is underwritten with a rectangle
+ of the specified color. The rectangle is the merged bounding rectangle for that line.
+ \returns 0 on success but nothing changed, >0 on error, <0 on success and a value changed.
+ \param tri pointer to TR_INFO structure
+ \param usebk 0 for no background, anything else uses background color
+ \param bkcolor background color to use
+*/
+int trinfo_load_bk(TR_INFO *tri, int usebk, TRCOLORREF bkcolor){
+ int status=0;
+ if(!tri){ status = 1; }
+ else {
+ if((usebk < BKCLR_NONE) || (usebk > BKCLR_ALL)){ status = 2; }
+ else {
+ status = trinfo_check_bk(tri, usebk, bkcolor);
+ tri->usebk = usebk;
+ tri->bkcolor = bkcolor;
+ }
+ }
+ return(status);
+}
+
+/**
+ \brief Are the proposed new background and background color a change?
+ \returns 0 if they are the same, -1 if either is different
+ \param tri pointer to TR_INFO structure
+ \param usebk 0 for no background, anything else uses background color
+ \param bkcolor background color to use
+*/
+int trinfo_check_bk(TR_INFO *tri, int usebk, TRCOLORREF bkcolor){
+ int status = 0;
+ if( (tri->usebk != usebk) || memcmp(&tri->bkcolor,&bkcolor,sizeof(TRCOLORREF))){ status = -1; }
+ return(status);
+}
+
+/**
+ \brief Set Freetype parameters and kerning mode (if any) in a TRI_INFO structure.
+ \returns 0 on success, !0 on error.
+ \param tri pointer to a TR_INFO structure
+ \param use_kern 0 if kerning is to be employed, !0 otherwise.
+ \param load_flags Controls internal advance:
+ FT_LOAD_NO_SCALE, internal advance is in 1/64th of a point. (kerning values are still scaled)
+ FT_LOAD_TARGET_NORMAL internal advance is in 1/64th of a point. The scale
+ factor seems to be (Font Size in points)*(DPI)/(32.0 pnts)*(72 dpi).
+ \param kern_mode FT_KERNING_DEFAULT, FT_KERNING_UNFITTED, or FT_KERNING_UNSCALED. Set to match calling application.
+*/
+int trinfo_load_ft_opts(TR_INFO *tri, int use_kern, int load_flags, int kern_mode){
+ if(!tri)return(1);
+ tri->use_kern = use_kern;
+ tri->load_flags = load_flags;
+ tri->kern_mode = kern_mode;
+ return(0);
+}
+
+/**
+ \brief Append text to a TR_INFO struct's output buffer, expanding it if necessary.
+ \returns 0 on success, !0 on error.
+ \param tri pointer to a TR_INFO structure
+ \param src Pointer to a text string.
+*/
+int trinfo_append_out(TR_INFO *tri, const char *src){
+ size_t slen;
+ if(!src)return(-1);
+ slen = strlen(src);
+ if(tri->outused + (int) slen + 1 >= tri->outspace){
+ tri->outspace += TEREMAX(ALLOCOUT_CHUNK,slen+1);
+ if(!(tri->out = realloc(tri->out, tri->outspace )))return(-1);
+ }
+ memcpy(tri->out + tri->outused, src, slen+1); /* copy the terminator */
+ tri->outused += slen; /* do not count the terminator in the length */
+ return(0);
+}
+
+
+/**
+ \brief Load a text object into a TR_INFO struct.
+ \returns 0 on success, !0 on error. -1 means that the escapement is different from the objects already loaded.
+ \param tri pointer to a TR_INFO structure
+ \param tsp pointer to a TCHUNK_SPECS structure (text object to load)
+ \param escapement angle in degrees of the text object.
+ \param flags special processing flags:
+ TR_EMFBOT calculate Y coordinates of ALIBOT object compatible with EMF files TA_BOTTOM alignment.
+*/
+int trinfo_load_textrec(TR_INFO *tri, const TCHUNK_SPECS *tsp, double escapement, int flags){
+
+ int status;
+ double x,y,xe;
+ double asc,dsc; /* these are the ascender/descender for the actual text */
+ int ymin,ymax;
+ double fasc,fdsc; /* these are the ascender/descender for the font as a whole (text independent) */
+ TP_INFO *tpi;
+ FT_INFO *fti;
+ BR_INFO *bri;
+ int current,idx,taln;
+ uint32_t prev;
+ uint32_t *text32,*tptr;
+ FNT_SPECS *fsp;
+ BRECT_SPECS bsp;
+
+ /* check incoming parameters */
+ if(!tri)return(1);
+ if(!tsp)return(2);
+ if(!tsp->string)return(3);
+ fti = tri->fti;
+ tpi = tri->tpi;
+ bri = tri->bri;
+ idx = tsp->fi_idx;
+ taln = tsp->taln;
+ if(!fti->used)return(4);
+ if(idx <0 || idx >= (int) fti->used)return(5);
+ fsp = &(fti->fonts[idx]);
+
+ if(!tri->dirty){
+ tri->x = tsp->x;
+ tri->y = tsp->y;
+ tri->esc = escapement;
+ tri->dirty = 1;
+ }
+ else {
+ if(tri->esc != escapement)return(-1);
+ }
+
+
+ tpinfo_insert(tpi,tsp);
+ current=tpi->used-1;
+ ymin = 64000;
+ ymax = -64000;
+
+ /* The geometry model has origin Y at the top of screen, positive Y is down, maximum positive
+ Y is at the bottom of the screen. That makes "top" (by positive Y) actually the bottom
+ (as viewed on the screen.) */
+
+ escapement *= 2.0 * M_PI / 360.0; /* degrees to radians */
+ x = tpi->chunks[current].x - tri->x; /* convert to internal orientation */
+ y = tpi->chunks[current].y - tri->y;
+ tpi->chunks[current].x = x * cos(escapement) - y * sin(escapement); /* coordinate transformation */
+ tpi->chunks[current].y = x * sin(escapement) + y * cos(escapement);
+
+/* Careful! face bbox does NOT scale with FT_Set_Char_Size
+printf("Face idx:%d bbox: xMax/Min:%ld,%ld yMax/Min:%ld,%ld UpEM:%d asc/des:%d,%d height:%d size:%lf\n",
+ idx,
+ fsp->face->bbox.xMax,fsp->face->bbox.xMin,
+ fsp->face->bbox.yMax,fsp->face->bbox.yMin,
+ fsp->face->units_per_EM,fsp->face->ascender,fsp->face->descender,fsp->face->height,fsp->fsize);
+*/
+
+ text32 = U_Utf8ToUtf32le((char *) tsp->string,0,NULL);
+ if(!text32){ // LATIN1 encoded >128 are generally not valid UTF, so the first will fail
+ text32 = U_Latin1ToUtf32le((char *) tsp->string,0,NULL);
+ if(!text32)return(5);
+ }
+ /* baseline advance is independent of character orientation */
+ for(xe=0.0, prev=0, tptr=text32; *tptr; tptr++){
+ status = TR_getadvance(fti, fsp, *tptr, (tri->use_kern ? prev: 0), tri->load_flags, tri->kern_mode, &ymin, &ymax);
+ if(status>=0){
+ xe += ((double) status)/64.0;
+ }
+ else { return(6); }
+ prev=*tptr;
+ }
+
+ /* Some glyphs in fonts have no vertical extent, for instance, Hebrew glyphs in Century Schoolbook L.
+ Use the 3/4 of the font size as a (very bad) approximation for the actual values. */
+ if(ymin==0 && ymax==0){
+ ymax = 0.75 * fsp->fsize * 64.0;
+ }
+
+ asc = ((double) (ymax))/64.0;
+ dsc = ((double) (ymin))/64.0; /* This is negative */
+/* This did not work very well because the ascender/descender went well beyond the actual characters, causing
+ overlaps on lines that did not actually overlap (vertically).
+ asc = ((double) (fsp->face->ascender) )/64.0;
+ dsc = ((double) (fsp->face->descender))/64.0;
+*/
+
+ free(text32);
+
+ /* find the font ascender descender (general one, not specific for current text) */
+ fasc = ((double) (fsp->face->ascender) )/64.0;
+ fdsc = ((double) (fsp->face->descender))/64.0;
+
+ if(tri->load_flags & FT_LOAD_NO_SCALE) xe *= tsp->fs/32.0;
+
+ /* now place the rectangle using ALN information */
+ if( taln & ALIHORI & ALILEFT ){
+ bsp.xll = tpi->chunks[current].x;
+ bsp.xur = tpi->chunks[current].x + xe;
+ }
+ else if( taln & ALIHORI & ALICENTER){
+ bsp.xll = tpi->chunks[current].x - xe/2.0;
+ bsp.xur = tpi->chunks[current].x + xe/2.0;
+ }
+ else{ /* taln & ALIHORI & ALIRIGHT */
+ bsp.xll = tpi->chunks[current].x - xe;
+ bsp.xur = tpi->chunks[current].x;
+ }
+ tpi->chunks[current].ldir = tsp->ldir;
+
+ if(tri->load_flags & FT_LOAD_NO_SCALE){
+ asc *= tsp->fs/32.0;
+ dsc *= tsp->fs/32.0;
+ fasc *= tsp->fs/32.0;
+ fdsc *= tsp->fs/32.0;
+ }
+
+
+ /* From this point forward y is on the baseline, so need to correct it in chunks. The asc/dsc are the general
+ ones for the font, else the text content will muck around with the baseline in BAD ways. */
+ if( taln & ALIVERT & ALITOP ){ tpi->chunks[current].y += fasc; }
+ else if( taln & ALIVERT & ALIBASE){ } /* no correction required */
+ else{ /* taln & ALIVERT & ALIBOT */
+ if(flags & TR_EMFBOT){ tpi->chunks[current].y -= 0.35 * tsp->fs; } /* compatible with EMF implementations */
+ else { tpi->chunks[current].y += fdsc; }
+ }
+ tpi->chunks[current].boff = -dsc;
+
+ /* since y is always on the baseline, the lower left and upper right are easy. These use asc/dsc for the particular text,
+ so that the bounding box will fit it tightly. */
+ bsp.yll = tpi->chunks[current].y - dsc;
+ bsp.yur = tpi->chunks[current].y - asc;
+ brinfo_insert(bri,&bsp);
+ tpi->chunks[current].rt_tidx = bri->used - 1; /* index of rectangle that contains it */
+
+ return(0);
+}
+
+/**
+ \brief Fontweight conversion. Fontconfig units to SVG units.
+ Anything not recognized becomes "normal" == 400.
+ There is no interpolation because a value that mapped to 775, for instance, most
+ likely would not display properly because it is intermediate between 700 and 800, and
+ only those need be supported in SVG viewers.
+ \returns SVG font weight
+ \param weight Fontconfig font weight.
+*/
+int TR_weight_FC_to_SVG(int weight){
+ int ret=400;
+ if( weight == 0){ ret = 100; }
+ else if(weight == 40){ ret = 200; }
+ else if(weight == 50){ ret = 300; }
+ else if(weight == 80){ ret = 400; }
+ else if(weight == 100){ ret = 500; }
+ else if(weight == 180){ ret = 600; }
+ else if(weight == 200){ ret = 700; }
+ else if(weight == 205){ ret = 800; }
+ else if(weight == 210){ ret = 900; }
+ else { ret = 400; }
+ return(ret);
+}
+
+/**
+ \brief Set the padding that will be added to bounding rectangles before checking for overlaps in brinfo_overlap().
+ \returns void
+ \param rt_pad pointer to an RT_PAD structure.
+ \param up padding for the top of a bounding rectangle.
+ \param down padding for the bottom of a bounding rectangle.
+ \param left padding for the left of a bounding rectangle.
+ \param right padding for the right of a bounding rectangle.
+*/
+void TR_rt_pad_set(RT_PAD *rt_pad, double up, double down, double left, double right){
+ rt_pad->up = up;
+ rt_pad->down = down;
+ rt_pad->left = left;
+ rt_pad->right = right;
+}
+
+/**
+ \brief Convert from analyzed complexes to SVG format.
+ \returns void
+ \param tri pointer to a TR_INFO struct which will be analyzed. Result is stored in its "out" buffer.
+*/
+void TR_layout_2_svg(TR_INFO *tri){
+ double x = tri->x;
+ double y = tri->y;
+ double dx,dy;
+ double esc;
+ double recenter; /* horizontal offset to set things up correctly for CJ and RJ text, is 0 for LJ*/
+ double lineheight=1.25;
+ int cutat;
+ FT_INFO *fti=tri->fti; /* Font info storage */
+ TP_INFO *tpi=tri->tpi; /* Text Info/Position Info storage */
+ BR_INFO *bri=tri->bri; /* bounding Rectangle Info storage */
+ CX_INFO *cxi=tri->cxi; /* Complexes deduced for this text */
+ TCHUNK_SPECS *tsp; /* current text object */
+ CX_SPECS *csp;
+ CX_SPECS *cline_sp;
+ unsigned int i,j,k,jdx,kdx;
+ int ldir;
+ char obuf[1024]; /* big enough for style and so forth */
+ char cbuf[16]; /* big enough for one hex color */
+
+ char stransform[128];
+ double newx,newy,tmpx;
+ uint32_t utmp;
+
+/*
+#define DBG_TR_PARA 0
+#define DBG_TR_INPUT 1
+*/
+ /* The debug section below is difficult to see if usebk is anything other than BKCLR_NONE */
+#if DBG_TR_PARA || DBG_TR_INPUT /* enable debugging code, writes extra information into SVG */
+ /* put rectangles down for each text string - debugging!!! This will not work properly for any Narrow fonts */
+ esc = tri->esc;
+ esc *= 2.0 * M_PI / 360.0; /* degrees to radians and change direction of rotation */
+ sprintf(stransform,"transform=\"matrix(%lf,%lf,%lf,%lf,%lf,%lf)\"\n",cos(esc),-sin(esc),sin(esc),cos(esc), 1.25*x,1.25*y);
+ for(i=cxi->phase1; i<cxi->used;i++){ /* over all complex members from phase2 == TR_PARA_* complexes */
+ csp = &(cxi->cx[i]);
+ for(j=0; j<csp->kids.used; j++){ /* over all members of these complexes, which are phase1 complexes */
+ jdx = csp->kids.members[j]; /* index of phase1 complex (all are TR_TEXT or TR_LINE) */
+ for(k=0; k<cxi->cx[jdx].kids.used; k++){ /* over all members of the phase1 complex */
+ kdx = cxi->cx[jdx].kids.members[k]; /* index for text objects in tpi */
+ tsp = &tpi->chunks[kdx];
+ ldir = tsp->ldir;
+ if(!j && !k){
+#if DBG_TR_PARA
+ TRPRINT(tri, "<rect\n");
+ TRPRINT(tri, "style=\"color:#0000FF;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:none;stroke:#000000;stroke-width:0.8;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;clip-rule:nonzero\"\n");
+ sprintf(obuf,"width=\"%lf\"\n", 1.25*(bri->rects[csp->rt_cidx].xur - bri->rects[csp->rt_cidx].xll));
+ TRPRINT(tri, obuf);
+ sprintf(obuf,"height=\"%lf\"\n",1.25*(bri->rects[csp->rt_cidx].yll - bri->rects[csp->rt_cidx].yur));
+ TRPRINT(tri, obuf);
+ sprintf(obuf,"x=\"%lf\" y=\"%lf\"\n",1.25*(bri->rects[csp->rt_cidx].xll),1.25*(bri->rects[csp->rt_cidx].yur));
+ TRPRINT(tri, obuf);
+ TRPRINT(tri, stransform);
+ TRPRINT(tri, "/>\n");
+#endif /* DBG_TR_PARA */
+ }
+#if DBG_TR_INPUT /* debugging code, this section writes the original text objects */
+ newx = 1.25*(ldir == LDIR_RL ? bri->rects[tsp->rt_tidx].xur : bri->rects[tsp->rt_tidx].xll);
+ newy = 1.25*(bri->rects[tsp->rt_tidx].yur);
+ TRPRINT(tri, "<rect\n");
+ TRPRINT(tri, "style=\"color:#000000;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:none;stroke:#00FF00;stroke-width:0.3;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;clip-rule:nonzero\"\n");
+ sprintf(obuf,"width=\"%lf\"\n", 1.25*(bri->rects[tsp->rt_tidx].xur - bri->rects[tsp->rt_tidx].xll));
+ TRPRINT(tri, obuf);
+ sprintf(obuf,"height=\"%lf\"\n",1.25*(bri->rects[tsp->rt_tidx].yll - bri->rects[tsp->rt_tidx].yur));
+ TRPRINT(tri, obuf);
+ sprintf(obuf,"x=\"%lf\" y=\"%lf\"\n",1.25*(bri->rects[tsp->rt_tidx].xll),newy);
+ TRPRINT(tri, obuf);
+ TRPRINT(tri, stransform);
+ TRPRINT(tri, "/>\n");
+
+ newy = 1.25*(bri->rects[tsp->rt_tidx].yll - tsp->boff);
+ sprintf(obuf,"<text x=\"%lf\" y=\"%lf\"\n",newx, newy );
+ TRPRINT(tri, obuf);
+ sprintf(obuf,"xml:space=\"preserve\"\n");
+ TRPRINT(tri, obuf);
+ TRPRINT(tri, stransform);
+ TRPRINT(tri, "style=\"fill:#FF0000;");
+ sprintf(obuf,"font-size:%lfpx;",tsp->fs*1.25); /*IMPORTANT, if the FS is given in pt it looks like crap in browsers. As if px != 1.25 pt, maybe 96 dpi not 90?*/
+ TRPRINT(tri, obuf);
+ sprintf(obuf,"font-style:%s;",(tsp->italics ? "italic" : "normal"));
+ TRPRINT(tri, obuf);
+ TRPRINT(tri, "font-variant:normal;");
+ sprintf(obuf,"font-weight:%d;",TR_weight_FC_to_SVG(tsp->weight));
+ TRPRINT(tri, obuf);
+ sprintf(obuf,"font-stretch:%s;",(tsp->condensed==100 ? "Normal" : "Condensed"));
+ TRPRINT(tri, obuf);
+ sprintf(obuf,"text-anchor:%s;",(tsp->ldir == LDIR_RL ? "end" : "start"));
+ TRPRINT(tri, obuf);
+ cutat=strcspn((char *)fti->fonts[tsp->fi_idx].fontspec,":");
+ sprintf(obuf,"font-family:%.*s;",cutat,fti->fonts[tsp->fi_idx].fontspec);
+ TRPRINT(tri, obuf);
+ sprintf(obuf,"\n\">%s</text>\n",&tsp->string[tsp->spaces]);
+ TRPRINT(tri, obuf);
+#endif /* DBG_TR_INPUT debugging code, original text objects */
+ }
+ }
+ }
+#endif /* DBG_TR_PARA and/or DBG_TR_INPUT */
+
+
+ if(tri->usebk){
+ esc = tri->esc;
+ esc *= 2.0 * M_PI / 360.0; /* degrees to radians and change direction of rotation */
+ sprintf(stransform,"transform=\"matrix(%lf,%lf,%lf,%lf,%lf,%lf)\"\n",cos(esc),-sin(esc),sin(esc),cos(esc), 1.25*x,1.25*y);
+
+ for(i=cxi->phase1; i<cxi->used;i++){ /* over all complex members from phase2 == TR_PARA_* complexes */
+ TRPRINT(tri, "<g>\n"); /* group backgrounds for each <text> object in the SVG */
+ csp = &(cxi->cx[i]);
+ for(j=0; j<csp->kids.used; j++){ /* over all members of these complexes, which are phase1 complexes */
+ jdx = csp->kids.members[j]; /* index of phase1 complex (all are TR_TEXT or TR_LINE) */
+ cline_sp = &(cxi->cx[jdx]);
+ if(tri->usebk == BKCLR_LINE){
+ TRPRINT(tri, "<rect\n");
+ sprintf(obuf,"style=\"color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#%2.2X%2.2X%2.2X;;stroke:none;;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;clip-rule:nonzero\"\n",tri->bkcolor.Red,tri->bkcolor.Green,tri->bkcolor.Blue);
+ TRPRINT(tri, obuf);
+ sprintf(obuf,"width=\"%lf\"\n", 1.25*(bri->rects[cline_sp->rt_cidx].xur - bri->rects[cline_sp->rt_cidx].xll));
+ TRPRINT(tri, obuf);
+ sprintf(obuf,"height=\"%lf\"\n",1.25*(bri->rects[cline_sp->rt_cidx].yll - bri->rects[cline_sp->rt_cidx].yur));
+ TRPRINT(tri, obuf);
+ sprintf(obuf,"x=\"%lf\" y=\"%lf\"\n",1.25*(bri->rects[cline_sp->rt_cidx].xll),1.25*(bri->rects[cline_sp->rt_cidx].yur));
+ TRPRINT(tri, obuf);
+ TRPRINT(tri, stransform);
+ TRPRINT(tri, "/>\n");
+ }
+
+ for(k=0; k<cxi->cx[jdx].kids.used; k++){ /* over all members of the phase1 complex */
+ kdx = cxi->cx[jdx].kids.members[k]; /* index for text objects in tpi */
+ tsp = &tpi->chunks[kdx];
+ ldir = tsp->ldir;
+ if(!j && !k){
+ if(tri->usebk == BKCLR_ALL){
+ TRPRINT(tri, "<rect\n");
+ sprintf(obuf,"style=\"color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#%2.2X%2.2X%2.2X;;stroke:none;;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;clip-rule:nonzero\"\n",tri->bkcolor.Red,tri->bkcolor.Green,tri->bkcolor.Blue);
+ TRPRINT(tri, obuf);
+ sprintf(obuf,"width=\"%lf\"\n", 1.25*(bri->rects[csp->rt_cidx].xur - bri->rects[csp->rt_cidx].xll));
+ TRPRINT(tri, obuf);
+ sprintf(obuf,"height=\"%lf\"\n",1.25*(bri->rects[csp->rt_cidx].yll - bri->rects[csp->rt_cidx].yur));
+ TRPRINT(tri, obuf);
+ sprintf(obuf,"x=\"%lf\" y=\"%lf\"\n",1.25*(bri->rects[csp->rt_cidx].xll),1.25*(bri->rects[csp->rt_cidx].yur));
+ TRPRINT(tri, obuf);
+ TRPRINT(tri, stransform);
+ TRPRINT(tri, "/>\n");
+ }
+ }
+ if(tri->usebk == BKCLR_FRAG){
+ newx = 1.25*(ldir == LDIR_RL ? bri->rects[tsp->rt_tidx].xur : bri->rects[tsp->rt_tidx].xll);
+ newy = 1.25*(bri->rects[tsp->rt_tidx].yur);
+ TRPRINT(tri, "<rect\n");
+ sprintf(obuf,"style=\"color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#%2.2X%2.2X%2.2X;;stroke:none;;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;clip-rule:nonzero\"\n",tri->bkcolor.Red,tri->bkcolor.Green,tri->bkcolor.Blue);
+ TRPRINT(tri, obuf);
+ sprintf(obuf,"width=\"%lf\"\n", 1.25*(bri->rects[tsp->rt_tidx].xur - bri->rects[tsp->rt_tidx].xll));
+ TRPRINT(tri, obuf);
+ sprintf(obuf,"height=\"%lf\"\n",1.25*(bri->rects[tsp->rt_tidx].yll - bri->rects[tsp->rt_tidx].yur));
+ TRPRINT(tri, obuf);
+ sprintf(obuf,"x=\"%lf\" y=\"%lf\"\n",newx,newy);
+ TRPRINT(tri, obuf);
+ TRPRINT(tri, stransform);
+ TRPRINT(tri, "/>\n");
+ }
+ }
+ }
+ TRPRINT(tri, "</g>\n"); /* end of grouping for backgrounds for each <text> object in the SVG */
+ }
+ }
+
+
+ /* over all complex members from phase2. Paragraphs == TR_PARA_* */
+ for(i=cxi->phase1; i<cxi->used;i++){
+ csp = &(cxi->cx[i]);
+ esc = tri->esc;
+ esc *= 2.0 * M_PI / 360.0; /* degrees to radians and change direction of rotation */
+
+ /* over all members of the present Paragraph. Each of these is a line and a phase 1 complex.
+ It may be either TR_TEXT or TR_LINE */
+ for(j=0; j<csp->kids.used; j++){
+ if(j){
+ sprintf(obuf,"</tspan>");
+ TRPRINT(tri, obuf);
+ }
+ jdx = csp->kids.members[j]; /* index of phase1 complex (all are TR_TEXT or TR_LINE) */
+ recenter = 0; /* mostly to quiet a compiler warning, should always be set below */
+
+
+ /* over all members of the present Line. These are the original text objects which were reassembled.
+ There will be one for TR_TEXT, more than one for TR_LINE */
+ for(k=0; k<cxi->cx[jdx].kids.used; k++){
+ kdx = cxi->cx[jdx].kids.members[k]; /* index for text objects in tpi, for this k */
+ tsp = &tpi->chunks[kdx]; /* text chunk for this k */
+ ldir = tsp->ldir; /* language direction for this k */
+ if(!k){ /* first iteration */
+ switch(csp->type){ /* set up the alignment, if there is one */
+ case TR_TEXT:
+ case TR_LINE:
+ /* these should never occur, this section quiets a compiler warning */
+ break;
+ case TR_PARA_UJ:
+ case TR_PARA_LJ:
+ if(ldir == LDIR_RL){ recenter = -(bri->rects[cxi->cx[jdx].rt_cidx].xur - bri->rects[cxi->cx[jdx].rt_cidx].xll); }
+ else { recenter = 0.0; }
+ break;
+ case TR_PARA_CJ:
+ if(ldir == LDIR_RL){ recenter = -(bri->rects[cxi->cx[jdx].rt_cidx].xur - bri->rects[cxi->cx[jdx].rt_cidx].xll)/2.0; }
+ else { recenter = +(bri->rects[cxi->cx[jdx].rt_cidx].xur - bri->rects[cxi->cx[jdx].rt_cidx].xll)/2.0; }
+ break;
+ case TR_PARA_RJ:
+ if(ldir == LDIR_RL){ recenter = 0.0; }
+ else { recenter = +(bri->rects[cxi->cx[jdx].rt_cidx].xur - bri->rects[cxi->cx[jdx].rt_cidx].xll); }
+ break;
+ }
+ if(!j){
+ TRPRINT(tri, "<text\n");
+ TRPRINT(tri, "xml:space=\"preserve\"\n");
+ TRPRINT(tri, "style=\"");
+ sprintf(obuf,"font-size:%lfpx;",tsp->fs*1.25); /*IMPORTANT, if the FS is given in pt it looks like crap in browsers. As if px != 1.25 pt, maybe 96 dpi not 90?*/
+ TRPRINT(tri, obuf);
+ sprintf(obuf,"font-style:%s;",(tsp->italics ? "italic" : "normal"));
+ TRPRINT(tri, obuf);
+ TRPRINT(tri, "font-variant:normal;");
+ sprintf(obuf,"font-weight:%d;",TR_weight_FC_to_SVG(tsp->weight));
+ TRPRINT(tri, obuf);
+ sprintf(obuf,"font-stretch:%s;",(tsp->condensed==100 ? "Normal" : "Condensed"));
+ TRPRINT(tri, obuf);
+ if(tsp->vadvance){ lineheight = tsp->vadvance *100.0; }
+ else { lineheight = 125.0; }
+ sprintf(obuf,"line-height:%lf%%;",lineheight);
+ TRPRINT(tri, obuf);
+ TRPRINT(tri, "letter-spacing:0px;");
+ TRPRINT(tri, "word-spacing:0px;");
+ TRPRINT(tri, "fill:#000000;");
+ TRPRINT(tri, "fill-opacity:1;");
+ TRPRINT(tri, "stroke:none;");
+ cutat=strcspn((char *)fti->fonts[tsp->fi_idx].fontspec,":");
+ sprintf(obuf,"font-family:%.*s;",cutat,fti->fonts[tsp->fi_idx].fontspec);
+ TRPRINT(tri, obuf);
+ switch(csp->type){ /* set up the alignment, if there is one */
+ case TR_TEXT:
+ case TR_LINE:
+ /* these should never occur, this section quiets a compiler warning */
+ break;
+ case TR_PARA_UJ:
+ case TR_PARA_LJ:
+ sprintf(obuf,"text-align:start;text-anchor:start;");
+ break;
+ case TR_PARA_CJ:
+ sprintf(obuf,"text-align:center;text-anchor:middle;");
+ break;
+ case TR_PARA_RJ:
+ sprintf(obuf,"text-align:end;text-anchor:end;");
+ break;
+ }
+ TRPRINT(tri, obuf);
+ TRPRINT(tri, "\"\n"); /* End of style specification */
+ sprintf(obuf,"transform=\"matrix(%lf,%lf,%lf,%lf,%lf,%lf)\"\n",cos(esc),-sin(esc),sin(esc),cos(esc),1.25*x,1.25*y);
+ TRPRINT(tri, obuf);
+ tmpx = 1.25*((ldir == LDIR_RL ? bri->rects[kdx].xur : bri->rects[kdx].xll) + recenter);
+ sprintf(obuf,"x=\"%lf\" y=\"%lf\"\n>",tmpx,1.25*(bri->rects[kdx].yll - tsp->boff));
+ TRPRINT(tri, obuf);
+ }
+ tmpx = 1.25*((ldir == LDIR_RL ? bri->rects[kdx].xur : bri->rects[kdx].xll) + recenter);
+ sprintf(obuf,"<tspan sodipodi:role=\"line\"\nx=\"%lf\" y=\"%lf\"\n>",tmpx,1.25*(bri->rects[kdx].yll - tsp->boff));
+ TRPRINT(tri, obuf);
+ }
+ TRPRINT(tri, "<tspan\n");
+
+ /* Scale kerning and make any other necessary adjustments
+
+ */
+ dx = 1.25 * tsp->xkern;
+ dy = 1.25 * tsp->ykern;
+
+ sprintf(obuf,"dx=\"%lf\" dy=\"%lf\" ",dx, dy);
+ TRPRINT(tri, obuf);
+ sprintf(obuf,"style=\"fill:#%2.2X%2.2X%2.2X;",tsp->color.Red,tsp->color.Green,tsp->color.Blue);
+ TRPRINT(tri, obuf);
+ sprintf(obuf,"font-size:%lfpx;",tsp->fs*1.25); /*IMPORTANT, if the FS is given in pt it looks like crap in browsers. As if px != 1.25 pt, maybe 96 dpi not 90?*/
+ TRPRINT(tri, obuf);
+ sprintf(obuf,"font-style:%s;",(tsp->italics ? "italic" : "normal"));
+ TRPRINT(tri, obuf);
+ if(tsp->decoration & TXTDECOR_TMASK){
+ sprintf(obuf,"text-decoration:");
+ /* multiple text decoration styles may be set */
+ utmp = tsp->decoration & TXTDECOR_TMASK;
+ if(utmp & TXTDECOR_UNDER ){ strcat(obuf,"underline"); }
+ if(utmp & TXTDECOR_OVER ){ strcat(obuf,"overline"); }
+ if(utmp & TXTDECOR_BLINK ){ strcat(obuf,"blink"); }
+ if(utmp & TXTDECOR_STRIKE){ strcat(obuf,"line-through");}
+ if(*obuf){
+ /* only a single text decoration line type may be set */
+ switch(tsp->decoration & TXTDECOR_LMASK){
+ case TXTDECOR_SOLID: break; // "solid" is the CSS 3 default, omitting it remains CSS 2 compatible
+ case TXTDECOR_DOUBLE: strcat(obuf," double"); break; // these are all CSS3
+ case TXTDECOR_DOTTED: strcat(obuf," dotted"); break;
+ case TXTDECOR_DASHED: strcat(obuf," dashed"); break;
+ case TXTDECOR_WAVY: strcat(obuf," wavy" ); break;
+ default: break;
+ }
+ if((tsp->decoration & TXTDECOR_CLRSET) && memcmp(&(tsp->decColor),&(tsp->color),sizeof(TRCOLORREF))){
+ /* CSS 3, CSS 2 implementations may choke on it. If the specified color matches text color omit, for better CSS 2 compatitiblity. */
+ sprintf(cbuf," #%2.2X%2.2X%2.2X",tsp->decColor.Red,tsp->decColor.Green,tsp->decColor.Blue);
+ strcat(obuf,cbuf);
+ }
+ }
+ strcat(obuf,";");
+ TRPRINT(tri,obuf);
+ }
+ TRPRINT(tri, "font-variant:normal;");
+ sprintf(obuf,"font-weight:%d;",TR_weight_FC_to_SVG(tsp->weight));
+ TRPRINT(tri, obuf);
+ sprintf(obuf,"font-stretch:%s;",(tsp->condensed==100 ? "Normal" : "Condensed"));
+ TRPRINT(tri, obuf);
+ cutat=strcspn((char *)fti->fonts[tsp->fi_idx].fontspec,":");
+ sprintf(obuf,"font-family:%.*s;\"",cutat,fti->fonts[tsp->fi_idx].fontspec);
+ TRPRINT(tri, obuf);
+ TRPRINT(tri, "\n>");
+ TRPRINT(tri, (char *) tsp->string);
+ TRPRINT(tri, "</tspan>");
+ } /* end of k loop */
+ } /* end of j loop */
+ TRPRINT(tri,"</tspan></text>\n");
+ } /* end of i loop */
+}
+
+/**
+ \brief Attempt to figure out the original organization, in lines and paragraphs, of the text objects.
+ The method is:
+ 1. Generate complexes from the text objects (strings) by overlaps (optionally allowing up to two spaces to be
+ added) to produce larger rectangles. Complexes that are more or less sequential and have 2 or more text objects
+ are TR_LINEs, therwise they are TR_TEXT.
+ 2. Group sequential complexes (TR_LINE or TR_TEXT) into TR_PARA_UJ (paragraphs,by smooth progression in vertical
+ position down page).
+ 3. Analyze the paragraphs to classify them as Left/Center/Right justified (possibly with indentation.) If
+ they do not fall into any of these categories break that one back down into TR_LINE/TR_TEXT.
+ 4. Return the number of complex text objects.
+ \returns Number of complexes. (>=1, <= number of text objects.) <0 is an error.
+ \param tri pointer to the TR_INFO structure holding the data, which will also hold the results.
+*/
+int TR_layout_analyze(TR_INFO *tri){
+ unsigned int i,j,k;
+ int ok;
+ int cxidx;
+ int src_rt;
+ int dst_rt;
+ TP_INFO *tpi;
+ BR_INFO *bri;
+ CX_INFO *cxi;
+ FT_INFO *fti;
+ BRECT_SPECS bsp;
+ RT_PAD rt_pad_i;
+ RT_PAD rt_pad_j;
+ double ratio;
+ double qsp,dx,dy;
+ double spcadv;
+ enum tr_classes type;
+ TCHUNK_SPECS *tspi;
+ TCHUNK_SPECS *tspj;
+ TCHUNK_SPECS *tspRevEnd=NULL;
+ TCHUNK_SPECS *tspRevStart=NULL;
+ CX_SPECS *csp;
+ CHILD_SPECS *kidp; /* used with preceding complex (see below) */
+ CHILD_SPECS *kidc; /* used with current complex (see below) */
+ int lastldir,ldir,rev;
+
+ if(!tri)return(-1);
+ if(!tri->cxi)return(-2);
+ if(!tri->tpi)return(-3);
+ if(!tri->bri)return(-4);
+ if(!tri->fti)return(-5);
+ tpi=tri->tpi;
+ cxi=tri->cxi;
+ bri=tri->bri;
+ fti=tri->fti;
+ cxi->lines = 0;
+ cxi->paras = 0;
+ cxi->phase1 = 0;
+
+/* When debugging
+ ftinfo_dump(fti);
+*/
+ /* Phase 1. Working sequentially, insert text. Initially as TR_TEXT and then try to extend to TR_LINE by checking
+ overlaps. When done the complexes will contain a mix of TR_LINE and TR_TEXT. */
+
+ for(i=0; i<tpi->used; i++){
+ tspi = &(tpi->chunks[i]);
+ memcpy(&bsp,&(bri->rects[tspi->rt_tidx]),sizeof(BRECT_SPECS)); /* Must make a copy as next call may reallocate rects! */
+ (void) brinfo_insert(bri,&bsp);
+ dst_rt = bri->used-1;
+ (void) cxinfo_insert(cxi, i, dst_rt, TR_TEXT);
+ cxidx = cxi->used-1;
+
+ spcadv = fti->fonts[tspi->fi_idx].spcadv * tspi->fs/32.0; /* spcadv was always FT_LOAD_NO_SCALE */
+ /* for the leading text: pad with no leading and two trailing spaces, leading and trailing depend on direction */
+ if(tspi->ldir == LDIR_RL){ TR_rt_pad_set(&rt_pad_i,tri->qe, tri->qe, tri->qe + 2.0 * spcadv, 0.0); }
+ else { TR_rt_pad_set(&rt_pad_i,tri->qe, tri->qe, 0.0, tri->qe + 2.0 * spcadv); }
+
+ for(j=i+1; j<tpi->used; j++){
+ tspj = &(tpi->chunks[j]);
+ /* Reject font size changes of greater than 50%, these are almost certainly not continuous text. These happen
+ in math formulas, for instance, where a sum or integral is much larger than the other symbols. */
+ ratio = (double)(tspj->fs)/(double)(tspi->fs);
+ if(ratio >2.0 || ratio <0.5)break;
+
+ spcadv = fti->fonts[tspj->fi_idx].spcadv * tspj->fs/32.0; /* spcadv was always FT_LOAD_NO_SCALE */
+ /* for the trailing text: pad with one leading and trailing spaces (so it should work L->R and R->L) */
+ TR_rt_pad_set(&rt_pad_j,tri->qe, tri->qe, spcadv, spcadv);
+ src_rt = tspj->rt_tidx;
+
+ /* Reject direction changes like [1 <- Hebrew][2 -> English], that is where the direction changes AND the
+ next logical piece of text is "upstream" positionally of its logical predecessor. The meaning of such
+ a construct is at best ambiguous. The test is only applied with respect to the first text chunk. This sort
+ of construct may appear when a valid initial construct like [1->English][2<-Hebrew][3->English] is edited
+ and the leading chunk of text removed.
+
+ Also reject reversed order text as in (English) <A><B><C> (draw order) arranged as <C><B><A>. This happens
+ if the language direction field is incorrect, perhaps due to a corrupt or malformed input file.
+ */
+ if(brinfo_upstream(bri,
+ dst_rt, /* index into bri for dst */
+ src_rt, /* index into bri for src */
+ tspi->ldir,tspj->ldir))break;
+
+ if(!brinfo_overlap(bri,
+ dst_rt, /* index into bri for dst */
+ src_rt, /* index into bri for src */
+ &rt_pad_i,&rt_pad_j)){
+ (void) cxinfo_append(cxi,j,TR_LINE);
+ (void) brinfo_merge(bri,dst_rt,src_rt);
+ /* for the leading text: pad with two leading and trailing spaces (so it should work L->R and R->L */
+ spcadv = fti->fonts[tspj->fi_idx].spcadv * tspj->fs/32.0; /* spcadv was always FT_LOAD_NO_SCALE */
+ TR_rt_pad_set(&rt_pad_i, tri->qe, tri->qe,
+ tri->qe + 2.0 * spcadv, tri->qe + 2.0 * spcadv);
+ }
+ else { /* either alignment ge*/
+ break;
+ }
+ }
+
+ /* Bidirectional text will cause complexes to not assemble in one pass.
+ This happens whenever a change of direction occurs with 2 or more sequential elements in
+ the opposite direction,
+
+ Let + = LR and - = RL.
+
+ Reading left to right, this happens with +-- or -++.
+ For instance, the sequence ++-+ ---+ would break into the two complexes shown.
+ Not until the last element in the second complex is added will the bounding rectangles for the complexes overlap.
+
+ Check for this effect now if there is a preceding complex and the first element of the current complex is
+ reversed from the last in the preceding. */
+ if(cxidx >= 1){
+ kidp = &(cxi->cx[cxidx-1].kids);
+ kidc = &(cxi->cx[cxidx ].kids);
+ tspi = &(tpi->chunks[ kidp->members[kidp->used - 1] ]); /* here, the last text element in preceding complex */
+ tspj = &(tpi->chunks[ kidc->members[0 ] ]); /* here, tge first text element in current complex */
+ if(tspi->ldir != tspj->ldir){
+ spcadv = fti->fonts[tspi->fi_idx].spcadv * tspi->fs/32.0;
+ if(tspi->ldir == LDIR_RL){ TR_rt_pad_set(&rt_pad_i,tri->qe, tri->qe, tri->qe + 2.0 * spcadv, 0.0); }
+ else { TR_rt_pad_set(&rt_pad_i,tri->qe, tri->qe, 0.0, tri->qe + 2.0 * spcadv); }
+ spcadv = fti->fonts[tspj->fi_idx].spcadv * tspj->fs/32.0;
+ TR_rt_pad_set(&rt_pad_j,tri->qe, tri->qe, spcadv, spcadv);
+ if(!brinfo_overlap(bri,
+ cxi->cx[cxidx-1].rt_cidx, /* index into rt for dst cx */
+ cxi->cx[cxidx].rt_cidx, /* index into rt for src cx */
+ &rt_pad_i,&rt_pad_j)){
+ /* Merge the current complex into the preceding one*/
+ (void) cxinfo_merge(cxi, cxidx-1, cxidx, TR_LINE);
+ (void) brinfo_merge(bri,cxi->cx[cxidx-1].rt_cidx,cxi->cx[cxidx].rt_cidx); /* merge the bounding boxes*/
+ (void) cxinfo_trim(cxi);
+ cxi->lines--; /* else the normal line count value is one too high */
+ /* remove the current complex */
+ }
+ }
+ }
+
+ if(cxi->cx[cxidx].type == TR_LINE)cxi->lines++;
+ i=j-1; /* start up after the last merged entry (there may not be any) */
+ }
+ cxi->phase1 = cxi->used; /* total complexes defined in this phase, all TR_LINE or TR_TEXT */
+
+ /* phase 1.5, calculate kerning. This is as good a place to do it as any. At this point all kern values
+ are zero. Each of these pieces is strictly unidirectional, but each piece can have a different direction.
+ The direction of the line is set by the first text element. The ends of runs of elements which are
+ reversed with respect to the line direction are special, everything else is simple:
+ Let: + == L->R, - == R->L, $ == end of text, the rules for kerning on B are:
+ A B others xkern
+ [+|$] + + [+|$] Bll - Aur
+ [-|$] - - [-|$] All - Bur (chs)
+ + - + [-|$] Bll - Aur (chs)
+ - + - [+|$] All - Bur
+ + - -...[-=C] [+|$] All - Cur (chs)
+ - + +...[+=C] [-|$] Cll - Aur
+
+ chs = change sign, because dx is an absolute direction, and direction of text on RTL is in -x.
+
+ Kerning calculations currently seems unstable for R->L if the kerning extends to the end of the line. If
+ the first and last characters are back in sync there are no issues. When things go south R->L left justified
+ text is not justified when read in.
+ */
+
+ for(i=0; i < cxi->phase1; i++){ /* over all lines */
+ csp = &(cxi->cx[i]);
+ if(csp->kids.used < 2)continue; /* no kerning possible */
+ tspi = &tpi->chunks[csp->kids.members[0]]; /* used here as last tsp. no kerning is applied to the first element */
+ lastldir = ldir = tspi->ldir;
+ rev = 0; /* the first ldir defines forward and reverse */
+ for(j=1; j<csp->kids.used; j++){
+ tspj = &tpi->chunks[csp->kids.members[j]];
+ ldir = tspj->ldir;
+ if(ldir != lastldir){ /* direction change */
+ rev = !rev; /* reverse direction tracker */
+ if(!rev){ /* back in original orientation */
+ if(ldir == LDIR_RL){ tspj->xkern = bri->rects[tspj->rt_tidx].xur - bri->rects[tspRevStart->rt_tidx].xll; }
+ else { tspj->xkern = bri->rects[tspj->rt_tidx].xll - bri->rects[tspRevStart->rt_tidx].xur; }
+ tspj->ykern = (bri->rects[tspj->rt_tidx].yll - tspj->boff) -
+ (bri->rects[tspRevStart->rt_tidx].yll - tspRevStart->boff);
+ }
+ else { /* now in reversed orientation */
+ tspRevStart = tspj; /* Save the beginning of this run (length >=1 ) */
+ /* scan forward for the last text object in this orientation, include the first */
+ for(k=j; k <csp->kids.used; k++){
+ if(tpi->chunks[csp->kids.members[k]].ldir == ldir){ tspRevEnd = &tpi->chunks[csp->kids.members[k]]; }
+ else { break; }
+ }
+ if(lastldir == LDIR_RL){ tspj->xkern = bri->rects[tspRevEnd->rt_tidx].xur - bri->rects[tspi->rt_tidx].xll; }
+ else { tspj->xkern = bri->rects[tspRevEnd->rt_tidx].xll - bri->rects[tspi->rt_tidx].xur; }
+ tspj->ykern = (bri->rects[tspRevEnd->rt_tidx].yll - tspRevEnd->boff) -
+ (bri->rects[ tspi->rt_tidx].yll - tspi->boff );
+ }
+ }
+ else {
+ if(ldir == LDIR_RL){ tspj->xkern = bri->rects[tspj->rt_tidx].xur - bri->rects[tspi->rt_tidx].xll; }
+ else { tspj->xkern = bri->rects[tspj->rt_tidx].xll - bri->rects[tspi->rt_tidx].xur; }
+ tspj->ykern = (bri->rects[tspj->rt_tidx].yll - tspj->boff) -
+ (bri->rects[tspi->rt_tidx].yll - tspi->boff);
+ }
+
+
+ /*
+ Sometimes a font substitution was absolutely terrible, for instance, for Arial Narrow on (most) Linux systems,
+ The resulting advance (xkern) may be much too large so that it overruns the next text chunk. Since
+ overlapping text on the same line is almost never encountered, this may be used to detect the bad
+ substitution so that a more appropriate offset can be used.
+ Detect this situation as a negative dx < 1/2 a space character's width while |dy| < an entire space width.
+ The y constraints allow super and subscripts, which overlap in x but are shifted above/below in y.
+ */
+ spcadv = fti->fonts[tspj->fi_idx].spcadv * tspj->fs/32.0;
+ qsp = 0.25 * spcadv;
+ dx = tspj->xkern;
+ dy = tspj->ykern;
+ if(dy <=qsp && dy >= -qsp){
+ if(ldir==LDIR_RL){
+ if(dx > 2*qsp)tspj->xkern = 0.0;
+ }
+ else {
+ if(dx < -2*qsp)tspj->xkern = 0.0;
+ }
+ }
+
+ /* if x or y kern is less than twice the quantization error it is probably noise, set it to zero */
+ if(fabs(tspj->xkern) <= 2.0*tri->qe)tspj->xkern = 0.0;
+ if(fabs(tspj->ykern) <= 2.0*tri->qe)tspj->ykern = 0.0;
+
+ /* reintroduce spaces on the leading edge of text "j" if the kerning can be in part or in whole replaced
+ with 1 or 2 spaces */
+ if(tspj->ykern == 0.0){
+ double spaces = tspj->xkern/spcadv; /* negative on RL language, positive on LR */
+ if((ldir == LDIR_RL && (spaces <= -0.9 && spaces >= -2.1)) ||
+ (ldir == LDIR_LR && (spaces >= 0.9 && spaces <= 2.1)) ){
+ int ispaces = lround(spaces);
+ tspj->xkern -= ((double)ispaces*spcadv);
+ if(ispaces<0)ispaces=-ispaces;
+ size_t slen = strlen((char *)tspj->string);
+ uint8_t *newstring = malloc(1 + ispaces + slen);
+ sprintf((char *)newstring," "); /* start with two spaces, possibly overwrite one in the next line */
+ memcpy(newstring+ispaces,tspj->string,slen+1); /* copy existing string to proper position */
+ free(tspj->string);
+ tspj->string = newstring;
+ tspj->spaces = ispaces; // only needed to fix optional debugging SVG output later
+ }
+ }
+
+ tspi = tspj;
+ lastldir = ldir;
+ }
+ }
+
+
+ /* Phase 2, try to group sequential lines. There may be "lines" that are still TR_TEXT, as in:
+
+ ... this is a sentence that wraps by one
+ word.
+
+ And some paragrahs might be single word lines (+ = bullet in the following)
+
+ +verbs
+ +nouns
+ +adjectives
+
+ Everything starts out as TR_PARA_UJ and if the next one can be lined up, the type changes to
+ an aligned paragraph and complexes are appended to the existing one.
+ */
+
+ for(i=0; i < cxi->phase1; i++){
+ type = TR_PARA_UJ; /* any paragraph alignment will be acceptable */
+ /* Must make a copy as next call may reallocate rects, so if we just passed a pointer to something in the structure
+ it would vaporize part way through the call. */
+ memcpy(&bsp,&(bri->rects[cxi->cx[i].rt_cidx]),sizeof(BRECT_SPECS));
+ (void) brinfo_insert(bri,&bsp);
+ dst_rt = bri->used-1;
+ (void) cxinfo_insert(cxi, i, dst_rt, type);
+
+ cxi->paras++;
+ ok = 1;
+ for(j=i+1; ok && (j < cxi->phase1); j++){
+ type = brinfo_pp_alignment(bri, cxi->cx[i].rt_cidx, cxi->cx[j].rt_cidx, 3*tri->qe, type);
+ switch (type){
+ case TR_PARA_UJ: /* paragraph type was set and j line does not fit, or no paragraph alignment matched */
+ ok = 0; /* force exit from j loop */
+ j--; /* this will increment at loop bottom */
+ break;
+ case TR_PARA_LJ:
+ case TR_PARA_CJ:
+ case TR_PARA_RJ:
+ /* two successive lines have been identified (possible following others already in the paragraph */
+ if(TR_check_set_vadvance(tri,j,i)){ /* check for compatibility with vadvance if set, set it if it isn't. */
+ ok = 0; /* force exit from j loop */
+ j--; /* this will increment at loop bottom */
+ }
+ else {
+ src_rt = cxi->cx[j].rt_cidx;
+ (void) cxinfo_append(cxi, j, type);
+ (void) brinfo_merge(bri, dst_rt, src_rt);
+ }
+ break;
+ default:
+ return(-6); /* programming error */
+ }
+ }
+ if(j>=cxi->phase1)break;
+ i=j-1;
+ }
+
+
+/* When debugging
+ cxinfo_dump(tri);
+*/
+
+ return(cxi->used);
+}
+
+
+/* no doxygen documentation below this point, these pieces are for the text program, not the library. */
+
+#if TEST
+#define MAXLINE 2048 /* big enough for testing */
+enum OP_TYPES {OPCOM,OPOOPS,OPFONT,OPESC,OPORI,OPXY,OPFS,OPTEXT,OPALN,OPLDIR,OPMUL,OPITA,OPWGT,OPDEC,OPCND,OPBKG,OPCLR,OPDCLR,OPBCLR,OPFLAGS,OPEMIT,OPDONE};
+
+int parseit(char *buffer,char **data){
+ int pre;
+ pre = strcspn(buffer,":");
+ if(!pre)return(OPOOPS);
+ *data=&buffer[pre+1];
+ buffer[pre]='\0';
+ if(*buffer=='#' )return(OPCOM );
+ if(0==strcmp("FONT",buffer))return(OPFONT);
+ if(0==strcmp("ESC" ,buffer))return(OPESC );
+ if(0==strcmp("ORI", buffer))return(OPORI );
+ if(0==strcmp("XY", buffer))return(OPXY );
+ if(0==strcmp("FS", buffer))return(OPFS );
+ if(0==strcmp("TEXT",buffer))return(OPTEXT);
+ if(0==strcmp("ALN", buffer))return(OPALN );
+ if(0==strcmp("LDIR",buffer))return(OPLDIR);
+ if(0==strcmp("MUL", buffer))return(OPMUL );
+ if(0==strcmp("ITA", buffer))return(OPITA );
+ if(0==strcmp("WGT", buffer))return(OPWGT );
+ if(0==strcmp("DEC", buffer))return(OPDEC );
+ if(0==strcmp("CND", buffer))return(OPCND );
+ if(0==strcmp("BKG", buffer))return(OPBKG );
+ if(0==strcmp("CLR", buffer))return(OPCLR );
+ if(0==strcmp("DCLR", buffer))return(OPDCLR );
+ if(0==strcmp("BCLR",buffer))return(OPBCLR );
+ if(0==strcmp("FLAG",buffer))return(OPFLAGS);
+ if(0==strcmp("EMIT",buffer))return(OPEMIT);
+ if(0==strcmp("DONE",buffer))return(OPDONE);
+ return(OPOOPS);
+}
+
+void boom(char *string,int lineno){
+ fprintf(stderr,"Fatal error at line %d %s\n",lineno,string);
+ exit(EXIT_FAILURE);
+}
+
+
+void init_as_svg(TR_INFO *tri){
+ TRPRINT(tri,"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
+ TRPRINT(tri,"<!-- Created with Inkscape (http://www.inkscape.org/) -->\n");
+ TRPRINT(tri,"\n");
+ TRPRINT(tri,"<svg\n");
+ TRPRINT(tri," xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n");
+ TRPRINT(tri," xmlns:cc=\"http://creativecommons.org/ns#\"\n");
+ TRPRINT(tri," xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\n");
+ TRPRINT(tri," xmlns:svg=\"http://www.w3.org/2000/svg\"\n");
+ TRPRINT(tri," xmlns=\"http://www.w3.org/2000/svg\"\n");
+ TRPRINT(tri," xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\"\n");
+ TRPRINT(tri," xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\"\n");
+ TRPRINT(tri," width=\"900\"\n");
+ TRPRINT(tri," height=\"675\"\n");
+ TRPRINT(tri," id=\"svg4122\"\n");
+ TRPRINT(tri," version=\"1.1\"\n");
+ TRPRINT(tri," inkscape:version=\"0.48+devel r11679 custom\"\n");
+ TRPRINT(tri," sodipodi:docname=\"simplest_text.svg\">\n");
+ TRPRINT(tri," <defs\n");
+ TRPRINT(tri," id=\"defs4124\" />\n");
+ TRPRINT(tri," <sodipodi:namedview\n");
+ TRPRINT(tri," id=\"base\"\n");
+ TRPRINT(tri," pagecolor=\"#ffffff\"\n");
+ TRPRINT(tri," bordercolor=\"#666666\"\n");
+ TRPRINT(tri," borderopacity=\"1.0\"\n");
+ TRPRINT(tri," inkscape:pageopacity=\"0.0\"\n");
+ TRPRINT(tri," inkscape:pageshadow=\"2\"\n");
+ TRPRINT(tri," inkscape:zoom=\"0.98994949\"\n");
+ TRPRINT(tri," inkscape:cx=\"309.88761\"\n");
+ TRPRINT(tri," inkscape:cy=\"482.63995\"\n");
+ TRPRINT(tri," inkscape:document-units=\"px\"\n");
+ TRPRINT(tri," inkscape:current-layer=\"layer1\"\n");
+ TRPRINT(tri," showgrid=\"false\"\n");
+ TRPRINT(tri," width=\"0px\"\n");
+ TRPRINT(tri," height=\"0px\"\n");
+ TRPRINT(tri," fit-margin-top=\"0\"\n");
+ TRPRINT(tri," fit-margin-left=\"0\"\n");
+ TRPRINT(tri," fit-margin-right=\"0\"\n");
+ TRPRINT(tri," fit-margin-bottom=\"0\"\n");
+ TRPRINT(tri," units=\"in\"\n");
+ TRPRINT(tri," inkscape:window-width=\"1200\"\n");
+ TRPRINT(tri," inkscape:window-height=\"675\"\n");
+ TRPRINT(tri," inkscape:window-x=\"26\"\n");
+ TRPRINT(tri," inkscape:window-y=\"51\"\n");
+ TRPRINT(tri," inkscape:window-maximized=\"0\" />\n");
+ TRPRINT(tri," <metadata\n");
+ TRPRINT(tri," id=\"metadata4127\">\n");
+ TRPRINT(tri," <rdf:RDF>\n");
+ TRPRINT(tri," <cc:Work\n");
+ TRPRINT(tri," rdf:about=\"\">\n");
+ TRPRINT(tri," <dc:format>image/svg+xml</dc:format>\n");
+ TRPRINT(tri," <dc:type\n");
+ TRPRINT(tri," rdf:resource=\"http://purl.org/dc/dcmitype/StillImage\" />\n");
+ TRPRINT(tri," <dc:title></dc:title>\n");
+ TRPRINT(tri," </cc:Work>\n");
+ TRPRINT(tri," </rdf:RDF>\n");
+ TRPRINT(tri," </metadata>\n");
+ TRPRINT(tri," <g\n");
+ TRPRINT(tri," inkscape:label=\"Layer 1\"\n");
+ TRPRINT(tri," inkscape:groupmode=\"layer\"\n");
+ TRPRINT(tri," id=\"layer1\"\n");
+ TRPRINT(tri," transform=\"translate(0,0)\">\n");
+ TRPRINT(tri,"\n");
+}
+
+
+void flush_as_svg(TR_INFO *tri, FILE *fp){
+ fwrite(tri->out,tri->outused,1,fp);
+}
+
+FILE *close_as_svg(TR_INFO *tri, FILE *fp){
+ TRPRINT(tri, " </g>\n");
+ TRPRINT(tri, "</svg>\n");
+ flush_as_svg(tri,fp);
+ fclose(fp);
+ return(NULL);
+}
+
+
+int main(int argc, char *argv[]){
+ char *data;
+ char inbuf[MAXLINE];
+ FILE *fpi = NULL;
+ FILE *fpo = NULL;
+ int op;
+ double fact = 1.0; /* input units to points */
+ double escapement = 0.0; /* degrees */
+ int lineno = 0;
+ int ok = 1;
+ int status;
+ TCHUNK_SPECS tsp;
+ TR_INFO *tri=NULL;
+ int flags=0;
+ char *infile;
+ uint32_t utmp32;
+ TRCOLORREF bkcolor;
+ int bkmode;
+ char *fontspec;
+
+ infile=malloc(strlen(argv[1])+1);
+ strcpy(infile,argv[1]);
+
+ if(argc < 2 || !(fpi = fopen(infile,"r"))){
+ printf("Usage: text_reassemble input_file\n");
+ printf(" Test program reads an input file containing lines like:\n");
+ printf(" FONT:(font for next text)\n");
+ printf(" ESC:(escapement angle degrees of text line, up from X axis)\n");
+ printf(" ORI:(angle degrees of character orientation, up from X axis)\n");
+ printf(" FS:(font size, units)\n");
+ printf(" XY:(x,y) X 0 is at left, N is at right, Y 0 is at top, N is at bottom, as page is viewed.\n");
+ printf(" TEXT:(UTF8 text)\n");
+ printf(" ALN:combination of {LCR}{BLT} = Text is placed on {X,Y} at Left/Center/Right of text, at Bottom,baseLine,Top of text.\n");
+ printf(" LDIR:{LR|RL|TB) Left to Right, Right to Left, and Top to Bottom \n");
+ printf(" MUL:(float, multiplicative factor to convert FS,XY units to points).\n");
+ printf(" ITA:(Italics, 0=normal, 100=italics, 110=oblique).\n");
+ printf(" WGT:(Weight, 0-215: 80=normal, 200=bold, 215=ultrablack, 0=thin)).\n");
+ printf(" DEC:(this is a bit field. For color see DCLR\n");
+ printf(" style: 000 none, 001 underline,002 overline, 004 blink, 008 strike-through\n");
+ printf(" line: 000 solid, 010 double, 020 dotted, 040 dashed, 080 wavy)\n");
+ printf(" CND:(Condensed 50-200: 100=normal, 50=ultracondensed, 75=condensed, 200=expanded).\n");
+ printf(" BKG:(Background color: 0 none, 1 by input fragment, 2 by assembled line, 3 by entire assembly. Use BCLR, THEN BKG) \n");
+ printf(" CLR:(Text RGB color, as 6 HEX digits, like: FF0000 (red) or 0000FF (blue)) \n");
+ printf(" DCLR:(Decoration color, specify like CLR, except 1000000 or higher disables.)\n");
+ printf(" BCLR:(Background RGB color, specify like CLR.) \n");
+ printf(" FLAG: Special processing options. 1 EMF compatible text alignment.\n");
+ printf(" EMIT:(Process everything up to this point, then start clean for remaining input).\n");
+ printf(" DONE:(no more input, process it).\n");
+ printf(" # comment\n");
+ printf("\n");
+ printf(" The output is a summary of how the pieces are to be assembled into complex text.\n");
+ printf("\n");
+ printf(" egrep pattern: '^LOAD:|^FONT:|^ESC:|^ORI:|^FS:|^XY:|^TEXT:|^ALN:|^LDIR:|^MUL:|^ITA:|^WGT:|^DEC:|^CND:|^BKG:|^CLR:|^BCLR:|^DCLR:|^FLAG:|^EMIT:^DONE:'\n");
+ exit(EXIT_FAILURE);
+ }
+
+ tri = trinfo_init(tri); /* If it loops the trinfo_clear at the end will reset tri to the proper state, do NOT call trinfo_init twice! */
+
+#ifdef DBG_LOOP
+ int ldx;
+ for(ldx=0;ldx<5;ldx++){
+ if(fpi)fclose(fpi);
+ fpi = fopen(infile,"r");
+#endif
+ tsp.string = NULL;
+ tsp.ori = 0.0; /* degrees */
+ tsp.fs = 12.0; /* font size */
+ tsp.x = 0.0;
+ tsp.y = 0.0;
+ tsp.boff = 0.0; /* offset to baseline from LL corner of bounding rectangle, changes with fs and taln*/
+ tsp.vadvance = 0.0; /* meaningful only when a complex contains two or more lines */
+ tsp.taln = ALILEFT + ALIBASE;
+ tsp.ldir = LDIR_LR;
+ tsp.color.Red = tsp.decColor.Red = 0; /* RGBA Black */
+ tsp.color.Green = tsp.decColor.Green = 0; /* RGBA Black */
+ tsp.color.Blue = tsp.decColor.Blue = 0; /* RGBA Black */
+ tsp.color.Reserved = tsp.decColor.Reserved = 0; /* unused */
+ tsp.italics = 0;
+ tsp.weight = 80;
+ tsp.condensed = 100;
+ tsp.decoration = 0; /* none */
+ tsp.spaces = 0; /* none */
+ tsp.fi_idx = -1; /* set to an invalid */
+ tsp.rt_tidx = -1; /* set to an invalid */
+ tsp.xkern = tsp.ykern = 0.0;
+ /* no need to set rt_tidx */
+
+
+
+ if(!tri){
+ fprintf(stderr,"Fatal error, could not initialize data structures\n");
+ exit(EXIT_FAILURE);
+ }
+ (void) trinfo_load_ft_opts(tri, 1,
+ FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP,
+ FT_KERNING_UNSCALED);
+
+ fpo=fopen("dump.svg","wb");
+ init_as_svg(tri);
+
+ while(ok){
+ lineno++;
+ if(!fgets(inbuf,MAXLINE,fpi))boom("Unexpected end of file - no DONE:",lineno);
+ inbuf[strlen(inbuf)-1]='\0'; /* step on the EOL character */
+ op = parseit(inbuf,&data);
+ switch(op){
+ case OPCOM: /* ignore comments*/
+ break;
+ case OPFONT:
+ /* If the font name includes "Narrow" condensed may not have been set */
+ if(0<= TR_findcasesub(data, "Narrow")){
+ tsp.co=1;
+ }
+ else {
+ tsp.co=0;
+ }
+ fontspec = TR_construct_fontspec(&tsp, data);
+ if((tsp.fi_idx = ftinfo_load_fontname(tri->fti, fontspec)) < 0 )boom("Font load failed",lineno);
+ free(fontspec);
+ break;
+ case OPESC:
+ if(1 != sscanf(data,"%lf",&escapement))boom("Invalid ESC:",lineno);
+ break;
+ case OPORI:
+ if(1 != sscanf(data,"%lf",&tsp.ori))boom("Invalid ORI:",lineno);
+ break;
+ case OPFS:
+ if(1 != sscanf(data,"%lf",&tsp.fs) || tsp.fs <= 0.0)boom("Invalid FS:",lineno);
+ tsp.fs *= fact;
+ break;
+ case OPXY:
+ if(2 != sscanf(data,"%lf,%lf",&tsp.x,&tsp.y) )boom("Invalid XY:",lineno);
+ tsp.x *= fact;
+ tsp.y *= fact;
+ break;
+ case OPTEXT:
+ tsp.string = (uint8_t *) U_strdup(data);
+ /* FreeType parameters match inkscape*/
+ status = trinfo_load_textrec(tri, &tsp, escapement,flags);
+ if(status==-1){ // change of escapement, emit what we have and reset
+ TR_layout_analyze(tri);
+ TR_layout_2_svg(tri);
+ flush_as_svg(tri, fpo);
+ tri = trinfo_clear(tri);
+ if(trinfo_load_textrec(tri, &tsp, escapement,flags)){ boom("Text load failed",lineno); }
+ }
+ else if(status){ boom("Text load failed",lineno); }
+ break;
+ case OPALN:
+ tsp.taln=0;
+ switch (*data++){
+ case 'L': tsp.taln |= ALILEFT; break;
+ case 'C': tsp.taln |= ALICENTER; break;
+ case 'R': tsp.taln |= ALIRIGHT; break;
+ default: boom("Invalid ALN:",lineno);
+ }
+ switch (*data++){
+ case 'T': tsp.taln |= ALITOP; break;
+ case 'L': tsp.taln |= ALIBASE; break;
+ case 'B': tsp.taln |= ALIBOT; break;
+ default: boom("Invalid ALN:",lineno);
+ }
+ break;
+ case OPLDIR:
+ tsp.ldir=0;
+ if(0==strcmp("LR",data)){ tsp.ldir=LDIR_LR; break;}
+ if(0==strcmp("RL",data)){ tsp.ldir=LDIR_RL; break;}
+ if(0==strcmp("TB",data)){ tsp.ldir=LDIR_TB; break;}
+ boom("Invalid LDIR:",lineno);
+ break;
+ case OPMUL:
+ if(1 != sscanf(data,"%lf",&fact) || fact <= 0.0)boom("Invalid MUL:",lineno);
+ (void) trinfo_load_qe(tri,fact);
+ break;
+ case OPITA:
+ if(1 != sscanf(data,"%d",&tsp.italics) || tsp.italics < 0 || tsp.italics>110)boom("Invalid ITA:",lineno);
+ break;
+ case OPWGT:
+ if(1 != sscanf(data,"%d",&tsp.weight) || tsp.weight < 0 || tsp.weight > 215)boom("Invalid WGT:",lineno);
+ break;
+ case OPDEC:
+ if(1 != sscanf(data,"%X",(unsigned int *) &tsp.decoration))boom("Invalid DEC:",lineno);
+ break;
+ case OPCND:
+ if(1 != sscanf(data,"%d",&tsp.condensed) || tsp.condensed < 50 || tsp.condensed > 200)boom("Invalid CND:",lineno);
+ break;
+ case OPBKG:
+ if(1 != sscanf(data,"%d",&bkmode) )boom("Invalid BKG:",lineno);
+ (void) trinfo_load_bk(tri,bkmode,bkcolor);
+ break;
+ case OPCLR:
+ if(1 != sscanf(data,"%x",&utmp32) )boom("Invalid CLR:",lineno);
+ tsp.color.Red = (utmp32 >> 16) & 0xFF;
+ tsp.color.Green = (utmp32 >> 8) & 0xFF;
+ tsp.color.Blue = (utmp32 >> 0) & 0xFF;
+ tsp.color.Reserved = 0;
+ break;
+ case OPDCLR:
+ if(1 != sscanf(data,"%x",&utmp32) )boom("Invalid DCLR:",lineno);
+ if(utmp32 >= 0x1000000){
+ tsp.decColor.Red = tsp.decColor.Green = tsp.decColor.Blue = tsp.decColor.Reserved = 0;
+ tsp.decoration &= ~TXTDECOR_CLRSET;
+ }
+ else {
+ tsp.decColor.Red = (utmp32 >> 16) & 0xFF;
+ tsp.decColor.Green = (utmp32 >> 8) & 0xFF;
+ tsp.decColor.Blue = (utmp32 >> 0) & 0xFF;
+ tsp.decColor.Reserved = 0;
+ tsp.decoration |= TXTDECOR_CLRSET;
+ }
+ break;
+ case OPBCLR:
+ if(1 != sscanf(data,"%x",&utmp32) )boom("Invalid BCLR:",lineno);
+ bkcolor.Red = (utmp32 >> 16) & 0xFF;
+ bkcolor.Green = (utmp32 >> 8) & 0xFF;
+ bkcolor.Blue = (utmp32 >> 0) & 0xFF;
+ bkcolor.Reserved = 0;
+ break;
+ case OPFLAGS:
+ if(1 != sscanf(data,"%d",&flags) )boom("Invalid FLAG:",lineno);
+ break;
+ case OPEMIT:
+ TR_layout_analyze(tri);
+ TR_layout_2_svg(tri);
+ flush_as_svg(tri, fpo);
+ tri = trinfo_clear(tri);
+ break;
+ case OPDONE:
+ TR_layout_analyze(tri);
+ TR_layout_2_svg(tri);
+ flush_as_svg(tri, fpo);
+ tri = trinfo_clear(tri);
+ ok = 0;
+ break;
+ case OPOOPS:
+ default:
+ boom("Input line cannot be parsed",lineno);
+ break;
+ }
+
+ }
+
+ if(fpo){
+ fpo=close_as_svg(tri, fpo);
+ }
+
+
+#ifdef DBG_LOOP
+ tri = trinfo_clear(tri);
+ ok = 1;
+ }
+#endif /* DBG_LOOP */
+
+ fclose(fpi);
+ tri = trinfo_release(tri);
+ free(infile);
+
+ exit(EXIT_SUCCESS);
+}
+#endif /* TEST */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/extension/internal/text_reassemble.h b/src/extension/internal/text_reassemble.h
new file mode 100644
index 000000000..6c1acafe5
--- /dev/null
+++ b/src/extension/internal/text_reassemble.h
@@ -0,0 +1,387 @@
+/**
+ @file text_reassemble.h libTERE headers.
+
+See text_reassemble.c for notes
+
+File: text_reassemble.h
+Version: 0.0.13
+Date: 06-FEB-2014
+Author: David Mathog, Biology Division, Caltech
+email: mathog@caltech.edu
+Copyright: 2014 David Mathog and California Institute of Technology (Caltech)
+*/
+
+#ifndef _TEXT_REASSEMBLE_
+#define _TEXT_REASSEMBLE_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <stdint.h>
+#include <ctype.h>
+#include <fontconfig/fontconfig.h>
+#include <ft2build.h>
+#include <iconv.h>
+#include FT_FREETYPE_H
+#include FT_GLYPH_H
+
+/** \cond */
+#define TEREMIN(A,B) (A < B ? A : B)
+#define TEREMAX(A,B) (A > B ? A : B)
+
+#ifndef M_PI
+# define M_PI 3.14159265358979323846 /* pi */
+#endif
+#define ALLOCINFO_CHUNK 32
+#define ALLOCOUT_CHUNK 8192
+#define TRPRINT trinfo_append_out
+/** \endcond */
+
+/** \defgroup color background options
+ Text is underwritten with the background color not at all,
+ by reassembled line, or by full assembly .
+ @{
+*/
+#define BKCLR_NONE 0x00 /**< text is not underwritten with background color (default) */
+#define BKCLR_FRAG 0x01 /**< each fragment of text is underwritten with background color */
+#define BKCLR_LINE 0x02 /**< each line of text is underwritten with background color */
+#define BKCLR_ALL 0x03 /**< entire assembly is underwritten with background color */
+/** @} */
+
+/** \defgroup decoration options
+ One of these values may be present in the decoration field.
+ Unused bits may be used by end user code.
+ These values are SVG specific. Other applications could use the text
+ decoration field for a different set of bits, so long as it provided its own
+ output function.
+ @{
+*/
+#define TXTDECOR_NONE 0x000 /**< text is not decorated (default) */
+#define TXTDECOR_UNDER 0x001 /**< underlined */
+#define TXTDECOR_OVER 0x002 /**< overlined */
+#define TXTDECOR_BLINK 0x004 /**< blinking text */
+#define TXTDECOR_STRIKE 0x008 /**< strike through */
+#define TXTDECOR_TMASK 0x00F /**< Mask for selecting bits above */
+
+#define TXTDECOR_SOLID 0x000 /**< draw as single solid line */
+#define TXTDECOR_DOUBLE 0x010 /**< draw as double solid line */
+#define TXTDECOR_DOTTED 0x020 /**< draw as single dotted line */
+#define TXTDECOR_DASHED 0x040 /**< draw as single dashed line */
+#define TXTDECOR_WAVY 0x080 /**< draw as single wavy line */
+#define TXTDECOR_LMASK 0x0F0 /**< Mask for selecting these bits */
+
+#define TXTDECOR_CLRSET 0x100 /**< decoration has its own color */
+
+/** @} */
+
+
+
+
+
+/** \defgroup text alignment types
+ Location of text's {X,Y} coordinate on bounding rectangle.
+ Values are compatible with Fontconfig.
+ @{
+*/
+#define ALILEFT 0x01 /**< text object horizontal alignment = left */
+#define ALICENTER 0x02 /**< text object horizontal alignment = center */
+#define ALIRIGHT 0x04 /**< text object horizontal alignment = right */
+#define ALIHORI 0x07 /**< text object horizontal alignment mask */
+#define ALITOP 0x08 /**< text object vertical alignment = top */
+#define ALIBASE 0x10 /**< text object vertical alignment = baseline */
+#define ALIBOT 0x20 /**< text object vertical alignment = bottom */
+#define ALIVERT 0x38 /**< text object vertical alignment mask */
+/** @} */
+
+/** \defgroup language direction types
+ @{
+*/
+#define LDIR_LR 0x00 /**< left to right */
+#define LDIR_RL 0x01 /**< right to left */
+#define LDIR_TB 0x02 /**< top to bottom */
+/** @} */
+
+/** \defgroup special processing flags
+ @{
+*/
+#define TR_EMFBOT 0x01 /**< use an approximation compatible with EMF file's "BOTTOM" text orientation, which is not the "bottom" for Freetype fonts */
+/** @} */
+
+/** \enum tr_classes
+classification of complexes
+ @{
+*/
+enum tr_classes {
+ TR_TEXT, /**< simple text object */
+ TR_LINE, /**< linear assembly of TR_TEXTs */
+ TR_PARA_UJ, /**< sequential assembly of TR_LINEs and TR_TEXTs into a paragraph -
+ unknown justification properties */
+ TR_PARA_LJ, /**< ditto, left justified */
+ TR_PARA_CJ, /**< ditto, center justified */
+ TR_PARA_RJ /**< ditto, right justified */
+ };
+/** @} */
+
+/**
+ \brief alt font entries.
+*/
+typedef struct {
+ uint32_t fi_idx; /**< index into FT_INFO fonts, for fonts added for missing glyphs */
+ uint32_t weight; /**< integer weight for alt fonts, kept sorted into descending order */
+} ALT_SPECS;
+
+/**
+ \brief Information for a font instance.
+*/
+typedef struct {
+ FcFontSet *fontset; /**< all matching fonts (for fallback on missing glyphs) */
+ ALT_SPECS *alts; /**< index into FT_INFO fonts, for fonts added for missing glyphs */
+ uint32_t space; /**< alts storage slots allocated */
+ uint32_t used; /**< alts storage slots in use */
+ FT_Face face; /**< font face structures (FT_FACE is a pointer!) */
+ uint8_t *file; /**< pointer to font paths to files */
+ uint8_t *fontspec; /**< pointer to a font specification (name:italics, etc.) */
+ FcPattern *fpat; /**< current font, must hang onto this or faces operations break */
+ double spcadv; /**< advance equal to a space, in points at font's face size */
+ double fsize; /**< font's face size in points */
+} FNT_SPECS;
+
+/**
+ \brief Information for all font instances.
+*/
+typedef struct {
+ FT_Library library; /**< Fontconfig handle */
+ FNT_SPECS *fonts; /**< Array of fontinfo structures */
+ uint32_t space; /**< storage slots allocated */
+ uint32_t used; /**< storage slots in use */
+} FT_INFO;
+
+typedef struct {
+ uint8_t Red; //!< Red color (0-255)
+ uint8_t Green; //!< Green color (0-255)
+ uint8_t Blue; //!< Blue color (0-255)
+ uint8_t Reserved; //!< Not used
+} TRCOLORREF;
+
+/**
+ \brief Information for a single text object
+*/
+typedef struct {
+ uint8_t *string; /**< UTF-8 text */
+ double ori; /**< Orientation, angle of characters with respect to baseline in degrees */
+ double fs; /**< font size of text */
+ double x; /**< x coordinate, relative to TR_INFO x,y, in points */
+ double y; /**< y coordinate, relative to TR_INFO x,y, in points */
+ double xkern; /**< x kern relative to preceding text chunk in complex (if any) */
+ double ykern; /**< y kern relative to preceding text chunk in complex (if any) */
+ double boff; /**< Y LL corner - boff finds baseline */
+ double vadvance; /**< Line spacing typically 1.25 or 1.2, only set on the first text
+ element in a complex */
+ TRCOLORREF color; /**< RGB */
+ int taln; /**< text alignment with respect to x,y */
+ int ldir; /**< language diretion LDIR_* */
+ int italics; /**< italics, as in FontConfig */
+ int weight; /**< weight, as in FontConfig */
+ int condensed; /**< condensed, as in FontConfig */
+ int decoration; /**< text decorations, ignored during assembly, used during output */
+ int spaces; /**< count of spaces converted from wide kerning (1 or 2) */
+ TRCOLORREF decColor; /**< text decoration color, ignored during assembly, used during output */
+ int co; /**< condensed override, if set Font name included narrow */
+ int rt_tidx; /**< index of rectangle that contains it */
+ int fi_idx; /**< index of the font it uses */
+} TCHUNK_SPECS;
+
+/**
+ \brief Information for all text objects.
+ Coordinates here are INTERNAL, after offset/rotate using values in TR_INFO.
+*/
+typedef struct {
+ TCHUNK_SPECS *chunks; /**< text chunks */
+ uint32_t space; /**< storage slots allocated */
+ uint32_t used; /**< storage slots in use */
+} TP_INFO;
+
+/**
+ \brief Information for a single bounding rectangle.
+ Coordinates here are INTERNAL, after offset/rotate using values in TR_INFO.
+*/
+typedef struct {
+ double xll; /**< x rectangle lower left corner */
+ double yll; /**< y " */
+ double xur; /**< x upper right corner */
+ double yur; /**< y " */
+ double xbearing; /**< x bearing of the leftmost character */
+} BRECT_SPECS;
+
+/**
+ \brief Information for all bounding rectangles.
+*/
+typedef struct {
+ BRECT_SPECS *rects; /**< bounding rectangles */
+ uint32_t space; /**< storage slots allocated */
+ uint32_t used; /**< storage slots in use */
+} BR_INFO;
+
+/**
+ \brief List of all members of a single complex.
+*/
+typedef struct {
+ int *members; /**< array of immediate children (for TR_PARA_* these are indicies
+ for TR_TEXT or TR_LINE complexes also in cxi. For TR_TEXT
+ and TR_LINE these are indices to the actual text in tpi.) */
+ uint32_t space; /**< storage slots allocated */
+ uint32_t used; /**< storage slots in use */
+} CHILD_SPECS;
+
+/**
+ \brief Information for a single complex.
+*/
+typedef struct {
+ int rt_cidx; /**< index of rectangle that contains all members */
+ enum tr_classes type; /**< classification of the complex */
+ CHILD_SPECS kids; /**< immediate child nodes of this complex, for type TR_TEXT the
+ idx refers to the tpi data. otherwise, cxi data */
+} CX_SPECS;
+
+/**
+ \brief Information for all complexes.
+*/
+typedef struct {
+ CX_SPECS *cx; /**< complexes */
+ uint32_t space; /**< storage slots allocated */
+ uint32_t used; /**< storage slots in use */
+ uint32_t phase1; /**< Number of complexes (lines + text fragments) entered in phase 1 */
+ uint32_t lines; /**< Number of lines in phase 1 */
+ uint32_t paras; /**< Number of complexes (paras) entered in phase 2 */
+} CX_INFO;
+
+/**
+ \brief Information for the entire text reassembly system.
+*/
+typedef struct {
+ FT_INFO *fti; /**< Font info storage */
+ TP_INFO *tpi; /**< Text Info/Position Info storage */
+ BR_INFO *bri; /**< Bounding Rectangle Info storage */
+ CX_INFO *cxi; /**< Complex Info storage */
+ uint8_t *out; /**< buffer to hold formatted output */
+ double qe; /**< quantization error in points. */
+ double esc; /**< escapement angle in DEGREES */
+ double x; /**< x coordinate of first text object, in points */
+ double y; /**< y coordinate of first text object, in points */
+ int dirty; /**< 1 if text records are loaded */
+ int use_kern; /**< 1 if kerning is used, 0 if not */
+ int load_flags; /**< FT_LOAD_NO_SCALE or FT_LOAD_TARGET_NORMAL */
+ int kern_mode; /**< FT_KERNING_DEFAULT, FT_KERNING_UNFITTED, or FT_KERNING_UNSCALED */
+ uint32_t outspace; /**< storage in output buffer allocated */
+ uint32_t outused; /**< storage in output buffer in use */
+ int usebk; /**< On output write the background color under the text */
+ TRCOLORREF bkcolor; /**< RGB background color */
+} TR_INFO;
+
+/* padding added to rectangles before overlap test */
+/**
+ \brief Information for one padding record. (Padding is added to bounding rectangles before overlap tests.)
+*/
+typedef struct {
+ double up; /**< to top */
+ double down; /**< to bottom */
+ double left; /**< to left */
+ double right; /**< to right */
+} RT_PAD;
+
+/** \cond */
+/*
+ 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 */
+
+/* Prototypes */
+int TR_findcasesub(const char *string, const char *sub);
+char *TR_construct_fontspec(const TCHUNK_SPECS *tsp, const char *fontname);
+char *TR_reconstruct_fontspec(const char *fontspec, const char *fontname);
+int TR_find_alternate_font(FT_INFO *fti, FNT_SPECS **efsp, uint32_t wc);
+int TR_getadvance(FT_INFO *fti, FNT_SPECS *fsp, uint32_t wc, uint32_t pc, int load_flags, int kern_mode, int *ymin, int *ymax);
+int TR_getkern2(FNT_SPECS *fsp, uint32_t wc, uint32_t pc, int kern_mode);
+int TR_kern_gap(FNT_SPECS *fsp, TCHUNK_SPECS *tsp, TCHUNK_SPECS *ptsp, int kern_mode);
+void TR_rt_pad_set(RT_PAD *rt_pad, double up, double down, double left, double right);
+double TR_baseline(TR_INFO *tri, int src, double *AscMax, double *DscMax);
+int TR_check_set_vadvance(TR_INFO *tri, int src, int lines);
+int TR_layout_analyze(TR_INFO *tri);
+void TR_layout_2_svg(TR_INFO *tri);
+int TR_weight_FC_to_SVG(int weight);
+
+FT_INFO *ftinfo_init(void);
+int ftinfo_make_insertable(FT_INFO *fti);
+int ftinfo_insert(FT_INFO *fti, FNT_SPECS *fsp);
+FT_INFO *ftinfo_release(FT_INFO *fti);
+FT_INFO *ftinfo_clear(FT_INFO *fti);
+int ftinfo_find_loaded_by_spec(const FT_INFO *fti, const uint8_t *fname);
+int ftinfo_find_loaded_by_src(const FT_INFO *fti, const uint8_t *filename);
+int ftinfo_load_fontname(FT_INFO *fti, const char *fontspec);
+void ftinfo_dump(const FT_INFO *fti);
+
+int fsp_alts_make_insertable(FNT_SPECS *fsp);
+int fsp_alts_insert(FNT_SPECS *fsp, uint32_t fi_idx);
+int fsp_alts_weight(FNT_SPECS *fsp, uint32_t a_idx);
+
+int csp_make_insertable(CHILD_SPECS *csp);
+int csp_insert(CHILD_SPECS *csp, int src);
+int csp_merge(CHILD_SPECS *dst, CHILD_SPECS *src);
+void csp_release(CHILD_SPECS *csp);
+void csp_clear(CHILD_SPECS *csp);
+
+CX_INFO *cxinfo_init(void);
+int cxinfo_make_insertable(CX_INFO *cxi);
+int cxinfo_insert(CX_INFO *cxi, int src, int src_rt_idx, enum tr_classes type);
+int cxinfo_append(CX_INFO *cxi, int src, enum tr_classes type);
+int cxinfo_merge(CX_INFO *cxi, int dst, int src, enum tr_classes type);
+int cxinfo_trim(CX_INFO *cxi);
+CX_INFO *cxinfo_release(CX_INFO *cxi);
+void cxinfo_dump(const TR_INFO *tri);
+
+TP_INFO *tpinfo_init(void);
+int tpinfo_make_insertable(TP_INFO *tpi);
+int tpinfo_insert(TP_INFO *tpi, const TCHUNK_SPECS *tsp);
+TP_INFO *tpinfo_release(TP_INFO *tpi);
+
+BR_INFO *brinfo_init(void);
+int brinfo_make_insertable(BR_INFO *bri);
+int brinfo_insert(BR_INFO *bri, const BRECT_SPECS *element);
+int brinfo_merge(BR_INFO *bri, int dst, int src);
+enum tr_classes
+ brinfo_pp_alignment(const BR_INFO *bri, int dst, int src, double slop, enum tr_classes type);
+int brinfo_overlap(const BR_INFO *bri, int dst, int src, RT_PAD *rp_dst, RT_PAD *rp_src);
+BR_INFO *brinfo_release(BR_INFO *bri);
+
+TR_INFO *trinfo_init(TR_INFO *tri);
+TR_INFO *trinfo_release(TR_INFO *tri);
+TR_INFO *trinfo_release_except_FC(TR_INFO *tri);
+TR_INFO *trinfo_clear(TR_INFO *tri);
+int trinfo_load_qe(TR_INFO *tri, double qe);
+int trinfo_load_bk(TR_INFO *tri, int usebk, TRCOLORREF bkcolor);
+int trinfo_load_ft_opts(TR_INFO *tri, int use_kern, int load_flags, int kern_mode);
+int trinfo_load_textrec(TR_INFO *tri, const TCHUNK_SPECS *tsp, double escapement, int flags);
+int trinfo_check_bk(TR_INFO *tri, int usebk, TRCOLORREF bkcolor);
+int trinfo_append_out(TR_INFO *tri, const char *src);
+
+int is_mn_unicode(int test);
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _TEXT_REASSEMBLE_ */
diff --git a/src/extension/internal/vsd-input.cpp b/src/extension/internal/vsd-input.cpp
new file mode 100644
index 000000000..6fc79237b
--- /dev/null
+++ b/src/extension/internal/vsd-input.cpp
@@ -0,0 +1,339 @@
+/*
+ * This file came from libwpg as a source, their utility wpg2svg
+ * specifically. It has been modified to work as an Inkscape extension.
+ * The Inkscape extension code is covered by this copyright, but the
+ * rest is covered by the one bellow.
+ *
+ * Authors:
+ * Fridrich Strba (fridrich.strba@bluewin.ch)
+ *
+ * Copyright (C) 2012 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ *
+ */
+
+#include <stdio.h>
+#include "config.h"
+
+#include "vsd-input.h"
+
+#ifdef WITH_LIBVISIO
+
+#include <string>
+#include <cstring>
+
+#include <libvisio/libvisio.h>
+#include <libwpd-stream/libwpd-stream.h>
+
+#include <gtkmm/alignment.h>
+#include <gtkmm/comboboxtext.h>
+#include <gtkmm/drawingarea.h>
+#include <gtkmm/frame.h>
+#include <gtkmm/scale.h>
+
+#include "extension/system.h"
+#include "extension/input.h"
+#include "document.h"
+
+#include "document-private.h"
+#include "document-undo.h"
+#include "inkscape.h"
+#include "util/units.h"
+
+#include "dialogs/dialog-events.h"
+#include <gtk/gtk.h>
+#include "ui/widget/spinbutton.h"
+#include "ui/widget/frame.h"
+#include <glibmm/i18n.h>
+
+#include <gdkmm/general.h>
+
+#include "svg-view.h"
+#include "svg-view-widget.h"
+
+namespace Inkscape {
+namespace Extension {
+namespace Internal {
+
+
+class VsdImportDialog : public Gtk::Dialog {
+public:
+ VsdImportDialog(const std::vector<WPXString> &vec);
+ virtual ~VsdImportDialog();
+
+ bool showDialog();
+ unsigned getSelectedPage();
+ void getImportSettings(Inkscape::XML::Node *prefs);
+
+private:
+ void _setPreviewPage(unsigned page);
+
+ // Signal handlers
+#if !WITH_GTKMM_3_0
+ bool _onExposePreview(GdkEventExpose *event);
+#endif
+
+ void _onPageNumberChanged();
+
+ class Gtk::Button * cancelbutton;
+ class Gtk::Button * okbutton;
+ class Gtk::Label * _labelSelect;
+ class Inkscape::UI::Widget::SpinButton * _pageNumberSpin;
+ class Gtk::Label * _labelTotalPages;
+ class Gtk::VBox * vbox1;
+ class Gtk::VBox * vbox2;
+ class Gtk::Widget * _previewArea;
+
+ const std::vector<WPXString> &_vec; // Document to be imported
+ unsigned _current_page; // Current selected page
+ int _preview_width, _preview_height; // Size of the preview area
+};
+
+VsdImportDialog::VsdImportDialog(const std::vector<WPXString> &vec)
+ : _vec(vec), _current_page(1)
+{
+ int num_pages = _vec.size();
+ if ( num_pages <= 1 )
+ return;
+ cancelbutton = Gtk::manage(new class Gtk::Button(Gtk::StockID("gtk-cancel")));
+ okbutton = Gtk::manage(new class Gtk::Button(Gtk::StockID("gtk-ok")));
+ _labelSelect = Gtk::manage(new class Gtk::Label(_("Select page:")));
+
+ // Page number
+#if WITH_GTKMM_3_0
+ Glib::RefPtr<Gtk::Adjustment> _pageNumberSpin_adj = Gtk::Adjustment::create(1, 1, _vec.size(), 1, 10, 0);
+ _pageNumberSpin = Gtk::manage(new Inkscape::UI::Widget::SpinButton(_pageNumberSpin_adj, 1, 1));
+#else
+ Gtk::Adjustment *_pageNumberSpin_adj = Gtk::manage(
+ new class Gtk::Adjustment(1, 1, _vec.size(), 1, 10, 0));
+ _pageNumberSpin = Gtk::manage(new class Inkscape::UI::Widget::SpinButton(*_pageNumberSpin_adj, 1, 1));
+#endif
+ _labelTotalPages = Gtk::manage(new class Gtk::Label());
+ gchar *label_text = g_strdup_printf(_("out of %i"), num_pages);
+ _labelTotalPages->set_label(label_text);
+ g_free(label_text);
+
+ vbox1 = Gtk::manage(new class Gtk::VBox(false, 4));
+ SPDocument *doc = SPDocument::createNewDocFromMem(_vec[0].cstr(), strlen(_vec[0].cstr()), 0);
+ _previewArea = Glib::wrap(sp_svg_view_widget_new(doc));
+
+ vbox2 = Gtk::manage(new class Gtk::VBox(false, 4));
+ cancelbutton->set_can_focus();
+ cancelbutton->set_can_default();
+ cancelbutton->set_relief(Gtk::RELIEF_NORMAL);
+ okbutton->set_can_focus();
+ okbutton->set_can_default();
+ okbutton->set_relief(Gtk::RELIEF_NORMAL);
+ this->get_action_area()->property_layout_style().set_value(Gtk::BUTTONBOX_END);
+ _labelSelect->set_line_wrap(false);
+ _labelSelect->set_use_markup(false);
+ _labelSelect->set_selectable(false);
+ _pageNumberSpin->set_can_focus();
+ _pageNumberSpin->set_update_policy(Gtk::UPDATE_ALWAYS);
+ _pageNumberSpin->set_numeric(true);
+ _pageNumberSpin->set_digits(0);
+ _pageNumberSpin->set_wrap(false);
+ _labelTotalPages->set_line_wrap(false);
+ _labelTotalPages->set_use_markup(false);
+ _labelTotalPages->set_selectable(false);
+ vbox2->pack_start(*_previewArea, Gtk::PACK_SHRINK, 0);
+ this->get_vbox()->set_homogeneous(false);
+ this->get_vbox()->set_spacing(0);
+ this->get_vbox()->pack_start(*vbox2);
+ this->set_title(_("Page Selector"));
+ this->set_modal(true);
+ sp_transientize(GTK_WIDGET(this->gobj())); //Make transient
+ this->property_window_position().set_value(Gtk::WIN_POS_NONE);
+ this->set_resizable(true);
+ this->property_destroy_with_parent().set_value(false);
+ this->get_action_area()->add(*_labelSelect);
+ this->add_action_widget(*_pageNumberSpin, -7);
+ this->get_action_area()->add(*_labelTotalPages);
+ this->add_action_widget(*cancelbutton, -6);
+ this->add_action_widget(*okbutton, -5);
+ cancelbutton->show();
+ okbutton->show();
+ _labelSelect->show();
+ _pageNumberSpin->show();
+ _labelTotalPages->show();
+ vbox1->show();
+ _previewArea->show();
+ vbox2->show();
+
+ // Connect signals
+ _pageNumberSpin_adj->signal_value_changed().connect(sigc::mem_fun(*this, &VsdImportDialog::_onPageNumberChanged));
+}
+
+VsdImportDialog::~VsdImportDialog() {}
+
+bool VsdImportDialog::showDialog()
+{
+ show();
+ gint b = run();
+ hide();
+ if ( b == Gtk::RESPONSE_OK ) {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+unsigned VsdImportDialog::getSelectedPage()
+{
+ return _current_page;
+}
+
+void VsdImportDialog::_onPageNumberChanged()
+{
+ unsigned page = static_cast<unsigned>(_pageNumberSpin->get_value_as_int());
+ _current_page = CLAMP(page, 1U, _vec.size());
+ _setPreviewPage(_current_page);
+}
+
+/**
+ * \brief Renders the given page's thumbnail
+ */
+void VsdImportDialog::_setPreviewPage(unsigned page)
+{
+ SPDocument *doc = SPDocument::createNewDocFromMem(_vec[page-1].cstr(), strlen(_vec[page-1].cstr()), 0);
+ Gtk::Widget * tmpPreviewArea = Glib::wrap(sp_svg_view_widget_new(doc));
+ std::swap(_previewArea, tmpPreviewArea);
+ if (tmpPreviewArea) {
+ _previewArea->set_size_request( tmpPreviewArea->get_width(), tmpPreviewArea->get_height() );
+ delete tmpPreviewArea;
+ }
+ vbox2->pack_start(*_previewArea, Gtk::PACK_SHRINK, 0);
+ _previewArea->show_now();
+}
+
+SPDocument *VsdInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * uri)
+{
+ WPXFileStream input(uri);
+
+ if (!libvisio::VisioDocument::isSupported(&input)) {
+ return NULL;
+ }
+
+ libvisio::VSDStringVector output;
+ if (!libvisio::VisioDocument::generateSVG(&input, output)) {
+ return NULL;
+ }
+
+ if (output.empty()) {
+ return NULL;
+ }
+
+ std::vector<WPXString> tmpSVGOutput;
+ for (unsigned i=0; i<output.size(); ++i) {
+ WPXString tmpString("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
+ tmpString.append(output[i]);
+ tmpSVGOutput.push_back(tmpString);
+ }
+
+ unsigned page_num = 1;
+
+ // If only one page is present, import that one without bothering user
+ if (tmpSVGOutput.size() > 1) {
+ VsdImportDialog *dlg = 0;
+ if (inkscape_use_gui()) {
+ dlg = new VsdImportDialog(tmpSVGOutput);
+ if (!dlg->showDialog()) {
+ delete dlg;
+ return NULL;
+ }
+ }
+
+ // Get needed page
+ if (dlg) {
+ page_num = dlg->getSelectedPage();
+ if (page_num < 1)
+ page_num = 1;
+ if (page_num > tmpSVGOutput.size())
+ page_num = tmpSVGOutput.size();
+ }
+ }
+
+ SPDocument * doc = SPDocument::createNewDocFromMem(tmpSVGOutput[page_num-1].cstr(), strlen(tmpSVGOutput[page_num-1].cstr()), TRUE);
+
+ // Set viewBox if it doesn't exist
+ if (!doc->getRoot()->viewBox_set) {
+ doc->setViewBox(Geom::Rect::from_xywh(0, 0, doc->getWidth().value(doc->getDefaultUnit()), doc->getHeight().value(doc->getDefaultUnit())));
+ }
+
+ return doc;
+}
+
+#include "clear-n_.h"
+
+void VsdInput::init(void)
+{
+ /* VSD */
+ Inkscape::Extension::build_from_mem(
+ "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
+ "<name>" N_("VSD Input") "</name>\n"
+ "<id>org.inkscape.input.vsd</id>\n"
+ "<input>\n"
+ "<extension>.vsd</extension>\n"
+ "<mimetype>application/vnd.visio</mimetype>\n"
+ "<filetypename>" N_("Microsoft Visio Diagram (*.vsd)") "</filetypename>\n"
+ "<filetypetooltip>" N_("File format used by Microsoft Visio 6 and later") "</filetypetooltip>\n"
+ "</input>\n"
+ "</inkscape-extension>", new VsdInput());
+
+ /* VDX */
+ Inkscape::Extension::build_from_mem(
+ "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
+ "<name>" N_("VDX Input") "</name>\n"
+ "<id>org.inkscape.input.vdx</id>\n"
+ "<input>\n"
+ "<extension>.vdx</extension>\n"
+ "<mimetype>application/vnd.visio</mimetype>\n"
+ "<filetypename>" N_("Microsoft Visio XML Diagram (*.vdx)") "</filetypename>\n"
+ "<filetypetooltip>" N_("File format used by Microsoft Visio 2010 and later") "</filetypetooltip>\n"
+ "</input>\n"
+ "</inkscape-extension>", new VsdInput());
+
+ /* VSDM */
+ Inkscape::Extension::build_from_mem(
+ "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
+ "<name>" N_("VSDM Input") "</name>\n"
+ "<id>org.inkscape.input.vsdm</id>\n"
+ "<input>\n"
+ "<extension>.vsdm</extension>\n"
+ "<mimetype>application/vnd.visio</mimetype>\n"
+ "<filetypename>" N_("Microsoft Visio 2013 drawing (*.vsdm)") "</filetypename>\n"
+ "<filetypetooltip>" N_("File format used by Microsoft Visio 2013 and later") "</filetypetooltip>\n"
+ "</input>\n"
+ "</inkscape-extension>", new VsdInput());
+
+ /* VSDX */
+ Inkscape::Extension::build_from_mem(
+ "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
+ "<name>" N_("VSDX Input") "</name>\n"
+ "<id>org.inkscape.input.vsdx</id>\n"
+ "<input>\n"
+ "<extension>.vsdx</extension>\n"
+ "<mimetype>application/vnd.visio</mimetype>\n"
+ "<filetypename>" N_("Microsoft Visio 2013 drawing (*.vsdx)") "</filetypename>\n"
+ "<filetypetooltip>" N_("File format used by Microsoft Visio 2013 and later") "</filetypetooltip>\n"
+ "</input>\n"
+ "</inkscape-extension>", new VsdInput());
+
+ return;
+
+} // init
+
+} } } /* namespace Inkscape, Extension, Implementation */
+#endif /* WITH_LIBVISIO */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/extension/internal/vsd-input.h b/src/extension/internal/vsd-input.h
new file mode 100644
index 000000000..3414e0ec9
--- /dev/null
+++ b/src/extension/internal/vsd-input.h
@@ -0,0 +1,58 @@
+/*
+ * This code abstracts the libwpg interfaces into the Inkscape
+ * input extension interface.
+ *
+ * Authors:
+ * Fridrich Strba (fridrich.strba@bluewin.ch)
+ *
+ * Copyright (C) 2012 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef __EXTENSION_INTERNAL_VSDOUTPUT_H__
+#define __EXTENSION_INTERNAL_VSDOUTPUT_H__
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef WITH_LIBVISIO
+
+#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H
+#include <glibmm/threads.h>
+#endif
+
+#include <gtkmm/dialog.h>
+
+#include "../implementation/implementation.h"
+
+namespace Inkscape {
+namespace Extension {
+namespace Internal {
+
+class VsdInput : public Inkscape::Extension::Implementation::Implementation {
+ VsdInput () { };
+public:
+ SPDocument *open( Inkscape::Extension::Input *mod,
+ const gchar *uri );
+ static void init( void );
+
+};
+
+} } } /* namespace Inkscape, Extension, Implementation */
+
+#endif /* WITH_LIBVISIO */
+#endif /* __EXTENSION_INTERNAL_VSDOUTPUT_H__ */
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/extension/internal/wmf-inout.cpp b/src/extension/internal/wmf-inout.cpp
new file mode 100644
index 000000000..ef95dbe45
--- /dev/null
+++ b/src/extension/internal/wmf-inout.cpp
@@ -0,0 +1,3135 @@
+/** @file
+ * @brief Windows-only Enhanced Metafile input and output.
+ */
+/* Authors:
+ * Ulf Erikson <ulferikson@users.sf.net>
+ * Jon A. Cruz <jon@joncruz.org>
+ * David Mathog
+ * 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
+
+//#include <png.h> //This must precede text_reassemble.h or it blows up in pngconf.h when compiling
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <libuemf/symbol_convert.h>
+
+#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 "util/units.h"
+#include "clear-n_.h"
+#include "document.h"
+#include "shape-editor.h"
+#include "sp-namedview.h"
+#include "document-undo.h"
+#include "inkscape.h"
+
+
+#include "wmf-inout.h"
+#include "wmf-print.h"
+
+#define PRINT_WMF "org.inkscape.print.wmf"
+
+#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 U_RECT16 rc_old;
+static bool clipset = false;
+static uint32_t BLTmode=0;
+
+Wmf::Wmf (void) // The null constructor
+{
+ return;
+}
+
+
+Wmf::~Wmf (void) //The destructor
+{
+ return;
+}
+
+
+bool
+Wmf::check (Inkscape::Extension::Extension * /*module*/)
+{
+ if (NULL == Inkscape::Extension::db.get(PRINT_WMF))
+ return FALSE;
+ return TRUE;
+}
+
+
+void
+Wmf::print_document_to_file(SPDocument *doc, const gchar *filename)
+{
+ Inkscape::Extension::Print *mod;
+ SPPrintContext context;
+ const gchar *oldconst;
+ gchar *oldoutput;
+ unsigned int ret;
+
+ doc->ensureUpToDate();
+
+ mod = Inkscape::Extension::get_print(PRINT_WMF);
+ 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
+Wmf::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar const *filename)
+{
+ Inkscape::Extension::Extension * ext;
+
+ ext = Inkscape::Extension::db.get(PRINT_WMF);
+ 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 WMF 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 WMF 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 PrintWmf::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);
+
+ print_document_to_file(doc, filename);
+
+ return;
+}
+
+
+/* WMF has no worldTransform, so this always returns 1.0. Retain it to keep WMF and WMF in sync as much as possible.*/
+double Wmf::current_scale(PWMF_CALLBACK_DATA /*d*/){
+ return 1.0;
+}
+
+/* WMF has no worldTransform, so this always returns an Identity rotation matrix, but the offsets may have values.*/
+std::string Wmf::current_matrix(PWMF_CALLBACK_DATA d, double x, double y, int useoffset){
+ std::stringstream cxform;
+ double scale = current_scale(d);
+ cxform << "\"matrix(";
+ cxform << 1.0/scale; cxform << ",";
+ cxform << 0.0; cxform << ",";
+ cxform << 0.0; cxform << ",";
+ cxform << 1.0/scale; cxform << ",";
+ if(useoffset){ cxform << x; cxform << ","; cxform << y; }
+ else { cxform << "0,0"; }
+ cxform << ")\"";
+ return(cxform.str());
+}
+
+/* WMF has no worldTransform, so this always returns 0. Retain it to keep WMF and WMF in sync as much as possible.*/
+double Wmf::current_rotation(PWMF_CALLBACK_DATA /*d*/){
+ return 0.0;
+}
+
+/* Add another 100 blank slots to the hatches array.
+*/
+void Wmf::enlarge_hatches(PWMF_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 Wmf::in_hatches(PWMF_CALLBACK_DATA d, char *test){
+ int i;
+ for(i=0; i<d->hatches.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 <defs>.
+ This is also used to add the path part of the hatches, which they reference with a xlink:href
+*/
+uint32_t Wmf::add_hatch(PWMF_CALLBACK_DATA d, uint32_t hatchType, U_COLORREF hatchColor){
+ char hatchname[64]; // big enough
+ char hpathname[64]; // big enough
+ char hbkname[64]; // big enough
+ char tmpcolor[8];
+ char bkcolor[8];
+ uint32_t idx;
+
+ switch(hatchType){
+ case U_HS_SOLIDTEXTCLR:
+ case U_HS_DITHEREDTEXTCLR:
+ sprintf(tmpcolor,"%6.6X",sethexcolor(d->dc[d->level].textColor));
+ break;
+ case U_HS_SOLIDBKCLR:
+ case U_HS_DITHEREDBKCLR:
+ sprintf(tmpcolor,"%6.6X",sethexcolor(d->dc[d->level].bkColor));
+ break;
+ default:
+ sprintf(tmpcolor,"%6.6X",sethexcolor(hatchColor));
+ break;
+ }
+
+ /* For both bkMode types set the PATH + FOREGROUND COLOR for the indicated standard hatch.
+ This will be used late to compose, or recompose the transparent or opaque final hatch.*/
+
+ std::string refpath; // used to reference later the path pieces which are about to be created
+ sprintf(hpathname,"WMFhpath%d_%s",hatchType,tmpcolor);
+ idx = in_hatches(d,hpathname);
+ if(!idx){ // add path/color if not already present
+ if(d->hatches.count == d->hatches.size){ enlarge_hatches(d); }
+ d->hatches.strings[d->hatches.count++]=strdup(hpathname);
+
+ *(d->defs) += "\n";
+ switch(hatchType){
+ case U_HS_HORIZONTAL:
+ *(d->defs) += " <path id=\"";
+ *(d->defs) += hpathname;
+ *(d->defs) += "\" d=\"M 0 0 6 0\" style=\"fill:none;stroke:#";
+ *(d->defs) += tmpcolor;
+ *(d->defs) += "\" />\n";
+ break;
+ case U_HS_VERTICAL:
+ *(d->defs) += " <path id=\"";
+ *(d->defs) += hpathname;
+ *(d->defs) += "\" d=\"M 0 0 0 6\" style=\"fill:none;stroke:#";
+ *(d->defs) += tmpcolor;
+ *(d->defs) += "\" />\n";
+ break;
+ case U_HS_FDIAGONAL:
+ *(d->defs) += " <line id=\"sub";
+ *(d->defs) += hpathname;
+ *(d->defs) += "\" x1=\"-1\" y1=\"-1\" x2=\"7\" y2=\"7\" stroke=\"#";
+ *(d->defs) += tmpcolor;
+ *(d->defs) += "\"/>\n";
+ break;
+ case U_HS_BDIAGONAL:
+ *(d->defs) += " <line id=\"sub";
+ *(d->defs) += hpathname;
+ *(d->defs) += "\" x1=\"-1\" y1=\"7\" x2=\"7\" y2=\"-1\" stroke=\"#";
+ *(d->defs) += tmpcolor;
+ *(d->defs) += "\"/>\n";
+ break;
+ case U_HS_CROSS:
+ *(d->defs) += " <path id=\"";
+ *(d->defs) += hpathname;
+ *(d->defs) += "\" d=\"M 0 0 6 0 M 0 0 0 6\" style=\"fill:none;stroke:#";
+ *(d->defs) += tmpcolor;
+ *(d->defs) += "\" />\n";
+ break;
+ case U_HS_DIAGCROSS:
+ *(d->defs) += " <line id=\"subfd";
+ *(d->defs) += hpathname;
+ *(d->defs) += "\" x1=\"-1\" y1=\"-1\" x2=\"7\" y2=\"7\" stroke=\"#";
+ *(d->defs) += tmpcolor;
+ *(d->defs) += "\"/>\n";
+ *(d->defs) += " <line id=\"subbd";
+ *(d->defs) += hpathname;
+ *(d->defs) += "\" x1=\"-1\" y1=\"7\" x2=\"7\" y2=\"-1\" stroke=\"#";
+ *(d->defs) += tmpcolor;
+ *(d->defs) += "\"/>\n";
+ break;
+ case U_HS_SOLIDCLR:
+ case U_HS_DITHEREDCLR:
+ case U_HS_SOLIDTEXTCLR:
+ case U_HS_DITHEREDTEXTCLR:
+ case U_HS_SOLIDBKCLR:
+ case U_HS_DITHEREDBKCLR:
+ default:
+ *(d->defs) += " <path id=\"";
+ *(d->defs) += hpathname;
+ *(d->defs) += "\" d=\"M 0 0 6 0 6 6 0 6 z\" style=\"fill:#";
+ *(d->defs) += tmpcolor;
+ *(d->defs) += ";stroke:none";
+ *(d->defs) += "\" />\n";
+ break;
+ }
+ }
+
+ // References to paths possibly just created above. These will be used in the actual patterns.
+ switch(hatchType){
+ case U_HS_HORIZONTAL:
+ case U_HS_VERTICAL:
+ case U_HS_CROSS:
+ case U_HS_SOLIDCLR:
+ case U_HS_DITHEREDCLR:
+ case U_HS_SOLIDTEXTCLR:
+ case U_HS_DITHEREDTEXTCLR:
+ case U_HS_SOLIDBKCLR:
+ case U_HS_DITHEREDBKCLR:
+ default:
+ refpath += " <use xlink:href=\"#";
+ refpath += hpathname;
+ refpath += "\" />\n";
+ break;
+ case U_HS_FDIAGONAL:
+ case U_HS_BDIAGONAL:
+ refpath += " <use xlink:href=\"#sub";
+ refpath += hpathname;
+ refpath += "\" />\n";
+ refpath += " <use xlink:href=\"#sub";
+ refpath += hpathname;
+ refpath += "\" transform=\"translate(6,0)\" />\n";
+ refpath += " <use xlink:href=\"#sub";
+ refpath += hpathname;
+ refpath += "\" transform=\"translate(-6,0)\" />\n";
+ break;
+ case U_HS_DIAGCROSS:
+ refpath += " <use xlink:href=\"#subfd";
+ refpath += hpathname;
+ refpath += "\" />\n";
+ refpath += " <use xlink:href=\"#subfd";
+ refpath += hpathname;
+ refpath += "\" transform=\"translate(6,0)\"/>\n";
+ refpath += " <use xlink:href=\"#subfd";
+ refpath += hpathname;
+ refpath += "\" transform=\"translate(-6,0)\"/>\n";
+ refpath += " <use xlink:href=\"#subbd";
+ refpath += hpathname;
+ refpath += "\" />\n";
+ refpath += " <use xlink:href=\"#subbd";
+ refpath += hpathname;
+ refpath += "\" transform=\"translate(6,0)\"/>\n";
+ refpath += " <use xlink:href=\"#subbd";
+ refpath += hpathname;
+ refpath += "\" transform=\"translate(-6,0)\"/>\n";
+ break;
+ }
+
+ if(d->dc[d->level].bkMode == U_TRANSPARENT || hatchType >= U_HS_SOLIDCLR){
+ sprintf(hatchname,"WMFhatch%d_%s",hatchType,tmpcolor);
+ sprintf(hpathname,"WMFhpath%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) += " <pattern id=\"";
+ *(d->defs) += hatchname;
+ *(d->defs) += "\" xlink:href=\"#WMFhbasepattern\">\n";
+ *(d->defs) += refpath;
+ *(d->defs) += " </pattern>\n";
+ idx = d->hatches.count;
+ }
+ }
+ else { // bkMode==U_OPAQUE
+ /* Set up an object in the defs for this background, if there is not one already there */
+ sprintf(bkcolor,"%6.6X",sethexcolor(d->dc[d->level].bkColor));
+ sprintf(hbkname,"WMFhbkclr_%s",bkcolor);
+ idx = in_hatches(d,hbkname);
+ if(!idx){ // add path/color if not already present. Hatchtype is not needed in the name.
+ if(d->hatches.count == d->hatches.size){ enlarge_hatches(d); }
+ d->hatches.strings[d->hatches.count++]=strdup(hbkname);
+
+ *(d->defs) += "\n";
+ *(d->defs) += " <rect id=\"";
+ *(d->defs) += hbkname;
+ *(d->defs) += "\" x=\"0\" y=\"0\" width=\"6\" height=\"6\" fill=\"#";
+ *(d->defs) += bkcolor;
+ *(d->defs) += "\" />\n";
+ }
+
+ // this is the pattern, its name will show up in Inkscape's pattern selector
+ sprintf(hatchname,"WMFhatch%d_%s_%s",hatchType,tmpcolor,bkcolor);
+ idx = in_hatches(d,hatchname);
+ if(!idx){ // add it if not already present
+ if(d->hatches.count == d->hatches.size){ enlarge_hatches(d); }
+ d->hatches.strings[d->hatches.count++]=strdup(hatchname);
+ *(d->defs) += "\n";
+ *(d->defs) += " <pattern id=\"";
+ *(d->defs) += hatchname;
+ *(d->defs) += "\" xlink:href=\"#WMFhbasepattern\">\n";
+ *(d->defs) += " <use xlink:href=\"#";
+ *(d->defs) += hbkname;
+ *(d->defs) += "\" />\n";
+ *(d->defs) += refpath;
+ *(d->defs) += " </pattern>\n";
+ idx = d->hatches.count;
+ }
+ }
+ return(idx-1);
+}
+
+/* Add another 100 blank slots to the images array.
+*/
+void Wmf::enlarge_images(PWMF_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 Wmf::in_images(PWMF_CALLBACK_DATA d, char *test){
+ int i;
+ for(i=0; i<d->images.count; i++){
+ if(strcmp(test,d->images.strings[i])==0)return(i+1);
+ }
+ return(0);
+}
+
+/* (Conditionally) add an image from a DIB. If a matching image already exists nothing happens. If one
+ does not exist it is added to the images list and also entered into <defs>.
+
+*/
+uint32_t Wmf::add_dib_image(PWMF_CALLBACK_DATA d, const char *dib, uint32_t iUsage){
+
+ uint32_t idx;
+ char imagename[64]; // big enough
+ char xywh[64]; // big enough
+ int dibparams = U_BI_UNKNOWN; // type of image not yet determined
+
+ MEMPNG mempng; // PNG in memory comes back in this
+ mempng.buffer = NULL;
+
+ char *rgba_px = NULL; // RGBA pixels
+ const char *px = NULL; // DIB pixels
+ const U_RGBQUAD *ct = NULL; // DIB color table
+ int32_t width, height, colortype, numCt, invert; // if needed these values will be set by wget_DIB_params
+ if(iUsage == U_DIB_RGB_COLORS){
+ // next call returns pointers and values, but allocates no memory
+ dibparams = wget_DIB_params(dib, &px, &ct, &numCt, &width, &height, &colortype, &invert);
+ if(dibparams == U_BI_RGB){
+ if(!DIB_to_RGBA(
+ px, // DIB pixel array
+ ct, // DIB color table
+ numCt, // DIB color table number of entries
+ &rgba_px, // U_RGBA pixel array (32 bits), created by this routine, caller must free.
+ width, // Width of pixel array in record
+ height, // Height of pixel array in record
+ colortype, // DIB BitCount Enumeration
+ numCt, // Color table used if not 0
+ invert // If DIB rows are in opposite order from RGBA rows
+ )){
+ toPNG( // Get the image from the RGBA px into mempng
+ &mempng,
+ width, height, // of the SRC bitmap
+ rgba_px
+ );
+ free(rgba_px);
+ }
+ }
+ }
+
+ gchar *base64String=NULL;
+ if(dibparams == U_BI_JPEG || dibparams==U_BI_PNG){ // image was binary png or jpg in source file
+ base64String = g_base64_encode((guchar*) px, numCt );
+ }
+ else if(mempng.buffer){ // image was DIB in source file, converted to png in this routine
+ base64String = g_base64_encode((guchar*) mempng.buffer, mempng.size );
+ free(mempng.buffer);
+ }
+ else { // failed conversion, insert the common bad image picture
+ width = 3;
+ height = 4;
+ base64String = bad_image_png();
+ }
+ idx = in_images(d, (char *) base64String);
+ if(!idx){ // add it if not already present - we looked at the actual data for comparison
+ if(d->images.count == d->images.size){ enlarge_images(d); }
+ idx = d->images.count;
+ d->images.strings[d->images.count++]=strdup(base64String);
+
+ sprintf(imagename,"WMFimage%d",idx++);
+ sprintf(xywh," x=\"0\" y=\"0\" width=\"%d\" height=\"%d\" ",width,height); // reuse this buffer
+
+ *(d->defs) += "\n";
+ *(d->defs) += " <image id=\"";
+ *(d->defs) += imagename;
+ *(d->defs) += "\"\n ";
+ *(d->defs) += xywh;
+ *(d->defs) += "\n";
+ if(dibparams == U_BI_JPEG){ *(d->defs) += " xlink:href=\"data:image/jpeg;base64,"; }
+ else { *(d->defs) += " xlink:href=\"data:image/png;base64,"; }
+ *(d->defs) += base64String;
+ *(d->defs) += "\"\n";
+ *(d->defs) += " preserveAspectRatio=\"none\"\n";
+ *(d->defs) += " />\n";
+
+
+ *(d->defs) += "\n";
+ *(d->defs) += " <pattern id=\"";
+ *(d->defs) += imagename;
+ *(d->defs) += "_ref\"\n ";
+ *(d->defs) += xywh;
+ *(d->defs) += "\n patternUnits=\"userSpaceOnUse\"";
+ *(d->defs) += " >\n";
+ *(d->defs) += " <use id=\"";
+ *(d->defs) += imagename;
+ *(d->defs) += "_ign\" ";
+ *(d->defs) += " xlink:href=\"#";
+ *(d->defs) += imagename;
+ *(d->defs) += "\" />\n";
+ *(d->defs) += " ";
+ *(d->defs) += " </pattern>\n";
+ }
+ g_free(base64String); //wait until this point to free because it might be a duplicate image
+ return(idx-1);
+}
+
+/* (Conditionally) add an image from a Bitmap16. If a matching image already exists nothing happens. If one
+ does not exist it is added to the images list and also entered into <defs>.
+
+*/
+uint32_t Wmf::add_bm16_image(PWMF_CALLBACK_DATA d, U_BITMAP16 Bm16, const char *px){
+
+ 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
+ const U_RGBQUAD *ct = NULL; // color table, always NULL here
+ int32_t width, height, colortype, numCt, invert;
+ numCt = 0;
+ width = Bm16.Width; // bitmap width in pixels.
+ height = Bm16.Height; // bitmap height in scan lines.
+ colortype = Bm16.BitsPixel; // seems to be BitCount Enumeration
+ invert = 0;
+ if(colortype < 16)return(U_WMR_INVALID); // these would need a colortable if they were a dib, no idea what bm16 is supposed to do instead.
+
+ if(!DIB_to_RGBA(// This is not really a dib, but close enough so that it still works.
+ px, // DIB pixel array
+ ct, // DIB color table (always NULL here)
+ numCt, // DIB color table number of entries (always 0)
+ &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
+ )){
+ toPNG( // Get the image from the RGBA px into mempng
+ &mempng,
+ width, height, // of the SRC bitmap
+ rgba_px
+ );
+ free(rgba_px);
+ }
+
+ gchar *base64String=NULL;
+ if(mempng.buffer){ // image was Bm16 in source file, converted to png in this routine
+ base64String = g_base64_encode((guchar*) mempng.buffer, mempng.size );
+ free(mempng.buffer);
+ }
+ else { // failed conversion, insert the common bad image picture
+ width = 3;
+ height = 4;
+ base64String = bad_image_png();
+ }
+
+ idx = in_images(d, (char *) base64String);
+ if(!idx){ // add it if not already present - we looked at the actual data for comparison
+ if(d->images.count == d->images.size){ enlarge_images(d); }
+ idx = d->images.count;
+ d->images.strings[d->images.count++]=g_strdup(base64String);
+
+ sprintf(imagename,"WMFimage%d",idx++);
+ sprintf(xywh," x=\"0\" y=\"0\" width=\"%d\" height=\"%d\" ",width,height); // reuse this buffer
+
+ *(d->defs) += "\n";
+ *(d->defs) += " <image id=\"";
+ *(d->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) += " preserveAspectRatio=\"none\"\n";
+ *(d->defs) += " />\n";
+
+
+ *(d->defs) += "\n";
+ *(d->defs) += " <pattern id=\"";
+ *(d->defs) += imagename;
+ *(d->defs) += "_ref\"\n ";
+ *(d->defs) += xywh;
+ *(d->defs) += "\n patternUnits=\"userSpaceOnUse\"";
+ *(d->defs) += " >\n";
+ *(d->defs) += " <use id=\"";
+ *(d->defs) += imagename;
+ *(d->defs) += "_ign\" ";
+ *(d->defs) += " xlink:href=\"#";
+ *(d->defs) += imagename;
+ *(d->defs) += "\" />\n";
+ *(d->defs) += " </pattern>\n";
+ }
+ g_free(base64String); //wait until this point to free because it might be a duplicate image
+ return(idx-1);
+}
+
+void
+Wmf::output_style(PWMF_CALLBACK_DATA d)
+{
+// 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_WMR_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 (!d->dc[d->level].fill_set || ( d->mask & U_DRAW_NOFILL)) { // nofill are lines and arcs
+ 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(#WMFimage%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 the stroke is the same as the fill, and the right size not to change the end size of the object, do not do it separately
+ if(
+ (d->dc[d->level].fill_set ) &&
+ (d->dc[d->level].stroke_set ) &&
+ (d->dc[d->level].style.stroke_width.value == 1 ) &&
+ (d->dc[d->level].fill_mode == d->dc[d->level].stroke_mode) &&
+ (
+ (d->dc[d->level].fill_mode != DRAW_PAINT) ||
+ (
+ (fill_rgb[0]==stroke_rgb[0]) &&
+ (fill_rgb[1]==stroke_rgb[1]) &&
+ (fill_rgb[2]==stroke_rgb[2])
+ )
+ )
+ ){
+ d->dc[d->level].stroke_set = false;
+ }
+ }
+
+ if (!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(#WMFimage%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;
+ }
+ if(d->dc[d->level].style.stroke_width.value){
+ tmp_style << "stroke-width:" <<
+ MAX( 0.001, d->dc[d->level].style.stroke_width.value ) << "px;";
+ }
+ else { // In a WMF a 0 width pixel means "1 pixel"
+ tmp_style << "stroke-width:" << pix_to_abs_size( d, 1 ) << "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_dasharray.values.empty())
+ {
+ tmp_style << "stroke-dasharray:";
+ for (unsigned i=0; i<d->dc[d->level].style.stroke_dasharray.values.size(); i++) {
+ if (i)
+ tmp_style << ",";
+ tmp_style << d->dc[d->level].style.stroke_dasharray.values[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(#clipWmfPath" << d->id << ")\" ";
+ clipset = false;
+
+ *(d->outsvg) += tmp_style.str().c_str();
+}
+
+
+double
+Wmf::_pix_x_to_point(PWMF_CALLBACK_DATA d, double px)
+{
+ double scale = (d->dc[d->level].ScaleInX ? d->dc[d->level].ScaleInX : 1.0);
+ double tmp;
+ tmp = ((((double) (px - d->dc[d->level].winorg.x))*scale) + d->dc[d->level].vieworg.x) * d->D2PscaleX;
+ tmp -= d->ulCornerOutX; //The WMF boundary rectangle can be anywhere, place its upper left corner in the Inkscape upper left corner
+ return(tmp);
+}
+
+double
+Wmf::_pix_y_to_point(PWMF_CALLBACK_DATA d, double py)
+{
+ double scale = (d->dc[d->level].ScaleInY ? d->dc[d->level].ScaleInY : 1.0);
+ double tmp;
+ tmp = ((((double) (py - d->dc[d->level].winorg.y))*scale) * d->E2IdirY + d->dc[d->level].vieworg.y) * d->D2PscaleY;
+ tmp -= d->ulCornerOutY; //The WMF boundary rectangle can be anywhere, place its upper left corner in the Inkscape upper left corner
+ return(tmp);
+}
+
+
+double
+Wmf::pix_to_x_point(PWMF_CALLBACK_DATA d, double px, double /*py*/)
+{
+ double x = _pix_x_to_point(d, px);
+ return x;
+}
+
+double
+Wmf::pix_to_y_point(PWMF_CALLBACK_DATA d, double /*px*/, double py)
+{
+ double y = _pix_y_to_point(d, py);
+ return y;
+
+}
+
+double
+Wmf::pix_to_abs_size(PWMF_CALLBACK_DATA d, double px)
+{
+ double ppx = fabs(px * (d->dc[d->level].ScaleInX ? d->dc[d->level].ScaleInX : 1.0) * d->D2PscaleX * current_scale(d));
+ return ppx;
+}
+
+/* returns "x,y" (without the quotes) in inkscape coordinates for a pair of WMF x,y coordinates
+*/
+std::string Wmf::pix_to_xy(PWMF_CALLBACK_DATA d, double x, double y){
+ std::stringstream cxform;
+ cxform << pix_to_x_point(d,x,y);
+ cxform << ",";
+ cxform << pix_to_y_point(d,x,y);
+ return(cxform.str());
+}
+
+
+void
+Wmf::select_pen(PWMF_CALLBACK_DATA d, int index)
+{
+ int width;
+ char *record = NULL;
+ U_PEN up;
+
+ if (index < 0 && index >= d->n_obj){ return; }
+ record = d->wmf_obj[index].record;
+ if(!record){ return; }
+ d->dc[d->level].active_pen = index;
+
+ (void) U_WMRCREATEPENINDIRECT_get(record, &up);
+ width = up.Widthw[0]; // width is stored in the first 16 bits of the 32.
+
+ switch (up.Style & U_PS_STYLE_MASK) {
+ case U_PS_DASH:
+ case U_PS_DOT:
+ case U_PS_DASHDOT:
+ case U_PS_DASHDOTDOT:
+ {
+ int penstyle = (up.Style & U_PS_STYLE_MASK);
+ if (!d->dc[d->level].style.stroke_dasharray.values.empty() && (d->level==0 || (d->level>0 && d->dc[d->level].style.stroke_dasharray.values!=d->dc[d->level-1].style.stroke_dasharray.values)))
+ d->dc[d->level].style.stroke_dasharray.values.clear();
+ if (penstyle==U_PS_DASH || penstyle==U_PS_DASHDOT || penstyle==U_PS_DASHDOTDOT) {
+ d->dc[d->level].style.stroke_dasharray.values.push_back( 3 );
+ d->dc[d->level].style.stroke_dasharray.values.push_back( 1 );
+ }
+ if (penstyle==U_PS_DOT || penstyle==U_PS_DASHDOT || penstyle==U_PS_DASHDOTDOT) {
+ d->dc[d->level].style.stroke_dasharray.values.push_back( 1 );
+ d->dc[d->level].style.stroke_dasharray.values.push_back( 1 );
+ }
+ if (penstyle==U_PS_DASHDOTDOT) {
+ d->dc[d->level].style.stroke_dasharray.values.push_back( 1 );
+ d->dc[d->level].style.stroke_dasharray.values.push_back( 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 (up.Style & 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 (up.Style & 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; }
+ }
+
+
+ double pen_width;
+ if (up.Style == U_PS_NULL) {
+ d->dc[d->level].stroke_set = false;
+ pen_width =0.0;
+ } else if (width) {
+ d->dc[d->level].stroke_set = true;
+ int cur_level = d->level;
+ d->level = d->wmf_obj[index].level; // this object may have been defined in some other DC.
+ pen_width = pix_to_abs_size( d, width );
+ d->level = cur_level;
+ } 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].stroke_set = true;
+ int cur_level = d->level;
+ d->level = d->wmf_obj[index].level; // this object may have been defined in some other DC.
+ pen_width = pix_to_abs_size( 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(up.Color) );
+ g = SP_COLOR_U_TO_F( U_RGBAGetG(up.Color) );
+ b = SP_COLOR_U_TO_F( U_RGBAGetB(up.Color) );
+ d->dc[d->level].style.stroke.value.color.set( r, g, b );
+}
+
+
+void
+Wmf::select_brush(PWMF_CALLBACK_DATA d, int index)
+{
+ uint8_t iType;
+ char *record;
+ const char *membrush;
+
+ if (index < 0 || index >= d->n_obj)return;
+ record = d->wmf_obj[index].record;
+ if(!record)return;
+ d->dc[d->level].active_brush = index;
+
+ iType = *(uint8_t *)(record + offsetof(U_METARECORD, iType ) );
+ if(iType == U_WMR_CREATEBRUSHINDIRECT){
+ U_WLOGBRUSH lb;
+ (void) U_WMRCREATEBRUSHINDIRECT_get(record, &membrush);
+ memcpy(&lb, membrush, U_SIZE_WLOGBRUSH);
+ if(lb.Style == U_BS_SOLID){
+ double r, g, b;
+ r = SP_COLOR_U_TO_F( U_RGBAGetR(lb.Color) );
+ g = SP_COLOR_U_TO_F( U_RGBAGetG(lb.Color) );
+ b = SP_COLOR_U_TO_F( U_RGBAGetB(lb.Color) );
+ 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(lb.Style == U_BS_HATCHED){
+ d->dc[d->level].fill_idx = add_hatch(d, lb.Hatch, lb.Color);
+ d->dc[d->level].fill_recidx = index; // used if the hatch needs to be redone due to bkMode, textmode, etc. changes
+ d->dc[d->level].fill_mode = DRAW_PATTERN;
+ d->dc[d->level].fill_set = true;
+ }
+ else if(lb.Style == U_BS_NULL){
+ d->dc[d->level].fill_mode = DRAW_PAINT; // set it to something
+ d->dc[d->level].fill_set = false;
+ }
+ }
+ else if(iType == U_WMR_DIBCREATEPATTERNBRUSH){
+ uint32_t tidx;
+ uint16_t Style;
+ uint16_t cUsage;
+ const char *Bm16h; // Pointer to Bitmap16 header (px follows)
+ const char *dib; // Pointer to DIB
+ (void) U_WMRDIBCREATEPATTERNBRUSH_get(record, &Style, &cUsage, &Bm16h, &dib);
+ if(dib || Bm16h){
+ if(dib){ tidx = add_dib_image(d, dib, cUsage); }
+ else if(Bm16h){
+ U_BITMAP16 Bm16;
+ const char *px;
+ memcpy(&Bm16, Bm16h, U_SIZE_BITMAP16);
+ px = Bm16h + U_SIZE_BITMAP16;
+ tidx = add_bm16_image(d, Bm16, px);
+ }
+ if(tidx == U_WMR_INVALID){ // Problem with the image, for instance, an unsupported bitmap16 type
+ 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;
+ }
+ else {
+ g_message("Please send WMF file to developers - select_brush U_WMR_DIBCREATEPATTERNBRUSH not bm16 or dib, not handled");
+ }
+ }
+ else if(iType == U_WMR_CREATEPATTERNBRUSH){
+ uint32_t tidx;
+ int cbPx;
+ U_BITMAP16 Bm16h;
+ const char *px;
+ if(U_WMRCREATEPATTERNBRUSH_get(record, &Bm16h, &cbPx, &px)){
+ tidx = add_bm16_image(d, Bm16h, px);
+ if(tidx == 0xFFFFFFFF){ // Problem with the image, for instance, an unsupported bitmap16 type
+ 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;
+ }
+ }
+}
+
+
+void
+Wmf::select_font(PWMF_CALLBACK_DATA d, int index)
+{
+ char *record = NULL;
+ const char *memfont;
+ const char *facename;
+ U_FONT font;
+
+ if (index < 0 || index >= d->n_obj)return;
+ record = d->wmf_obj[index].record;
+ if (!record)return;
+ d->dc[d->level].active_font = index;
+
+
+ (void) U_WMRCREATEFONTINDIRECT_get(record, &memfont);
+ memcpy(&font,memfont,U_SIZE_FONT_CORE); //make sure it is in a properly aligned structure before touching it
+ facename = memfont + U_SIZE_FONT_CORE;
+
+ /* The logfont information always starts with a U_LOGFONT structure but the U_WMRCREATEFONTINDIRECT
+ 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->wmf_obj[index].level;
+ double font_size = pix_to_abs_size( d, font.Height );
+ /* snap the font_size to the nearest 1/32nd of a point.
+ (The size is converted from Pixels to points, snapped, and converted back.)
+ See the notes where d->D2Pscale[XY] are set for the reason why.
+ Typically this will set the font to the desired exact size. If some peculiar size
+ was intended this will, at worst, make it .03125 off, which is unlikely to be a problem. */
+ font_size = round(20.0 * 0.8 * font_size)/(20.0 * 0.8);
+ d->level = cur_level;
+ d->dc[d->level].style.font_size.computed = font_size;
+ d->dc[d->level].style.font_weight.value =
+ font.Weight == U_FW_THIN ? SP_CSS_FONT_WEIGHT_100 :
+ font.Weight == U_FW_EXTRALIGHT ? SP_CSS_FONT_WEIGHT_200 :
+ font.Weight == U_FW_LIGHT ? SP_CSS_FONT_WEIGHT_300 :
+ font.Weight == U_FW_NORMAL ? SP_CSS_FONT_WEIGHT_400 :
+ font.Weight == U_FW_MEDIUM ? SP_CSS_FONT_WEIGHT_500 :
+ font.Weight == U_FW_SEMIBOLD ? SP_CSS_FONT_WEIGHT_600 :
+ font.Weight == U_FW_BOLD ? SP_CSS_FONT_WEIGHT_700 :
+ font.Weight == U_FW_EXTRABOLD ? SP_CSS_FONT_WEIGHT_800 :
+ font.Weight == U_FW_HEAVY ? SP_CSS_FONT_WEIGHT_900 :
+ font.Weight == U_FW_NORMAL ? SP_CSS_FONT_WEIGHT_NORMAL :
+ font.Weight == U_FW_BOLD ? SP_CSS_FONT_WEIGHT_BOLD :
+ font.Weight == U_FW_EXTRALIGHT ? SP_CSS_FONT_WEIGHT_LIGHTER :
+ font.Weight == U_FW_EXTRABOLD ? SP_CSS_FONT_WEIGHT_BOLDER :
+ U_FW_NORMAL;
+ d->dc[d->level].style.font_style.value = (font.Italic ? SP_CSS_FONT_STYLE_ITALIC : SP_CSS_FONT_STYLE_NORMAL);
+ d->dc[d->level].style.text_decoration_line.underline = font.Underline;
+ d->dc[d->level].style.text_decoration_line.line_through = font.StrikeOut;
+ d->dc[d->level].style.text_decoration_line.set = true;
+ d->dc[d->level].style.text_decoration_line.inherit = false;
+
+ // malformed WMF with empty filename may exist, ignore font change if encountered
+ if(d->dc[d->level].font_name)free(d->dc[d->level].font_name);
+ if(*facename){
+ d->dc[d->level].font_name = strdup(facename);
+ }
+ else { // Malformed WMF might specify an empty font name
+ d->dc[d->level].font_name = strdup("Arial"); // Default font, WMF spec says device can pick whatever it wants
+ }
+ d->dc[d->level].style.baseline_shift.value = round((double)((font.Escapement + 3600) % 3600) / 10.0); // use baseline_shift instead of text_transform to avoid overflow
+}
+
+/* Find the first free hole where an object may be stored.
+ If there are not any return -1. This is a big error, possibly from a corrupt WMF file.
+*/
+int Wmf::insertable_object(PWMF_CALLBACK_DATA d)
+{
+ int index = d->low_water; // Start looking from here, it may already have been filled
+ while(index < d->n_obj && d->wmf_obj[index].record != NULL){ index++; }
+ if(index >= d->n_obj)return(-1); // this is a big problem, percolate it back up so the program can get out of this gracefully
+ d->low_water = index; // Could probably be index+1
+ return(index);
+}
+
+void
+Wmf::delete_object(PWMF_CALLBACK_DATA d, int index)
+{
+ if (index >= 0 && index < d->n_obj) {
+ // If the active object is deleted set default draw values
+ if(index == d->dc[d->level].active_pen){ // Use default pen: solid, black, 1 pixel wide
+ d->dc[d->level].active_pen = -1;
+ d->dc[d->level].style.stroke_dasharray.set = 0;
+ d->dc[d->level].style.stroke_linecap.computed = 2; // U_PS_ENDCAP_SQUARE
+ d->dc[d->level].style.stroke_linejoin.computed = 0; // U_PS_JOIN_MITER;
+ d->dc[d->level].stroke_set = true;
+ d->dc[d->level].style.stroke_width.value = 1.0;
+ d->dc[d->level].style.stroke.value.color.set( 0, 0, 0 );
+ }
+ else if(index == d->dc[d->level].active_brush){
+ d->dc[d->level].active_brush = -1;
+ d->dc[d->level].fill_set = false;
+ }
+ else if(index == d->dc[d->level].active_font){
+ d->dc[d->level].active_font = -1;
+ if(d->dc[d->level].font_name){ free(d->dc[d->level].font_name);}
+ d->dc[d->level].font_name = strdup("Arial"); // Default font, WMF spec says device can pick whatever it wants
+ d->dc[d->level].style.font_size.computed = 16.0;
+ d->dc[d->level].style.font_weight.value = SP_CSS_FONT_WEIGHT_400;
+ d->dc[d->level].style.font_style.value = SP_CSS_FONT_STYLE_NORMAL;
+ d->dc[d->level].style.text_decoration_line.underline = 0;
+ d->dc[d->level].style.text_decoration_line.line_through = 0;
+ d->dc[d->level].style.baseline_shift.value = 0;
+ }
+
+
+ d->wmf_obj[index].type = 0;
+// We are keeping a copy of the WMR rather than just a structure. Currently that is not necessary as the entire
+// WMF 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 WMF
+// files too big to fit into memory.
+ if (d->wmf_obj[index].record)
+ free(d->wmf_obj[index].record);
+ d->wmf_obj[index].record = NULL;
+ if(index < d->low_water)d->low_water = index;
+ }
+}
+
+
+// returns the new index, or -1 on error.
+int Wmf::insert_object(PWMF_CALLBACK_DATA d, int type, const char *record)
+{
+ int index = insertable_object(d);
+ if(index>=0){
+ d->wmf_obj[index].type = type;
+ d->wmf_obj[index].level = d->level;
+ d->wmf_obj[index].record = wmr_dup(record);
+ }
+ return(index);
+}
+
+
+/**
+ \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 *Wmf::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<count; i++){ res[i] = 0xFFFD; }
+ res[count]=0;
+ return res;
+}
+
+/**
+ \brief store SVG for an image given the pixmap and various coordinate information
+ \param d
+ \param dib packed DIB in memory
+ \param dx (double) destination x in inkscape pixels
+ \param dy (double) destination y in inkscape pixels
+ \param dw (double) destination width in inkscape pixels
+ \param dh (double) destination height in inkscape pixels
+ \param sx (int) source x in src image pixels
+ \param sy (int) source y in src image pixels
+ \param iUsage
+*/
+void Wmf::common_dib_to_image(PWMF_CALLBACK_DATA d, const char *dib,
+ double dx, double dy, double dw, double dh, int sx, int sy, int sw, int sh, uint32_t iUsage){
+
+ SVGOStringStream tmp_image;
+ int dibparams = U_BI_UNKNOWN; // type of image not yet determined
+
+ tmp_image << "\n\t <image\n";
+ tmp_image << " y=\"" << dy << "\"\n x=\"" << dx <<"\"\n ";
+
+ MEMPNG mempng; // PNG in memory comes back in this
+ mempng.buffer = NULL;
+
+ char *rgba_px = NULL; // RGBA pixels
+ char *sub_px = NULL; // RGBA pixels, subarray
+ const char *px = NULL; // DIB pixels
+ const U_RGBQUAD *ct = NULL; // color table
+ int32_t width, height, colortype, numCt, invert; // if needed these values will be set in wget_DIB_params
+ if(iUsage == U_DIB_RGB_COLORS){
+ // next call returns pointers and values, but allocates no memory
+ dibparams = wget_DIB_params(dib, &px, &ct, &numCt, &width, &height, &colortype, &invert);
+ if(dibparams == U_BI_RGB){
+ if(sw == 0 || sh == 0){
+ sw = width;
+ sh = height;
+ }
+ if(!DIB_to_RGBA(
+ px, // DIB pixel array
+ ct, // DIB color table
+ numCt, // DIB color table number of entries
+ &rgba_px, // U_RGBA pixel array (32 bits), created by this routine, caller must free.
+ width, // Width of pixel array
+ height, // Height of pixel array
+ colortype, // DIB BitCount Enumeration
+ numCt, // Color table used if not 0
+ invert // If DIB rows are in opposite order from RGBA rows
+ )){
+ sub_px = RGBA_to_RGBA( // returns either a subset (side effect: frees rgba_px) or NULL (for subset == entire image)
+ rgba_px, // full pixel array from DIB
+ width, // Width of pixel array
+ height, // Height of pixel array
+ sx,sy, // starting point in pixel array
+ &sw,&sh // columns/rows to extract from the pixel array (output array size)
+ );
+
+ if(!sub_px)sub_px=rgba_px;
+ toPNG( // Get the image from the RGBA px into mempng
+ &mempng,
+ sw, sh, // size of the extracted pixel array
+ sub_px
+ );
+ free(sub_px);
+ }
+ }
+ }
+
+ gchar *base64String=NULL;
+ if(dibparams == U_BI_JPEG){ // image was binary jpg in source file
+ tmp_image << " xlink:href=\"data:image/jpeg;base64,";
+ base64String = g_base64_encode((guchar*) px, numCt );
+ }
+ else if(dibparams==U_BI_PNG){ // image was binary png in source file
+ tmp_image << " xlink:href=\"data:image/png;base64,";
+ base64String = g_base64_encode((guchar*) px, numCt );
+ }
+ else if(mempng.buffer){ // image was DIB in source file, converted to png in this routine
+ tmp_image << " xlink:href=\"data:image/png;base64,";
+ base64String = g_base64_encode((guchar*) mempng.buffer, mempng.size );
+ free(mempng.buffer);
+ }
+ else { // unknown or unsupported image type or failed conversion, insert the common bad image picture
+ tmp_image << " xlink:href=\"data:image/png;base64,";
+ base64String = bad_image_png();
+ }
+ tmp_image << base64String;
+ g_free(base64String);
+
+ tmp_image << "\"\n height=\"" << dh << "\"\n width=\"" << dw << "\"\n";
+ tmp_image << " transform=" << current_matrix(d, 0.0, 0.0, 0); // returns an identity matrix, no offsets.
+ tmp_image << " preserveAspectRatio=\"none\"\n";
+ tmp_image << "/> \n";
+
+ *(d->outsvg) += tmp_image.str().c_str();
+ *(d->path) = "";
+}
+
+/**
+ \brief store SVG for an image given the pixmap and various coordinate information
+ \param d
+ \param Bm16 core Bitmap16 header
+ \param px pointer to Bitmap16 image data
+ \param dx (double) destination x in inkscape pixels
+ \param dy (double) destination y in inkscape pixels
+ \param dw (double) destination width in inkscape pixels
+ \param dh (double) destination height in inkscape pixels
+ \param sx (int) source x in src image pixels
+ \param sy (int) source y in src image pixels
+ \param iUsage
+*/
+void Wmf::common_bm16_to_image(PWMF_CALLBACK_DATA d, U_BITMAP16 Bm16, const char *px,
+ double dx, double dy, double dw, double dh, int sx, int sy, int sw, int sh){
+
+ SVGOStringStream tmp_image;
+
+ tmp_image << "\n\t <image\n";
+ tmp_image << " y=\"" << dy << "\"\n x=\"" << dx <<"\"\n ";
+
+ MEMPNG mempng; // PNG in memory comes back in this
+ mempng.buffer = NULL;
+
+ char *rgba_px = NULL; // RGBA pixels
+ char *sub_px = NULL; // RGBA pixels, subarray
+ const U_RGBQUAD *ct = NULL; // color table
+ int32_t width, height, colortype, numCt, invert;
+
+ numCt = 0;
+ width = Bm16.Width; // bitmap width in pixels.
+ height = Bm16.Height; // bitmap height in scan lines.
+ colortype = Bm16.BitsPixel; // seems to be BitCount Enumeration
+ invert = 0;
+
+ if(sw == 0 || sh == 0){
+ sw = width;
+ sh = height;
+ }
+
+ if(colortype < 16)return; // these would need a colortable if they were a dib, no idea what bm16 is supposed to do instead.
+
+ if(!DIB_to_RGBA(// This is not really a dib, but close enough so that it still works.
+ px, // DIB pixel array
+ ct, // DIB color table (always NULL here)
+ numCt, // DIB color table number of entries (always 0)
+ &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
+ )){
+ sub_px = RGBA_to_RGBA( // returns either a subset (side effect: frees rgba_px) or NULL (for subset == entire image)
+ rgba_px, // full pixel array from DIB
+ width, // Width of pixel array
+ height, // Height of pixel array
+ sx,sy, // starting point in pixel array
+ &sw,&sh // columns/rows to extract from the pixel array (output array size)
+ );
+
+ if(!sub_px)sub_px=rgba_px;
+ toPNG( // Get the image from the RGBA px into mempng
+ &mempng,
+ sw, sh, // size of the extracted pixel array
+ sub_px
+ );
+ free(sub_px);
+ }
+
+ gchar *base64String=NULL;
+ if(mempng.buffer){ // image was Bm16 in source file, converted to png in this routine
+ tmp_image << " xlink:href=\"data:image/png;base64,";
+ base64String = g_base64_encode((guchar*) mempng.buffer, mempng.size );
+ free(mempng.buffer);
+ }
+ else { // unknown or unsupported image type or failed conversion, insert the common bad image picture
+ tmp_image << " xlink:href=\"data:image/png;base64,";
+ base64String = bad_image_png();
+ }
+ tmp_image << base64String;
+ g_free(base64String);
+
+ tmp_image << "\"\n height=\"" << dh << "\"\n width=\"" << dw << "\"\n";
+ tmp_image << " transform=" << current_matrix(d, 0.0, 0.0, 0); // returns an identity matrix, no offsets.
+ tmp_image << " preserveAspectRatio=\"none\"\n";
+ tmp_image << "/> \n";
+
+ *(d->outsvg) += tmp_image.str().c_str();
+ *(d->path) = "";
+}
+
+/**
+ \fn myMetaFileProc(char *contents, unsigned int length, PWMF_CALLBACK_DATA lpData)
+ \returns 1 on success, 0 on error
+ \param contents binary contents of an WMF file
+ \param length length in bytes of contents
+ \param d Inkscape data structures returned by this call
+*/
+//THis was a callback, just build it into a normal function
+int Wmf::myMetaFileProc(const char *contents, unsigned int length, PWMF_CALLBACK_DATA d)
+{
+ uint32_t off=0;
+ uint32_t wmr_mask;
+ int OK =1;
+ TCHUNK_SPECS tsp;
+ uint8_t iType;
+ int nSize; // size of the current record, in bytes, or an error value if <=0
+ const char *blimit = contents + length; // 1 byte past the end of the last record
+
+ /* variables used to retrieve data from WMF records */
+ uint16_t utmp16;
+ U_POINT16 pt16; // any point
+ U_RECT16 rc; // any rectangle, usually a bounding rectangle
+ U_POINT16 Dst; // Destination coordinates
+ U_POINT16 cDst; // Destination w,h, if different from Src
+ U_POINT16 Src; // Source coordinates
+ U_POINT16 cSrc; // Source w,h, if different from Dst
+ U_POINT16 cwh; // w,h, if Src and Dst use the same values
+ uint16_t cUsage; // colorusage enumeration
+ uint32_t dwRop3; // raster operations, these are only barely supported here
+ const char *dib; // DIB style image structure
+ U_BITMAP16 Bm16; // Bitmap16 style image structure
+ const char *px; // Image for Bm16
+ uint16_t cPts; // number of points in the next variable
+ const char *points; // any list of U_POINT16, may not be aligned
+ int16_t tlen; // length of returned text, in bytes
+ const char *text; // returned text, Latin1 encoded
+ uint16_t Opts;
+ const int16_t *dx; // character spacing for one text mode, inkscape ignores this
+ double left, right, top, bottom; // values used, because a bounding rect can have values reversed L<->R, T<->B
+
+ uint16_t tbkMode = U_TRANSPARENT; // holds proposed change to bkMode, if text is involved saving these to the DC must wait until the text is written
+ U_COLORREF tbkColor = U_RGB(255, 255, 255); // holds proposed change to bkColor
+
+ /* initialize the tsp for text reassembly */
+ tsp.string = NULL;
+ tsp.ori = 0.0; /* degrees */
+ tsp.fs = 12.0; /* font size */
+ tsp.x = 0.0;
+ tsp.y = 0.0;
+ tsp.boff = 0.0; /* offset to baseline from LL corner of bounding rectangle, changes with fs and taln*/
+ tsp.vadvance = 0.0; /* meaningful only when a complex contains two or more lines */
+ tsp.taln = ALILEFT + ALIBASE;
+ tsp.ldir = LDIR_LR;
+ tsp.spaces = 0; // this field is only used for debugging
+ tsp.color.Red = 0; /* RGB Black */
+ tsp.color.Green = 0; /* RGB Black */
+ tsp.color.Blue = 0; /* RGB Black */
+ tsp.color.Reserved = 0; /* not used */
+ tsp.italics = 0;
+ tsp.weight = 80;
+ tsp.decoration = TXTDECOR_NONE;
+ tsp.condensed = 100;
+ tsp.co = 0;
+ tsp.fi_idx = -1; /* set to an invalid */
+ tsp.rt_tidx = -1; /* set to an invalid */
+
+ SVGOStringStream dbg_str;
+
+ /* There is very little information in WMF headers, get what is there. In many cases pretty much everything will have to
+ default. If there is no placeable header we know pretty much nothing about the size of the page, in which case
+ assume that it is 1440 WMF pixels/inch and make the page A4 landscape. That is almost certainly the wrong page size
+ but it has to be set to something, and nothing horrible happens if the drawing goes off the page. */
+ {
+
+ U_WMRPLACEABLE Placeable;
+ U_WMRHEADER Header;
+ off = 0;
+ nSize = wmfheader_get(contents, blimit, &Placeable, &Header);
+ if(!nSize)return(0);
+ if(!Header.nObjects){ Header.nObjects = 256; }// there _may_ be WMF files with no objects, more likely it is corrupt. Try to use it anyway.
+ d->n_obj = Header.nObjects;
+ d->wmf_obj = new WMF_OBJECT[d->n_obj];
+ d->low_water = 0; // completely empty at this point, so start searches at 0
+
+ // Init the new wmf_obj list elements to null, provided the
+ // dynamic allocation succeeded.
+ if ( d->wmf_obj != NULL )
+ {
+ for( int i=0; i < d->n_obj; ++i )
+ d->wmf_obj[i].record = NULL;
+ } //if
+
+ if(!Placeable.Inch){ Placeable.Inch= 1440; }
+ if(!Placeable.Dst.right && !Placeable.Dst.left){ // no page size has been supplied
+ // This is gross, scan forward looking for a SETWINDOWEXT record, use the first one found to
+ // define the page size
+ int hold_nSize = off = nSize;
+ Placeable.Dst.left = 0;
+ Placeable.Dst.top = 0;
+ while(OK){
+ nSize = U_WMRRECSAFE_get(contents + off, blimit);
+ if(nSize){
+ iType = *(uint8_t *)(contents + off + offsetof(U_METARECORD, iType ) );
+ if(iType == U_WMR_SETWINDOWEXT){
+ OK=0;
+ (void) U_WMRSETWINDOWEXT_get(contents + off, &Dst);
+ Placeable.Dst.right = Dst.x;
+ Placeable.Dst.bottom = Dst.y;
+ }
+ else if(iType == U_WMR_EOF){
+ OK=0;
+ // Really messed up WMF, have to set the page to something, make it A4 horizontal
+ Placeable.Dst.right = round(((double) Placeable.Inch) * 297.0/25.4);
+ Placeable.Dst.bottom = round(((double) Placeable.Inch) * 210.0/25.4);
+ }
+ else {
+ off += nSize;
+ }
+ }
+ }
+ off=0;
+ nSize = hold_nSize;
+ OK=1;
+ }
+
+ // drawing size in WMF pixels
+ d->PixelsInX = Placeable.Dst.right - Placeable.Dst.left + 1;
+ d->PixelsInY = Placeable.Dst.bottom - Placeable.Dst.top + 1;
+
+ /*
+ Set values for Window and ViewPort extents to 0 - not defined yet.
+ */
+ d->dc[d->level].sizeView.x = d->dc[d->level].sizeWnd.x = 0;
+ d->dc[d->level].sizeView.y = d->dc[d->level].sizeWnd.y = 0;
+
+ /* Upper left corner in device units, usually both 0, but not always.
+ If a placeable header is used, and later a windoworg/windowext are found, then
+ the placeable information will be ignored.
+ */
+ d->ulCornerInX = Placeable.Dst.left;
+ d->ulCornerInY = Placeable.Dst.top;
+
+ d->E2IdirY = 1.0; // assume MM_ANISOTROPIC, if not, this will be changed later
+ d->D2PscaleX = d->D2PscaleY = Inkscape::Util::Quantity::convert(1, "in", "px")/(double) Placeable.Inch;
+ trinfo_load_qe(d->tri, d->D2PscaleX); /* quantization error that will affect text positions */
+
+ // drawing size in Inkscape pixels
+ d->PixelsOutX = d->PixelsInX * d->D2PscaleX;
+ d->PixelsOutY = d->PixelsInY * d->D2PscaleY;
+
+ // Upper left corner in Inkscape units
+ d->ulCornerOutX = d->ulCornerInX * d->D2PscaleX;
+ d->ulCornerOutY = d->ulCornerInY * d->E2IdirY * d->D2PscaleY;
+
+ d->dc[0].style.stroke_width.value = pix_to_abs_size( d, 1 ); // This could not be set until the size of the WMF was known
+ dbg_str << "<!-- U_WMR_HEADER -->\n";
+
+ *(d->outdef) += "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n";
+
+ SVGOStringStream tmp_outdef;
+ tmp_outdef << "<svg\n";
+ tmp_outdef << " xmlns:svg=\"http://www.w3.org/2000/svg\"\n";
+ tmp_outdef << " xmlns=\"http://www.w3.org/2000/svg\"\n";
+ tmp_outdef << " xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n";
+ tmp_outdef << " xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\"\n"; // needed for sodipodi:role
+ tmp_outdef << " version=\"1.0\"\n";
+
+ tmp_outdef <<
+ " width=\"" << Inkscape::Util::Quantity::convert(d->PixelsOutX, "px", "mm") << "mm\"\n" <<
+ " height=\"" << Inkscape::Util::Quantity::convert(d->PixelsOutY, "px", "mm") << "mm\">\n";
+ *(d->outdef) += tmp_outdef.str().c_str();
+ *(d->outdef) += "<defs>"; // temporary end of header
+
+ // d->defs holds any defines which are read in.
+
+
+ }
+
+
+
+ while(OK){
+ if(off>=length)return(0); //normally should exit from while after WMREOF sets OK to false.
+ contents += nSize; // pointer to the start of the next record
+ off += nSize; // offset from beginning of buffer to the start of the next record
+
+ SVGOStringStream tmp_path;
+ SVGOStringStream tmp_str;
+
+ /* Check that the current record size is OK, abort if not.
+ Pointer math might wrap, so check both sides of the range.
+ Some of the records will reset this with the same value,others will not
+ return a value at this time. */
+ nSize = U_WMRRECSAFE_get(contents, blimit);
+ if(!nSize)break;
+
+ iType = *(uint8_t *)(contents + offsetof(U_METARECORD, iType ) );
+
+// Uncomment the following to track down toxic records
+// std::cout << "record type: " << (int) iType << " name " << U_wmr_names(iType) << " length: " << nSize << " offset: " << off <<std::endl;
+
+ wmr_mask = U_wmr_properties(iType);
+ if(wmr_mask == U_WMR_INVALID){ throw "Inkscape fatal programming error at U_wmr_properties"; }
+
+/* Uncomment the following to track down text problems */
+//std::cout << "tri->dirty:"<< d->tri->dirty << " wmr_mask: " << std::hex << wmr_mask << std::dec << std::endl;
+
+ // incompatible change to text drawing detected (color or background change) forces out existing text
+ // OR
+ // next record is valid type and forces pending text to be drawn immediately
+ if ((d->dc[d->level].dirty & DIRTY_TEXT) || ((wmr_mask != U_WMR_INVALID) && (wmr_mask & U_DRAW_TEXT) && d->tri->dirty)){
+ TR_layout_analyze(d->tri);
+ TR_layout_2_svg(d->tri);
+ SVGOStringStream ts;
+ ts << d->tri->out;
+ *(d->outsvg) += ts.str().c_str();
+ d->tri = trinfo_clear(d->tri);
+ }
+ if(d->dc[d->level].dirty){ //Apply the delayed background changes, clear the flag
+ d->dc[d->level].bkMode = tbkMode;
+ memcpy(&(d->dc[d->level].bkColor),&tbkColor, sizeof(U_COLORREF));
+
+ if(d->dc[d->level].dirty & DIRTY_TEXT){
+ // U_COLORREF and TRCOLORREF are exactly the same in memory, but the compiler needs some convincing...
+ if(tbkMode == U_TRANSPARENT){ (void) trinfo_load_bk(d->tri, BKCLR_NONE, *(TRCOLORREF *) &tbkColor); }
+ else { (void) trinfo_load_bk(d->tri, BKCLR_LINE, *(TRCOLORREF *) &tbkColor); } // Opaque
+ }
+
+ /* It is possible to have a series of EMF records that would result in
+ the following creating hash patterns which are never used. For instance, if
+ there were a series of records that changed the background color but did nothing
+ else.
+ */
+ if((d->dc[d->level].fill_mode == DRAW_PATTERN) && (d->dc[d->level].dirty & DIRTY_FILL)){
+ select_brush(d, d->dc[d->level].fill_recidx);
+ }
+
+ d->dc[d->level].dirty = 0;
+ }
+
+//std::cout << "BEFORE DRAW logic d->mask: " << std::hex << d->mask << " wmr_mask: " << wmr_mask << std::dec << std::endl;
+/*
+std::cout << "BEFORE DRAW"
+ << " test0 " << ( d->mask & U_DRAW_VISIBLE)
+ << " test1 " << ( d->mask & U_DRAW_FORCE)
+ << " test2 " << (wmr_mask & U_DRAW_ALTERS)
+ << " test3 " << (wmr_mask & U_DRAW_VISIBLE)
+ << " test4 " << !(d->mask & U_DRAW_ONLYTO)
+ << " test5 " << ((d->mask & U_DRAW_ONLYTO) && !(wmr_mask & U_DRAW_ONLYTO) )
+ << std::endl;
+*/
+
+ if(
+ (wmr_mask != U_WMR_INVALID) && // 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
+ (wmr_mask & U_DRAW_ALTERS) || // Next record would alter the drawing environment in some way
+ ( (wmr_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) && !(wmr_mask & U_DRAW_ONLYTO) )// *TO records can only be followed by other *TO records
+ )
+ )
+ )
+ ){
+// std::cout << "PATH DRAW at TOP <<+++++++++++++++++++++++++++++++++++++" << std::endl;
+ *(d->outsvg) += " <path "; // this is the ONLY place <path should be used!!!!
+ output_style(d);
+ *(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 path
+ // reset the flags
+ d->mask = 0;
+ d->drawtype = 0;
+ }
+// std::cout << "AFTER DRAW logic d->mask: " << std::hex << d->mask << " wmr_mask: " << wmr_mask << std::dec << std::endl;
+ switch (iType)
+ {
+ case U_WMR_EOF:
+ {
+ dbg_str << "<!-- U_WMR_EOF -->\n";
+
+ *(d->outsvg) = *(d->outdef) + *(d->defs) + "\n</defs>\n\n" + *(d->outsvg) + "</svg>\n";
+ OK=0;
+ break;
+ }
+ case U_WMR_SETBKCOLOR:
+ {
+ dbg_str << "<!-- U_WMR_SETBKCOLOR -->\n";
+ nSize = U_WMRSETBKCOLOR_get(contents, &tbkColor);
+ if(memcmp(&tbkColor, &(d->dc[d->level].bkColor), sizeof(U_COLORREF))){
+ d->dc[d->level].dirty |= DIRTY_TEXT;
+ if(d->dc[d->level].fill_mode == DRAW_PATTERN){ d->dc[d->level].dirty |= DIRTY_FILL; }
+ tbkMode = d->dc[d->level].bkMode;
+ }
+ break;
+ }
+ case U_WMR_SETBKMODE:{
+ dbg_str << "<!-- U_WMR_SETBKMODE -->\n";
+ nSize = U_WMRSETBKMODE_get(contents, &tbkMode);
+ if(tbkMode != d->dc[d->level].bkMode){
+ d->dc[d->level].dirty |= DIRTY_TEXT;
+ if(tbkMode != d->dc[d->level].bkMode){
+ if(d->dc[d->level].fill_mode == DRAW_PATTERN){ d->dc[d->level].dirty |= DIRTY_FILL; }
+ }
+ memcpy(&tbkColor,&(d->dc[d->level].bkColor),sizeof(U_COLORREF));
+ }
+ break;
+ }
+ case U_WMR_SETMAPMODE:
+ {
+ dbg_str << "<!-- U_WMR_SETMAPMODE -->\n";
+ nSize = U_WMRSETMAPMODE_get(contents, &utmp16);
+ switch (utmp16){
+ case U_MM_TEXT:
+ default:
+ // Use all values from the header.
+ break;
+ /* For all of the following the indicated scale this will be encoded in WindowExtEx/ViewportExtex
+ and show up in ScaleIn[XY]
+ */
+ case U_MM_LOMETRIC: // 1 LU = 0.1 mm,
+ case U_MM_HIMETRIC: // 1 LU = 0.01 mm
+ case U_MM_LOENGLISH: // 1 LU = 0.1 in
+ case U_MM_HIENGLISH: // 1 LU = 0.01 in
+ case U_MM_TWIPS: // 1 LU = 1/1440 in
+ d->E2IdirY = -1.0;
+ // Use d->D2Pscale[XY] values from the header.
+ break;
+ case U_MM_ISOTROPIC: // ScaleIn[XY] should be set elsewhere by SETVIEWPORTEXTEX and SETWINDOWEXTEX
+ case U_MM_ANISOTROPIC:
+ break;
+ }
+ break;
+ }
+ case U_WMR_SETROP2:
+ {
+ dbg_str << "<!-- U_WMR_SETROP2 -->\n";
+ nSize = U_WMRSETROP2_get(contents, &utmp16);
+ d->dwRop2 = utmp16;
+ break;
+ }
+ case U_WMR_SETRELABS: dbg_str << "<!-- U_WMR_SETRELABS -->\n"; break;
+ case U_WMR_SETPOLYFILLMODE:
+ {
+ dbg_str << "<!-- U_WMR_SETPOLYFILLMODE -->\n";
+ nSize = U_WMRSETPOLYFILLMODE_get(contents, &utmp16);
+ d->dc[d->level].style.fill_rule.value = (utmp16 == U_ALTERNATE ? 0 : utmp16 == U_WINDING ? 1 : 0);
+ break;
+ }
+ case U_WMR_SETSTRETCHBLTMODE:
+ {
+ dbg_str << "<!-- U_WMR_SETSTRETCHBLTMODE -->\n";
+ nSize = U_WMRSETSTRETCHBLTMODE_get(contents, &utmp16);
+ BLTmode = utmp16;
+ break;
+ }
+ case U_WMR_SETTEXTCHAREXTRA: dbg_str << "<!-- U_WMR_SETTEXTCHAREXTRA -->\n"; break;
+ case U_WMR_SETTEXTCOLOR:
+ {
+ dbg_str << "<!-- U_WMR_SETTEXTCOLOR -->\n";
+ nSize = U_WMRSETTEXTCOLOR_get(contents, &(d->dc[d->level].textColor));
+ if(tbkMode != d->dc[d->level].bkMode){
+ if(d->dc[d->level].fill_mode == DRAW_PATTERN){ d->dc[d->level].dirty |= DIRTY_FILL; }
+ }
+ // not text_dirty, because multicolored complex text is supported in libTERE
+ break;
+ }
+ case U_WMR_SETTEXTJUSTIFICATION: dbg_str << "<!-- U_WMR_SETTEXTJUSTIFICATION -->\n"; break;
+ case U_WMR_SETWINDOWORG:
+ {
+ dbg_str << "<!-- U_WMR_SETWINDOWORG -->\n";
+ nSize = U_WMRSETWINDOWORG_get(contents, &d->dc[d->level].winorg);
+ d->ulCornerOutX = 0.0; // In the examples seen to date if this record is used with a placeable header, that header is ignored
+ d->ulCornerOutY = 0.0;
+ break;
+ }
+ case U_WMR_SETWINDOWEXT:
+ {
+ dbg_str << "<!-- U_WMR_SETWINDOWEXT -->\n";
+
+ nSize = U_WMRSETWINDOWEXT_get(contents, &d->dc[d->level].sizeWnd);
+
+ if (!d->dc[d->level].sizeWnd.x || !d->dc[d->level].sizeWnd.y) {
+ d->dc[d->level].sizeWnd = d->dc[d->level].sizeView;
+ if (!d->dc[d->level].sizeWnd.x || !d->dc[d->level].sizeWnd.y) {
+ d->dc[d->level].sizeWnd.x = d->PixelsOutX;
+ d->dc[d->level].sizeWnd.y = d->PixelsOutY;
+ }
+ }
+ else {
+ /* There are a lot WMF files in circulation with the x,y values in the setwindowext reversed. If this is detected, swap them.
+ There is a remote possibility that the strange scaling this implies was intended, and those will be rendered incorrectly */
+ double Ox = d->PixelsOutX;
+ double Oy = d->PixelsOutY;
+ double Wx = d->dc[d->level].sizeWnd.x;
+ double Wy = d->dc[d->level].sizeWnd.y;
+ if(Wx != Wy && Geom::are_near(Ox/Wy, Oy/Wx, 1.01/MIN(Wx,Wy)) ){
+ int tmp;
+ tmp = d->dc[d->level].sizeWnd.x;
+ d->dc[d->level].sizeWnd.x = d->dc[d->level].sizeWnd.y;
+ d->dc[d->level].sizeWnd.y = tmp;
+ }
+ }
+
+ if (!d->dc[d->level].sizeView.x || !d->dc[d->level].sizeView.y) {
+ /* Previously it used sizeWnd, but that always resulted in scale = 1 if no viewport ever appeared, and in most files, it did not */
+ d->dc[d->level].sizeView.x = d->PixelsInX - 1;
+ d->dc[d->level].sizeView.y = d->PixelsInY - 1;
+ }
+
+ /* scales logical to WMF pixels, transfer a negative sign on Y, if any */
+ if (d->dc[d->level].sizeWnd.x && d->dc[d->level].sizeWnd.y) {
+ d->dc[d->level].ScaleInX = (double) d->dc[d->level].sizeView.x / (double) d->dc[d->level].sizeWnd.x;
+ d->dc[d->level].ScaleInY = (double) d->dc[d->level].sizeView.y / (double) d->dc[d->level].sizeWnd.y;
+ if(d->dc[d->level].ScaleInY < 0){
+ d->dc[d->level].ScaleInY *= -1.0;
+ d->E2IdirY = -1.0;
+ }
+ }
+ else {
+ d->dc[d->level].ScaleInX = 1;
+ d->dc[d->level].ScaleInY = 1;
+ }
+ break;
+ }
+ case U_WMR_SETVIEWPORTORG:
+ {
+ dbg_str << "<!-- U_WMR_SETWINDOWORG -->\n";
+ nSize = U_WMRSETVIEWPORTORG_get(contents, &d->dc[d->level].vieworg);
+ break;
+ }
+ case U_WMR_SETVIEWPORTEXT:
+ {
+ dbg_str << "<!-- U_WMR_SETVIEWPORTEXTEX -->\n";
+
+ nSize = U_WMRSETVIEWPORTEXT_get(contents, &d->dc[d->level].sizeView);
+
+ if (!d->dc[d->level].sizeView.x || !d->dc[d->level].sizeView.y) {
+ d->dc[d->level].sizeView = d->dc[d->level].sizeWnd;
+ if (!d->dc[d->level].sizeView.x || !d->dc[d->level].sizeView.y) {
+ d->dc[d->level].sizeView.x = d->PixelsOutX;
+ d->dc[d->level].sizeView.y = d->PixelsOutY;
+ }
+ }
+
+ if (!d->dc[d->level].sizeWnd.x || !d->dc[d->level].sizeWnd.y) {
+ d->dc[d->level].sizeWnd = d->dc[d->level].sizeView;
+ }
+
+ /* scales logical to WMF pixels, transfer a negative sign on Y, if any */
+ if (d->dc[d->level].sizeWnd.x && d->dc[d->level].sizeWnd.y) {
+ d->dc[d->level].ScaleInX = (double) d->dc[d->level].sizeView.x / (double) d->dc[d->level].sizeWnd.x;
+ d->dc[d->level].ScaleInY = (double) d->dc[d->level].sizeView.y / (double) d->dc[d->level].sizeWnd.y;
+ if(d->dc[d->level].ScaleInY < 0){
+ d->dc[d->level].ScaleInY *= -1.0;
+ d->E2IdirY = -1.0;
+ }
+ }
+ else {
+ d->dc[d->level].ScaleInX = 1;
+ d->dc[d->level].ScaleInY = 1;
+ }
+ break;
+ }
+ case U_WMR_OFFSETWINDOWORG: dbg_str << "<!-- U_WMR_OFFSETWINDOWORG -->\n"; break;
+ case U_WMR_SCALEWINDOWEXT: dbg_str << "<!-- U_WMR_SCALEWINDOWEXT -->\n"; break;
+ case U_WMR_OFFSETVIEWPORTORG: dbg_str << "<!-- U_WMR_OFFSETVIEWPORTORG -->\n"; break;
+ case U_WMR_SCALEVIEWPORTEXT: dbg_str << "<!-- U_WMR_SCALEVIEWPORTEXT -->\n"; break;
+ case U_WMR_LINETO:
+ {
+ dbg_str << "<!-- U_WMR_LINETO -->\n";
+
+ nSize = U_WMRLINETO_get(contents, &pt16);
+
+ d->mask |= wmr_mask;
+
+ tmp_path << "\n\tL " << pix_to_xy( d, pt16.x, pt16.y) << " ";
+ break;
+ }
+ case U_WMR_MOVETO:
+ {
+ dbg_str << "<!-- U_WMR_MOVETO -->\n";
+
+ nSize = U_WMRLINETO_get(contents, &pt16);
+
+ d->mask |= wmr_mask;
+
+ d->dc[d->level].cur = pt16;
+
+ tmp_path <<
+ "\n\tM " << pix_to_xy( d, pt16.x, pt16.y ) << " ";
+ break;
+ }
+ case U_WMR_EXCLUDECLIPRECT: dbg_str << "<!-- U_WMR_EXCLUDECLIPRECT -->\n"; break;
+ case U_WMR_INTERSECTCLIPRECT:
+ {
+ dbg_str << "<!-- U_WMR_INTERSECTCLIPRECT -->\n";
+
+ nSize = U_WMRINTERSECTCLIPRECT_get(contents, &rc);
+ 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 dx = pix_to_x_point( d, rc.left, rc.top );
+ double dy = pix_to_y_point( d, rc.left, rc.top );
+ double dw = pix_to_abs_size( d, rc.right - rc.left + 1);
+ double dh = pix_to_abs_size( d, rc.bottom - rc.top + 1);
+
+ SVGOStringStream tmp_rectangle;
+ tmp_rectangle << "\n<clipPath\n\tclipPathUnits=\"userSpaceOnUse\" ";
+ tmp_rectangle << "\nid=\"clipWmfPath" << ++(d->id) << "\" >";
+ tmp_rectangle << "\n<rect ";
+ tmp_rectangle << "\n x=\"" << dx << "\" ";
+ tmp_rectangle << "\n y=\"" << dy << "\" ";
+ tmp_rectangle << "\n width=\"" << dw << "\" ";
+ tmp_rectangle << "\n height=\"" << dh << "\" />";
+ tmp_rectangle << "\n</clipPath>";
+
+ *(d->outdef) += tmp_rectangle.str().c_str();
+ *(d->path) = "";
+ break;
+ }
+ case U_WMR_ARC:
+ {
+ dbg_str << "<!-- U_WMR_ARC -->\n";
+ U_POINT16 ArcStart, ArcEnd;
+ nSize = U_WMRARC_get(contents, &ArcStart, &ArcEnd, &rc);
+
+ U_PAIRF center,start,end,size;
+ int f1;
+ int f2 = (d->arcdir == U_AD_COUNTERCLOCKWISE ? 0 : 1);
+ int stat = wmr_arc_points(rc, ArcStart, ArcEnd,&f1, f2, &center, &start, &end, &size);
+ if(!stat){
+ tmp_path << "\n\tM " << pix_to_xy(d, start.x, start.y);
+ tmp_path << " A " << pix_to_abs_size(d, size.x)/2.0 << "," << pix_to_abs_size(d, size.y)/2.0;
+ tmp_path << " ";
+ tmp_path << 180.0 * current_rotation(d)/M_PI;
+ tmp_path << " ";
+ tmp_path << " " << f1 << "," << f2 << " ";
+ tmp_path << pix_to_xy(d, end.x, end.y) << " \n";
+ d->mask |= wmr_mask;
+ }
+ else {
+ dbg_str << "<!-- ARC record is invalid -->\n";
+ }
+ break;
+ }
+ case U_WMR_ELLIPSE:
+ {
+ dbg_str << "<!-- U_WMR_ELLIPSE -->\n";
+
+ nSize = U_WMRELLIPSE_get(contents, &rc);
+
+ double cx = pix_to_x_point( d, (rc.left + rc.right)/2.0, (rc.bottom + rc.top)/2.0 );
+ double cy = pix_to_y_point( d, (rc.left + rc.right)/2.0, (rc.bottom + rc.top)/2.0 );
+ double rx = pix_to_abs_size( d, fabs(rc.right - rc.left )/2.0 );
+ double ry = pix_to_abs_size( d, fabs(rc.top - rc.bottom)/2.0 );
+
+ SVGOStringStream tmp_ellipse;
+ tmp_ellipse << "cx=\"" << cx << "\" ";
+ tmp_ellipse << "cy=\"" << cy << "\" ";
+ tmp_ellipse << "rx=\"" << rx << "\" ";
+ tmp_ellipse << "ry=\"" << ry << "\" ";
+
+ d->mask |= wmr_mask;
+
+ *(d->outsvg) += " <ellipse ";
+ output_style(d);
+ *(d->outsvg) += "\n\t";
+ *(d->outsvg) += tmp_ellipse.str().c_str();
+ *(d->outsvg) += "/> \n";
+ *(d->path) = "";
+ break;
+ }
+ case U_WMR_FLOODFILL: dbg_str << "<!-- U_WMR_EXTFLOODFILL -->\n"; break;
+ case U_WMR_PIE:
+ {
+ dbg_str << "<!-- U_WMR_PIE -->\n";
+ U_POINT16 ArcStart, ArcEnd;
+ nSize = U_WMRPIE_get(contents, &ArcStart, &ArcEnd, &rc);
+ U_PAIRF center,start,end,size;
+ int f1;
+ int f2 = (d->arcdir == U_AD_COUNTERCLOCKWISE ? 0 : 1);
+ if(!wmr_arc_points(rc, ArcStart, ArcEnd, &f1, f2, &center, &start, &end, &size)){
+ tmp_path << "\n\tM " << pix_to_xy(d, center.x, center.y);
+ tmp_path << "\n\tL " << pix_to_xy(d, start.x, start.y);
+ tmp_path << " A " << pix_to_abs_size(d, size.x)/2.0 << "," << pix_to_abs_size(d, size.y)/2.0;
+ tmp_path << " ";
+ tmp_path << 180.0 * current_rotation(d)/M_PI;
+ tmp_path << " ";
+ tmp_path << " " << f1 << "," << f2 << " ";
+ tmp_path << pix_to_xy(d, end.x, end.y) << " \n";
+ tmp_path << " z ";
+ d->mask |= wmr_mask;
+ }
+ else {
+ dbg_str << "<!-- PIE record is invalid -->\n";
+ }
+ break;
+ }
+ case U_WMR_RECTANGLE:
+ {
+ dbg_str << "<!-- U_WMR_RECTANGLE -->\n";
+
+ nSize = U_WMRRECTANGLE_get(contents, &rc);
+ U_sanerect16(rc, &left, &top, &right, &bottom);
+
+ SVGOStringStream tmp_rectangle;
+ tmp_rectangle << "\n\tM " << pix_to_xy( d, left , top ) << " ";
+ tmp_rectangle << "\n\tL " << pix_to_xy( d, right, top ) << " ";
+ tmp_rectangle << "\n\tL " << pix_to_xy( d, right, bottom ) << " ";
+ tmp_rectangle << "\n\tL " << pix_to_xy( d, left, bottom ) << " ";
+ tmp_rectangle << "\n\tz";
+
+ d->mask |= wmr_mask;
+
+ tmp_path << tmp_rectangle.str().c_str();
+ break;
+ }
+ case U_WMR_ROUNDRECT:
+ {
+ dbg_str << "<!-- U_WMR_ROUNDRECT -->\n";
+
+ int16_t Height,Width;
+ nSize = U_WMRROUNDRECT_get(contents, &Width, &Height, &rc);
+ U_sanerect16(rc, &left, &top, &right, &bottom);
+ double f = 4.*(sqrt(2) - 1)/3;
+ double f1 = 1.0 - f;
+ double cnx = Width/2;
+ double cny = Height/2;
+
+
+ SVGOStringStream tmp_rectangle;
+ tmp_rectangle << "\n"
+ << " M "
+ << pix_to_xy(d, left , top + cny )
+ << "\n";
+ tmp_rectangle << " C "
+ << pix_to_xy(d, left , top + cny*f1 )
+ << " "
+ << pix_to_xy(d, left + cnx*f1 , top )
+ << " "
+ << pix_to_xy(d, left + cnx , top )
+ << "\n";
+ tmp_rectangle << " L "
+ << pix_to_xy(d, right - cnx , top )
+ << "\n";
+ tmp_rectangle << " C "
+ << pix_to_xy(d, right - cnx*f1 , top )
+ << " "
+ << pix_to_xy(d, right , top + cny*f1 )
+ << " "
+ << pix_to_xy(d, right , top + cny )
+ << "\n";
+ tmp_rectangle << " L "
+ << pix_to_xy(d, right , bottom - cny )
+ << "\n";
+ tmp_rectangle << " C "
+ << pix_to_xy(d, right , bottom - cny*f1 )
+ << " "
+ << pix_to_xy(d, right - cnx*f1 , bottom )
+ << " "
+ << pix_to_xy(d, right - cnx , bottom )
+ << "\n";
+ tmp_rectangle << " L "
+ << pix_to_xy(d, left + cnx , bottom )
+ << "\n";
+ tmp_rectangle << " C "
+ << pix_to_xy(d, left + cnx*f1 , bottom )
+ << " "
+ << pix_to_xy(d, left , bottom - cny*f1 )
+ << " "
+ << pix_to_xy(d, left , bottom - cny )
+ << "\n";
+ tmp_rectangle << " z\n";
+
+
+ d->mask |= wmr_mask;
+
+ tmp_path << tmp_rectangle.str().c_str();
+ break;
+ }
+ case U_WMR_PATBLT:
+ {
+ dbg_str << "<!-- U_WMR_PATBLT -->\n";
+ // Treat this like any other rectangle, ie, ignore the dwRop3
+ nSize = U_WMRPATBLT_get(contents, &Dst, &cwh, &dwRop3);
+ SVGOStringStream tmp_rectangle;
+ tmp_rectangle << "\n\tM " << pix_to_xy( d, Dst.x , Dst.y ) << " ";
+ tmp_rectangle << "\n\tL " << pix_to_xy( d, Dst.x + cwh.x, Dst.y ) << " ";
+ tmp_rectangle << "\n\tL " << pix_to_xy( d, Dst.x + cwh.x, Dst.y + cwh.y ) << " ";
+ tmp_rectangle << "\n\tL " << pix_to_xy( d, Dst.x, Dst.y + cwh.y ) << " ";
+ tmp_rectangle << "\n\tz";
+
+ d->mask |= wmr_mask;
+
+ tmp_path << tmp_rectangle.str().c_str();
+ break;
+ }
+ case U_WMR_SAVEDC:
+ {
+ dbg_str << "<!-- U_WMR_SAVEDC -->\n";
+
+ if (d->level < WMF_MAX_DC) {
+ d->dc[d->level + 1] = d->dc[d->level];
+ if(d->dc[d->level].font_name){
+ d->dc[d->level + 1].font_name = strdup(d->dc[d->level].font_name); // or memory access problems because font name pointer duplicated
+ }
+ d->level = d->level + 1;
+ }
+ break;
+ }
+ case U_WMR_SETPIXEL: dbg_str << "<!-- U_WMR_SETPIXEL -->\n"; break;
+ case U_WMR_OFFSETCLIPRGN: dbg_str << "<!-- U_WMR_OFFSETCLIPRGN/POLYLINE -->\n"; break;
+ // U_WMR_TEXTOUT should be here, but has been moved down to merge with U_WMR_EXTTEXTOUT
+ case U_WMR_BITBLT:
+ {
+ dbg_str << "<!-- U_WMR_BITBLT -->\n";
+ nSize = U_WMRBITBLT_get(contents,&Dst,&cwh,&Src,&dwRop3,&Bm16,&px);
+ if(!px){
+ if(dwRop3 == U_NOOP)break; /* GDI applications apparently often end with this as a sort of flush(), nothing should be drawn */
+ int32_t dx = Dst.x;
+ int32_t dy = Dst.y;
+ int32_t dw = cwh.x;
+ int32_t dh = cwh.y;
+ SVGOStringStream tmp_rectangle;
+ tmp_rectangle << "\n\tM " << pix_to_xy( d, dx, dy ) << " ";
+ tmp_rectangle << "\n\tL " << pix_to_xy( d, dx + dw, dy ) << " ";
+ tmp_rectangle << "\n\tL " << pix_to_xy( d, dx + dw, dy + dh ) << " ";
+ tmp_rectangle << "\n\tL " << pix_to_xy( d, dx, dy + dh ) << " ";
+ tmp_rectangle << "\n\tz";
+
+ d->mask |= wmr_mask;
+ d->dwRop3 = dwRop3; // 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();
+ }
+ else { /* Not done yet, Bm16 image present */ }
+ double dx = pix_to_x_point( d, Dst.x, Dst.y);
+ double dy = pix_to_y_point( d, Dst.x, Dst.y);
+ double dw = pix_to_abs_size( d, cwh.x);
+ double dh = pix_to_abs_size( d, cwh.y);
+ //source position within the bitmap, in pixels
+ int sx = Src.x;
+ int sy = Src.y;
+ int sw = 0; // extract all of the image
+ int sh = 0;
+ if(sx<0)sx=0;
+ if(sy<0)sy=0;
+ common_bm16_to_image(d,Bm16,px,dx,dy,dw,dh,sx,sy,sw,sh);
+ break;
+ }
+ case U_WMR_STRETCHBLT:
+ {
+ dbg_str << "<!-- U_WMR_STRETCHBLT -->\n";
+ nSize = U_WMRSTRETCHBLT_get(contents,&Dst,&cDst,&Src,&cSrc,&dwRop3,&Bm16,&px);
+ if(!px){
+ if(dwRop3 == U_NOOP)break; /* GDI applications apparently often end with this as a sort of flush(), nothing should be drawn */
+ int32_t dx = Dst.x;
+ int32_t dy = Dst.y;
+ int32_t dw = cDst.x;
+ int32_t dh = cDst.y;
+ SVGOStringStream tmp_rectangle;
+ tmp_rectangle << "\n\tM " << pix_to_xy( d, dx, dy ) << " ";
+ tmp_rectangle << "\n\tL " << pix_to_xy( d, dx + dw, dy ) << " ";
+ tmp_rectangle << "\n\tL " << pix_to_xy( d, dx + dw, dy + dh ) << " ";
+ tmp_rectangle << "\n\tL " << pix_to_xy( d, dx, dy + dh ) << " ";
+ tmp_rectangle << "\n\tz";
+
+ d->mask |= wmr_mask;
+ d->dwRop3 = dwRop3; // 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();
+ }
+ else { /* Not done yet, Bm16 image present */ }
+ double dx = pix_to_x_point( d, Dst.x, Dst.y);
+ double dy = pix_to_y_point( d, Dst.x, Dst.y);
+ double dw = pix_to_abs_size( d, cDst.x);
+ double dh = pix_to_abs_size( d, cDst.y);
+ //source position within the bitmap, in pixels
+ int sx = Src.x;
+ int sy = Src.y;
+ int sw = cSrc.x; // extract the specified amount of the image
+ int sh = cSrc.y;
+ if(sx<0)sx=0;
+ if(sy<0)sy=0;
+ common_bm16_to_image(d,Bm16,px,dx,dy,dw,dh,sx,sy,sw,sh);
+ break;
+ }
+ case U_WMR_POLYGON:
+ case U_WMR_POLYLINE:
+ {
+ dbg_str << "<!-- U_WMR_POLYGON/POLYLINE -->\n";
+ nSize = U_WMRPOLYGON_get(contents, &cPts, &points);
+ uint32_t i;
+
+ if (cPts < 2)break;
+
+ d->mask |= wmr_mask;
+ memcpy(&pt16,points,U_SIZE_POINT16); points += U_SIZE_POINT16;
+
+ tmp_str << "\n\tM " << pix_to_xy( d, pt16.x, pt16.y) << " ";
+
+ for (i=1; i<cPts; i++) {
+ memcpy(&pt16,points,U_SIZE_POINT16); points+=U_SIZE_POINT16;
+ tmp_str << "\n\tL " << pix_to_xy( d, pt16.x, pt16.y) << " ";
+ }
+
+ tmp_path << tmp_str.str().c_str();
+ if(iType==U_WMR_POLYGON){ tmp_path << " z"; }
+
+ break;
+ }
+ case U_WMR_ESCAPE: // only 3 types of escape are implemented
+ {
+ dbg_str << "<!-- U_WMR_ESCAPE -->\n";
+ uint16_t Escape, elen;
+ nSize = U_WMRESCAPE_get(contents, &Escape, &elen, &text);
+ if(elen>=4){
+ uint32_t utmp4;
+ memcpy(&utmp4, text ,4);
+ if(Escape == U_MFE_SETLINECAP){
+ switch (utmp4 & 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; }
+ }
+ }
+ else if(Escape == U_MFE_SETLINEJOIN){
+ switch (utmp4 & 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; }
+ }
+ }
+ else if(Escape == U_MFE_SETMITERLIMIT){
+ //The function takes a float but uses a 32 bit int in the record.
+ float miterlimit = utmp4;
+ 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_WMR_RESTOREDC:
+ {
+ dbg_str << "<!-- U_WMR_RESTOREDC -->\n";
+
+ int16_t DC;
+ nSize = U_WMRRESTOREDC_get(contents, &DC);
+ int old_level = d->level;
+ if (DC >= 0) {
+ if (DC < d->level)
+ d->level = DC;
+ }
+ else {
+ if (d->level + DC >= 0)
+ d->level = d->level + DC;
+ }
+ while (old_level > d->level) {
+ if (!d->dc[old_level].style.stroke_dasharray.values.empty() && (old_level==0 || (old_level>0 && d->dc[old_level].style.stroke_dasharray.values!=d->dc[old_level-1].style.stroke_dasharray.values))){
+ d->dc[old_level].style.stroke_dasharray.values.clear();
+ }
+ if(d->dc[old_level].font_name){
+ free(d->dc[old_level].font_name); // else memory leak
+ d->dc[old_level].font_name = NULL;
+ }
+ old_level--;
+ }
+ break;
+ }
+ case U_WMR_FILLREGION: dbg_str << "<!-- U_WMR_FILLREGION -->\n"; break;
+ case U_WMR_FRAMEREGION: dbg_str << "<!-- U_WMR_FRAMEREGION -->\n"; break;
+ case U_WMR_INVERTREGION: dbg_str << "<!-- U_WMR_INVERTREGION -->\n"; break;
+ case U_WMR_PAINTREGION: dbg_str << "<!-- U_WMR_PAINTREGION -->\n"; break;
+ case U_WMR_SELECTCLIPREGION:
+ {
+ dbg_str << "<!-- U_WMR_EXTSELECTCLIPRGN -->\n";
+ nSize = U_WMRSELECTCLIPREGION_get(contents, &utmp16);
+ if (utmp16 == U_RGN_COPY)
+ clipset = false;
+ break;
+ }
+ case U_WMR_SELECTOBJECT:
+ {
+ dbg_str << "<!-- U_WMR_SELECTOBJECT -->\n";
+
+ nSize = U_WMRSELECTOBJECT_get(contents, &utmp16);
+ unsigned int index = utmp16;
+
+ // WMF has no stock objects
+ if ( /*index >= 0 &&*/ index < (unsigned int) d->n_obj) {
+ switch (d->wmf_obj[index].type)
+ {
+ case U_WMR_CREATEPENINDIRECT:
+ select_pen(d, index);
+ break;
+ case U_WMR_CREATEBRUSHINDIRECT:
+ case U_WMR_DIBCREATEPATTERNBRUSH:
+ case U_WMR_CREATEPATTERNBRUSH: // <- this one did not display properly on XP, DIBCREATEPATTERNBRUSH works
+ select_brush(d, index);
+ break;
+ case U_WMR_CREATEFONTINDIRECT:
+ select_font(d, index);
+ break;
+ case U_WMR_CREATEPALETTE:
+ case U_WMR_CREATEBITMAPINDIRECT:
+ case U_WMR_CREATEBITMAP:
+ case U_WMR_CREATEREGION:
+ /* these do not do anything, but their objects must be kept in the count */
+ break;
+ }
+ }
+ break;
+ }
+ case U_WMR_SETTEXTALIGN:
+ {
+ dbg_str << "<!-- U_WMR_SETTEXTALIGN -->\n";
+ nSize = U_WMRSETTEXTALIGN_get(contents, &(d->dc[d->level].textAlign));
+ break;
+ }
+ case U_WMR_DRAWTEXT: dbg_str << "<!-- U_WMR_DRAWTEXT -->\n"; break;
+ case U_WMR_CHORD:
+ {
+ dbg_str << "<!-- U_WMR_CHORD -->\n";
+ U_POINT16 ArcStart, ArcEnd;
+ nSize = U_WMRCHORD_get(contents, &ArcStart, &ArcEnd, &rc);
+ U_PAIRF center,start,end,size;
+ int f1;
+ int f2 = (d->arcdir == U_AD_COUNTERCLOCKWISE ? 0 : 1);
+ if(!wmr_arc_points(rc, ArcStart, ArcEnd, &f1, f2, &center, &start, &end, &size)){
+ tmp_path << "\n\tM " << pix_to_xy(d, start.x, start.y);
+ tmp_path << " A " << pix_to_abs_size(d, size.x)/2.0 << "," << pix_to_abs_size(d, size.y)/2.0 ;
+ tmp_path << " ";
+ tmp_path << 180.0 * current_rotation(d)/M_PI;
+ tmp_path << " ";
+ tmp_path << " " << f1 << "," << f2 << " ";
+ tmp_path << pix_to_xy(d, end.x, end.y) << " \n";
+ tmp_path << " z ";
+ d->mask |= wmr_mask;
+ }
+ else {
+ dbg_str << "<!-- CHORD record is invalid -->\n";
+ }
+ break;
+ }
+ case U_WMR_SETMAPPERFLAGS: dbg_str << "<!-- U_WMR_SETMAPPERFLAGS -->\n"; break;
+ case U_WMR_TEXTOUT:
+ case U_WMR_EXTTEXTOUT:
+ {
+ if(iType == U_WMR_TEXTOUT){
+ dbg_str << "<!-- U_WMR_TEXTOUT -->\n";
+ nSize = U_WMRTEXTOUT_get(contents, &Dst, &tlen, &text);
+ Opts=0;
+ }
+ else {
+ dbg_str << "<!-- U_WMR_EXTTEXTOUT -->\n";
+ nSize = U_WMREXTTEXTOUT_get(contents, &Dst, &tlen, &Opts, &text, &dx, &rc );
+ }
+ uint32_t fOptions = Opts;
+
+ double x1,y1;
+ int cChars;
+ x1 = Dst.x;
+ y1 = Dst.y;
+ cChars = tlen;
+
+ 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);
+
+ /* Rotation issues are handled entirely in libTERE now */
+
+ uint32_t *dup_wt = NULL;
+
+ dup_wt = U_Latin1ToUtf32le(text, cChars, NULL);
+ if(!dup_wt)dup_wt = unknown_chars(cChars);
+
+ msdepua(dup_wt); //convert everything in Microsoft's private use area. For Symbol, Wingdings, Dingbats
+
+ if(NonToUnicode(dup_wt, d->dc[d->level].font_name)){
+ free(d->dc[d->level].font_name);
+ d->dc[d->level].font_name = strdup("Times New Roman");
+ }
+
+ char *ansi_text;
+ ansi_text = (char *) U_Utf32leToUtf8((uint32_t *)dup_wt, 0, NULL);
+ free(dup_wt);
+ // Empty text or starts with an invalid escape/control sequence, which is bogus text. Throw it out before g_markup_escape_text can make things worse
+ if(*((uint8_t *)ansi_text) <= 0x1F){
+ free(ansi_text);
+ ansi_text=NULL;
+ }
+
+ if (ansi_text) {
+
+ SVGOStringStream ts;
+
+ gchar *escaped_text = g_markup_escape_text(ansi_text, -1);
+
+ tsp.x = x*0.8; // TERE expects sizes in points
+ tsp.y = y*0.8;
+ tsp.color.Red = d->dc[d->level].textColor.Red;
+ tsp.color.Green = d->dc[d->level].textColor.Green;
+ tsp.color.Blue = d->dc[d->level].textColor.Blue;
+ tsp.color.Reserved = 0;
+ switch(d->dc[d->level].style.font_style.value){
+ case SP_CSS_FONT_STYLE_OBLIQUE:
+ tsp.italics = FC_SLANT_OBLIQUE; break;
+ case SP_CSS_FONT_STYLE_ITALIC:
+ tsp.italics = FC_SLANT_ITALIC; break;
+ default:
+ case SP_CSS_FONT_STYLE_NORMAL:
+ tsp.italics = FC_SLANT_ROMAN; break;
+ }
+ switch(d->dc[d->level].style.font_weight.value){
+ case SP_CSS_FONT_WEIGHT_100: tsp.weight = FC_WEIGHT_THIN ; break;
+ case SP_CSS_FONT_WEIGHT_200: tsp.weight = FC_WEIGHT_EXTRALIGHT ; break;
+ case SP_CSS_FONT_WEIGHT_300: tsp.weight = FC_WEIGHT_LIGHT ; break;
+ case SP_CSS_FONT_WEIGHT_400: tsp.weight = FC_WEIGHT_NORMAL ; break;
+ case SP_CSS_FONT_WEIGHT_500: tsp.weight = FC_WEIGHT_MEDIUM ; break;
+ case SP_CSS_FONT_WEIGHT_600: tsp.weight = FC_WEIGHT_SEMIBOLD ; break;
+ case SP_CSS_FONT_WEIGHT_700: tsp.weight = FC_WEIGHT_BOLD ; break;
+ case SP_CSS_FONT_WEIGHT_800: tsp.weight = FC_WEIGHT_EXTRABOLD ; break;
+ case SP_CSS_FONT_WEIGHT_900: tsp.weight = FC_WEIGHT_HEAVY ; break;
+ case SP_CSS_FONT_WEIGHT_NORMAL: tsp.weight = FC_WEIGHT_NORMAL ; break;
+ case SP_CSS_FONT_WEIGHT_BOLD: tsp.weight = FC_WEIGHT_BOLD ; break;
+ case SP_CSS_FONT_WEIGHT_LIGHTER: tsp.weight = FC_WEIGHT_EXTRALIGHT ; break;
+ case SP_CSS_FONT_WEIGHT_BOLDER: tsp.weight = FC_WEIGHT_EXTRABOLD ; break;
+ default: tsp.weight = FC_WEIGHT_NORMAL ; break;
+ }
+ // WMF only supports two types of text decoration
+ tsp.decoration = TXTDECOR_NONE;
+ if(d->dc[d->level].style.text_decoration_line.underline){ tsp.decoration |= TXTDECOR_UNDER; }
+ if(d->dc[d->level].style.text_decoration_line.line_through){ tsp.decoration |= TXTDECOR_STRIKE;}
+
+ // WMF textalignment is a bit strange: 0x6 is center, 0x2 is right, 0x0 is left, the value 0x4 is also drawn left
+ tsp.taln = ((d->dc[d->level].textAlign & U_TA_CENTER) == U_TA_CENTER) ? ALICENTER :
+ (((d->dc[d->level].textAlign & U_TA_CENTER) == U_TA_LEFT) ? ALILEFT :
+ ALIRIGHT);
+ tsp.taln |= ((d->dc[d->level].textAlign & U_TA_BASEBIT) ? ALIBASE :
+ ((d->dc[d->level].textAlign & U_TA_BOTTOM) ? ALIBOT :
+ ALITOP));
+
+ // language direction can be encoded two ways, U_TA_RTLREADING is preferred
+ if( (fOptions & U_ETO_RTLREADING) || (d->dc[d->level].textAlign & U_TA_RTLREADING) ){ tsp.ldir = LDIR_RL; }
+ else{ tsp.ldir = LDIR_LR; }
+
+ tsp.condensed = FC_WIDTH_NORMAL; // Not implemented well in libTERE (yet)
+ tsp.ori = d->dc[d->level].style.baseline_shift.value; // For now orientation is always the same as escapement
+ // There is no world transform, so ori need not be further rotated
+ tsp.string = (uint8_t *) U_strdup(escaped_text); // this will be free'd much later at a trinfo_clear().
+ tsp.fs = d->dc[d->level].style.font_size.computed * 0.8; // Font size in points
+ char *fontspec = TR_construct_fontspec(&tsp, d->dc[d->level].font_name);
+ tsp.fi_idx = ftinfo_load_fontname(d->tri->fti,fontspec);
+ free(fontspec);
+ // when font name includes narrow it may not be set to "condensed". Narrow fonts do not work well anyway though
+ // as the metrics from fontconfig may not match, or the font may not be present.
+ if(0<= TR_findcasesub(d->dc[d->level].font_name, (char *) "Narrow")){ tsp.co=1; }
+ else { tsp.co=0; }
+
+ int status;
+
+ status = trinfo_load_textrec(d->tri, &tsp, tsp.ori,TR_EMFBOT); // ori is actually escapement
+ if(status==-1){ // change of escapement, emit what we have and reset
+ TR_layout_analyze(d->tri);
+ TR_layout_2_svg(d->tri);
+ ts << d->tri->out;
+ *(d->outsvg) += ts.str().c_str();
+ d->tri = trinfo_clear(d->tri);
+ (void) trinfo_load_textrec(d->tri, &tsp, tsp.ori,TR_EMFBOT); // ignore return status, it must work
+ }
+
+ g_free(escaped_text);
+ free(ansi_text);
+ }
+
+ break;
+ }
+ case U_WMR_SETDIBTODEV: dbg_str << "<!-- U_WMR_EXTTEXTOUT -->\n"; break;
+ case U_WMR_SELECTPALETTE: dbg_str << "<!-- U_WMR_SELECTPALETTE -->\n"; break;
+ case U_WMR_REALIZEPALETTE: dbg_str << "<!-- U_WMR_REALIZEPALETTE -->\n"; break;
+ case U_WMR_ANIMATEPALETTE: dbg_str << "<!-- U_WMR_ANIMATEPALETTE -->\n"; break;
+ case U_WMR_SETPALENTRIES: dbg_str << "<!-- U_WMR_SETPALENTRIES -->\n"; break;
+ case U_WMR_POLYPOLYGON:
+ {
+ dbg_str << "<!-- U_WMR_POLYPOLYGON16 -->\n";
+ uint16_t nPolys;
+ const uint16_t *aPolyCounts;
+ const char *Points;
+ int cpts; /* total number of points in Points*/
+ nSize = U_WMRPOLYPOLYGON_get(contents, &nPolys, &aPolyCounts, &Points);
+ int n, i, j;
+
+ d->mask |= wmr_mask;
+
+ U_POINT16 apt;
+ for (n=cpts=0; n < nPolys; n++) { cpts += aPolyCounts[n]; }
+ i = 0; // offset in BYTES
+ cpts *= U_SIZE_POINT16; // limit for offset i, in BYTES
+
+ for (n=0; n < nPolys && i<cpts; n++) {
+ SVGOStringStream poly_path;
+
+ memcpy(&apt, Points + i, U_SIZE_POINT16); // points may not be aligned, copy them this way
+
+ poly_path << "\n\tM " << pix_to_xy( d, apt.x, apt.y) << " ";
+ i += U_SIZE_POINT16;
+
+ for (j=1; j < aPolyCounts[n] && i < cpts; j++) {
+ memcpy(&apt, Points + i, U_SIZE_POINT16); // points may not be aligned, copy them this way
+ poly_path << "\n\tL " << pix_to_xy( d, apt.x, apt.y) << " ";
+ i += U_SIZE_POINT16;
+ }
+
+ tmp_str << poly_path.str().c_str();
+ tmp_str << " z";
+ tmp_str << " \n";
+ }
+
+ tmp_path << tmp_str.str().c_str();
+
+ break;
+ }
+ case U_WMR_RESIZEPALETTE: dbg_str << "<!-- U_WMR_RESIZEPALETTE -->\n"; break;
+ case U_WMR_3A:
+ case U_WMR_3B:
+ case U_WMR_3C:
+ case U_WMR_3D:
+ case U_WMR_3E:
+ case U_WMR_3F:
+ {
+ dbg_str << "<!-- U_WMR_3A..3F -->\n";
+ break;
+ }
+ case U_WMR_DIBBITBLT:
+ {
+ dbg_str << "<!-- U_WMR_DIBBITBLT -->\n";
+ nSize = U_WMRDIBBITBLT_get(contents, &Dst, &cwh, &Src, &dwRop3, &dib);
+
+ // Treat all nonImage bitblts as a rectangular write. Definitely not correct, but at
+ // least it leaves objects where the operations should have been.
+ if (!dib) {
+ // should be an application of a DIBPATTERNBRUSHPT, use a solid color instead
+
+ if(dwRop3 == U_NOOP)break; /* GDI applications apparently often end with this as a sort of flush(), nothing should be drawn */
+ int32_t dx = Dst.x;
+ int32_t dy = Dst.y;
+ int32_t dw = cwh.x;
+ int32_t dh = cwh.y;
+ SVGOStringStream tmp_rectangle;
+ tmp_rectangle << "\n\tM " << pix_to_xy( d, dx, dy ) << " ";
+ tmp_rectangle << "\n\tL " << pix_to_xy( d, dx + dw, dy ) << " ";
+ tmp_rectangle << "\n\tL " << pix_to_xy( d, dx + dw, dy + dh ) << " ";
+ tmp_rectangle << "\n\tL " << pix_to_xy( d, dx, dy + dh ) << " ";
+ tmp_rectangle << "\n\tz";
+
+ d->mask |= wmr_mask;
+ d->dwRop3 = dwRop3; // 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();
+ }
+ else {
+ double dx = pix_to_x_point( d, Dst.x, Dst.y);
+ double dy = pix_to_y_point( d, Dst.x, Dst.y);
+ double dw = pix_to_abs_size( d, cDst.x);
+ double dh = pix_to_abs_size( d, cDst.y);
+ //source position within the bitmap, in pixels
+ int sx = Src.x;
+ int sy = Src.y;
+ int sw = 0; // extract all of the image
+ int sh = 0;
+ if(sx<0)sx=0;
+ if(sy<0)sy=0;
+ // usageSrc not defined, implicitly it must be U_DIB_RGB_COLORS
+ common_dib_to_image(d,dib,dx,dy,dw,dh,sx,sy,sw,sh,U_DIB_RGB_COLORS);
+ }
+ break;
+ }
+ case U_WMR_DIBSTRETCHBLT:
+ {
+ dbg_str << "<!-- U_WMR_DIBSTRETCHBLT -->\n";
+ nSize = U_WMRDIBSTRETCHBLT_get(contents, &Dst, &cDst, &Src, &cSrc, &dwRop3, &dib);
+ // Always grab image, ignore modes.
+ if (dib) {
+ double dx = pix_to_x_point( d, Dst.x, Dst.y);
+ double dy = pix_to_y_point( d, Dst.x, Dst.y);
+ double dw = pix_to_abs_size( d, cDst.x);
+ double dh = pix_to_abs_size( d, cDst.y);
+ //source position within the bitmap, in pixels
+ int sx = Src.x;
+ int sy = Src.y;
+ int sw = cSrc.x; // extract the specified amount of the image
+ int sh = cSrc.y;
+ // usageSrc not defined, implicitly it must be U_DIB_RGB_COLORS
+ common_dib_to_image(d,dib,dx,dy,dw,dh,sx,sy,sw,sh, U_DIB_RGB_COLORS);
+ }
+ break;
+ }
+ case U_WMR_DIBCREATEPATTERNBRUSH:
+ {
+ dbg_str << "<!-- U_WMR_DIBCREATEPATTERNBRUSH -->\n";
+ insert_object(d, U_WMR_DIBCREATEPATTERNBRUSH, contents);
+ break;
+ }
+ case U_WMR_STRETCHDIB:
+ {
+ dbg_str << "<!-- U_WMR_STRETCHDIB -->\n";
+ nSize = U_WMRSTRETCHDIB_get(contents, &Dst, &cDst, &Src, &cSrc, &cUsage, &dwRop3, &dib);
+ double dx = pix_to_x_point( d, Dst.x, Dst.y );
+ double dy = pix_to_y_point( d, Dst.x, Dst.y );
+ double dw = pix_to_abs_size( d, cDst.x);
+ double dh = pix_to_abs_size( d, cDst.y);
+ int sx = Src.x; //source position within the bitmap, in pixels
+ int sy = Src.y;
+ int sw = cSrc.x; // extract the specified amount of the image
+ int sh = cSrc.y;
+ uint32_t iUsageSrc;
+ iUsageSrc = cUsage;
+ common_dib_to_image(d,dib,dx,dy,dw,dh,sx,sy,sw,sh,iUsageSrc);
+
+ break;
+ }
+ case U_WMR_44:
+ case U_WMR_45:
+ case U_WMR_46:
+ case U_WMR_47:
+ {
+ dbg_str << "<!-- U_WMR_44..47 -->\n";
+ break;
+ }
+ case U_WMR_EXTFLOODFILL: dbg_str << "<!-- U_WMR_EXTFLOODFILL -->\n"; break;
+ case U_WMR_49:
+ case U_WMR_4A:
+ case U_WMR_4B:
+ case U_WMR_4C:
+ case U_WMR_4D:
+ case U_WMR_4E:
+ case U_WMR_4F:
+ case U_WMR_50:
+ case U_WMR_51:
+ case U_WMR_52:
+ case U_WMR_53:
+ case U_WMR_54:
+ case U_WMR_55:
+ case U_WMR_56:
+ case U_WMR_57:
+ case U_WMR_58:
+ case U_WMR_59:
+ case U_WMR_5A:
+ case U_WMR_5B:
+ case U_WMR_5C:
+ case U_WMR_5D:
+ case U_WMR_5E:
+ case U_WMR_5F:
+ case U_WMR_60:
+ case U_WMR_61:
+ case U_WMR_62:
+ case U_WMR_63:
+ case U_WMR_64:
+ case U_WMR_65:
+ case U_WMR_66:
+ case U_WMR_67:
+ case U_WMR_68:
+ case U_WMR_69:
+ case U_WMR_6A:
+ case U_WMR_6B:
+ case U_WMR_6C:
+ case U_WMR_6D:
+ case U_WMR_6E:
+ case U_WMR_6F:
+ case U_WMR_70:
+ case U_WMR_71:
+ case U_WMR_72:
+ case U_WMR_73:
+ case U_WMR_74:
+ case U_WMR_75:
+ case U_WMR_76:
+ case U_WMR_77:
+ case U_WMR_78:
+ case U_WMR_79:
+ case U_WMR_7A:
+ case U_WMR_7B:
+ case U_WMR_7C:
+ case U_WMR_7D:
+ case U_WMR_7E:
+ case U_WMR_7F:
+ case U_WMR_80:
+ case U_WMR_81:
+ case U_WMR_82:
+ case U_WMR_83:
+ case U_WMR_84:
+ case U_WMR_85:
+ case U_WMR_86:
+ case U_WMR_87:
+ case U_WMR_88:
+ case U_WMR_89:
+ case U_WMR_8A:
+ case U_WMR_8B:
+ case U_WMR_8C:
+ case U_WMR_8D:
+ case U_WMR_8E:
+ case U_WMR_8F:
+ case U_WMR_90:
+ case U_WMR_91:
+ case U_WMR_92:
+ case U_WMR_93:
+ case U_WMR_94:
+ case U_WMR_95:
+ case U_WMR_96:
+ case U_WMR_97:
+ case U_WMR_98:
+ case U_WMR_99:
+ case U_WMR_9A:
+ case U_WMR_9B:
+ case U_WMR_9C:
+ case U_WMR_9D:
+ case U_WMR_9E:
+ case U_WMR_9F:
+ case U_WMR_A0:
+ case U_WMR_A1:
+ case U_WMR_A2:
+ case U_WMR_A3:
+ case U_WMR_A4:
+ case U_WMR_A5:
+ case U_WMR_A6:
+ case U_WMR_A7:
+ case U_WMR_A8:
+ case U_WMR_A9:
+ case U_WMR_AA:
+ case U_WMR_AB:
+ case U_WMR_AC:
+ case U_WMR_AD:
+ case U_WMR_AE:
+ case U_WMR_AF:
+ case U_WMR_B0:
+ case U_WMR_B1:
+ case U_WMR_B2:
+ case U_WMR_B3:
+ case U_WMR_B4:
+ case U_WMR_B5:
+ case U_WMR_B6:
+ case U_WMR_B7:
+ case U_WMR_B8:
+ case U_WMR_B9:
+ case U_WMR_BA:
+ case U_WMR_BB:
+ case U_WMR_BC:
+ case U_WMR_BD:
+ case U_WMR_BE:
+ case U_WMR_BF:
+ case U_WMR_C0:
+ case U_WMR_C1:
+ case U_WMR_C2:
+ case U_WMR_C3:
+ case U_WMR_C4:
+ case U_WMR_C5:
+ case U_WMR_C6:
+ case U_WMR_C7:
+ case U_WMR_C8:
+ case U_WMR_C9:
+ case U_WMR_CA:
+ case U_WMR_CB:
+ case U_WMR_CC:
+ case U_WMR_CD:
+ case U_WMR_CE:
+ case U_WMR_CF:
+ case U_WMR_D0:
+ case U_WMR_D1:
+ case U_WMR_D2:
+ case U_WMR_D3:
+ case U_WMR_D4:
+ case U_WMR_D5:
+ case U_WMR_D6:
+ case U_WMR_D7:
+ case U_WMR_D8:
+ case U_WMR_D9:
+ case U_WMR_DA:
+ case U_WMR_DB:
+ case U_WMR_DC:
+ case U_WMR_DD:
+ case U_WMR_DE:
+ case U_WMR_DF:
+ case U_WMR_E0:
+ case U_WMR_E1:
+ case U_WMR_E2:
+ case U_WMR_E3:
+ case U_WMR_E4:
+ case U_WMR_E5:
+ case U_WMR_E6:
+ case U_WMR_E7:
+ case U_WMR_E8:
+ case U_WMR_E9:
+ case U_WMR_EA:
+ case U_WMR_EB:
+ case U_WMR_EC:
+ case U_WMR_ED:
+ case U_WMR_EE:
+ case U_WMR_EF:
+ {
+ dbg_str << "<!-- U_WMR_EXTFLOODFILL..EF -->\n";
+ break;
+ }
+ case U_WMR_DELETEOBJECT:
+ {
+ dbg_str << "<!-- U_WMR_DELETEOBJECT -->\n";
+ nSize = U_WMRDELETEOBJECT_get(contents, &utmp16);
+ delete_object(d, utmp16);
+ break;
+ }
+ case U_WMR_F1:
+ case U_WMR_F2:
+ case U_WMR_F3:
+ case U_WMR_F4:
+ case U_WMR_F5:
+ case U_WMR_F6:
+ {
+ dbg_str << "<!-- F1..F6 -->\n";
+ break;
+ }
+ case U_WMR_CREATEPALETTE:
+ {
+ dbg_str << "<!-- U_WMR_CREATEPALETTE -->\n";
+ insert_object(d, U_WMR_CREATEPALETTE, contents);
+ break;
+ }
+ case U_WMR_F8: dbg_str << "<!-- F8 -->\n"; break;
+ case U_WMR_CREATEPATTERNBRUSH:
+ {
+ dbg_str << "<!-- U_WMR_CREATEPATTERNBRUSH -->\n";
+ insert_object(d, U_WMR_CREATEPATTERNBRUSH, contents);
+ break;
+ }
+ case U_WMR_CREATEPENINDIRECT:
+ {
+ dbg_str << "<!-- U_WMR_EXTCREATEPEN -->\n";
+ insert_object(d, U_WMR_CREATEPENINDIRECT, contents);
+ break;
+ }
+ case U_WMR_CREATEFONTINDIRECT:
+ {
+ dbg_str << "<!-- U_WMR_CREATEFONTINDIRECT -->\n";
+ insert_object(d, U_WMR_CREATEFONTINDIRECT, contents);
+ break;
+ }
+ case U_WMR_CREATEBRUSHINDIRECT:
+ {
+ dbg_str << "<!-- U_WMR_CREATEBRUSHINDIRECT -->\n";
+ insert_object(d, U_WMR_CREATEBRUSHINDIRECT, contents);
+ break;
+ }
+ case U_WMR_CREATEBITMAPINDIRECT:
+ {
+ dbg_str << "<!-- U_WMR_CREATEBITMAPINDIRECT -->\n";
+ insert_object(d, U_WMR_CREATEBITMAPINDIRECT, contents);
+ break;
+ }
+ case U_WMR_CREATEBITMAP:
+ {
+ dbg_str << "<!-- U_WMR_CREATEBITMAP -->\n";
+ insert_object(d, U_WMR_CREATEBITMAP, contents);
+ break;
+ }
+ case U_WMR_CREATEREGION:
+ {
+ dbg_str << "<!-- U_WMR_CREATEREGION -->\n";
+ insert_object(d, U_WMR_CREATEREGION, contents);
+ break;
+ }
+ default:
+ dbg_str << "<!-- U_WMR_??? -->\n";
+ break;
+ } //end of switch
+// When testing, uncomment the following to place a comment for each processed WMR record in the SVG
+// *(d->outsvg) += dbg_str.str().c_str();
+ *(d->path) += tmp_path.str().c_str();
+ if(!nSize){ OK=0; std::cout << "nSize == 0, oops!!!" << std::endl; } // There was some problem with this record, it is not safe to continue
+
+ } //end of while
+// When testing, uncomment the following to show the final SVG derived from the WMF
+// std::cout << *(d->outsvg) << std::endl;
+ (void) U_wmr_properties(U_WMR_INVALID); // force the release of the lookup table memory, returned value is irrelevant
+
+ return 1;
+}
+
+void Wmf::free_wmf_strings(WMF_STRINGS name){
+ if(name.count){
+ for(int i=0; i< name.count; i++){ free(name.strings[i]); }
+ free(name.strings);
+ }
+}
+
+SPDocument *
+Wmf::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri )
+{
+
+ WMF_CALLBACK_DATA d;
+
+ memset(&d, 0, sizeof(WMF_CALLBACK_DATA));
+
+ for(int i = 0; i < WMF_MAX_DC+1; i++){ // be sure all values and pointers are empty to start with
+ memset(&(d.dc[i]),0,sizeof(WMF_DEVICE_CONTEXT));
+ }
+ // set default drawing objects, these are active if no object has been selected
+ d.dc[0].active_pen = -1; // -1 when the default is used instead of a selected object
+ d.dc[0].active_brush = -1;
+ d.dc[0].active_font = -1;
+ // Default font, WMF spec says device can pick whatever it wants. WMF files that do not specify a font are unlikely to look very good!
+ d.dc[0].font_name = strdup("Arial");
+ d.dc[0].style.font_size.computed = 16.0;
+ d.dc[0].style.font_weight.value = SP_CSS_FONT_WEIGHT_400;
+ d.dc[0].style.font_style.value = SP_CSS_FONT_STYLE_NORMAL;
+ d.dc[0].style.text_decoration_line.underline = 0;
+ d.dc[0].style.text_decoration_line.line_through = 0;
+ d.dc[0].style.baseline_shift.value = 0;
+ d.dc[0].textColor = U_RGB(0, 0, 0); // default foreground color (black)
+ d.dc[0].bkColor = U_RGB(255, 255, 255); // default background color (white)
+ d.dc[0].bkMode = U_TRANSPARENT;
+ d.dc[0].dirty = 0;
+ // Default pen, WMF files that do not specify a pen are unlikely to look very good!
+ d.dc[0].style.stroke_dasharray.set = 0;
+ d.dc[0].style.stroke_linecap.computed = 2; // U_PS_ENDCAP_SQUARE;
+ d.dc[0].style.stroke_linejoin.computed = 0; // U_PS_JOIN_MITER;
+ d.dc[0].stroke_set = true;
+ d.dc[0].style.stroke_width.value = 1.0; // will be reset to something reasonable once WMF draying size is known
+ d.dc[0].style.stroke.value.color.set( 0, 0, 0 );
+ // Default brush = none, WMF files that do not specify a brush are unlikely to look very good!
+ d.dc[0].fill_set = false;
+
+ 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.E2IdirY = 1.0;
+ d.D2PscaleX = 1.0;
+ d.D2PscaleY = 1.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;
+
+ // set up the size default for patterns in defs. This might not be referenced if there are no patterns defined in the drawing.
+
+ *(d.defs) += "\n";
+ *(d.defs) += " <pattern id=\"WMFhbasepattern\" \n";
+ *(d.defs) += " patternUnits=\"userSpaceOnUse\"\n";
+ *(d.defs) += " width=\"6\" \n";
+ *(d.defs) += " height=\"6\" \n";
+ *(d.defs) += " x=\"0\" \n";
+ *(d.defs) += " y=\"0\"> \n";
+ *(d.defs) += " </pattern> \n";
+
+
+ size_t length;
+ char *contents;
+ if(wmf_readdata(uri, &contents, &length))return(NULL);
+
+ // set up the text reassembly system
+ if(!(d.tri = trinfo_init(NULL)))return(NULL);
+ (void) trinfo_load_ft_opts(d.tri, 1,
+ FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP,
+ FT_KERNING_UNSCALED);
+
+ (void) myMetaFileProc(contents,length, &d);
+ free(contents);
+
+// 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;
+ free_wmf_strings(d.hatches);
+ free_wmf_strings(d.images);
+
+ if (d.wmf_obj) {
+ int i;
+ for (i=0; i<d.n_obj; i++)
+ delete_object(&d, i);
+ delete[] d.wmf_obj;
+ }
+
+ d.dc[0].style.stroke_dasharray.values.clear();
+
+ for(int i=0; i<=d.level;i++){
+ if(d.dc[i].font_name)free(d.dc[i].font_name);
+ }
+
+ d.tri = trinfo_release_except_FC(d.tri);
+
+ // Set viewBox if it doesn't exist
+ if (doc && !doc->getRoot()->viewBox_set) {
+ bool saved = Inkscape::DocumentUndo::getUndoSensitive(doc);
+ Inkscape::DocumentUndo::setUndoSensitive(doc, false);
+
+ doc->ensureUpToDate();
+
+ // Set document unit
+ Inkscape::XML::Node *repr = sp_document_namedview(doc, 0)->getRepr();
+ Inkscape::SVGOStringStream os;
+ Inkscape::Util::Unit const* doc_unit = doc->getWidth().unit;
+ os << doc_unit->abbr;
+ repr->setAttribute("inkscape:document-units", os.str().c_str());
+
+ // Set viewBox
+ doc->setViewBox(Geom::Rect::from_xywh(0, 0, doc->getWidth().value(doc_unit), doc->getHeight().value(doc_unit)));
+ doc->ensureUpToDate();
+
+ // Scale and translate objects
+ double scale = Inkscape::Util::Quantity::convert(1, "px", doc_unit);
+ ShapeEditor::blockSetItem(true);
+ double dh;
+ if(SP_ACTIVE_DOCUMENT){ // for file menu open or import, or paste from clipboard
+ dh = SP_ACTIVE_DOCUMENT->getHeight().value("px");
+ }
+ else { // for open via --file on command line
+ dh = doc->getHeight().value("px");
+ }
+ doc->getRoot()->scaleChildItemsRec(Geom::Scale(scale), Geom::Point(0, dh));
+ ShapeEditor::blockSetItem(false);
+
+ Inkscape::DocumentUndo::setUndoSensitive(doc, saved);
+ }
+
+ return doc;
+}
+
+
+void
+Wmf::init (void)
+{
+ /* WMF in */
+ Inkscape::Extension::build_from_mem(
+ "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
+ "<name>" N_("WMF Input") "</name>\n"
+ "<id>org.inkscape.input.wmf</id>\n"
+ "<input>\n"
+ "<extension>.wmf</extension>\n"
+ "<mimetype>image/x-wmf</mimetype>\n"
+ "<filetypename>" N_("Windows Metafiles (*.wmf)") "</filetypename>\n"
+ "<filetypetooltip>" N_("Windows Metafiles") "</filetypetooltip>\n"
+ "<output_extension>org.inkscape.output.wmf</output_extension>\n"
+ "</input>\n"
+ "</inkscape-extension>", new Wmf());
+
+ /* WMF out */
+ Inkscape::Extension::build_from_mem(
+ "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
+ "<name>" N_("WMF Output") "</name>\n"
+ "<id>org.inkscape.output.wmf</id>\n"
+ "<param name=\"textToPath\" gui-text=\"" N_("Convert texts to paths") "\" type=\"boolean\">true</param>\n"
+ "<param name=\"TnrToSymbol\" gui-text=\"" N_("Map Unicode to Symbol font") "\" type=\"boolean\">true</param>\n"
+ "<param name=\"TnrToWingdings\" gui-text=\"" N_("Map Unicode to Wingdings") "\" type=\"boolean\">true</param>\n"
+ "<param name=\"TnrToZapfDingbats\" gui-text=\"" N_("Map Unicode to Zapf Dingbats") "\" type=\"boolean\">true</param>\n"
+ "<param name=\"UsePUA\" gui-text=\"" N_("Use MS Unicode PUA (0xF020-0xF0FF) for converted characters") "\" type=\"boolean\">false</param>\n"
+ "<param name=\"FixPPTCharPos\" gui-text=\"" N_("Compensate for PPT font bug") "\" type=\"boolean\">false</param>\n"
+ "<param name=\"FixPPTDashLine\" gui-text=\"" N_("Convert dashed/dotted lines to single lines") "\" type=\"boolean\">false</param>\n"
+ "<param name=\"FixPPTGrad2Polys\" gui-text=\"" N_("Convert gradients to colored polygon series") "\" type=\"boolean\">false</param>\n"
+ "<param name=\"FixPPTPatternAsHatch\" gui-text=\"" N_("Map all fill patterns to standard WMF hatches") "\" type=\"boolean\">false</param>\n"
+ "<output>\n"
+ "<extension>.wmf</extension>\n"
+ "<mimetype>image/x-wmf</mimetype>\n"
+ "<filetypename>" N_("Windows Metafile (*.wmf)") "</filetypename>\n"
+ "<filetypetooltip>" N_("Windows Metafile") "</filetypetooltip>\n"
+ "</output>\n"
+ "</inkscape-extension>", new Wmf());
+
+ 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 --git a/src/extension/internal/wmf-inout.h b/src/extension/internal/wmf-inout.h
new file mode 100644
index 000000000..3d23ca749
--- /dev/null
+++ b/src/extension/internal/wmf-inout.h
@@ -0,0 +1,180 @@
+/** @file
+ * @brief Windows Metafile Input/Output
+ */
+/* Authors:
+ * Ulf Erikson <ulferikson@users.sf.net>
+ *
+ * Copyright (C) 2006-2008 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+#ifndef SEEN_EXTENSION_INTERNAL_WMF_H
+#define SEEN_EXTENSION_INTERNAL_WMF_H
+
+#include <libuemf/uwmf.h>
+#include "extension/internal/metafile-inout.h" // picks up PNG
+#include "extension/implementation/implementation.h"
+#include "style.h"
+#include "text_reassemble.h"
+
+namespace Inkscape {
+namespace Extension {
+namespace Internal {
+
+#define DIRTY_NONE 0x00
+#define DIRTY_TEXT 0x01
+#define DIRTY_FILL 0x02
+#define DIRTY_STROKE 0x04 // not used currently
+
+typedef struct {
+ int type;
+ int level;
+ char *record;
+} WMF_OBJECT, *PWMF_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
+} WMF_STRINGS, *PWMF_STRINGS;
+
+typedef struct wmf_device_context {
+ struct SPStyle style;
+ char *font_name;
+ bool stroke_set;
+ int stroke_mode; // enumeration from drawmode, not used if fill_set is not True
+ int stroke_idx; // used with DRAW_PATTERN and DRAW_IMAGE to return the appropriate fill
+ int stroke_recidx;// record used to regenerate hatch when it needs to be redone due to bkmode, textmode, etc. change
+ 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
+ int fill_recidx; // record used to regenerate hatch when it needs to be redone due to bkmode, textmode, etc. change
+ int dirty; // holds the dirty bits for text, stroke, fill
+ int active_pen; // used when the active object is deleted to set the default values, -1 is none active
+ int active_brush; // ditto
+ int active_font; // ditto. also used to hold object number in case font needs to be remade due to textcolor change.
+ U_POINT16 sizeWnd;
+ U_POINT16 sizeView;
+ U_POINT16 winorg;
+ U_POINT16 vieworg;
+ double ScaleInX, ScaleInY;
+ double ScaleOutX, ScaleOutY;
+ uint16_t bkMode;
+ U_COLORREF bkColor;
+ U_COLORREF textColor;
+ uint16_t textAlign;
+ U_POINT16 cur;
+} WMF_DEVICE_CONTEXT, *PWMF_DEVICE_CONTEXT;
+
+#define WMF_MAX_DC 128
+
+
+// like this causes a mysterious crash on the return from Wmf::open
+//typedef struct emf_callback_data {
+// this fixes it, so some confusion between this struct and the one in emf-inout???
+//typedef struct wmf_callback_data {
+// as does this
+typedef struct {
+ Glib::ustring *outsvg;
+ Glib::ustring *path;
+ Glib::ustring *outdef;
+ Glib::ustring *defs;
+
+ WMF_DEVICE_CONTEXT dc[WMF_MAX_DC+1]; // FIXME: This should be dynamic..
+ int level;
+
+ double E2IdirY; // WMF Y direction relative to Inkscape Y direction. Will be negative for MM_LOMETRIC etc.
+ double D2PscaleX,D2PscaleY; // WMF device to Inkscape Page scale.
+ float PixelsInX, PixelsInY; // size of the drawing, in WMF device pixels
+ float PixelsOutX, PixelsOutY; // size of the drawing, in Inkscape pixels
+ double ulCornerInX,ulCornerInY; // Upper left corner, from header rclBounds, in logical units
+ double ulCornerOutX,ulCornerOutY; // Upper left corner, in Inkscape pixels
+ uint32_t mask; // Draw properties
+ int arcdir; // U_AD_COUNTERCLOCKWISE 1 or U_AD_CLOCKWISE 2
+
+ uint32_t dwRop2; // Binary raster operation, 0 if none (use brush/pen unmolested)
+ uint32_t dwRop3; // Ternary raster operation, 0 if none (use brush/pen unmolested)
+
+ unsigned int id;
+ unsigned int drawtype; // one of 0 or U_WMR_FILLPATH, U_WMR_STROKEPATH, U_WMR_STROKEANDFILLPATH
+ // both of these end up in <defs> under the names shown here. These structures allow duplicates to be avoided.
+ WMF_STRINGS hatches; // hold pattern names, all like WMFhatch#_$$$$$$ where # is the WMF hatch code and $$$$$$ is the color
+ WMF_STRINGS images; // hold images, all like Image#, where # is the slot the image lives.
+ TR_INFO *tri; // Text Reassembly data structure
+
+
+ int n_obj;
+ int low_water; // first object slot which _might_ be unoccupied. Everything below is filled.
+ PWMF_OBJECT wmf_obj;
+} WMF_CALLBACK_DATA, *PWMF_CALLBACK_DATA;
+
+class Wmf : public Metafile
+{
+
+public:
+ Wmf(); // Empty constructor
+
+ virtual ~Wmf();//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:
+protected:
+ static void print_document_to_file(SPDocument *doc, const gchar *filename);
+ static double current_scale(PWMF_CALLBACK_DATA d);
+ static std::string current_matrix(PWMF_CALLBACK_DATA d, double x, double y, int useoffset);
+ static double current_rotation(PWMF_CALLBACK_DATA d);
+ static void enlarge_hatches(PWMF_CALLBACK_DATA d);
+ static int in_hatches(PWMF_CALLBACK_DATA d, char *test);
+ static uint32_t add_hatch(PWMF_CALLBACK_DATA d, uint32_t hatchType, U_COLORREF hatchColor);
+ static void enlarge_images(PWMF_CALLBACK_DATA d);
+ static int in_images(PWMF_CALLBACK_DATA d, char *test);
+ static uint32_t add_dib_image(PWMF_CALLBACK_DATA d, const char *dib, uint32_t iUsage);
+ static uint32_t add_bm16_image(PWMF_CALLBACK_DATA d, U_BITMAP16 Bm16, const char *px);
+ static void output_style(PWMF_CALLBACK_DATA d);
+ static double _pix_x_to_point(PWMF_CALLBACK_DATA d, double px);
+ static double _pix_y_to_point(PWMF_CALLBACK_DATA d, double py);
+ static double pix_to_x_point(PWMF_CALLBACK_DATA d, double px, double py);
+ static double pix_to_y_point(PWMF_CALLBACK_DATA d, double px, double py);
+ static double pix_to_abs_size(PWMF_CALLBACK_DATA d, double px);
+ static std::string pix_to_xy(PWMF_CALLBACK_DATA d, double x, double y);
+ static void select_brush(PWMF_CALLBACK_DATA d, int index);
+ static void select_font(PWMF_CALLBACK_DATA d, int index);
+ static void select_pen(PWMF_CALLBACK_DATA d, int index);
+ static int insertable_object(PWMF_CALLBACK_DATA d);
+ static void delete_object(PWMF_CALLBACK_DATA d, int index);
+ static int insert_object(PWMF_CALLBACK_DATA d, int type, const char *record);
+ static uint32_t *unknown_chars(size_t count);
+ static void common_dib_to_image(PWMF_CALLBACK_DATA d, const char *dib,
+ double dx, double dy, double dw, double dh, int sx, int sy, int sw, int sh, uint32_t iUsage);
+ static void common_bm16_to_image(PWMF_CALLBACK_DATA d, U_BITMAP16 Bm16, const char *px,
+ double dx, double dy, double dw, double dh, int sx, int sy, int sw, int sh);
+ static int myMetaFileProc(const char *contents, unsigned int length, PWMF_CALLBACK_DATA d);
+ static void free_wmf_strings(WMF_STRINGS name);
+
+};
+
+} } } /* namespace Inkscape, Extension, Implementation */
+
+
+#endif /* EXTENSION_INTERNAL_WMF_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 --git a/src/extension/internal/wmf-print.cpp b/src/extension/internal/wmf-print.cpp
new file mode 100644
index 000000000..5a552ad83
--- /dev/null
+++ b/src/extension/internal/wmf-print.cpp
@@ -0,0 +1,1578 @@
+/** @file
+ * @brief Windows Metafile printing
+ */
+/* Authors:
+ * Ulf Erikson <ulferikson@users.sf.net>
+ * Jon A. Cruz <jon@joncruz.org>
+ * 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 "util/units.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 "display/cairo-utils.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 "wmf-print.h"
+
+#include <string.h>
+#include <libuemf/symbol_convert.h>
+
+namespace Inkscape {
+namespace Extension {
+namespace Internal {
+
+#define PXPERMETER 2835
+#define MAXDISP 2.0 // This should be set in the output dialog. This is ok for experimenting, no more than 2 pixel deviation. Not actually used at present
+
+
+/* globals */
+static double PX2WORLD; // value set in begin()
+static bool FixPPTCharPos, FixPPTDashLine, FixPPTGrad2Polys, FixPPTPatternAsHatch;
+static WMFTRACK *wt = NULL;
+static WMFHANDLES *wht = NULL;
+
+void PrintWmf::smuggle_adxky_out(const char *string, int16_t **adx, double *ky, int *rtl, int *ndx, float scale)
+{
+ float fdx;
+ int i;
+ int16_t *ladx;
+ const char *cptr = &string[strlen(string) + 1]; // this works because of the first fake terminator
+
+ *adx = NULL;
+ *ky = 0.0; // set a default value
+ sscanf(cptr, "%7d", ndx);
+ if (!*ndx) {
+ return; // this could happen with an empty string
+ }
+ cptr += 7;
+ ladx = (int16_t *) malloc(*ndx * sizeof(int16_t));
+ if (!ladx) {
+ g_error("Out of memory");
+ }
+ *adx = ladx;
+ for (i = 0; i < *ndx; i++, cptr += 7, ladx++) {
+ sscanf(cptr, "%7f", &fdx);
+ *ladx = (int16_t) round(fdx * scale);
+ }
+ cptr++; // skip 2nd fake terminator
+ sscanf(cptr, "%7f", &fdx);
+ *ky = fdx;
+ cptr += 7; // advance over ky and its space
+ sscanf(cptr, "%07d", rtl);
+}
+
+PrintWmf::PrintWmf()
+{
+ // all of the class variables are initialized elsewhere, many in PrintWmf::Begin,
+}
+
+
+unsigned int PrintWmf::setup(Inkscape::Extension::Print * /*mod*/)
+{
+ return TRUE;
+}
+
+
+unsigned int PrintWmf::begin(Inkscape::Extension::Print *mod, SPDocument *doc)
+{
+ char *rec;
+ gchar const *utf8_fn = mod->get_param_string("destination");
+
+ // Typically PX2WORLD is 1200/90, using inkscape's default dpi
+ PX2WORLD = 1200.0 / Inkscape::Util::Quantity::convert(1.0, "in", "px");
+ FixPPTCharPos = mod->get_param_bool("FixPPTCharPos");
+ FixPPTDashLine = mod->get_param_bool("FixPPTDashLine");
+ FixPPTGrad2Polys = mod->get_param_bool("FixPPTGrad2Polys");
+ FixPPTPatternAsHatch = mod->get_param_bool("FixPPTPatternAsHatch");
+
+ (void) wmf_start(utf8_fn, 1000000, 250000, &wt); // Initialize the wt structure
+ (void) wmf_htable_create(128, 128, &wht); // Initialize the wht structure
+
+ // WMF header the only things that can be set are the page size in inches (w,h) and the dpi
+ // width and height in px
+ _width = doc->getWidth().value("px");
+ _height = doc->getHeight().value("px");
+
+ // initialize a few global variables
+ hbrush = hpen = 0;
+ htextalignment = U_TA_BASELINE | U_TA_LEFT;
+ use_stroke = use_fill = simple_shape = usebk = false;
+
+ 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(Inkscape::Util::Quantity::convert(1, "px", "in")); // 90 dpi inside inkscape, wmf file will be 1200 dpi
+
+ /* -1/1200 in next two lines so that WMF read in will write out again at exactly the same size */
+ float dwInchesX = d.width() - 1.0 / 1200.0;
+ float dwInchesY = d.height() - 1.0 / 1200.0;
+ int dwPxX = round(dwInchesX * 1200.0);
+ int dwPxY = round(dwInchesY * 1200.0);
+#if 0
+ float dwInchesX = d.width();
+ float dwInchesY = d.height();
+ int dwPxX = round(d.width() * 1200.0);
+ int dwPxY = round(d.height() * 1200.0);
+#endif
+
+ U_PAIRF *ps = U_PAIRF_set(dwInchesX, dwInchesY);
+ rec = U_WMRHEADER_set(ps, 1200); // Example: drawing is A4 horizontal, 1200 dpi
+ if (!rec) {
+ g_error("Fatal programming error in PrintWmf::begin at WMRSETMAPMODE");
+ }
+ (void) wmf_header_append((U_METARECORD *)rec, wt, 1);
+ free(ps);
+
+ rec = U_WMRSETWINDOWEXT_set(point16_set(dwPxX, dwPxY));
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::begin at WMRSETWINDOWEXT");
+ }
+
+ rec = U_WMRSETWINDOWORG_set(point16_set(0, 0));
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::begin at WMRSETWINDOWORG");
+ }
+
+ rec = U_WMRSETMAPMODE_set(U_MM_ANISOTROPIC);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::begin at WMRSETMAPMODE");
+ }
+
+ /* set some parameters, else the program that reads the WMF may default to other values */
+
+ rec = U_WMRSETBKMODE_set(U_TRANSPARENT);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::begin at U_WMRSETBKMODE");
+ }
+
+ hpolyfillmode = U_WINDING;
+ rec = U_WMRSETPOLYFILLMODE_set(U_WINDING);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::begin at U_WMRSETPOLYFILLMODE");
+ }
+
+ // Text alignment: (only changed if RTL text is encountered )
+ // - (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 WMF text alignment must always be TA_BASELINE|TA_LEFT.
+ rec = U_WMRSETTEXTALIGN_set(U_TA_BASELINE | U_TA_LEFT);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::begin at U_WMRSETTEXTALIGN_set");
+ }
+
+ htextcolor_rgb[0] = htextcolor_rgb[1] = htextcolor_rgb[2] = 0.0;
+ rec = U_WMRSETTEXTCOLOR_set(U_RGB(0, 0, 0));
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::begin at U_WMRSETTEXTCOLOR_set");
+ }
+
+ rec = U_WMRSETROP2_set(U_R2_COPYPEN);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::begin at U_WMRSETROP2");
+ }
+
+ hmiterlimit = 5;
+ rec = wmiterlimit_set(5);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::begin at wmiterlimit_set");
+ }
+
+
+ // create a pen as object 0. We never use it (except by mistake). Its purpose it to make all of the other object indices >=1
+ U_PEN up = U_PEN_set(U_PS_SOLID, 1, colorref_set(0, 0, 0));
+ uint32_t Pen;
+ rec = wcreatepenindirect_set(&Pen, wht, up);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::begin at wcreatepenindirect_set");
+ }
+
+ // create a null pen. If no specific pen is set, this is used
+ up = U_PEN_set(U_PS_NULL, 1, colorref_set(0, 0, 0));
+ rec = wcreatepenindirect_set(&hpen_null, wht, up);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::begin at wcreatepenindirect_set");
+ }
+ destroy_pen(); // make this pen active
+
+ // create a null brush. If no specific brush is set, this is used
+ U_WLOGBRUSH lb = U_WLOGBRUSH_set(U_BS_NULL, U_RGB(0, 0, 0), U_HS_HORIZONTAL);
+ rec = wcreatebrushindirect_set(&hbrush_null, wht, lb);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::begin at wcreatebrushindirect_set");
+ }
+ destroy_brush(); // make this brush active
+
+ return 0;
+}
+
+
+unsigned int PrintWmf::finish(Inkscape::Extension::Print * /*mod*/)
+{
+ char *rec;
+ if (!wt) {
+ return 0;
+ }
+
+ // get rid of null brush
+ rec = wdeleteobject_set(&hbrush_null, wht);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::finish at wdeleteobject_set null brush");
+ }
+
+ // get rid of null pen
+ rec = wdeleteobject_set(&hpen_null, wht);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::finish at wdeleteobject_set null pen");
+ }
+
+ // get rid of object 0, which was a pen that was used to shift the other object indices to >=1.
+ hpen = 0;
+ rec = wdeleteobject_set(&hpen, wht);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::finish at wdeleteobject_set filler object");
+ }
+
+ rec = U_WMREOF_set(); // generate the EOF record
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::finish");
+ }
+ (void) wmf_finish(wt); // Finalize and write out the WMF
+ wmf_free(&wt); // clean up
+ wmf_htable_free(&wht); // clean up
+
+ return 0;
+}
+
+
+unsigned int PrintWmf::comment(Inkscape::Extension::Print * /*module*/, const char * /*comment*/)
+{
+ if (!wt) {
+ return 0;
+ }
+
+ // earlier versions had flush of fill here, but it never executed and was removed
+
+ return 0;
+}
+
+
+// fcolor is defined when gradients are being expanded, it is the color of one stripe or ring.
+int PrintWmf::create_brush(SPStyle const *style, U_COLORREF *fcolor)
+{
+ float rgb[3];
+ char *rec;
+ U_WLOGBRUSH lb;
+ uint32_t brush, fmode;
+ MFDrawMode fill_mode;
+ Inkscape::Pixbuf *pixbuf;
+ uint32_t brushStyle;
+ int hatchType;
+ U_COLORREF hatchColor;
+ U_COLORREF bkColor;
+ uint32_t width = 0; // quiets a harmless compiler warning, initialization not otherwise required.
+ uint32_t height = 0;
+
+ if (!wt) {
+ 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;
+ bkColor = U_RGB(0, 0, 0);
+ 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) {
+ opacity = 0.0; // basically the same as no fill
+ }
+
+ 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, &bkColor);
+ 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
+ }
+
+ 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:
+ // SVG text has no background attribute, so OPAQUE mode ALWAYS cancels after the next draw, otherwise it would mess up future text output.
+ if (usebk) {
+ rec = U_WMRSETBKCOLOR_set(bkColor);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::create_brush at U_WMRSETBKCOLOR_set");
+ }
+ rec = U_WMRSETBKMODE_set(U_OPAQUE);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::create_brush at U_WMRSETBKMODE_set");
+ }
+ }
+ lb = U_WLOGBRUSH_set(brushStyle, hatchColor, hatchType);
+ rec = wcreatebrushindirect_set(&brush, wht, lb);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::create_brush at createbrushindirect_set");
+ }
+ break;
+ case DRAW_IMAGE:
+ char *px;
+ char *rgba_px;
+ uint32_t cbPx;
+ uint32_t colortype;
+ U_RGBQUAD *ct;
+ int numCt;
+ U_BITMAPINFOHEADER Bmih;
+ U_BITMAPINFO *Bmi;
+ rgba_px = (char *) pixbuf->pixels(); // 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 = wcreatedibpatternbrush_srcdib_set(&brush, wht, U_DIB_RGB_COLORS, Bmi, cbPx, px);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::create_brush at createdibpatternbrushpt_set");
+ }
+ free(px);
+ free(Bmi); // ct will be NULL because of colortype
+ break;
+ }
+
+ hbrush = brush; // need this later for destroy_brush
+ rec = wselectobject_set(brush, wht);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::create_brush at wselectobject_set");
+ }
+
+ if (fmode != hpolyfillmode) {
+ hpolyfillmode = fmode;
+ rec = U_WMRSETPOLYFILLMODE_set(fmode);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::create_brush at U_WMRSETPOLYFILLMODE_set");
+ }
+ }
+
+ return 0;
+}
+
+
+void PrintWmf::destroy_brush()
+{
+ char *rec;
+ // WMF lets any object be deleted whenever, and the chips fall where they may...
+ if (hbrush) {
+ rec = wdeleteobject_set(&hbrush, wht);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::destroy_brush");
+ }
+ hbrush = 0;
+ }
+
+ // (re)select the null brush
+
+ rec = wselectobject_set(hbrush_null, wht);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::destroy_brush");
+ }
+}
+
+
+int PrintWmf::create_pen(SPStyle const *style, const Geom::Affine &transform)
+{
+ char *rec = NULL;
+ uint32_t pen;
+ uint32_t penstyle;
+ U_COLORREF penColor;
+ U_PEN up;
+ int modstyle;
+
+ if (!wt) {
+ return 0;
+ }
+
+ // set a default stroke in case we can't figure out a better way to do it
+ penstyle = U_PS_SOLID;
+ modstyle = 0;
+ penColor = U_RGB(0, 0, 0);
+ uint32_t linewidth = 1;
+
+ if (style) { // override some or all of the preceding
+ float rgb[3];
+
+ // WMF does not support hatched, bitmap, or gradient pens, just set the color.
+ sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
+ penColor = U_RGB(255 * rgb[0], 255 * rgb[1], 255 * rgb[2]);
+
+ 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
+ }
+ linewidth = MAX(1, (uint32_t) round(scale * style->stroke_width.computed * PX2WORLD));
+
+ // most WMF readers will ignore linecap and linejoin, but set them anyway. Inkscape itself can read them back in.
+
+ if (style->stroke_linecap.computed == 0) {
+ modstyle |= U_PS_ENDCAP_FLAT;
+ } else if (style->stroke_linecap.computed == 1) {
+ modstyle |= U_PS_ENDCAP_ROUND;
+ } else {
+ modstyle |= U_PS_ENDCAP_SQUARE;
+ }
+
+ if (style->stroke_linejoin.computed == 0) {
+ float miterlimit = style->stroke_miterlimit.value; // This is a ratio.
+ if (miterlimit < 1) {
+ miterlimit = 1;
+ }
+
+ // most WMF readers will ignore miterlimit, but set it anyway. Inkscape itself can read it back in
+ if ((uint32_t)miterlimit != hmiterlimit) {
+ hmiterlimit = (uint32_t)miterlimit;
+ rec = wmiterlimit_set((uint32_t) miterlimit);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::create_pen at wmiterlimit_set");
+ }
+ }
+ modstyle |= U_PS_JOIN_MITER;
+ } else if (style->stroke_linejoin.computed == 1) {
+ modstyle |= U_PS_JOIN_ROUND;
+ } else {
+ modstyle |= U_PS_JOIN_BEVEL;
+ }
+
+ if (!style->stroke_dasharray.values.empty()) {
+ if (!FixPPTDashLine) { // if this is set code elsewhere will break dots/dashes into many smaller lines.
+ penstyle = U_PS_DASH;// userstyle not supported apparently, for now map all Inkscape dot/dash to just dash
+ }
+ }
+
+ }
+
+ up = U_PEN_set(penstyle | modstyle, linewidth, penColor);
+ rec = wcreatepenindirect_set(&pen, wht, up);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::create_pen at wcreatepenindirect_set");
+ }
+
+ rec = wselectobject_set(pen, wht);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::create_pen at wselectobject_set");
+ }
+ hpen = pen; // need this later for destroy_pen
+
+ return 0;
+}
+
+// delete the defined pen object
+void PrintWmf::destroy_pen()
+{
+ char *rec = NULL;
+ // WMF lets any object be deleted whenever, and the chips fall where they may...
+ if (hpen) {
+ rec = wdeleteobject_set(&hpen, wht);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::destroy_pen");
+ }
+ hpen = 0;
+ }
+
+ // (re)select the null pen
+
+ rec = wselectobject_set(hpen_null, wht);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::destroy_pen");
+ }
+}
+
+
+unsigned int PrintWmf::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*/)
+{
+ using Geom::X;
+ using Geom::Y;
+
+ Geom::Affine tf = m_tr_stack.top();
+
+ use_fill = true;
+ use_stroke = false;
+
+ 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 WMF 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 position */
+ 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 {
+ g_error("Fatal programming error in PrintWmf::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.isNone() || style->stroke.noneSet || style->stroke_width.computed == 0.0) ||
+ (!style->stroke_dasharray.values.empty() && FixPPTDashLine) ||
+ !all_closed
+ ) {
+ print_pathv(pathv, fill_transform); // do any fills. side effect: clears fill_pathv
+ use_fill = false;
+ }
+ }
+
+ return 0;
+}
+
+
+unsigned int PrintWmf::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*/)
+{
+
+ char *rec = NULL;
+ Geom::Affine tf = m_tr_stack.top();
+
+ use_stroke = true;
+ // use_fill was set in ::fill, if it is needed, if not, the null brush is used, it should be already set
+
+ if (create_pen(style, tf)) {
+ return 0;
+ }
+
+ if (!style->stroke_dasharray.values.empty() && FixPPTDashLine) {
+ // convert the path, gets its complete length, and then make a new path with parameter length instead of t
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > tmp_pathpw; // pathv-> sbasis
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > tmp_pathpw2; // sbasis using arc length parameter
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > tmp_pathpw3; // new (discontinuous) path, composed of dots/dashes
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > first_frag; // first fragment, will be appended at end
+ int n_dash = style->stroke_dasharray.values.size();
+ 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_dasharray.values[i++];
+ if (elength > tlength) {
+ elength = tlength;
+ }
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > fragment(portion(tmp_pathpw2, slength, elength));
+ if (slength) {
+ tmp_pathpw3.concat(fragment);
+ } else {
+ first_frag = fragment;
+ }
+ slength = elength;
+ slength += style->stroke_dasharray.values[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;
+
+ if (usebk) { // OPAQUE was set, revert to TRANSPARENT
+ usebk = false;
+ rec = U_WMRSETBKMODE_set(U_TRANSPARENT);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::stroke at U_WMRSETBKMODE_set");
+ }
+ }
+
+ return 0;
+}
+
+
+// Draws simple_shapes, those with closed WMR_* 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 PrintWmf::print_simple_shape(Geom::PathVector const &pathv, const Geom::Affine &transform)
+{
+
+ Geom::PathVector pv = pathv_to_linear(pathv * transform, MAXDISP);
+
+ int nodes = 0;
+ int moves = 0;
+ int lines = 0;
+ int curves = 0;
+ char *rec = NULL;
+
+ 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 (&*cit) {
+ curves++;
+ }
+ }
+ }
+
+ if (!nodes) {
+ return false;
+ }
+
+ U_POINT16 *lpPoints = new U_POINT16[moves + lines + curves * 3];
+ int i = 0;
+
+ /** For all Subpaths in the <path> */
+
+ 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<Geom::CubicBezier const *>(&*cit)) {
+ std::vector<Geom::Point> 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) {
+ // pens and brushes already set by caller, do not touch them
+
+ if (polygon) {
+ if (rectangle) {
+ U_RECT16 rcl = U_RECT16_set((U_POINT16) {
+ lpPoints[0].x, lpPoints[0].y
+ }, (U_POINT16) {
+ lpPoints[2].x, lpPoints[2].y
+ });
+ rec = U_WMRRECTANGLE_set(rcl);
+ } else {
+ rec = U_WMRPOLYGON_set(nodes, lpPoints);
+ }
+ } else if (ellipse) {
+ U_RECT16 rcl = U_RECT16_set((U_POINT16) {
+ lpPoints[6].x, lpPoints[3].y
+ }, (U_POINT16) {
+ lpPoints[0].x, lpPoints[9].y
+ });
+ rec = U_WMRELLIPSE_set(rcl);
+ }
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::print_simple_shape at retangle/ellipse/polygon");
+ }
+
+ done = true;
+
+ }
+
+ delete[] lpPoints;
+
+ return done;
+}
+
+/** Some parts based on win32.cpp by Lauris Kaplinski <lauris@kaplinski.com>. 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. Images lose their rotation, one corner stays in the same place.
+ 2. Transparency is lost on export. (A limitation of the WMF 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 PrintWmf::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_rect, /** affine transform only used for defining location and size of rect, for all other tranforms, use the one from m_tr_stack */
+ SPStyle const * /*style*/) /** provides indirect link to image object */
+{
+ double x1, y1, dw, dh;
+ char *rec = NULL;
+ Geom::Affine tf = m_tr_stack.top();
+
+ rec = U_WMRSETSTRETCHBLTMODE_set(U_COLORONCOLOR);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::image at EMRHEADER");
+ }
+
+ x1 = tf_rect[4];
+ y1 = tf_rect[5];
+ dw = ((double) w) * tf_rect[0];
+ dh = ((double) h) * tf_rect[3];
+ Geom::Point pLL(x1, y1);
+ Geom::Point pLL2 = pLL * tf; //location of LL corner in Inkscape coordinates
+
+ /* adjust scale of w and h. This works properly when there is no rotation. The values are
+ a bit strange when there is rotation, but since WMF cannot handle rotation in any case, all
+ answers are equally wrong.
+ */
+ Geom::Point pWH(dw, dh);
+ Geom::Point pWH2 = pWH * tf.withoutTranslation();
+
+ char *px;
+ uint32_t cbPx;
+ uint32_t colortype;
+ U_RGBQUAD *ct;
+ int numCt;
+ U_BITMAPINFOHEADER Bmih;
+ U_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_POINT16 Dest = point16_set(round(pLL2[Geom::X] * PX2WORLD), round(pLL2[Geom::Y] * PX2WORLD));
+ U_POINT16 cDest = point16_set(round(pWH2[Geom::X] * PX2WORLD), round(pWH2[Geom::Y] * PX2WORLD));
+ U_POINT16 Src = point16_set(0, 0);
+ U_POINT16 cSrc = point16_set(w, h);
+ rec = U_WMRSTRETCHDIB_set(
+ 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 || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::image at U_WMRSTRETCHDIB_set");
+ }
+ free(px);
+ free(Bmi);
+ if (numCt) {
+ free(ct);
+ }
+ return 0;
+}
+
+// may also be called with a simple_shape or an empty path, whereupon it just returns without doing anything
+unsigned int PrintWmf::print_pathv(Geom::PathVector const &pathv, const Geom::Affine &transform)
+{
+ char *rec = NULL;
+ U_POINT16 *pt16hold, *pt16ptr;
+ uint16_t *n16hold;
+ uint16_t *n16ptr;
+
+ simple_shape = print_simple_shape(pathv, transform);
+ if (!simple_shape && !pathv.empty()) {
+ // WMF does not have beziers, need to convert to ONLY linears with something like this:
+ Geom::PathVector pv = pathv_to_linear(pathv * transform, MAXDISP);
+
+ /** For all Subpaths in the <path> */
+
+ /* If the path consists entirely of closed subpaths use one polypolygon.
+ Otherwise use a mix of polygon or polyline separately on each path.
+ If the polyline turns out to be single line segments, use a series of MOVETO/LINETO instead,
+ because WMF has no POLYPOLYLINE.
+ The former allows path delimited donuts and the like, which
+ cannot be represented in WMF with polygon or polyline because there is no external way to combine paths
+ as there is in EMF or SVG.
+ For polygons specify the last point the same as the first. The WMF/EMF manuals say that the
+ reading program SHOULD close the path, which allows a conforming program not to, potentially rendering
+ a closed path as an open one. */
+ int nPolys = 0;
+ int totPoints = 0;
+ for (Geom::PathVector::const_iterator pit = pv.begin(); pit != pv.end(); ++pit) {
+ totPoints += 1 + pit->size_default(); // big array, will hold all points, for all polygons. Size_default ignores first point in each path.
+ if (pit->end_default() == pit->end_closed()) {
+ nPolys++;
+ } else {
+ nPolys = 0;
+ break;
+ }
+ }
+
+ if (nPolys > 1) { // a single polypolygon, a single polygon falls through to the else
+ pt16hold = pt16ptr = (U_POINT16 *) malloc(totPoints * sizeof(U_POINT16));
+ if (!pt16ptr) {
+ return(false);
+ }
+
+ n16hold = n16ptr = (uint16_t *) malloc(nPolys * sizeof(uint16_t));
+ if (!n16ptr) {
+ free(pt16hold);
+ return(false);
+ }
+
+ for (Geom::PathVector::const_iterator pit = pv.begin(); pit != pv.end(); ++pit) {
+ using Geom::X;
+ using Geom::Y;
+
+
+ *n16ptr++ = pit->size_default(); // points in the subpath
+
+ /** For each segment in the subpath */
+
+ Geom::Point p1 = pit->initialPoint(); // This point is special, it isn't in the interator
+
+ p1[X] = (p1[X] * PX2WORLD);
+ p1[Y] = (p1[Y] * PX2WORLD);
+ *pt16ptr++ = point16_set((int32_t) round(p1[X]), (int32_t) round(p1[Y]));
+
+ for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_open(); ++cit) {
+ Geom::Point p1 = cit->finalPoint();
+
+ p1[X] = (p1[X] * PX2WORLD);
+ p1[Y] = (p1[Y] * PX2WORLD);
+ *pt16ptr++ = point16_set((int32_t) round(p1[X]), (int32_t) round(p1[Y]));
+ }
+
+ }
+ rec = U_WMRPOLYPOLYGON_set(nPolys, n16hold, pt16hold);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::print_pathv at U_WMRPOLYPOLYGON_set");
+ }
+ free(pt16hold);
+ free(n16hold);
+ } else { // one or more polyline or polygons (but not all polygons, that would be the preceding case)
+ for (Geom::PathVector::const_iterator pit = pv.begin(); pit != pv.end(); ++pit) {
+ using Geom::X;
+ using Geom::Y;
+
+ /* Malformatted Polylines with a sequence like M L M M L have been seen, the 2nd M does nothing
+ and that point must not go into the output. */
+ if (!(pit->size_default())) {
+ continue;
+ }
+ /* Figure out how many points there are, make an array big enough to hold them, and store
+ all the points. This is the same for open or closed path. This gives the upper bound for
+ the number of points. The actual number used is calculated on the fly.
+ */
+ int nPoints = 1 + pit->size_default();
+
+ pt16hold = pt16ptr = (U_POINT16 *) malloc(nPoints * sizeof(U_POINT16));
+ if (!pt16ptr) {
+ break;
+ }
+
+ /** For each segment in the subpath */
+
+ Geom::Point p1 = pit->initialPoint(); // This point is special, it isn't in the interator
+
+ p1[X] = (p1[X] * PX2WORLD);
+ p1[Y] = (p1[Y] * PX2WORLD);
+ *pt16ptr++ = point16_set((int32_t) round(p1[X]), (int32_t) round(p1[Y]));
+ nPoints = 1;
+
+ for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_default(); ++cit, nPoints++) {
+ Geom::Point p1 = cit->finalPoint();
+
+ p1[X] = (p1[X] * PX2WORLD);
+ p1[Y] = (p1[Y] * PX2WORLD);
+ *pt16ptr++ = point16_set((int32_t) round(p1[X]), (int32_t) round(p1[Y]));
+ }
+
+ if (pit->end_default() == pit->end_closed()) {
+ rec = U_WMRPOLYGON_set(nPoints, pt16hold);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::print_pathv at U_WMRPOLYGON_set");
+ }
+ } else if (nPoints > 2) {
+ rec = U_WMRPOLYLINE_set(nPoints, pt16hold);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::print_pathv at U_POLYLINE_set");
+ }
+ } else if (nPoints == 2) {
+ rec = U_WMRMOVETO_set(pt16hold[0]);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::print_pathv at U_WMRMOVETO_set");
+ }
+ rec = U_WMRLINETO_set(pt16hold[1]);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::print_pathv at U_WMRLINETO_set");
+ }
+ }
+ free(pt16hold);
+ }
+ }
+ }
+
+ // WMF has no fill or stroke commands, the draw does it with active pen/brush
+
+ // clean out brush and pen, but only after all parts of the draw complete
+ if (use_fill) {
+ destroy_brush();
+ }
+ if (use_stroke) {
+ destroy_pen();
+ }
+
+ return TRUE;
+}
+
+
+unsigned int PrintWmf::text(Inkscape::Extension::Print * /*mod*/, char const *text, Geom::Point const &p,
+ SPStyle const *const style)
+{
+ if (!wt) {
+ 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 ky;
+
+ // the dx array is smuggled in like: text<nul>w1 w2 w3 ...wn<nul><nul>, where the widths are floats 7 characters wide, including the space
+ int ndx, rtl;
+ int16_t *adx;
+ smuggle_adxky_out(text, &adx, &ky, &rtl, &ndx, PX2WORLD * std::min(tf.expansionX(), tf.expansionY())); // side effect: free() adx
+
+ uint32_t textalignment;
+ if (rtl > 0) {
+ textalignment = U_TA_BASELINE | U_TA_LEFT;
+ } else {
+ textalignment = U_TA_BASELINE | U_TA_RIGHT | U_TA_RTLREADING;
+ }
+ if (textalignment != htextalignment) {
+ htextalignment = textalignment;
+ rec = U_WMRSETTEXTALIGN_set(textalignment);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::text at U_WMRSETTEXTALIGN_set");
+ }
+ }
+
+ 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 as Utf16le 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);
+ // The preceding hopefully handled conversions to symbol, wingdings or zapf dingbats. Now slam everything
+ // else down into latin1, which is all WMF can handle. If the language isn't English expect terrible results.
+ char *latin1_text = U_Utf16leToLatin1(unicode_text, 0, NULL);
+ free(unicode_text);
+
+ //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.
+ FontfixParams params;
+
+ if (FixPPTCharPos) {
+ switch (newfont) {
+ case CVTSYM:
+ _lookup_ppt_fontfix("Convert To Symbol", params);
+ break;
+ case CVTZDG:
+ _lookup_ppt_fontfix("Convert To Zapf Dingbats", params);
+ break;
+ case CVTWDG:
+ _lookup_ppt_fontfix("Convert To Wingdings", params);
+ break;
+ default: //also CVTNON
+ _lookup_ppt_fontfix(style->text->font_family.value, params);
+ break;
+ }
+ if (params.f2 != 0 || params.f3 != 0) {
+ 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 WMF as fairly small integers and that limits their precision.
+ The WMF output files produced here have been designed so that the integer valued pt sizes
+ land right on an integer value in the WMF 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.
+ char *facename;
+ if (!newfont) {
+ facename = U_Utf8ToLatin1(style->text->font_family.value, 0, NULL);
+ } else {
+ facename = U_Utf8ToLatin1(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 WMF which is scaled
+ // differently by height/width, so leave lfWidth alone.
+
+ U_FONT *puf = U_FONT_set(
+ textheight,
+ 0,
+ round(rot),
+ round(rot),
+ _translate_weight(style->font_weight.computed),
+ (style->font_style.computed == SP_CSS_FONT_STYLE_ITALIC),
+ style->text_decoration_line.underline,
+ style->text_decoration_line.line_through,
+ U_DEFAULT_CHARSET,
+ U_OUT_DEFAULT_PRECIS,
+ U_CLIP_DEFAULT_PRECIS,
+ U_DEFAULT_QUALITY,
+ U_DEFAULT_PITCH | U_FF_DONTCARE,
+ facename);
+ free(facename);
+
+ rec = wcreatefontindirect_set(&hfont, wht, puf);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::text at wcreatefontindirect_set");
+ }
+ free(puf);
+ }
+
+ rec = wselectobject_set(hfont, wht);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::text at wselectobject_set");
+ }
+
+ float rgb[3];
+ sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
+ // only change the text color when it needs to be changed
+ if (memcmp(htextcolor_rgb, rgb, 3 * sizeof(float))) {
+ memcpy(htextcolor_rgb, rgb, 3 * sizeof(float));
+ rec = U_WMRSETTEXTCOLOR_set(U_RGB(255 * rgb[0], 255 * rgb[1], 255 * rgb[2]));
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::text at U_WMRSETTEXTCOLOR_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 WMF text alignment must always be TA_BASELINE|TA_LEFT.
+ // this is set at the beginning of the file and never changed
+
+ // Transparent text background, never changes, set at the beginning of the file
+
+ Geom::Point p2 = p * tf;
+
+ //Handle super/subscripts and vertical kerning
+ /* Previously used this, but vertical kerning was not supported
+ p2[Geom::X] -= style->baseline_shift.computed * std::sin( rotb );
+ p2[Geom::Y] -= style->baseline_shift.computed * std::cos( rotb );
+ */
+ p2[Geom::X] += ky * std::sin(rotb);
+ p2[Geom::Y] += ky * std::cos(rotb);
+
+ //Conditionally handle compensation for PPT WMF import bug (affects PPT 2003-2010, at least)
+ if (FixPPTCharPos) {
+ if (fix90n == 1) { //vertical
+ dx = 0.0;
+ dy = params.f3 * style->font_size.computed * std::cos(rotb);
+ } else if (fix90n == 2) { //horizontal
+ dx = params.f2 * style->font_size.computed * std::sin(rotb);
+ dy = 0.0;
+ } else {
+ dx = params.f1 * style->font_size.computed * std::sin(rotb);
+ dy = params.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
+ if (rtl > 0) {
+ rec = U_WMREXTTEXTOUT_set((U_POINT16) {
+ (int16_t) xpos, (int16_t) ypos
+ },
+ ndx, U_ETO_NONE, latin1_text, adx, U_RCL16_DEF);
+ } else { // RTL text, U_TA_RTLREADING should be enough, but set this one too just in case
+ rec = U_WMREXTTEXTOUT_set((U_POINT16) {
+ (int16_t) xpos, (int16_t) ypos
+ },
+ ndx, U_ETO_RTLREADING, latin1_text, adx, U_RCL16_DEF);
+ }
+ free(latin1_text);
+ free(adx);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::text at U_WMREXTTEXTOUTW_set");
+ }
+
+ rec = wdeleteobject_set(&hfont, wht);
+ if (!rec || wmf_append((U_METARECORD *)rec, wt, U_REC_FREE)) {
+ g_error("Fatal programming error in PrintWmf::text at wdeleteobject_set");
+ }
+
+ return 0;
+}
+
+void PrintWmf::init(void)
+{
+ _load_ppt_fontfix_data();
+
+ /* WMF print */
+ Inkscape::Extension::build_from_mem(
+ "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
+ "<name>Windows Metafile Print</name>\n"
+ "<id>org.inkscape.print.wmf</id>\n"
+ "<param name=\"destination\" type=\"string\"></param>\n"
+ "<param name=\"textToPath\" type=\"boolean\">true</param>\n"
+ "<param name=\"pageBoundingBox\" type=\"boolean\">true</param>\n"
+ "<param name=\"FixPPTCharPos\" type=\"boolean\">false</param>\n"
+ "<param name=\"FixPPTDashLine\" type=\"boolean\">false</param>\n"
+ "<param name=\"FixPPTGrad2Polys\" type=\"boolean\">false</param>\n"
+ "<param name=\"FixPPTPatternAsHatch\" type=\"boolean\">false</param>\n"
+ "<print/>\n"
+ "</inkscape-extension>", new PrintWmf());
+
+ 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 --git a/src/extension/internal/emf-win32-print.h b/src/extension/internal/wmf-print.h
index e7bd08477..1e5d4c323 100644
--- a/src/extension/internal/emf-win32-print.h
+++ b/src/extension/internal/wmf-print.h
@@ -1,5 +1,5 @@
/** @file
- * @brief Enhanced Metafile printing - implementation
+ * @brief Windows Metafile printing - implementation
*/
/* Author:
* Ulf Erikson <ulferikson@users.sf.net>
@@ -8,59 +8,34 @@
*
* 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
+#ifndef SEEN_INKSCAPE_EXTENSION_INTERNAL_WMF_PRINT_H
+#define SEEN_INKSCAPE_EXTENSION_INTERNAL_WMF_PRINT_H
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-
-#include "extension/implementation/implementation.h"
-//#include "extension/extension.h"
-
-#include <2geom/pathvector.h>
+#include <libuemf/uwmf.h>
+#include "extension/internal/metafile-print.h"
-#include <stack>
+#include "sp-gradient.h"
+#include "splivarot.h" // pieces for union on shapes
+#include "display/canvas-bpath.h" // for SPWindRule
namespace Inkscape {
namespace Extension {
namespace Internal {
-class PrintEmfWin32 : public Inkscape::Extension::Implementation::Implementation
+class PrintWmf : public PrintMetafile
{
- double _width;
- double _height;
- HDC hdc;
- RECT rc;
-
- HBRUSH hbrush, hbrushOld;
- HPEN hpen, hpenOld;
-
- std::stack<Geom::Affine> m_tr_stack;
- Geom::PathVector fill_pathv;
- Geom::Affine fill_transform;
- bool stroke_and_fill;
- bool fill_only;
- bool simple_shape;
+ uint32_t hbrush, hpen, hpenOld, hbrush_null, hpen_null;
+ uint32_t hmiterlimit; // used to minimize redundant records that set this
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);
+ PrintWmf();
/* Print functions */
virtual unsigned int setup (Inkscape::Extension::Print * module);
@@ -69,8 +44,6 @@ public:
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,
@@ -81,32 +54,33 @@ public:
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);
+ static void smuggle_adxky_out(const char *string, int16_t **adx, double *ky, int *rtl, int *ndx, float scale);
+ int create_brush(SPStyle const *style, PU_COLORREF fcolor);
void destroy_brush();
-
- void create_pen(SPStyle const *style, const Geom::Affine &transform);
-
+ int 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__ */
+#endif /* __INKSCAPE_EXTENSION_INTERNAL_PRINT_WMF_H__ */
/*
Local Variables:
diff --git a/src/extension/internal/wpg-input.cpp b/src/extension/internal/wpg-input.cpp
index cb1677547..14ff3ec77 100644
--- a/src/extension/internal/wpg-input.cpp
+++ b/src/extension/internal/wpg-input.cpp
@@ -48,6 +48,8 @@
#include "extension/system.h"
#include "extension/input.h"
#include "document.h"
+#include "sp-root.h"
+#include "util/units.h"
#include <cstring>
// Take a guess and fallback to 0.1.x if no configure has run
@@ -109,6 +111,12 @@ SPDocument *WpgInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * u
//printf("I've got a doc: \n%s", painter.document.c_str());
SPDocument * doc = SPDocument::createNewDocFromMem(output.cstr(), strlen(output.cstr()), TRUE);
+
+ // Set viewBox if it doesn't exist
+ if (doc && !doc->getRoot()->viewBox_set) {
+ doc->setViewBox(Geom::Rect::from_xywh(0, 0, doc->getWidth().value(doc->getDefaultUnit()), doc->getHeight().value(doc->getDefaultUnit())));
+ }
+
delete input;
return doc;
}